@agent-native/core 0.77.7 → 0.77.10

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 (192) hide show
  1. package/corpus/README.md +2 -2
  2. package/corpus/core/CHANGELOG.md +46 -0
  3. package/corpus/core/package.json +1 -1
  4. package/corpus/core/src/a2a/task-store.ts +25 -3
  5. package/corpus/core/src/agent/harness/store.ts +89 -55
  6. package/corpus/core/src/agent/observational-memory/store.ts +40 -14
  7. package/corpus/core/src/agent/production-agent.ts +18 -0
  8. package/corpus/core/src/agent/run-store.ts +116 -59
  9. package/corpus/core/src/application-state/store.ts +36 -2
  10. package/corpus/core/src/audit/store.ts +37 -6
  11. package/corpus/core/src/browser-sessions/store.ts +31 -12
  12. package/corpus/core/src/chat-threads/store.ts +67 -3
  13. package/corpus/core/src/checkpoints/store.ts +14 -3
  14. package/corpus/core/src/cli/create.ts +9 -0
  15. package/corpus/core/src/collab/storage.ts +20 -2
  16. package/corpus/core/src/db/ddl-guard.ts +302 -0
  17. package/corpus/core/src/extensions/store.ts +76 -3
  18. package/corpus/core/src/integrations/a2a-continuations-store.ts +69 -26
  19. package/corpus/core/src/integrations/config-store.ts +19 -18
  20. package/corpus/core/src/integrations/pending-tasks-store.ts +40 -16
  21. package/corpus/core/src/integrations/remote-commands-store.ts +37 -23
  22. package/corpus/core/src/integrations/remote-devices-store.ts +64 -20
  23. package/corpus/core/src/integrations/remote-push-store.ts +75 -35
  24. package/corpus/core/src/integrations/remote-run-events-store.ts +38 -12
  25. package/corpus/core/src/integrations/thread-mapping-store.ts +11 -2
  26. package/corpus/core/src/mcp/connect-store.ts +53 -16
  27. package/corpus/core/src/mcp/oauth-store.ts +32 -7
  28. package/corpus/core/src/notifications/store.ts +20 -4
  29. package/corpus/core/src/oauth-tokens/store.ts +45 -2
  30. package/corpus/core/src/observability/store.ts +154 -47
  31. package/corpus/core/src/progress/store.ts +36 -21
  32. package/corpus/core/src/provider-api/corpus-jobs-store.ts +25 -5
  33. package/corpus/core/src/provider-api/custom-registry.ts +15 -4
  34. package/corpus/core/src/provider-api/quota-governor.ts +15 -3
  35. package/corpus/core/src/provider-api/staged-datasets-store.ts +22 -5
  36. package/corpus/core/src/resources/store.ts +66 -31
  37. package/corpus/core/src/secrets/storage.ts +32 -2
  38. package/corpus/core/src/server/agent-teams-run-queue.ts +22 -10
  39. package/corpus/core/src/server/auth.ts +22 -4
  40. package/corpus/core/src/server/better-auth-instance.ts +49 -15
  41. package/corpus/core/src/server/embed-session.ts +19 -4
  42. package/corpus/core/src/server/identity-sso-store.ts +40 -11
  43. package/corpus/core/src/server/recap-image-store.ts +28 -9
  44. package/corpus/core/src/settings/store.ts +35 -6
  45. package/corpus/core/src/usage/store.ts +51 -16
  46. package/corpus/core/src/workspace-connections/store.ts +200 -22
  47. package/corpus/templates/calendar/changelog/2026-06-25-booking-link-previews-now-show-the-content-directly-on-the-g.md +6 -0
  48. package/corpus/templates/calendar/server/lib/booking-og-image.ts +0 -2
  49. package/corpus/templates/forms/changelog/2026-06-25-form-previews-now-show-the-content-directly-on-the-grid-back.md +6 -0
  50. package/corpus/templates/forms/server/lib/form-og-image.ts +0 -2
  51. package/corpus/templates/plan/app/global.css +6 -11
  52. package/corpus/templates/plan/app/pages/PlansPage.tsx +12 -10
  53. package/corpus/templates/plan/changelog/2026-06-25-plan-access-request-pages-stay-visible-during-background-ref.md +6 -0
  54. package/corpus/templates/plan/changelog/2026-06-25-plan-documents-keep-their-full-reading-width-below-1200px-an.md +6 -0
  55. package/dist/a2a/task-store.d.ts.map +1 -1
  56. package/dist/a2a/task-store.js +19 -3
  57. package/dist/a2a/task-store.js.map +1 -1
  58. package/dist/agent/harness/store.d.ts.map +1 -1
  59. package/dist/agent/harness/store.js +63 -34
  60. package/dist/agent/harness/store.js.map +1 -1
  61. package/dist/agent/observational-memory/store.d.ts.map +1 -1
  62. package/dist/agent/observational-memory/store.js +30 -8
  63. package/dist/agent/observational-memory/store.js.map +1 -1
  64. package/dist/agent/production-agent.d.ts.map +1 -1
  65. package/dist/agent/production-agent.js +18 -0
  66. package/dist/agent/production-agent.js.map +1 -1
  67. package/dist/agent/run-store.d.ts.map +1 -1
  68. package/dist/agent/run-store.js +103 -46
  69. package/dist/agent/run-store.js.map +1 -1
  70. package/dist/application-state/store.d.ts.map +1 -1
  71. package/dist/application-state/store.js +28 -2
  72. package/dist/application-state/store.js.map +1 -1
  73. package/dist/audit/store.d.ts.map +1 -1
  74. package/dist/audit/store.js +18 -3
  75. package/dist/audit/store.js.map +1 -1
  76. package/dist/browser-sessions/store.d.ts.map +1 -1
  77. package/dist/browser-sessions/store.js +22 -7
  78. package/dist/browser-sessions/store.js.map +1 -1
  79. package/dist/chat-threads/store.d.ts.map +1 -1
  80. package/dist/chat-threads/store.js +51 -3
  81. package/dist/chat-threads/store.js.map +1 -1
  82. package/dist/checkpoints/store.d.ts.map +1 -1
  83. package/dist/checkpoints/store.js +12 -3
  84. package/dist/checkpoints/store.js.map +1 -1
  85. package/dist/cli/create.d.ts.map +1 -1
  86. package/dist/cli/create.js +9 -0
  87. package/dist/cli/create.js.map +1 -1
  88. package/dist/collab/routes.d.ts +2 -2
  89. package/dist/collab/storage.d.ts.map +1 -1
  90. package/dist/collab/storage.js +14 -2
  91. package/dist/collab/storage.js.map +1 -1
  92. package/dist/db/ddl-guard.d.ts +151 -0
  93. package/dist/db/ddl-guard.d.ts.map +1 -0
  94. package/dist/db/ddl-guard.js +265 -0
  95. package/dist/db/ddl-guard.js.map +1 -0
  96. package/dist/extensions/store.d.ts.map +1 -1
  97. package/dist/extensions/store.js +32 -3
  98. package/dist/extensions/store.js.map +1 -1
  99. package/dist/file-upload/actions/upload-image.d.ts +2 -2
  100. package/dist/integrations/a2a-continuations-store.d.ts.map +1 -1
  101. package/dist/integrations/a2a-continuations-store.js +44 -24
  102. package/dist/integrations/a2a-continuations-store.js.map +1 -1
  103. package/dist/integrations/config-store.d.ts.map +1 -1
  104. package/dist/integrations/config-store.js +17 -11
  105. package/dist/integrations/config-store.js.map +1 -1
  106. package/dist/integrations/pending-tasks-store.d.ts.map +1 -1
  107. package/dist/integrations/pending-tasks-store.js +24 -16
  108. package/dist/integrations/pending-tasks-store.js.map +1 -1
  109. package/dist/integrations/remote-commands-store.d.ts.map +1 -1
  110. package/dist/integrations/remote-commands-store.js +29 -21
  111. package/dist/integrations/remote-commands-store.js.map +1 -1
  112. package/dist/integrations/remote-devices-store.d.ts.map +1 -1
  113. package/dist/integrations/remote-devices-store.js +33 -18
  114. package/dist/integrations/remote-devices-store.js.map +1 -1
  115. package/dist/integrations/remote-push-store.d.ts.map +1 -1
  116. package/dist/integrations/remote-push-store.js +53 -31
  117. package/dist/integrations/remote-push-store.js.map +1 -1
  118. package/dist/integrations/remote-run-events-store.d.ts.map +1 -1
  119. package/dist/integrations/remote-run-events-store.js +26 -10
  120. package/dist/integrations/remote-run-events-store.js.map +1 -1
  121. package/dist/integrations/thread-mapping-store.d.ts.map +1 -1
  122. package/dist/integrations/thread-mapping-store.js +10 -2
  123. package/dist/integrations/thread-mapping-store.js.map +1 -1
  124. package/dist/mcp/connect-store.d.ts.map +1 -1
  125. package/dist/mcp/connect-store.js +34 -16
  126. package/dist/mcp/connect-store.js.map +1 -1
  127. package/dist/mcp/oauth-store.d.ts.map +1 -1
  128. package/dist/mcp/oauth-store.js +22 -7
  129. package/dist/mcp/oauth-store.js.map +1 -1
  130. package/dist/notifications/routes.d.ts +2 -2
  131. package/dist/notifications/store.d.ts.map +1 -1
  132. package/dist/notifications/store.js +15 -3
  133. package/dist/notifications/store.js.map +1 -1
  134. package/dist/oauth-tokens/store.d.ts.map +1 -1
  135. package/dist/oauth-tokens/store.js +33 -2
  136. package/dist/oauth-tokens/store.js.map +1 -1
  137. package/dist/observability/routes.d.ts +7 -7
  138. package/dist/observability/store.d.ts.map +1 -1
  139. package/dist/observability/store.js +72 -29
  140. package/dist/observability/store.js.map +1 -1
  141. package/dist/progress/store.d.ts.map +1 -1
  142. package/dist/progress/store.js +31 -16
  143. package/dist/progress/store.js.map +1 -1
  144. package/dist/provider-api/corpus-jobs-store.d.ts.map +1 -1
  145. package/dist/provider-api/corpus-jobs-store.js +16 -5
  146. package/dist/provider-api/corpus-jobs-store.js.map +1 -1
  147. package/dist/provider-api/custom-registry.d.ts.map +1 -1
  148. package/dist/provider-api/custom-registry.js +14 -4
  149. package/dist/provider-api/custom-registry.js.map +1 -1
  150. package/dist/provider-api/quota-governor.d.ts.map +1 -1
  151. package/dist/provider-api/quota-governor.js +12 -3
  152. package/dist/provider-api/quota-governor.js.map +1 -1
  153. package/dist/provider-api/staged-datasets-store.d.ts.map +1 -1
  154. package/dist/provider-api/staged-datasets-store.js +16 -5
  155. package/dist/provider-api/staged-datasets-store.js.map +1 -1
  156. package/dist/resources/handlers.d.ts +2 -2
  157. package/dist/resources/store.d.ts.map +1 -1
  158. package/dist/resources/store.js +56 -25
  159. package/dist/resources/store.js.map +1 -1
  160. package/dist/secrets/storage.d.ts.map +1 -1
  161. package/dist/secrets/storage.js +22 -2
  162. package/dist/secrets/storage.js.map +1 -1
  163. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  164. package/dist/server/agent-teams-run-queue.d.ts.map +1 -1
  165. package/dist/server/agent-teams-run-queue.js +15 -4
  166. package/dist/server/agent-teams-run-queue.js.map +1 -1
  167. package/dist/server/auth.d.ts.map +1 -1
  168. package/dist/server/auth.js +16 -2
  169. package/dist/server/auth.js.map +1 -1
  170. package/dist/server/better-auth-instance.d.ts.map +1 -1
  171. package/dist/server/better-auth-instance.js +55 -21
  172. package/dist/server/better-auth-instance.js.map +1 -1
  173. package/dist/server/embed-session.d.ts.map +1 -1
  174. package/dist/server/embed-session.js +15 -4
  175. package/dist/server/embed-session.js.map +1 -1
  176. package/dist/server/identity-sso-store.d.ts.map +1 -1
  177. package/dist/server/identity-sso-store.js +30 -11
  178. package/dist/server/identity-sso-store.js.map +1 -1
  179. package/dist/server/recap-image-store.d.ts.map +1 -1
  180. package/dist/server/recap-image-store.js +21 -7
  181. package/dist/server/recap-image-store.js.map +1 -1
  182. package/dist/server/transcribe-voice.d.ts +1 -1
  183. package/dist/settings/store.d.ts.map +1 -1
  184. package/dist/settings/store.js +30 -6
  185. package/dist/settings/store.js.map +1 -1
  186. package/dist/usage/store.d.ts.map +1 -1
  187. package/dist/usage/store.js +36 -11
  188. package/dist/usage/store.js.map +1 -1
  189. package/dist/workspace-connections/store.d.ts.map +1 -1
  190. package/dist/workspace-connections/store.js +57 -7
  191. package/dist/workspace-connections/store.js.map +1 -1
  192. package/package.json +1 -1
