palaryn 0.1.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 (607) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +716 -0
  3. package/dist/sdk/typescript/src/client.d.ts +71 -0
  4. package/dist/sdk/typescript/src/client.d.ts.map +1 -0
  5. package/dist/sdk/typescript/src/client.js +176 -0
  6. package/dist/sdk/typescript/src/client.js.map +1 -0
  7. package/dist/sdk/typescript/src/errors.d.ts +50 -0
  8. package/dist/sdk/typescript/src/errors.d.ts.map +1 -0
  9. package/dist/sdk/typescript/src/errors.js +103 -0
  10. package/dist/sdk/typescript/src/errors.js.map +1 -0
  11. package/dist/sdk/typescript/src/index.d.ts +4 -0
  12. package/dist/sdk/typescript/src/index.d.ts.map +1 -0
  13. package/dist/sdk/typescript/src/index.js +15 -0
  14. package/dist/sdk/typescript/src/index.js.map +1 -0
  15. package/dist/sdk/typescript/src/types.d.ts +101 -0
  16. package/dist/sdk/typescript/src/types.d.ts.map +1 -0
  17. package/dist/sdk/typescript/src/types.js +6 -0
  18. package/dist/sdk/typescript/src/types.js.map +1 -0
  19. package/dist/src/admin/index.d.ts +2 -0
  20. package/dist/src/admin/index.d.ts.map +1 -0
  21. package/dist/src/admin/index.js +6 -0
  22. package/dist/src/admin/index.js.map +1 -0
  23. package/dist/src/admin/routes.d.ts +5 -0
  24. package/dist/src/admin/routes.d.ts.map +1 -0
  25. package/dist/src/admin/routes.js +471 -0
  26. package/dist/src/admin/routes.js.map +1 -0
  27. package/dist/src/admin/templates.d.ts +51 -0
  28. package/dist/src/admin/templates.d.ts.map +1 -0
  29. package/dist/src/admin/templates.js +500 -0
  30. package/dist/src/admin/templates.js.map +1 -0
  31. package/dist/src/anomaly/detector.d.ts +141 -0
  32. package/dist/src/anomaly/detector.d.ts.map +1 -0
  33. package/dist/src/anomaly/detector.js +554 -0
  34. package/dist/src/anomaly/detector.js.map +1 -0
  35. package/dist/src/anomaly/index.d.ts +2 -0
  36. package/dist/src/anomaly/index.d.ts.map +1 -0
  37. package/dist/src/anomaly/index.js +7 -0
  38. package/dist/src/anomaly/index.js.map +1 -0
  39. package/dist/src/approval/manager.d.ts +147 -0
  40. package/dist/src/approval/manager.d.ts.map +1 -0
  41. package/dist/src/approval/manager.js +511 -0
  42. package/dist/src/approval/manager.js.map +1 -0
  43. package/dist/src/approval/webhook.d.ts +36 -0
  44. package/dist/src/approval/webhook.d.ts.map +1 -0
  45. package/dist/src/approval/webhook.js +135 -0
  46. package/dist/src/approval/webhook.js.map +1 -0
  47. package/dist/src/audit/logger.d.ts +70 -0
  48. package/dist/src/audit/logger.d.ts.map +1 -0
  49. package/dist/src/audit/logger.js +440 -0
  50. package/dist/src/audit/logger.js.map +1 -0
  51. package/dist/src/auth/index.d.ts +6 -0
  52. package/dist/src/auth/index.d.ts.map +1 -0
  53. package/dist/src/auth/index.js +22 -0
  54. package/dist/src/auth/index.js.map +1 -0
  55. package/dist/src/auth/password.d.ts +3 -0
  56. package/dist/src/auth/password.d.ts.map +1 -0
  57. package/dist/src/auth/password.js +25 -0
  58. package/dist/src/auth/password.js.map +1 -0
  59. package/dist/src/auth/pkce.d.ts +13 -0
  60. package/dist/src/auth/pkce.d.ts.map +1 -0
  61. package/dist/src/auth/pkce.js +58 -0
  62. package/dist/src/auth/pkce.js.map +1 -0
  63. package/dist/src/auth/providers.d.ts +28 -0
  64. package/dist/src/auth/providers.d.ts.map +1 -0
  65. package/dist/src/auth/providers.js +198 -0
  66. package/dist/src/auth/providers.js.map +1 -0
  67. package/dist/src/auth/routes.d.ts +14 -0
  68. package/dist/src/auth/routes.d.ts.map +1 -0
  69. package/dist/src/auth/routes.js +431 -0
  70. package/dist/src/auth/routes.js.map +1 -0
  71. package/dist/src/auth/session.d.ts +24 -0
  72. package/dist/src/auth/session.d.ts.map +1 -0
  73. package/dist/src/auth/session.js +105 -0
  74. package/dist/src/auth/session.js.map +1 -0
  75. package/dist/src/billing/index.d.ts +7 -0
  76. package/dist/src/billing/index.d.ts.map +1 -0
  77. package/dist/src/billing/index.js +14 -0
  78. package/dist/src/billing/index.js.map +1 -0
  79. package/dist/src/billing/plan-enforcer.d.ts +44 -0
  80. package/dist/src/billing/plan-enforcer.d.ts.map +1 -0
  81. package/dist/src/billing/plan-enforcer.js +110 -0
  82. package/dist/src/billing/plan-enforcer.js.map +1 -0
  83. package/dist/src/billing/routes.d.ts +15 -0
  84. package/dist/src/billing/routes.d.ts.map +1 -0
  85. package/dist/src/billing/routes.js +193 -0
  86. package/dist/src/billing/routes.js.map +1 -0
  87. package/dist/src/billing/stripe-client.d.ts +14 -0
  88. package/dist/src/billing/stripe-client.d.ts.map +1 -0
  89. package/dist/src/billing/stripe-client.js +51 -0
  90. package/dist/src/billing/stripe-client.js.map +1 -0
  91. package/dist/src/billing/webhook-handler.d.ts +19 -0
  92. package/dist/src/billing/webhook-handler.d.ts.map +1 -0
  93. package/dist/src/billing/webhook-handler.js +169 -0
  94. package/dist/src/billing/webhook-handler.js.map +1 -0
  95. package/dist/src/billing/webhook-routes.d.ts +5 -0
  96. package/dist/src/billing/webhook-routes.d.ts.map +1 -0
  97. package/dist/src/billing/webhook-routes.js +30 -0
  98. package/dist/src/billing/webhook-routes.js.map +1 -0
  99. package/dist/src/budget/manager.d.ts +95 -0
  100. package/dist/src/budget/manager.d.ts.map +1 -0
  101. package/dist/src/budget/manager.js +547 -0
  102. package/dist/src/budget/manager.js.map +1 -0
  103. package/dist/src/budget/usage-extractor.d.ts +38 -0
  104. package/dist/src/budget/usage-extractor.d.ts.map +1 -0
  105. package/dist/src/budget/usage-extractor.js +165 -0
  106. package/dist/src/budget/usage-extractor.js.map +1 -0
  107. package/dist/src/cli.d.ts +3 -0
  108. package/dist/src/cli.d.ts.map +1 -0
  109. package/dist/src/cli.js +115 -0
  110. package/dist/src/cli.js.map +1 -0
  111. package/dist/src/config/defaults.d.ts +3 -0
  112. package/dist/src/config/defaults.d.ts.map +1 -0
  113. package/dist/src/config/defaults.js +243 -0
  114. package/dist/src/config/defaults.js.map +1 -0
  115. package/dist/src/config/validate.d.ts +15 -0
  116. package/dist/src/config/validate.d.ts.map +1 -0
  117. package/dist/src/config/validate.js +105 -0
  118. package/dist/src/config/validate.js.map +1 -0
  119. package/dist/src/dlp/composite-scanner.d.ts +47 -0
  120. package/dist/src/dlp/composite-scanner.d.ts.map +1 -0
  121. package/dist/src/dlp/composite-scanner.js +186 -0
  122. package/dist/src/dlp/composite-scanner.js.map +1 -0
  123. package/dist/src/dlp/index.d.ts +10 -0
  124. package/dist/src/dlp/index.d.ts.map +1 -0
  125. package/dist/src/dlp/index.js +26 -0
  126. package/dist/src/dlp/index.js.map +1 -0
  127. package/dist/src/dlp/interfaces.d.ts +33 -0
  128. package/dist/src/dlp/interfaces.d.ts.map +1 -0
  129. package/dist/src/dlp/interfaces.js +3 -0
  130. package/dist/src/dlp/interfaces.js.map +1 -0
  131. package/dist/src/dlp/patterns.d.ts +9 -0
  132. package/dist/src/dlp/patterns.d.ts.map +1 -0
  133. package/dist/src/dlp/patterns.js +25 -0
  134. package/dist/src/dlp/patterns.js.map +1 -0
  135. package/dist/src/dlp/prompt-injection-backend.d.ts +68 -0
  136. package/dist/src/dlp/prompt-injection-backend.d.ts.map +1 -0
  137. package/dist/src/dlp/prompt-injection-backend.js +148 -0
  138. package/dist/src/dlp/prompt-injection-backend.js.map +1 -0
  139. package/dist/src/dlp/prompt-injection-patterns.d.ts +32 -0
  140. package/dist/src/dlp/prompt-injection-patterns.d.ts.map +1 -0
  141. package/dist/src/dlp/prompt-injection-patterns.js +290 -0
  142. package/dist/src/dlp/prompt-injection-patterns.js.map +1 -0
  143. package/dist/src/dlp/regex-backend.d.ts +32 -0
  144. package/dist/src/dlp/regex-backend.d.ts.map +1 -0
  145. package/dist/src/dlp/regex-backend.js +153 -0
  146. package/dist/src/dlp/regex-backend.js.map +1 -0
  147. package/dist/src/dlp/scanner.d.ts +122 -0
  148. package/dist/src/dlp/scanner.d.ts.map +1 -0
  149. package/dist/src/dlp/scanner.js +444 -0
  150. package/dist/src/dlp/scanner.js.map +1 -0
  151. package/dist/src/dlp/text-normalizer.d.ts +41 -0
  152. package/dist/src/dlp/text-normalizer.d.ts.map +1 -0
  153. package/dist/src/dlp/text-normalizer.js +203 -0
  154. package/dist/src/dlp/text-normalizer.js.map +1 -0
  155. package/dist/src/dlp/trufflehog-backend.d.ts +64 -0
  156. package/dist/src/dlp/trufflehog-backend.d.ts.map +1 -0
  157. package/dist/src/dlp/trufflehog-backend.js +151 -0
  158. package/dist/src/dlp/trufflehog-backend.js.map +1 -0
  159. package/dist/src/executor/http-executor.d.ts +25 -0
  160. package/dist/src/executor/http-executor.d.ts.map +1 -0
  161. package/dist/src/executor/http-executor.js +333 -0
  162. package/dist/src/executor/http-executor.js.map +1 -0
  163. package/dist/src/executor/index.d.ts +6 -0
  164. package/dist/src/executor/index.d.ts.map +1 -0
  165. package/dist/src/executor/index.js +12 -0
  166. package/dist/src/executor/index.js.map +1 -0
  167. package/dist/src/executor/interfaces.d.ts +11 -0
  168. package/dist/src/executor/interfaces.d.ts.map +1 -0
  169. package/dist/src/executor/interfaces.js +3 -0
  170. package/dist/src/executor/interfaces.js.map +1 -0
  171. package/dist/src/executor/noop-executor.d.ts +13 -0
  172. package/dist/src/executor/noop-executor.d.ts.map +1 -0
  173. package/dist/src/executor/noop-executor.js +21 -0
  174. package/dist/src/executor/noop-executor.js.map +1 -0
  175. package/dist/src/executor/registry.d.ts +30 -0
  176. package/dist/src/executor/registry.d.ts.map +1 -0
  177. package/dist/src/executor/registry.js +62 -0
  178. package/dist/src/executor/registry.js.map +1 -0
  179. package/dist/src/executor/slack-executor.d.ts +24 -0
  180. package/dist/src/executor/slack-executor.d.ts.map +1 -0
  181. package/dist/src/executor/slack-executor.js +147 -0
  182. package/dist/src/executor/slack-executor.js.map +1 -0
  183. package/dist/src/index.d.ts +25 -0
  184. package/dist/src/index.d.ts.map +1 -0
  185. package/dist/src/index.js +74 -0
  186. package/dist/src/index.js.map +1 -0
  187. package/dist/src/mcp/auth-verifier.d.ts +23 -0
  188. package/dist/src/mcp/auth-verifier.d.ts.map +1 -0
  189. package/dist/src/mcp/auth-verifier.js +162 -0
  190. package/dist/src/mcp/auth-verifier.js.map +1 -0
  191. package/dist/src/mcp/bridge.d.ts +132 -0
  192. package/dist/src/mcp/bridge.d.ts.map +1 -0
  193. package/dist/src/mcp/bridge.js +734 -0
  194. package/dist/src/mcp/bridge.js.map +1 -0
  195. package/dist/src/mcp/http-transport.d.ts +32 -0
  196. package/dist/src/mcp/http-transport.d.ts.map +1 -0
  197. package/dist/src/mcp/http-transport.js +538 -0
  198. package/dist/src/mcp/http-transport.js.map +1 -0
  199. package/dist/src/mcp/index.d.ts +10 -0
  200. package/dist/src/mcp/index.d.ts.map +1 -0
  201. package/dist/src/mcp/index.js +17 -0
  202. package/dist/src/mcp/index.js.map +1 -0
  203. package/dist/src/mcp/oauth-pages.d.ts +23 -0
  204. package/dist/src/mcp/oauth-pages.d.ts.map +1 -0
  205. package/dist/src/mcp/oauth-pages.js +121 -0
  206. package/dist/src/mcp/oauth-pages.js.map +1 -0
  207. package/dist/src/mcp/oauth-postgres-stores.d.ts +55 -0
  208. package/dist/src/mcp/oauth-postgres-stores.d.ts.map +1 -0
  209. package/dist/src/mcp/oauth-postgres-stores.js +226 -0
  210. package/dist/src/mcp/oauth-postgres-stores.js.map +1 -0
  211. package/dist/src/mcp/oauth-provider.d.ts +95 -0
  212. package/dist/src/mcp/oauth-provider.d.ts.map +1 -0
  213. package/dist/src/mcp/oauth-provider.js +360 -0
  214. package/dist/src/mcp/oauth-provider.js.map +1 -0
  215. package/dist/src/mcp/oauth-stores.d.ts +62 -0
  216. package/dist/src/mcp/oauth-stores.d.ts.map +1 -0
  217. package/dist/src/mcp/oauth-stores.js +154 -0
  218. package/dist/src/mcp/oauth-stores.js.map +1 -0
  219. package/dist/src/mcp/server.d.ts +18 -0
  220. package/dist/src/mcp/server.d.ts.map +1 -0
  221. package/dist/src/mcp/server.js +51 -0
  222. package/dist/src/mcp/server.js.map +1 -0
  223. package/dist/src/metrics/collector.d.ts +106 -0
  224. package/dist/src/metrics/collector.d.ts.map +1 -0
  225. package/dist/src/metrics/collector.js +311 -0
  226. package/dist/src/metrics/collector.js.map +1 -0
  227. package/dist/src/metrics/index.d.ts +2 -0
  228. package/dist/src/metrics/index.d.ts.map +1 -0
  229. package/dist/src/metrics/index.js +6 -0
  230. package/dist/src/metrics/index.js.map +1 -0
  231. package/dist/src/middleware/auth.d.ts +77 -0
  232. package/dist/src/middleware/auth.d.ts.map +1 -0
  233. package/dist/src/middleware/auth.js +720 -0
  234. package/dist/src/middleware/auth.js.map +1 -0
  235. package/dist/src/middleware/session.d.ts +18 -0
  236. package/dist/src/middleware/session.d.ts.map +1 -0
  237. package/dist/src/middleware/session.js +67 -0
  238. package/dist/src/middleware/session.js.map +1 -0
  239. package/dist/src/middleware/validate.d.ts +3 -0
  240. package/dist/src/middleware/validate.d.ts.map +1 -0
  241. package/dist/src/middleware/validate.js +85 -0
  242. package/dist/src/middleware/validate.js.map +1 -0
  243. package/dist/src/policy/engine.d.ts +107 -0
  244. package/dist/src/policy/engine.d.ts.map +1 -0
  245. package/dist/src/policy/engine.js +646 -0
  246. package/dist/src/policy/engine.js.map +1 -0
  247. package/dist/src/policy/index.d.ts +3 -0
  248. package/dist/src/policy/index.d.ts.map +1 -0
  249. package/dist/src/policy/index.js +8 -0
  250. package/dist/src/policy/index.js.map +1 -0
  251. package/dist/src/policy/opa-engine.d.ts +176 -0
  252. package/dist/src/policy/opa-engine.d.ts.map +1 -0
  253. package/dist/src/policy/opa-engine.js +790 -0
  254. package/dist/src/policy/opa-engine.js.map +1 -0
  255. package/dist/src/proxy/forward-proxy.d.ts +30 -0
  256. package/dist/src/proxy/forward-proxy.d.ts.map +1 -0
  257. package/dist/src/proxy/forward-proxy.js +580 -0
  258. package/dist/src/proxy/forward-proxy.js.map +1 -0
  259. package/dist/src/proxy/index.d.ts +2 -0
  260. package/dist/src/proxy/index.d.ts.map +1 -0
  261. package/dist/src/proxy/index.js +8 -0
  262. package/dist/src/proxy/index.js.map +1 -0
  263. package/dist/src/ratelimit/limiter.d.ts +45 -0
  264. package/dist/src/ratelimit/limiter.d.ts.map +1 -0
  265. package/dist/src/ratelimit/limiter.js +158 -0
  266. package/dist/src/ratelimit/limiter.js.map +1 -0
  267. package/dist/src/replay/engine.d.ts +40 -0
  268. package/dist/src/replay/engine.d.ts.map +1 -0
  269. package/dist/src/replay/engine.js +106 -0
  270. package/dist/src/replay/engine.js.map +1 -0
  271. package/dist/src/replay/index.d.ts +2 -0
  272. package/dist/src/replay/index.d.ts.map +1 -0
  273. package/dist/src/replay/index.js +6 -0
  274. package/dist/src/replay/index.js.map +1 -0
  275. package/dist/src/saas/index.d.ts +2 -0
  276. package/dist/src/saas/index.d.ts.map +1 -0
  277. package/dist/src/saas/index.js +18 -0
  278. package/dist/src/saas/index.js.map +1 -0
  279. package/dist/src/saas/routes.d.ts +18 -0
  280. package/dist/src/saas/routes.d.ts.map +1 -0
  281. package/dist/src/saas/routes.js +1566 -0
  282. package/dist/src/saas/routes.js.map +1 -0
  283. package/dist/src/server/app.d.ts +44 -0
  284. package/dist/src/server/app.d.ts.map +1 -0
  285. package/dist/src/server/app.js +854 -0
  286. package/dist/src/server/app.js.map +1 -0
  287. package/dist/src/server/errors.d.ts +32 -0
  288. package/dist/src/server/errors.d.ts.map +1 -0
  289. package/dist/src/server/errors.js +39 -0
  290. package/dist/src/server/errors.js.map +1 -0
  291. package/dist/src/server/gateway.d.ts +165 -0
  292. package/dist/src/server/gateway.d.ts.map +1 -0
  293. package/dist/src/server/gateway.js +964 -0
  294. package/dist/src/server/gateway.js.map +1 -0
  295. package/dist/src/server/index.d.ts +2 -0
  296. package/dist/src/server/index.d.ts.map +1 -0
  297. package/dist/src/server/index.js +295 -0
  298. package/dist/src/server/index.js.map +1 -0
  299. package/dist/src/server/logger.d.ts +33 -0
  300. package/dist/src/server/logger.d.ts.map +1 -0
  301. package/dist/src/server/logger.js +230 -0
  302. package/dist/src/server/logger.js.map +1 -0
  303. package/dist/src/server/stream-proxy.d.ts +32 -0
  304. package/dist/src/server/stream-proxy.d.ts.map +1 -0
  305. package/dist/src/server/stream-proxy.js +184 -0
  306. package/dist/src/server/stream-proxy.js.map +1 -0
  307. package/dist/src/storage/file-persistence.d.ts +48 -0
  308. package/dist/src/storage/file-persistence.d.ts.map +1 -0
  309. package/dist/src/storage/file-persistence.js +280 -0
  310. package/dist/src/storage/file-persistence.js.map +1 -0
  311. package/dist/src/storage/index.d.ts +5 -0
  312. package/dist/src/storage/index.d.ts.map +1 -0
  313. package/dist/src/storage/index.js +21 -0
  314. package/dist/src/storage/index.js.map +1 -0
  315. package/dist/src/storage/interfaces.d.ts +237 -0
  316. package/dist/src/storage/interfaces.d.ts.map +1 -0
  317. package/dist/src/storage/interfaces.js +3 -0
  318. package/dist/src/storage/interfaces.js.map +1 -0
  319. package/dist/src/storage/memory.d.ts +162 -0
  320. package/dist/src/storage/memory.d.ts.map +1 -0
  321. package/dist/src/storage/memory.js +603 -0
  322. package/dist/src/storage/memory.js.map +1 -0
  323. package/dist/src/storage/postgres.d.ts +267 -0
  324. package/dist/src/storage/postgres.d.ts.map +1 -0
  325. package/dist/src/storage/postgres.js +1555 -0
  326. package/dist/src/storage/postgres.js.map +1 -0
  327. package/dist/src/storage/redis.d.ts +202 -0
  328. package/dist/src/storage/redis.d.ts.map +1 -0
  329. package/dist/src/storage/redis.js +629 -0
  330. package/dist/src/storage/redis.js.map +1 -0
  331. package/dist/src/tracing/index.d.ts +2 -0
  332. package/dist/src/tracing/index.d.ts.map +1 -0
  333. package/dist/src/tracing/index.js +6 -0
  334. package/dist/src/tracing/index.js.map +1 -0
  335. package/dist/src/tracing/provider.d.ts +43 -0
  336. package/dist/src/tracing/provider.d.ts.map +1 -0
  337. package/dist/src/tracing/provider.js +74 -0
  338. package/dist/src/tracing/provider.js.map +1 -0
  339. package/dist/src/trust/calculator.d.ts +54 -0
  340. package/dist/src/trust/calculator.d.ts.map +1 -0
  341. package/dist/src/trust/calculator.js +102 -0
  342. package/dist/src/trust/calculator.js.map +1 -0
  343. package/dist/src/trust/index.d.ts +2 -0
  344. package/dist/src/trust/index.d.ts.map +1 -0
  345. package/dist/src/trust/index.js +7 -0
  346. package/dist/src/trust/index.js.map +1 -0
  347. package/dist/src/types/budget.d.ts +30 -0
  348. package/dist/src/types/budget.d.ts.map +1 -0
  349. package/dist/src/types/budget.js +3 -0
  350. package/dist/src/types/budget.js.map +1 -0
  351. package/dist/src/types/config.d.ts +176 -0
  352. package/dist/src/types/config.d.ts.map +1 -0
  353. package/dist/src/types/config.js +3 -0
  354. package/dist/src/types/config.js.map +1 -0
  355. package/dist/src/types/events.d.ts +24 -0
  356. package/dist/src/types/events.d.ts.map +1 -0
  357. package/dist/src/types/events.js +3 -0
  358. package/dist/src/types/events.js.map +1 -0
  359. package/dist/src/types/index.d.ts +8 -0
  360. package/dist/src/types/index.d.ts.map +1 -0
  361. package/dist/src/types/index.js +24 -0
  362. package/dist/src/types/index.js.map +1 -0
  363. package/dist/src/types/policy.d.ts +60 -0
  364. package/dist/src/types/policy.d.ts.map +1 -0
  365. package/dist/src/types/policy.js +3 -0
  366. package/dist/src/types/policy.js.map +1 -0
  367. package/dist/src/types/stripe-config.d.ts +12 -0
  368. package/dist/src/types/stripe-config.d.ts.map +1 -0
  369. package/dist/src/types/stripe-config.js +3 -0
  370. package/dist/src/types/stripe-config.js.map +1 -0
  371. package/dist/src/types/subscription.d.ts +24 -0
  372. package/dist/src/types/subscription.d.ts.map +1 -0
  373. package/dist/src/types/subscription.js +38 -0
  374. package/dist/src/types/subscription.js.map +1 -0
  375. package/dist/src/types/tool-call.d.ts +42 -0
  376. package/dist/src/types/tool-call.d.ts.map +1 -0
  377. package/dist/src/types/tool-call.js +3 -0
  378. package/dist/src/types/tool-call.js.map +1 -0
  379. package/dist/src/types/tool-result.d.ts +58 -0
  380. package/dist/src/types/tool-result.d.ts.map +1 -0
  381. package/dist/src/types/tool-result.js +3 -0
  382. package/dist/src/types/tool-result.js.map +1 -0
  383. package/dist/src/types/user.d.ts +101 -0
  384. package/dist/src/types/user.d.ts.map +1 -0
  385. package/dist/src/types/user.js +6 -0
  386. package/dist/src/types/user.js.map +1 -0
  387. package/dist/tests/integration/api.test.d.ts +2 -0
  388. package/dist/tests/integration/api.test.d.ts.map +1 -0
  389. package/dist/tests/integration/api.test.js +1199 -0
  390. package/dist/tests/integration/api.test.js.map +1 -0
  391. package/dist/tests/integration/proxy.test.d.ts +2 -0
  392. package/dist/tests/integration/proxy.test.d.ts.map +1 -0
  393. package/dist/tests/integration/proxy.test.js +251 -0
  394. package/dist/tests/integration/proxy.test.js.map +1 -0
  395. package/dist/tests/integration/storage.test.d.ts +16 -0
  396. package/dist/tests/integration/storage.test.d.ts.map +1 -0
  397. package/dist/tests/integration/storage.test.js +826 -0
  398. package/dist/tests/integration/storage.test.js.map +1 -0
  399. package/dist/tests/unit/admin.test.d.ts +2 -0
  400. package/dist/tests/unit/admin.test.d.ts.map +1 -0
  401. package/dist/tests/unit/admin.test.js +698 -0
  402. package/dist/tests/unit/admin.test.js.map +1 -0
  403. package/dist/tests/unit/anomaly-detector.test.d.ts +2 -0
  404. package/dist/tests/unit/anomaly-detector.test.d.ts.map +1 -0
  405. package/dist/tests/unit/anomaly-detector.test.js +903 -0
  406. package/dist/tests/unit/anomaly-detector.test.js.map +1 -0
  407. package/dist/tests/unit/approval-manager.test.d.ts +2 -0
  408. package/dist/tests/unit/approval-manager.test.d.ts.map +1 -0
  409. package/dist/tests/unit/approval-manager.test.js +528 -0
  410. package/dist/tests/unit/approval-manager.test.js.map +1 -0
  411. package/dist/tests/unit/approval-webhook.test.d.ts +2 -0
  412. package/dist/tests/unit/approval-webhook.test.d.ts.map +1 -0
  413. package/dist/tests/unit/approval-webhook.test.js +355 -0
  414. package/dist/tests/unit/approval-webhook.test.js.map +1 -0
  415. package/dist/tests/unit/audit-logger.test.d.ts +2 -0
  416. package/dist/tests/unit/audit-logger.test.d.ts.map +1 -0
  417. package/dist/tests/unit/audit-logger.test.js +635 -0
  418. package/dist/tests/unit/audit-logger.test.js.map +1 -0
  419. package/dist/tests/unit/auth-routes.test.d.ts +2 -0
  420. package/dist/tests/unit/auth-routes.test.d.ts.map +1 -0
  421. package/dist/tests/unit/auth-routes.test.js +281 -0
  422. package/dist/tests/unit/auth-routes.test.js.map +1 -0
  423. package/dist/tests/unit/auth.test.d.ts +2 -0
  424. package/dist/tests/unit/auth.test.d.ts.map +1 -0
  425. package/dist/tests/unit/auth.test.js +1382 -0
  426. package/dist/tests/unit/auth.test.js.map +1 -0
  427. package/dist/tests/unit/billing.test.d.ts +2 -0
  428. package/dist/tests/unit/billing.test.d.ts.map +1 -0
  429. package/dist/tests/unit/billing.test.js +579 -0
  430. package/dist/tests/unit/billing.test.js.map +1 -0
  431. package/dist/tests/unit/budget-manager.test.d.ts +2 -0
  432. package/dist/tests/unit/budget-manager.test.d.ts.map +1 -0
  433. package/dist/tests/unit/budget-manager.test.js +778 -0
  434. package/dist/tests/unit/budget-manager.test.js.map +1 -0
  435. package/dist/tests/unit/budget-race.test.d.ts +2 -0
  436. package/dist/tests/unit/budget-race.test.d.ts.map +1 -0
  437. package/dist/tests/unit/budget-race.test.js +58 -0
  438. package/dist/tests/unit/budget-race.test.js.map +1 -0
  439. package/dist/tests/unit/cli.test.d.ts +2 -0
  440. package/dist/tests/unit/cli.test.d.ts.map +1 -0
  441. package/dist/tests/unit/cli.test.js +93 -0
  442. package/dist/tests/unit/cli.test.js.map +1 -0
  443. package/dist/tests/unit/concurrency.test.d.ts +2 -0
  444. package/dist/tests/unit/concurrency.test.d.ts.map +1 -0
  445. package/dist/tests/unit/concurrency.test.js +1270 -0
  446. package/dist/tests/unit/concurrency.test.js.map +1 -0
  447. package/dist/tests/unit/config-validate.test.d.ts +2 -0
  448. package/dist/tests/unit/config-validate.test.d.ts.map +1 -0
  449. package/dist/tests/unit/config-validate.test.js +230 -0
  450. package/dist/tests/unit/config-validate.test.js.map +1 -0
  451. package/dist/tests/unit/defaults.test.d.ts +2 -0
  452. package/dist/tests/unit/defaults.test.d.ts.map +1 -0
  453. package/dist/tests/unit/defaults.test.js +364 -0
  454. package/dist/tests/unit/defaults.test.js.map +1 -0
  455. package/dist/tests/unit/dlp-backends.test.d.ts +2 -0
  456. package/dist/tests/unit/dlp-backends.test.d.ts.map +1 -0
  457. package/dist/tests/unit/dlp-backends.test.js +563 -0
  458. package/dist/tests/unit/dlp-backends.test.js.map +1 -0
  459. package/dist/tests/unit/dlp-scanner.test.d.ts +2 -0
  460. package/dist/tests/unit/dlp-scanner.test.d.ts.map +1 -0
  461. package/dist/tests/unit/dlp-scanner.test.js +739 -0
  462. package/dist/tests/unit/dlp-scanner.test.js.map +1 -0
  463. package/dist/tests/unit/error-responses.test.d.ts +2 -0
  464. package/dist/tests/unit/error-responses.test.d.ts.map +1 -0
  465. package/dist/tests/unit/error-responses.test.js +101 -0
  466. package/dist/tests/unit/error-responses.test.js.map +1 -0
  467. package/dist/tests/unit/executor-registry.test.d.ts +2 -0
  468. package/dist/tests/unit/executor-registry.test.d.ts.map +1 -0
  469. package/dist/tests/unit/executor-registry.test.js +390 -0
  470. package/dist/tests/unit/executor-registry.test.js.map +1 -0
  471. package/dist/tests/unit/forward-proxy.test.d.ts +2 -0
  472. package/dist/tests/unit/forward-proxy.test.d.ts.map +1 -0
  473. package/dist/tests/unit/forward-proxy.test.js +621 -0
  474. package/dist/tests/unit/forward-proxy.test.js.map +1 -0
  475. package/dist/tests/unit/gateway-features.test.d.ts +2 -0
  476. package/dist/tests/unit/gateway-features.test.d.ts.map +1 -0
  477. package/dist/tests/unit/gateway-features.test.js +753 -0
  478. package/dist/tests/unit/gateway-features.test.js.map +1 -0
  479. package/dist/tests/unit/http-executor.test.d.ts +2 -0
  480. package/dist/tests/unit/http-executor.test.d.ts.map +1 -0
  481. package/dist/tests/unit/http-executor.test.js +310 -0
  482. package/dist/tests/unit/http-executor.test.js.map +1 -0
  483. package/dist/tests/unit/mcp-bridge.test.d.ts +2 -0
  484. package/dist/tests/unit/mcp-bridge.test.d.ts.map +1 -0
  485. package/dist/tests/unit/mcp-bridge.test.js +1136 -0
  486. package/dist/tests/unit/mcp-bridge.test.js.map +1 -0
  487. package/dist/tests/unit/mcp-http-transport.test.d.ts +2 -0
  488. package/dist/tests/unit/mcp-http-transport.test.d.ts.map +1 -0
  489. package/dist/tests/unit/mcp-http-transport.test.js +899 -0
  490. package/dist/tests/unit/mcp-http-transport.test.js.map +1 -0
  491. package/dist/tests/unit/mcp-oauth.test.d.ts +2 -0
  492. package/dist/tests/unit/mcp-oauth.test.d.ts.map +1 -0
  493. package/dist/tests/unit/mcp-oauth.test.js +759 -0
  494. package/dist/tests/unit/mcp-oauth.test.js.map +1 -0
  495. package/dist/tests/unit/mcp-server.test.d.ts +15 -0
  496. package/dist/tests/unit/mcp-server.test.d.ts.map +1 -0
  497. package/dist/tests/unit/mcp-server.test.js +158 -0
  498. package/dist/tests/unit/mcp-server.test.js.map +1 -0
  499. package/dist/tests/unit/metrics.test.d.ts +2 -0
  500. package/dist/tests/unit/metrics.test.d.ts.map +1 -0
  501. package/dist/tests/unit/metrics.test.js +208 -0
  502. package/dist/tests/unit/metrics.test.js.map +1 -0
  503. package/dist/tests/unit/oauth.test.d.ts +2 -0
  504. package/dist/tests/unit/oauth.test.d.ts.map +1 -0
  505. package/dist/tests/unit/oauth.test.js +281 -0
  506. package/dist/tests/unit/oauth.test.js.map +1 -0
  507. package/dist/tests/unit/opa-circuit-breaker.test.d.ts +2 -0
  508. package/dist/tests/unit/opa-circuit-breaker.test.d.ts.map +1 -0
  509. package/dist/tests/unit/opa-circuit-breaker.test.js +297 -0
  510. package/dist/tests/unit/opa-circuit-breaker.test.js.map +1 -0
  511. package/dist/tests/unit/opa-engine.test.d.ts +2 -0
  512. package/dist/tests/unit/opa-engine.test.d.ts.map +1 -0
  513. package/dist/tests/unit/opa-engine.test.js +1813 -0
  514. package/dist/tests/unit/opa-engine.test.js.map +1 -0
  515. package/dist/tests/unit/pipeline-timing.test.d.ts +2 -0
  516. package/dist/tests/unit/pipeline-timing.test.d.ts.map +1 -0
  517. package/dist/tests/unit/pipeline-timing.test.js +528 -0
  518. package/dist/tests/unit/pipeline-timing.test.js.map +1 -0
  519. package/dist/tests/unit/policy-engine.test.d.ts +2 -0
  520. package/dist/tests/unit/policy-engine.test.d.ts.map +1 -0
  521. package/dist/tests/unit/policy-engine.test.js +1345 -0
  522. package/dist/tests/unit/policy-engine.test.js.map +1 -0
  523. package/dist/tests/unit/policy-store.test.d.ts +2 -0
  524. package/dist/tests/unit/policy-store.test.d.ts.map +1 -0
  525. package/dist/tests/unit/policy-store.test.js +60 -0
  526. package/dist/tests/unit/policy-store.test.js.map +1 -0
  527. package/dist/tests/unit/postgres-storage.test.d.ts +2 -0
  528. package/dist/tests/unit/postgres-storage.test.d.ts.map +1 -0
  529. package/dist/tests/unit/postgres-storage.test.js +614 -0
  530. package/dist/tests/unit/postgres-storage.test.js.map +1 -0
  531. package/dist/tests/unit/prompt-injection-backend.test.d.ts +2 -0
  532. package/dist/tests/unit/prompt-injection-backend.test.d.ts.map +1 -0
  533. package/dist/tests/unit/prompt-injection-backend.test.js +621 -0
  534. package/dist/tests/unit/prompt-injection-backend.test.js.map +1 -0
  535. package/dist/tests/unit/proxy-hardening.test.d.ts +2 -0
  536. package/dist/tests/unit/proxy-hardening.test.d.ts.map +1 -0
  537. package/dist/tests/unit/proxy-hardening.test.js +166 -0
  538. package/dist/tests/unit/proxy-hardening.test.js.map +1 -0
  539. package/dist/tests/unit/rate-limiter.test.d.ts +2 -0
  540. package/dist/tests/unit/rate-limiter.test.d.ts.map +1 -0
  541. package/dist/tests/unit/rate-limiter.test.js +443 -0
  542. package/dist/tests/unit/rate-limiter.test.js.map +1 -0
  543. package/dist/tests/unit/redis-storage.test.d.ts +2 -0
  544. package/dist/tests/unit/redis-storage.test.d.ts.map +1 -0
  545. package/dist/tests/unit/redis-storage.test.js +766 -0
  546. package/dist/tests/unit/redis-storage.test.js.map +1 -0
  547. package/dist/tests/unit/replay-engine.test.d.ts +2 -0
  548. package/dist/tests/unit/replay-engine.test.d.ts.map +1 -0
  549. package/dist/tests/unit/replay-engine.test.js +371 -0
  550. package/dist/tests/unit/replay-engine.test.js.map +1 -0
  551. package/dist/tests/unit/saas-routes.test.d.ts +2 -0
  552. package/dist/tests/unit/saas-routes.test.d.ts.map +1 -0
  553. package/dist/tests/unit/saas-routes.test.js +1399 -0
  554. package/dist/tests/unit/saas-routes.test.js.map +1 -0
  555. package/dist/tests/unit/session.test.d.ts +2 -0
  556. package/dist/tests/unit/session.test.d.ts.map +1 -0
  557. package/dist/tests/unit/session.test.js +532 -0
  558. package/dist/tests/unit/session.test.js.map +1 -0
  559. package/dist/tests/unit/slack-executor.test.d.ts +2 -0
  560. package/dist/tests/unit/slack-executor.test.d.ts.map +1 -0
  561. package/dist/tests/unit/slack-executor.test.js +209 -0
  562. package/dist/tests/unit/slack-executor.test.js.map +1 -0
  563. package/dist/tests/unit/storage-hardening.test.d.ts +2 -0
  564. package/dist/tests/unit/storage-hardening.test.d.ts.map +1 -0
  565. package/dist/tests/unit/storage-hardening.test.js +165 -0
  566. package/dist/tests/unit/storage-hardening.test.js.map +1 -0
  567. package/dist/tests/unit/storage.test.d.ts +2 -0
  568. package/dist/tests/unit/storage.test.d.ts.map +1 -0
  569. package/dist/tests/unit/storage.test.js +698 -0
  570. package/dist/tests/unit/storage.test.js.map +1 -0
  571. package/dist/tests/unit/text-normalizer.test.d.ts +2 -0
  572. package/dist/tests/unit/text-normalizer.test.d.ts.map +1 -0
  573. package/dist/tests/unit/text-normalizer.test.js +229 -0
  574. package/dist/tests/unit/text-normalizer.test.js.map +1 -0
  575. package/dist/tests/unit/tracing.test.d.ts +2 -0
  576. package/dist/tests/unit/tracing.test.d.ts.map +1 -0
  577. package/dist/tests/unit/tracing.test.js +611 -0
  578. package/dist/tests/unit/tracing.test.js.map +1 -0
  579. package/dist/tests/unit/trust-calculator.test.d.ts +2 -0
  580. package/dist/tests/unit/trust-calculator.test.d.ts.map +1 -0
  581. package/dist/tests/unit/trust-calculator.test.js +497 -0
  582. package/dist/tests/unit/trust-calculator.test.js.map +1 -0
  583. package/dist/tests/unit/ts-sdk.test.d.ts +2 -0
  584. package/dist/tests/unit/ts-sdk.test.d.ts.map +1 -0
  585. package/dist/tests/unit/ts-sdk.test.js +421 -0
  586. package/dist/tests/unit/ts-sdk.test.js.map +1 -0
  587. package/dist/tests/unit/usage-extractor-llm.test.d.ts +2 -0
  588. package/dist/tests/unit/usage-extractor-llm.test.d.ts.map +1 -0
  589. package/dist/tests/unit/usage-extractor-llm.test.js +139 -0
  590. package/dist/tests/unit/usage-extractor-llm.test.js.map +1 -0
  591. package/dist/tests/unit/usage-extractor.test.d.ts +2 -0
  592. package/dist/tests/unit/usage-extractor.test.d.ts.map +1 -0
  593. package/dist/tests/unit/usage-extractor.test.js +271 -0
  594. package/dist/tests/unit/usage-extractor.test.js.map +1 -0
  595. package/dist/tests/unit/user-stores.test.d.ts +2 -0
  596. package/dist/tests/unit/user-stores.test.d.ts.map +1 -0
  597. package/dist/tests/unit/user-stores.test.js +687 -0
  598. package/dist/tests/unit/user-stores.test.js.map +1 -0
  599. package/dist/tests/unit/validate.test.d.ts +2 -0
  600. package/dist/tests/unit/validate.test.d.ts.map +1 -0
  601. package/dist/tests/unit/validate.test.js +545 -0
  602. package/dist/tests/unit/validate.test.js.map +1 -0
  603. package/package.json +86 -0
  604. package/policy-packs/README.md +42 -0
  605. package/policy-packs/default.yaml +46 -0
  606. package/policy-packs/dev_fast.yaml +54 -0
  607. package/policy-packs/prod_strict.yaml +83 -0
