@agent-native/core 0.131.7 → 0.132.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 (320) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +123 -0
  3. package/corpus/core/docs/content/durable-background-runs.mdx +15 -6
  4. package/corpus/core/docs/design/durable-agent-runs.md +15 -11
  5. package/corpus/core/package.json +1 -1
  6. package/corpus/core/src/a2a/activity.ts +127 -10
  7. package/corpus/core/src/a2a/index.ts +3 -0
  8. package/corpus/core/src/agent/durable-background.ts +38 -28
  9. package/corpus/core/src/agent/harness/background.ts +12 -2
  10. package/corpus/core/src/agent/production-agent.ts +18 -15
  11. package/corpus/core/src/agent/run-store.ts +45 -3
  12. package/corpus/core/src/application-state/handlers.ts +45 -0
  13. package/corpus/core/src/application-state/index.ts +3 -0
  14. package/corpus/core/src/application-state/store.ts +38 -34
  15. package/corpus/core/src/audit/redact.ts +104 -18
  16. package/corpus/core/src/cli/create.ts +115 -16
  17. package/corpus/core/src/client/AgentPanel.tsx +2 -7
  18. package/corpus/core/src/client/AssistantChat.tsx +10 -2
  19. package/corpus/core/src/client/MultiTabAssistantChat.tsx +3 -0
  20. package/corpus/core/src/client/RunStuckBanner.tsx +29 -6
  21. package/corpus/core/src/client/agent-chat.ts +4 -9
  22. package/corpus/core/src/client/application-state.ts +115 -12
  23. package/corpus/core/src/client/dynamic-suggestions.ts +4 -12
  24. package/corpus/core/src/client/frame-protocol.ts +5 -1
  25. package/corpus/core/src/client/guided-questions.tsx +12 -23
  26. package/corpus/core/src/db/client.ts +148 -28
  27. package/corpus/core/src/db/create-get-db.ts +39 -6
  28. package/corpus/core/src/db/runtime-diagnostics.ts +41 -6
  29. package/corpus/core/src/deploy/build.ts +26 -33
  30. package/corpus/core/src/feature-flags/store.ts +10 -5
  31. package/corpus/core/src/integrations/a2a-continuations-store.ts +11 -0
  32. package/corpus/core/src/integrations/config-store.ts +13 -0
  33. package/corpus/core/src/integrations/google-docs-poller.ts +26 -2
  34. package/corpus/core/src/mcp-client/routes.ts +33 -2
  35. package/corpus/core/src/onboarding/plugin.ts +31 -25
  36. package/corpus/core/src/org/accept-pending.ts +2 -0
  37. package/corpus/core/src/org/auto-join-domain.ts +2 -0
  38. package/corpus/core/src/org/context.ts +35 -10
  39. package/corpus/core/src/org/handlers.ts +6 -0
  40. package/corpus/core/src/org/request-org-cache.ts +71 -0
  41. package/corpus/core/src/resources/store.ts +10 -9
  42. package/corpus/core/src/scripts/runner.ts +6 -3
  43. package/corpus/core/src/secrets/storage.ts +97 -5
  44. package/corpus/core/src/server/action-change.ts +33 -0
  45. package/corpus/core/src/server/action-routes.ts +11 -9
  46. package/corpus/core/src/server/agent-teams.ts +10 -2
  47. package/corpus/core/src/server/better-auth-instance.ts +39 -12
  48. package/corpus/core/src/server/core-routes-plugin.ts +19 -2
  49. package/corpus/core/src/server/credential-provider.ts +57 -14
  50. package/corpus/core/src/server/http-response-telemetry.ts +1 -0
  51. package/corpus/core/src/server/poll.ts +95 -57
  52. package/corpus/core/src/server/voice-providers-status.ts +19 -4
  53. package/corpus/core/src/workspace-files/tool.ts +18 -11
  54. package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +38 -0
  55. package/corpus/templates/analytics/actions/dashboard-mutation-api.ts +1 -1
  56. package/corpus/templates/analytics/actions/ensure-native-v2-dashboards.ts +299 -0
  57. package/corpus/templates/analytics/actions/list-native-v2-dashboard-templates.ts +34 -0
  58. package/corpus/templates/analytics/app/components/dashboard/SqlChart.tsx +82 -0
  59. package/corpus/templates/analytics/app/components/layout/NewDashboardDialog.tsx +2 -2
  60. package/corpus/templates/analytics/app/hooks/use-dashboard-chat-context.ts +40 -34
  61. package/corpus/templates/analytics/app/hooks/use-user-pref.ts +3 -3
  62. package/corpus/templates/analytics/app/i18n-data.ts +40 -0
  63. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/PanelEditorDialog.tsx +18 -10
  64. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +14 -0
  65. package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/types.ts +1 -0
  66. package/corpus/templates/analytics/app/routes/chart.tsx +3 -0
  67. package/corpus/templates/analytics/changelog/2026-07-30-analytics-dashboards-now-expose-native-funnel-heatmap-callou.md +6 -0
  68. package/corpus/templates/analytics/changelog/2026-07-30-analytics-now-ships-reusable-native-v2-dashboard-manifests-w.md +6 -0
  69. package/corpus/templates/analytics/server/lib/dashboard-report-render.ts +81 -1
  70. package/corpus/templates/analytics/server/lib/native-v2-dashboards.ts +457 -0
  71. package/corpus/templates/analytics/server/lib/report-chart-svg.ts +73 -2
  72. package/corpus/templates/analytics/server/plugins/agent-chat.ts +1 -1
  73. package/corpus/templates/analytics/shared/dashboard-funnel.ts +83 -0
  74. package/corpus/templates/clips/actions/generate-workflow.ts +6 -4
  75. package/corpus/templates/clips/actions/reconcile-workflow-generation.ts +257 -0
  76. package/corpus/templates/clips/app/hooks/use-auto-title.ts +166 -4
  77. package/corpus/templates/clips/app/routes/r.$recordingId.tsx +1 -1
  78. package/corpus/templates/clips/changelog/2026-07-27-stopped-agent-runs-no-longer-leave-generated-workflow-cards-.md +6 -0
  79. package/corpus/templates/clips/changelog/2026-07-30-workflow-generation-now-stops-retrying-after-repeated-failur.md +6 -0
  80. package/corpus/templates/clips/shared/workflow.ts +5 -0
  81. package/corpus/templates/content/changelog/2026-07-30-long-running-delegated-requests-now-continue-reliably.md +6 -0
  82. package/corpus/templates/content/netlify.toml +3 -0
  83. package/corpus/templates/content/server/plugins/agent-chat.ts +1 -0
  84. package/corpus/templates/slides/.agents/skills/create-deck/SKILL.md +2 -1
  85. package/corpus/templates/slides/actions/patch-deck.ts +25 -1
  86. package/corpus/templates/slides/app/components/deck/DeckCard.tsx +17 -4
  87. package/corpus/templates/slides/app/components/deck/SlideRenderer.tsx +9 -1
  88. package/corpus/templates/slides/app/components/layout/Layout.tsx +1 -1
  89. package/corpus/templates/slides/app/context/DeckContext.tsx +11 -3
  90. package/corpus/templates/slides/app/lib/sanitize-slide-html.ts +39 -11
  91. package/corpus/templates/slides/app/pages/Index.tsx +2 -2
  92. package/corpus/templates/slides/changelog/2026-07-30-slide-previews-keep-their-aspect-ratio-isolate-embedded-styl.md +6 -0
  93. package/corpus/templates/slides/changelog/2026-07-30-slides-no-longer-lose-decks-while-organization-access-is-loa.md +6 -0
  94. package/corpus/toolkit/CHANGELOG.md +8 -0
  95. package/corpus/toolkit/package.json +1 -1
  96. package/corpus/toolkit/src/composer/runtime-adapters.tsx +16 -11
  97. package/dist/a2a/activity.d.ts +22 -4
  98. package/dist/a2a/activity.d.ts.map +1 -1
  99. package/dist/a2a/activity.js +97 -11
  100. package/dist/a2a/activity.js.map +1 -1
  101. package/dist/a2a/index.d.ts +1 -1
  102. package/dist/a2a/index.d.ts.map +1 -1
  103. package/dist/a2a/index.js +1 -1
  104. package/dist/a2a/index.js.map +1 -1
  105. package/dist/agent/durable-background.d.ts +13 -8
  106. package/dist/agent/durable-background.d.ts.map +1 -1
  107. package/dist/agent/durable-background.js +36 -28
  108. package/dist/agent/durable-background.js.map +1 -1
  109. package/dist/agent/harness/background.d.ts.map +1 -1
  110. package/dist/agent/harness/background.js +12 -2
  111. package/dist/agent/harness/background.js.map +1 -1
  112. package/dist/agent/production-agent.d.ts.map +1 -1
  113. package/dist/agent/production-agent.js +18 -15
  114. package/dist/agent/production-agent.js.map +1 -1
  115. package/dist/agent/run-store.d.ts +2 -0
  116. package/dist/agent/run-store.d.ts.map +1 -1
  117. package/dist/agent/run-store.js +44 -3
  118. package/dist/agent/run-store.js.map +1 -1
  119. package/dist/application-state/handlers.d.ts +15 -0
  120. package/dist/application-state/handlers.d.ts.map +1 -1
  121. package/dist/application-state/handlers.js +42 -2
  122. package/dist/application-state/handlers.js.map +1 -1
  123. package/dist/application-state/index.d.ts +2 -2
  124. package/dist/application-state/index.d.ts.map +1 -1
  125. package/dist/application-state/index.js +2 -2
  126. package/dist/application-state/index.js.map +1 -1
  127. package/dist/application-state/store.d.ts +11 -0
  128. package/dist/application-state/store.d.ts.map +1 -1
  129. package/dist/application-state/store.js +37 -40
  130. package/dist/application-state/store.js.map +1 -1
  131. package/dist/audit/redact.d.ts +25 -2
  132. package/dist/audit/redact.d.ts.map +1 -1
  133. package/dist/audit/redact.js +85 -21
  134. package/dist/audit/redact.js.map +1 -1
  135. package/dist/cli/create.d.ts +8 -1
  136. package/dist/cli/create.d.ts.map +1 -1
  137. package/dist/cli/create.js +98 -16
  138. package/dist/cli/create.js.map +1 -1
  139. package/dist/client/AgentPanel.d.ts.map +1 -1
  140. package/dist/client/AgentPanel.js +2 -7
  141. package/dist/client/AgentPanel.js.map +1 -1
  142. package/dist/client/AssistantChat.d.ts.map +1 -1
  143. package/dist/client/AssistantChat.js +10 -2
  144. package/dist/client/AssistantChat.js.map +1 -1
  145. package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
  146. package/dist/client/MultiTabAssistantChat.js +1 -1
  147. package/dist/client/MultiTabAssistantChat.js.map +1 -1
  148. package/dist/client/RunStuckBanner.d.ts +11 -1
  149. package/dist/client/RunStuckBanner.d.ts.map +1 -1
  150. package/dist/client/RunStuckBanner.js +20 -7
  151. package/dist/client/RunStuckBanner.js.map +1 -1
  152. package/dist/client/agent-chat.d.ts.map +1 -1
  153. package/dist/client/agent-chat.js +4 -6
  154. package/dist/client/agent-chat.js.map +1 -1
  155. package/dist/client/application-state.d.ts +11 -0
  156. package/dist/client/application-state.d.ts.map +1 -1
  157. package/dist/client/application-state.js +87 -9
  158. package/dist/client/application-state.js.map +1 -1
  159. package/dist/client/dynamic-suggestions.d.ts.map +1 -1
  160. package/dist/client/dynamic-suggestions.js +4 -13
  161. package/dist/client/dynamic-suggestions.js.map +1 -1
  162. package/dist/client/frame-protocol.d.ts +1 -0
  163. package/dist/client/frame-protocol.d.ts.map +1 -1
  164. package/dist/client/frame-protocol.js.map +1 -1
  165. package/dist/client/guided-questions.d.ts.map +1 -1
  166. package/dist/client/guided-questions.js +6 -22
  167. package/dist/client/guided-questions.js.map +1 -1
  168. package/dist/collab/awareness.d.ts +2 -2
  169. package/dist/collab/awareness.d.ts.map +1 -1
  170. package/dist/collab/routes.d.ts +1 -1
  171. package/dist/db/client.d.ts +57 -10
  172. package/dist/db/client.d.ts.map +1 -1
  173. package/dist/db/client.js +122 -31
  174. package/dist/db/client.js.map +1 -1
  175. package/dist/db/create-get-db.d.ts.map +1 -1
  176. package/dist/db/create-get-db.js +31 -7
  177. package/dist/db/create-get-db.js.map +1 -1
  178. package/dist/db/runtime-diagnostics.d.ts +1 -0
  179. package/dist/db/runtime-diagnostics.d.ts.map +1 -1
  180. package/dist/db/runtime-diagnostics.js +31 -6
  181. package/dist/db/runtime-diagnostics.js.map +1 -1
  182. package/dist/deploy/build.d.ts +5 -20
  183. package/dist/deploy/build.d.ts.map +1 -1
  184. package/dist/deploy/build.js +18 -30
  185. package/dist/deploy/build.js.map +1 -1
  186. package/dist/feature-flags/store.d.ts.map +1 -1
  187. package/dist/feature-flags/store.js +10 -5
  188. package/dist/feature-flags/store.js.map +1 -1
  189. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  190. package/dist/integrations/a2a-continuations-store.js +12 -0
  191. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  192. package/dist/integrations/config-store.d.ts +1 -0
  193. package/dist/integrations/config-store.d.ts.map +1 -1
  194. package/dist/integrations/config-store.js +11 -0
  195. package/dist/integrations/config-store.js.map +1 -1
  196. package/dist/integrations/google-docs-poller.d.ts.map +1 -1
  197. package/dist/integrations/google-docs-poller.js +19 -2
  198. package/dist/integrations/google-docs-poller.js.map +1 -1
  199. package/dist/mcp/screen-memory-stdio.d.ts +7 -7
  200. package/dist/mcp/screen-memory-stdio.d.ts.map +1 -1
  201. package/dist/mcp-client/routes.d.ts.map +1 -1
  202. package/dist/mcp-client/routes.js +30 -2
  203. package/dist/mcp-client/routes.js.map +1 -1
  204. package/dist/notifications/routes.d.ts +6 -6
  205. package/dist/onboarding/plugin.d.ts.map +1 -1
  206. package/dist/onboarding/plugin.js +12 -8
  207. package/dist/onboarding/plugin.js.map +1 -1
  208. package/dist/org/accept-pending.d.ts.map +1 -1
  209. package/dist/org/accept-pending.js +2 -0
  210. package/dist/org/accept-pending.js.map +1 -1
  211. package/dist/org/auto-join-domain.d.ts.map +1 -1
  212. package/dist/org/auto-join-domain.js +2 -0
  213. package/dist/org/auto-join-domain.js.map +1 -1
  214. package/dist/org/context.d.ts.map +1 -1
  215. package/dist/org/context.js +29 -9
  216. package/dist/org/context.js.map +1 -1
  217. package/dist/org/handlers.d.ts.map +1 -1
  218. package/dist/org/handlers.js +5 -0
  219. package/dist/org/handlers.js.map +1 -1
  220. package/dist/org/request-org-cache.d.ts +16 -0
  221. package/dist/org/request-org-cache.d.ts.map +1 -0
  222. package/dist/org/request-org-cache.js +62 -0
  223. package/dist/org/request-org-cache.js.map +1 -0
  224. package/dist/progress/routes.d.ts +1 -1
  225. package/dist/resources/handlers.d.ts +1 -1
  226. package/dist/resources/store.d.ts.map +1 -1
  227. package/dist/resources/store.js +11 -9
  228. package/dist/resources/store.js.map +1 -1
  229. package/dist/scripts/runner.d.ts.map +1 -1
  230. package/dist/scripts/runner.js +3 -3
  231. package/dist/scripts/runner.js.map +1 -1
  232. package/dist/secrets/storage.d.ts.map +1 -1
  233. package/dist/secrets/storage.js +82 -5
  234. package/dist/secrets/storage.js.map +1 -1
  235. package/dist/server/action-change.d.ts +20 -0
  236. package/dist/server/action-change.d.ts.map +1 -1
  237. package/dist/server/action-change.js +29 -0
  238. package/dist/server/action-change.js.map +1 -1
  239. package/dist/server/action-routes.d.ts.map +1 -1
  240. package/dist/server/action-routes.js +7 -8
  241. package/dist/server/action-routes.js.map +1 -1
  242. package/dist/server/agent-teams.d.ts.map +1 -1
  243. package/dist/server/agent-teams.js +10 -2
  244. package/dist/server/agent-teams.js.map +1 -1
  245. package/dist/server/better-auth-instance.d.ts.map +1 -1
  246. package/dist/server/better-auth-instance.js +31 -14
  247. package/dist/server/better-auth-instance.js.map +1 -1
  248. package/dist/server/core-routes-plugin.d.ts +1 -0
  249. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  250. package/dist/server/core-routes-plugin.js +18 -4
  251. package/dist/server/core-routes-plugin.js.map +1 -1
  252. package/dist/server/credential-provider.d.ts +16 -0
  253. package/dist/server/credential-provider.d.ts.map +1 -1
  254. package/dist/server/credential-provider.js +49 -13
  255. package/dist/server/credential-provider.js.map +1 -1
  256. package/dist/server/http-response-telemetry.d.ts.map +1 -1
  257. package/dist/server/http-response-telemetry.js +1 -0
  258. package/dist/server/http-response-telemetry.js.map +1 -1
  259. package/dist/server/poll.d.ts +6 -0
  260. package/dist/server/poll.d.ts.map +1 -1
  261. package/dist/server/poll.js +84 -47
  262. package/dist/server/poll.js.map +1 -1
  263. package/dist/server/realtime-token.d.ts +1 -1
  264. package/dist/server/voice-providers-status.d.ts.map +1 -1
  265. package/dist/server/voice-providers-status.js +12 -3
  266. package/dist/server/voice-providers-status.js.map +1 -1
  267. package/dist/workspace-files/tool.d.ts.map +1 -1
  268. package/dist/workspace-files/tool.js +18 -11
  269. package/dist/workspace-files/tool.js.map +1 -1
  270. package/docs/content/durable-background-runs.mdx +15 -6
  271. package/docs/design/durable-agent-runs.md +15 -11
  272. package/package.json +3 -3
  273. package/src/a2a/activity.ts +127 -10
  274. package/src/a2a/index.ts +3 -0
  275. package/src/agent/durable-background.ts +38 -28
  276. package/src/agent/harness/background.ts +12 -2
  277. package/src/agent/production-agent.ts +18 -15
  278. package/src/agent/run-store.ts +45 -3
  279. package/src/application-state/handlers.ts +45 -0
  280. package/src/application-state/index.ts +3 -0
  281. package/src/application-state/store.ts +38 -34
  282. package/src/audit/redact.ts +104 -18
  283. package/src/cli/create.ts +115 -16
  284. package/src/client/AgentPanel.tsx +2 -7
  285. package/src/client/AssistantChat.tsx +10 -2
  286. package/src/client/MultiTabAssistantChat.tsx +3 -0
  287. package/src/client/RunStuckBanner.tsx +29 -6
  288. package/src/client/agent-chat.ts +4 -9
  289. package/src/client/application-state.ts +115 -12
  290. package/src/client/dynamic-suggestions.ts +4 -12
  291. package/src/client/frame-protocol.ts +5 -1
  292. package/src/client/guided-questions.tsx +12 -23
  293. package/src/db/client.ts +148 -28
  294. package/src/db/create-get-db.ts +39 -6
  295. package/src/db/runtime-diagnostics.ts +41 -6
  296. package/src/deploy/build.ts +26 -33
  297. package/src/feature-flags/store.ts +10 -5
  298. package/src/integrations/a2a-continuations-store.ts +11 -0
  299. package/src/integrations/config-store.ts +13 -0
  300. package/src/integrations/google-docs-poller.ts +26 -2
  301. package/src/mcp-client/routes.ts +33 -2
  302. package/src/onboarding/plugin.ts +31 -25
  303. package/src/org/accept-pending.ts +2 -0
  304. package/src/org/auto-join-domain.ts +2 -0
  305. package/src/org/context.ts +35 -10
  306. package/src/org/handlers.ts +6 -0
  307. package/src/org/request-org-cache.ts +71 -0
  308. package/src/resources/store.ts +10 -9
  309. package/src/scripts/runner.ts +6 -3
  310. package/src/secrets/storage.ts +97 -5
  311. package/src/server/action-change.ts +33 -0
  312. package/src/server/action-routes.ts +11 -9
  313. package/src/server/agent-teams.ts +10 -2
  314. package/src/server/better-auth-instance.ts +39 -12
  315. package/src/server/core-routes-plugin.ts +19 -2
  316. package/src/server/credential-provider.ts +57 -14
  317. package/src/server/http-response-telemetry.ts +1 -0
  318. package/src/server/poll.ts +95 -57
  319. package/src/server/voice-providers-status.ts +19 -4
  320. package/src/workspace-files/tool.ts +18 -11