@@ -486,8 +486,10 @@
486
486
  padding-inline-start: 0.75rem;
487
487
  }
488
488
 
489
- /* Narrow-width stand-in for the contents rail; swapped in/out by the queries below. */
489
+ /* Hidden below the rail breakpoint: the document should use the full reading
490
+ * width instead of replacing the rail with an inline contents widget. */
490
491
  .plan-document-toc-inline {
492
+ display: none;
491
493
  margin-top: 1.25rem;
492
494
  border: 1px solid var(--plan-line);
493
495
  border-radius: 0.75rem;
@@ -548,23 +550,16 @@
548
550
  list-style: none;
549
551
  }
550
552
 
551
- /* Hide the accordion once its rail shows, and in screenshot capture. */
553
+ /* Hide the accordion in screenshot capture too. */
552
554
  .plan-content-surface[data-recap-screenshot-theme] .plan-document-toc-inline {
553
555
  display: none;
554
556
  }
555
- @container plan-doc (min-width: 48rem) {
556
- .plan-content-surface:not([data-recap-screenshot-theme])
557
- .plan-document-body[data-has-toc]
558
- > .plan-document-toc-inline {
559
- display: none;
560
- }
561
- }
562
557
 
563
558
  /* Wide layout: the contents rail is a real grid column. A matching empty left
564
559
  * column balances the rail so the document itself remains visually centered. */
565
560
 
566
561
  /* Shared cell placement. Inert until the parent is a grid. */
