@adonis-agora/agent 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 (314) hide show
  1. package/LICENSE +21 -0
  2. package/dist/configure.d.ts +18 -0
  3. package/dist/configure.d.ts.map +1 -0
  4. package/dist/configure.js +29 -0
  5. package/dist/configure.js.map +1 -0
  6. package/dist/providers/agent_provider.d.ts +25 -0
  7. package/dist/providers/agent_provider.d.ts.map +1 -0
  8. package/dist/providers/agent_provider.js +511 -0
  9. package/dist/providers/agent_provider.js.map +1 -0
  10. package/dist/src/actor-resolver.d.ts +41 -0
  11. package/dist/src/actor-resolver.d.ts.map +1 -0
  12. package/dist/src/agent-deps-factory.d.ts +59 -0
  13. package/dist/src/agent-deps-factory.d.ts.map +1 -0
  14. package/dist/src/agent-deps-factory.js +111 -0
  15. package/dist/src/agent-deps-factory.js.map +1 -0
  16. package/dist/src/agent-deps.d.ts +42 -0
  17. package/dist/src/agent-deps.d.ts.map +1 -0
  18. package/dist/src/agent-deps.js +5 -0
  19. package/dist/src/agent-deps.js.map +1 -0
  20. package/dist/src/agent-loop.d.ts +101 -0
  21. package/dist/src/agent-loop.d.ts.map +1 -0
  22. package/dist/src/agent-loop.js +396 -0
  23. package/dist/src/agent-loop.js.map +1 -0
  24. package/dist/src/agent-registry.d.ts +10 -0
  25. package/dist/src/agent-registry.d.ts.map +1 -0
  26. package/dist/src/agent-service.d.ts +50 -0
  27. package/dist/src/agent-service.d.ts.map +1 -0
  28. package/dist/src/agent-service.js +82 -0
  29. package/dist/src/agent-service.js.map +1 -0
  30. package/dist/src/ai-sdk/ai-sdk-model.d.ts +17 -0
  31. package/dist/src/ai-sdk/ai-sdk-model.d.ts.map +1 -0
  32. package/dist/src/ai-sdk/ai-sdk-model.js +263 -0
  33. package/dist/src/ai-sdk/ai-sdk-model.js.map +1 -0
  34. package/dist/src/ai-sdk/index.d.ts +2 -0
  35. package/dist/src/ai-sdk/index.d.ts.map +1 -0
  36. package/dist/src/ai-tool-ref.d.ts +79 -0
  37. package/dist/src/ai-tool-ref.d.ts.map +1 -0
  38. package/dist/src/ai-tool-ref.js +81 -0
  39. package/dist/src/ai-tool-ref.js.map +1 -0
  40. package/dist/src/authorizer.d.ts +18 -0
  41. package/dist/src/authorizer.d.ts.map +1 -0
  42. package/dist/src/authz/authz-tool-authorizer.d.ts +85 -0
  43. package/dist/src/authz/authz-tool-authorizer.d.ts.map +1 -0
  44. package/dist/src/authz/authz-tool-authorizer.js +63 -0
  45. package/dist/src/authz/authz-tool-authorizer.js.map +1 -0
  46. package/dist/src/authz/index.d.ts +3 -0
  47. package/dist/src/authz/index.d.ts.map +1 -0
  48. package/dist/src/authz/index.js +2 -0
  49. package/dist/src/authz/index.js.map +1 -0
  50. package/dist/src/data/data-tool.d.ts +64 -0
  51. package/dist/src/data/data-tool.d.ts.map +1 -0
  52. package/dist/src/data/data-tool.js +146 -0
  53. package/dist/src/data/data-tool.js.map +1 -0
  54. package/dist/src/data/index.d.ts +7 -0
  55. package/dist/src/data/index.d.ts.map +1 -0
  56. package/dist/src/data/index.js +7 -0
  57. package/dist/src/data/index.js.map +1 -0
  58. package/dist/src/data/limit.d.ts +12 -0
  59. package/dist/src/data/limit.d.ts.map +1 -0
  60. package/dist/src/data/limit.js +29 -0
  61. package/dist/src/data/limit.js.map +1 -0
  62. package/dist/src/data/parser.d.ts +27 -0
  63. package/dist/src/data/parser.d.ts.map +1 -0
  64. package/dist/src/data/parser.js +18 -0
  65. package/dist/src/data/parser.js.map +1 -0
  66. package/dist/src/data/sql-validator.d.ts +37 -0
  67. package/dist/src/data/sql-validator.d.ts.map +1 -0
  68. package/dist/src/data/sql-validator.js +92 -0
  69. package/dist/src/data/sql-validator.js.map +1 -0
  70. package/dist/src/data/table-access.d.ts +40 -0
  71. package/dist/src/data/table-access.d.ts.map +1 -0
  72. package/dist/src/data/table-access.js +41 -0
  73. package/dist/src/data/table-access.js.map +1 -0
  74. package/dist/src/data/tenant-scope.d.ts +39 -0
  75. package/dist/src/data/tenant-scope.d.ts.map +1 -0
  76. package/dist/src/data/tenant-scope.js +126 -0
  77. package/dist/src/data/tenant-scope.js.map +1 -0
  78. package/dist/src/define_config.d.ts +153 -0
  79. package/dist/src/define_config.d.ts.map +1 -0
  80. package/dist/src/define_config.js +7 -0
  81. package/dist/src/define_config.js.map +1 -0
  82. package/dist/src/diagnostics.d.ts +82 -0
  83. package/dist/src/diagnostics.d.ts.map +1 -0
  84. package/dist/src/diagnostics.js +50 -0
  85. package/dist/src/diagnostics.js.map +1 -0
  86. package/dist/src/durable/agent-run-context.d.ts +24 -0
  87. package/dist/src/durable/agent-run-context.d.ts.map +1 -0
  88. package/dist/src/durable/agent-run-context.js +17 -0
  89. package/dist/src/durable/agent-run-context.js.map +1 -0
  90. package/dist/src/durable/agent-run-workflow.d.ts +37 -0
  91. package/dist/src/durable/agent-run-workflow.d.ts.map +1 -0
  92. package/dist/src/durable/agent-run-workflow.js +128 -0
  93. package/dist/src/durable/agent-run-workflow.js.map +1 -0
  94. package/dist/src/durable/durable-agent-runner.d.ts +25 -0
  95. package/dist/src/durable/durable-agent-runner.d.ts.map +1 -0
  96. package/dist/src/durable/durable-agent-runner.js +59 -0
  97. package/dist/src/durable/durable-agent-runner.js.map +1 -0
  98. package/dist/src/durable/index.d.ts +17 -0
  99. package/dist/src/durable/index.d.ts.map +1 -0
  100. package/dist/src/durable/index.js +20 -0
  101. package/dist/src/durable/index.js.map +1 -0
  102. package/dist/src/index.d.ts +55 -0
  103. package/dist/src/index.d.ts.map +1 -0
  104. package/dist/src/index.js +45 -0
  105. package/dist/src/index.js.map +1 -0
  106. package/dist/src/personas.d.ts +7 -0
  107. package/dist/src/personas.d.ts.map +1 -0
  108. package/dist/src/rag/chunk.d.ts +14 -0
  109. package/dist/src/rag/chunk.d.ts.map +1 -0
  110. package/dist/src/rag/chunk.js +42 -0
  111. package/dist/src/rag/chunk.js.map +1 -0
  112. package/dist/src/rag/embedding-retriever.d.ts +15 -0
  113. package/dist/src/rag/embedding-retriever.d.ts.map +1 -0
  114. package/dist/src/rag/embedding-retriever.js +24 -0
  115. package/dist/src/rag/embedding-retriever.js.map +1 -0
  116. package/dist/src/rag/filter.d.ts +6 -0
  117. package/dist/src/rag/filter.d.ts.map +1 -0
  118. package/dist/src/rag/filter.js +11 -0
  119. package/dist/src/rag/filter.js.map +1 -0
  120. package/dist/src/rag/index.d.ts +8 -0
  121. package/dist/src/rag/index.d.ts.map +1 -0
  122. package/dist/src/rag/index.js +8 -0
  123. package/dist/src/rag/index.js.map +1 -0
  124. package/dist/src/rag/ingest.d.ts +33 -0
  125. package/dist/src/rag/ingest.d.ts.map +1 -0
  126. package/dist/src/rag/ingest.js +37 -0
  127. package/dist/src/rag/ingest.js.map +1 -0
  128. package/dist/src/rag/memory-vector-store.d.ts +16 -0
  129. package/dist/src/rag/memory-vector-store.d.ts.map +1 -0
  130. package/dist/src/rag/memory-vector-store.js +70 -0
  131. package/dist/src/rag/memory-vector-store.js.map +1 -0
  132. package/dist/src/rag/pg-vector-store.d.ts +84 -0
  133. package/dist/src/rag/pg-vector-store.d.ts.map +1 -0
  134. package/dist/src/rag/pg-vector-store.js +220 -0
  135. package/dist/src/rag/pg-vector-store.js.map +1 -0
  136. package/dist/src/rag/vector-store.d.ts +47 -0
  137. package/dist/src/rag/vector-store.d.ts.map +1 -0
  138. package/dist/src/rag/vector-store.js +5 -0
  139. package/dist/src/rag/vector-store.js.map +1 -0
  140. package/dist/src/rag-media/diagnostics.d.ts +43 -0
  141. package/dist/src/rag-media/diagnostics.d.ts.map +1 -0
  142. package/dist/src/rag-media/diagnostics.js +36 -0
  143. package/dist/src/rag-media/diagnostics.js.map +1 -0
  144. package/dist/src/rag-media/index.d.ts +5 -0
  145. package/dist/src/rag-media/index.d.ts.map +1 -0
  146. package/dist/src/rag-media/index.js +5 -0
  147. package/dist/src/rag-media/index.js.map +1 -0
  148. package/dist/src/rag-media/media-events.d.ts +23 -0
  149. package/dist/src/rag-media/media-events.d.ts.map +1 -0
  150. package/dist/src/rag-media/media-events.js +28 -0
  151. package/dist/src/rag-media/media-events.js.map +1 -0
  152. package/dist/src/rag-media/media-rag-ingestion.d.ts +155 -0
  153. package/dist/src/rag-media/media-rag-ingestion.d.ts.map +1 -0
  154. package/dist/src/rag-media/media-rag-ingestion.js +204 -0
  155. package/dist/src/rag-media/media-rag-ingestion.js.map +1 -0
  156. package/dist/src/rag-media/text-extractor.d.ts +46 -0
  157. package/dist/src/rag-media/text-extractor.d.ts.map +1 -0
  158. package/dist/src/rag-media/text-extractor.js +92 -0
  159. package/dist/src/rag-media/text-extractor.js.map +1 -0
  160. package/dist/src/redis-stream-client.d.ts +29 -0
  161. package/dist/src/redis-stream-client.d.ts.map +1 -0
  162. package/dist/src/redis-stream-client.js +2 -0
  163. package/dist/src/redis-stream-client.js.map +1 -0
  164. package/dist/src/redis-token-stream-sink.d.ts +36 -0
  165. package/dist/src/redis-token-stream-sink.d.ts.map +1 -0
  166. package/dist/src/redis-token-stream-sink.js +94 -0
  167. package/dist/src/redis-token-stream-sink.js.map +1 -0
  168. package/dist/src/runners/inline-agent-runner.d.ts +26 -0
  169. package/dist/src/runners/inline-agent-runner.d.ts.map +1 -0
  170. package/dist/src/runners/inline-agent-runner.js +85 -0
  171. package/dist/src/runners/inline-agent-runner.js.map +1 -0
  172. package/dist/src/spi/actor-directory.d.ts +23 -0
  173. package/dist/src/spi/actor-directory.d.ts.map +1 -0
  174. package/dist/src/spi/actor-directory.js +2 -0
  175. package/dist/src/spi/actor-directory.js.map +1 -0
  176. package/dist/src/spi/actor-resolver.d.ts +18 -0
  177. package/dist/src/spi/actor-resolver.d.ts.map +1 -0
  178. package/dist/src/spi/agent-runner.d.ts +20 -0
  179. package/dist/src/spi/agent-runner.d.ts.map +1 -0
  180. package/dist/src/spi/agent-store.d.ts +104 -0
  181. package/dist/src/spi/agent-store.d.ts.map +1 -0
  182. package/dist/src/spi/agent-store.js +2 -0
  183. package/dist/src/spi/agent-store.js.map +1 -0
  184. package/dist/src/spi/attachment-staging.d.ts +27 -0
  185. package/dist/src/spi/attachment-staging.d.ts.map +1 -0
  186. package/dist/src/spi/attachment-staging.js +2 -0
  187. package/dist/src/spi/attachment-staging.js.map +1 -0
  188. package/dist/src/spi/embedding-provider.d.ts +12 -0
  189. package/dist/src/spi/embedding-provider.d.ts.map +1 -0
  190. package/dist/src/spi/embedding-provider.js +2 -0
  191. package/dist/src/spi/embedding-provider.js.map +1 -0
  192. package/dist/src/spi/governance-queries.d.ts +215 -0
  193. package/dist/src/spi/governance-queries.d.ts.map +1 -0
  194. package/dist/src/spi/governance-queries.js +15 -0
  195. package/dist/src/spi/governance-queries.js.map +1 -0
  196. package/dist/src/spi/model-provider.d.ts +41 -0
  197. package/dist/src/spi/model-provider.d.ts.map +1 -0
  198. package/dist/src/spi/pricing-store.d.ts +45 -0
  199. package/dist/src/spi/pricing-store.d.ts.map +1 -0
  200. package/dist/src/spi/pricing-store.js +24 -0
  201. package/dist/src/spi/pricing-store.js.map +1 -0
  202. package/dist/src/spi/quota-store.d.ts +7 -0
  203. package/dist/src/spi/quota-store.d.ts.map +1 -0
  204. package/dist/src/spi/reranker.d.ts +17 -0
  205. package/dist/src/spi/reranker.d.ts.map +1 -0
  206. package/dist/src/spi/reranker.js +2 -0
  207. package/dist/src/spi/reranker.js.map +1 -0
  208. package/dist/src/spi/retriever.d.ts +28 -0
  209. package/dist/src/spi/retriever.d.ts.map +1 -0
  210. package/dist/src/spi/retriever.js +11 -0
  211. package/dist/src/spi/retriever.js.map +1 -0
  212. package/dist/src/spi/roles-policy.d.ts +11 -0
  213. package/dist/src/spi/roles-policy.d.ts.map +1 -0
  214. package/dist/src/spi/tool.d.ts +21 -0
  215. package/dist/src/spi/tool.d.ts.map +1 -0
  216. package/dist/src/stores/factory.d.ts +363 -0
  217. package/dist/src/stores/factory.d.ts.map +1 -0
  218. package/dist/src/stores/factory.js +341 -0
  219. package/dist/src/stores/factory.js.map +1 -0
  220. package/dist/src/stores/ledger-quota.d.ts +23 -0
  221. package/dist/src/stores/ledger-quota.d.ts.map +1 -0
  222. package/dist/src/stores/ledger-quota.js +32 -0
  223. package/dist/src/stores/ledger-quota.js.map +1 -0
  224. package/dist/src/stores/lucid-governance-queries.d.ts +73 -0
  225. package/dist/src/stores/lucid-governance-queries.d.ts.map +1 -0
  226. package/dist/src/stores/lucid-governance-queries.js +510 -0
  227. package/dist/src/stores/lucid-governance-queries.js.map +1 -0
  228. package/dist/src/stores/lucid-pricing.d.ts +20 -0
  229. package/dist/src/stores/lucid-pricing.d.ts.map +1 -0
  230. package/dist/src/stores/lucid-pricing.js +67 -0
  231. package/dist/src/stores/lucid-pricing.js.map +1 -0
  232. package/dist/src/stores/lucid-schema.d.ts +37 -0
  233. package/dist/src/stores/lucid-schema.d.ts.map +1 -0
  234. package/dist/src/stores/lucid-schema.js +139 -0
  235. package/dist/src/stores/lucid-schema.js.map +1 -0
  236. package/dist/src/stores/lucid.d.ts +73 -0
  237. package/dist/src/stores/lucid.d.ts.map +1 -0
  238. package/dist/src/stores/lucid.js +400 -0
  239. package/dist/src/stores/lucid.js.map +1 -0
  240. package/dist/src/telescope/dashboard.d.ts +21 -0
  241. package/dist/src/telescope/dashboard.d.ts.map +1 -0
  242. package/dist/src/telescope/dashboard.js +140 -0
  243. package/dist/src/telescope/dashboard.js.map +1 -0
  244. package/dist/src/telescope/data-providers.d.ts +39 -0
  245. package/dist/src/telescope/data-providers.d.ts.map +1 -0
  246. package/dist/src/telescope/data-providers.js +345 -0
  247. package/dist/src/telescope/data-providers.js.map +1 -0
  248. package/dist/src/telescope/extension.d.ts +27 -0
  249. package/dist/src/telescope/extension.d.ts.map +1 -0
  250. package/dist/src/telescope/extension.js +45 -0
  251. package/dist/src/telescope/extension.js.map +1 -0
  252. package/dist/src/telescope/index.d.ts +5 -0
  253. package/dist/src/telescope/index.d.ts.map +1 -0
  254. package/dist/src/telescope/index.js +4 -0
  255. package/dist/src/telescope/index.js.map +1 -0
  256. package/dist/src/telescope/telescope-sdk.d.ts +126 -0
  257. package/dist/src/telescope/telescope-sdk.d.ts.map +1 -0
  258. package/dist/src/telescope/telescope-sdk.js +15 -0
  259. package/dist/src/telescope/telescope-sdk.js.map +1 -0
  260. package/dist/src/testing/fake-embedding-provider.d.ts +37 -0
  261. package/dist/src/testing/fake-embedding-provider.d.ts.map +1 -0
  262. package/dist/src/testing/fake-embedding-provider.js +67 -0
  263. package/dist/src/testing/fake-embedding-provider.js.map +1 -0
  264. package/dist/src/testing/fake-media.d.ts +44 -0
  265. package/dist/src/testing/fake-media.d.ts.map +1 -0
  266. package/dist/src/testing/fake-media.js +69 -0
  267. package/dist/src/testing/fake-media.js.map +1 -0
  268. package/dist/src/testing/fake-model-provider.d.ts +29 -0
  269. package/dist/src/testing/fake-model-provider.d.ts.map +1 -0
  270. package/dist/src/testing/in-memory-actor-directory.d.ts +14 -0
  271. package/dist/src/testing/in-memory-actor-directory.d.ts.map +1 -0
  272. package/dist/src/testing/in-memory-actor-directory.js +27 -0
  273. package/dist/src/testing/in-memory-actor-directory.js.map +1 -0
  274. package/dist/src/testing/in-memory-attachment-staging.d.ts +22 -0
  275. package/dist/src/testing/in-memory-attachment-staging.d.ts.map +1 -0
  276. package/dist/src/testing/in-memory-attachment-staging.js +28 -0
  277. package/dist/src/testing/in-memory-attachment-staging.js.map +1 -0
  278. package/dist/src/testing/in-memory-governance-queries.d.ts +34 -0
  279. package/dist/src/testing/in-memory-governance-queries.d.ts.map +1 -0
  280. package/dist/src/testing/in-memory-governance-queries.js +373 -0
  281. package/dist/src/testing/in-memory-governance-queries.js.map +1 -0
  282. package/dist/src/testing/in-memory-pricing.d.ts +13 -0
  283. package/dist/src/testing/in-memory-pricing.d.ts.map +1 -0
  284. package/dist/src/testing/in-memory-pricing.js +27 -0
  285. package/dist/src/testing/in-memory-pricing.js.map +1 -0
  286. package/dist/src/testing/in-memory-quota.d.ts +11 -0
  287. package/dist/src/testing/in-memory-quota.d.ts.map +1 -0
  288. package/dist/src/testing/in-memory-sink.d.ts +14 -0
  289. package/dist/src/testing/in-memory-sink.d.ts.map +1 -0
  290. package/dist/src/testing/in-memory-store.d.ts +119 -0
  291. package/dist/src/testing/in-memory-store.d.ts.map +1 -0
  292. package/dist/src/testing/in-memory-store.js +317 -0
  293. package/dist/src/testing/in-memory-store.js.map +1 -0
  294. package/dist/src/testing/index.d.ts +11 -0
  295. package/dist/src/testing/index.d.ts.map +1 -0
  296. package/dist/src/testing/index.js +11 -0
  297. package/dist/src/testing/index.js.map +1 -0
  298. package/dist/src/tool-discovery.d.ts +35 -0
  299. package/dist/src/tool-discovery.d.ts.map +1 -0
  300. package/dist/src/tool-registry.d.ts +45 -0
  301. package/dist/src/tool-registry.d.ts.map +1 -0
  302. package/dist/src/tool-retry.d.ts +56 -0
  303. package/dist/src/tool-retry.d.ts.map +1 -0
  304. package/dist/src/tool-retry.js +95 -0
  305. package/dist/src/tool-retry.js.map +1 -0
  306. package/dist/src/types.d.ts +222 -0
  307. package/dist/src/types.d.ts.map +1 -0
  308. package/dist/src/types.js +2 -0
  309. package/dist/src/types.js.map +1 -0
  310. package/dist/stubs/config/agent.stub +55 -0
  311. package/dist/stubs/database/migrations/create_agent_rag_chunks.stub +41 -0
  312. package/dist/stubs/database/migrations/create_agent_run_tracking.stub +80 -0
  313. package/dist/stubs/database/migrations/create_agent_tables.stub +113 -0
  314. package/package.json +174 -0