@@ -0,0 +1,964 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Gateway = void 0;
37
+ const crypto = __importStar(require("crypto"));
38
+ const api_1 = require("@opentelemetry/api");
39
+ const engine_1 = require("../policy/engine");
40
+ const opa_engine_1 = require("../policy/opa-engine");
41
+ const scanner_1 = require("../dlp/scanner");
42
+ const composite_scanner_1 = require("../dlp/composite-scanner");
43
+ const prompt_injection_backend_1 = require("../dlp/prompt-injection-backend");
44
+ const trufflehog_backend_1 = require("../dlp/trufflehog-backend");
45
+ const manager_1 = require("../budget/manager");
46
+ const usage_extractor_1 = require("../budget/usage-extractor");
47
+ const logger_1 = require("../audit/logger");
48
+ const http_executor_1 = require("../executor/http-executor");
49
+ const registry_1 = require("../executor/registry");
50
+ const manager_2 = require("../approval/manager");
51
+ const limiter_1 = require("../ratelimit/limiter");
52
+ const memory_1 = require("../storage/memory");
53
+ const anomaly_1 = require("../anomaly");
54
+ const logger_2 = require("./logger");
55
+ /** Compute a short body hash for idempotency cache keys (A2). */
56
+ function computeBodyHash(toolCall) {
57
+ const payload = toolCall.tool.name + JSON.stringify(toolCall.args);
58
+ return crypto.createHash('sha256').update(payload).digest('hex').substring(0, 16);
59
+ }
60
+ const DEFAULT_RATE_LIMIT = {
61
+ enabled: false,
62
+ actor_max_per_window: 100,
63
+ workspace_max_per_window: 500,
64
+ window_ms: 60000,
65
+ };
66
+ // ---------------------------------------------------------------------------
67
+ // Span helpers — no-op when otel is undefined
68
+ // ---------------------------------------------------------------------------
69
+ function childSpan(otel, name, fn) {
70
+ if (!otel)
71
+ return fn();
72
+ return otel.startActiveSpan(name, (s) => {
73
+ try {
74
+ const result = fn();
75
+ s.end();
76
+ return result;
77
+ }
78
+ catch (e) {
79
+ s.setStatus({ code: api_1.SpanStatusCode.ERROR, message: String(e) });
80
+ s.recordException(e);
81
+ s.end();
82
+ throw e;
83
+ }
84
+ });
85
+ }
86
+ async function asyncChildSpan(otel, name, fn) {
87
+ if (!otel)
88
+ return fn();
89
+ return otel.startActiveSpan(name, async (s) => {
90
+ try {
91
+ const result = await fn();
92
+ s.end();
93
+ return result;
94
+ }
95
+ catch (e) {
96
+ s.setStatus({ code: api_1.SpanStatusCode.ERROR, message: String(e) });
97
+ s.recordException(e);
98
+ s.end();
99
+ throw e;
100
+ }
101
+ });
102
+ }
103
+ class Gateway {
104
+ constructor(config, metrics, tracer) {
105
+ /**
106
+ * Tracks tool_call_ids currently being processed to prevent TOCTOU races.
107
+ * Maps cacheKey -> InFlightEntry so duplicate arrivals can await
108
+ * the in-flight result instead of executing a second time.
109
+ */
110
+ this.inFlightCalls = new Map();
111
+ this._shuttingDown = false;
112
+ this.config = config;
113
+ this.metrics = metrics;
114
+ this.tracer = tracer;
115
+ this.policyEngine = new engine_1.PolicyEngine(config.policy.pack_path, config.policy.default_effect);
116
+ // Build DLP pipeline: use CompositeDLPScanner when additional backends are configured
117
+ const dlpBackends = [];
118
+ if (config.dlp.prompt_injection_detection !== false) {
119
+ dlpBackends.push(new prompt_injection_backend_1.PromptInjectionBackend({
120
+ scan_output: config.dlp.scan_output,
121
+ }));
122
+ }
123
+ if (config.dlp.trufflehog?.enabled) {
124
+ dlpBackends.push(new trufflehog_backend_1.TruffleHogBackend({
125
+ binaryPath: config.dlp.trufflehog.binary_path,
126
+ timeout: config.dlp.trufflehog.timeout_ms,
127
+ }));
128
+ }
129
+ if (dlpBackends.length > 0) {
130
+ this.dlpScanner = new composite_scanner_1.CompositeDLPScanner(config.dlp, dlpBackends);
131
+ }
132
+ else {
133
+ this.dlpScanner = new scanner_1.DLPScanner(config.dlp);
134
+ }
135
+ this.budgetManager = new manager_1.BudgetManager(config.budget);
136
+ this.auditLogger = new logger_1.AuditLogger(config.audit);
137
+ this.approvalManager = new manager_2.ApprovalManager(config.approval);
138
+ this.rateLimiter = new limiter_1.RateLimiter(config.rate_limit || DEFAULT_RATE_LIMIT);
139
+ this.idempotencyStore = new memory_1.InMemoryIdempotencyStore();
140
+ // Set up anomaly detector if enabled
141
+ if (config.anomaly?.enabled) {
142
+ this.anomalyDetector = new anomaly_1.AnomalyDetector(config.anomaly);
143
+ }
144
+ // Set up OPA engine if enabled
145
+ if (config.policy.opa?.enabled) {
146
+ this.opaEngine = new opa_engine_1.OPAEngine(config.policy.opa);
147
+ }
148
+ this.usageExtractor = new usage_extractor_1.UsageExtractor(config.budget.token_pricing);
149
+ // Set up executor registry with HTTP as default + catch-all fallback
150
+ this.executorRegistry = new registry_1.ExecutorRegistry();
151
+ this.httpExecutor = new http_executor_1.HttpExecutor(config.executor);
152
+ this.executorRegistry.register('http.*', this.httpExecutor);
153
+ this.executorRegistry.register('*', this.httpExecutor); // fallback
154
+ // A1: Periodic cleanup of stale inFlightCalls entries (every 60s, remove entries older than 5min)
155
+ this.inFlightCleanupInterval = setInterval(() => {
156
+ const now = Date.now();
157
+ const MAX_AGE_MS = 5 * 60 * 1000;
158
+ for (const [key, entry] of this.inFlightCalls) {
159
+ if (now - entry.createdAt > MAX_AGE_MS) {
160
+ clearTimeout(entry.timeout);
161
+ this.inFlightCalls.delete(key);
162
+ }
163
+ }
164
+ }, 60000);
165
+ // Don't let the interval prevent process exit
166
+ if (this.inFlightCleanupInterval.unref) {
167
+ this.inFlightCleanupInterval.unref();
168
+ }
169
+ }
170
+ /** Register a custom executor for a tool name pattern (prepends to take priority over catch-all) */
171
+ registerExecutor(pattern, executor) {
172
+ this.executorRegistry.register(pattern, executor, true);
173
+ }
174
+ /**
175
+ * Run the pre-execution pipeline: rate limit, anomaly, policy, DLP args, budget.
176
+ * Does NOT handle idempotency or in-flight tracking — the caller manages those.
177
+ * Returns { allowed: true, ... } with pipeline state on success, or
178
+ * { allowed: false, result } with a ToolResult to return to the client.
179
+ */
180
+ async preExecute(toolCall, otel, requestingApiKeyId) {
181
+ const startTime = Date.now();
182
+ const stepTimings = {};
183
+ let stepStart;
184
+ // A3: Capture a local reference to the policy engine so the request uses
185
+ // a consistent snapshot even if reloadPolicy() swaps the engine mid-request.
186
+ const policyEngine = this.policyEngine;
187
+ if (!toolCall.timestamp) {
188
+ toolCall.timestamp = new Date().toISOString();
189
+ }
190
+ logger_2.log.pipelineStart(toolCall.tool_call_id, toolCall.tool.name);
191
+ // Log receipt
192
+ this.auditLogger.logToolCallReceived(toolCall);
193
+ // Rate limit check (with optional per-workspace overrides)
194
+ stepStart = Date.now();
195
+ const wsRateLimitOverrides = toolCall.workspace_id
196
+ ? this.getWorkspaceRateLimitConfig(toolCall.workspace_id)
197
+ : undefined;
198
+ const rateLimitResult = childSpan(otel, 'gateway.rate_limit', () => {
199
+ return this.rateLimiter.check(toolCall, wsRateLimitOverrides);
200
+ });
201
+ stepTimings.rate_limit = Date.now() - stepStart;
202
+ if (!rateLimitResult.allowed) {
203
+ logger_2.log.rateLimit(false, rateLimitResult.current, rateLimitResult.limit, rateLimitResult.blocked_by);
204
+ this.metrics?.recordRateLimitBlock(rateLimitResult.blocked_by || 'unknown');
205
+ const durationSec = (Date.now() - startTime) / 1000;
206
+ this.metrics?.recordRequest('blocked', toolCall.tool.name, toolCall.tool.capability, durationSec);
207
+ const result = this.buildResult(toolCall, 'blocked', {
208
+ decision: 'deny',
209
+ rule_id: 'rate_limit',
210
+ rule_name: 'Rate limit',
211
+ reasons: [`Rate limit exceeded (${rateLimitResult.blocked_by}): ${rateLimitResult.current}/${rateLimitResult.limit} requests in window`],
212
+ }, startTime, undefined, `Rate limit exceeded by ${rateLimitResult.blocked_by}: ${rateLimitResult.current}/${rateLimitResult.limit} requests. Resets at ${rateLimitResult.reset_at}`);
213
+ logger_2.log.pipelineEnd('blocked', Date.now() - startTime);
214
+ return { allowed: false, result, stepTimings, startTime };
215
+ }
216
+ logger_2.log.rateLimit(true, rateLimitResult.current, rateLimitResult.limit);
217
+ // Anomaly detection
218
+ stepStart = Date.now();
219
+ const anomalyAlerts = childSpan(otel, 'gateway.anomaly_detection', () => {
220
+ return this.anomalyDetector?.analyze(toolCall) || [];
221
+ });
222
+ stepTimings.anomaly = Date.now() - stepStart;
223
+ if (anomalyAlerts.length > 0 && this.config.anomaly?.action === 'block') {
224
+ const highSeverity = anomalyAlerts.filter(a => a.severity === 'high');
225
+ if (highSeverity.length > 0) {
226
+ logger_2.log.anomaly(anomalyAlerts.length, true);
227
+ const durationSec = (Date.now() - startTime) / 1000;
228
+ this.metrics?.recordRequest('blocked', toolCall.tool.name, toolCall.tool.capability, durationSec);
229
+ const result = this.buildResult(toolCall, 'blocked', {
230
+ decision: 'deny',
231
+ rule_id: 'anomaly_detection',
232
+ rule_name: 'Anomaly detection',
233
+ reasons: highSeverity.map(a => `${a.anomaly_type}: ${a.metric} z-score=${a.z_score.toFixed(2)}`),
234
+ }, startTime, undefined, 'Blocked by anomaly detection');
235
+ logger_2.log.pipelineEnd('blocked', Date.now() - startTime);
236
+ return { allowed: false, result, stepTimings, startTime };
237
+ }
238
+ }
239
+ logger_2.log.anomaly(anomalyAlerts.length, false);
240
+ // DLP scan on full toolCall (args + context + actor fields)
241
+ // Runs BEFORE policy evaluation so secrets are always detected regardless of policy outcome
242
+ stepStart = Date.now();
243
+ const argsDlp = childSpan(otel, 'gateway.dlp_scan_args', () => {
244
+ if (!this.config.dlp.scan_args) {
245
+ return { detected: [], redactions: [], severity: 'low' };
246
+ }
247
+ return this.dlpScanner.scan(toolCall, '');
248
+ });
249
+ stepTimings.dlp_args = Date.now() - stepStart;
250
+ this.auditLogger.logDLPScanned(toolCall, argsDlp.detected, argsDlp.severity, argsDlp.redactions.length, argsDlp.redactions);
251
+ logger_2.log.dlp('args', argsDlp.detected, argsDlp.severity, argsDlp.redactions.length);
252
+ if (argsDlp.detected.length > 0) {
253
+ for (const detectionType of argsDlp.detected) {
254
+ this.metrics?.recordDLPDetection(detectionType, argsDlp.severity);
255
+ }
256
+ }
257
+ if (argsDlp.severity === 'high') {
258
+ this.auditLogger.logIncident(toolCall, 'high', 'dlp_secret_detected', `High severity DLP detection in args: ${argsDlp.detected.join(', ')}`, 'Review and rotate any exposed credentials');
259
+ }
260
+ // Prompt injection blocking check (before policy, so it always runs)
261
+ const piAction = this.config.dlp.prompt_injection_action || 'log';
262
+ if (piAction === 'block' && argsDlp.detected.length > 0) {
263
+ const piDetections = argsDlp.detected.filter((d) => d.startsWith('prompt_injection_'));
264
+ if (piDetections.length > 0) {
265
+ const threshold = this.config.dlp.prompt_injection_block_threshold || 'high';
266
+ const severityRank = { low: 0, medium: 1, high: 2 };
267
+ const thresholdRank = severityRank[threshold] ?? 2;
268
+ const maxSeverityRank = severityRank[argsDlp.severity] ?? 0;
269
+ if (maxSeverityRank >= thresholdRank) {
270
+ const responseMode = this.config.dlp.prompt_injection_response || 'deny';
271
+ if (responseMode === 'require_approval') {
272
+ const { approval, token } = this.approvalManager.createApproval(toolCall, 'admin', `Prompt injection detected: ${piDetections.join(', ')}`, undefined, requestingApiKeyId);
273
+ await this.approvalManager.flush();
274
+ const durationSec = (Date.now() - startTime) / 1000;
275
+ this.metrics?.recordRequest('needs_approval', toolCall.tool.name, toolCall.tool.capability, durationSec);
276
+ const result = this.buildResult(toolCall, 'needs_approval', { decision: 'require_approval', rule_id: 'prompt_injection', rule_name: 'Prompt injection detected', reasons: piDetections }, startTime, undefined, undefined, { approval_id: approval.approval_id, token, expires_at: approval.expires_at }, argsDlp);
277
+ logger_2.log.pipelineEnd('needs_approval', Date.now() - startTime);
278
+ return { allowed: false, result, stepTimings, startTime };
279
+ }
280
+ logger_2.log.pipelineStep('🛡️', 'PROMPT_INJECTION_BLOCK', `Blocked: ${piDetections.join(', ')} (severity: ${argsDlp.severity}, threshold: ${threshold})`);
281
+ const durationSec = (Date.now() - startTime) / 1000;
282
+ this.metrics?.recordRequest('blocked', toolCall.tool.name, toolCall.tool.capability, durationSec);
283
+ const result = this.buildResult(toolCall, 'blocked', {
284
+ decision: 'deny',
285
+ rule_id: 'prompt_injection_block',
286
+ rule_name: 'Prompt injection detected',
287
+ reasons: [`Prompt injection detected: ${piDetections.join(', ')}`],
288
+ }, startTime, undefined, `Blocked by prompt injection detection: ${piDetections.join(', ')} (severity: ${argsDlp.severity})`, undefined, argsDlp);
289
+ logger_2.log.pipelineEnd('blocked', Date.now() - startTime);
290
+ return { allowed: false, result, stepTimings, startTime };
291
+ }
292
+ }
293
+ }
294
+ // Policy evaluation — DLP context is passed so DLP-conditioned rules
295
+ // compete with all other rules in a single priority-ordered pass.
296
+ stepStart = Date.now();
297
+ const dlpContext = argsDlp.detected.length > 0
298
+ ? { detected: argsDlp.detected, severity: argsDlp.severity, pattern_names: argsDlp.detected }
299
+ : undefined;
300
+ let policyResult;
301
+ let usedWorkspacePolicy = false;
302
+ if (this.policyStore && toolCall.workspace_id) {
303
+ const workspacePack = this.policyStore.getByWorkspaceId(toolCall.workspace_id);
304
+ if (workspacePack) {
305
+ policyResult = childSpan(otel, 'gateway.policy_eval_workspace', () => {
306
+ const ephemeralEngine = engine_1.PolicyEngine.fromPack(workspacePack);
307
+ return ephemeralEngine.evaluate(toolCall, dlpContext);
308
+ });
309
+ usedWorkspacePolicy = true;
310
+ }
311
+ }
312
+ if (!usedWorkspacePolicy) {
313
+ if (this.opaEngine) {
314
+ try {
315
+ policyResult = await asyncChildSpan(otel, 'gateway.policy_eval_opa', async () => {
316
+ return this.opaEngine.evaluate(toolCall);
317
+ });
318
+ if (policyResult.rule_id === 'opa_fallback') {
319
+ policyResult = childSpan(otel, 'gateway.policy_eval', () => {
320
+ return policyEngine.evaluate(toolCall, dlpContext);
321
+ });
322
+ }
323
+ }
324
+ catch (err) {
325
+ logger_2.logger.error('OPA evaluation failed, falling back to YAML engine', { component: 'gateway', error: err instanceof Error ? err.message : String(err) });
326
+ policyResult = childSpan(otel, 'gateway.policy_eval', () => {
327
+ return policyEngine.evaluate(toolCall, dlpContext);
328
+ });
329
+ }
330
+ }
331
+ else {
332
+ policyResult = childSpan(otel, 'gateway.policy_eval', () => {
333
+ return policyEngine.evaluate(toolCall, dlpContext);
334
+ });
335
+ }
336
+ }
337
+ stepTimings.policy = Date.now() - stepStart;
338
+ this.auditLogger.logPolicyDecided(toolCall, policyResult.decision, policyResult.rule_id, policyResult.reasons);
339
+ this.metrics?.recordPolicyDecision(policyResult.decision, policyResult.rule_id || 'unknown');
340
+ logger_2.log.policy(policyResult.decision, policyResult.rule_id, policyResult.reasons);
341
+ // Policy: deny
342
+ if (policyResult.decision === 'deny') {
343
+ const durationSec = (Date.now() - startTime) / 1000;
344
+ this.metrics?.recordRequest('blocked', toolCall.tool.name, toolCall.tool.capability, durationSec);
345
+ const ruleInfo = policyResult.rule_id ? ` [rule: ${policyResult.rule_id}]` : '';
346
+ const result = this.buildResult(toolCall, 'blocked', policyResult, startTime, undefined, `Blocked by policy${ruleInfo}: ${policyResult.reasons.join(', ')}`, undefined, argsDlp);
347
+ logger_2.log.pipelineEnd('blocked', Date.now() - startTime);
348
+ return { allowed: false, result, policyResult, argsDlp, stepTimings, startTime };
349
+ }
350
+ // Policy: require_approval (DLP report is now always included)
351
+ if (policyResult.decision === 'require_approval') {
352
+ const existingApproval = this.approvalManager.findApprovedForTask(toolCall.task_id, toolCall.actor.id, toolCall.tool.name, toolCall.tool.capability);
353
+ if (existingApproval) {
354
+ logger_2.log.pipelineStep('✅', 'APPROVAL_BYPASS', `Reusing approval ${existingApproval.approval_id} for task ${toolCall.task_id}`);
355
+ policyResult = { ...policyResult, decision: 'allow', rule_id: `approved:${existingApproval.approval_id}` };
356
+ // Fall through to budget/execute below
357
+ }
358
+ else {
359
+ const { approval, token } = this.approvalManager.createApproval(toolCall, policyResult.approval?.scope || 'admin', policyResult.approval?.reason || policyResult.reasons.join(', '), policyResult.approval?.ttl_seconds, requestingApiKeyId);
360
+ await this.approvalManager.flush();
361
+ this.auditLogger.logApprovalRequested(toolCall, approval.scope, approval.reason, this.config.approval.default_ttl_seconds);
362
+ const durationSec = (Date.now() - startTime) / 1000;
363
+ this.metrics?.recordRequest('needs_approval', toolCall.tool.name, toolCall.tool.capability, durationSec);
364
+ this.metrics?.setActiveApprovals(this.approvalManager.getPendingApprovals().length);
365
+ const result = this.buildResult(toolCall, 'needs_approval', policyResult, startTime, undefined, undefined, { approval_id: approval.approval_id, token, expires_at: approval.expires_at }, argsDlp);
366
+ return { allowed: false, result, policyResult, argsDlp, stepTimings, startTime };
367
+ }
368
+ }
369
+ // Apply transformations
370
+ let processedToolCall = toolCall;
371
+ if (policyResult.decision === 'transform' && policyResult.transformations) {
372
+ logger_2.log.transform(policyResult.transformations);
373
+ processedToolCall = this.applyTransformations(toolCall, policyResult.transformations);
374
+ }
375
+ // Budget check + atomic reservation (S5) with optional per-workspace overrides
376
+ stepStart = Date.now();
377
+ const wsBudgetOverrides = processedToolCall.workspace_id
378
+ ? this.getWorkspaceBudgetConfig(processedToolCall.workspace_id)
379
+ : undefined;
380
+ const budgetCheck = await asyncChildSpan(otel, 'gateway.budget_check', () => {
381
+ return this.budgetManager.reserveAndCheck(processedToolCall, wsBudgetOverrides);
382
+ });
383
+ stepTimings.budget = Date.now() - stepStart;
384
+ this.auditLogger.logBudgetChecked(toolCall, budgetCheck.report.estimated_cost_usd, budgetCheck.report.spent_cost_usd_task, budgetCheck.report.remaining_cost_usd_task);
385
+ if (!budgetCheck.allowed) {
386
+ logger_2.log.budget(false, budgetCheck.report.estimated_cost_usd, budgetCheck.report.spent_cost_usd_task, budgetCheck.report.remaining_cost_usd_task, budgetCheck.reason);
387
+ this.metrics?.recordBudgetBlock(this.classifyBudgetReason(budgetCheck.reason || ''));
388
+ const durationSec = (Date.now() - startTime) / 1000;
389
+ this.metrics?.recordRequest('blocked', toolCall.tool.name, toolCall.tool.capability, durationSec);
390
+ const spent = budgetCheck.report.spent_cost_usd_task?.toFixed(4) ?? '?';
391
+ const remaining = budgetCheck.report.remaining_cost_usd_task?.toFixed(4) ?? '?';
392
+ const estimated = budgetCheck.report.estimated_cost_usd?.toFixed(4) ?? '?';
393
+ const result = this.buildResult(toolCall, 'blocked', policyResult, startTime, undefined, `Budget exceeded: ${budgetCheck.reason} (spent: $${spent}, remaining: $${remaining}, estimated: $${estimated})`, undefined, argsDlp, budgetCheck.report);
394
+ logger_2.log.pipelineEnd('blocked', Date.now() - startTime);
395
+ return { allowed: false, result, policyResult, processedToolCall, argsDlp, budgetCheck, stepTimings, startTime };
396
+ }
397
+ logger_2.log.budget(true, budgetCheck.report.estimated_cost_usd, budgetCheck.report.spent_cost_usd_task, budgetCheck.report.remaining_cost_usd_task);
398
+ return { allowed: true, policyResult, processedToolCall, argsDlp, budgetCheck, reservationKey: budgetCheck.reservationKey, stepTimings, startTime };
399
+ }
400
+ /**
401
+ * Run the post-execution pipeline: DLP scan output, usage extraction, budget recording,
402
+ * metrics, audit log, and build the final ToolResult.
403
+ * Does NOT cache for idempotency — the caller handles that.
404
+ */
405
+ async postExecute(toolCall, output, pre, otel) {
406
+ const { policyResult, processedToolCall, argsDlp, budgetCheck, reservationKey, stepTimings, startTime } = pre;
407
+ const executionDuration = Date.now() - startTime;
408
+ logger_2.log.executed(output.http_status || 200, executionDuration);
409
+ this.auditLogger.logToolExecuted(toolCall, 'ok', executionDuration, output.http_status);
410
+ // Record result for anomaly detection
411
+ this.anomalyDetector?.recordResult(toolCall, executionDuration, false);
412
+ this.anomalyDetector?.analyzeResult(toolCall, executionDuration, false);
413
+ // DLP scan on output
414
+ let stepStart = Date.now();
415
+ let outputDlp = { detected: [], redactions: [], severity: 'low' };
416
+ if (this.config.dlp.scan_output && output.body) {
417
+ outputDlp = childSpan(otel, 'gateway.dlp_scan_output', () => {
418
+ return this.dlpScanner.scan(output, 'output');
419
+ });
420
+ logger_2.log.dlp('output', outputDlp.detected, outputDlp.severity, outputDlp.redactions.length);
421
+ if (outputDlp.detected.length > 0) {
422
+ this.auditLogger.logDLPScanned(toolCall, outputDlp.detected, outputDlp.severity, outputDlp.redactions.length, outputDlp.redactions);
423
+ for (const detectionType of outputDlp.detected) {
424
+ this.metrics?.recordDLPDetection(detectionType, outputDlp.severity);
425
+ }
426
+ }
427
+ }
428
+ else if (!this.config.dlp.scan_output && output.body) {
429
+ // Warn when DLP output scanning is disabled for sensitive operations
430
+ const capability = toolCall.tool.capability;
431
+ if (capability === 'write' || capability === 'delete' || capability === 'admin') {
432
+ logger_2.logger.warn('DLP output scanning is disabled for sensitive operation', {
433
+ component: 'gateway',
434
+ tool: toolCall.tool.name,
435
+ capability,
436
+ hint: 'Enable dlp.scan_output for production use',
437
+ });
438
+ }
439
+ }
440
+ stepTimings.dlp_out = Date.now() - stepStart;
441
+ // Extract usage data from response
442
+ const headerUsage = this.usageExtractor.extractFromHeaders(output.headers);
443
+ const bodyUsage = this.usageExtractor.extractFromBody(output.body);
444
+ const mergedUsage = this.usageExtractor.merge(headerUsage, bodyUsage);
445
+ // Compute actual cost from usage if available
446
+ let actualCostUsd;
447
+ if (mergedUsage) {
448
+ if (mergedUsage.provider_cost_usd !== undefined) {
449
+ actualCostUsd = mergedUsage.provider_cost_usd;
450
+ }
451
+ else {
452
+ const effectiveToolCall = processedToolCall || toolCall;
453
+ const model = typeof effectiveToolCall.args.model === 'string' ? effectiveToolCall.args.model : undefined;
454
+ const computedCost = this.usageExtractor.computeCost(mergedUsage, model);
455
+ if (computedCost !== undefined) {
456
+ actualCostUsd = computedCost;
457
+ mergedUsage.computed_cost_usd = computedCost;
458
+ }
459
+ }
460
+ }
461
+ // Record cost (skip budget recording when budgetCheck is missing, e.g. passthrough)
462
+ const estimatedCost = budgetCheck?.report?.estimated_cost_usd ?? 0;
463
+ const recordedCost = actualCostUsd ?? estimatedCost;
464
+ const effectiveToolCall = processedToolCall || toolCall;
465
+ if (budgetCheck) {
466
+ // Commit the reservation with actual cost (releases difference between estimate and actual)
467
+ if (reservationKey) {
468
+ this.budgetManager.commitReservation(reservationKey, recordedCost);
469
+ }
470
+ // Record step count and cost record metadata.
471
+ // Skip cost increment when reservation already accounts for the cost.
472
+ const costRecord = {
473
+ estimated_cost_usd: estimatedCost,
474
+ actual_cost_usd: actualCostUsd,
475
+ usage: mergedUsage,
476
+ };
477
+ this.budgetManager.record(effectiveToolCall, costRecord, !!reservationKey);
478
+ await this.budgetManager.flush();
479
+ }
480
+ // Extract model info for LLM monitoring
481
+ const model = this.usageExtractor.extractModelFromBody(output.body)
482
+ || (typeof effectiveToolCall.args.model === 'string' ? effectiveToolCall.args.model : undefined);
483
+ if (model && mergedUsage) {
484
+ const provider = this.usageExtractor.detectProvider(model);
485
+ mergedUsage.model = model;
486
+ mergedUsage.provider = provider;
487
+ // Record LLM-specific metrics
488
+ if (this.metrics) {
489
+ this.metrics.recordLLMUsage({
490
+ model,
491
+ provider,
492
+ inputTokens: mergedUsage.input_tokens,
493
+ outputTokens: mergedUsage.output_tokens,
494
+ costUsd: actualCostUsd,
495
+ durationSeconds: (Date.now() - startTime) / 1000,
496
+ status: 'ok',
497
+ });
498
+ }
499
+ }
500
+ // Record cost and token usage metrics
501
+ if (this.metrics) {
502
+ this.metrics.recordCost(recordedCost, mergedUsage?.provider_cost_usd !== undefined ? 'provider' : actualCostUsd !== undefined ? 'computed' : 'estimated', toolCall.tool.name);
503
+ if (mergedUsage) {
504
+ if (mergedUsage.input_tokens !== undefined) {
505
+ this.metrics.recordTokenUsage('input', toolCall.tool.name, mergedUsage.input_tokens);
506
+ }
507
+ if (mergedUsage.output_tokens !== undefined) {
508
+ this.metrics.recordTokenUsage('output', toolCall.tool.name, mergedUsage.output_tokens);
509
+ }
510
+ }
511
+ }
512
+ // Merge DLP reports
513
+ const argsDlpSafe = argsDlp || { detected: [], redactions: [], severity: 'low' };
514
+ const mergedDlp = {
515
+ detected: [...new Set([...argsDlpSafe.detected, ...outputDlp.detected])],
516
+ redactions: [...argsDlpSafe.redactions, ...outputDlp.redactions],
517
+ severity: this.maxSeverity(argsDlpSafe.severity, outputDlp.severity),
518
+ };
519
+ // Build final result
520
+ const budgetReportWithActual = budgetCheck
521
+ ? this.budgetManager.getReportWithActual(effectiveToolCall, estimatedCost, actualCostUsd, mergedUsage)
522
+ : { estimated_cost_usd: 0, spent_cost_usd_task: 0, remaining_cost_usd_task: 0 };
523
+ const defaultPolicy = policyResult || { decision: 'allow', rule_id: 'passthrough', rule_name: 'Passthrough', reasons: [] };
524
+ const result = this.buildResult(toolCall, 'ok', defaultPolicy, startTime, output, undefined, undefined, mergedDlp, budgetReportWithActual);
525
+ const auditMeta = { step_timings: stepTimings };
526
+ if (model) {
527
+ auditMeta.model = model;
528
+ auditMeta.provider = mergedUsage?.provider;
529
+ auditMeta.input_tokens = mergedUsage?.input_tokens;
530
+ auditMeta.output_tokens = mergedUsage?.output_tokens;
531
+ auditMeta.cost_usd = actualCostUsd;
532
+ }
533
+ this.auditLogger.logToolResultReturned(toolCall, 'ok', Date.now() - startTime, auditMeta);
534
+ // Record successful request metrics
535
+ const durationSec = (Date.now() - startTime) / 1000;
536
+ this.metrics?.recordRequest('ok', toolCall.tool.name, toolCall.tool.capability, durationSec);
537
+ logger_2.log.pipelineEnd('ok', Date.now() - startTime);
538
+ return result;
539
+ }
540
+ // Main execution pipeline - implements the full runtime path
541
+ async execute(toolCall, requestingApiKeyId) {
542
+ const otel = this.tracer?.getTracer();
543
+ // If no tracer or not enabled, run without spans
544
+ if (!otel) {
545
+ return this._executeInternal(toolCall, undefined, requestingApiKeyId);
546
+ }
547
+ return otel.startActiveSpan('gateway.execute', {
548
+ kind: api_1.SpanKind.SERVER,
549
+ attributes: {
550
+ 'palaryn.tool_call_id': toolCall.tool_call_id,
551
+ 'palaryn.task_id': toolCall.task_id,
552
+ 'palaryn.tool': toolCall.tool.name,
553
+ 'palaryn.capability': toolCall.tool.capability,
554
+ 'palaryn.actor': toolCall.actor.id,
555
+ 'palaryn.workspace': toolCall.workspace_id,
556
+ },
557
+ }, async (span) => {
558
+ try {
559
+ const result = await this._executeInternal(toolCall, otel, requestingApiKeyId);
560
+ span.setAttribute('palaryn.status', result.status);
561
+ span.setAttribute('palaryn.duration_ms', result.timing.duration_ms);
562
+ if (result.status === 'error') {
563
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: result.error });
564
+ }
565
+ else {
566
+ span.setStatus({ code: api_1.SpanStatusCode.OK });
567
+ }
568
+ return result;
569
+ }
570
+ catch (err) {
571
+ span.setStatus({ code: api_1.SpanStatusCode.ERROR, message: String(err) });
572
+ span.recordException(err);
573
+ throw err;
574
+ }
575
+ finally {
576
+ span.end();
577
+ }
578
+ });
579
+ }
580
+ // Internal pipeline with optional tracing — delegates to preExecute/postExecute
581
+ async _executeInternal(toolCall, otel, requestingApiKeyId) {
582
+ const startTime = Date.now();
583
+ // A2: Compute cache key using tool_call_id + body hash
584
+ const bodyHash = computeBodyHash(toolCall);
585
+ const cacheKey = `${toolCall.tool_call_id}:${bodyHash}`;
586
+ // Step 0: Idempotency check - return cached result for duplicate tool_call_id + body
587
+ const cached = await asyncChildSpan(otel, 'gateway.idempotency_check', async () => {
588
+ return this.idempotencyStore.get(cacheKey);
589
+ });
590
+ if (cached) {
591
+ this.metrics?.recordIdempotencyHit();
592
+ logger_2.log.idempotencyHit(toolCall.tool_call_id);
593
+ logger_2.log.pipelineEnd('ok (cached)', Date.now() - startTime);
594
+ return cached;
595
+ }
596
+ logger_2.log.idempotencyMiss();
597
+ // Step 0.5: TOCTOU guard — if another request with the same cache key is already
598
+ // in flight, await its result instead of executing a duplicate.
599
+ const existingFlight = this.inFlightCalls.get(cacheKey);
600
+ if (existingFlight) {
601
+ logger_2.log.idempotencyHit(toolCall.tool_call_id);
602
+ return existingFlight.promise;
603
+ }
604
+ // S6: Register this execution as in-flight via a deferred promise with timeout.
605
+ let resolveInFlight;
606
+ let rejectInFlight;
607
+ const flightPromise = new Promise((resolve, reject) => {
608
+ resolveInFlight = resolve;
609
+ rejectInFlight = reject;
610
+ });
611
+ const flightTimeout = setTimeout(() => {
612
+ rejectInFlight(new Error('In-flight request timed out after 60s'));
613
+ this.inFlightCalls.delete(cacheKey);
614
+ }, 60000);
615
+ this.inFlightCalls.set(cacheKey, {
616
+ promise: flightPromise,
617
+ timeout: flightTimeout,
618
+ createdAt: Date.now(),
619
+ });
620
+ const executeAndResolve = async () => {
621
+ // Run pre-execution pipeline (rate limit, anomaly, policy, DLP args, budget)
622
+ const pre = await this.preExecute(toolCall, otel, requestingApiKeyId);
623
+ if (!pre.allowed) {
624
+ // Release budget reservation if pre-execute denied after reservation
625
+ if (pre.reservationKey) {
626
+ this.budgetManager.releaseReservation(pre.reservationKey);
627
+ }
628
+ return pre.result;
629
+ }
630
+ const { processedToolCall, policyResult, argsDlp, budgetCheck, reservationKey, stepTimings } = pre;
631
+ // Step 6: Execute tool via executor registry
632
+ logger_2.log.executing(processedToolCall.tool.name, processedToolCall.args.url);
633
+ try {
634
+ let stepStart = Date.now();
635
+ const output = await asyncChildSpan(otel, 'gateway.tool_execute', () => {
636
+ return this.executorRegistry.execute(processedToolCall);
637
+ });
638
+ stepTimings.execute = Date.now() - stepStart;
639
+ // Run post-execution pipeline (DLP output, usage, budget recording, metrics, audit)
640
+ const result = await this.postExecute(toolCall, output, pre, otel);
641
+ // Cache result for idempotency (5 minute TTL) using cache key with body hash
642
+ this.idempotencyStore.set(cacheKey, result, 300000);
643
+ if (this.idempotencyStore.flush)
644
+ await this.idempotencyStore.flush();
645
+ return result;
646
+ }
647
+ catch (err) {
648
+ // S5: Release budget reservation on execution error
649
+ if (reservationKey) {
650
+ this.budgetManager.releaseReservation(reservationKey);
651
+ }
652
+ const errorMsg = err instanceof Error ? err.message : String(err);
653
+ const errorType = err instanceof Error ? err.constructor.name : 'UnknownError';
654
+ const executionDuration = Date.now() - pre.startTime;
655
+ logger_2.log.executionError(errorMsg);
656
+ this.auditLogger.logToolExecuted(toolCall, 'error', executionDuration);
657
+ this.auditLogger.logToolResultReturned(toolCall, 'error', executionDuration, { step_timings: stepTimings });
658
+ // Record result for anomaly detection (error tracking)
659
+ this.anomalyDetector?.recordResult(toolCall, executionDuration, true);
660
+ this.anomalyDetector?.analyzeResult(toolCall, executionDuration, true);
661
+ // Record error metrics
662
+ const durationSec = executionDuration / 1000;
663
+ this.metrics?.recordExecutorError(toolCall.tool.name, errorType);
664
+ this.metrics?.recordRequest('error', toolCall.tool.name, toolCall.tool.capability, durationSec);
665
+ const result = this.buildResult(toolCall, 'error', policyResult, pre.startTime, undefined, errorMsg, undefined, argsDlp, budgetCheck.report);
666
+ logger_2.log.pipelineEnd('error', Date.now() - pre.startTime);
667
+ return result;
668
+ }
669
+ };
670
+ try {
671
+ const result = await executeAndResolve();
672
+ clearTimeout(flightTimeout);
673
+ resolveInFlight(result);
674
+ return result;
675
+ }
676
+ catch (err) {
677
+ clearTimeout(flightTimeout);
678
+ // Even on unexpected errors, resolve the promise so waiters don't hang
679
+ const errorResult = this.buildResult(toolCall, 'error', {
680
+ decision: 'deny', rule_id: 'internal_error', rule_name: 'Internal error', reasons: [String(err)],
681
+ }, startTime, undefined, String(err));
682
+ resolveInFlight(errorResult);
683
+ throw err;
684
+ }
685
+ finally {
686
+ this.inFlightCalls.delete(cacheKey);
687
+ }
688
+ }
689
+ // Process an approval token
690
+ async processApproval(token, approverId, approved, reason, approverApiKeyId) {
691
+ try {
692
+ if (approved) {
693
+ const result = await this.approvalManager.approve(token, approverId, approverApiKeyId);
694
+ if (!result.approved) {
695
+ return { success: false, error: 'Approval failed or expired' };
696
+ }
697
+ return { success: true };
698
+ }
699
+ else {
700
+ await this.approvalManager.deny(token, approverId, reason || 'Denied by approver');
701
+ return { success: true };
702
+ }
703
+ }
704
+ catch (err) {
705
+ const message = err instanceof Error ? err.message : 'Approval processing failed';
706
+ return { success: false, error: message };
707
+ }
708
+ }
709
+ // Report usage from a client (e.g. Android reporting actual LLM costs)
710
+ reportUsage(params) {
711
+ // Log audit event
712
+ this.auditLogger.log({
713
+ event_type: 'USAGE_REPORTED',
714
+ tool_call_id: params.tool_call_id,
715
+ task_id: params.task_id,
716
+ workspace_id: params.workspace_id || 'unknown',
717
+ actor_id: params.actor_id || 'unknown',
718
+ tool_name: 'client_report',
719
+ metadata: {
720
+ actual_cost_usd: params.actual_cost_usd,
721
+ usage: params.usage,
722
+ },
723
+ });
724
+ // Record metrics
725
+ if (this.metrics) {
726
+ if (params.actual_cost_usd !== undefined) {
727
+ this.metrics.recordCost(params.actual_cost_usd, 'client_reported', 'client_report');
728
+ }
729
+ if (params.usage) {
730
+ if (params.usage.input_tokens !== undefined) {
731
+ this.metrics.recordTokenUsage('input', 'client_report', params.usage.input_tokens);
732
+ }
733
+ if (params.usage.output_tokens !== undefined) {
734
+ this.metrics.recordTokenUsage('output', 'client_report', params.usage.output_tokens);
735
+ }
736
+ }
737
+ }
738
+ }
739
+ // Get trace for a task
740
+ getTaskTrace(taskId) {
741
+ return this.auditLogger.getTaskTrace(taskId);
742
+ }
743
+ // Get current policy pack
744
+ getCurrentPolicy() {
745
+ return this.policyEngine.getPack();
746
+ }
747
+ // Validate a policy pack
748
+ validatePolicy(pack) {
749
+ return engine_1.PolicyEngine.validate(pack);
750
+ }
751
+ // Get pending approvals
752
+ getPendingApprovals(workspaceId) {
753
+ return this.approvalManager.getPendingApprovals(workspaceId);
754
+ }
755
+ /** Reload the policy pack from disk. Creates a new engine and swaps atomically. */
756
+ reloadPolicy() {
757
+ try {
758
+ // A3: Create new engine, load it, then swap the reference atomically
759
+ const newEngine = new engine_1.PolicyEngine(this.config.policy.pack_path, this.config.policy.default_effect);
760
+ const pack = newEngine.getPack();
761
+ this.policyEngine = newEngine;
762
+ return { success: true, ruleCount: pack.rules.length };
763
+ }
764
+ catch (err) {
765
+ const message = err instanceof Error ? err.message : String(err);
766
+ return { success: false, ruleCount: 0, error: message };
767
+ }
768
+ }
769
+ /** Get the file path for the active policy pack */
770
+ getPolicyPackPath() {
771
+ return this.config.policy.pack_path;
772
+ }
773
+ // ---------------------------------------------------------------------------
774
+ // Store injection — replace default in-memory stores with external backends
775
+ // ---------------------------------------------------------------------------
776
+ /** Replace the idempotency store (default: in-memory) */
777
+ setIdempotencyStore(store) {
778
+ this.idempotencyStore = store;
779
+ }
780
+ /** Replace the rate limiter with one backed by an external store */
781
+ setRateLimiter(limiter) {
782
+ this.rateLimiter = limiter;
783
+ }
784
+ /** Inject all external stores at once (e.g. from Redis or Postgres) */
785
+ setStores(stores) {
786
+ if (stores.idempotencyStore) {
787
+ this.idempotencyStore = stores.idempotencyStore;
788
+ }
789
+ if (stores.rateLimitStore) {
790
+ this.rateLimiter = new limiter_1.RateLimiter(this.config.rate_limit || DEFAULT_RATE_LIMIT, stores.rateLimitStore);
791
+ }
792
+ if (stores.auditStore) {
793
+ this.auditLogger.setStore(stores.auditStore);
794
+ }
795
+ if (stores.budgetStore) {
796
+ this.budgetManager = new manager_1.BudgetManager(this.config.budget, stores.budgetStore);
797
+ }
798
+ if (stores.approvalStore) {
799
+ this.approvalManager = new manager_2.ApprovalManager(this.config.approval, stores.approvalStore);
800
+ }
801
+ if (stores.policyStore) {
802
+ this.policyStore = stores.policyStore;
803
+ }
804
+ if (stores.rateLimitConfigStore) {
805
+ this.rateLimitConfigStore = stores.rateLimitConfigStore;
806
+ }
807
+ if (stores.budgetConfigStore) {
808
+ this.budgetConfigStore = stores.budgetConfigStore;
809
+ }
810
+ }
811
+ // Get components for testing / introspection
812
+ getAuditLogger() { return this.auditLogger; }
813
+ getBudgetManager() { return this.budgetManager; }
814
+ getPolicyEngine() { return this.policyEngine; }
815
+ getDLPScanner() { return this.dlpScanner; }
816
+ getApprovalManager() { return this.approvalManager; }
817
+ getExecutorRegistry() { return this.executorRegistry; }
818
+ getHttpExecutor() { return this.httpExecutor; }
819
+ getRateLimiter() { return this.rateLimiter; }
820
+ getIdempotencyStore() { return this.idempotencyStore; }
821
+ getAnomalyDetector() { return this.anomalyDetector; }
822
+ getOPAEngine() { return this.opaEngine; }
823
+ getPolicyStore() { return this.policyStore; }
824
+ getRateLimitConfigStore() { return this.rateLimitConfigStore; }
825
+ getBudgetConfigStore() { return this.budgetConfigStore; }
826
+ /** Return the workspace-specific policy if one exists, otherwise the global policy. */
827
+ getWorkspacePolicy(workspaceId) {
828
+ if (this.policyStore) {
829
+ const custom = this.policyStore.getByWorkspaceId(workspaceId);
830
+ if (custom)
831
+ return { policy: custom, is_custom: true };
832
+ }
833
+ return { policy: this.policyEngine.getPack(), is_custom: false };
834
+ }
835
+ /** Return workspace-specific rate limit config if one exists. */
836
+ getWorkspaceRateLimitConfig(workspaceId) {
837
+ return this.rateLimitConfigStore?.getByWorkspaceId(workspaceId);
838
+ }
839
+ /** Return workspace-specific budget config if one exists. */
840
+ getWorkspaceBudgetConfig(workspaceId) {
841
+ return this.budgetConfigStore?.getByWorkspaceId(workspaceId);
842
+ }
843
+ // Helper: Build a ToolResult
844
+ buildResult(toolCall, status, policyResult, startTime, output, error, approvalInfo, dlpReport, budgetReport) {
845
+ const result = {
846
+ tool_call_id: toolCall.tool_call_id,
847
+ task_id: toolCall.task_id,
848
+ status,
849
+ policy: {
850
+ decision: policyResult.decision,
851
+ rule_id: policyResult.rule_id,
852
+ reasons: policyResult.reasons,
853
+ },
854
+ dlp: dlpReport || { detected: [], redactions: [], severity: 'low' },
855
+ budget: budgetReport || { estimated_cost_usd: 0, spent_cost_usd_task: 0, remaining_cost_usd_task: 0 },
856
+ output: output || undefined,
857
+ error: error || undefined,
858
+ timing: {
859
+ started_at: new Date(startTime).toISOString(),
860
+ duration_ms: Date.now() - startTime,
861
+ },
862
+ };
863
+ if (approvalInfo) {
864
+ result.approval = approvalInfo;
865
+ }
866
+ return result;
867
+ }
868
+ // Helper: Apply policy transformations to a ToolCall
869
+ applyTransformations(toolCall, transformations) {
870
+ const clone = JSON.parse(JSON.stringify(toolCall));
871
+ for (const t of transformations) {
872
+ switch (t.type) {
873
+ case 'strip_header':
874
+ if (clone.args.headers) {
875
+ delete clone.args.headers[t.target];
876
+ }
877
+ break;
878
+ case 'redact_field':
879
+ this.setNestedValue(clone.args, t.target, '[REDACTED]');
880
+ break;
881
+ case 'replace_value':
882
+ this.setNestedValue(clone.args, t.target, t.value || '');
883
+ break;
884
+ }
885
+ }
886
+ return clone;
887
+ }
888
+ // Helper: Set a nested value using dot notation
889
+ setNestedValue(obj, path, value) {
890
+ const parts = path.split('.');
891
+ let current = obj;
892
+ for (let i = 0; i < parts.length - 1; i++) {
893
+ if (current[parts[i]] === undefined)
894
+ return;
895
+ current = current[parts[i]];
896
+ }
897
+ current[parts[parts.length - 1]] = value;
898
+ }
899
+ // Helper: Classify budget block reason into a metric label
900
+ classifyBudgetReason(reason) {
901
+ if (reason.startsWith('Task budget'))
902
+ return 'task';
903
+ if (reason.startsWith('User daily'))
904
+ return 'user_daily';
905
+ if (reason.startsWith('User monthly'))
906
+ return 'user_monthly';
907
+ if (reason.startsWith('Workspace daily'))
908
+ return 'workspace_daily';
909
+ if (reason.startsWith('Workspace monthly'))
910
+ return 'workspace_monthly';
911
+ if (reason.startsWith('Step limit'))
912
+ return 'step_limit';
913
+ if (reason.startsWith('Wall clock'))
914
+ return 'wall_clock';
915
+ return 'unknown';
916
+ }
917
+ // Helper: Get max severity
918
+ maxSeverity(a, b) {
919
+ const order = { low: 0, medium: 1, high: 2 };
920
+ const aVal = order[a] || 0;
921
+ const bVal = order[b] || 0;
922
+ return aVal >= bVal ? a : b;
923
+ }
924
+ /** Returns true if the gateway is in the process of shutting down. */
925
+ get isShuttingDown() {
926
+ return this._shuttingDown;
927
+ }
928
+ // Shutdown — flush pending writes, close logger, clear caches, reset limiter
929
+ async shutdown() {
930
+ this._shuttingDown = true;
931
+ // Clear the in-flight cleanup interval
932
+ if (this.inFlightCleanupInterval) {
933
+ clearInterval(this.inFlightCleanupInterval);
934
+ this.inFlightCleanupInterval = undefined;
935
+ }
936
+ // Clear in-flight call timeouts
937
+ for (const [key, entry] of this.inFlightCalls) {
938
+ clearTimeout(entry.timeout);
939
+ }
940
+ this.inFlightCalls.clear();
941
+ // 1. Flush all pending async writes to storage backends
942
+ // Use Promise.all so errors propagate to the caller instead of being silently swallowed
943
+ const flushes = [];
944
+ if (this.budgetManager.flush)
945
+ flushes.push(this.budgetManager.flush());
946
+ if (this.approvalManager.flush)
947
+ flushes.push(this.approvalManager.flush());
948
+ if (this.idempotencyStore.flush)
949
+ flushes.push(this.idempotencyStore.flush());
950
+ if (this.rateLimiter.flush)
951
+ flushes.push(this.rateLimiter.flush());
952
+ if (flushes.length > 0) {
953
+ await Promise.all(flushes);
954
+ }
955
+ // 2. Flush audit logger pending writes, then close file handles
956
+ await this.auditLogger.flush();
957
+ this.auditLogger.close();
958
+ // 3. Clear caches and reset limiter
959
+ this.idempotencyStore.clear();
960
+ this.rateLimiter.reset();
961
+ }
962
+ }
963
+ exports.Gateway = Gateway;
964
+ //# sourceMappingURL=gateway.js.map