@cursor/july 0.1.7 → 0.1.9

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 (210) hide show
  1. package/dist/bin/agent-serve.js +31 -2
  2. package/dist/channels/slack/channel-watch.d.ts +6 -1
  3. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  4. package/dist/channels/slack/channel-watch.js +22 -1
  5. package/dist/channels/slack/cursor-account.d.ts +4 -0
  6. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.js +12 -2
  8. package/dist/channels/slack/defaults.d.ts +10 -5
  9. package/dist/channels/slack/defaults.d.ts.map +1 -1
  10. package/dist/channels/slack/defaults.js +39 -17
  11. package/dist/channels/slack/dispatch.d.ts +18 -0
  12. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  13. package/dist/channels/slack/dispatch.js +141 -38
  14. package/dist/channels/slack/inbound.d.ts +13 -3
  15. package/dist/channels/slack/inbound.d.ts.map +1 -1
  16. package/dist/channels/slack/inbound.js +17 -9
  17. package/dist/channels/slack/index.d.ts +4 -0
  18. package/dist/channels/slack/index.d.ts.map +1 -1
  19. package/dist/channels/slack/index.js +4 -0
  20. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  21. package/dist/channels/slack/live-delivery.js +1 -0
  22. package/dist/channels/slack/manifest.d.ts.map +1 -1
  23. package/dist/channels/slack/manifest.js +3 -0
  24. package/dist/channels/slack/nudge-carrier.d.ts +19 -0
  25. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
  26. package/dist/channels/slack/nudge-carrier.js +166 -0
  27. package/dist/channels/slack/nudge-store.d.ts +61 -0
  28. package/dist/channels/slack/nudge-store.d.ts.map +1 -0
  29. package/dist/channels/slack/nudge-store.js +266 -0
  30. package/dist/channels/slack/post-update-delivery.js +318 -0
  31. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  32. package/dist/channels/slack/slack-channel.js +74 -25
  33. package/dist/channels/slack/stop.d.ts +14 -0
  34. package/dist/channels/slack/stop.d.ts.map +1 -0
  35. package/dist/channels/slack/stop.js +22 -0
  36. package/dist/channels/slack/thread-context.d.ts +81 -0
  37. package/dist/channels/slack/thread-context.d.ts.map +1 -0
  38. package/dist/channels/slack/thread-context.js +470 -0
  39. package/dist/channels/slack/types.d.ts +27 -0
  40. package/dist/channels/slack/types.d.ts.map +1 -1
  41. package/dist/docs/404.html +2 -2
  42. package/dist/docs/ab.html +4 -4
  43. package/dist/docs/assets/{app.COTN7wgo.js → app.Ciosf24M.js} +1 -1
  44. package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +1 -0
  45. package/dist/docs/assets/chunks/{VPLocalSearchBox.BW3TBdT0.js → VPLocalSearchBox.BcRDly7n.js} +1 -1
  46. package/dist/docs/assets/chunks/{theme.BEJW0vE7.js → theme.CMpbjdfx.js} +2 -2
  47. package/dist/docs/assets/{deployment.md.BtfEsc9S.js → deployment.md.DC5-ImcM.js} +1 -1
  48. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.js → example-agents_approval-buddy.md.DFGBYLcc.js} +2 -2
  49. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.lean.js → example-agents_approval-buddy.md.DFGBYLcc.lean.js} +1 -1
  50. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.js +2 -0
  51. package/dist/docs/assets/example-agents_index.md.BPXjUtRB.lean.js +1 -0
  52. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
  53. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
  54. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.js → example-agents_security-reviewer.md.CE9v-aKm.js} +2 -2
  55. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.lean.js → example-agents_security-reviewer.md.CE9v-aKm.lean.js} +1 -1
  56. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
  57. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
  58. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
  59. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
  60. package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.DRTdnFvd.js} +9 -2
  61. package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.DRTdnFvd.lean.js} +1 -1
  62. package/dist/docs/building-with-agents.html +4 -4
  63. package/dist/docs/concepts.html +4 -4
  64. package/dist/docs/deployment.html +6 -6
  65. package/dist/docs/evals.html +4 -4
  66. package/dist/docs/example-agents/approval-buddy.html +5 -5
  67. package/dist/docs/example-agents/benny.html +5 -5
  68. package/dist/docs/example-agents/bugbot.html +5 -5
  69. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  70. package/dist/docs/example-agents/codeowners-review.html +4 -4
  71. package/dist/docs/example-agents/concierge.html +4 -4
  72. package/dist/docs/example-agents/fsd.html +4 -4
  73. package/dist/docs/example-agents/index.html +6 -6
  74. package/dist/docs/example-agents/knowledge-base.html +4 -4
  75. package/dist/docs/example-agents/oncall.html +34 -0
  76. package/dist/docs/example-agents/security-reviewer.html +6 -6
  77. package/dist/docs/example-agents/slack-agent.html +4 -4
  78. package/dist/docs/example-agents/weather-agent.html +4 -4
  79. package/dist/docs/guides/agent-to-agent.html +4 -4
  80. package/dist/docs/guides/cloud-runtime.html +4 -4
  81. package/dist/docs/guides/github.html +4 -4
  82. package/dist/docs/guides/human-in-the-loop.html +4 -4
  83. package/dist/docs/guides/slack.html +7 -6
  84. package/dist/docs/guides/webhooks.html +4 -4
  85. package/dist/docs/hashmap.json +1 -1
  86. package/dist/docs/hillclimbing.html +4 -4
  87. package/dist/docs/index.html +4 -4
  88. package/dist/docs/quickstart.html +4 -4
  89. package/dist/docs/reference/agent-config.html +4 -4
  90. package/dist/docs/reference/channels.html +4 -4
  91. package/dist/docs/reference/cli.html +4 -4
  92. package/dist/docs/reference/connections.html +4 -4
  93. package/dist/docs/reference/hooks.html +4 -4
  94. package/dist/docs/reference/http-api.html +4 -4
  95. package/dist/docs/reference/instructions.html +4 -4
  96. package/dist/docs/reference/playground.html +4 -4
  97. package/dist/docs/reference/project-layout.html +4 -4
  98. package/dist/docs/reference/schedules.html +4 -4
  99. package/dist/docs/reference/sessions.html +5 -5
  100. package/dist/docs/reference/skills.html +4 -4
  101. package/dist/docs/reference/subagents.html +4 -4
  102. package/dist/docs/reference/tools.html +4 -4
  103. package/dist/docs/scaffolding-agents.html +4 -4
  104. package/dist/docs/storage.html +13 -6
  105. package/dist/docs/troubleshooting.html +4 -4
  106. package/dist/internal/cli-deploy.d.ts.map +1 -1
  107. package/dist/internal/cli-deploy.js +45 -18
  108. package/dist/internal/cli-mcp.d.ts +64 -0
  109. package/dist/internal/cli-mcp.d.ts.map +1 -0
  110. package/dist/internal/cli-mcp.js +570 -0
  111. package/dist/internal/deploy-client.d.ts +7 -1
  112. package/dist/internal/deploy-client.d.ts.map +1 -1
  113. package/dist/internal/deploy-client.js +3 -0
  114. package/dist/internal/deploy-source.d.ts +3 -0
  115. package/dist/internal/deploy-source.d.ts.map +1 -1
  116. package/dist/internal/deploy-source.js +25 -7
  117. package/dist/internal/discovery.js +1 -1
  118. package/dist/internal/distribution.d.ts.map +1 -1
  119. package/dist/internal/distribution.js +3 -0
  120. package/dist/internal/host-kv.d.ts +17 -0
  121. package/dist/internal/host-kv.d.ts.map +1 -0
  122. package/dist/internal/host-kv.js +77 -0
  123. package/dist/internal/hosting.d.ts +12 -2
  124. package/dist/internal/hosting.d.ts.map +1 -1
  125. package/dist/internal/hosting.js +21 -4
  126. package/dist/internal/json-dir-store.js +100 -0
  127. package/dist/internal/logs-client.d.ts +16 -2
  128. package/dist/internal/logs-client.d.ts.map +1 -1
  129. package/dist/internal/logs-client.js +51 -16
  130. package/dist/internal/server.d.ts.map +1 -1
  131. package/dist/internal/server.js +8 -0
  132. package/dist/internal/session-engine.d.ts +42 -7
  133. package/dist/internal/session-engine.d.ts.map +1 -1
  134. package/dist/internal/session-engine.js +276 -66
  135. package/dist/internal/storage-coordinator.d.ts +18 -1
  136. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  137. package/dist/internal/storage-coordinator.js +43 -0
  138. package/dist/internal/workspace.d.ts +19 -0
  139. package/dist/internal/workspace.d.ts.map +1 -1
  140. package/dist/internal/workspace.js +47 -13
  141. package/dist/playground/assets/{index-72vCOBWO.js → index-C0-QEG4O.js} +42 -42
  142. package/dist/playground/assets/index-z3JY9O2x.css +1 -0
  143. package/dist/playground/index.html +2 -2
  144. package/dist/storage-backends/cursor-hosted.d.ts +65 -0
  145. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
  146. package/dist/storage-backends/cursor-hosted.js +203 -0
  147. package/dist/storage-backends/file-kv.d.ts +42 -0
  148. package/dist/storage-backends/file-kv.d.ts.map +1 -0
  149. package/dist/storage-backends/file-kv.js +190 -0
  150. package/dist/storage-backends/postgres-kv.d.ts +82 -0
  151. package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
  152. package/dist/storage-backends/postgres-kv.js +185 -0
  153. package/dist/storage.d.ts +10 -1
  154. package/dist/storage.d.ts.map +1 -1
  155. package/dist/storage.js +10 -1
  156. package/dist/types.d.ts +79 -0
  157. package/dist/types.d.ts.map +1 -1
  158. package/docs/.vitepress/cache/deps/package.json +3 -0
  159. package/docs/deployment.md +7 -3
  160. package/docs/example-agents/approval-buddy.md +1 -1
  161. package/docs/example-agents/index.md +15 -10
  162. package/docs/example-agents/oncall.md +216 -0
  163. package/docs/example-agents/security-reviewer.md +5 -5
  164. package/docs/guides/slack.md +11 -2
  165. package/docs/reference/sessions.md +13 -4
  166. package/docs/storage.md +41 -0
  167. package/package.json +22 -1
  168. package/skills/framework-map/SKILL.md +6 -4
  169. package/src/bin/agent-serve.ts +32 -2
  170. package/src/channels/slack/channel-watch.ts +28 -1
  171. package/src/channels/slack/cursor-account.ts +20 -0
  172. package/src/channels/slack/defaults.ts +51 -20
  173. package/src/channels/slack/dispatch.ts +215 -40
  174. package/src/channels/slack/inbound.ts +30 -10
  175. package/src/channels/slack/index.ts +4 -0
  176. package/src/channels/slack/live-delivery.ts +1 -0
  177. package/src/channels/slack/manifest.ts +3 -0
  178. package/src/channels/slack/nudge-carrier.ts +182 -0
  179. package/src/channels/slack/nudge-store.ts +308 -0
  180. package/src/channels/slack/slack-channel.ts +106 -31
  181. package/src/channels/slack/stop.ts +25 -0
  182. package/src/channels/slack/thread-context.ts +572 -0
  183. package/src/channels/slack/types.ts +27 -0
  184. package/src/internal/cli-deploy.ts +58 -20
  185. package/src/internal/cli-mcp.ts +708 -0
  186. package/src/internal/deploy-client.ts +11 -0
  187. package/src/internal/deploy-source.ts +24 -6
  188. package/src/internal/discovery.ts +1 -1
  189. package/src/internal/distribution.ts +3 -0
  190. package/src/internal/host-kv.ts +85 -0
  191. package/src/internal/hosting.ts +24 -4
  192. package/src/internal/logs-client.ts +67 -21
  193. package/src/internal/server.ts +31 -0
  194. package/src/internal/session-engine.ts +379 -61
  195. package/src/internal/storage-coordinator.ts +41 -0
  196. package/src/internal/workspace.ts +48 -11
  197. package/src/storage-backends/cursor-hosted.ts +246 -0
  198. package/src/storage-backends/file-kv.ts +221 -0
  199. package/src/storage-backends/postgres-kv.ts +253 -0
  200. package/src/storage.ts +11 -1
  201. package/src/types.ts +83 -0
  202. package/dist/docs/assets/chunks/@localSearchIndexroot.B7UcKvIn.js +0 -1
  203. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
  204. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.lean.js +0 -1
  205. package/dist/internal/persistence-coordinator.d.ts +0 -127
  206. package/dist/internal/persistence-coordinator.d.ts.map +0 -1
  207. package/dist/persistence.d.ts +0 -184
  208. package/dist/persistence.d.ts.map +0 -1
  209. package/dist/playground/assets/index-BjnMwYoR.css +0 -1
  210. /package/dist/docs/assets/{deployment.md.BtfEsc9S.lean.js → deployment.md.DC5-ImcM.lean.js} +0 -0