@@ -0,0 +1,64 @@
1
+ import type { BrandedFunctionalTool } from '../ai-tool-ref.js';
2
+ import type { LucidDatabaseLike } from '../stores/lucid.js';
3
+ import { type GroupTableAccessConfig, type TableAccessPolicy } from './table-access.js';
4
+ import { type TenantScopeConfig } from './tenant-scope.js';
5
+ /** App-supplied runner over a read-only connection. The package never opens a DB itself. */
6
+ export interface QueryRunner {
7
+ run(sql: string): Promise<Record<string, unknown>[]>;
8
+ }
9
+ /**
10
+ * Configuration for {@link dataTool} — the governed read-only SQL agent tool. Mirrors the reference
11
+ * `ExecuteSqlDeps`, adapted to the AdonisJS `defineConfig`/factory idiom. Supply the DB handle as a
12
+ * structural {@link LucidDatabaseLike} (so `@adonisjs/lucid` stays an optional peer) or a custom
13
+ * {@link QueryRunner}.
14
+ */
15
+ export interface DataToolConfig {
16
+ /**
17
+ * Read-only Lucid DB handle (structural). Queries run through `db.rawQuery(sql)`. Point it at a
18
+ * read-only connection/replica. Provide this OR {@link DataToolConfig.runner}.
19
+ */
20
+ db?: LucidDatabaseLike;
21
+ /** Custom query runner. Overrides {@link DataToolConfig.db} when both are given. */
22
+ runner?: QueryRunner;
23
+ /**
24
+ * Coarse table-level allowlist checked for every referenced table. Pass a {@link TableAccessPolicy}
25
+ * instance, or a {@link GroupTableAccessConfig} to build the default {@link GroupTableAccessPolicy}.
26
+ * **Required** — there is no implicit allow-all (fail-closed).
27
+ */
28
+ tableAccess: TableAccessPolicy | GroupTableAccessConfig;
29
+ /** Optional per-row tenant constraint injected before the query runs. */
30
+ tenant?: TenantScopeConfig;
31
+ /** Row cap injected when the query has no LIMIT. Default 100. */
32
+ maxRows?: number;
33
+ /**
34
+ * Reject the query if the runner has not resolved within this many ms — a soft statement-timeout
35
+ * guard (the query may keep running server-side; the model gets a timeout error to re-plan). Off by
36
+ * default; pair with a DB-level statement timeout for a hard cap.
37
+ */
38
+ statementTimeoutMs?: number;
39
+ /** Tool name the model sees. Default `executeSql`. */
40
+ name?: string;
41
+ /** Roles allowed to invoke the tool (spec-level gate). Omit → config defaults (ADMIN-only). */
42
+ roles?: string[];
43
+ /** Authz ability checked by an ability-aware `RolesPolicy`. */
44
+ ability?: string;
45
+ }
46
+ export interface DataToolResult {
47
+ rows: Record<string, unknown>[];
48
+ rowCount: number;
49
+ sql: string;
50
+ truncated?: true;
51
+ }
52
+ /**
53
+ * Builds the governed `executeSql` read tool as a branded functional tool ({@link defineTool}) the agent
54
+ * registry auto-discovers. Register it in `config/agent.ts` (`defineConfig({ tools: [dataTool({ ... })] })`)
55
+ * or export it from an `app/agent_tools/` module.
56
+ *
57
+ * The pipeline per call (identical to the reference satellite) is:
58
+ * validate (single SELECT) → assert table access for every referenced table →
59
+ * tenant-scope rewrite (if configured) → inject LIMIT → run → return rows,
60
+ * truncating the payload if it would exceed ~256KB. `node-sql-parser` is imported lazily on the first
61
+ * call, so it stays an optional peer.
62
+ */
63
+ export declare function dataTool(config: DataToolConfig): BrandedFunctionalTool;
64
+ //# sourceMappingURL=data-tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-tool.d.ts","sourceRoot":"","sources":["../../../src/data/data-tool.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAG/D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAI5D,OAAO,EACL,KAAK,sBAAsB,EAE3B,KAAK,iBAAiB,EACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,KAAK,iBAAiB,EAAuB,MAAM,mBAAmB,CAAC;AAEhF,4FAA4F;AAC5F,MAAM,WAAW,WAAW;IAC1B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;CACtD;AAED;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;OAIG;IACH,WAAW,EAAE,iBAAiB,GAAG,sBAAsB,CAAC;IACxD,yEAAyE;IACzE,MAAM,CAAC,EAAE,iBAAiB,CAAC;IAC3B,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,+DAA+D;IAC/D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB;AA4DD;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,qBAAqB,CA0DtE"}
@@ -0,0 +1,146 @@
1
+ import { z } from 'zod';
2
+ import { defineTool } from '../ai-tool-ref.js';
3
+ import { injectLimit } from './limit.js';
4
+ import { loadSqlParser } from './parser.js';
5
+ import { SqlValidator } from './sql-validator.js';
6
+ import { GroupTableAccessPolicy, } from './table-access.js';
7
+ import { TenantScopeRewriter } from './tenant-scope.js';
8
+ const DEFAULT_MAX_ROWS = 100;
9
+ const DEFAULT_TOOL_NAME = 'executeSql';
10
+ /** Serialized rows above this size are truncated so a single tool result can't blow the context. */
11
+ const MAX_RESULT_BYTES = 256 * 1024;
12
+ const inputSchema = z.object({
13
+ sql: z.string().min(1).describe('A single read-only MySQL SELECT statement'),
14
+ });
15
+ const DESCRIPTION = 'Execute a single read-only MySQL SELECT statement. Use this to answer questions about real ' +
16
+ 'data. Only SELECT is allowed (no INSERT/UPDATE/DELETE/DDL). Access is restricted to the ' +
17
+ 'tables your role is permitted to read, results are capped, and tenant-scoped tables are ' +
18
+ 'automatically constrained to your current tenant.';
19
+ function isTableAccessPolicy(value) {
20
+ return typeof value.canAccess === 'function';
21
+ }
22
+ /** Normalize whatever a Lucid `rawQuery` returns (SQLite array, PG `{rows}`, MySQL `[rows,fields]`). */
23
+ function normalizeRows(raw) {
24
+ if (Array.isArray(raw)) {
25
+ // mysql2 returns `[rows, fields]`; the first element is itself the rows array.
26
+ if (raw.length > 0 && Array.isArray(raw[0])) {
27
+ return raw[0];
28
+ }
29
+ return raw;
30
+ }
31
+ if (raw !== null && typeof raw === 'object' && Array.isArray(raw.rows)) {
32
+ return raw.rows;
33
+ }
34
+ return [];
35
+ }
36
+ function withTimeout(promise, ms) {
37
+ if (ms === undefined)
38
+ return promise;
39
+ return new Promise((resolve, reject) => {
40
+ const timer = setTimeout(() => {
41
+ reject(new Error(`Query exceeded the ${ms}ms statement timeout.`));
42
+ }, ms);
43
+ promise.then((value) => {
44
+ clearTimeout(timer);
45
+ resolve(value);
46
+ }, (err) => {
47
+ clearTimeout(timer);
48
+ reject(err);
49
+ });
50
+ });
51
+ }
52
+ /**
53
+ * Builds the governed `executeSql` read tool as a branded functional tool ({@link defineTool}) the agent
54
+ * registry auto-discovers. Register it in `config/agent.ts` (`defineConfig({ tools: [dataTool({ ... })] })`)
55
+ * or export it from an `app/agent_tools/` module.
56
+ *
57
+ * The pipeline per call (identical to the reference satellite) is:
58
+ * validate (single SELECT) → assert table access for every referenced table →
59
+ * tenant-scope rewrite (if configured) → inject LIMIT → run → return rows,
60
+ * truncating the payload if it would exceed ~256KB. `node-sql-parser` is imported lazily on the first
61
+ * call, so it stays an optional peer.
62
+ */
63
+ export function dataTool(config) {
64
+ const maxRows = config.maxRows ?? DEFAULT_MAX_ROWS;
65
+ const tableAccess = isTableAccessPolicy(config.tableAccess)
66
+ ? config.tableAccess
67
+ : new GroupTableAccessPolicy(config.tableAccess);
68
+ const runner = resolveRunner(config);
69
+ // Lazily built once from the lazily-imported parser, then reused across calls.
70
+ let pipeline = null;
71
+ const getPipeline = () => {
72
+ if (pipeline === null) {
73
+ pipeline = loadSqlParser().then((parser) => ({
74
+ validator: new SqlValidator(parser),
75
+ ...(config.tenant !== undefined
76
+ ? { tenantScope: new TenantScopeRewriter(config.tenant, parser) }
77
+ : {}),
78
+ }));
79
+ }
80
+ return pipeline;
81
+ };
82
+ return defineTool({
83
+ name: config.name ?? DEFAULT_TOOL_NAME,
84
+ kind: 'read',
85
+ description: DESCRIPTION,
86
+ input: inputSchema,
87
+ ...(config.roles !== undefined ? { roles: config.roles } : {}),
88
+ ...(config.ability !== undefined ? { ability: config.ability } : {}),
89
+ }, async (input, ctx) => {
90
+ const { validator, tenantScope } = await getPipeline();
91
+ const { tables } = validator.validate(input.sql);
92
+ const roles = ctx.actor.roles ?? [];
93
+ const forbidden = tables.filter((table) => !tableAccess.canAccess(roles, table));
94
+ if (forbidden.length > 0) {
95
+ const formatted = forbidden.map((table) => `\`${table}\``).join(', ');
96
+ const rolesLabel = roles.length > 0 ? roles.join(', ') : 'none';
97
+ throw new Error(`Your roles (${rolesLabel}) are not allowed to query ${formatted}.`);
98
+ }
99
+ let sql = input.sql;
100
+ if (tenantScope) {
101
+ // CRITICAL: pass ctx.actor.tenantRef through UNTOUCHED. A strictly-`undefined` tenantRef is the
102
+ // privileged pass-through; a `null`/empty one must NOT be coerced to undefined (that would leak
103
+ // every tenant's rows), so never `?? undefined` here.
104
+ sql = tenantScope.rewrite(sql, ctx.actor.tenantRef);
105
+ }
106
+ const parser = await loadSqlParser();
107
+ sql = injectLimit(parser, sql, maxRows);
108
+ const rows = await withTimeout(runner.run(sql), config.statementTimeoutMs);
109
+ return buildResult(rows, sql);
110
+ });
111
+ }
112
+ function resolveRunner(config) {
113
+ if (config.runner !== undefined)
114
+ return config.runner;
115
+ const db = config.db;
116
+ if (db === undefined) {
117
+ throw new Error('dataTool: provide either `db` (a Lucid database handle) or a custom `runner`.');
118
+ }
119
+ return {
120
+ async run(sql) {
121
+ const raw = await db.rawQuery(sql);
122
+ return normalizeRows(raw);
123
+ },
124
+ };
125
+ }
126
+ function buildResult(rows, sql) {
127
+ const rowCount = rows.length;
128
+ if (byteLength(rows) <= MAX_RESULT_BYTES) {
129
+ return { rows, rowCount, sql };
130
+ }
131
+ // Keep prefix rows until the serialized payload would exceed the cap.
132
+ const kept = [];
133
+ let size = 2; // opening + closing bracket of the JSON array
134
+ for (const row of rows) {
135
+ const rowSize = byteLength(row) + 1; // +1 for the joining comma
136
+ if (size + rowSize > MAX_RESULT_BYTES)
137
+ break;
138
+ kept.push(row);
139
+ size += rowSize;
140
+ }
141
+ return { rows: kept, rowCount, sql, truncated: true };
142
+ }
143
+ function byteLength(value) {
144
+ return Buffer.byteLength(JSON.stringify(value) ?? '', 'utf8');
145
+ }
146
+ //# sourceMappingURL=data-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-tool.js","sourceRoot":"","sources":["../../../src/data/data-tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export { loadSqlParser, type SqlParserLike } from './parser.js';
2
+ export { SqlValidator, SqlValidationError, type SqlValidationResult } from './sql-validator.js';
3
+ export { type TableAccessPolicy, type GroupTableAccessConfig, GroupTableAccessPolicy, } from './table-access.js';
4
+ export { TenantScopeRewriter, type TenantScopeConfig } from './tenant-scope.js';
5
+ export { injectLimit } from './limit.js';
6
+ export { dataTool, type DataToolConfig, type DataToolResult, type QueryRunner, } from './data-tool.js';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/data/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAChG,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAAE,KAAK,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,QAAQ,EACR,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,WAAW,GACjB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export { loadSqlParser } from './parser.js';
2
+ export { SqlValidator, SqlValidationError } from './sql-validator.js';
3
+ export { GroupTableAccessPolicy, } from './table-access.js';
4
+ export { TenantScopeRewriter } from './tenant-scope.js';
5
+ export { injectLimit } from './limit.js';
6
+ export { dataTool, } from './data-tool.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/data/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAsB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAA4B,MAAM,oBAAoB,CAAC;AAChG,OAAO,EAGL,sBAAsB,GACvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,mBAAmB,EAA0B,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EACL,QAAQ,GAIT,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,12 @@
1
+ import type { SqlParserLike } from './parser.js';
2
+ /**
3
+ * Ensures a SELECT returns at most `max` rows. If the statement already carries
4
+ * a LIMIT it is returned unchanged; otherwise it is wrapped in a bounding
5
+ * subquery (`SELECT * FROM (<sql>) AS subq LIMIT <max>`) so any ORDER BY /
6
+ * GROUP BY / UNION inside `sql` is preserved.
7
+ *
8
+ * The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
9
+ * optional peer at module load.
10
+ */
11
+ export declare function injectLimit(parser: SqlParserLike, sql: string, max: number): string;
12
+ //# sourceMappingURL=limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit.d.ts","sourceRoot":"","sources":["../../../src/data/limit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAInF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Ensures a SELECT returns at most `max` rows. If the statement already carries
3
+ * a LIMIT it is returned unchanged; otherwise it is wrapped in a bounding
4
+ * subquery (`SELECT * FROM (<sql>) AS subq LIMIT <max>`) so any ORDER BY /
5
+ * GROUP BY / UNION inside `sql` is preserved.
6
+ *
7
+ * The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
8
+ * optional peer at module load.
9
+ */
10
+ export function injectLimit(parser, sql, max) {
11
+ const trimmed = sql.trim().replace(/;\s*$/, '');
12
+ if (hasLimit(parser, trimmed))
13
+ return trimmed;
14
+ return `SELECT * FROM (${trimmed}) AS subq LIMIT ${max}`;
15
+ }
16
+ function hasLimit(parser, sql) {
17
+ try {
18
+ const parsed = parser.astify(sql, { database: 'MySQL' });
19
+ const statement = (Array.isArray(parsed) ? parsed[0] : parsed);
20
+ const limit = statement?.limit;
21
+ return Boolean(limit && Array.isArray(limit.value) && limit.value.length > 0);
22
+ }
23
+ catch {
24
+ // If we can't parse it here, fall back to wrapping — the validator already
25
+ // ran and accepted it, so wrapping is the safe choice.
26
+ return false;
27
+ }
28
+ }
29
+ //# sourceMappingURL=limit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"limit.js","sourceRoot":"","sources":["../../../src/data/limit.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,WAAW,CAAC,MAAqB,EAAE,GAAW,EAAE,GAAW;IACzE,MAAM,OAAO,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAChD,IAAI,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9C,OAAO,kBAAkB,OAAO,mBAAmB,GAAG,EAAE,CAAC;AAC3D,CAAC;AAED,SAAS,QAAQ,CAAC,MAAqB,EAAE,GAAW;IAClD,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAEhD,CAAC;QACd,MAAM,KAAK,GAAG,SAAS,EAAE,KAAK,CAAC;QAC/B,OAAO,OAAO,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAChF,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,uDAAuD;QACvD,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
@@ -0,0 +1,27 @@
1
+ import type { AST } from 'node-sql-parser';
2
+ /**
3
+ * The slice of `node-sql-parser`'s `Parser` the data satellite uses. Typed structurally so the guardrail
4
+ * files (`sql-validator`, `tenant-scope`, `limit`) never import the package at module load — they take a
5
+ * parser instance. `node-sql-parser` stays an **optional lazy peer**: only {@link loadSqlParser} imports it,
6
+ * and only when the `data` tool is actually built.
7
+ */
8
+ export interface SqlParserLike {
9
+ astify(sql: string, opt?: {
10
+ database?: string;
11
+ }): AST | AST[];
12
+ sqlify(ast: AST, opt?: {
13
+ database?: string;
14
+ }): string;
15
+ tableList(sql: string, opt?: {
16
+ database?: string;
17
+ }): string[];
18
+ }
19
+ /** Re-export the parser AST type for the guardrail files (type-only — erased at compile). */
20
+ export type { AST } from 'node-sql-parser';
21
+ /**
22
+ * Lazily import `node-sql-parser` and build a single shared {@link SqlParserLike}. The import lives here
23
+ * (not at any module's top level) so the package is only pulled in when the `data` tool is configured —
24
+ * keeping `node-sql-parser` an optional peer, exactly like `@adonisjs/lucid` is for the stores.
25
+ */
26
+ export declare function loadSqlParser(): Promise<SqlParserLike>;
27
+ //# sourceMappingURL=parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../../src/data/parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,GAAG,GAAG,GAAG,EAAE,CAAC;IAC9D,MAAM,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,CAAC;IACtD,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,MAAM,EAAE,CAAC;CAC/D;AAED,6FAA6F;AAC7F,YAAY,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAI3C;;;;GAIG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAgBtD"}
@@ -0,0 +1,18 @@
1
+ let cached = null;
2
+ /**
3
+ * Lazily import `node-sql-parser` and build a single shared {@link SqlParserLike}. The import lives here
4
+ * (not at any module's top level) so the package is only pulled in when the `data` tool is configured —
5
+ * keeping `node-sql-parser` an optional peer, exactly like `@adonisjs/lucid` is for the stores.
6
+ */
7
+ export function loadSqlParser() {
8
+ if (cached === null) {
9
+ cached = import('node-sql-parser').then((mod) => {
10
+ // `node-sql-parser` is CJS: under Node ESM the named `Parser` export isn't surfaced, so reach it
11
+ // through the interop default. Fall back to a real named export for bundlers that do surface it.
12
+ const ParserCtor = mod.default?.Parser ?? mod.Parser;
13
+ return new ParserCtor();
14
+ });
15
+ }
16
+ return cached;
17
+ }
18
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../../src/data/parser.ts"],"names":[],"mappings":"AAiBA,IAAI,MAAM,GAAkC,IAAI,CAAC;AAEjD;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,MAAM,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE;YAC9C,iGAAiG;YACjG,iGAAiG;YACjG,MAAM,UAAU,GAEZ,GAID,CAAC,OAAO,EAAE,MAAM,IAAK,GAAsD,CAAC,MAAM,CAAC;YACtF,OAAO,IAAI,UAAU,EAA8B,CAAC;QACtD,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,37 @@
1
+ import type { SqlParserLike } from './parser.js';
2
+ /**
3
+ * Thrown when a statement is not a single, read-only SELECT — i.e. it is an
4
+ * INSERT/UPDATE/DELETE, DDL, a CALL, a multi-statement string, or it fails to
5
+ * parse. The handler surfaces `.message` to the model so it can re-plan.
6
+ */
7
+ export declare class SqlValidationError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ export interface SqlValidationResult {
11
+ /** Every base table the statement references (CTEs, joins, subqueries walked). */
12
+ tables: string[];
13
+ }
14
+ /**
15
+ * Parses SQL (MySQL dialect) and asserts it is a single SELECT, returning the
16
+ * set of tables it touches. Domain-agnostic: it knows nothing about which
17
+ * tables a caller may read — that is the `TableAccessPolicy`'s job.
18
+ *
19
+ * The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
20
+ * optional peer at module load.
21
+ */
22
+ export declare class SqlValidator {
23
+ private readonly parser;
24
+ constructor(parser: SqlParserLike);
25
+ /**
26
+ * Throws `SqlValidationError` unless `sql` is exactly one SELECT statement.
27
+ * On success returns the distinct base table names referenced.
28
+ */
29
+ validate(sql: string): SqlValidationResult;
30
+ /**
31
+ * Distinct base table names a statement touches — walking CTEs, subqueries,
32
+ * and joins. Backed by `node-sql-parser`'s `tableList`, which emits
33
+ * `mode::db::table` strings; the table name is the last segment.
34
+ */
35
+ private extractReferencedTables;
36
+ }
37
+ //# sourceMappingURL=sql-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-validator.d.ts","sourceRoot":"","sources":["../../../src/data/sql-validator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;gBAC/B,OAAO,EAAE,MAAM;CAI5B;AAwBD,MAAM,WAAW,mBAAmB;IAClC,kFAAkF;IAClF,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;;GAOG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAElD;;;OAGG;IACH,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB;IAiC1C;;;;OAIG;IACH,OAAO,CAAC,uBAAuB;CAUhC"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Thrown when a statement is not a single, read-only SELECT — i.e. it is an
3
+ * INSERT/UPDATE/DELETE, DDL, a CALL, a multi-statement string, or it fails to
4
+ * parse. The handler surfaces `.message` to the model so it can re-plan.
5
+ */
6
+ export class SqlValidationError extends Error {
7
+ constructor(message) {
8
+ super(message);
9
+ this.name = 'SqlValidationError';
10
+ }
11
+ }
12
+ /** Statement types that are categorically rejected (anything that writes or runs code). */
13
+ const FORBIDDEN_AST_TYPES = new Set([
14
+ 'insert',
15
+ 'update',
16
+ 'delete',
17
+ 'replace',
18
+ 'create',
19
+ 'drop',
20
+ 'alter',
21
+ 'truncate',
22
+ 'rename',
23
+ 'load_data',
24
+ 'lock',
25
+ 'unlock',
26
+ 'set',
27
+ 'call',
28
+ 'handler',
29
+ 'use',
30
+ 'grant',
31
+ 'revoke',
32
+ ]);
33
+ /**
34
+ * Parses SQL (MySQL dialect) and asserts it is a single SELECT, returning the
35
+ * set of tables it touches. Domain-agnostic: it knows nothing about which
36
+ * tables a caller may read — that is the `TableAccessPolicy`'s job.
37
+ *
38
+ * The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
39
+ * optional peer at module load.
40
+ */
41
+ export class SqlValidator {
42
+ parser;
43
+ constructor(parser) {
44
+ this.parser = parser;
45
+ }
46
+ /**
47
+ * Throws `SqlValidationError` unless `sql` is exactly one SELECT statement.
48
+ * On success returns the distinct base table names referenced.
49
+ */
50
+ validate(sql) {
51
+ let parsed;
52
+ try {
53
+ parsed = this.parser.astify(sql, { database: 'MySQL' });
54
+ }
55
+ catch (err) {
56
+ throw new SqlValidationError(`Parse error: ${err.message}`);
57
+ }
58
+ const statements = Array.isArray(parsed) ? parsed : [parsed];
59
+ if (statements.length !== 1) {
60
+ throw new SqlValidationError('Only a single statement is allowed');
61
+ }
62
+ const statement = statements[0];
63
+ const type = (statement?.type ?? '').toLowerCase();
64
+ if (type !== 'select') {
65
+ if (['create', 'drop', 'alter', 'truncate', 'rename'].includes(type)) {
66
+ throw new SqlValidationError('DDL is not allowed; only SELECT statements are accepted');
67
+ }
68
+ if (FORBIDDEN_AST_TYPES.has(type)) {
69
+ throw new SqlValidationError(`${type.toUpperCase()} is not allowed; only SELECT statements are accepted`);
70
+ }
71
+ throw new SqlValidationError(`Statement type "${type || 'unknown'}" is not allowed; only SELECT statements are accepted`);
72
+ }
73
+ return { tables: this.extractReferencedTables(sql) };
74
+ }
75
+ /**
76
+ * Distinct base table names a statement touches — walking CTEs, subqueries,
77
+ * and joins. Backed by `node-sql-parser`'s `tableList`, which emits
78
+ * `mode::db::table` strings; the table name is the last segment.
79
+ */
80
+ extractReferencedTables(sql) {
81
+ const raw = this.parser.tableList(sql, { database: 'MySQL' });
82
+ const tables = new Set();
83
+ for (const entry of raw) {
84
+ const parts = entry.split('::');
85
+ const table = parts[parts.length - 1];
86
+ if (table && table !== 'null')
87
+ tables.add(table);
88
+ }
89
+ return Array.from(tables);
90
+ }
91
+ }
92
+ //# sourceMappingURL=sql-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sql-validator.js","sourceRoot":"","sources":["../../../src/data/sql-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Decides whether a role may read a given table at all. This is the coarse,
3
+ * table-level gate; per-row scoping (see `TenantScopeRewriter`) is a separate
4
+ * layer applied at query time.
5
+ *
6
+ * Domain-agnostic: the host app supplies the role→group and group→table maps.
7
+ */
8
+ export interface TableAccessPolicy {
9
+ /** True iff any of the caller's `roles` is permitted to read `table`. Fail-closed by contract. */
10
+ canAccess(roles: readonly string[], table: string): boolean;
11
+ }
12
+ /** Inputs for {@link GroupTableAccessPolicy}: roles map to groups, groups to tables. */
13
+ export interface GroupTableAccessConfig {
14
+ /** Role name → the table groups that role may read. */
15
+ roleGroups: Record<string, string[]>;
16
+ /**
17
+ * Group name → the tables in that group. Entries are exact table names or
18
+ * `prefix_*` patterns (e.g. `pribuy_*`).
19
+ */
20
+ tablesByGroup: Record<string, string[]>;
21
+ }
22
+ /**
23
+ * Two-layer, data-driven table allowlist:
24
+ *
25
+ * 1. Every table is classified into a group (`tablesByGroup`).
26
+ * 2. Every role lists the groups it can read (`roleGroups`).
27
+ *
28
+ * `canAccess(roles, table)` is then "is the table's group in ANY of the roles'
29
+ * group lists?". **Fail-closed:** an unclassified table, unknown roles, or an
30
+ * empty role set is denied — a forgotten table never accidentally leaks.
31
+ */
32
+ export declare class GroupTableAccessPolicy implements TableAccessPolicy {
33
+ private readonly roleGroups;
34
+ private readonly tablesByGroup;
35
+ constructor(config: GroupTableAccessConfig);
36
+ canAccess(roles: readonly string[], table: string): boolean;
37
+ /** Resolve a table to its group, or `undefined` if unclassified (fail-closed). */
38
+ private resolveGroup;
39
+ }
40
+ //# sourceMappingURL=table-access.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-access.d.ts","sourceRoot":"","sources":["../../../src/data/table-access.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,iBAAiB;IAChC,kGAAkG;IAClG,SAAS,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC;CAC7D;AAED,wFAAwF;AACxF,MAAM,WAAW,sBAAsB;IACrC,uDAAuD;IACvD,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACrC;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACzC;AAED;;;;;;;;;GASG;AACH,qBAAa,sBAAuB,YAAW,iBAAiB;IAC9D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA2B;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAA2B;gBAE7C,MAAM,EAAE,sBAAsB;IAK1C,SAAS,CAAC,KAAK,EAAE,SAAS,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;IAO3D,kFAAkF;IAClF,OAAO,CAAC,YAAY;CAQrB"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Two-layer, data-driven table allowlist:
3
+ *
4
+ * 1. Every table is classified into a group (`tablesByGroup`).
5
+ * 2. Every role lists the groups it can read (`roleGroups`).
6
+ *
7
+ * `canAccess(roles, table)` is then "is the table's group in ANY of the roles'
8
+ * group lists?". **Fail-closed:** an unclassified table, unknown roles, or an
9
+ * empty role set is denied — a forgotten table never accidentally leaks.
10
+ */
11
+ export class GroupTableAccessPolicy {
12
+ roleGroups;
13
+ tablesByGroup;
14
+ constructor(config) {
15
+ this.roleGroups = config.roleGroups;
16
+ this.tablesByGroup = config.tablesByGroup;
17
+ }
18
+ canAccess(roles, table) {
19
+ const group = this.resolveGroup(table);
20
+ if (group === undefined)
21
+ return false;
22
+ return roles.some((role) => this.roleGroups[role]?.includes(group) ?? false);
23
+ }
24
+ /** Resolve a table to its group, or `undefined` if unclassified (fail-closed). */
25
+ resolveGroup(table) {
26
+ for (const [group, patterns] of Object.entries(this.tablesByGroup)) {
27
+ for (const pattern of patterns) {
28
+ if (matchesPattern(table, pattern))
29
+ return group;
30
+ }
31
+ }
32
+ return undefined;
33
+ }
34
+ }
35
+ function matchesPattern(table, pattern) {
36
+ if (pattern.endsWith('*')) {
37
+ return table.startsWith(pattern.slice(0, -1));
38
+ }
39
+ return table === pattern;
40
+ }
41
+ //# sourceMappingURL=table-access.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"table-access.js","sourceRoot":"","sources":["../../../src/data/table-access.ts"],"names":[],"mappings":"AAuBA;;;;;;;;;GASG;AACH,MAAM,OAAO,sBAAsB;IAChB,UAAU,CAA2B;IACrC,aAAa,CAA2B;IAEzD,YAAY,MAA8B;QACxC,IAAI,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;QACpC,IAAI,CAAC,aAAa,GAAG,MAAM,CAAC,aAAa,CAAC;IAC5C,CAAC;IAED,SAAS,CAAC,KAAwB,EAAE,KAAa;QAC/C,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAEtC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC;IAC/E,CAAC;IAED,kFAAkF;IAC1E,YAAY,CAAC,KAAa;QAChC,KAAK,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,EAAE,CAAC;YACnE,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;gBAC/B,IAAI,cAAc,CAAC,KAAK,EAAE,OAAO,CAAC;oBAAE,OAAO,KAAK,CAAC;YACnD,CAAC;QACH,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;CACF;AAED,SAAS,cAAc,CAAC,KAAa,EAAE,OAAe;IACpD,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC1B,OAAO,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,KAAK,KAAK,OAAO,CAAC;AAC3B,CAAC"}
@@ -0,0 +1,39 @@
1
+ import type { SqlParserLike } from './parser.js';
2
+ /** Configuration for {@link TenantScopeRewriter}. */
3
+ export interface TenantScopeConfig {
4
+ /** The column that carries the tenant key on every scoped table (e.g. `base_id`, `org_id`). */
5
+ tenantColumn: string;
6
+ /** Tables that must be constrained to the caller's tenant when referenced. */
7
+ scopedTables: string[];
8
+ }
9
+ /**
10
+ * Rewrites a SELECT so every reference to a scoped table is constrained to a
11
+ * single tenant: `<tenantColumn> = '<tenantRef>'` is AND-ed into the WHERE for
12
+ * each scoped table in the FROM. An existing predicate for a different tenant
13
+ * is rejected (no cross-tenant reads). `tenantRef === undefined` is the
14
+ * privileged path and passes the SQL through unchanged.
15
+ *
16
+ * CRITICAL: only a strictly `=== undefined` tenantRef is privileged. A `null`
17
+ * or empty-string tenantRef is NOT privileged — it flows into the rewrite and
18
+ * injects a predicate that matches nothing (fail-closed), so a missing tenant
19
+ * can never accidentally read every tenant's rows.
20
+ *
21
+ * Scoped mode rejects CTEs, UNION/INTERSECT/EXCEPT, and subqueries in FROM:
22
+ * those make it impossible to statically guarantee every tenant-bearing source
23
+ * is constrained, so we fail closed and ask the caller to rephrase.
24
+ *
25
+ * The `node-sql-parser` `Parser` is injected (see {@link loadSqlParser}) so this file never imports the
26
+ * optional peer at module load.
27
+ */
28
+ export declare class TenantScopeRewriter {
29
+ private readonly parser;
30
+ private readonly tenantColumn;
31
+ private readonly scopedTables;
32
+ constructor(config: TenantScopeConfig, parser: SqlParserLike);
33
+ /** Rewrite `sql` to constrain scoped tables to `tenantRef`. Strictly-`undefined` → pass through. */
34
+ rewrite(sql: string, tenantRef: string | undefined): string;
35
+ private collectTenantPredicates;
36
+ private buildTenantEquality;
37
+ private andCondition;
38
+ }
39
+ //# sourceMappingURL=tenant-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tenant-scope.d.ts","sourceRoot":"","sources":["../../../src/data/tenant-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,aAAa,EAAE,MAAM,aAAa,CAAC;AAEtD,qDAAqD;AACrD,MAAM,WAAW,iBAAiB;IAChC,+FAA+F;IAC/F,YAAY,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAqCD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAmB;IAM5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IALzB,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAc;gBAGzC,MAAM,EAAE,iBAAiB,EACR,MAAM,EAAE,aAAa;IAMxC,oGAAoG;IACpG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM;IAyD3D,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,YAAY;CASrB"}