@@ -1,4 +1,19 @@
1
1
  export declare const getState: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<Record<string, unknown>>>;
2
+ /** Upper bound on one batched read, so a crafted URL can't fan out unbounded. */
3
+ export declare const MAX_APP_STATE_BATCH_KEYS = 100;
4
+ /**
5
+ * `GET /_agent-native/application-state?keys=a,b,c` — read many keys for the
6
+ * caller's session in one round trip.
7
+ *
8
+ * `values` carries only the keys that have a stored row; every other requested
9
+ * key is listed in `missing`. A key whose stored value is `null` therefore
10
+ * appears in `values` with a `null` value and NOT in `missing`, which is what
11
+ * keeps "no row" distinguishable from "row holding an empty value".
12
+ */
13
+ export declare const getStateMany: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
14
+ values: Record<string, unknown>;
15
+ missing: string[];
16
+ }>>;
2
17
  export declare const putState: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<any>>;
3
18
  export declare const deleteState: import("h3").EventHandlerWithFetch<import("h3").EventHandlerRequest, Promise<{
4
19
  ok: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/application-state/handlers.ts"],"names":[],"mappings":"AAwCA,eAAO,MAAM,QAAQ,wGAKnB,CAAC;AAEH,eAAO,MAAM,QAAQ,oFAOnB,CAAC;AAEH,eAAO,MAAM,WAAW;;GAMtB,CAAC;AAQH,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,0GAI5B,CAAC;AAEH,iCAAiC;AACjC,eAAO,MAAM,eAAe,wGAK1B,CAAC;AAEH,uCAAuC;AACvC,eAAO,MAAM,eAAe,oFAe1B,CAAC;AAEH,oCAAoC;AACpC,eAAO,MAAM,kBAAkB;;GAM7B,CAAC;AAEH,gCAAgC;AAChC,eAAO,MAAM,sBAAsB;;GAOlC,CAAC"}
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../../src/application-state/handlers.ts"],"names":[],"mappings":"AA0CA,eAAO,MAAM,QAAQ,wGAKnB,CAAC;AAEH,iFAAiF;AACjF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C;;;;;;;;GAQG;AACH,eAAO,MAAM,YAAY;;;GA6BvB,CAAC;AAEH,eAAO,MAAM,QAAQ,oFAOnB,CAAC;AAEH,eAAO,MAAM,WAAW;;GAMtB,CAAC;AAQH,8BAA8B;AAC9B,eAAO,MAAM,iBAAiB,0GAI5B,CAAC;AAEH,iCAAiC;AACjC,eAAO,MAAM,eAAe,wGAK1B,CAAC;AAEH,uCAAuC;AACvC,eAAO,MAAM,eAAe,oFAe1B,CAAC;AAEH,oCAAoC;AACpC,eAAO,MAAM,kBAAkB;;GAM7B,CAAC;AAEH,gCAAgC;AAChC,eAAO,MAAM,sBAAsB;;GAOlC,CAAC"}
@@ -1,7 +1,7 @@
1
- import { createError, defineEventHandler, getRouterParam, getHeader, setResponseStatus, } from "h3";
1
+ import { createError, defineEventHandler, getRouterParam, getHeader, getQuery, setResponseStatus, } from "h3";
2
2
  import { getSession } from "../server/auth.js";
3
3
  import { readBody } from "../server/h3-helpers.js";
4
- import { appStateGet, appStatePut, appStateDelete, appStateList, appStateDeleteByPrefix, } from "./store.js";
4
+ import { appStateGet, appStateGetManyEntries, appStatePut, appStateDelete, appStateList, appStateDeleteByPrefix, } from "./store.js";
5
5
  /**
6
6
  * Resolve the session ID for app state scoping. Returns the authenticated
7
7
  * user's email; throws 401 when the request has no session.
@@ -26,6 +26,46 @@ export const getState = defineEventHandler(async (event) => {
26
26
  const value = await appStateGet(sessionId, key);
27
27
  return value ?? null;
28
28
  });
29
+ /** Upper bound on one batched read, so a crafted URL can't fan out unbounded. */
30
+ export const MAX_APP_STATE_BATCH_KEYS = 100;
31
+ /**
32
+ * `GET /_agent-native/application-state?keys=a,b,c` — read many keys for the
33
+ * caller's session in one round trip.
34
+ *
35
+ * `values` carries only the keys that have a stored row; every other requested
36
+ * key is listed in `missing`. A key whose stored value is `null` therefore
37
+ * appears in `values` with a `null` value and NOT in `missing`, which is what
38
+ * keeps "no row" distinguishable from "row holding an empty value".
39
+ */
40
+ export const getStateMany = defineEventHandler(async (event) => {
41
+ const sessionId = await getSessionId(event);
42
+ const raw = getQuery(event).keys;
43
+ const requested = (Array.isArray(raw) ? raw : [raw])
44
+ .flatMap((entry) => String(entry ?? "").split(","))
45
+ .map((key) => safeKey(key.trim()))
46
+ .filter((key) => key.length > 0);
47
+ const keys = [...new Set(requested)];
48
+ if (keys.length === 0) {
49
+ throw createError({
50
+ statusCode: 400,
51
+ statusMessage: "Query parameter `keys` is required",
52
+ });
53
+ }
54
+ if (keys.length > MAX_APP_STATE_BATCH_KEYS) {
55
+ throw createError({
56
+ statusCode: 400,
57
+ statusMessage: `At most ${MAX_APP_STATE_BATCH_KEYS} keys may be read at once`,
58
+ });
59
+ }
60
+ const entries = await appStateGetManyEntries(sessionId, keys);
61
+ const values = {};
62
+ for (const entry of entries)
63
+ values[entry.key] = entry.value;
64
+ return {
65
+ values,
66
+ missing: keys.filter((key) => !(key in values)),
67
+ };
68
+ });
29
69
  export const putState = defineEventHandler(async (event) => {
30
70
  const sessionId = await getSessionId(event);
31
71
  const key = safeKey(String(getRouterParam(event, "key")));
@@ -1 +1 @@
1
- {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/application-state/handlers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createError,\n defineEventHandler,\n getRouterParam,\n getHeader,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n appStateGet,\n appStatePut,\n appStateDelete,\n appStateList,\n appStateDeleteByPrefix,\n} from \"./store.js\";\n\n/**\n * Resolve the session ID for app state scoping. Returns the authenticated\n * user's email; throws 401 when the request has no session.\n */\nasync function getSessionId(event: H3Event): Promise<string> {\n const session = await getSession(event);\n if (!session?.email) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unauthenticated\",\n });\n }\n return session.email;\n}\n\nfunction safeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_:-]/g, \"\");\n}\n\n// --- Generic state handlers ---\n\nexport const getState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const value = await appStateGet(sessionId, key);\n return value ?? null;\n});\n\nexport const putState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const body = await readBody(event);\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStatePut(sessionId, key, body, { requestSource });\n return body;\n});\n\nexport const deleteState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDelete(sessionId, key, { requestSource });\n return { ok: true };\n});\n\n// --- Multi-draft compose handlers ---\n\nfunction composeDraftKey(id: string): string {\n return `compose-${safeKey(id)}`;\n}\n\n/** List all compose drafts */\nexport const listComposeDrafts = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const items = await appStateList(sessionId, \"compose-\");\n return items.map((item) => item.value);\n});\n\n/** Get a single compose draft */\nexport const getComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const value = await appStateGet(sessionId, composeDraftKey(id));\n return value ?? null;\n});\n\n/** Create or update a compose draft */\nexport const putComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const body = await readBody(event);\n const { subject, body: bodyText } = body;\n\n if (typeof subject !== \"string\" || typeof bodyText !== \"string\") {\n setResponseStatus(event, 400);\n return { error: \"subject and body are required strings\" };\n }\n\n const state = { ...body, id };\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStatePut(sessionId, composeDraftKey(id), state, { requestSource });\n return state;\n});\n\n/** Delete a single compose draft */\nexport const deleteComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDelete(sessionId, composeDraftKey(id), { requestSource });\n return { ok: true };\n});\n\n/** Delete all compose drafts */\nexport const deleteAllComposeDrafts = defineEventHandler(\n async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDeleteByPrefix(sessionId, \"compose-\", { requestSource });\n return { ok: true };\n },\n);\n"]}
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../../src/application-state/handlers.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n createError,\n defineEventHandler,\n getRouterParam,\n getHeader,\n getQuery,\n setResponseStatus,\n type H3Event,\n} from \"h3\";\n\nimport { getSession } from \"../server/auth.js\";\nimport { readBody } from \"../server/h3-helpers.js\";\nimport {\n appStateGet,\n appStateGetManyEntries,\n appStatePut,\n appStateDelete,\n appStateList,\n appStateDeleteByPrefix,\n} from \"./store.js\";\n\n/**\n * Resolve the session ID for app state scoping. Returns the authenticated\n * user's email; throws 401 when the request has no session.\n */\nasync function getSessionId(event: H3Event): Promise<string> {\n const session = await getSession(event);\n if (!session?.email) {\n throw createError({\n statusCode: 401,\n statusMessage: \"Unauthenticated\",\n });\n }\n return session.email;\n}\n\nfunction safeKey(key: string): string {\n return key.replace(/[^a-zA-Z0-9_:-]/g, \"\");\n}\n\n// --- Generic state handlers ---\n\nexport const getState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const value = await appStateGet(sessionId, key);\n return value ?? null;\n});\n\n/** Upper bound on one batched read, so a crafted URL can't fan out unbounded. */\nexport const MAX_APP_STATE_BATCH_KEYS = 100;\n\n/**\n * `GET /_agent-native/application-state?keys=a,b,c` — read many keys for the\n * caller's session in one round trip.\n *\n * `values` carries only the keys that have a stored row; every other requested\n * key is listed in `missing`. A key whose stored value is `null` therefore\n * appears in `values` with a `null` value and NOT in `missing`, which is what\n * keeps \"no row\" distinguishable from \"row holding an empty value\".\n */\nexport const getStateMany = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const raw = getQuery(event).keys;\n const requested = (Array.isArray(raw) ? raw : [raw])\n .flatMap((entry) => String(entry ?? \"\").split(\",\"))\n .map((key) => safeKey(key.trim()))\n .filter((key) => key.length > 0);\n const keys = [...new Set(requested)];\n\n if (keys.length === 0) {\n throw createError({\n statusCode: 400,\n statusMessage: \"Query parameter `keys` is required\",\n });\n }\n if (keys.length > MAX_APP_STATE_BATCH_KEYS) {\n throw createError({\n statusCode: 400,\n statusMessage: `At most ${MAX_APP_STATE_BATCH_KEYS} keys may be read at once`,\n });\n }\n\n const entries = await appStateGetManyEntries(sessionId, keys);\n const values: Record<string, unknown> = {};\n for (const entry of entries) values[entry.key] = entry.value;\n return {\n values,\n missing: keys.filter((key) => !(key in values)),\n };\n});\n\nexport const putState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const body = await readBody(event);\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStatePut(sessionId, key, body, { requestSource });\n return body;\n});\n\nexport const deleteState = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const key = safeKey(String(getRouterParam(event, \"key\")));\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDelete(sessionId, key, { requestSource });\n return { ok: true };\n});\n\n// --- Multi-draft compose handlers ---\n\nfunction composeDraftKey(id: string): string {\n return `compose-${safeKey(id)}`;\n}\n\n/** List all compose drafts */\nexport const listComposeDrafts = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const items = await appStateList(sessionId, \"compose-\");\n return items.map((item) => item.value);\n});\n\n/** Get a single compose draft */\nexport const getComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const value = await appStateGet(sessionId, composeDraftKey(id));\n return value ?? null;\n});\n\n/** Create or update a compose draft */\nexport const putComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const body = await readBody(event);\n const { subject, body: bodyText } = body;\n\n if (typeof subject !== \"string\" || typeof bodyText !== \"string\") {\n setResponseStatus(event, 400);\n return { error: \"subject and body are required strings\" };\n }\n\n const state = { ...body, id };\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStatePut(sessionId, composeDraftKey(id), state, { requestSource });\n return state;\n});\n\n/** Delete a single compose draft */\nexport const deleteComposeDraft = defineEventHandler(async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const id = getRouterParam(event, \"id\") as string;\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDelete(sessionId, composeDraftKey(id), { requestSource });\n return { ok: true };\n});\n\n/** Delete all compose drafts */\nexport const deleteAllComposeDrafts = defineEventHandler(\n async (event: H3Event) => {\n const sessionId = await getSessionId(event);\n const requestSource = getHeader(event, \"x-request-source\") || undefined;\n await appStateDeleteByPrefix(sessionId, \"compose-\", { requestSource });\n return { ok: true };\n },\n);\n"]}
@@ -1,5 +1,5 @@
1
- export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, type AppStateCompareAndSetOperation, } from "./store.js";
1
+ export { appStateGet, appStateGetMany, appStateGetManyEntries, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, type AppStateCompareAndSetOperation, } from "./store.js";
2
2
  export { getAppStateEmitter, emitAppStateChange, emitAppStateDelete, type AppStateEvent, } from "./emitter.js";
