@elizaos/agent 2.0.3-beta.2 → 2.0.3-beta.3

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 (381) hide show
  1. package/__tests__/view-user-journeys.d.ts.map +1 -1
  2. package/__tests__/view-user-journeys.js +52 -41
  3. package/actions/context-signal.d.ts +2 -2
  4. package/actions/files.d.ts +9 -0
  5. package/actions/files.d.ts.map +1 -0
  6. package/actions/files.js +183 -0
  7. package/actions/index.d.ts +17 -17
  8. package/api/accounts-routes.d.ts +1 -1
  9. package/api/accounts-routes.js +1 -1
  10. package/api/agent-admin-routes.d.ts +1 -1
  11. package/api/agent-admin-routes.js +2 -2
  12. package/api/agent-model.d.ts +1 -1
  13. package/api/agent-status-routes.d.ts +1 -3
  14. package/api/agent-status-routes.d.ts.map +1 -1
  15. package/api/agent-status-routes.js +0 -2
  16. package/api/approval-routes.d.ts +38 -0
  17. package/api/approval-routes.d.ts.map +1 -0
  18. package/api/approval-routes.js +223 -0
  19. package/api/background-routes.d.ts +13 -0
  20. package/api/background-routes.d.ts.map +1 -0
  21. package/api/background-routes.js +91 -0
  22. package/api/binance-skill-helpers.d.ts +2 -2
  23. package/api/binance-skill-helpers.js +2 -2
  24. package/api/builtin-views.d.ts.map +1 -1
  25. package/api/builtin-views.js +44 -4
  26. package/api/character-routes.d.ts +1 -1
  27. package/api/chat-augmentation.d.ts.map +1 -1
  28. package/api/chat-augmentation.js +17 -0
  29. package/api/chat-routes.d.ts +29 -2
  30. package/api/chat-routes.d.ts.map +1 -1
  31. package/api/chat-routes.js +76 -1
  32. package/api/commands-routes.d.ts +10 -8
  33. package/api/commands-routes.d.ts.map +1 -1
  34. package/api/commands-routes.js +17 -59
  35. package/api/config-routes.d.ts +1 -1
  36. package/api/config-routes.d.ts.map +1 -1
  37. package/api/config-routes.js +0 -2
  38. package/api/connector-routes.d.ts +1 -1
  39. package/api/conversation-metadata.d.ts +1 -1
  40. package/api/conversation-metadata.d.ts.map +1 -1
  41. package/api/conversation-metadata.js +0 -1
  42. package/api/conversation-routes.d.ts +4 -3
  43. package/api/conversation-routes.d.ts.map +1 -1
  44. package/api/conversation-routes.js +34 -1
  45. package/api/documents-service-loader.d.ts +0 -4
  46. package/api/documents-service-loader.d.ts.map +1 -1
  47. package/api/documents-service-loader.js +0 -2
  48. package/api/files-routes.d.ts +14 -0
  49. package/api/files-routes.d.ts.map +1 -0
  50. package/api/files-routes.js +47 -0
  51. package/api/first-run-routes.d.ts +1 -1
  52. package/api/health-routes.d.ts +2 -2
  53. package/api/index.d.ts +40 -39
  54. package/api/index.d.ts.map +1 -1
  55. package/api/index.js +1 -0
  56. package/api/interactions-routes.d.ts +33 -0
  57. package/api/interactions-routes.d.ts.map +1 -0
  58. package/api/interactions-routes.js +63 -0
  59. package/api/media-runtime.d.ts +2 -1
  60. package/api/media-runtime.d.ts.map +1 -1
  61. package/api/media-runtime.js +64 -7
  62. package/api/media-store.d.ts +36 -0
  63. package/api/media-store.d.ts.map +1 -1
  64. package/api/media-store.js +141 -2
  65. package/api/memory-bounds.d.ts +0 -1
  66. package/api/memory-bounds.d.ts.map +1 -1
  67. package/api/memory-bounds.js +0 -1
  68. package/api/misc-routes.d.ts +1 -1
  69. package/api/model-provider-helpers.d.ts +2 -2
  70. package/api/model-provider-helpers.js +2 -2
  71. package/api/permissions-routes-extra.d.ts +1 -1
  72. package/api/permissions-routes.d.ts +1 -1
  73. package/api/permissions-routes.d.ts.map +1 -1
  74. package/api/permissions-routes.js +8 -7
  75. package/api/plugin-discovery-helpers.d.ts +5 -6
  76. package/api/plugin-discovery-helpers.d.ts.map +1 -1
  77. package/api/plugin-discovery-helpers.js +3 -4
  78. package/api/plugin-runtime-apply.d.ts +2 -2
  79. package/api/plugin-runtime-apply.d.ts.map +1 -1
  80. package/api/plugin-runtime-apply.js +74 -0
  81. package/api/provider-switch-config.d.ts +1 -1
  82. package/api/provider-switch-routes.d.ts +2 -2
  83. package/api/registry-routes.d.ts +1 -1
  84. package/api/registry-service.d.ts +1 -1
  85. package/api/remote-capability-routes.d.ts +2 -2
  86. package/api/remote-capability-routes.d.ts.map +1 -1
  87. package/api/remote-capability-routes.js +19 -0
  88. package/api/server-autonomy-helpers.d.ts +2 -2
  89. package/api/server-helpers-auth.d.ts +10 -0
  90. package/api/server-helpers-auth.d.ts.map +1 -1
  91. package/api/server-helpers-auth.js +16 -210
  92. package/api/server-helpers-config.d.ts +1 -1
  93. package/api/server-helpers-mcp.d.ts +2 -2
  94. package/api/server-helpers-plugin.d.ts +1 -1
  95. package/api/server-helpers-swarm.d.ts +3 -3
  96. package/api/server-helpers-swarm.d.ts.map +1 -1
  97. package/api/server-helpers-swarm.js +15 -5
  98. package/api/server-helpers-wallet.d.ts +1 -1
  99. package/api/server-helpers.d.ts +4 -4
  100. package/api/server-lazy-routes.d.ts +40 -38
  101. package/api/server-lazy-routes.d.ts.map +1 -1
  102. package/api/server-lazy-routes.js +7 -0
  103. package/api/server-route-dispatch.d.ts +2 -2
  104. package/api/server-route-dispatch.d.ts.map +1 -1
  105. package/api/server-route-dispatch.js +7 -0
  106. package/api/server-types.d.ts +9 -7
  107. package/api/server-types.d.ts.map +1 -1
  108. package/api/server.d.ts +19 -19
  109. package/api/server.d.ts.map +1 -1
  110. package/api/server.js +111 -17
  111. package/api/static-file-server.d.ts +2 -2
  112. package/api/static-file-server.js +2 -2
  113. package/api/subscription-routes.d.ts +4 -4
  114. package/api/subscription-routes.js +3 -4
  115. package/api/suggestions-routes.d.ts +1 -1
  116. package/api/suggestions-routes.d.ts.map +1 -1
  117. package/api/suggestions-routes.js +2 -3
  118. package/api/terminal-execution-routing.d.ts +1 -1
  119. package/api/training-service-like.d.ts +1 -1
  120. package/api/trajectory-fallback-routes.d.ts.map +1 -1
  121. package/api/trajectory-fallback-routes.js +21 -1
  122. package/api/update-routes.d.ts +1 -1
  123. package/api/view-registry-types.d.ts +1 -1
  124. package/api/views-registry.d.ts +14 -4
  125. package/api/views-registry.d.ts.map +1 -1
  126. package/api/views-registry.js +50 -26
  127. package/api/views-routes.d.ts +1 -1
  128. package/api/views-routes.d.ts.map +1 -1
  129. package/api/views-routes.js +131 -14
  130. package/api/views-search-index.d.ts +1 -1
  131. package/api/wallet-capability.d.ts +3 -3
  132. package/api/wallet-rpc.d.ts +1 -1
  133. package/api/wallet.d.ts +3 -3
  134. package/api/wallet.d.ts.map +1 -1
  135. package/api/wallet.js +4 -3
  136. package/api/workbench-context.d.ts +1 -1
  137. package/api/workbench-helpers.d.ts +3 -3
  138. package/api/workbench-helpers.js +3 -3
  139. package/api/workbench-routes.d.ts +2 -2
  140. package/api/workbench-vfs-routes.d.ts +1 -1
  141. package/api/ws-event-replay.d.ts +1 -2
  142. package/api/ws-event-replay.d.ts.map +1 -1
  143. package/api/ws-event-replay.js +1 -2
  144. package/api/x-relay-routes.d.ts +2 -2
  145. package/api/x402-route-validation.d.ts +4 -0
  146. package/api/x402-route-validation.d.ts.map +1 -0
  147. package/api/x402-route-validation.js +6 -0
  148. package/assets/view-heroes/background.png +0 -0
  149. package/auth/account-storage.d.ts +1 -1
  150. package/auth/anthropic.d.ts +1 -1
  151. package/auth/credentials.d.ts +2 -2
  152. package/auth/index.d.ts +7 -7
  153. package/auth/oauth-flow.d.ts +2 -2
  154. package/auth/openai-codex.d.ts +1 -1
  155. package/awareness/index.d.ts +1 -1
  156. package/config/config.js +0 -1
  157. package/config/env-vars.d.ts +1 -1
  158. package/config/env-vars.d.ts.map +1 -1
  159. package/config/env-vars.js +0 -1
  160. package/config/index.d.ts +10 -10
  161. package/config/model-metadata.d.ts +1 -1
  162. package/config/owner-contacts.d.ts +1 -1
  163. package/config/schema.d.ts +1 -1
  164. package/hooks/discovery.d.ts +1 -1
  165. package/hooks/eligibility.d.ts +2 -2
  166. package/hooks/index.d.ts +2 -2
  167. package/hooks/loader.d.ts +2 -2
  168. package/hooks/registry.d.ts +1 -1
  169. package/index.d.ts +87 -85
  170. package/index.d.ts.map +1 -1
  171. package/index.js +11 -3
  172. package/package.json +54 -44
  173. package/providers/media-provider.d.ts +1 -1
  174. package/providers/page-scoped-context.d.ts.map +1 -1
  175. package/providers/page-scoped-context.js +1 -70
  176. package/providers/relevant-conversations.d.ts.map +1 -1
  177. package/providers/relevant-conversations.js +8 -9
  178. package/providers/workspace.d.ts +1 -1
  179. package/runtime/actions/web-fetch.d.ts.map +1 -1
  180. package/runtime/actions/web-fetch.js +23 -3
  181. package/runtime/actions/web-search.d.ts +32 -0
  182. package/runtime/actions/web-search.d.ts.map +1 -0
  183. package/runtime/actions/web-search.js +245 -0
  184. package/runtime/advanced-capabilities-config.d.ts +1 -1
  185. package/runtime/boot-telemetry.d.ts +1 -1
  186. package/runtime/conversation-compactor-runtime.d.ts +1 -1
  187. package/runtime/conversation-compactor-runtime.js +1 -1
  188. package/runtime/conversation-compactor.d.ts +1 -1
  189. package/runtime/core-plugins.d.ts.map +1 -1
  190. package/runtime/core-plugins.js +9 -0
  191. package/runtime/custom-actions.d.ts +18 -9
  192. package/runtime/custom-actions.d.ts.map +1 -1
  193. package/runtime/custom-actions.js +85 -23
  194. package/runtime/eliza-plugin.d.ts.map +1 -1
  195. package/runtime/eliza-plugin.js +15 -1
  196. package/runtime/eliza.d.ts +9 -7
  197. package/runtime/eliza.d.ts.map +1 -1
  198. package/runtime/eliza.js +86 -23
  199. package/runtime/first-time-setup.d.ts +1 -3
  200. package/runtime/first-time-setup.d.ts.map +1 -1
  201. package/runtime/first-time-setup.js +0 -2
  202. package/runtime/index.d.ts +18 -18
  203. package/runtime/load-plugin-from-vfs.d.ts +2 -2
  204. package/runtime/mobile-dns.d.ts.map +1 -1
  205. package/runtime/mobile-dns.js +2 -5
  206. package/runtime/operations/classifier.d.ts +1 -1
  207. package/runtime/operations/cold-strategy.d.ts +1 -1
  208. package/runtime/operations/health-checks.d.ts +1 -1
  209. package/runtime/operations/health.d.ts +1 -1
  210. package/runtime/operations/index.d.ts +10 -10
  211. package/runtime/operations/manager.d.ts +3 -3
  212. package/runtime/operations/reload-hot.d.ts +1 -1
  213. package/runtime/operations/repository.d.ts +1 -1
  214. package/runtime/operations/vault-bridge.d.ts +1 -1
  215. package/runtime/plugin-collector.d.ts +6 -1
  216. package/runtime/plugin-collector.d.ts.map +1 -1
  217. package/runtime/plugin-collector.js +11 -28
  218. package/runtime/plugin-lifecycle.d.ts.map +1 -1
  219. package/runtime/plugin-lifecycle.js +1 -0
  220. package/runtime/plugin-resolver.d.ts +2 -2
  221. package/runtime/plugin-resolver.d.ts.map +1 -1
  222. package/runtime/plugin-resolver.js +113 -65
  223. package/runtime/plugin-types.d.ts +1 -1
  224. package/runtime/prompt-optimization.d.ts +4 -4
  225. package/runtime/remote-coding-runner-gate.d.ts +7 -0
  226. package/runtime/remote-coding-runner-gate.d.ts.map +1 -0
  227. package/runtime/remote-coding-runner-gate.js +36 -0
  228. package/runtime/roles/src/index.d.ts +4 -4
  229. package/runtime/roles.d.ts +2 -2
  230. package/runtime/sandbox-character.d.ts +1 -1
  231. package/runtime/tool-call-cache/cache.d.ts +1 -1
  232. package/runtime/tool-call-cache/disk-store.d.ts +1 -1
  233. package/runtime/tool-call-cache/index.d.ts +6 -6
  234. package/runtime/tool-call-cache/key.d.ts +1 -1
  235. package/runtime/tool-call-cache/redact.d.ts +1 -1
  236. package/runtime/tool-call-cache/registry.d.ts +1 -1
  237. package/runtime/tool-call-cache-wrapper.d.ts +7 -9
  238. package/runtime/tool-call-cache-wrapper.d.ts.map +1 -1
  239. package/runtime/tool-call-cache-wrapper.js +6 -11
  240. package/runtime/trajectory-export.d.ts +3 -3
  241. package/runtime/trajectory-internals.d.ts +2 -1
  242. package/runtime/trajectory-internals.d.ts.map +1 -1
  243. package/runtime/trajectory-persistence.d.ts +5 -5
  244. package/runtime/trajectory-steps-reader.d.ts +1 -1
  245. package/runtime/trajectory-steps-writer.d.ts +1 -1
  246. package/runtime/trajectory-storage.d.ts +4 -4
  247. package/runtime/view-action-affinity.d.ts +18 -15
  248. package/runtime/view-action-affinity.d.ts.map +1 -1
  249. package/runtime/view-action-affinity.js +26 -29
  250. package/runtime/web-search-tools.d.ts +4 -1
  251. package/runtime/web-search-tools.d.ts.map +1 -1
  252. package/runtime/web-search-tools.js +37 -9
  253. package/security/index.d.ts +3 -3
  254. package/services/approval/index.d.ts +9 -0
  255. package/services/approval/index.d.ts.map +1 -0
  256. package/services/approval/index.js +8 -0
  257. package/services/approval/service.d.ts +35 -0
  258. package/services/approval/service.d.ts.map +1 -0
  259. package/services/approval/service.js +40 -0
  260. package/services/approval/sql.d.ts +18 -0
  261. package/services/approval/sql.d.ts.map +1 -0
  262. package/services/approval/sql.js +104 -0
  263. package/services/approval/store.d.ts +39 -0
  264. package/services/approval/store.d.ts.map +1 -0
  265. package/services/approval/store.js +534 -0
  266. package/services/approval/types.d.ts +207 -0
  267. package/services/approval/types.d.ts.map +1 -0
  268. package/services/approval/types.js +34 -0
  269. package/services/character-persistence.d.ts +2 -2
  270. package/services/client-chat-sender.d.ts +1 -1
  271. package/services/config-plugin-manager.d.ts +2 -2
  272. package/services/connector-setup-service.d.ts +1 -1
  273. package/services/cove-quote-x509.d.ts +1 -1
  274. package/services/cove-quote.d.ts +2 -2
  275. package/services/dstack-tee-provider.d.ts +1 -1
  276. package/services/file-storage.d.ts +20 -0
  277. package/services/file-storage.d.ts.map +1 -0
  278. package/services/file-storage.js +70 -0
  279. package/services/global-pause/index.d.ts +8 -0
  280. package/services/global-pause/index.d.ts.map +1 -0
  281. package/services/global-pause/index.js +7 -0
  282. package/services/global-pause/service.d.ts +29 -0
  283. package/services/global-pause/service.d.ts.map +1 -0
  284. package/services/global-pause/service.js +34 -0
  285. package/services/global-pause/store.d.ts +31 -0
  286. package/services/global-pause/store.d.ts.map +1 -0
  287. package/services/global-pause/store.js +83 -0
  288. package/services/handoff/index.d.ts +8 -0
  289. package/services/handoff/index.d.ts.map +1 -0
  290. package/services/handoff/index.js +7 -0
  291. package/services/handoff/service.d.ts +29 -0
  292. package/services/handoff/service.d.ts.map +1 -0
  293. package/services/handoff/service.js +34 -0
  294. package/services/handoff/store.d.ts +76 -0
  295. package/services/handoff/store.d.ts.map +1 -0
  296. package/services/handoff/store.js +148 -0
  297. package/services/index.d.ts +30 -31
  298. package/services/index.d.ts.map +1 -1
  299. package/services/index.js +0 -6
  300. package/services/js-runtime-bridge.d.ts.map +1 -1
  301. package/services/js-runtime-bridge.js +8 -2
  302. package/services/knowledge-graph/index.d.ts +4 -4
  303. package/services/knowledge-graph/service.d.ts +2 -2
  304. package/services/pending-prompts/index.d.ts +8 -0
  305. package/services/pending-prompts/index.d.ts.map +1 -0
  306. package/services/pending-prompts/index.js +7 -0
  307. package/services/pending-prompts/service.d.ts +34 -0
  308. package/services/pending-prompts/service.d.ts.map +1 -0
  309. package/services/pending-prompts/service.js +68 -0
  310. package/services/pending-prompts/store.d.ts +70 -0
  311. package/services/pending-prompts/store.d.ts.map +1 -0
  312. package/services/pending-prompts/store.js +191 -0
  313. package/services/plugin-compiler.d.ts +1 -1
  314. package/services/plugin-installer.d.ts.map +1 -1
  315. package/services/plugin-installer.js +21 -11
  316. package/services/plugin-manager-types.d.ts +1 -1
  317. package/services/proactive-interaction-decider.d.ts +62 -11
  318. package/services/proactive-interaction-decider.d.ts.map +1 -1
  319. package/services/proactive-interaction-decider.js +223 -35
  320. package/services/push/apns-provider.d.ts +1 -1
  321. package/services/push/fcm-provider.d.ts +1 -1
  322. package/services/push/notification-push-service.d.ts +2 -2
  323. package/services/registry-client-app-meta.d.ts +1 -1
  324. package/services/registry-client-endpoints.d.ts +2 -2
  325. package/services/registry-client-local.d.ts +1 -1
  326. package/services/registry-client-network.d.ts +1 -1
  327. package/services/registry-client-queries.d.ts +1 -1
  328. package/services/registry-client.d.ts +3 -3
  329. package/services/relationships-graph.d.ts +1 -1
  330. package/services/remote-capability-cloud-sandbox.d.ts +3 -3
  331. package/services/remote-capability-endpoint-conformance.d.ts +1 -1
  332. package/services/remote-capability-endpoint-provider.d.ts +4 -4
  333. package/services/remote-capability-url-endpoint-providers.d.ts +1 -1
  334. package/services/remote-plugin-adapter.d.ts.map +1 -1
  335. package/services/remote-plugin-adapter.js +3 -0
  336. package/services/remote-plugin-bridge.d.ts.map +1 -1
  337. package/services/remote-plugin-bridge.js +97 -26
  338. package/services/remote-signing-service.d.ts +4 -4
  339. package/services/research-task-executor.d.ts +1 -1
  340. package/services/sandbox-manager.d.ts +1 -1
  341. package/services/self-updater.d.ts +1 -1
  342. package/services/self-updater.d.ts.map +1 -1
  343. package/services/self-updater.js +23 -9
  344. package/services/shell-execution-router.d.ts +1 -1
  345. package/services/shell-execution-router.d.ts.map +1 -1
  346. package/services/shell-execution-router.js +19 -2
  347. package/services/tee-boot-gate-state.d.ts +1 -1
  348. package/services/tee-boot-gate.d.ts +4 -4
  349. package/services/tee-confidential-inference.d.ts +3 -3
  350. package/services/tee-key-release.d.ts +2 -2
  351. package/services/tee-model-key-boot.d.ts +4 -4
  352. package/services/tee-policy.d.ts +1 -1
  353. package/services/tee-production-profile.d.ts +1 -1
  354. package/services/tee-release-policy.d.ts +1 -1
  355. package/services/tee-revocation.d.ts +2 -2
  356. package/services/tee-runtime-config.d.ts +1 -1
  357. package/services/tee-sealed-volume.d.ts +3 -3
  358. package/services/tee-signer-backend.d.ts +3 -3
  359. package/services/update-checker.d.ts +1 -1
  360. package/services/vault-signer-backend.d.ts +1 -1
  361. package/services/vfs-git.d.ts +1 -1
  362. package/test-support/index.d.ts +3 -3
  363. package/triggers/runtime.d.ts +1 -1
  364. package/triggers/scheduling.d.ts +3 -22
  365. package/triggers/scheduling.d.ts.map +1 -1
  366. package/triggers/scheduling.js +2 -214
  367. package/tui/agent-terminal-tui.d.ts.map +1 -1
  368. package/tui/agent-terminal-tui.js +174 -72
  369. package/types/index.d.ts +3 -3
  370. package/api/nfa-routes.d.ts +0 -6
  371. package/api/nfa-routes.d.ts.map +0 -1
  372. package/api/nfa-routes.js +0 -125
  373. package/api/server-auth.d.ts +0 -46
  374. package/api/server-auth.d.ts.map +0 -1
  375. package/api/server-auth.js +0 -504
  376. package/runtime/restart.d.ts +0 -9
  377. package/runtime/restart.d.ts.map +0 -1
  378. package/runtime/restart.js +0 -8
  379. package/test-utils/sqlite-compat.d.ts +0 -23
  380. package/test-utils/sqlite-compat.d.ts.map +0 -1
  381. package/test-utils/sqlite-compat.js +0 -214
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Self-contained raw-SQL helpers for the runtime approval queue.
3
+ *
4
+ * The approval store issues raw SQL against the agent's database adapter over
5
+ * the `approval_requests` table owned by `@elizaos/plugin-sql` (public schema).
6
+ * This is the minimal subset of encoders/parsers/runners the store needs; it is
7
+ * intentionally local to `@elizaos/agent` so the runtime approval queue carries
8
+ * no dependency on plugin-side SQL glue. Mirrors `knowledge-graph/sql.ts`.
9
+ */
10
+ let cachedSqlRaw = null;
11
+ function asObject(value) {
12
+ if (!value || typeof value !== "object" || Array.isArray(value))
13
+ return null;
14
+ return value;
15
+ }
16
+ export function toText(value, fallback = "") {
17
+ if (typeof value === "string")
18
+ return value;
19
+ if (value === null || value === undefined)
20
+ return fallback;
21
+ return String(value);
22
+ }
23
+ function isMissingJsonValue(value) {
24
+ return value === null || value === undefined || value === "";
25
+ }
26
+ function parseJsonValue(value, fallback) {
27
+ if (isMissingJsonValue(value))
28
+ return fallback;
29
+ if (typeof value !== "string") {
30
+ if (typeof value === "object")
31
+ return value;
32
+ throw new Error(`[ApprovalSql] Expected JSON string or object, received ${typeof value}`);
33
+ }
34
+ try {
35
+ return JSON.parse(value);
36
+ }
37
+ catch (error) {
38
+ const message = error instanceof Error ? error.message : String(error);
39
+ throw new Error(`[ApprovalSql] Invalid JSON value: ${message}`);
40
+ }
41
+ }
42
+ export function parseJsonRecord(value) {
43
+ if (isMissingJsonValue(value))
44
+ return {};
45
+ const parsed = parseJsonValue(value, null);
46
+ const object = asObject(parsed);
47
+ if (object)
48
+ return object;
49
+ throw new Error("[ApprovalSql] Expected JSON object");
50
+ }
51
+ function extractRows(result) {
52
+ if (Array.isArray(result)) {
53
+ return result
54
+ .map((row) => asObject(row))
55
+ .filter((row) => row !== null);
56
+ }
57
+ const object = asObject(result);
58
+ if (!object)
59
+ return [];
60
+ const rows = object.rows;
61
+ if (!Array.isArray(rows))
62
+ return [];
63
+ return rows
64
+ .map((row) => asObject(row))
65
+ .filter((row) => row !== null);
66
+ }
67
+ async function getSqlRaw() {
68
+ if (cachedSqlRaw)
69
+ return cachedSqlRaw;
70
+ const drizzle = (await import("drizzle-orm"));
71
+ cachedSqlRaw = drizzle.sql.raw;
72
+ return cachedSqlRaw;
73
+ }
74
+ function getRuntimeDb(runtime) {
75
+ const db = runtime.adapter.db;
76
+ if (!db || typeof db.execute !== "function") {
77
+ throw new Error("runtime database adapter unavailable");
78
+ }
79
+ return db;
80
+ }
81
+ export async function executeRawSql(runtime, sqlText) {
82
+ const raw = await getSqlRaw();
83
+ const db = getRuntimeDb(runtime);
84
+ const result = await db.execute(raw(sqlText));
85
+ return extractRows(result);
86
+ }
87
+ export function sqlQuote(value) {
88
+ return `'${value.replace(/'/g, "''")}'`;
89
+ }
90
+ export function sqlText(value) {
91
+ if (value === null || value === undefined)
92
+ return "NULL";
93
+ return sqlQuote(value);
94
+ }
95
+ export function sqlInteger(value) {
96
+ if (value === null || value === undefined)
97
+ return "NULL";
98
+ if (!Number.isFinite(value))
99
+ throw new Error("invalid numeric SQL literal");
100
+ return String(Math.trunc(value));
101
+ }
102
+ export function sqlJson(value) {
103
+ return sqlQuote(JSON.stringify(value ?? null));
104
+ }
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `PgApprovalQueue` — concrete approval queue backed by the `approval_requests`
3
+ * table from `@elizaos/plugin-sql` (public schema, no schema prefix).
4
+ *
5
+ * Promoted from `@elizaos/plugin-personal-assistant`'s `lifeops/approval-queue.ts`
6
+ * into a first-class `@elizaos/agent` runtime store (LifeOps Slice 4). The raw
7
+ * SQL is unchanged — it has no schema prefix, so it targets plugin-sql's public
8
+ * `approval_requests` table exactly as before. There is no data migration.
9
+ *
10
+ * Design notes:
11
+ * - The state-transition table below is the single source of truth for
12
+ * legal moves. Anything not enumerated throws
13
+ * `ApprovalStateTransitionError` — there is no fallback, no auto-retry,
14
+ * no silent normalization (Commandment 8).
15
+ * - All logging goes through the structured logger only (Commandment 9).
16
+ * - Each row is scoped to an agent via `agentId`. Cross-agent access is
17
+ * not supported.
18
+ */
19
+ import { type IAgentRuntime } from "@elizaos/core";
20
+ import { type ApprovalEnqueueInput, type ApprovalListFilter, type ApprovalQueue, type ApprovalQueueOptions, type ApprovalRequest, type ApprovalResolution } from "./types.js";
21
+ export declare class PgApprovalQueue implements ApprovalQueue {
22
+ private readonly runtime;
23
+ private readonly agentId;
24
+ constructor(runtime: IAgentRuntime, options: ApprovalQueueOptions);
25
+ enqueue(input: ApprovalEnqueueInput): Promise<ApprovalRequest>;
26
+ list(filter: ApprovalListFilter): Promise<ReadonlyArray<ApprovalRequest>>;
27
+ byId(id: string): Promise<ApprovalRequest | null>;
28
+ approve(id: string, resolution: ApprovalResolution): Promise<ApprovalRequest>;
29
+ reject(id: string, resolution: ApprovalResolution): Promise<ApprovalRequest>;
30
+ markExecuting(id: string): Promise<ApprovalRequest>;
31
+ markDone(id: string): Promise<ApprovalRequest>;
32
+ markExpired(id: string): Promise<ApprovalRequest>;
33
+ purgeExpired(now: Date): Promise<ReadonlyArray<string>>;
34
+ private fetchById;
35
+ private transitionWithResolution;
36
+ private transitionWithoutResolution;
37
+ }
38
+ export declare function createApprovalQueue(runtime: IAgentRuntime, options: ApprovalQueueOptions): ApprovalQueue;
39
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/services/approval/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAE,KAAK,aAAa,EAAuB,MAAM,eAAe,CAAC;AASxE,OAAO,EAGL,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EAGvB,KAAK,aAAa,EAClB,KAAK,oBAAoB,EACzB,KAAK,eAAe,EAEpB,KAAK,kBAAkB,EAExB,MAAM,YAAY,CAAC;AA8gBpB,qBAAa,eAAgB,YAAW,aAAa;IACnD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAErB,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,oBAAoB;IAK3D,OAAO,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAqD9D,IAAI,CACR,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAmBpC,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAKjD,OAAO,CACX,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,eAAe,CAAC;IAIrB,MAAM,CACV,EAAE,EAAE,MAAM,EACV,UAAU,EAAE,kBAAkB,GAC7B,OAAO,CAAC,eAAe,CAAC;IAIrB,aAAa,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAInD,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAI9C,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAIjD,YAAY,CAAC,GAAG,EAAE,IAAI,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAe/C,SAAS;YAST,wBAAwB;YA2BxB,2BAA2B;CAoB1C;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,aAAa,EACtB,OAAO,EAAE,oBAAoB,GAC5B,aAAa,CAEf"}
@@ -0,0 +1,534 @@
1
+ /**
2
+ * `PgApprovalQueue` — concrete approval queue backed by the `approval_requests`
3
+ * table from `@elizaos/plugin-sql` (public schema, no schema prefix).
4
+ *
5
+ * Promoted from `@elizaos/plugin-personal-assistant`'s `lifeops/approval-queue.ts`
6
+ * into a first-class `@elizaos/agent` runtime store (LifeOps Slice 4). The raw
7
+ * SQL is unchanged — it has no schema prefix, so it targets plugin-sql's public
8
+ * `approval_requests` table exactly as before. There is no data migration.
9
+ *
10
+ * Design notes:
11
+ * - The state-transition table below is the single source of truth for
12
+ * legal moves. Anything not enumerated throws
13
+ * `ApprovalStateTransitionError` — there is no fallback, no auto-retry,
14
+ * no silent normalization (Commandment 8).
15
+ * - All logging goes through the structured logger only (Commandment 9).
16
+ * - Each row is scoped to an agent via `agentId`. Cross-agent access is
17
+ * not supported.
18
+ */
19
+ import { randomUUID } from "node:crypto";
20
+ import { logger, ServiceType } from "@elizaos/core";
21
+ import { executeRawSql, parseJsonRecord, sqlInteger, sqlJson, sqlText, toText, } from "./sql.js";
22
+ import { ApprovalNotFoundError, ApprovalStateTransitionError, } from "./types.js";
23
+ const ALLOWED_TRANSITIONS = {
24
+ pending: ["approved", "rejected", "expired"],
25
+ approved: ["executing", "rejected"],
26
+ executing: ["done"],
27
+ done: [],
28
+ rejected: [],
29
+ expired: [],
30
+ };
31
+ function assertTransition(id, from, to) {
32
+ if (!ALLOWED_TRANSITIONS[from].includes(to)) {
33
+ throw new ApprovalStateTransitionError(id, from, to);
34
+ }
35
+ }
36
+ const VALID_STATES = new Set([
37
+ "pending",
38
+ "approved",
39
+ "executing",
40
+ "done",
41
+ "rejected",
42
+ "expired",
43
+ ]);
44
+ const VALID_ACTIONS = new Set([
45
+ "send_message",
46
+ "send_email",
47
+ "schedule_event",
48
+ "modify_event",
49
+ "cancel_event",
50
+ "book_travel",
51
+ "make_call",
52
+ "sign_document",
53
+ "execute_workflow",
54
+ "spend_money",
55
+ ]);
56
+ const VALID_CHANNELS = new Set([
57
+ "telegram",
58
+ "discord",
59
+ "slack",
60
+ "imessage",
61
+ "sms",
62
+ "x_dm",
63
+ "email",
64
+ "google_calendar",
65
+ "browser",
66
+ "phone",
67
+ "internal",
68
+ ]);
69
+ function isRecord(value) {
70
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
71
+ }
72
+ function parseState(value) {
73
+ const text = toText(value);
74
+ if (!VALID_STATES.has(text)) {
75
+ throw new Error(`[ApprovalQueue] unknown state from db: ${text}`);
76
+ }
77
+ return text;
78
+ }
79
+ function parseAction(value) {
80
+ const text = toText(value);
81
+ if (!VALID_ACTIONS.has(text)) {
82
+ throw new Error(`[ApprovalQueue] unknown action from db: ${text}`);
83
+ }
84
+ return text;
85
+ }
86
+ function parseChannel(value) {
87
+ const text = toText(value);
88
+ if (!VALID_CHANNELS.has(text)) {
89
+ throw new Error(`[ApprovalQueue] unknown channel from db: ${text}`);
90
+ }
91
+ return text;
92
+ }
93
+ function parseTimestamp(value) {
94
+ if (value instanceof Date)
95
+ return value;
96
+ const text = toText(value);
97
+ if (!text) {
98
+ throw new Error("[ApprovalQueue] missing timestamp from db");
99
+ }
100
+ const date = new Date(text);
101
+ if (Number.isNaN(date.getTime())) {
102
+ throw new Error(`[ApprovalQueue] invalid timestamp from db: ${text}`);
103
+ }
104
+ return date;
105
+ }
106
+ function parseOptionalTimestamp(value) {
107
+ if (value === null || value === undefined || value === "")
108
+ return null;
109
+ return parseTimestamp(value);
110
+ }
111
+ function parseOptionalText(value) {
112
+ if (value === null || value === undefined)
113
+ return null;
114
+ const text = toText(value);
115
+ return text === "" ? null : text;
116
+ }
117
+ function requireRecord(value, label) {
118
+ if (!isRecord(value)) {
119
+ throw new Error(`[ApprovalQueue] invalid ${label}: expected object`);
120
+ }
121
+ return value;
122
+ }
123
+ function requireStringField(record, field, label) {
124
+ if (typeof record[field] !== "string") {
125
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected string`);
126
+ }
127
+ }
128
+ function requireNullableStringField(record, field, label) {
129
+ const value = record[field];
130
+ if (value !== null && typeof value !== "string") {
131
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected string or null`);
132
+ }
133
+ }
134
+ function requireOptionalNullableStringField(record, field, label) {
135
+ if (record[field] === undefined) {
136
+ return;
137
+ }
138
+ requireNullableStringField(record, field, label);
139
+ }
140
+ function requireStringArrayField(record, field, label) {
141
+ const value = record[field];
142
+ if (!Array.isArray(value) ||
143
+ value.some((entry) => typeof entry !== "string")) {
144
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected string[]`);
145
+ }
146
+ }
147
+ function requireFiniteNumberField(record, field, label) {
148
+ const value = record[field];
149
+ if (typeof value !== "number" || !Number.isFinite(value)) {
150
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected number`);
151
+ }
152
+ }
153
+ function requireNullableFiniteNumberField(record, field, label) {
154
+ const value = record[field];
155
+ if (value !== null &&
156
+ (typeof value !== "number" || !Number.isFinite(value))) {
157
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected number or null`);
158
+ }
159
+ }
160
+ function requireBooleanField(record, field, label) {
161
+ if (typeof record[field] !== "boolean") {
162
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected boolean`);
163
+ }
164
+ }
165
+ function requireOptionalRecordField(record, field, label) {
166
+ const value = record[field];
167
+ if (value !== undefined && value !== null && !isRecord(value)) {
168
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}: expected object or null`);
169
+ }
170
+ }
171
+ function requirePrimitiveRecordField(record, field, label) {
172
+ const value = requireRecord(record[field], `${label}.${field}`);
173
+ for (const [key, entry] of Object.entries(value)) {
174
+ if (typeof entry !== "string" &&
175
+ typeof entry !== "number" &&
176
+ typeof entry !== "boolean") {
177
+ throw new Error(`[ApprovalQueue] invalid ${label}.${field}.${key}: expected string, number, or boolean`);
178
+ }
179
+ }
180
+ }
181
+ function requireTravelPassengers(record, label) {
182
+ const passengers = record.passengers;
183
+ if (passengers === undefined) {
184
+ return;
185
+ }
186
+ if (!Array.isArray(passengers)) {
187
+ throw new Error(`[ApprovalQueue] invalid ${label}.passengers: expected array`);
188
+ }
189
+ passengers.forEach((passenger, index) => {
190
+ const passengerRecord = requireRecord(passenger, `${label}.passengers[${index}]`);
191
+ requireStringField(passengerRecord, "givenName", `${label}.passengers[${index}]`);
192
+ requireStringField(passengerRecord, "familyName", `${label}.passengers[${index}]`);
193
+ requireStringField(passengerRecord, "bornOn", `${label}.passengers[${index}]`);
194
+ for (const field of [
195
+ "offerPassengerId",
196
+ "email",
197
+ "phoneNumber",
198
+ "title",
199
+ "gender",
200
+ ]) {
201
+ requireOptionalNullableStringField(passengerRecord, field, `${label}.passengers[${index}]`);
202
+ }
203
+ });
204
+ }
205
+ function requireTravelCalendarSync(record, label) {
206
+ const value = record.calendarSync;
207
+ if (value === undefined || value === null) {
208
+ return;
209
+ }
210
+ const calendarSync = requireRecord(value, `${label}.calendarSync`);
211
+ requireBooleanField(calendarSync, "enabled", `${label}.calendarSync`);
212
+ for (const field of [
213
+ "calendarId",
214
+ "title",
215
+ "description",
216
+ "location",
217
+ "timeZone",
218
+ ]) {
219
+ requireOptionalNullableStringField(calendarSync, field, `${label}.calendarSync`);
220
+ }
221
+ }
222
+ function requireTravelCost(record, label) {
223
+ const value = record.cost;
224
+ if (value === undefined || value === null) {
225
+ return;
226
+ }
227
+ const cost = requireRecord(value, `${label}.cost`);
228
+ requireFiniteNumberField(cost, "totalUsd", `${label}.cost`);
229
+ requireFiniteNumberField(cost, "creatorMarkupUsd", `${label}.cost`);
230
+ requireFiniteNumberField(cost, "platformFeeUsd", `${label}.cost`);
231
+ requireNullableFiniteNumberField(cost, "markupPercent", `${label}.cost`);
232
+ }
233
+ function requirePaymentRequired(record, label) {
234
+ const value = record.paymentRequired;
235
+ if (value === undefined || value === null) {
236
+ return;
237
+ }
238
+ const payment = requireRecord(value, `${label}.paymentRequired`);
239
+ for (const field of ["amount", "asset", "network", "payTo", "scheme"]) {
240
+ requireStringField(payment, field, `${label}.paymentRequired`);
241
+ }
242
+ requireNullableStringField(payment, "expiresAt", `${label}.paymentRequired`);
243
+ requireNullableStringField(payment, "description", `${label}.paymentRequired`);
244
+ }
245
+ function assertApprovalPayload(record, action, label) {
246
+ switch (action) {
247
+ case "send_message":
248
+ requireStringField(record, "recipient", label);
249
+ requireStringField(record, "body", label);
250
+ requireNullableStringField(record, "replyToMessageId", label);
251
+ break;
252
+ case "send_email":
253
+ requireStringArrayField(record, "to", label);
254
+ requireStringArrayField(record, "cc", label);
255
+ requireStringArrayField(record, "bcc", label);
256
+ requireStringField(record, "subject", label);
257
+ requireStringField(record, "body", label);
258
+ requireNullableStringField(record, "threadId", label);
259
+ requireOptionalNullableStringField(record, "replyToMessageId", label);
260
+ break;
261
+ case "schedule_event":
262
+ requireStringField(record, "calendarId", label);
263
+ requireStringField(record, "title", label);
264
+ requireFiniteNumberField(record, "startsAtMs", label);
265
+ requireFiniteNumberField(record, "endsAtMs", label);
266
+ requireStringArrayField(record, "attendees", label);
267
+ requireNullableStringField(record, "location", label);
268
+ requireNullableStringField(record, "description", label);
269
+ break;
270
+ case "modify_event": {
271
+ requireStringField(record, "calendarId", label);
272
+ requireStringField(record, "eventId", label);
273
+ const patch = requireRecord(record.patch, `${label}.patch`);
274
+ requireNullableStringField(patch, "title", `${label}.patch`);
275
+ requireNullableFiniteNumberField(patch, "startsAtMs", `${label}.patch`);
276
+ requireNullableFiniteNumberField(patch, "endsAtMs", `${label}.patch`);
277
+ const attendees = patch.attendees;
278
+ if (attendees !== null &&
279
+ (!Array.isArray(attendees) ||
280
+ attendees.some((entry) => typeof entry !== "string"))) {
281
+ throw new Error(`[ApprovalQueue] invalid ${label}.patch.attendees: expected string[] or null`);
282
+ }
283
+ requireNullableStringField(patch, "location", `${label}.patch`);
284
+ requireNullableStringField(patch, "description", `${label}.patch`);
285
+ break;
286
+ }
287
+ case "cancel_event":
288
+ requireStringField(record, "calendarId", label);
289
+ requireStringField(record, "eventId", label);
290
+ requireBooleanField(record, "notifyAttendees", label);
291
+ break;
292
+ case "book_travel":
293
+ if (record.kind !== "flight" &&
294
+ record.kind !== "hotel" &&
295
+ record.kind !== "ground") {
296
+ throw new Error(`[ApprovalQueue] invalid ${label}.kind`);
297
+ }
298
+ requireStringField(record, "provider", label);
299
+ requireStringField(record, "itineraryRef", label);
300
+ requireFiniteNumberField(record, "totalCents", label);
301
+ requireStringField(record, "currency", label);
302
+ requireOptionalNullableStringField(record, "offerId", label);
303
+ requireOptionalNullableStringField(record, "offerRequestId", label);
304
+ if (record.orderType !== undefined &&
305
+ record.orderType !== null &&
306
+ record.orderType !== "hold" &&
307
+ record.orderType !== "instant") {
308
+ throw new Error(`[ApprovalQueue] invalid ${label}.orderType`);
309
+ }
310
+ requireOptionalRecordField(record, "search", label);
311
+ requireTravelPassengers(record, label);
312
+ requireTravelCalendarSync(record, label);
313
+ requireOptionalNullableStringField(record, "summary", label);
314
+ requireTravelCost(record, label);
315
+ requirePaymentRequired(record, label);
316
+ break;
317
+ case "make_call":
318
+ requireStringField(record, "to", label);
319
+ requireStringField(record, "script", label);
320
+ requireFiniteNumberField(record, "maxDurationSeconds", label);
321
+ break;
322
+ case "sign_document":
323
+ requireStringField(record, "documentId", label);
324
+ requireStringField(record, "documentName", label);
325
+ requireStringField(record, "signatureUrl", label);
326
+ requireStringField(record, "deadline", label);
327
+ break;
328
+ case "execute_workflow":
329
+ requireStringField(record, "workflowId", label);
330
+ requirePrimitiveRecordField(record, "input", label);
331
+ break;
332
+ case "spend_money":
333
+ requireStringField(record, "vendor", label);
334
+ requireFiniteNumberField(record, "amountCents", label);
335
+ requireStringField(record, "currency", label);
336
+ requireStringField(record, "memo", label);
337
+ break;
338
+ }
339
+ }
340
+ function validateApprovalPayload(value, label) {
341
+ const record = requireRecord(value, label);
342
+ const action = parseAction(record.action);
343
+ assertApprovalPayload(record, action, label);
344
+ return record;
345
+ }
346
+ function rowToRequest(row) {
347
+ const action = parseAction(row.action);
348
+ const payload = validateApprovalPayload(parseJsonRecord(row.payload), `row ${toText(row.id)} payload`);
349
+ if (payload.action !== action) {
350
+ throw new Error(`[ApprovalQueue] row ${toText(row.id)} payload action ${payload.action} does not match request action ${action}`);
351
+ }
352
+ return {
353
+ id: toText(row.id),
354
+ createdAt: parseTimestamp(row.created_at),
355
+ updatedAt: parseTimestamp(row.updated_at),
356
+ state: parseState(row.state),
357
+ requestedBy: toText(row.requested_by),
358
+ subjectUserId: toText(row.subject_user_id),
359
+ action,
360
+ payload,
361
+ channel: parseChannel(row.channel),
362
+ reason: toText(row.reason),
363
+ expiresAt: parseTimestamp(row.expires_at),
364
+ resolvedAt: parseOptionalTimestamp(row.resolved_at),
365
+ resolvedBy: parseOptionalText(row.resolved_by),
366
+ resolutionReason: parseOptionalText(row.resolution_reason),
367
+ };
368
+ }
369
+ const SELECT_COLUMNS = "id, state, requested_by, subject_user_id, action, payload, channel, reason, expires_at, resolved_at, resolved_by, resolution_reason, created_at, updated_at";
370
+ function timestampLiteral(date) {
371
+ return sqlText(date.toISOString());
372
+ }
373
+ function getNotifier(runtime) {
374
+ const svc = runtime.getService(ServiceType.NOTIFICATION);
375
+ return svc && typeof svc.notify === "function" ? svc : null;
376
+ }
377
+ export class PgApprovalQueue {
378
+ runtime;
379
+ agentId;
380
+ constructor(runtime, options) {
381
+ this.runtime = runtime;
382
+ this.agentId = options.agentId;
383
+ }
384
+ async enqueue(input) {
385
+ const payload = validateApprovalPayload(input.payload, "enqueue payload");
386
+ if (input.action !== payload.action) {
387
+ throw new Error(`[ApprovalQueue] payload action ${payload.action} does not match request action ${input.action}`);
388
+ }
389
+ const id = randomUUID();
390
+ const now = new Date();
391
+ const sql = `INSERT INTO approval_requests (
392
+ id, state, requested_by, subject_user_id, action, payload, channel, reason,
393
+ expires_at, resolved_at, resolved_by, resolution_reason,
394
+ agent_id, created_at, updated_at
395
+ ) VALUES (
396
+ ${sqlText(id)},
397
+ ${sqlText("pending")},
398
+ ${sqlText(input.requestedBy)},
399
+ ${sqlText(input.subjectUserId)},
400
+ ${sqlText(input.action)},
401
+ ${sqlJson(payload)},
402
+ ${sqlText(input.channel)},
403
+ ${sqlText(input.reason)},
404
+ ${timestampLiteral(input.expiresAt)},
405
+ NULL, NULL, NULL,
406
+ ${sqlText(this.agentId)},
407
+ ${timestampLiteral(now)},
408
+ ${timestampLiteral(now)}
409
+ ) RETURNING ${SELECT_COLUMNS}`;
410
+ const rows = await executeRawSql(this.runtime, sql);
411
+ if (rows.length === 0) {
412
+ throw new Error("[ApprovalQueue] enqueue returned no rows");
413
+ }
414
+ logger.info(`[ApprovalQueue] enqueued ${input.action} for ${input.subjectUserId} as ${id}`);
415
+ // An outbound action now needs the owner's go-ahead. Surface it on the
416
+ // notification rail so the owner can act without watching the queue
417
+ // (fire-and-forget; a notify failure must not block the enqueue).
418
+ void getNotifier(this.runtime)
419
+ ?.notify({
420
+ title: "Approval needed",
421
+ body: input.reason.slice(0, 200),
422
+ category: "approval",
423
+ priority: "high",
424
+ source: "lifeops",
425
+ deepLink: "/chat",
426
+ groupKey: `approval:${id}`,
427
+ data: { requestId: id, kind: input.action },
428
+ })
429
+ .catch(() => { });
430
+ return rowToRequest(rows[0]);
431
+ }
432
+ async list(filter) {
433
+ const where = [`agent_id = ${sqlText(this.agentId)}`];
434
+ if (filter.subjectUserId !== null) {
435
+ where.push(`subject_user_id = ${sqlText(filter.subjectUserId)}`);
436
+ }
437
+ if (filter.state !== null) {
438
+ where.push(`state = ${sqlText(filter.state)}`);
439
+ }
440
+ if (filter.action !== null) {
441
+ where.push(`action = ${sqlText(filter.action)}`);
442
+ }
443
+ const sql = `SELECT ${SELECT_COLUMNS} FROM approval_requests
444
+ WHERE ${where.join(" AND ")}
445
+ ORDER BY created_at DESC
446
+ LIMIT ${sqlInteger(filter.limit)}`;
447
+ const rows = await executeRawSql(this.runtime, sql);
448
+ return rows.map(rowToRequest);
449
+ }
450
+ async byId(id) {
451
+ const rows = await this.fetchById(id);
452
+ return rows ?? null;
453
+ }
454
+ async approve(id, resolution) {
455
+ return this.transitionWithResolution(id, "approved", resolution);
456
+ }
457
+ async reject(id, resolution) {
458
+ return this.transitionWithResolution(id, "rejected", resolution);
459
+ }
460
+ async markExecuting(id) {
461
+ return this.transitionWithoutResolution(id, "executing");
462
+ }
463
+ async markDone(id) {
464
+ return this.transitionWithoutResolution(id, "done");
465
+ }
466
+ async markExpired(id) {
467
+ return this.transitionWithoutResolution(id, "expired");
468
+ }
469
+ async purgeExpired(now) {
470
+ const sql = `UPDATE approval_requests
471
+ SET state = ${sqlText("expired")}, updated_at = ${timestampLiteral(now)}
472
+ WHERE agent_id = ${sqlText(this.agentId)}
473
+ AND state = ${sqlText("pending")}
474
+ AND expires_at <= ${timestampLiteral(now)}
475
+ RETURNING id`;
476
+ const rows = await executeRawSql(this.runtime, sql);
477
+ const ids = rows.map((row) => toText(row.id));
478
+ if (ids.length > 0) {
479
+ logger.info(`[ApprovalQueue] purged ${ids.length} expired requests`);
480
+ }
481
+ return ids;
482
+ }
483
+ async fetchById(id) {
484
+ const sql = `SELECT ${SELECT_COLUMNS} FROM approval_requests
485
+ WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}
486
+ LIMIT 1`;
487
+ const rows = await executeRawSql(this.runtime, sql);
488
+ if (rows.length === 0)
489
+ return null;
490
+ return rowToRequest(rows[0]);
491
+ }
492
+ async transitionWithResolution(id, target, resolution) {
493
+ const current = await this.fetchById(id);
494
+ if (!current)
495
+ throw new ApprovalNotFoundError(id);
496
+ assertTransition(id, current.state, target);
497
+ const now = new Date();
498
+ const sql = `UPDATE approval_requests
499
+ SET state = ${sqlText(target)},
500
+ resolved_at = ${timestampLiteral(now)},
501
+ resolved_by = ${sqlText(resolution.resolvedBy)},
502
+ resolution_reason = ${sqlText(resolution.resolutionReason)},
503
+ updated_at = ${timestampLiteral(now)}
504
+ WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}
505
+ RETURNING ${SELECT_COLUMNS}`;
506
+ const rows = await executeRawSql(this.runtime, sql);
507
+ if (rows.length === 0) {
508
+ throw new ApprovalNotFoundError(id);
509
+ }
510
+ logger.info(`[ApprovalQueue] ${current.state} -> ${target} (${id}) by ${resolution.resolvedBy}`);
511
+ return rowToRequest(rows[0]);
512
+ }
513
+ async transitionWithoutResolution(id, target) {
514
+ const current = await this.fetchById(id);
515
+ if (!current)
516
+ throw new ApprovalNotFoundError(id);
517
+ assertTransition(id, current.state, target);
518
+ const now = new Date();
519
+ const sql = `UPDATE approval_requests
520
+ SET state = ${sqlText(target)},
521
+ updated_at = ${timestampLiteral(now)}
522
+ WHERE id = ${sqlText(id)} AND agent_id = ${sqlText(this.agentId)}
523
+ RETURNING ${SELECT_COLUMNS}`;
524
+ const rows = await executeRawSql(this.runtime, sql);
525
+ if (rows.length === 0) {
526
+ throw new ApprovalNotFoundError(id);
527
+ }
528
+ logger.info(`[ApprovalQueue] ${current.state} -> ${target} (${id})`);
529
+ return rowToRequest(rows[0]);
530
+ }
531
+ }
532
+ export function createApprovalQueue(runtime, options) {
533
+ return new PgApprovalQueue(runtime, options);
534
+ }