567
- @container plan-doc (min-width: 48rem) {
562
+ @container plan-doc (min-width: 1200px) {
568
563
  .plan-content-surface:not([data-recap-screenshot-theme])
569
564
  .plan-document-body
570
565
  > header {
@@ -588,7 +583,7 @@
588
583
  }
589
584
 
590
585
  /* Document + contents rail. The empty first column balances the right rail. */
591
- @container plan-doc (min-width: 48rem) {
586
+ @container plan-doc (min-width: 1200px) {
592
587
  .plan-content-surface:not([data-recap-screenshot-theme])
593
588
  .plan-document-body[data-has-toc] {
594
589
  display: grid;
@@ -593,22 +593,25 @@ export function shouldShowPlanLoadError(input: {
593
593
  hasSelectedId: boolean;
594
594
  localPlanMode: boolean;
595
595
  hasBundle: boolean;
596
- planQueryPending: boolean;
596
+ planQueryInitialPending: boolean;
597
597
  planQueryError: boolean;
598
598
  planQueryPaused: boolean;
599
- accessStatusPending: boolean;
599
+ accessStatusInitialPending: boolean;
600
600
  accessStatusPaused: boolean;
601
601
  accessDenied: boolean;
602
602
  }): boolean {
603
603
  if (!input.hasSelectedId || input.localPlanMode || input.hasBundle) {
604
604
  return false;
605
605
  }
606
- // While a read is actively in flight, keep showing the skeleton.
607
- if (input.planQueryPending) return false;
608
606
  if (input.planQueryError) return true;
607
+ if (!input.accessStatusInitialPending && input.accessDenied) return true;
608
+ // While the first read is actively in flight, keep showing the skeleton.
609
+ // Background refetches must not hide a settled access/error card.
610
+ if (input.planQueryInitialPending || input.accessStatusInitialPending) {
611
+ return false;
612
+ }
609
613
  // Paused/stalled read that will never settle on its own input.
610
614
  if (input.planQueryPaused || input.accessStatusPaused) return true;
611
- if (!input.accessStatusPending && input.accessDenied) return true;
612
615
  return false;
613
616
  }
614
617
 
@@ -3120,17 +3123,16 @@ export function PlansPage({ localPlanSlug }: { localPlanSlug?: string } = {}) {
3120
3123
  Boolean(selectedId && !bundle && !localPlanMode),
3121
3124
  );
3122
3125
  const planAccessStatus = planAccessStatusQuery.data ?? null;
3123
- const planQueryPending = planQuery.isLoading || planQuery.isFetching;
3124
- const planAccessStatusPending =
3125
- planAccessStatusQuery.isLoading || planAccessStatusQuery.isFetching;
3126
+ const planQueryInitialPending = planQuery.isLoading;
3127
+ const planAccessStatusInitialPending = planAccessStatusQuery.isLoading;
3126
3128
  const showPlanLoadError = shouldShowPlanLoadError({
3127
3129
  hasSelectedId: Boolean(selectedId),
3128
3130
  localPlanMode,
3129
3131
  hasBundle: Boolean(bundle),
3130
- planQueryPending,
3132
+ planQueryInitialPending,
3131
3133
  planQueryError: planQuery.isError,
3132
3134
  planQueryPaused: planQuery.isPaused,
3133
- accessStatusPending: planAccessStatusPending,
3135
+ accessStatusInitialPending: planAccessStatusInitialPending,
3134
3136
  accessStatusPaused: planAccessStatusQuery.isPaused,
3135
3137
  accessDenied: Boolean(planAccessStatus && !planAccessStatus.hasAccess),
3136
3138
  });
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-06-25
4
+ ---
5
+
6
+ Plan access request pages stay visible during background refreshes instead of flashing back to loading.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-06-25
4
+ ---
5
+
6
+ Plan documents keep their full reading width below 1200px and show the contents rail only on wider screens.
@@ -1 +1 @@
1
- {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AA2ErE,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAwBtB;AAED,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CAAC,CAeR;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7E;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAazE;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAS9D;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE;IACN,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;CACxB,GACA,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA4CtB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAgBnE"}
1
+ {"version":3,"file":"task-store.d.ts","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAgGrE,wBAAsB,UAAU,CAC9B,OAAO,EAAE,OAAO,EAChB,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,GACzB,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED;;;;;;;GAOG;AACH,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAUrE;AAED;;;;;;;;GAQG;AACH,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAwBtB;AAED,wBAAsB,uBAAuB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IACjE,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAC;IAC9C,SAAS,EAAE,MAAM,CAAC;CACnB,GAAG,IAAI,CAAC,CAeR;AAED,wBAAsB,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAa7E;AAED,wBAAsB,sBAAsB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAazE;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAiBlB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,MAAM,EACV,gBAAgB,EAAE,MAAM,EACxB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAsBlB;AAED,wBAAsB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAS9D;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,MAAM,EACV,MAAM,EAAE;IACN,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;CACxB,GACA,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CA4CtB;AAED,wBAAsB,uBAAuB,CAC3C,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,GACf,OAAO,CAAC,IAAI,CAAC,CAcf;AAED,wBAAsB,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAgBnE"}
@@ -1,11 +1,12 @@
1
1
  import crypto from "crypto";
2
- import { getDbExec, intType } from "../db/client.js";
2
+ import { getDbExec, intType, isPostgres } from "../db/client.js";
3
+ import { ensureTableExists, ensureColumnExists } from "../db/ddl-guard.js";
3
4
  let _initPromise;
4
5
  async function ensureTable() {
5
6
  if (!_initPromise) {
6
7
  _initPromise = (async () => {
7
8
  const client = getDbExec();
8
- await client.execute(`
9
+ const createSql = `
9
10
  CREATE TABLE IF NOT EXISTS a2a_tasks (
10
11
  id TEXT PRIMARY KEY,
11
12
  context_id TEXT,
@@ -18,7 +19,22 @@ async function ensureTable() {
18
19
  created_at ${intType()} NOT NULL,
19
20
  updated_at ${intType()} NOT NULL
20
21
  )
21
- `);
22
+ `;
23
+ if (isPostgres()) {
24
+ // PG-guard: probe information_schema before issuing DDL to avoid ACCESS
25
+ // EXCLUSIVE lock contention in fresh background-worker processes.
26
+ await ensureTableExists("a2a_tasks", createSql);
27
+ // Additive migration: owner_email column. Bound to the JWT-verified
28
+ // caller at task-creation time so handleGet / handleCancel can reject
29
+ // mismatched callers (the IDOR class fixed in PR #369). Existing rows
30
+ // have NULL owner_email and remain accessible to legacy callers via
31
+ // the legacy-token apiKeyEnv path; new rows are scoped from this point
32
+ // forward.
33
+ await ensureColumnExists("a2a_tasks", "owner_email", `ALTER TABLE a2a_tasks ADD COLUMN IF NOT EXISTS owner_email TEXT`);
34
+ return;
35
+ }
36
+ // SQLite (local dev): no lock problem — keep the original behaviour.
37
+ await client.execute(createSql);
22
38
  // Additive migration: owner_email column. Bound to the JWT-verified
23
39
  // caller at task-creation time so handleGet / handleCancel can reject
24
40
  // mismatched callers (the IDOR class fixed in PR #369). Existing rows
@@ -1 +1 @@
1
- {"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGrD,IAAI,YAAuC,CAAC;AAE5C,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,MAAM,CAAC,OAAO,CAAC;;;;;;;;;;uBAUJ,OAAO,EAAE;uBACT,OAAO,EAAE;;OAEzB,CAAC,CAAC;YACH,oEAAoE;YACpE,sEAAsE;YACtE,sEAAsE;YACtE,oEAAoE;YACpE,uEAAuE;YACvE,WAAW;YACX,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,OAAO,CAClB,mDAAmD,CACpD,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,qEAAqE;YACvE,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,SAAS,WAAW,CAAC,GAAQ;IAC3B,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,SAAS,EAAG,GAAG,CAAC,UAAqB,IAAI,SAAS;QAClD,MAAM,EAAE;YACN,KAAK,EAAE,GAAG,CAAC,YAAyB;YACpC,OAAO,EAAE,GAAG,CAAC,cAAc;gBACzB,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,cAAwB,CAAC;gBAC1C,CAAC,CAAC,SAAS;YACb,SAAS,EAAE,GAAG,CAAC,gBAA0B;SAC1C;QACD,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAiB,CAAC;QAC1C,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAmB,CAAC;QAC9C,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;QACvE,UAAU,EAAG,GAAG,CAAC,WAA6B,IAAI,IAAI;KACvD,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,MAAe;IAC1C,MAAM,YAAY,GAAG,MAMR,CAAC;IACd,OAAO,CACL,YAAY,EAAE,YAAY,IAAI,YAAY,EAAE,QAAQ,IAAI,YAAY,EAAE,KAAK,CAC5E,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,OAAgB,EAChB,SAAkB,EAClB,QAAkC,EAClC,UAA0B;IAE1B,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,EAAE,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAC/B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,MAAM,IAAI,GAAS;QACjB,EAAE;QACF,SAAS;QACT,MAAM,EAAE,EAAE,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE;QACzC,OAAO,EAAE,CAAC,OAAO,CAAC;QAClB,SAAS,EAAE,EAAE;QACb,QAAQ;KACT,CAAC;IAEF,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,iLAAiL;QACtL,IAAI,EAAE;YACJ,EAAE;YACF,SAAS,IAAI,IAAI;YACjB,WAAW;YACX,SAAS;YACT,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC;YACzB,IAAI;YACJ,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI;YAC1C,UAAU,IAAI,IAAI;YAClB,GAAG;YACH,GAAG;SACJ;KACF,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,EAAU;IAC3C,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,gDAAgD;QACrD,IAAI,EAAE,CAAC,EAAE,CAAC;KACX,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,UAAU,GAAI,IAAI,CAAC,CAAC,CAAS,CAAC,WAAW,CAAC;IAChD,OAAO,OAAO,UAAU,KAAK,QAAQ,IAAI,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC;AAC1E,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,EAAU;IAEV,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAE3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;;;yDAKgD;QACrD,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC;KAC3B,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,IAAI,QAAQ,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEhC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,sCAAsC;QAC3C,IAAI,EAAE,CAAC,EAAE,CAAC;KACX,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAAC,EAAU;IAMtD,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,2EAA2E;QAChF,IAAI,EAAE,CAAC,EAAE,CAAC;KACX,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAQ,CAAC;IAC3B,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAY;QACpB,WAAW,EAAE,GAAG,CAAC,YAAsB;QACvC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAkB,CAAC,CAAC,CAAC,CAAC,SAAS;QACvE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,IAAI,CAAC,CAAC;KACvC,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAAC,EAAU;IACzD,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;yDAGgD;QACrD,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;KAChB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,EAAU;IACrD,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;4CAGmC;QACxC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC;KAChB,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,EAAU,EACV,gBAAwB;IAExB,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;;;;gCAMuB;QAC5B,IAAI,EAAE,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,gBAAgB,CAAC;KAC7C,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAU,EACV,gBAAwB,EACxB,MAAc;IAEd,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,OAAO,GAAY;QACvB,IAAI,EAAE,OAAO;QACb,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;KACxC,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QAClC,GAAG,EAAE;;;;;;;gCAOuB;QAC5B,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,EAAE,gBAAgB,CAAC;KACtE,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO,QAAQ,KAAK,CAAC,CAAC;AACxB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,EAAU;IACtC,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,sCAAsC;QAC3C,IAAI,EAAE,CAAC,EAAE,CAAC;KACX,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,EAAU,EACV,MAIC;IAED,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,oBAAoB;IACpB,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;QACpC,GAAG,EAAE,sCAAsC;QAC3C,IAAI,EAAE,CAAC,EAAE,CAAC;KACX,CAAC,CAAC;IACH,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,IAAI,CAAC,MAAM,GAAG;YACZ,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO;YAC9C,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC;IACJ,CAAC;IAED,IAAI,MAAM,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;QACnC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QACrB,IAAI,CAAC,SAAS,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,IAAI,EAAE,CAAC,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACpE,CAAC;IAED,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE,0IAA0I;QAC/I,IAAI,EAAE;YACJ,IAAI,CAAC,MAAM,CAAC,KAAK;YACjB,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;YAChE,IAAI,CAAC,MAAM,CAAC,SAAS;YACrB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC;YAC5B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC;YAC9B,GAAG;YACH,EAAE;SACH;KACF,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,uBAAuB,CAC3C,EAAU,EACV,OAAgB;IAEhB,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,MAAM,CAAC,OAAO,CAAC;QACnB,GAAG,EAAE;;;;;uEAK8D;QACnE,IAAI,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,EAAE,CAAC;KACpD,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,SAAkB;IAChD,MAAM,WAAW,EAAE,CAAC;IACpB,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAE3B,IAAI,SAAS,EAAE,CAAC;QACd,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC;YACpC,GAAG,EAAE,uEAAuE;YAC5E,IAAI,EAAE,CAAC,SAAS,CAAC;SAClB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC/B,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CACnC,kDAAkD,CACnD,CAAC;IACF,OAAO,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;AAC/B,CAAC","sourcesContent":["import crypto from \"crypto\";\n\nimport { getDbExec, intType } from \"../db/client.js\";\nimport type { Task, Message, TaskState, Artifact } from \"./types.js\";\n\nlet _initPromise: Promise<void> | undefined;\n\nasync function ensureTable(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n await client.execute(`\n CREATE TABLE IF NOT EXISTS a2a_tasks (\n id TEXT PRIMARY KEY,\n context_id TEXT,\n status_state TEXT NOT NULL DEFAULT 'submitted',\n status_message TEXT,\n status_timestamp TEXT NOT NULL,\n history TEXT NOT NULL DEFAULT '[]',\n artifacts TEXT NOT NULL DEFAULT '[]',\n metadata TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `);\n // Additive migration: owner_email column. Bound to the JWT-verified\n // caller at task-creation time so handleGet / handleCancel can reject\n // mismatched callers (the IDOR class fixed in PR #369). Existing rows\n // have NULL owner_email and remain accessible to legacy callers via\n // the legacy-token apiKeyEnv path; new rows are scoped from this point\n // forward.\n try {\n await client.execute(\n `ALTER TABLE a2a_tasks ADD COLUMN owner_email TEXT`,\n );\n } catch {\n // Column already exists — expected on every restart after first run.\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\nfunction taskFromRow(row: any): Task & { ownerEmail?: string | null } {\n return {\n id: row.id as string,\n contextId: (row.context_id as string) || undefined,\n status: {\n state: row.status_state as TaskState,\n message: row.status_message\n ? JSON.parse(row.status_message as string)\n : undefined,\n timestamp: row.status_timestamp as string,\n },\n history: JSON.parse(row.history as string),\n artifacts: JSON.parse(row.artifacts as string),\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n ownerEmail: (row.owner_email as string | null) ?? null,\n };\n}\n\nfunction getAffectedRowCount(result: unknown): number | undefined {\n const resultRecord = result as\n | {\n rowsAffected?: number;\n rowCount?: number;\n count?: number;\n }\n | undefined;\n return (\n resultRecord?.rowsAffected ?? resultRecord?.rowCount ?? resultRecord?.count\n );\n}\n\nexport async function createTask(\n message: Message,\n contextId?: string,\n metadata?: Record<string, unknown>,\n ownerEmail?: string | null,\n): Promise<Task> {\n await ensureTable();\n const client = getDbExec();\n const id = crypto.randomUUID();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n\n const task: Task = {\n id,\n contextId,\n status: { state: \"submitted\", timestamp },\n history: [message],\n artifacts: [],\n metadata,\n };\n\n await client.execute({\n sql: `INSERT INTO a2a_tasks (id, context_id, status_state, status_timestamp, history, artifacts, metadata, owner_email, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n contextId ?? null,\n \"submitted\",\n timestamp,\n JSON.stringify([message]),\n \"[]\",\n metadata ? JSON.stringify(metadata) : null,\n ownerEmail ?? null,\n now,\n now,\n ],\n });\n\n return task;\n}\n\n/**\n * Fetch the verified owner email recorded against a task at creation time.\n * Returns null when the task has no owner (legacy rows or unauthenticated\n * deployments) or when the task is missing.\n *\n * Used by `handleGet` / `handleCancel` to reject IDOR access — the JWT-\n * verified caller's email must match `owner_email` to read or cancel.\n */\nexport async function getTaskOwner(id: string): Promise<string | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT owner_email FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n const ownerEmail = (rows[0] as any).owner_email;\n return typeof ownerEmail === \"string\" && ownerEmail ? ownerEmail : null;\n}\n\n/**\n * Atomically claim a task for processing. Only succeeds when the task is in\n * state 'submitted' or 'working' — flipping it to 'processing' so concurrent\n * processors can't pick it up twice. Returns the task if claimed, null if it\n * was already claimed/completed/missing.\n *\n * Used by the cross-platform async processor (`_process-task` route) to avoid\n * duplicate handler runs when retries fire.\n */\nexport async function claimA2ATaskForProcessing(\n id: string,\n): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'processing',\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working')`,\n args: [timestamp, now, id],\n });\n const affected = getAffectedRowCount(result);\n if (affected === 0) return null;\n\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return taskFromRow(rows[0]);\n}\n\nexport async function getA2ATaskDispatchState(id: string): Promise<{\n id: string;\n statusState: string;\n metadata: Record<string, unknown> | undefined;\n updatedAt: number;\n} | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, status_state, metadata, updated_at FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n const row = rows[0] as any;\n if (!row) return null;\n return {\n id: row.id as string,\n statusState: row.status_state as string,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport async function touchQueuedA2ATaskDispatch(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working')`,\n args: [now, id],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function touchProcessingA2ATask(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'`,\n args: [now, id],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function resetStuckA2ATaskForRetry(\n id: string,\n processingCutoff: number,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'working',\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'\n AND updated_at <= ?`,\n args: [timestamp, now, id, processingCutoff],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function failStuckA2ATask(\n id: string,\n processingCutoff: number,\n reason: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n const message: Message = {\n role: \"agent\",\n parts: [{ type: \"text\", text: reason }],\n };\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'failed',\n status_message = ?,\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'\n AND updated_at <= ?`,\n args: [JSON.stringify(message), timestamp, now, id, processingCutoff],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function getTask(id: string): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return taskFromRow(rows[0]);\n}\n\nexport async function updateTask(\n id: string,\n update: {\n state?: TaskState;\n message?: Message;\n artifacts?: Artifact[];\n },\n): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n\n // Read current task\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n\n const task = taskFromRow(rows[0]);\n const now = Date.now();\n\n if (update.state) {\n task.status = {\n state: update.state,\n message: update.message ?? task.status.message,\n timestamp: new Date().toISOString(),\n };\n }\n\n if (update.message && task.history) {\n task.history.push(update.message);\n }\n\n if (update.artifacts) {\n task.artifacts = [...(task.artifacts ?? []), ...update.artifacts];\n }\n\n await client.execute({\n sql: `UPDATE a2a_tasks SET status_state = ?, status_message = ?, status_timestamp = ?, history = ?, artifacts = ?, updated_at = ? WHERE id = ?`,\n args: [\n task.status.state,\n task.status.message ? JSON.stringify(task.status.message) : null,\n task.status.timestamp,\n JSON.stringify(task.history),\n JSON.stringify(task.artifacts),\n now,\n id,\n ],\n });\n\n return task;\n}\n\nexport async function updateTaskStatusMessage(\n id: string,\n message: Message,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_message = ?,\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working', 'processing')`,\n args: [JSON.stringify(message), timestamp, now, id],\n });\n}\n\nexport async function listTasks(contextId?: string): Promise<Task[]> {\n await ensureTable();\n const client = getDbExec();\n\n if (contextId) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE context_id = ? ORDER BY created_at DESC`,\n args: [contextId],\n });\n return rows.map(taskFromRow);\n }\n\n const { rows } = await client.execute(\n `SELECT * FROM a2a_tasks ORDER BY created_at DESC`,\n );\n return rows.map(taskFromRow);\n}\n"]}
1
+ {"version":3,"file":"task-store.js","sourceRoot":"","sources":["../../src/a2a/task-store.ts"],"names":[],"mappings":"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","sourcesContent":["import crypto from \"crypto\";\n\nimport { getDbExec, intType, isPostgres } from \"../db/client.js\";\nimport { ensureTableExists, ensureColumnExists } from \"../db/ddl-guard.js\";\nimport type { Task, Message, TaskState, Artifact } from \"./types.js\";\n\nlet _initPromise: Promise<void> | undefined;\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 a2a_tasks (\n id TEXT PRIMARY KEY,\n context_id TEXT,\n status_state TEXT NOT NULL DEFAULT 'submitted',\n status_message TEXT,\n status_timestamp TEXT NOT NULL,\n history TEXT NOT NULL DEFAULT '[]',\n artifacts TEXT NOT NULL DEFAULT '[]',\n metadata TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema before issuing DDL to avoid ACCESS\n // EXCLUSIVE lock contention in fresh background-worker processes.\n await ensureTableExists(\"a2a_tasks\", createSql);\n // Additive migration: owner_email column. Bound to the JWT-verified\n // caller at task-creation time so handleGet / handleCancel can reject\n // mismatched callers (the IDOR class fixed in PR #369). Existing rows\n // have NULL owner_email and remain accessible to legacy callers via\n // the legacy-token apiKeyEnv path; new rows are scoped from this point\n // forward.\n await ensureColumnExists(\n \"a2a_tasks\",\n \"owner_email\",\n `ALTER TABLE a2a_tasks ADD COLUMN IF NOT EXISTS owner_email TEXT`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n // Additive migration: owner_email column. Bound to the JWT-verified\n // caller at task-creation time so handleGet / handleCancel can reject\n // mismatched callers (the IDOR class fixed in PR #369). Existing rows\n // have NULL owner_email and remain accessible to legacy callers via\n // the legacy-token apiKeyEnv path; new rows are scoped from this point\n // forward.\n try {\n await client.execute(\n `ALTER TABLE a2a_tasks ADD COLUMN owner_email TEXT`,\n );\n } catch {\n // Column already exists — expected on every restart after first run.\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\nfunction taskFromRow(row: any): Task & { ownerEmail?: string | null } {\n return {\n id: row.id as string,\n contextId: (row.context_id as string) || undefined,\n status: {\n state: row.status_state as TaskState,\n message: row.status_message\n ? JSON.parse(row.status_message as string)\n : undefined,\n timestamp: row.status_timestamp as string,\n },\n history: JSON.parse(row.history as string),\n artifacts: JSON.parse(row.artifacts as string),\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n ownerEmail: (row.owner_email as string | null) ?? null,\n };\n}\n\nfunction getAffectedRowCount(result: unknown): number | undefined {\n const resultRecord = result as\n | {\n rowsAffected?: number;\n rowCount?: number;\n count?: number;\n }\n | undefined;\n return (\n resultRecord?.rowsAffected ?? resultRecord?.rowCount ?? resultRecord?.count\n );\n}\n\nexport async function createTask(\n message: Message,\n contextId?: string,\n metadata?: Record<string, unknown>,\n ownerEmail?: string | null,\n): Promise<Task> {\n await ensureTable();\n const client = getDbExec();\n const id = crypto.randomUUID();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n\n const task: Task = {\n id,\n contextId,\n status: { state: \"submitted\", timestamp },\n history: [message],\n artifacts: [],\n metadata,\n };\n\n await client.execute({\n sql: `INSERT INTO a2a_tasks (id, context_id, status_state, status_timestamp, history, artifacts, metadata, owner_email, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n args: [\n id,\n contextId ?? null,\n \"submitted\",\n timestamp,\n JSON.stringify([message]),\n \"[]\",\n metadata ? JSON.stringify(metadata) : null,\n ownerEmail ?? null,\n now,\n now,\n ],\n });\n\n return task;\n}\n\n/**\n * Fetch the verified owner email recorded against a task at creation time.\n * Returns null when the task has no owner (legacy rows or unauthenticated\n * deployments) or when the task is missing.\n *\n * Used by `handleGet` / `handleCancel` to reject IDOR access — the JWT-\n * verified caller's email must match `owner_email` to read or cancel.\n */\nexport async function getTaskOwner(id: string): Promise<string | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT owner_email FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n const ownerEmail = (rows[0] as any).owner_email;\n return typeof ownerEmail === \"string\" && ownerEmail ? ownerEmail : null;\n}\n\n/**\n * Atomically claim a task for processing. Only succeeds when the task is in\n * state 'submitted' or 'working' — flipping it to 'processing' so concurrent\n * processors can't pick it up twice. Returns the task if claimed, null if it\n * was already claimed/completed/missing.\n *\n * Used by the cross-platform async processor (`_process-task` route) to avoid\n * duplicate handler runs when retries fire.\n */\nexport async function claimA2ATaskForProcessing(\n id: string,\n): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'processing',\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working')`,\n args: [timestamp, now, id],\n });\n const affected = getAffectedRowCount(result);\n if (affected === 0) return null;\n\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return taskFromRow(rows[0]);\n}\n\nexport async function getA2ATaskDispatchState(id: string): Promise<{\n id: string;\n statusState: string;\n metadata: Record<string, unknown> | undefined;\n updatedAt: number;\n} | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT id, status_state, metadata, updated_at FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n const row = rows[0] as any;\n if (!row) return null;\n return {\n id: row.id as string,\n statusState: row.status_state as string,\n metadata: row.metadata ? JSON.parse(row.metadata as string) : undefined,\n updatedAt: Number(row.updated_at ?? 0),\n };\n}\n\nexport async function touchQueuedA2ATaskDispatch(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working')`,\n args: [now, id],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function touchProcessingA2ATask(id: string): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'`,\n args: [now, id],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function resetStuckA2ATaskForRetry(\n id: string,\n processingCutoff: number,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'working',\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'\n AND updated_at <= ?`,\n args: [timestamp, now, id, processingCutoff],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function failStuckA2ATask(\n id: string,\n processingCutoff: number,\n reason: string,\n): Promise<boolean> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n const message: Message = {\n role: \"agent\",\n parts: [{ type: \"text\", text: reason }],\n };\n const result = await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_state = 'failed',\n status_message = ?,\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state = 'processing'\n AND updated_at <= ?`,\n args: [JSON.stringify(message), timestamp, now, id, processingCutoff],\n });\n const affected = getAffectedRowCount(result);\n return affected !== 0;\n}\n\nexport async function getTask(id: string): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n return taskFromRow(rows[0]);\n}\n\nexport async function updateTask(\n id: string,\n update: {\n state?: TaskState;\n message?: Message;\n artifacts?: Artifact[];\n },\n): Promise<Task | null> {\n await ensureTable();\n const client = getDbExec();\n\n // Read current task\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE id = ?`,\n args: [id],\n });\n if (rows.length === 0) return null;\n\n const task = taskFromRow(rows[0]);\n const now = Date.now();\n\n if (update.state) {\n task.status = {\n state: update.state,\n message: update.message ?? task.status.message,\n timestamp: new Date().toISOString(),\n };\n }\n\n if (update.message && task.history) {\n task.history.push(update.message);\n }\n\n if (update.artifacts) {\n task.artifacts = [...(task.artifacts ?? []), ...update.artifacts];\n }\n\n await client.execute({\n sql: `UPDATE a2a_tasks SET status_state = ?, status_message = ?, status_timestamp = ?, history = ?, artifacts = ?, updated_at = ? WHERE id = ?`,\n args: [\n task.status.state,\n task.status.message ? JSON.stringify(task.status.message) : null,\n task.status.timestamp,\n JSON.stringify(task.history),\n JSON.stringify(task.artifacts),\n now,\n id,\n ],\n });\n\n return task;\n}\n\nexport async function updateTaskStatusMessage(\n id: string,\n message: Message,\n): Promise<void> {\n await ensureTable();\n const client = getDbExec();\n const now = Date.now();\n const timestamp = new Date().toISOString();\n await client.execute({\n sql: `UPDATE a2a_tasks\n SET status_message = ?,\n status_timestamp = ?,\n updated_at = ?\n WHERE id = ?\n AND status_state IN ('submitted', 'working', 'processing')`,\n args: [JSON.stringify(message), timestamp, now, id],\n });\n}\n\nexport async function listTasks(contextId?: string): Promise<Task[]> {\n await ensureTable();\n const client = getDbExec();\n\n if (contextId) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM a2a_tasks WHERE context_id = ? ORDER BY created_at DESC`,\n args: [contextId],\n });\n return rows.map(taskFromRow);\n }\n\n const { rows } = await client.execute(\n `SELECT * FROM a2a_tasks ORDER BY created_at DESC`,\n );\n return rows.map(taskFromRow);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/agent/harness/store.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,yBAAyB,GACjC,SAAS,GACT,MAAM,GACN,SAAS,GACT,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAsB,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAiFrE;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAgEpC;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,CAAC,GACvD,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAwB3C;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAQ3C;AAED,wBAAsB,qCAAqC,CACzD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAiB3C;AAED,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAU3C;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,GACL,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAoDtC;AAED,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,CACb,yBAAyB,EACzB,SAAS,GAAG,WAAW,GAAG,SAAS,CACpC,GACA,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAM3C"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/agent/harness/store.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,yBAAyB,GACjC,SAAS,GACT,MAAM,GACN,SAAS,GACT,SAAS,GACT,WAAW,CAAC;AAEhB,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,EAAE,yBAAyB,CAAC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,iBAAiB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAID,wBAAsB,+BAA+B,IAAI,OAAO,CAAC,IAAI,CAAC,CAmHrE;AAED,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,4BAA4B,GAClC,OAAO,CAAC,yBAAyB,CAAC,CAgEpC;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,4BAA4B,EAAE,IAAI,CAAC,CAAC,GACvD,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAwB3C;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,GACT,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAQ3C;AAED,wBAAsB,qCAAqC,CACzD,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAiB3C;AAED,wBAAsB,6BAA6B,CACjD,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAU3C;AAED,wBAAsB,wBAAwB,CAC5C,OAAO,GAAE;IACP,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;CACX,GACL,OAAO,CAAC,yBAAyB,EAAE,CAAC,CAoDtC;AAED,wBAAsB,8BAA8B,CAClD,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,OAAO,CACb,yBAAyB,EACzB,SAAS,GAAG,WAAW,GAAG,SAAS,CACpC,GACA,OAAO,CAAC,yBAAyB,GAAG,IAAI,CAAC,CAM3C"}
@@ -1,27 +1,51 @@
1
- import { getDbExec, intType, isPostgres, retryOnDdlRace, } from "../../db/client.js";
1
+ import { getDbExec, intType, isPostgres } from "../../db/client.js";
2
+ import { ensureColumnExists, ensureIndexExists, ensureTableExists, } from "../../db/ddl-guard.js";
2
3
  let initPromise;
3
4
  export async function ensureAgentHarnessSessionTables() {
4
5
  if (!initPromise) {
5
6
  initPromise = (async () => {
6
7
  const client = getDbExec();
7
- await retryOnDdlRace(() => client.execute(`
8
- CREATE TABLE IF NOT EXISTS agent_harness_sessions (
9
- id TEXT PRIMARY KEY,
10
- harness_name TEXT NOT NULL,
11
- thread_id TEXT NOT NULL,
12
- run_id TEXT,
13
- provider_session_id TEXT,
14
- status TEXT NOT NULL DEFAULT 'idle',
15
- resume_state TEXT,
16
- workspace_ref TEXT,
17
- pending_approval TEXT,
18
- owner_email TEXT,
19
- org_id TEXT,
20
- created_at ${intType()} NOT NULL,
21
- updated_at ${intType()} NOT NULL,
22
- stopped_at ${intType()}
23
- )
24
- `));
8
+ const createSql = `
9
+ CREATE TABLE IF NOT EXISTS agent_harness_sessions (
10
+ id TEXT PRIMARY KEY,
11
+ harness_name TEXT NOT NULL,
12
+ thread_id TEXT NOT NULL,
13
+ run_id TEXT,
14
+ provider_session_id TEXT,
15
+ status TEXT NOT NULL DEFAULT 'idle',
16
+ resume_state TEXT,
17
+ workspace_ref TEXT,
18
+ pending_approval TEXT,
19
+ owner_email TEXT,
20
+ org_id TEXT,
21
+ created_at ${intType()} NOT NULL,
22
+ updated_at ${intType()} NOT NULL,
23
+ stopped_at ${intType()}
24
+ )
25
+ `;
26
+ if (isPostgres()) {
27
+ // PG-guard: probe information_schema / pg_indexes before issuing DDL to
28
+ // avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes.
29
+ await ensureTableExists("agent_harness_sessions", createSql);
30
+ for (const col of [
31
+ "run_id",
32
+ "provider_session_id",
33
+ "resume_state",
34
+ "workspace_ref",
35
+ "pending_approval",
36
+ "owner_email",
37
+ "org_id",
38
+ ]) {
39
+ await ensureColumnExists("agent_harness_sessions", col, `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS ${col} TEXT`);
40
+ }
41
+ await ensureColumnExists("agent_harness_sessions", "stopped_at", `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS stopped_at ${intType()}`);
42
+ await ensureIndexExists("idx_agent_harness_sessions_thread", `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`);
43
+ await ensureIndexExists("idx_agent_harness_sessions_status", `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`);
44
+ await ensureIndexExists("idx_agent_harness_sessions_owner", `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`);
45
+ return;
46
+ }
47
+ // SQLite (local dev): no lock problem — keep the original behaviour.
48
+ await client.execute(createSql);
25
49
  for (const col of [
26
50
  "run_id",
27
51
  "provider_session_id",
@@ -32,31 +56,36 @@ export async function ensureAgentHarnessSessionTables() {
32
56
  "org_id",
33
57
  ]) {
34
58
  try {
35
- if (isPostgres()) {
36
- await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS ${col} TEXT`);
37
- }
38
- else {
39
- await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN ${col} TEXT`);
40
- }
59
+ await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN ${col} TEXT`);
41
60
  }
42
61
  catch {
43
62
  // Column already exists.
44
63
  }
45
64
  }
46
65
  try {
47
- if (isPostgres()) {
48
- await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS stopped_at ${intType()}`);
49
- }
50
- else {
51
- await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN stopped_at ${intType()}`);
52
- }
66
+ await client.execute(`ALTER TABLE agent_harness_sessions ADD COLUMN stopped_at ${intType()}`);
53
67
  }
54
68
  catch {
55
69
  // Column already exists.
56
70
  }
57
- await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`));
58
- await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`));
59
- await retryOnDdlRace(() => client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`));
71
+ try {
72
+ await client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`);
73
+ }
74
+ catch {
75
+ // Index already exists.
76
+ }
77
+ try {
78
+ await client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`);
79
+ }
80
+ catch {
81
+ // Index already exists.
82
+ }
83
+ try {
84
+ await client.execute(`CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`);
85
+ }
86
+ catch {
87
+ // Index already exists.
88
+ }
60
89
  })().catch((err) => {
61
90
  initPromise = undefined;
62
91
  throw err;
@@ -1 +1 @@
1
- {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/agent/harness/store.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n getDbExec,\n intType,\n isPostgres,\n retryOnDdlRace,\n} from \"../../db/client.js\";\n\nexport type AgentHarnessSessionStatus =\n | \"running\"\n | \"idle\"\n | \"stopped\"\n | \"errored\"\n | \"destroyed\";\n\nexport interface StoredAgentHarnessSession {\n id: string;\n harnessName: string;\n threadId: string;\n runId?: string | null;\n providerSessionId?: string | null;\n status: AgentHarnessSessionStatus;\n resumeState?: unknown;\n workspaceRef?: string | null;\n pendingApproval?: unknown;\n ownerEmail?: string | null;\n orgId?: string | null;\n createdAt: number;\n updatedAt: number;\n stoppedAt?: number | null;\n}\n\nexport interface SaveAgentHarnessSessionInput {\n id: string;\n harnessName: string;\n threadId: string;\n runId?: string | null;\n providerSessionId?: string | null;\n status?: AgentHarnessSessionStatus;\n resumeState?: unknown;\n workspaceRef?: string | null;\n pendingApproval?: unknown;\n ownerEmail?: string | null;\n orgId?: string | null;\n stoppedAt?: number | null;\n}\n\nlet initPromise: Promise<void> | undefined;\n\nexport async function ensureAgentHarnessSessionTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const client = getDbExec();\n await retryOnDdlRace(() =>\n client.execute(`\n CREATE TABLE IF NOT EXISTS agent_harness_sessions (\n id TEXT PRIMARY KEY,\n harness_name TEXT NOT NULL,\n thread_id TEXT NOT NULL,\n run_id TEXT,\n provider_session_id TEXT,\n status TEXT NOT NULL DEFAULT 'idle',\n resume_state TEXT,\n workspace_ref TEXT,\n pending_approval TEXT,\n owner_email TEXT,\n org_id TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n stopped_at ${intType()}\n )\n `),\n );\n for (const col of [\n \"run_id\",\n \"provider_session_id\",\n \"resume_state\",\n \"workspace_ref\",\n \"pending_approval\",\n \"owner_email\",\n \"org_id\",\n ] as const) {\n try {\n if (isPostgres()) {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS ${col} TEXT`,\n );\n } else {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN ${col} TEXT`,\n );\n }\n } catch {\n // Column already exists.\n }\n }\n try {\n if (isPostgres()) {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS stopped_at ${intType()}`,\n );\n } else {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN stopped_at ${intType()}`,\n );\n }\n } catch {\n // Column already exists.\n }\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`,\n ),\n );\n await retryOnDdlRace(() =>\n client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`,\n ),\n );\n })().catch((err) => {\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nexport async function saveAgentHarnessSession(\n input: SaveAgentHarnessSessionInput,\n): Promise<StoredAgentHarnessSession> {\n await ensureAgentHarnessSessionTables();\n const now = Date.now();\n const existing = await getAgentHarnessSession(input.id);\n const record: StoredAgentHarnessSession = {\n id: input.id,\n harnessName: input.harnessName,\n threadId: input.threadId,\n runId: input.runId ?? null,\n providerSessionId: input.providerSessionId ?? null,\n status: input.status ?? existing?.status ?? \"idle\",\n resumeState:\n input.resumeState !== undefined\n ? input.resumeState\n : existing?.resumeState,\n workspaceRef: input.workspaceRef ?? existing?.workspaceRef ?? null,\n pendingApproval:\n input.pendingApproval !== undefined\n ? input.pendingApproval\n : existing?.pendingApproval,\n ownerEmail: input.ownerEmail ?? existing?.ownerEmail ?? null,\n orgId: input.orgId ?? existing?.orgId ?? null,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n stoppedAt: input.stoppedAt ?? existing?.stoppedAt ?? null,\n };\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO agent_harness_sessions (\n id, harness_name, thread_id, run_id, provider_session_id, status,\n resume_state, workspace_ref, pending_approval, owner_email, org_id,\n created_at, updated_at, stopped_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n harness_name = excluded.harness_name,\n thread_id = excluded.thread_id,\n run_id = excluded.run_id,\n provider_session_id = excluded.provider_session_id,\n status = excluded.status,\n resume_state = excluded.resume_state,\n workspace_ref = excluded.workspace_ref,\n pending_approval = excluded.pending_approval,\n owner_email = excluded.owner_email,\n org_id = excluded.org_id,\n updated_at = excluded.updated_at,\n stopped_at = excluded.stopped_at`,\n args: [\n record.id,\n record.harnessName,\n record.threadId,\n record.runId ?? null,\n record.providerSessionId ?? null,\n record.status,\n serializeJson(record.resumeState),\n record.workspaceRef ?? null,\n serializeJson(record.pendingApproval),\n record.ownerEmail ?? null,\n record.orgId ?? null,\n record.createdAt,\n record.updatedAt,\n record.stoppedAt ?? null,\n ],\n });\n return record;\n}\n\nexport async function updateAgentHarnessSession(\n id: string,\n patch: Partial<Omit<SaveAgentHarnessSessionInput, \"id\">>,\n): Promise<StoredAgentHarnessSession | null> {\n const existing = await getAgentHarnessSession(id);\n if (!existing) return null;\n return saveAgentHarnessSession({\n id,\n harnessName: patch.harnessName ?? existing.harnessName,\n threadId: patch.threadId ?? existing.threadId,\n runId: patch.runId ?? existing.runId ?? null,\n providerSessionId:\n patch.providerSessionId ?? existing.providerSessionId ?? null,\n status: patch.status ?? existing.status,\n resumeState:\n patch.resumeState !== undefined\n ? patch.resumeState\n : existing.resumeState,\n workspaceRef: patch.workspaceRef ?? existing.workspaceRef ?? null,\n pendingApproval:\n patch.pendingApproval !== undefined\n ? patch.pendingApproval\n : existing.pendingApproval,\n ownerEmail: patch.ownerEmail ?? existing.ownerEmail ?? null,\n orgId: patch.orgId ?? existing.orgId ?? null,\n stoppedAt: patch.stoppedAt ?? existing.stoppedAt ?? null,\n });\n}\n\nexport async function getAgentHarnessSession(\n id: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions WHERE id = ?`,\n args: [id],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function getLatestAgentHarnessSessionForThread(\n threadId: string,\n harnessName?: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = harnessName\n ? await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND harness_name = ? AND status != 'destroyed'\n ORDER BY updated_at DESC LIMIT 1`,\n args: [threadId, harnessName],\n })\n : await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND status != 'destroyed'\n ORDER BY updated_at DESC LIMIT 1`,\n args: [threadId],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function getAgentHarnessSessionByRunId(\n runId: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE run_id = ?\n ORDER BY updated_at DESC LIMIT 1`,\n args: [runId],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function listAgentHarnessSessions(\n options: {\n status?: AgentHarnessSessionStatus;\n threadId?: string;\n ownerEmail?: string | null;\n orgId?: string | null;\n limit?: number;\n } = {},\n): Promise<StoredAgentHarnessSession[]> {\n await ensureAgentHarnessSessionTables();\n const limit = Math.max(1, Math.min(200, options.limit ?? 50));\n const client = getDbExec();\n if (options.ownerEmail) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE owner_email = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.ownerEmail, limit],\n });\n const sessions = rows.map(rowToHarnessSession).filter(isStoredSession);\n return sessions.filter(\n (session) =>\n (!options.threadId || session.threadId === options.threadId) &&\n (!options.status || session.status === options.status) &&\n (!options.orgId || session.orgId === options.orgId),\n );\n }\n if (options.threadId && options.status) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND status = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.threadId, options.status, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n if (options.threadId) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.threadId, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n if (options.status) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE status = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.status, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n ORDER BY updated_at DESC LIMIT ?`,\n args: [limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n}\n\nexport async function markAgentHarnessSessionStopped(\n id: string,\n status: Extract<\n AgentHarnessSessionStatus,\n \"stopped\" | \"destroyed\" | \"errored\"\n >,\n): Promise<StoredAgentHarnessSession | null> {\n return updateAgentHarnessSession(id, {\n status,\n stoppedAt: Date.now(),\n pendingApproval: null,\n });\n}\n\nfunction rowToHarnessSession(row: unknown): StoredAgentHarnessSession | null {\n if (!row || typeof row !== \"object\") return null;\n const record = row as Record<string, unknown>;\n return {\n id: String(record.id),\n harnessName: String(record.harness_name),\n threadId: String(record.thread_id),\n runId: stringOrNull(record.run_id),\n providerSessionId: stringOrNull(record.provider_session_id),\n status: normalizeStatus(record.status),\n resumeState: parseJson(record.resume_state),\n workspaceRef: stringOrNull(record.workspace_ref),\n pendingApproval: parseJson(record.pending_approval),\n ownerEmail: stringOrNull(record.owner_email),\n orgId: stringOrNull(record.org_id),\n createdAt: numberValue(record.created_at),\n updatedAt: numberValue(record.updated_at),\n stoppedAt:\n record.stopped_at === null || record.stopped_at === undefined\n ? null\n : numberValue(record.stopped_at),\n };\n}\n\nfunction isStoredSession(\n session: StoredAgentHarnessSession | null,\n): session is StoredAgentHarnessSession {\n return Boolean(session);\n}\n\nfunction normalizeStatus(value: unknown): AgentHarnessSessionStatus {\n return value === \"running\" ||\n value === \"idle\" ||\n value === \"stopped\" ||\n value === \"errored\" ||\n value === \"destroyed\"\n ? value\n : \"idle\";\n}\n\nfunction serializeJson(value: unknown): string | null {\n if (value === undefined || value === null) return null;\n return JSON.stringify(value);\n}\n\nfunction parseJson(value: unknown): unknown {\n if (typeof value !== \"string\" || !value) return undefined;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === \"string\" && value ? value : null;\n}\n\nfunction numberValue(value: unknown): number {\n return typeof value === \"number\" ? value : Number(value ?? 0);\n}\n"]}
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/agent/harness/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { getDbExec, intType, isPostgres } from \"../../db/client.js\";\nimport {\n ensureColumnExists,\n ensureIndexExists,\n ensureTableExists,\n} from \"../../db/ddl-guard.js\";\n\nexport type AgentHarnessSessionStatus =\n | \"running\"\n | \"idle\"\n | \"stopped\"\n | \"errored\"\n | \"destroyed\";\n\nexport interface StoredAgentHarnessSession {\n id: string;\n harnessName: string;\n threadId: string;\n runId?: string | null;\n providerSessionId?: string | null;\n status: AgentHarnessSessionStatus;\n resumeState?: unknown;\n workspaceRef?: string | null;\n pendingApproval?: unknown;\n ownerEmail?: string | null;\n orgId?: string | null;\n createdAt: number;\n updatedAt: number;\n stoppedAt?: number | null;\n}\n\nexport interface SaveAgentHarnessSessionInput {\n id: string;\n harnessName: string;\n threadId: string;\n runId?: string | null;\n providerSessionId?: string | null;\n status?: AgentHarnessSessionStatus;\n resumeState?: unknown;\n workspaceRef?: string | null;\n pendingApproval?: unknown;\n ownerEmail?: string | null;\n orgId?: string | null;\n stoppedAt?: number | null;\n}\n\nlet initPromise: Promise<void> | undefined;\n\nexport async function ensureAgentHarnessSessionTables(): Promise<void> {\n if (!initPromise) {\n initPromise = (async () => {\n const client = getDbExec();\n const createSql = `\n CREATE TABLE IF NOT EXISTS agent_harness_sessions (\n id TEXT PRIMARY KEY,\n harness_name TEXT NOT NULL,\n thread_id TEXT NOT NULL,\n run_id TEXT,\n provider_session_id TEXT,\n status TEXT NOT NULL DEFAULT 'idle',\n resume_state TEXT,\n workspace_ref TEXT,\n pending_approval TEXT,\n owner_email TEXT,\n org_id TEXT,\n created_at ${intType()} NOT NULL,\n updated_at ${intType()} NOT NULL,\n stopped_at ${intType()}\n )\n `;\n\n if (isPostgres()) {\n // PG-guard: probe information_schema / pg_indexes before issuing DDL to\n // avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes.\n await ensureTableExists(\"agent_harness_sessions\", createSql);\n for (const col of [\n \"run_id\",\n \"provider_session_id\",\n \"resume_state\",\n \"workspace_ref\",\n \"pending_approval\",\n \"owner_email\",\n \"org_id\",\n ] as const) {\n await ensureColumnExists(\n \"agent_harness_sessions\",\n col,\n `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS ${col} TEXT`,\n );\n }\n await ensureColumnExists(\n \"agent_harness_sessions\",\n \"stopped_at\",\n `ALTER TABLE agent_harness_sessions ADD COLUMN IF NOT EXISTS stopped_at ${intType()}`,\n );\n await ensureIndexExists(\n \"idx_agent_harness_sessions_thread\",\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`,\n );\n await ensureIndexExists(\n \"idx_agent_harness_sessions_status\",\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`,\n );\n await ensureIndexExists(\n \"idx_agent_harness_sessions_owner\",\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`,\n );\n return;\n }\n\n // SQLite (local dev): no lock problem — keep the original behaviour.\n await client.execute(createSql);\n for (const col of [\n \"run_id\",\n \"provider_session_id\",\n \"resume_state\",\n \"workspace_ref\",\n \"pending_approval\",\n \"owner_email\",\n \"org_id\",\n ] as const) {\n try {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN ${col} TEXT`,\n );\n } catch {\n // Column already exists.\n }\n }\n try {\n await client.execute(\n `ALTER TABLE agent_harness_sessions ADD COLUMN stopped_at ${intType()}`,\n );\n } catch {\n // Column already exists.\n }\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_thread ON agent_harness_sessions(thread_id, updated_at)`,\n );\n } catch {\n // Index already exists.\n }\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_status ON agent_harness_sessions(status, updated_at)`,\n );\n } catch {\n // Index already exists.\n }\n try {\n await client.execute(\n `CREATE INDEX IF NOT EXISTS idx_agent_harness_sessions_owner ON agent_harness_sessions(owner_email, updated_at)`,\n );\n } catch {\n // Index already exists.\n }\n })().catch((err) => {\n initPromise = undefined;\n throw err;\n });\n }\n return initPromise;\n}\n\nexport async function saveAgentHarnessSession(\n input: SaveAgentHarnessSessionInput,\n): Promise<StoredAgentHarnessSession> {\n await ensureAgentHarnessSessionTables();\n const now = Date.now();\n const existing = await getAgentHarnessSession(input.id);\n const record: StoredAgentHarnessSession = {\n id: input.id,\n harnessName: input.harnessName,\n threadId: input.threadId,\n runId: input.runId ?? null,\n providerSessionId: input.providerSessionId ?? null,\n status: input.status ?? existing?.status ?? \"idle\",\n resumeState:\n input.resumeState !== undefined\n ? input.resumeState\n : existing?.resumeState,\n workspaceRef: input.workspaceRef ?? existing?.workspaceRef ?? null,\n pendingApproval:\n input.pendingApproval !== undefined\n ? input.pendingApproval\n : existing?.pendingApproval,\n ownerEmail: input.ownerEmail ?? existing?.ownerEmail ?? null,\n orgId: input.orgId ?? existing?.orgId ?? null,\n createdAt: existing?.createdAt ?? now,\n updatedAt: now,\n stoppedAt: input.stoppedAt ?? existing?.stoppedAt ?? null,\n };\n const client = getDbExec();\n await client.execute({\n sql: `INSERT INTO agent_harness_sessions (\n id, harness_name, thread_id, run_id, provider_session_id, status,\n resume_state, workspace_ref, pending_approval, owner_email, org_id,\n created_at, updated_at, stopped_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(id) DO UPDATE SET\n harness_name = excluded.harness_name,\n thread_id = excluded.thread_id,\n run_id = excluded.run_id,\n provider_session_id = excluded.provider_session_id,\n status = excluded.status,\n resume_state = excluded.resume_state,\n workspace_ref = excluded.workspace_ref,\n pending_approval = excluded.pending_approval,\n owner_email = excluded.owner_email,\n org_id = excluded.org_id,\n updated_at = excluded.updated_at,\n stopped_at = excluded.stopped_at`,\n args: [\n record.id,\n record.harnessName,\n record.threadId,\n record.runId ?? null,\n record.providerSessionId ?? null,\n record.status,\n serializeJson(record.resumeState),\n record.workspaceRef ?? null,\n serializeJson(record.pendingApproval),\n record.ownerEmail ?? null,\n record.orgId ?? null,\n record.createdAt,\n record.updatedAt,\n record.stoppedAt ?? null,\n ],\n });\n return record;\n}\n\nexport async function updateAgentHarnessSession(\n id: string,\n patch: Partial<Omit<SaveAgentHarnessSessionInput, \"id\">>,\n): Promise<StoredAgentHarnessSession | null> {\n const existing = await getAgentHarnessSession(id);\n if (!existing) return null;\n return saveAgentHarnessSession({\n id,\n harnessName: patch.harnessName ?? existing.harnessName,\n threadId: patch.threadId ?? existing.threadId,\n runId: patch.runId ?? existing.runId ?? null,\n providerSessionId:\n patch.providerSessionId ?? existing.providerSessionId ?? null,\n status: patch.status ?? existing.status,\n resumeState:\n patch.resumeState !== undefined\n ? patch.resumeState\n : existing.resumeState,\n workspaceRef: patch.workspaceRef ?? existing.workspaceRef ?? null,\n pendingApproval:\n patch.pendingApproval !== undefined\n ? patch.pendingApproval\n : existing.pendingApproval,\n ownerEmail: patch.ownerEmail ?? existing.ownerEmail ?? null,\n orgId: patch.orgId ?? existing.orgId ?? null,\n stoppedAt: patch.stoppedAt ?? existing.stoppedAt ?? null,\n });\n}\n\nexport async function getAgentHarnessSession(\n id: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions WHERE id = ?`,\n args: [id],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function getLatestAgentHarnessSessionForThread(\n threadId: string,\n harnessName?: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = harnessName\n ? await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND harness_name = ? AND status != 'destroyed'\n ORDER BY updated_at DESC LIMIT 1`,\n args: [threadId, harnessName],\n })\n : await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND status != 'destroyed'\n ORDER BY updated_at DESC LIMIT 1`,\n args: [threadId],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function getAgentHarnessSessionByRunId(\n runId: string,\n): Promise<StoredAgentHarnessSession | null> {\n await ensureAgentHarnessSessionTables();\n const client = getDbExec();\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE run_id = ?\n ORDER BY updated_at DESC LIMIT 1`,\n args: [runId],\n });\n return rowToHarnessSession(rows[0]);\n}\n\nexport async function listAgentHarnessSessions(\n options: {\n status?: AgentHarnessSessionStatus;\n threadId?: string;\n ownerEmail?: string | null;\n orgId?: string | null;\n limit?: number;\n } = {},\n): Promise<StoredAgentHarnessSession[]> {\n await ensureAgentHarnessSessionTables();\n const limit = Math.max(1, Math.min(200, options.limit ?? 50));\n const client = getDbExec();\n if (options.ownerEmail) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE owner_email = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.ownerEmail, limit],\n });\n const sessions = rows.map(rowToHarnessSession).filter(isStoredSession);\n return sessions.filter(\n (session) =>\n (!options.threadId || session.threadId === options.threadId) &&\n (!options.status || session.status === options.status) &&\n (!options.orgId || session.orgId === options.orgId),\n );\n }\n if (options.threadId && options.status) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ? AND status = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.threadId, options.status, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n if (options.threadId) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE thread_id = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.threadId, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n if (options.status) {\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n WHERE status = ?\n ORDER BY updated_at DESC LIMIT ?`,\n args: [options.status, limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n }\n const { rows } = await client.execute({\n sql: `SELECT * FROM agent_harness_sessions\n ORDER BY updated_at DESC LIMIT ?`,\n args: [limit],\n });\n return rows.map(rowToHarnessSession).filter(isStoredSession);\n}\n\nexport async function markAgentHarnessSessionStopped(\n id: string,\n status: Extract<\n AgentHarnessSessionStatus,\n \"stopped\" | \"destroyed\" | \"errored\"\n >,\n): Promise<StoredAgentHarnessSession | null> {\n return updateAgentHarnessSession(id, {\n status,\n stoppedAt: Date.now(),\n pendingApproval: null,\n });\n}\n\nfunction rowToHarnessSession(row: unknown): StoredAgentHarnessSession | null {\n if (!row || typeof row !== \"object\") return null;\n const record = row as Record<string, unknown>;\n return {\n id: String(record.id),\n harnessName: String(record.harness_name),\n threadId: String(record.thread_id),\n runId: stringOrNull(record.run_id),\n providerSessionId: stringOrNull(record.provider_session_id),\n status: normalizeStatus(record.status),\n resumeState: parseJson(record.resume_state),\n workspaceRef: stringOrNull(record.workspace_ref),\n pendingApproval: parseJson(record.pending_approval),\n ownerEmail: stringOrNull(record.owner_email),\n orgId: stringOrNull(record.org_id),\n createdAt: numberValue(record.created_at),\n updatedAt: numberValue(record.updated_at),\n stoppedAt:\n record.stopped_at === null || record.stopped_at === undefined\n ? null\n : numberValue(record.stopped_at),\n };\n}\n\nfunction isStoredSession(\n session: StoredAgentHarnessSession | null,\n): session is StoredAgentHarnessSession {\n return Boolean(session);\n}\n\nfunction normalizeStatus(value: unknown): AgentHarnessSessionStatus {\n return value === \"running\" ||\n value === \"idle\" ||\n value === \"stopped\" ||\n value === \"errored\" ||\n value === \"destroyed\"\n ? value\n : \"idle\";\n}\n\nfunction serializeJson(value: unknown): string | null {\n if (value === undefined || value === null) return null;\n return JSON.stringify(value);\n}\n\nfunction parseJson(value: unknown): unknown {\n if (typeof value !== \"string\" || !value) return undefined;\n try {\n return JSON.parse(value);\n } catch {\n return undefined;\n }\n}\n\nfunction stringOrNull(value: unknown): string | null {\n return typeof value === \"string\" && value ? value : null;\n}\n\nfunction numberValue(value: unknown): number {\n return typeof value === \"number\" ? value : Number(value ?? 0);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/agent/observational-memory/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AA0CpB,6DAA6D;AAC7D,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D;AAqDD,MAAM,WAAW,8BAA+B,SAAQ,wBAAwB;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC3C;AAED,wDAAwD;AACxD,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,wBAAwB,CAAC,CAyCnC;AAED,MAAM,WAAW,8BAA+B,SAAQ,wBAAwB;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,IAAI,CAAC,EAAE,uBAAuB,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAiBrC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,8EAA8E;AAC9E,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CAcjB"}
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/agent/observational-memory/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EACV,wBAAwB,EACxB,wBAAwB,EACxB,uBAAuB,EACxB,MAAM,YAAY,CAAC;AAoEpB,6DAA6D;AAC7D,wBAAgB,oCAAoC,IAAI,IAAI,CAE3D;AAqDD,MAAM,WAAW,8BAA+B,SAAQ,wBAAwB;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,uBAAuB,CAAC;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC3C;AAED,wDAAwD;AACxD,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,8BAA8B,GACpC,OAAO,CAAC,wBAAwB,CAAC,CAyCnC;AAED,MAAM,WAAW,8BAA+B,SAAQ,wBAAwB;IAC9E,QAAQ,EAAE,MAAM,CAAC;IACjB,wDAAwD;IACxD,IAAI,CAAC,EAAE,uBAAuB,CAAC;CAChC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,8BAA8B,GACtC,OAAO,CAAC,wBAAwB,EAAE,CAAC,CAiBrC;AAED;;;;GAIG;AACH,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CAejB;AAED,8EAA8E;AAC9E,wBAAsB,uBAAuB,CAC3C,OAAO,EAAE,wBAAwB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GACvD,OAAO,CAAC,MAAM,CAAC,CAcjB"}
@@ -11,8 +11,8 @@
11
11
  * suspenders pattern as `chat-threads/store.ts`), so OM works in tests and at
12
12
  * runtime even before the migration plugin is registered.
13
13
  */
14
- import { getDbExec } from "../../db/client.js";
15
- import { isPostgres } from "../../db/client.js";
14
+ import { getDbExec, isPostgres } from "../../db/client.js";
15
+ import { ensureTableExists, ensureIndexExists } from "../../db/ddl-guard.js";
16
16
  let tableReady = null;
17
17
  async function ensureTable() {
18
18
  if (tableReady)
@@ -20,7 +20,7 @@ async function ensureTable() {
20
20
  tableReady = (async () => {
21
21
  const client = getDbExec();
22
22
  const intType = isPostgres() ? "BIGINT" : "INTEGER";
23
- await client.execute(`CREATE TABLE IF NOT EXISTS observational_memory (
23
+ const createSql = `CREATE TABLE IF NOT EXISTS observational_memory (
24
24
  id TEXT PRIMARY KEY,
25
25
  thread_id TEXT NOT NULL,
26
26
  tier TEXT NOT NULL,
@@ -34,11 +34,33 @@ async function ensureTable() {
34
34
  owner_email TEXT NOT NULL,
35
35
  org_id TEXT,
36
36
  visibility TEXT NOT NULL DEFAULT 'private'
37
- )`);
38
- await client.execute(`CREATE INDEX IF NOT EXISTS observational_memory_thread_tier_idx
39
- ON observational_memory(thread_id, tier, created_at)`);
40
- await client.execute(`CREATE INDEX IF NOT EXISTS observational_memory_thread_owner_idx
41
- ON observational_memory(thread_id, owner_email)`);
37
+ )`;
38
+ if (isPostgres()) {
39
+ // PG-guard: probe information_schema / pg_indexes before issuing DDL to
40
+ // avoid ACCESS EXCLUSIVE lock contention in fresh background-worker processes.
41
+ await ensureTableExists("observational_memory", createSql);
42
+ await ensureIndexExists("observational_memory_thread_tier_idx", `CREATE INDEX IF NOT EXISTS observational_memory_thread_tier_idx
43
+ ON observational_memory(thread_id, tier, created_at)`);
44
+ await ensureIndexExists("observational_memory_thread_owner_idx", `CREATE INDEX IF NOT EXISTS observational_memory_thread_owner_idx
45
+ ON observational_memory(thread_id, owner_email)`);
46
+ return;
47
+ }
48
+ // SQLite (local dev): no lock problem — keep the original behaviour.
49
+ await client.execute(createSql);
50
+ try {
51
+ await client.execute(`CREATE INDEX IF NOT EXISTS observational_memory_thread_tier_idx
52
+ ON observational_memory(thread_id, tier, created_at)`);
53
+ }
54
+ catch {
55
+ // Index already exists.
56
+ }
57
+ try {
58
+ await client.execute(`CREATE INDEX IF NOT EXISTS observational_memory_thread_owner_idx
59
+ ON observational_memory(thread_id, owner_email)`);
60
+ }
61
+ catch {
62
+ // Index already exists.
63
+ }
42
64
  })().catch((err) => {
43
65
  // Reset so a transient failure (e.g. SQLITE_BUSY on HMR) can retry.
44
66
  tableReady = null;