@@ -8,8 +8,8 @@
8
8
  />
9
9
  <meta name="viewport" content="width=device-width, initial-scale=1" />
10
10
  <title>agent-serve playground</title>
11
- <script type="module" crossorigin src="./assets/index-72vCOBWO.js"></script>
12
- <link rel="stylesheet" crossorigin href="./assets/index-BjnMwYoR.css">
11
+ <script type="module" crossorigin src="./assets/index-C0-QEG4O.js"></script>
12
+ <link rel="stylesheet" crossorigin href="./assets/index-z3JY9O2x.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Cursor-managed durable storage for hosted agents.
3
+ *
4
+ * Talks to the control-plane storage proxy
5
+ * (`POST /internal/agent-serve/storage/*`) using the engine's pod
6
+ * `CURSOR_API_KEY`. The server resolves `deployment_id` from that service
7
+ * account — engines never receive a Bugbot DSN and cannot address another
8
+ * deployment's rows.
9
+ *
10
+ * On Cursor-managed hosting the start script pins `CURSOR_API_BASE_URL` to
11
+ * `https://api.cursor.com` (where the proxy is mounted). Locally, point
12
+ * `CURSOR_API_BASE_URL` at a backend that mounts Agent Serve routes and use
13
+ * a deployment SA key, or use `@cursor/july/storage/postgres-kv` /
14
+ * `file-kv` instead.
15
+ *
16
+ * ```ts
17
+ * import { defineStorage } from "@cursor/july/storage";
18
+ * import { cursorHostedStorage } from "@cursor/july/storage/cursor-hosted";
19
+ *
20
+ * export default defineStorage({
21
+ * ...cursorHostedStorage(),
22
+ * });
23
+ * ```
24
+ */
25
+ import type { StorageABTable, StorageConfig, StorageEvalsTable } from "../storage.js";
26
+ /**
27
+ * Reserved so user `secrets set` cannot smuggle a Bugbot DSN into the pod.
28
+ * Not injected by Cursor hosting (storage is HTTP-proxied).
29
+ */
30
+ export declare const AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR: "BUGBOTDB_URL";
31
+ /**
32
+ * Diagnostic / self-host deployment id. Not the hosted-storage authz
33
+ * boundary — the proxy scopes rows from the authenticated service account.
34
+ */
35
+ export declare const AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR: "AGENT_SERVE_DEPLOYMENT_ID";
36
+ /**
37
+ * Platform-managed secret / env names. User `secrets set` / DELETE cannot
38
+ * claim these.
39
+ */
40
+ export declare const CURSOR_HOSTED_STORAGE_ENV_VARS: readonly [
41
+ typeof AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR,
42
+ typeof AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR
43
+ ];
44
+ export interface CursorHostedStorageOptions {
45
+ /**
46
+ * Control-plane base URL (default: `CURSOR_API_BASE_URL`, else
47
+ * `https://api.cursor.com`).
48
+ */
49
+ baseUrl?: string;
50
+ /** Bearer credential (default: `CURSOR_API_KEY`). */
51
+ apiKey?: string;
52
+ /** Injected for tests. */
53
+ fetch?: typeof fetch;
54
+ }
55
+ type CursorHostedStorageMethods = Required<Pick<StorageConfig, "put" | "get" | "delete" | "list">> & {
56
+ evals: StorageEvalsTable;
57
+ abs: StorageABTable;
58
+ };
59
+ /**
60
+ * Cursor-hosted `defineStorage` methods (control-plane storage proxy →
61
+ * Bugbot `agent_serve_*` tables). Spread into `defineStorage({...})`.
62
+ */
63
+ export declare function cursorHostedStorage(options?: CursorHostedStorageOptions): CursorHostedStorageMethods;
64
+ export {};
65
+ //# sourceMappingURL=cursor-hosted.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cursor-hosted.d.ts","sourceRoot":"","sources":["../../src/storage-backends/cursor-hosted.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAYvB;;;GAGG;AACH,eAAO,MAAM,+CAA+C,EAC1D,cAAuB,CAAC;AAE1B;;;GAGG;AACH,eAAO,MAAM,iCAAiC,EAC5C,2BAAoC,CAAC;AAEvC;;;GAGG;AACH,eAAO,MAAM,8BAA8B,EAAE,SAAS;IACpD,OAAO,+CAA+C;IACtD,OAAO,iCAAiC;CAIzC,CAAC;AAEF,MAAM,WAAW,0BAA0B;IACzC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,qDAAqD;IACrD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,0BAA0B;IAC1B,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,KAAK,0BAA0B,GAAG,QAAQ,CACxC,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC,CACvD,GAAG;IACF,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC;AA8DF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,GAAE,0BAA+B,GACvC,0BAA0B,CA0F5B"}
@@ -0,0 +1,203 @@
1
+ /**
2
+ * Cursor-managed durable storage for hosted agents.
3
+ *
4
+ * Talks to the control-plane storage proxy
5
+ * (`POST /internal/agent-serve/storage/*`) using the engine's pod
6
+ * `CURSOR_API_KEY`. The server resolves `deployment_id` from that service
7
+ * account — engines never receive a Bugbot DSN and cannot address another
8
+ * deployment's rows.
9
+ *
10
+ * On Cursor-managed hosting the start script pins `CURSOR_API_BASE_URL` to
11
+ * `https://api.cursor.com` (where the proxy is mounted). Locally, point
12
+ * `CURSOR_API_BASE_URL` at a backend that mounts Agent Serve routes and use
13
+ * a deployment SA key, or use `@cursor/july/storage/postgres-kv` /
14
+ * `file-kv` instead.
15
+ *
16
+ * ```ts
17
+ * import { defineStorage } from "@cursor/july/storage";
18
+ * import { cursorHostedStorage } from "@cursor/july/storage/cursor-hosted";
19
+ *
20
+ * export default defineStorage({
21
+ * ...cursorHostedStorage(),
22
+ * });
23
+ * ```
24
+ */
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ /**
35
+ * Same default as {@link cursorExternalApiUrl} in the CLI credentials module,
36
+ * inlined here so this storage entrypoint does not import CLI/zod code.
37
+ * Hosted monorepo examples load this file from `packages/agent-serve/src`
38
+ * while `npm install` runs in the example package — a credentials import
39
+ * would resolve `zod` from the wrong `node_modules` tree and crash serve.
40
+ */
41
+ const DEFAULT_CURSOR_EXTERNAL_API_URL = "https://api.cursor.com";
42
+ /**
43
+ * Reserved so user `secrets set` cannot smuggle a Bugbot DSN into the pod.
44
+ * Not injected by Cursor hosting (storage is HTTP-proxied).
45
+ */
46
+ export const AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR = "BUGBOTDB_URL";
47
+ /**
48
+ * Diagnostic / self-host deployment id. Not the hosted-storage authz
49
+ * boundary — the proxy scopes rows from the authenticated service account.
50
+ */
51
+ export const AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR = "AGENT_SERVE_DEPLOYMENT_ID";
52
+ /**
53
+ * Platform-managed secret / env names. User `secrets set` / DELETE cannot
54
+ * claim these.
55
+ */
56
+ export const CURSOR_HOSTED_STORAGE_ENV_VARS = [
57
+ AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR,
58
+ AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR,
59
+ ];
60
+ function resolveApiKey(options) {
61
+ var _a;
62
+ var _b;
63
+ const key = (_a = ((_b = options.apiKey) !== null && _b !== void 0 ? _b : process.env.CURSOR_API_KEY)) === null || _a === void 0 ? void 0 : _a.trim();
64
+ if (key === undefined || key === "") {
65
+ throw new Error("cursorHostedStorage requires CURSOR_API_KEY (deployment pod credential on Cursor hosting)");
66
+ }
67
+ return key;
68
+ }
69
+ function resolveBaseUrl(options) {
70
+ var _a, _b;
71
+ const raw = ((_b = (_a = options.baseUrl) !== null && _a !== void 0 ? _a : process.env.CURSOR_API_BASE_URL) !== null && _b !== void 0 ? _b : DEFAULT_CURSOR_EXTERNAL_API_URL).trim();
72
+ return raw.endsWith("/") ? raw.slice(0, -1) : raw;
73
+ }
74
+ function storageRpc(options, path, body) {
75
+ return __awaiter(this, void 0, void 0, function* () {
76
+ var _a;
77
+ const baseUrl = resolveBaseUrl(options);
78
+ const apiKey = resolveApiKey(options);
79
+ const fetchImpl = (_a = options.fetch) !== null && _a !== void 0 ? _a : globalThis.fetch;
80
+ if (typeof fetchImpl !== "function") {
81
+ throw new Error("cursorHostedStorage requires fetch");
82
+ }
83
+ const response = yield fetchImpl(`${baseUrl}${path}`, {
84
+ method: "POST",
85
+ headers: {
86
+ Authorization: `Bearer ${apiKey}`,
87
+ "Content-Type": "application/json",
88
+ },
89
+ body: JSON.stringify(body),
90
+ });
91
+ const text = yield response.text();
92
+ let parsed;
93
+ try {
94
+ parsed = text.length === 0 ? {} : JSON.parse(text);
95
+ }
96
+ catch (_b) {
97
+ throw new Error(`cursorHostedStorage ${path} returned non-JSON (${response.status})`);
98
+ }
99
+ if (!response.ok) {
100
+ const error = parsed !== null &&
101
+ typeof parsed === "object" &&
102
+ "error" in parsed &&
103
+ typeof parsed.error === "string"
104
+ ? parsed.error
105
+ : `HTTP ${response.status}`;
106
+ throw new Error(`cursorHostedStorage ${path}: ${error}`);
107
+ }
108
+ return parsed;
109
+ });
110
+ }
111
+ /**
112
+ * Cursor-hosted `defineStorage` methods (control-plane storage proxy →
113
+ * Bugbot `agent_serve_*` tables). Spread into `defineStorage({...})`.
114
+ */
115
+ export function cursorHostedStorage(options = {}) {
116
+ return {
117
+ put(key, value) {
118
+ return __awaiter(this, void 0, void 0, function* () {
119
+ yield storageRpc(options, "/internal/agent-serve/storage/kv/put", {
120
+ key,
121
+ value,
122
+ });
123
+ });
124
+ },
125
+ get(key) {
126
+ return __awaiter(this, void 0, void 0, function* () {
127
+ var _a;
128
+ const result = yield storageRpc(options, "/internal/agent-serve/storage/kv/get", { key });
129
+ if (!result.found) {
130
+ return undefined;
131
+ }
132
+ return (_a = result.value) !== null && _a !== void 0 ? _a : undefined;
133
+ });
134
+ },
135
+ delete(key) {
136
+ return __awaiter(this, void 0, void 0, function* () {
137
+ yield storageRpc(options, "/internal/agent-serve/storage/kv/delete", {
138
+ key,
139
+ });
140
+ });
141
+ },
142
+ list(prefix) {
143
+ return __awaiter(this, void 0, void 0, function* () {
144
+ const result = yield storageRpc(options, "/internal/agent-serve/storage/kv/list", { prefix });
145
+ return result.entries;
146
+ });
147
+ },
148
+ evals: {
149
+ put(run, ctx) {
150
+ return __awaiter(this, void 0, void 0, function* () {
151
+ yield storageRpc(options, "/internal/agent-serve/storage/evals/put", {
152
+ agentName: ctx.agentName,
153
+ run,
154
+ });
155
+ });
156
+ },
157
+ delete(runId, ctx) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ yield storageRpc(options, "/internal/agent-serve/storage/evals/delete", {
160
+ agentName: ctx.agentName,
161
+ runId,
162
+ });
163
+ });
164
+ },
165
+ list(ctx) {
166
+ return __awaiter(this, void 0, void 0, function* () {
167
+ const result = yield storageRpc(options, "/internal/agent-serve/storage/evals/list", { agentName: ctx.agentName });
168
+ return result.runs;
169
+ });
170
+ },
171
+ },
172
+ abs: {
173
+ putSample(sample, ctx) {
174
+ return __awaiter(this, void 0, void 0, function* () {
175
+ yield storageRpc(options, "/internal/agent-serve/storage/abs/put-sample", {
176
+ agentName: ctx.agentName,
177
+ sample,
178
+ });
179
+ });
180
+ },
181
+ putSnapshot(snapshot, ctx) {
182
+ return __awaiter(this, void 0, void 0, function* () {
183
+ yield storageRpc(options, "/internal/agent-serve/storage/abs/put-snapshot", {
184
+ agentName: ctx.agentName,
185
+ snapshot,
186
+ });
187
+ });
188
+ },
189
+ getSnapshot(ctx) {
190
+ return __awaiter(this, void 0, void 0, function* () {
191
+ var _a;
192
+ const result = yield storageRpc(options, "/internal/agent-serve/storage/abs/get-snapshot", {
193
+ agentName: ctx.agentName,
194
+ });
195
+ if (!result.found) {
196
+ return undefined;
197
+ }
198
+ return (_a = result.snapshot) !== null && _a !== void 0 ? _a : undefined;
199
+ });
200
+ },
201
+ },
202
+ };
203
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * File-backed `defineStorage` helper: a file-per-key KV store plus
3
+ * file-backed `evals` / `abs` tables.
4
+ *
5
+ * Implements the whole `defineStorage` surface with zero external
6
+ * dependencies: the four KV methods against a flat directory of JSON files
7
+ * (one file per key, filename = URI-encoded key), eval runs as
8
+ * `eval-runs/<agent>/<runId>.json`, and A/B metrics as
9
+ * `ab/<agent>/samples.ndjson` (append-only) plus `ab/<agent>/snapshot.json`
10
+ * (latest aggregate). KV keys carry the agent name inside the framework
11
+ * key scheme, but the evals/abs tables have no such scheme — the agent
12
+ * subdirectory keeps agents sharing one storage `dir` from mixing or
13
+ * overwriting each other's history. Swap this for a real backend
14
+ * (Postgres, S3, …) in production; the contracts are identical.
15
+ *
16
+ * Layout notes:
17
+ * - KV filenames encode the full key (`encodeURIComponent(key) + ".json"`),
18
+ * so `list(prefix)` is a readdir + decode + filter, returned in key
19
+ * order as the contract requires.
20
+ * - `put` writes via tmp + rename: the framework serializes writes, but a
21
+ * restore-path `get`/`list` can run concurrently and must never see a
22
+ * torn file.
23
+ * - `get` distinguishes a definitive miss (ENOENT → `undefined`) from a
24
+ * real failure (anything else throws) — on the lazy-restore path a
25
+ * throw correctly fails the follow-up instead of forking the session.
26
+ */
27
+ import type { StorageABTable, StorageConfig, StorageEvalsTable } from "../storage.js";
28
+ export interface FileKvOptions {
29
+ /**
30
+ * Directory holding the storage files, relative to the agent project root
31
+ * (default `".agent-serve/kv"`). Absolute paths are used as-is.
32
+ */
33
+ dir?: string;
34
+ }
35
+ type FileKvMethods = Required<Pick<StorageConfig, "put" | "get" | "delete" | "list">> & {
36
+ evals: StorageEvalsTable;
37
+ abs: StorageABTable;
38
+ };
39
+ /** File-backed `defineStorage` methods, ready to spread into `defineStorage({...})`. */
40
+ export declare function fileKv(options?: FileKvOptions): FileKvMethods;
41
+ export {};
42
+ //# sourceMappingURL=file-kv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-kv.d.ts","sourceRoot":"","sources":["../../src/storage-backends/file-kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAeH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAGb,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,MAAM,WAAW,aAAa;IAC5B;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,KAAK,aAAa,GAAG,QAAQ,CAC3B,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC,CACvD,GAAG;IACF,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC;AAEF,wFAAwF;AACxF,wBAAgB,MAAM,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,aAAa,CA+I7D"}
@@ -0,0 +1,190 @@
1
+ /**
2
+ * File-backed `defineStorage` helper: a file-per-key KV store plus
3
+ * file-backed `evals` / `abs` tables.
4
+ *
5
+ * Implements the whole `defineStorage` surface with zero external
6
+ * dependencies: the four KV methods against a flat directory of JSON files
7
+ * (one file per key, filename = URI-encoded key), eval runs as
8
+ * `eval-runs/<agent>/<runId>.json`, and A/B metrics as
9
+ * `ab/<agent>/samples.ndjson` (append-only) plus `ab/<agent>/snapshot.json`
10
+ * (latest aggregate). KV keys carry the agent name inside the framework
11
+ * key scheme, but the evals/abs tables have no such scheme — the agent
12
+ * subdirectory keeps agents sharing one storage `dir` from mixing or
13
+ * overwriting each other's history. Swap this for a real backend
14
+ * (Postgres, S3, …) in production; the contracts are identical.
15
+ *
16
+ * Layout notes:
17
+ * - KV filenames encode the full key (`encodeURIComponent(key) + ".json"`),
18
+ * so `list(prefix)` is a readdir + decode + filter, returned in key
19
+ * order as the contract requires.
20
+ * - `put` writes via tmp + rename: the framework serializes writes, but a
21
+ * restore-path `get`/`list` can run concurrently and must never see a
22
+ * torn file.
23
+ * - `get` distinguishes a definitive miss (ENOENT → `undefined`) from a
24
+ * real failure (anything else throws) — on the lazy-restore path a
25
+ * throw correctly fails the follow-up instead of forking the session.
26
+ */
27
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
28
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
29
+ return new (P || (P = Promise))(function (resolve, reject) {
30
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
31
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
32
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
33
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
34
+ });
35
+ };
36
+ import { randomUUID } from "node:crypto";
37
+ import { appendFile, mkdir, readdir, readFile, rename, rm, writeFile, } from "node:fs/promises";
38
+ import { join } from "node:path";
39
+ /** File-backed `defineStorage` methods, ready to spread into `defineStorage({...})`. */
40
+ export function fileKv(options) {
41
+ const resolveDir = (ctx) => { var _a; return join(ctx.projectRoot, (_a = options === null || options === void 0 ? void 0 : options.dir) !== null && _a !== void 0 ? _a : ".agent-serve/kv"); };
42
+ // The evals/abs tables are scoped per agent (KV keys already carry the
43
+ // agent name via the framework key scheme).
44
+ const evalRunsDir = (ctx) => join(resolveDir(ctx), "eval-runs", encodeURIComponent(ctx.agentName));
45
+ const abDir = (ctx) => join(resolveDir(ctx), "ab", encodeURIComponent(ctx.agentName));
46
+ const filePath = (ctx, key) => join(resolveDir(ctx), `${encodeURIComponent(key)}.json`);
47
+ const readKey = (ctx, key) => __awaiter(this, void 0, void 0, function* () {
48
+ try {
49
+ return JSON.parse(yield readFile(filePath(ctx, key), "utf8"));
50
+ }
51
+ catch (error) {
52
+ if (error.code === "ENOENT") {
53
+ return undefined; // definitive miss — key was never written
54
+ }
55
+ throw error; // real failure: let lazy restore fail the follow-up
56
+ }
57
+ });
58
+ return {
59
+ put(key, value, ctx) {
60
+ return __awaiter(this, void 0, void 0, function* () {
61
+ yield writeJsonAtomic({
62
+ dir: resolveDir(ctx),
63
+ fileName: `${encodeURIComponent(key)}.json`,
64
+ value,
65
+ });
66
+ });
67
+ },
68
+ get(key, ctx) {
69
+ return readKey(ctx, key);
70
+ },
71
+ delete(key, ctx) {
72
+ return __awaiter(this, void 0, void 0, function* () {
73
+ yield rm(filePath(ctx, key), { force: true });
74
+ });
75
+ },
76
+ list(prefix, ctx) {
77
+ return __awaiter(this, void 0, void 0, function* () {
78
+ let names;
79
+ try {
80
+ names = yield readdir(resolveDir(ctx));
81
+ }
82
+ catch (error) {
83
+ if (error.code === "ENOENT") {
84
+ return []; // nothing stored yet
85
+ }
86
+ throw error;
87
+ }
88
+ const keys = names
89
+ .filter((name) => name.endsWith(".json"))
90
+ .map((name) => decodeURIComponent(name.slice(0, -".json".length)))
91
+ .filter((key) => key.startsWith(prefix))
92
+ .sort();
93
+ const entries = [];
94
+ for (const key of keys) {
95
+ const value = yield readKey(ctx, key);
96
+ if (value !== undefined) {
97
+ entries.push({ key, value });
98
+ }
99
+ }
100
+ return entries;
101
+ });
102
+ },
103
+ // Playground eval batches: one JSON file per run under eval-runs/<agent>/.
104
+ evals: {
105
+ put(run, ctx) {
106
+ return __awaiter(this, void 0, void 0, function* () {
107
+ yield writeJsonAtomic({
108
+ dir: evalRunsDir(ctx),
109
+ fileName: `${encodeURIComponent(run.runId)}.json`,
110
+ value: run,
111
+ });
112
+ });
113
+ },
114
+ delete(runId, ctx) {
115
+ return __awaiter(this, void 0, void 0, function* () {
116
+ yield rm(join(evalRunsDir(ctx), `${encodeURIComponent(runId)}.json`), {
117
+ force: true,
118
+ });
119
+ });
120
+ },
121
+ list(ctx) {
122
+ return __awaiter(this, void 0, void 0, function* () {
123
+ const dir = evalRunsDir(ctx);
124
+ let names;
125
+ try {
126
+ names = yield readdir(dir);
127
+ }
128
+ catch (error) {
129
+ if (error.code === "ENOENT") {
130
+ return [];
131
+ }
132
+ throw error;
133
+ }
134
+ const runs = [];
135
+ for (const name of names.filter((n) => n.endsWith(".json")).sort()) {
136
+ // One unreadable/corrupt file must not wipe the whole history:
137
+ // hydrate treats a throwing list() as "nothing saved".
138
+ try {
139
+ runs.push(JSON.parse(yield readFile(join(dir, name), "utf8")));
140
+ }
141
+ catch (_a) { }
142
+ }
143
+ return runs;
144
+ });
145
+ },
146
+ },
147
+ // A/B metrics: append-only sample log + latest aggregate snapshot,
148
+ // both under ab/<agent>/.
149
+ abs: {
150
+ putSample(sample, ctx) {
151
+ return __awaiter(this, void 0, void 0, function* () {
152
+ const dir = abDir(ctx);
153
+ yield mkdir(dir, { recursive: true });
154
+ yield appendFile(join(dir, "samples.ndjson"), `${JSON.stringify(sample)}\n`, "utf8");
155
+ });
156
+ },
157
+ putSnapshot(snapshot, ctx) {
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ yield writeJsonAtomic({
160
+ dir: abDir(ctx),
161
+ fileName: "snapshot.json",
162
+ value: snapshot,
163
+ });
164
+ });
165
+ },
166
+ getSnapshot(ctx) {
167
+ return __awaiter(this, void 0, void 0, function* () {
168
+ try {
169
+ return JSON.parse(yield readFile(join(abDir(ctx), "snapshot.json"), "utf8"));
170
+ }
171
+ catch (error) {
172
+ if (error.code === "ENOENT") {
173
+ return undefined;
174
+ }
175
+ throw error;
176
+ }
177
+ });
178
+ },
179
+ },
180
+ };
181
+ }
182
+ /** tmp + rename write so concurrent readers never see a torn file. */
183
+ function writeJsonAtomic(args) {
184
+ return __awaiter(this, void 0, void 0, function* () {
185
+ yield mkdir(args.dir, { recursive: true });
186
+ const tmp = join(args.dir, `.tmp-${randomUUID()}`);
187
+ yield writeFile(tmp, JSON.stringify(args.value), "utf8");
188
+ yield rename(tmp, join(args.dir, args.fileName));
189
+ });
190
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Postgres `defineStorage` helper on the agent-serve tables
3
+ * (backend/packages/prisma-bugbot):
4
+ *
5
+ * agent_serve_kv(deployment_id, key, value JSONB, updated_at)
6
+ * PRIMARY KEY (deployment_id, key)
7
+ * agent_serve_eval_runs(deployment_id, agent, run_id, run JSONB, updated_at)
8
+ * PRIMARY KEY (deployment_id, agent, run_id)
9
+ * agent_serve_ab(deployment_id, agent, kind, ref, value JSONB, updated_at)
10
+ * PRIMARY KEY (deployment_id, agent, kind, ref)
11
+ *
12
+ * For Cursor-managed hosting prefer `cursorHostedStorage` from
13
+ * `@cursor/july/storage/cursor-hosted` — same tables via a control-plane
14
+ * HTTP proxy (no pod-visible DSN). Use this helper when you supply
15
+ * `BUGBOTDB_URL` / `AGENT_SERVE_DEPLOYMENT_ID` yourself (self-host).
16
+ *
17
+ * `postgresStorage` implements the whole `defineStorage` surface as plain
18
+ * SQL: the four KV methods against the last-write-wins `agent_serve_kv`
19
+ * table (sessions, event chunks, continuations, reminders), the `evals`
20
+ * table against `agent_serve_eval_runs` (one row per playground eval
21
+ * batch), and the `abs` table against `agent_serve_ab` (`kind = 'sample'`
22
+ * rows per metric sample plus one `kind = 'snapshot'` row holding the
23
+ * latest aggregate).
24
+ *
25
+ * - KV `put` upserts on the primary key (LWW matches the framework contract).
26
+ * - KV `list` is a prefix range scan on the PK btree (`key >= p AND key < p||…`
27
+ * spelled as LIKE with escaped wildcards, ordered by key).
28
+ * - `get` returning no row is a definitive miss (`undefined`); a connection
29
+ * error throws, so lazy restore fails the follow-up instead of forking
30
+ * the session.
31
+ *
32
+ * Connection comes from `BUGBOTDB_URL` — the repo-wide convention for the
33
+ * bugbot Postgres database, whose Prisma package owns these tables' schema
34
+ * and migrations; locally it falls back to the dev bugbot DB. Non-local
35
+ * URLs enable TLS (`ssl.rejectUnauthorized`) because PlanetScale requires
36
+ * SSL even when the vault URL omits `sslmode`.
37
+ * `deploymentId` scopes rows per deployment (`AGENT_SERVE_DEPLOYMENT_ID`);
38
+ * the local default keeps dev runs of different checkouts from colliding
39
+ * only by agent name.
40
+ *
41
+ * Callers must pass `Pool` from their own `import { Pool } from "pg"`.
42
+ * This module must not import `pg` itself: Node resolves modules from
43
+ * *this* file's directory tree, so a `pg` dep declared only on the
44
+ * consuming agent package would never be found after a hosted install.
45
+ */
46
+ import type { StorageABTable, StorageConfig, StorageEvalsTable } from "../storage.js";
47
+ /** Minimal `pg.Pool` surface used here (avoids importing `pg` from this path). */
48
+ export interface PostgresQueryable {
49
+ query<T extends Record<string, unknown> = Record<string, unknown>>(text: string, values?: readonly unknown[]): Promise<{
50
+ rows: T[];
51
+ }>;
52
+ }
53
+ /** `pg.Pool` constructor supplied by the consuming example package. */
54
+ export type PostgresPoolConstructor = new (config?: {
55
+ connectionString?: string;
56
+ max?: number;
57
+ ssl?: boolean | {
58
+ rejectUnauthorized?: boolean;
59
+ };
60
+ }) => PostgresQueryable;
61
+ export interface PostgresStorageOptions {
62
+ /**
63
+ * Pass `Pool` from `import { Pool } from "pg"` in the example that declares
64
+ * the `pg` dependency (hosted installs put it in that package's
65
+ * `node_modules` only).
66
+ */
67
+ Pool: PostgresPoolConstructor;
68
+ /** Postgres connection string (default: env, then local bugbot dev DB). */
69
+ databaseUrl?: string;
70
+ /** Value for the tables' deployment_id column (default: env or "local-dev"). */
71
+ deploymentId?: string;
72
+ }
73
+ type PostgresStorageMethods = Required<Pick<StorageConfig, "put" | "get" | "delete" | "list">> & {
74
+ evals: StorageEvalsTable;
75
+ abs: StorageABTable;
76
+ };
77
+ /** True for local bugbot/dev URLs that do not speak TLS. */
78
+ export declare function isLocalPostgresUrl(connectionString: string): boolean;
79
+ /** Postgres-backed `defineStorage` methods, ready to spread into `defineStorage({...})`. */
80
+ export declare function postgresStorage(options: PostgresStorageOptions): PostgresStorageMethods;
81
+ export {};
82
+ //# sourceMappingURL=postgres-kv.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres-kv.d.ts","sourceRoot":"","sources":["../../src/storage-backends/postgres-kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAIH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EAEb,iBAAiB,EAClB,MAAM,eAAe,CAAC;AAGvB,kFAAkF;AAClF,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/D,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,SAAS,OAAO,EAAE,GAC1B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;CAC3B;AAED,uEAAuE;AACvE,MAAM,MAAM,uBAAuB,GAAG,KAAK,MAAM,CAAC,EAAE;IAClD,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,OAAO,GAAG;QAAE,kBAAkB,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC;CAClD,KAAK,iBAAiB,CAAC;AAExB,MAAM,WAAW,sBAAsB;IACrC;;;;OAIG;IACH,IAAI,EAAE,uBAAuB,CAAC;IAC9B,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,KAAK,sBAAsB,GAAG,QAAQ,CACpC,IAAI,CAAC,aAAa,EAAE,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC,CACvD,GAAG;IACF,KAAK,EAAE,iBAAiB,CAAC;IACzB,GAAG,EAAE,cAAc,CAAC;CACrB,CAAC;AAEF,4DAA4D;AAC5D,wBAAgB,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAYpE;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAC7B,OAAO,EAAE,sBAAsB,GAC9B,sBAAsB,CA4HxB"}