3
- export { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
3
+ export { getState, getStateMany, MAX_APP_STATE_BATCH_KEYS, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
4
4
  export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, compareAndSetManyAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,KAAK,8BAA8B,GACpC,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,KAAK,8BAA8B,GACpC,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAGtB,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAGvB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC"}
@@ -1,9 +1,9 @@
1
1
  // Store
2
- export { appStateGet, appStateGetMany, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, } from "./store.js";
2
+ export { appStateGet, appStateGetMany, appStateGetManyEntries, appStatePut, appStateDelete, appStateCompareAndSet, appStateCompareAndSetMany, appStateList, appStateDeleteByPrefix, } from "./store.js";
3
3
  // Emitter (for SSE wiring)
4
4
  export { getAppStateEmitter, emitAppStateChange, emitAppStateDelete, } from "./emitter.js";
5
5
  // H3 route handlers (for templates)
6
- export { getState, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
6
+ export { getState, getStateMany, MAX_APP_STATE_BATCH_KEYS, putState, deleteState, listComposeDrafts, getComposeDraft, putComposeDraft, deleteComposeDraft, deleteAllComposeDrafts, } from "./handlers.js";
7
7
  // Script helpers
8
8
  export { readAppState, writeAppState, deleteAppState, compareAndSetAppState, compareAndSetManyAppState, listAppState, deleteAppStateByPrefix, readAppStateForCurrentTab, writeAppStateForCurrentTab, appStateKeyForBrowserTab, getCurrentRequestBrowserTabId, } from "./script-helpers.js";
9
9
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,WAAW,EACX,eAAe,EACf,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,cAAc,CAAC;AAEtB,oCAAoC;AACpC,OAAO,EACL,QAAQ,EACR,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC","sourcesContent":["// Store\nexport {\n appStateGet,\n appStateGetMany,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateCompareAndSetMany,\n appStateList,\n appStateDeleteByPrefix,\n type AppStateCompareAndSetOperation,\n} from \"./store.js\";\n\n// Emitter (for SSE wiring)\nexport {\n getAppStateEmitter,\n emitAppStateChange,\n emitAppStateDelete,\n type AppStateEvent,\n} from \"./emitter.js\";\n\n// H3 route handlers (for templates)\nexport {\n getState,\n putState,\n deleteState,\n listComposeDrafts,\n getComposeDraft,\n putComposeDraft,\n deleteComposeDraft,\n deleteAllComposeDrafts,\n} from \"./handlers.js\";\n\n// Script helpers\nexport {\n readAppState,\n writeAppState,\n deleteAppState,\n compareAndSetAppState,\n compareAndSetManyAppState,\n listAppState,\n deleteAppStateByPrefix,\n readAppStateForCurrentTab,\n writeAppStateForCurrentTab,\n appStateKeyForBrowserTab,\n getCurrentRequestBrowserTabId,\n} from \"./script-helpers.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/application-state/index.ts"],"names":[],"mappings":"AAAA,QAAQ;AACR,OAAO,EACL,WAAW,EACX,eAAe,EACf,sBAAsB,EACtB,WAAW,EACX,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,GAEvB,MAAM,YAAY,CAAC;AAEpB,2BAA2B;AAC3B,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,cAAc,CAAC;AAEtB,oCAAoC;AACpC,OAAO,EACL,QAAQ,EACR,YAAY,EACZ,wBAAwB,EACxB,QAAQ,EACR,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,eAAe,EACf,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,eAAe,CAAC;AAEvB,iBAAiB;AACjB,OAAO,EACL,YAAY,EACZ,aAAa,EACb,cAAc,EACd,qBAAqB,EACrB,yBAAyB,EACzB,YAAY,EACZ,sBAAsB,EACtB,yBAAyB,EACzB,0BAA0B,EAC1B,wBAAwB,EACxB,6BAA6B,GAC9B,MAAM,qBAAqB,CAAC","sourcesContent":["// Store\nexport {\n appStateGet,\n appStateGetMany,\n appStateGetManyEntries,\n appStatePut,\n appStateDelete,\n appStateCompareAndSet,\n appStateCompareAndSetMany,\n appStateList,\n appStateDeleteByPrefix,\n type AppStateCompareAndSetOperation,\n} from \"./store.js\";\n\n// Emitter (for SSE wiring)\nexport {\n getAppStateEmitter,\n emitAppStateChange,\n emitAppStateDelete,\n type AppStateEvent,\n} from \"./emitter.js\";\n\n// H3 route handlers (for templates)\nexport {\n getState,\n getStateMany,\n MAX_APP_STATE_BATCH_KEYS,\n putState,\n deleteState,\n listComposeDrafts,\n getComposeDraft,\n putComposeDraft,\n deleteComposeDraft,\n deleteAllComposeDrafts,\n} from \"./handlers.js\";\n\n// Script helpers\nexport {\n readAppState,\n writeAppState,\n deleteAppState,\n compareAndSetAppState,\n compareAndSetManyAppState,\n listAppState,\n deleteAppStateByPrefix,\n readAppStateForCurrentTab,\n writeAppStateForCurrentTab,\n appStateKeyForBrowserTab,\n getCurrentRequestBrowserTabId,\n} from \"./script-helpers.js\";\n"]}
@@ -1,5 +1,16 @@
1
1
  import type { StoreWriteOptions } from "../settings/store.js";
2
2
  export declare function appStateGet(sessionId: string, key: string): Promise<Record<string, unknown> | null>;
3
+ /**
4
+ * Read several application-state keys for one session in a single SQL query,
5
+ * returning ONLY the rows that exist. A key absent from the result has no row;
6
+ * a key present with a `null` value has a row that stores `null`. Callers that
7
+ * must not conflate the two (the batched HTTP read) depend on that difference,
8
+ * so this deliberately does not pad missing keys.
9
+ */
10
+ export declare function appStateGetManyEntries(sessionId: string, keys: readonly string[]): Promise<Array<{
11
+ key: string;
12
+ value: Record<string, unknown> | null;
13
+ }>>;
3
14
  /**
4
15
  * Read several application-state keys for one session in a single SQL query.
5
16
  * Missing keys are returned as `null` so callers can preserve the requested
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAyF9D,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAgBzC;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAsBzD;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC7C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACzC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,MAAM,WAAW,8BAA8B;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AA0GD,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,SAAS,8BAA8B,EAAE,EACrD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CA+FlB;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAWjE;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAyF9D,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CASzC;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAA;CAAE,CAAC,CAAC,CAcxE;AAED;;;;GAIG;AACH,wBAAsB,eAAe,CACnC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,SAAS,MAAM,EAAE,GACtB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,CAQzD;AAED,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,IAAI,CAAC,CAmBf;AAED,wBAAsB,cAAc,CAClC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EAC7C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,EACzC,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CAkBlB;AAED,MAAM,WAAW,8BAA8B;IAC7C,GAAG,EAAE,MAAM,CAAC;IACZ,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC3C;AA0GD,wBAAsB,yBAAyB,CAC7C,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,SAAS,8BAA8B,EAAE,EACrD,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,OAAO,CAAC,CA+FlB;AAED,wBAAsB,YAAY,CAChC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAAE,CAAC,CAAC,CAWjE;AAED,wBAAsB,sBAAsB,CAC1C,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,OAAO,CAAC,MAAM,CAAC,CAsBjB"}
@@ -1,4 +1,4 @@
1
- import { getDbExec, getDialect, isLocalDatabase, isConnectionError, isPostgres, intType, } from "../db/client.js";
1
+ import { getDbExec, getDialect, isLocalDatabase, isPostgres, intType, } from "../db/client.js";
2
2
  import { ensureIndexExists, ensureTableExists } from "../db/ddl-guard.js";
3
3
  import { widenIntColumnsToBigInt } from "../db/widen-columns.js";
4
4
  import { emitAppStateChange, emitAppStateDelete } from "./emitter.js";
@@ -79,24 +79,38 @@ async function ensureTable() {
79
79
  return _initPromise;
80
80
  }
81
81
  export async function appStateGet(sessionId, key) {
82
- try {
83
- await ensureTable();
84
- const client = getDbExec();
85
- const { rows } = await client.execute({
86
- sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,
87
- args: [sessionId, key],
88
- });
89
- if (rows.length === 0)
90
- return null;
91
- return JSON.parse(rows[0].value);
92
- }
93
- catch (err) {
94
- // Transient WS / connection drops (Neon serverless) caller polls every
95
- // 2s and will see the value on the next tick. Swallow rather than 500.
96
- if (isConnectionError(err))
97
- return null;
98
- throw err;
99
- }
82
+ await ensureTable();
83
+ const client = getDbExec();
84
+ const { rows } = await client.execute({
85
+ sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,
86
+ args: [sessionId, key],
87
+ });
88
+ if (rows.length === 0)
89
+ return null;
90
+ return JSON.parse(rows[0].value);
91
+ }
92
+ /**
93
+ * Read several application-state keys for one session in a single SQL query,
94
+ * returning ONLY the rows that exist. A key absent from the result has no row;
95
+ * a key present with a `null` value has a row that stores `null`. Callers that
96
+ * must not conflate the two (the batched HTTP read) depend on that difference,
97
+ * so this deliberately does not pad missing keys.
98
+ */
99
+ export async function appStateGetManyEntries(sessionId, keys) {
100
+ const uniqueKeys = [...new Set(keys)];
101
+ if (uniqueKeys.length === 0)
102
+ return [];
103
+ await ensureTable();
104
+ const client = getDbExec();
105
+ const placeholders = uniqueKeys.map(() => "?").join(", ");
106
+ const { rows } = await client.execute({
107
+ sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,
108
+ args: [sessionId, ...uniqueKeys],
109
+ });
110
+ return rows.map((row) => ({
111
+ key: row.key,
112
+ value: JSON.parse(row.value),
113
+ }));
100
114
  }
101
115
  /**
102
116
  * Read several application-state keys for one session in a single SQL query.
@@ -104,30 +118,13 @@ export async function appStateGet(sessionId, key) {
104
118
  * shape without issuing one fallback query per key.
105
119
  */
106
120
  export async function appStateGetMany(sessionId, keys) {
107
- const uniqueKeys = [...new Set(keys)];
108
121
  const values = {};
109
- for (const key of uniqueKeys)
122
+ for (const key of new Set(keys))
110
123
  values[key] = null;
111
- if (uniqueKeys.length === 0)
112
- return values;
113
- try {
114
- await ensureTable();
115
- const client = getDbExec();
116
- const placeholders = uniqueKeys.map(() => "?").join(", ");
117
- const { rows } = await client.execute({
118
- sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,
119
- args: [sessionId, ...uniqueKeys],
120
- });
121
- for (const row of rows) {
122
- values[row.key] = JSON.parse(row.value);
123
- }
124
- return values;
125
- }
126
- catch (err) {
127
- if (isConnectionError(err))
128
- return values;
129
- throw err;
124
+ for (const entry of await appStateGetManyEntries(sessionId, keys)) {
125
+ values[entry.key] = entry.value;
130
126
  }
127
+ return values;
131
128
  }
132
129
  export async function appStatePut(sessionId, key, value, options) {
133
130
  await ensureTable();
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n getDialect,\n isLocalDatabase,\n isConnectionError,\n isPostgres,\n intType,\n type DbExec,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\nconst MAX_HOSTED_APP_STATE_VALUE_BYTES = 1024 * 1024;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '!'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `application_state` table and its poll indexes are\n // virtually always already present in production. Issuing `CREATE\n // TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh\n // background-worker process behind a concurrent connection on the\n // shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE\n // TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*\n // wrappers probe `information_schema`/`pg_indexes` first (plain reads,\n // no lock) and run DDL ONLY for what is actually missing, bounded by a\n // transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves\n // the schema still missing they RE-PROBE and THROW rather than letting\n // init memoize success against absent schema.\n await ensureTableExists(\"application_state\", createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths. Probe pg_indexes first (no lock)\n // and skip the SHARE-locking CREATE INDEX when already present.\n await ensureIndexExists(\n \"app_state_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n );\n await ensureIndexExists(\n \"app_state_key_updated_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n try {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n } catch (err) {\n // Transient WS / connection drops (Neon serverless) — caller polls every\n // 2s and will see the value on the next tick. Swallow rather than 500.\n if (isConnectionError(err)) return null;\n throw err;\n }\n}\n\n/**\n * Read several application-state keys for one session in a single SQL query.\n * Missing keys are returned as `null` so callers can preserve the requested\n * shape without issuing one fallback query per key.\n */\nexport async function appStateGetMany(\n sessionId: string,\n keys: readonly string[],\n): Promise<Record<string, Record<string, unknown> | null>> {\n const uniqueKeys = [...new Set(keys)];\n const values: Record<string, Record<string, unknown> | null> = {};\n for (const key of uniqueKeys) values[key] = null;\n if (uniqueKeys.length === 0) return values;\n\n try {\n await ensureTable();\n const client = getDbExec();\n const placeholders = uniqueKeys.map(() => \"?\").join(\", \");\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,\n args: [sessionId, ...uniqueKeys],\n });\n for (const row of rows) {\n values[row.key as string] = JSON.parse(row.value as string);\n }\n return values;\n } catch (err) {\n if (isConnectionError(err)) return values;\n throw err;\n }\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, serialized, Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateCompareAndSet(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const changed = await executeAppStateCompareAndSet(\n client,\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n if (changed) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return changed;\n}\n\nexport interface AppStateCompareAndSetOperation {\n key: string;\n expectedValue: Record<string, unknown> | null;\n nextValue: Record<string, unknown> | null;\n}\n\nconst APP_STATE_CAS_MISMATCH = Symbol(\"app-state-cas-mismatch\");\n\nasync function executeAppStateCompareAndSet(\n client: DbExec,\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const statement = buildAppStateCompareAndSetStatement(\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n const result = await client.execute(statement);\n return result.rowsAffected > 0;\n}\n\nfunction buildAppStateCompareAndSetStatement(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): { sql: string; args: unknown[] } {\n if (expectedValue === null) {\n if (nextValue === null) {\n throw new Error(\n \"Application state CAS cannot replace absence with absence.\",\n );\n }\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?) ON CONFLICT (session_id, key) DO NOTHING`\n : `INSERT OR IGNORE INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)`,\n args: [sessionId, key, next, Date.now(), sessionId, key],\n };\n }\n\n const expected = JSON.stringify(expectedValue);\n if (nextValue === null) {\n return {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ? AND value = ?`,\n args: [sessionId, key, expected],\n };\n }\n\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: `UPDATE application_state SET value = ?, updated_at = ? WHERE session_id = ? AND key = ? AND value = ?`,\n args: [next, Date.now(), sessionId, key, expected],\n };\n}\n\nfunction buildD1CompareAndSetGuard(\n sessionId: string,\n guardKey: string,\n operation: AppStateCompareAndSetOperation,\n): { sql: string; args: unknown[] } {\n const expected = operation.expectedValue;\n const condition =\n expected === null\n ? \"NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)\"\n : \"EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ? AND value = ?)\";\n return {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, '{}', ? WHERE NOT (${condition})`,\n args:\n expected === null\n ? [sessionId, guardKey, Date.now(), sessionId, operation.key]\n : [\n sessionId,\n guardKey,\n Date.now(),\n sessionId,\n operation.key,\n JSON.stringify(expected),\n ],\n };\n}\n\nfunction isD1CompareAndSetMismatch(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /unique constraint failed:\\s*application_state\\.session_id,\\s*application_state\\.key/i.test(\n message,\n );\n}\n\nfunction serializeAppStateValue(\n key: string,\n value: Record<string, unknown>,\n): string {\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n return serialized;\n}\n\nexport async function appStateCompareAndSetMany(\n sessionId: string,\n operations: readonly AppStateCompareAndSetOperation[],\n options?: StoreWriteOptions,\n): Promise<boolean> {\n if (operations.length === 0) return true;\n const keys = new Set(operations.map(({ key }) => key));\n if (keys.size !== operations.length) {\n throw new Error(\"Application state multi-key CAS requires unique keys.\");\n }\n for (const { key, nextValue } of operations) {\n if (nextValue) serializeAppStateValue(key, nextValue);\n }\n const orderedOperations = [...operations].sort((a, b) =>\n a.key.localeCompare(b.key),\n );\n\n await ensureTable();\n const client = getDbExec();\n if (getDialect() === \"d1\") {\n if (!client.atomicBatch) {\n throw new Error(\n \"D1 application-state CAS requires atomic batch support.\",\n );\n }\n const guardKey = `__agent_native_cas_guard__:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n const guardInsert = {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, '{}', ?)`,\n args: [sessionId, guardKey, Date.now()],\n };\n const guards = orderedOperations.map((operation) =>\n buildD1CompareAndSetGuard(sessionId, guardKey, operation),\n );\n const mutations = orderedOperations.map((operation) =>\n buildAppStateCompareAndSetStatement(\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n ),\n );\n let results;\n try {\n results = await client.atomicBatch([\n guardInsert,\n ...guards,\n ...mutations,\n {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, guardKey],\n },\n ]);\n } catch (error) {\n if (isD1CompareAndSetMismatch(error)) return false;\n throw error;\n }\n const mutationResults = results.slice(\n 1 + guards.length,\n 1 + guards.length + mutations.length,\n );\n if (\n mutationResults.length !== mutations.length ||\n mutationResults.some((result) => result.rowsAffected !== 1)\n ) {\n throw new Error(\n \"D1 application-state CAS completed without applying every mutation.\",\n );\n }\n } else {\n if (!client.transaction) {\n throw new Error(\"Application state multi-key CAS requires transactions.\");\n }\n try {\n await client.transaction(async (tx) => {\n for (const operation of orderedOperations) {\n const changed = await executeAppStateCompareAndSet(\n tx,\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n );\n if (!changed) throw APP_STATE_CAS_MISMATCH;\n }\n });\n } catch (error) {\n if (error === APP_STATE_CAS_MISMATCH) return false;\n throw error;\n }\n }\n\n for (const { key, nextValue } of operations) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return true;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/application-state/store.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EACT,UAAU,EACV,eAAe,EACf,UAAU,EACV,OAAO,GAER,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC1E,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAEtE,IAAI,YAAuC,CAAC;AAC5C,MAAM,gCAAgC,GAAG,IAAI,GAAG,IAAI,CAAC;AAErD,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,qEAAqE;AACrE,SAAS,UAAU,CAAC,CAAS;IAC3B,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;AACrD,CAAC;AAED,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,YAAY,GAAG,CAAC,KAAK,IAAI,EAAE;YACzB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;YAC3B,MAAM,SAAS,GAAG;;;;;uBAKD,OAAO,EAAE;;;OAGzB,CAAC;YAEF,IAAI,UAAU,EAAE,EAAE,CAAC;gBACjB,mEAAmE;gBACnE,kEAAkE;gBAClE,6DAA6D;gBAC7D,kEAAkE;gBAClE,uEAAuE;gBACvE,oEAAoE;gBACpE,uEAAuE;gBACvE,uEAAuE;gBACvE,wEAAwE;gBACxE,uEAAuE;gBACvE,8CAA8C;gBAC9C,MAAM,iBAAiB,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;gBACxD,0EAA0E;gBAC1E,0EAA0E;gBAC1E,mEAAmE;gBACnE,MAAM,uBAAuB,CAAC,mBAAmB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;gBACnE,uEAAuE;gBACvE,gEAAgE;gBAChE,MAAM,iBAAiB,CACrB,0BAA0B,EAC1B,uFAAuF,CACxF,CAAC;gBACF,MAAM,iBAAiB,CACrB,2BAA2B,EAC3B,6FAA6F,CAC9F,CAAC;gBACF,OAAO;YACT,CAAC;YAED,qEAAqE;YACrE,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAChC,0EAA0E;YAC1E,0EAA0E;YAC1E,mEAAmE;YACnE,MAAM,uBAAuB,CAAC,mBAAmB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;YACnE,sCAAsC;YACtC,wEAAwE;YACxE,yEAAyE;YACzE,qEAAqE;YACrE,qEAAqE;YACrE,oDAAoD;YACpD,KAAK,MAAM,GAAG,IAAI;gBAChB,uFAAuF;gBACvF,6FAA6F;aAC9F,EAAE,CAAC;gBACF,IAAI,CAAC;oBACH,MAAM,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC5B,CAAC;gBAAC,MAAM,CAAC;oBACP,4DAA4D;gBAC9D,CAAC;YACH,CAAC;QACH,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;YACjB,sDAAsD;YACtD,YAAY,GAAG,SAAS,CAAC;YACzB,MAAM,GAAG,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,GAAW;IAEX,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,sEAAsE;QAC3E,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;KACvB,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAe,CAAC,CAAC;AAC7C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAiB,EACjB,IAAuB;IAEvB,MAAM,UAAU,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACtC,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACvC,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,YAAY,GAAG,UAAU,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1D,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,6EAA6E,YAAY,GAAG;QACjG,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,UAAU,CAAC;KACjC,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,GAAG,EAAE,GAAG,CAAC,GAAa;QACtB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAe,CAAC;KACvC,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,SAAiB,EACjB,IAAuB;IAEvB,MAAM,MAAM,GAAmD,EAAE,CAAC;IAClE,KAAK,MAAM,GAAG,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC;QAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAEpD,KAAK,MAAM,KAAK,IAAI,MAAM,sBAAsB,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;QAClE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,SAAiB,EACjB,GAAW,EACX,KAA8B,EAC9B,OAA2B;IAE3B,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,IACE,CAAC,eAAe,EAAE;QAClB,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,gCAAgC,EACxE,CAAC;QACD,MAAM,IAAI,KAAK,CACb,4BAA4B,GAAG,4HAA4H,CAC5J,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,UAAU,EAAE;YACf,CAAC,CAAC,yLAAyL;YAC3L,CAAC,CAAC,mGAAmG;QACvG,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;KAC/C,CAAC,CAAC;IACH,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;AAC7D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,SAAiB,EACjB,GAAW,EACX,OAA2B;IAE3B,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE,gEAAgE;QACrE,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,CAAC;KACvB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;IACxC,IAAI,OAAO;QAAE,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IACxE,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,SAAiB,EACjB,GAAW,EACX,aAA6C,EAC7C,SAAyC,EACzC,OAA2B;IAE3B,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,OAAO,GAAG,MAAM,4BAA4B,CAChD,MAAM,EACN,SAAS,EACT,GAAG,EACH,aAAa,EACb,SAAS,CACV,CAAC;IACF,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAQD,MAAM,sBAAsB,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;AAEhE,KAAK,UAAU,4BAA4B,CACzC,MAAc,EACd,SAAiB,EACjB,GAAW,EACX,aAA6C,EAC7C,SAAyC;IAEzC,MAAM,SAAS,GAAG,mCAAmC,CACnD,SAAS,EACT,GAAG,EACH,aAAa,EACb,SAAS,CACV,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/C,OAAO,MAAM,CAAC,YAAY,GAAG,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,mCAAmC,CAC1C,SAAiB,EACjB,GAAW,EACX,aAA6C,EAC7C,SAAyC;IAEzC,IAAI,aAAa,KAAK,IAAI,EAAE,CAAC;QAC3B,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QACpD,OAAO;YACL,GAAG,EAAE,UAAU,EAAE;gBACf,CAAC,CAAC,mNAAmN;gBACrN,CAAC,CAAC,oLAAoL;YACxL,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACvB,OAAO;YACL,GAAG,EAAE,8EAA8E;YACnF,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC;SACjC,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,sBAAsB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACpD,OAAO;QACL,GAAG,EAAE,uGAAuG;QAC5G,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,GAAG,EAAE,QAAQ,CAAC;KACnD,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAChC,SAAiB,EACjB,QAAgB,EAChB,SAAyC;IAEzC,MAAM,QAAQ,GAAG,SAAS,CAAC,aAAa,CAAC;IACzC,MAAM,SAAS,GACb,QAAQ,KAAK,IAAI;QACf,CAAC,CAAC,+EAA+E;QACjF,CAAC,CAAC,yFAAyF,CAAC;IAChG,OAAO;QACL,GAAG,EAAE,sGAAsG,SAAS,GAAG;QACvH,IAAI,EACF,QAAQ,KAAK,IAAI;YACf,CAAC,CAAC,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,SAAS,CAAC,GAAG,CAAC;YAC7D,CAAC,CAAC;gBACE,SAAS;gBACT,QAAQ;gBACR,IAAI,CAAC,GAAG,EAAE;gBACV,SAAS;gBACT,SAAS,CAAC,GAAG;gBACb,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;aACzB;KACR,CAAC;AACJ,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAc;IAC/C,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,OAAO,sFAAsF,CAAC,IAAI,CAChG,OAAO,CACR,CAAC;AACJ,CAAC;AAED,SAAS,sBAAsB,CAC7B,GAAW,EACX,KAA8B;IAE9B,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IACzC,IACE,CAAC,eAAe,EAAE;QAClB,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,GAAG,gCAAgC,EACxE,CAAC;QACD,MAAM,IAAI,KAAK,CACb,4BAA4B,GAAG,4HAA4H,CAC5J,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,SAAiB,EACjB,UAAqD,EACrD,OAA2B;IAE3B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,IAAI,IAAI,CAAC,IAAI,KAAK,UAAU,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;IAC3E,CAAC;IACD,KAAK,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5C,IAAI,SAAS;YAAE,sBAAsB,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACxD,CAAC;IACD,MAAM,iBAAiB,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACtD,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAC3B,CAAC;IAEF,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,IAAI,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CACb,yDAAyD,CAC1D,CAAC;QACJ,CAAC;QACD,MAAM,QAAQ,GAAG,8BAA8B,IAAI,CAAC,GAAG,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;QACnG,MAAM,WAAW,GAAG;YAClB,GAAG,EAAE,2FAA2F;YAChG,IAAI,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,CAAC;SACxC,CAAC;QACF,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACjD,yBAAyB,CAAC,SAAS,EAAE,QAAQ,EAAE,SAAS,CAAC,CAC1D,CAAC;QACF,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CACpD,mCAAmC,CACjC,SAAS,EACT,SAAS,CAAC,GAAG,EACb,SAAS,CAAC,aAAa,EACvB,SAAS,CAAC,SAAS,CACpB,CACF,CAAC;QACF,IAAI,OAAO,CAAC;QACZ,IAAI,CAAC;YACH,OAAO,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC;gBACjC,WAAW;gBACX,GAAG,MAAM;gBACT,GAAG,SAAS;gBACZ;oBACE,GAAG,EAAE,gEAAgE;oBACrE,IAAI,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC;iBAC5B;aACF,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,yBAAyB,CAAC,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YACnD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,eAAe,GAAG,OAAO,CAAC,KAAK,CACnC,CAAC,GAAG,MAAM,CAAC,MAAM,EACjB,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC,MAAM,CACrC,CAAC;QACF,IACE,eAAe,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM;YAC3C,eAAe,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,YAAY,KAAK,CAAC,CAAC,EAC3D,CAAC;YACD,MAAM,IAAI,KAAK,CACb,qEAAqE,CACtE,CAAC;QACJ,CAAC;IACH,CAAC;SAAM,CAAC;QACN,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YACxB,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAC5E,CAAC;QACD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,EAAE,EAAE,EAAE;gBACpC,KAAK,MAAM,SAAS,IAAI,iBAAiB,EAAE,CAAC;oBAC1C,MAAM,OAAO,GAAG,MAAM,4BAA4B,CAChD,EAAE,EACF,SAAS,EACT,SAAS,CAAC,GAAG,EACb,SAAS,CAAC,aAAa,EACvB,SAAS,CAAC,SAAS,CACpB,CAAC;oBACF,IAAI,CAAC,OAAO;wBAAE,MAAM,sBAAsB,CAAC;gBAC7C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,KAAK,sBAAsB;gBAAE,OAAO,KAAK,CAAC;YACnD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED,KAAK,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,IAAI,UAAU,EAAE,CAAC;QAC5C,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;YACvB,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;aAAM,CAAC;YACN,kBAAkB,CAAC,GAAG,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,SAAiB;IAEjB,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,yFAAyF;QAC9F,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;KAC/C,CAAC,CAAC;IACH,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxB,GAAG,EAAE,GAAG,CAAC,GAAa;QACtB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAe,CAAC;KACvC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,SAAiB,EACjB,SAAiB,EACjB,OAA2B;IAE3B,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,uCAAuC;IACvC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,kFAAkF;QACvF,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;KAC/C,CAAC,CAAC;IAEH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEhC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE,8EAA8E;QACnF,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,SAAS,CAAC,GAAG,GAAG,CAAC;KAC/C,CAAC,CAAC;IAEH,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,kBAAkB,CAAC,GAAG,CAAC,GAAa,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC;IAC3E,CAAC;IAED,OAAO,MAAM,CAAC,YAAY,CAAC;AAC7B,CAAC","sourcesContent":["import {\n getDbExec,\n getDialect,\n isLocalDatabase,\n isPostgres,\n intType,\n type DbExec,\n} from \"../db/client.js\";\nimport { ensureIndexExists, ensureTableExists } from \"../db/ddl-guard.js\";\nimport { widenIntColumnsToBigInt } from \"../db/widen-columns.js\";\nimport type { StoreWriteOptions } from \"../settings/store.js\";\nimport { emitAppStateChange, emitAppStateDelete } from \"./emitter.js\";\n\nlet _initPromise: Promise<void> | undefined;\nconst MAX_HOSTED_APP_STATE_VALUE_BYTES = 1024 * 1024;\n\n// Escapes LIKE wildcards (`%`, `_`) and the escape char itself so a caller's\n// literal prefix is matched verbatim. Used with `ESCAPE '!'` in prefix queries\n// below; without this, a prefix such as `user_settings` would treat `_` as a\n// single-char wildcard and over-match (e.g. delete `userXsettings`).\nfunction escapeLike(s: string): string {\n return s.replace(/[!%_]/g, (match) => `!${match}`);\n}\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS application_state (\n session_id TEXT NOT NULL,\n key TEXT NOT NULL,\n value TEXT NOT NULL,\n updated_at ${intType()} NOT NULL,\n PRIMARY KEY (session_id, key)\n )\n `;\n\n if (isPostgres()) {\n // Hot path: the `application_state` table and its poll indexes are\n // virtually always already present in production. Issuing `CREATE\n // TABLE`/ `CREATE INDEX` still takes a lock that, in a fresh\n // background-worker process behind a concurrent connection on the\n // shared Neon DB, can block ~indefinitely (ACCESS EXCLUSIVE for CREATE\n // TABLE; a write-blocking SHARE lock for CREATE INDEX). The ensure*\n // wrappers probe `information_schema`/`pg_indexes` first (plain reads,\n // no lock) and run DDL ONLY for what is actually missing, bounded by a\n // transaction-scoped `lock_timeout`. If a swallowed lock-timeout leaves\n // the schema still missing they RE-PROBE and THROW rather than letting\n // init memoize success against absent schema.\n await ensureTableExists(\"application_state\", createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths. Probe pg_indexes first (no lock)\n // and skip the SHARE-locking CREATE INDEX when already present.\n await ensureIndexExists(\n \"app_state_updated_at_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n );\n await ensureIndexExists(\n \"app_state_key_updated_idx\",\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Older deployments created `updated_at` as 32-bit `INTEGER`; on Postgres\n // the `Date.now()` written by appStatePut() on every turn overflows int4.\n // Widen it in place (no-op once done / on fresh BIGINT databases).\n await widenIntColumnsToBigInt(\"application_state\", [\"updated_at\"]);\n // Indexes for the two hot poll paths:\n // - `SELECT … WHERE updated_at > ?` (watermark scan, every poll cycle)\n // - `SELECT … WHERE key = ? … ORDER BY updated_at ASC` (marker lookups)\n // Both are dialect-agnostic (no DESC/partial/PG-only syntax) so they\n // apply identically on SQLite and Postgres. IF NOT EXISTS makes them\n // idempotent across restarts on existing databases.\n for (const ddl of [\n `CREATE INDEX IF NOT EXISTS app_state_updated_at_idx ON application_state (updated_at)`,\n `CREATE INDEX IF NOT EXISTS app_state_key_updated_idx ON application_state (key, updated_at)`,\n ]) {\n try {\n await client.execute(ddl);\n } catch {\n // Index already exists or the dialect rejected a duplicate.\n }\n }\n })().catch((err) => {\n // Retry init on the next call after a failed startup.\n _initPromise = undefined;\n throw err;\n });\n }\n return _initPromise;\n}\n\nexport async function appStateGet(\n sessionId: string,\n key: string,\n): Promise<Record<string, unknown> | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT value FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n if (rows.length === 0) return null;\n return JSON.parse(rows[0].value as string);\n}\n\n/**\n * Read several application-state keys for one session in a single SQL query,\n * returning ONLY the rows that exist. A key absent from the result has no row;\n * a key present with a `null` value has a row that stores `null`. Callers that\n * must not conflate the two (the batched HTTP read) depend on that difference,\n * so this deliberately does not pad missing keys.\n */\nexport async function appStateGetManyEntries(\n sessionId: string,\n keys: readonly string[],\n): Promise<Array<{ key: string; value: Record<string, unknown> | null }>> {\n const uniqueKeys = [...new Set(keys)];\n if (uniqueKeys.length === 0) return [];\n await ensureTable();\n const client = getDbExec();\n const placeholders = uniqueKeys.map(() => \"?\").join(\", \");\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key IN (${placeholders})`,\n args: [sessionId, ...uniqueKeys],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\n/**\n * Read several application-state keys for one session in a single SQL query.\n * Missing keys are returned as `null` so callers can preserve the requested\n * shape without issuing one fallback query per key.\n */\nexport async function appStateGetMany(\n sessionId: string,\n keys: readonly string[],\n): Promise<Record<string, Record<string, unknown> | null>> {\n const values: Record<string, Record<string, unknown> | null> = {};\n for (const key of new Set(keys)) values[key] = null;\n\n for (const entry of await appStateGetManyEntries(sessionId, keys)) {\n values[entry.key] = entry.value;\n }\n return values;\n}\n\nexport async function appStatePut(\n sessionId: string,\n key: string,\n value: Record<string, unknown>,\n options?: StoreWriteOptions,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n await client.execute({\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?) ON CONFLICT (session_id, key) DO UPDATE SET value=EXCLUDED.value, updated_at=EXCLUDED.updated_at`\n : `INSERT OR REPLACE INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, ?, ?)`,\n args: [sessionId, key, serialized, Date.now()],\n });\n emitAppStateChange(key, options?.requestSource, sessionId);\n}\n\nexport async function appStateDelete(\n sessionId: string,\n key: string,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, key],\n });\n const deleted = result.rowsAffected > 0;\n if (deleted) emitAppStateDelete(key, options?.requestSource, sessionId);\n return deleted;\n}\n\nexport async function appStateCompareAndSet(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n options?: StoreWriteOptions,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const changed = await executeAppStateCompareAndSet(\n client,\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n if (changed) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return changed;\n}\n\nexport interface AppStateCompareAndSetOperation {\n key: string;\n expectedValue: Record<string, unknown> | null;\n nextValue: Record<string, unknown> | null;\n}\n\nconst APP_STATE_CAS_MISMATCH = Symbol(\"app-state-cas-mismatch\");\n\nasync function executeAppStateCompareAndSet(\n client: DbExec,\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): Promise<boolean> {\n const statement = buildAppStateCompareAndSetStatement(\n sessionId,\n key,\n expectedValue,\n nextValue,\n );\n const result = await client.execute(statement);\n return result.rowsAffected > 0;\n}\n\nfunction buildAppStateCompareAndSetStatement(\n sessionId: string,\n key: string,\n expectedValue: Record<string, unknown> | null,\n nextValue: Record<string, unknown> | null,\n): { sql: string; args: unknown[] } {\n if (expectedValue === null) {\n if (nextValue === null) {\n throw new Error(\n \"Application state CAS cannot replace absence with absence.\",\n );\n }\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: isPostgres()\n ? `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?) ON CONFLICT (session_id, key) DO NOTHING`\n : `INSERT OR IGNORE INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, ?, ? WHERE NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)`,\n args: [sessionId, key, next, Date.now(), sessionId, key],\n };\n }\n\n const expected = JSON.stringify(expectedValue);\n if (nextValue === null) {\n return {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ? AND value = ?`,\n args: [sessionId, key, expected],\n };\n }\n\n const next = serializeAppStateValue(key, nextValue);\n return {\n sql: `UPDATE application_state SET value = ?, updated_at = ? WHERE session_id = ? AND key = ? AND value = ?`,\n args: [next, Date.now(), sessionId, key, expected],\n };\n}\n\nfunction buildD1CompareAndSetGuard(\n sessionId: string,\n guardKey: string,\n operation: AppStateCompareAndSetOperation,\n): { sql: string; args: unknown[] } {\n const expected = operation.expectedValue;\n const condition =\n expected === null\n ? \"NOT EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ?)\"\n : \"EXISTS (SELECT 1 FROM application_state WHERE session_id = ? AND key = ? AND value = ?)\";\n return {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) SELECT ?, ?, '{}', ? WHERE NOT (${condition})`,\n args:\n expected === null\n ? [sessionId, guardKey, Date.now(), sessionId, operation.key]\n : [\n sessionId,\n guardKey,\n Date.now(),\n sessionId,\n operation.key,\n JSON.stringify(expected),\n ],\n };\n}\n\nfunction isD1CompareAndSetMismatch(error: unknown): boolean {\n const message = error instanceof Error ? error.message : String(error);\n return /unique constraint failed:\\s*application_state\\.session_id,\\s*application_state\\.key/i.test(\n message,\n );\n}\n\nfunction serializeAppStateValue(\n key: string,\n value: Record<string, unknown>,\n): string {\n const serialized = JSON.stringify(value);\n if (\n !isLocalDatabase() &&\n Buffer.byteLength(serialized, \"utf8\") > MAX_HOSTED_APP_STATE_VALUE_BYTES\n ) {\n throw new Error(\n `application_state value \"${key}\" is too large for hosted SQL storage. Store large files, base64, or blobs in file storage and write only a URL or handle.`,\n );\n }\n return serialized;\n}\n\nexport async function appStateCompareAndSetMany(\n sessionId: string,\n operations: readonly AppStateCompareAndSetOperation[],\n options?: StoreWriteOptions,\n): Promise<boolean> {\n if (operations.length === 0) return true;\n const keys = new Set(operations.map(({ key }) => key));\n if (keys.size !== operations.length) {\n throw new Error(\"Application state multi-key CAS requires unique keys.\");\n }\n for (const { key, nextValue } of operations) {\n if (nextValue) serializeAppStateValue(key, nextValue);\n }\n const orderedOperations = [...operations].sort((a, b) =>\n a.key.localeCompare(b.key),\n );\n\n await ensureTable();\n const client = getDbExec();\n if (getDialect() === \"d1\") {\n if (!client.atomicBatch) {\n throw new Error(\n \"D1 application-state CAS requires atomic batch support.\",\n );\n }\n const guardKey = `__agent_native_cas_guard__:${Date.now()}:${Math.random().toString(36).slice(2)}`;\n const guardInsert = {\n sql: `INSERT INTO application_state (session_id, key, value, updated_at) VALUES (?, ?, '{}', ?)`,\n args: [sessionId, guardKey, Date.now()],\n };\n const guards = orderedOperations.map((operation) =>\n buildD1CompareAndSetGuard(sessionId, guardKey, operation),\n );\n const mutations = orderedOperations.map((operation) =>\n buildAppStateCompareAndSetStatement(\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n ),\n );\n let results;\n try {\n results = await client.atomicBatch([\n guardInsert,\n ...guards,\n ...mutations,\n {\n sql: `DELETE FROM application_state WHERE session_id = ? AND key = ?`,\n args: [sessionId, guardKey],\n },\n ]);\n } catch (error) {\n if (isD1CompareAndSetMismatch(error)) return false;\n throw error;\n }\n const mutationResults = results.slice(\n 1 + guards.length,\n 1 + guards.length + mutations.length,\n );\n if (\n mutationResults.length !== mutations.length ||\n mutationResults.some((result) => result.rowsAffected !== 1)\n ) {\n throw new Error(\n \"D1 application-state CAS completed without applying every mutation.\",\n );\n }\n } else {\n if (!client.transaction) {\n throw new Error(\"Application state multi-key CAS requires transactions.\");\n }\n try {\n await client.transaction(async (tx) => {\n for (const operation of orderedOperations) {\n const changed = await executeAppStateCompareAndSet(\n tx,\n sessionId,\n operation.key,\n operation.expectedValue,\n operation.nextValue,\n );\n if (!changed) throw APP_STATE_CAS_MISMATCH;\n }\n });\n } catch (error) {\n if (error === APP_STATE_CAS_MISMATCH) return false;\n throw error;\n }\n }\n\n for (const { key, nextValue } of operations) {\n if (nextValue === null) {\n emitAppStateDelete(key, options?.requestSource, sessionId);\n } else {\n emitAppStateChange(key, options?.requestSource, sessionId);\n }\n }\n return true;\n}\n\nexport async function appStateList(\n sessionId: string,\n keyPrefix: string,\n): Promise<Array<{ key: string; value: Record<string, unknown> }>> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT key, value FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n return rows.map((row) => ({\n key: row.key as string,\n value: JSON.parse(row.value as string),\n }));\n}\n\nexport async function appStateDeleteByPrefix(\n sessionId: string,\n keyPrefix: string,\n options?: StoreWriteOptions,\n): Promise<number> {\n await ensureTable();\n const client = getDbExec();\n\n // Get keys first so we can emit events\n const { rows } = await client.execute({\n sql: `SELECT key FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n if (rows.length === 0) return 0;\n\n const result = await client.execute({\n sql: `DELETE FROM application_state WHERE session_id = ? AND key LIKE ? ESCAPE '!'`,\n args: [sessionId, escapeLike(keyPrefix) + \"%\"],\n });\n\n for (const row of rows) {\n emitAppStateDelete(row.key as string, options?.requestSource, sessionId);\n }\n\n return result.rowsAffected;\n}\n"]}
@@ -12,16 +12,39 @@
12
12
  */
13
13
  /** Heuristic: does a bare string value look like a secret? */
14
14
  declare function looksSecret(value: string): boolean;
15
- declare function redact(value: unknown, depth: number): unknown;
15
+ declare function redactEmbeddedSecrets(value: string): string;
16
+ declare function redact(value: unknown, depth: number, maxString: number): unknown;
17
+ export interface RedactLimits {
18
+ /**
19
+ * Hard cap on the serialized output, INCLUDING the truncation envelope.
20
+ * Surfaces with their own wire budget (the A2A activity snapshot) pass a
21
+ * tighter cap than the audit-log default.
22
+ */
23
+ maxJson?: number;
24
+ /** Cap on any single string value before it gets a truncation marker. */
25
+ maxString?: number;
26
+ }
16
27
  /**
17
28
  * Redact and serialize call arguments to a capped JSON string, or `null` when
18
29
  * there is nothing to record. Never throws.
19
30
  */
20
- export declare function redactArgsToJson(args: unknown): string | null;
31
+ export declare function redactArgsToJson(args: unknown, limits?: RedactLimits): string | null;
32
+ /**
33
+ * Same bounds as `redactArgsToJson`, returned as a structural value for
34
+ * surfaces whose consumers expect an object (transcript `metadata.input`).
35
+ */
36
+ export declare function redactArgsToValue(args: unknown, limits?: RedactLimits): unknown;
37
+ /**
38
+ * Bounded, redacted plain-text summary of a tool result. Oversized text keeps
39
+ * its head and gains an explicit `…(N more chars)` marker, so a reader can
40
+ * always tell a short result from a clipped one.
41
+ */
42
+ export declare function redactTextToSummary(text: string, maxChars?: number): string | null;
21
43
  /** Exposed for tests. */
22
44
  export declare const __test: {
23
45
  looksSecret: typeof looksSecret;
24
46
  redact: typeof redact;
47
+ redactEmbeddedSecrets: typeof redactEmbeddedSecrets;
25
48
  SENSITIVE_KEY: RegExp;
26
49
  };
27
50
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/audit/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAYH,8DAA8D;AAC9D,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkB3C;AAUD,iBAAS,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CA0BtD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAqB7D;AAED,yBAAyB;AACzB,eAAO,MAAM,MAAM;;;;CAAyC,CAAC"}
1
+ {"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../src/audit/redact.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiBH,8DAA8D;AAC9D,iBAAS,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAkB3C;AAUD,iBAAS,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOpD;AAED,iBAAS,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CA8BzE;AAED,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yEAAyE;IACzE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,YAAY,GACpB,MAAM,GAAG,IAAI,CA+Bf;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,OAAO,EACb,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAQT;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,MAAM,EACZ,QAAQ,SAAa,GACpB,MAAM,GAAG,IAAI,CAYf;AAED,yBAAyB;AACzB,eAAO,MAAM,MAAM;;;;;CAKlB,CAAC"}