@cursor/july 0.1.1 → 0.1.2

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 (199) hide show
  1. package/AGENTS.md +24 -2
  2. package/README.md +25 -15
  3. package/dist/bin/agent-serve.js +101 -12
  4. package/dist/channels/github/api.d.ts.map +1 -1
  5. package/dist/channels/github/api.js +31 -14
  6. package/dist/channels/github/cursor-account.d.ts +43 -0
  7. package/dist/channels/github/cursor-account.d.ts.map +1 -0
  8. package/dist/channels/github/cursor-account.js +95 -0
  9. package/dist/channels/github/github-channel.d.ts.map +1 -1
  10. package/dist/channels/github/github-channel.js +46 -9
  11. package/dist/channels/github/index.d.ts +2 -2
  12. package/dist/channels/github/index.js +2 -2
  13. package/dist/channels/github/types.d.ts +17 -0
  14. package/dist/channels/github/types.d.ts.map +1 -1
  15. package/dist/channels/slack/slack-channel.d.ts +8 -2
  16. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  17. package/dist/channels/slack/slack-channel.js +8 -0
  18. package/dist/channels/slack/types.d.ts +24 -3
  19. package/dist/channels/slack/types.d.ts.map +1 -1
  20. package/dist/channels/slack/types.js +15 -1
  21. package/dist/docs/404.html +2 -2
  22. package/dist/docs/ab.html +8 -8
  23. package/dist/docs/assets/{ab.md.COdXkces.js → ab.md.BMCZ6Hd7.js} +3 -3
  24. package/dist/docs/assets/{ab.md.COdXkces.lean.js → ab.md.BMCZ6Hd7.lean.js} +1 -1
  25. package/dist/docs/assets/{app.DqfFEmJd.js → app.Oje4vhlk.js} +1 -1
  26. package/dist/docs/assets/chunks/@localSearchIndexroot.zwQ9RCQ7.js +1 -0
  27. package/dist/docs/assets/chunks/{VPLocalSearchBox.BaLEdS15.js → VPLocalSearchBox.H5XZ2zCB.js} +1 -1
  28. package/dist/docs/assets/chunks/{theme.CZRvu_0q.js → theme.CTR_TuaE.js} +2 -2
  29. package/dist/docs/assets/{deployment.md.Dx1TYNk5.js → deployment.md.DTKwE15Z.js} +3 -3
  30. package/dist/docs/assets/{deployment.md.Dx1TYNk5.lean.js → deployment.md.DTKwE15Z.lean.js} +1 -1
  31. package/dist/docs/assets/{evals.md.DPZ_MAnI.js → evals.md.DAgEc_hL.js} +3 -3
  32. package/dist/docs/assets/{guides_agent-to-agent.md.CrtrsySy.js → guides_agent-to-agent.md.Bpzgq2Pq.js} +1 -1
  33. package/dist/docs/assets/{guides_cloud-runtime.md.CYlNMTNp.js → guides_cloud-runtime.md.gVzabdQL.js} +1 -1
  34. package/dist/docs/assets/{guides_github.md.DwbKhCeS.js → guides_github.md.DOOCpqsW.js} +11 -4
  35. package/dist/docs/assets/{guides_github.md.DwbKhCeS.lean.js → guides_github.md.DOOCpqsW.lean.js} +1 -1
  36. package/dist/docs/assets/{guides_human-in-the-loop.md.Dvuctx7s.js → guides_human-in-the-loop.md.DlUqsp1S.js} +2 -2
  37. package/dist/docs/assets/{guides_slack.md.bv41fHfW.js → guides_slack.md.CCwqHvSV.js} +4 -4
  38. package/dist/docs/assets/{guides_slack.md.bv41fHfW.lean.js → guides_slack.md.CCwqHvSV.lean.js} +1 -1
  39. package/dist/docs/assets/{guides_webhooks.md.hFTik3lf.js → guides_webhooks.md.B1EswtUu.js} +2 -2
  40. package/dist/docs/assets/index.md.m81y7TY7.js +20 -0
  41. package/dist/docs/assets/{index.md.BPKcj5AI.lean.js → index.md.m81y7TY7.lean.js} +1 -1
  42. package/dist/docs/assets/quickstart.md.CfU8_uTC.js +192 -0
  43. package/dist/docs/assets/quickstart.md.CfU8_uTC.lean.js +1 -0
  44. package/dist/docs/assets/{reference_agent-config.md.Bpd7HQwf.js → reference_agent-config.md.DrW2JUM8.js} +4 -4
  45. package/dist/docs/assets/{reference_agent-config.md.Bpd7HQwf.lean.js → reference_agent-config.md.DrW2JUM8.lean.js} +1 -1
  46. package/dist/docs/assets/{reference_channels.md.D7JTR03W.js → reference_channels.md.DdmiKgqf.js} +4 -4
  47. package/dist/docs/assets/{reference_channels.md.D7JTR03W.lean.js → reference_channels.md.DdmiKgqf.lean.js} +1 -1
  48. package/dist/docs/assets/{reference_connections.md.C3vNH_DE.js → reference_connections.md.zaEYCLHT.js} +1 -1
  49. package/dist/docs/assets/{reference_hooks.md.BCEc3MyM.js → reference_hooks.md.DyLVfE1O.js} +1 -1
  50. package/dist/docs/assets/{reference_hooks.md.BCEc3MyM.lean.js → reference_hooks.md.DyLVfE1O.lean.js} +1 -1
  51. package/dist/docs/assets/{reference_http-api.md.DBAahtdz.js → reference_http-api.md.Dx_nmDG6.js} +1 -1
  52. package/dist/docs/assets/{reference_instructions.md.BC05LEQ8.js → reference_instructions.md.CgoV-YEb.js} +9 -7
  53. package/dist/docs/assets/{reference_instructions.md.BC05LEQ8.lean.js → reference_instructions.md.CgoV-YEb.lean.js} +1 -1
  54. package/dist/docs/assets/{reference_schedules.md.D7qijxLk.js → reference_schedules.md.w_F2mXB6.js} +2 -2
  55. package/dist/docs/assets/{reference_skills.md.VQnlBT3Q.js → reference_skills.md.B_jHN7JL.js} +3 -3
  56. package/dist/docs/assets/{reference_skills.md.VQnlBT3Q.lean.js → reference_skills.md.B_jHN7JL.lean.js} +1 -1
  57. package/dist/docs/assets/{reference_subagents.md.CIRAVcPK.js → reference_subagents.md.zWAMNfi1.js} +1 -1
  58. package/dist/docs/assets/{reference_tools.md.DF5kwlt0.js → reference_tools.md.CqgJroI0.js} +2 -2
  59. package/dist/docs/assets/scaffolding-agents.md.C3pTrmoE.js +1 -0
  60. package/dist/docs/assets/scaffolding-agents.md.C3pTrmoE.lean.js +1 -0
  61. package/dist/docs/assets/storage.md.CVnInNiN.js +17 -0
  62. package/dist/docs/assets/storage.md.CVnInNiN.lean.js +1 -0
  63. package/dist/docs/building-with-agents.html +4 -4
  64. package/dist/docs/concepts.html +4 -4
  65. package/dist/docs/deployment.html +7 -7
  66. package/dist/docs/evals.html +7 -7
  67. package/dist/docs/guides/agent-to-agent.html +6 -6
  68. package/dist/docs/guides/cloud-runtime.html +5 -5
  69. package/dist/docs/guides/github.html +14 -7
  70. package/dist/docs/guides/human-in-the-loop.html +6 -6
  71. package/dist/docs/guides/slack.html +8 -8
  72. package/dist/docs/guides/webhooks.html +6 -6
  73. package/dist/docs/hashmap.json +1 -1
  74. package/dist/docs/hillclimbing.html +5 -5
  75. package/dist/docs/index.html +7 -7
  76. package/dist/docs/quickstart.html +180 -23
  77. package/dist/docs/reference/agent-config.html +7 -7
  78. package/dist/docs/reference/channels.html +8 -8
  79. package/dist/docs/reference/cli.html +4 -4
  80. package/dist/docs/reference/connections.html +5 -5
  81. package/dist/docs/reference/hooks.html +5 -5
  82. package/dist/docs/reference/http-api.html +6 -6
  83. package/dist/docs/reference/instructions.html +13 -11
  84. package/dist/docs/reference/playground.html +4 -4
  85. package/dist/docs/reference/project-layout.html +4 -4
  86. package/dist/docs/reference/schedules.html +7 -7
  87. package/dist/docs/reference/sessions.html +4 -4
  88. package/dist/docs/reference/skills.html +6 -6
  89. package/dist/docs/reference/subagents.html +6 -6
  90. package/dist/docs/reference/tools.html +7 -7
  91. package/dist/docs/scaffolding-agents.html +5 -5
  92. package/dist/docs/storage.html +41 -0
  93. package/dist/docs/troubleshooting.html +4 -4
  94. package/dist/index.d.ts +2 -0
  95. package/dist/index.d.ts.map +1 -1
  96. package/dist/index.js +1 -0
  97. package/dist/internal/cli-deploy.d.ts +52 -0
  98. package/dist/internal/cli-deploy.d.ts.map +1 -0
  99. package/dist/internal/cli-deploy.js +731 -0
  100. package/dist/internal/cursor/backend-client.d.ts +10 -1
  101. package/dist/internal/cursor/backend-client.d.ts.map +1 -1
  102. package/dist/internal/cursor/backend-client.js +79 -1
  103. package/dist/internal/cursor/github-credentials.d.ts +44 -0
  104. package/dist/internal/cursor/github-credentials.d.ts.map +1 -0
  105. package/dist/internal/cursor/github-credentials.js +195 -0
  106. package/dist/internal/deploy-client.d.ts +176 -0
  107. package/dist/internal/deploy-client.d.ts.map +1 -0
  108. package/dist/internal/deploy-client.js +375 -0
  109. package/dist/internal/discovery.d.ts.map +1 -1
  110. package/dist/internal/discovery.js +73 -5
  111. package/dist/internal/distribution.d.ts.map +1 -1
  112. package/dist/internal/distribution.js +1 -0
  113. package/dist/internal/eval-run-store.d.ts +22 -3
  114. package/dist/internal/eval-run-store.d.ts.map +1 -1
  115. package/dist/internal/eval-run-store.js +37 -19
  116. package/dist/internal/handleAgentServeTrigger.d.ts.map +1 -1
  117. package/dist/internal/handleAgentServeTrigger.js +10 -12
  118. package/dist/internal/host-platforms.d.ts +7 -2
  119. package/dist/internal/host-platforms.d.ts.map +1 -1
  120. package/dist/internal/host-platforms.js +15 -10
  121. package/dist/internal/hosting.d.ts +37 -0
  122. package/dist/internal/hosting.d.ts.map +1 -0
  123. package/dist/internal/hosting.js +67 -0
  124. package/dist/internal/reminder-runner.d.ts +7 -0
  125. package/dist/internal/reminder-runner.d.ts.map +1 -1
  126. package/dist/internal/reminder-runner.js +50 -6
  127. package/dist/internal/reminder-store.d.ts +2 -0
  128. package/dist/internal/reminder-store.d.ts.map +1 -1
  129. package/dist/internal/reminder-store.js +18 -0
  130. package/dist/internal/server.d.ts.map +1 -1
  131. package/dist/internal/server.js +173 -42
  132. package/dist/internal/session-engine.d.ts +49 -0
  133. package/dist/internal/session-engine.d.ts.map +1 -1
  134. package/dist/internal/session-engine.js +246 -17
  135. package/dist/internal/storage-coordinator.d.ts +139 -0
  136. package/dist/internal/storage-coordinator.d.ts.map +1 -0
  137. package/dist/internal/storage-coordinator.js +499 -0
  138. package/dist/playground/assets/cursor-icons-outline-BxTT_FVJ.woff2 +0 -0
  139. package/dist/playground/assets/index-B1Qc9h2u.css +1 -0
  140. package/dist/playground/assets/{index-FlWjhg3x.js → index-CpDYCj8W.js} +42 -42
  141. package/dist/playground/index.html +2 -2
  142. package/dist/storage.d.ts +204 -0
  143. package/dist/storage.d.ts.map +1 -0
  144. package/dist/storage.js +153 -0
  145. package/dist/types.d.ts +44 -3
  146. package/dist/types.d.ts.map +1 -1
  147. package/docs/README.md +3 -2
  148. package/docs/guides/github.md +43 -8
  149. package/docs/guides/slack.md +1 -1
  150. package/docs/quickstart.md +329 -51
  151. package/docs/reference/instructions.md +8 -6
  152. package/docs/scaffolding-agents.md +1 -1
  153. package/docs/storage.md +98 -0
  154. package/package.json +8 -1
  155. package/skills/github/SKILL.md +9 -1
  156. package/src/bin/agent-serve.ts +139 -0
  157. package/src/channels/github/api.ts +42 -23
  158. package/src/channels/github/cursor-account.ts +165 -0
  159. package/src/channels/github/github-channel.ts +66 -6
  160. package/src/channels/github/index.ts +2 -2
  161. package/src/channels/github/types.ts +19 -0
  162. package/src/channels/slack/slack-channel.ts +17 -3
  163. package/src/channels/slack/types.ts +44 -3
  164. package/src/index.ts +13 -0
  165. package/src/internal/cli-deploy.ts +940 -0
  166. package/src/internal/cursor/backend-client.ts +103 -1
  167. package/src/internal/cursor/github-credentials.ts +248 -0
  168. package/src/internal/deploy-client.ts +591 -0
  169. package/src/internal/discovery.ts +88 -1
  170. package/src/internal/distribution.ts +1 -0
  171. package/src/internal/eval-run-store.ts +48 -19
  172. package/src/internal/handleAgentServeTrigger.ts +10 -12
  173. package/src/internal/host-platforms.ts +28 -11
  174. package/src/internal/hosting.ts +77 -0
  175. package/src/internal/reminder-runner.ts +50 -6
  176. package/src/internal/reminder-store.ts +21 -0
  177. package/src/internal/server.ts +213 -27
  178. package/src/internal/session-engine.ts +285 -7
  179. package/src/internal/storage-coordinator.ts +615 -0
  180. package/src/storage.ts +325 -0
  181. package/src/types.ts +40 -2
  182. package/dist/docs/assets/chunks/@localSearchIndexroot.CcVk1uKq.js +0 -1
  183. package/dist/docs/assets/index.md.BPKcj5AI.js +0 -20
  184. package/dist/docs/assets/quickstart.md.tVPiGK_L.js +0 -35
  185. package/dist/docs/assets/quickstart.md.tVPiGK_L.lean.js +0 -1
  186. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.js +0 -1
  187. package/dist/docs/assets/scaffolding-agents.md.CyYfWGdc.lean.js +0 -1
  188. package/dist/playground/assets/cursor-icons-outline-oY2V_mvK.woff2 +0 -0
  189. package/dist/playground/assets/index-1K-hG-7p.css +0 -1
  190. /package/dist/docs/assets/{evals.md.DPZ_MAnI.lean.js → evals.md.DAgEc_hL.lean.js} +0 -0
  191. /package/dist/docs/assets/{guides_agent-to-agent.md.CrtrsySy.lean.js → guides_agent-to-agent.md.Bpzgq2Pq.lean.js} +0 -0
  192. /package/dist/docs/assets/{guides_cloud-runtime.md.CYlNMTNp.lean.js → guides_cloud-runtime.md.gVzabdQL.lean.js} +0 -0
  193. /package/dist/docs/assets/{guides_human-in-the-loop.md.Dvuctx7s.lean.js → guides_human-in-the-loop.md.DlUqsp1S.lean.js} +0 -0
  194. /package/dist/docs/assets/{guides_webhooks.md.hFTik3lf.lean.js → guides_webhooks.md.B1EswtUu.lean.js} +0 -0
  195. /package/dist/docs/assets/{reference_connections.md.C3vNH_DE.lean.js → reference_connections.md.zaEYCLHT.lean.js} +0 -0
  196. /package/dist/docs/assets/{reference_http-api.md.DBAahtdz.lean.js → reference_http-api.md.Dx_nmDG6.lean.js} +0 -0
  197. /package/dist/docs/assets/{reference_schedules.md.D7qijxLk.lean.js → reference_schedules.md.w_F2mXB6.lean.js} +0 -0
  198. /package/dist/docs/assets/{reference_subagents.md.CIRAVcPK.lean.js → reference_subagents.md.zWAMNfi1.lean.js} +0 -0
  199. /package/dist/docs/assets/{reference_tools.md.DF5kwlt0.lean.js → reference_tools.md.CqgJroI0.lean.js} +0 -0
package/src/storage.ts ADDED
@@ -0,0 +1,325 @@
1
+ /**
2
+ * Durable storage plug-in for agent-serve.
3
+ *
4
+ * Author `agent/storage.ts` with {@link defineStorage} to mirror the
5
+ * framework's durable state into storage you own (a database, S3, a data
6
+ * pipeline, …). Without it, state lives under `--state-root` on local disk
7
+ * only (and eval/A/B history follows the narrower `persistRuns` /
8
+ * `persistSamples` / `persistSnapshots` hooks).
9
+ *
10
+ * The sink is a plain key-value store — four functions, no schema:
11
+ *
12
+ * ```ts
13
+ * import { defineStorage } from "@anysphere/agent-serve/storage";
14
+ *
15
+ * export default defineStorage({
16
+ * put: (key, value) => db.upsert(key, value),
17
+ * get: (key) => db.get(key),
18
+ * delete: (key) => db.delete(key),
19
+ * list: (prefix) => db.listByPrefix(prefix), // [{ key, value }] in key order
20
+ * });
21
+ * ```
22
+ *
23
+ * The **framework mints every key** from a stable, versioned scheme (see
24
+ * {@link storageKeys}) and decides **when** to call the sink: session
25
+ * records and event chunks flush when a turn's handlers have settled,
26
+ * reads happen at serve start (bulk restore), on continuation-token misses
27
+ * (lazy restore), and at playground hydration. Authors do not schedule
28
+ * reads or writes — the only timing knobs are {@link StoragePolicy}'s
29
+ * `debounceMs` (event write batching) and `restore` (startup hydration).
30
+ *
31
+ * Because keys are opaque strings to the sink, new kinds of durable state
32
+ * (channel cursors, thread affinity, …) are new key prefixes — existing
33
+ * sinks store them with no code changes.
34
+ *
35
+ * Delivery semantics: writes are **serialized** (one sink call in flight
36
+ * per agent, in order), **bounded** (a sink that falls behind sheds writes
37
+ * rather than growing memory), and **at-most-once** — a throwing `put` is
38
+ * logged and dropped, never retried, and never fails a turn. The local
39
+ * event log under `--state-root` remains the live source of truth; this
40
+ * interface is the durable mirror.
41
+ */
42
+
43
+ import { createHash } from "node:crypto";
44
+ import { brandDefinition } from "./internal/brand.js";
45
+ import type { JsonValue } from "./types.js";
46
+
47
+ // ============================================================================
48
+ // Sink interface
49
+ // ============================================================================
50
+
51
+ /** One `{ key, value }` pair returned by {@link StorageConfig.list}. */
52
+ export interface StorageEntry {
53
+ key: string;
54
+ value: JsonValue;
55
+ }
56
+
57
+ /** Context passed to every sink call. */
58
+ export interface StorageContext {
59
+ /** Agent name (also baked into every key; see {@link storageKeys}). */
60
+ agentName: string;
61
+ /** Absolute agent project root (directory that contains `agent/`). */
62
+ projectRoot: string;
63
+ /**
64
+ * Why the framework is calling:
65
+ * - `"policy"` — a flush trigger fired (turn end, debounce, change)
66
+ * - `"shutdown"` — the serve process is draining; last chance to write
67
+ * - `"restore"` — serve start or a lazy restore; reads rebuilding state
68
+ */
69
+ reason: "policy" | "shutdown" | "restore";
70
+ }
71
+
72
+ export interface StoragePolicy {
73
+ /**
74
+ * Batch event-chunk writes on a quiet-period timer instead of flushing
75
+ * once per turn. The debounce **spans turn boundaries** — a rapid
76
+ * multi-turn exchange becomes one write when the session goes quiet —
77
+ * so it is the right choice for chatty sessions where per-turn writes
78
+ * are too many. Unset (default): one event chunk per turn.
79
+ */
80
+ debounceMs?: number;
81
+ /**
82
+ * Guardrails for the **startup bulk restore**. Whatever `list` returns
83
+ * is filtered to these caps before anything is written to local disk, so
84
+ * a large store cannot blow up `--state-root` or stall serve start.
85
+ * Newest sessions (by `updatedAt`) win within each cap.
86
+ *
87
+ * `"off"` disables the startup restore entirely — sessions then restore
88
+ * one at a time as follow-ups actually arrive (lazy-only; recommended
89
+ * for high-traffic deployments).
90
+ */
91
+ restore?: StorageRestorePolicy | "off";
92
+ }
93
+
94
+ /** Caps applied to the startup bulk restore. See {@link StoragePolicy.restore}. */
95
+ export interface StorageRestorePolicy {
96
+ /** Max sessions materialized (default {@link STORAGE_DEFAULT_RESTORE_MAX_SESSIONS}). */
97
+ maxSessions?: number;
98
+ /**
99
+ * Skip sessions whose `updatedAt` is older than this (default
100
+ * {@link STORAGE_DEFAULT_RESTORE_MAX_AGE_MS}). Older sessions remain
101
+ * reachable lazily on their next follow-up.
102
+ */
103
+ maxAgeMs?: number;
104
+ /**
105
+ * Stop restoring once this many bytes of records + events have been
106
+ * written (default {@link STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES}).
107
+ * Checked before each session is written, so one oversized stream
108
+ * cannot blow past the budget.
109
+ */
110
+ maxTotalBytes?: number;
111
+ }
112
+
113
+ export interface StorageConfig {
114
+ /** Optional label surfaced on `GET /v1/info` diagnostics. */
115
+ name?: string;
116
+ /** Timing knobs; see {@link StoragePolicy}. */
117
+ policy?: StoragePolicy;
118
+ /**
119
+ * Store one value under a key (upsert, last-write-wins). Called on the
120
+ * framework's schedule — never concurrently, always in order. Keep it
121
+ * fast or buffer internally: the delivery queue is bounded, so a sink
122
+ * that falls behind sustained traffic sheds writes (logged) instead of
123
+ * growing memory; it never stalls the agent loop.
124
+ */
125
+ put(key: string, value: JsonValue, ctx: StorageContext): void | Promise<void>;
126
+ /** Remove a key. Optional — without it, deletions are skipped. */
127
+ delete?(key: string, ctx: StorageContext): void | Promise<void>;
128
+ /**
129
+ * Point lookup. Optional — required for **lazy restore** (resolving a
130
+ * continuation token on a replacement host) and the A/B backfill.
131
+ * Return `undefined`/`null` only for a **definitive** miss: on the lazy
132
+ * restore path a throw propagates and fails the follow-up (retryable) —
133
+ * a store outage must not read as "unknown token", which would fork the
134
+ * conversation onto a new session.
135
+ */
136
+ get?(
137
+ key: string,
138
+ ctx: StorageContext
139
+ ): JsonValue | undefined | null | Promise<JsonValue | undefined | null>;
140
+ /**
141
+ * All entries under a key prefix, in ascending key order. Optional —
142
+ * required for the **startup bulk restore** (sessions + event streams)
143
+ * and playground eval history.
144
+ */
145
+ list?(
146
+ prefix: string,
147
+ ctx: StorageContext
148
+ ): StorageEntry[] | Promise<StorageEntry[]>;
149
+ }
150
+
151
+ export type StorageDefinition = StorageConfig & {
152
+ readonly __agentServe: "storage";
153
+ };
154
+
155
+ /**
156
+ * Author the project storage sink (`agent/storage.ts`, default
157
+ * export). See the module doc for semantics and an example.
158
+ */
159
+ export function defineStorage(config: StorageConfig): StorageDefinition {
160
+ if (typeof config.put !== "function") {
161
+ throw new Error("defineStorage: config.put must be a function");
162
+ }
163
+ for (const hook of ["get", "list", "delete"] as const) {
164
+ const value = config[hook];
165
+ if (value !== undefined && typeof value !== "function") {
166
+ throw new Error(
167
+ `defineStorage: config.${hook} must be a function when set`
168
+ );
169
+ }
170
+ }
171
+ // Validate eagerly so a bad policy fails at discovery, not first flush.
172
+ resolveStoragePolicy(config.policy);
173
+ return brandDefinition("storage", config);
174
+ }
175
+
176
+ // ============================================================================
177
+ // Key scheme
178
+ // ============================================================================
179
+
180
+ /**
181
+ * Framework-owned key root. All {@link storageKeys} values live under this
182
+ * prefix so a shared store can route or namespace agentkit data.
183
+ */
184
+ export const STORAGE_KEY_ROOT = "agentkit/v1" as const;
185
+
186
+ /**
187
+ * The framework-owned key scheme. Keys are a **stable, versioned contract**
188
+ * under {@link STORAGE_KEY_ROOT}: sinks may treat them as opaque strings, or
189
+ * route on prefixes (e.g. event chunks to object storage, everything else
190
+ * to a database). Channel ids and continuation tokens are the only segments
191
+ * that may contain caller-controlled characters; they are URI-encoded, and
192
+ * a segment whose encoding exceeds {@link MAX_KEY_SEGMENT_BYTES} is replaced
193
+ * by a `sha256:…` digest — so every minted key has a bounded length that any
194
+ * backend (VARCHAR columns, btree index tuples, S3 key limits) can store,
195
+ * no matter what a caller stuffs into a token. The substitution is
196
+ * deterministic: writes and continuation lookups build the same key.
197
+ *
198
+ * | Key | Value |
199
+ * | --- | --- |
200
+ * | `agentkit/v1/{agent}/session/{sessionId}` | `SessionRecord` |
201
+ * | `agentkit/v1/{agent}/session-events/{sessionId}/{index}` | `SessionEvent[]` chunk (index = first event's index, zero-padded) |
202
+ * | `agentkit/v1/{agent}/continuation/{channelId}/{token}` | `{ sessionId }` |
203
+ * | `agentkit/v1/{agent}/reminder/{reminderId}` | `ReminderRecord` |
204
+ * | `agentkit/v1/{agent}/eval-run/{runId}` | `EvalRunSnapshot` |
205
+ * | `agentkit/v1/{agent}/ab-sample/{sessionId}/{at}` | `ABMetricSample` |
206
+ * | `agentkit/v1/{agent}/ab-snapshot` | latest aggregate `ABSnapshot` |
207
+ */
208
+ export const storageKeys = {
209
+ session: (agent: string, sessionId: string): string =>
210
+ `${STORAGE_KEY_ROOT}/${agent}/session/${sessionId}`,
211
+ sessionPrefix: (agent: string): string =>
212
+ `${STORAGE_KEY_ROOT}/${agent}/session/`,
213
+ sessionEvents: (
214
+ agent: string,
215
+ sessionId: string,
216
+ firstIndex: number
217
+ ): string =>
218
+ `${STORAGE_KEY_ROOT}/${agent}/session-events/${sessionId}/${String(firstIndex).padStart(8, "0")}`,
219
+ sessionEventsPrefix: (agent: string, sessionId: string): string =>
220
+ `${STORAGE_KEY_ROOT}/${agent}/session-events/${sessionId}/`,
221
+ continuation: (
222
+ agent: string,
223
+ channelId: string,
224
+ continuationKey: string
225
+ ): string =>
226
+ `${STORAGE_KEY_ROOT}/${agent}/continuation/${keySegment(channelId)}/${keySegment(continuationKey)}`,
227
+ reminder: (agent: string, reminderId: string): string =>
228
+ `${STORAGE_KEY_ROOT}/${agent}/reminder/${reminderId}`,
229
+ reminderPrefix: (agent: string): string =>
230
+ `${STORAGE_KEY_ROOT}/${agent}/reminder/`,
231
+ evalRun: (agent: string, runId: string): string =>
232
+ `${STORAGE_KEY_ROOT}/${agent}/eval-run/${runId}`,
233
+ evalRunPrefix: (agent: string): string =>
234
+ `${STORAGE_KEY_ROOT}/${agent}/eval-run/`,
235
+ abSample: (agent: string, sessionId: string, at: string): string =>
236
+ `${STORAGE_KEY_ROOT}/${agent}/ab-sample/${sessionId}/${at}`,
237
+ abSnapshot: (agent: string): string =>
238
+ `${STORAGE_KEY_ROOT}/${agent}/ab-snapshot`,
239
+ } as const;
240
+
241
+ /**
242
+ * Max bytes a caller-controlled key segment may occupy after URI-encoding.
243
+ * Chosen so full keys stay well under common backend limits (Postgres btree
244
+ * index tuples cap at ~2704 bytes; S3 keys at 1024). Longer segments are
245
+ * replaced by their SHA-256 digest, keeping every minted key bounded.
246
+ */
247
+ export const MAX_KEY_SEGMENT_BYTES = 256;
248
+
249
+ /**
250
+ * URI-encode one caller-controlled key segment, substituting a `sha256:…`
251
+ * digest of the raw value when the encoding exceeds
252
+ * {@link MAX_KEY_SEGMENT_BYTES}. Deterministic, so key construction on the
253
+ * write path and the continuation-lookup path always agree.
254
+ */
255
+ function keySegment(raw: string): string {
256
+ const encoded = encodeURIComponent(raw);
257
+ if (Buffer.byteLength(encoded, "utf8") <= MAX_KEY_SEGMENT_BYTES) {
258
+ return encoded;
259
+ }
260
+ return `sha256:${createHash("sha256").update(raw, "utf8").digest("hex")}`;
261
+ }
262
+
263
+ // ============================================================================
264
+ // Policy resolution
265
+ // ============================================================================
266
+
267
+ export const STORAGE_DEFAULT_RESTORE_MAX_SESSIONS = 1_000;
268
+ export const STORAGE_DEFAULT_RESTORE_MAX_AGE_MS: number = 30 * 24 * 60 * 60_000;
269
+ export const STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES = 1_073_741_824; // 1 GiB
270
+
271
+ /** {@link StoragePolicy} with defaults applied. */
272
+ export interface ResolvedStoragePolicy {
273
+ /** Event-chunk flush trigger: per turn, or debounced across turns. */
274
+ events: "turnEnd" | { debounceMs: number };
275
+ restore:
276
+ | "off"
277
+ | { maxSessions: number; maxAgeMs: number; maxTotalBytes: number };
278
+ }
279
+
280
+ /** Apply {@link StoragePolicy} defaults (exposed for tooling/tests). */
281
+ export function resolveStoragePolicy(
282
+ policy: StoragePolicy | undefined
283
+ ): ResolvedStoragePolicy {
284
+ return {
285
+ events:
286
+ policy?.debounceMs === undefined
287
+ ? "turnEnd"
288
+ : { debounceMs: clampInt(policy.debounceMs, 2_000, 0, 60_000) },
289
+ restore:
290
+ policy?.restore === "off"
291
+ ? "off"
292
+ : {
293
+ maxSessions: clampInt(
294
+ policy?.restore?.maxSessions,
295
+ STORAGE_DEFAULT_RESTORE_MAX_SESSIONS,
296
+ 0,
297
+ 1_000_000
298
+ ),
299
+ maxAgeMs: clampInt(
300
+ policy?.restore?.maxAgeMs,
301
+ STORAGE_DEFAULT_RESTORE_MAX_AGE_MS,
302
+ 0,
303
+ 10 * 365 * 24 * 60 * 60_000
304
+ ),
305
+ maxTotalBytes: clampInt(
306
+ policy?.restore?.maxTotalBytes,
307
+ STORAGE_DEFAULT_RESTORE_MAX_TOTAL_BYTES,
308
+ 0,
309
+ 1_099_511_627_776 // 1 TiB
310
+ ),
311
+ },
312
+ };
313
+ }
314
+
315
+ function clampInt(
316
+ value: number | undefined,
317
+ fallback: number,
318
+ min: number,
319
+ max: number
320
+ ): number {
321
+ if (value === undefined || !Number.isFinite(value)) {
322
+ return fallback;
323
+ }
324
+ return Math.min(max, Math.max(min, Math.floor(value)));
325
+ }
package/src/types.ts CHANGED
@@ -11,6 +11,7 @@
11
11
  import type { InteractionUpdate, SDKCustomTool } from "@cursor/sdk";
12
12
  import type { z } from "zod";
13
13
  import type { ABConfigFile } from "./ab.js";
14
+ import type { StorageDefinition } from "./storage.js";
14
15
 
15
16
  // ============================================================================
16
17
  // JSON
@@ -43,7 +44,8 @@ export type DefinitionKind =
43
44
  | "reminder"
44
45
  | "hook"
45
46
  | "eval"
46
- | "ab";
47
+ | "ab"
48
+ | "storage";
47
49
 
48
50
  export interface BrandedDefinition<K extends DefinitionKind> {
49
51
  readonly __agentServe: K;
@@ -186,6 +188,34 @@ export interface AgentConfig {
186
188
  * {@link cloud}.
187
189
  */
188
190
  local?: AgentLocalOptions;
191
+ /**
192
+ * Managed-hosting declarations (`agentkit deploy` onto Cursor's
193
+ * agent-serve hosting). The manifest is the source of truth: deploy reads
194
+ * it and sends it with the deployment request. Ignored by local serving.
195
+ */
196
+ hosting?: AgentHostingOptions;
197
+ }
198
+
199
+ /**
200
+ * Managed-hosting block on {@link AgentConfig.hosting}.
201
+ */
202
+ export interface AgentHostingOptions {
203
+ /**
204
+ * Domains the deployed engine pod may reach (egress allowlist), e.g.
205
+ * `"api.example.com"` or `"*.example.com"` (single leading wildcard).
206
+ * Lowercase hostnames with at least two labels and an alphabetic TLD;
207
+ * max 20. `agentkit deploy` sends these as `egressAllowedDomains`
208
+ * (unioned with any `--allow-domain` flags). Only valid on repo-backed
209
+ * deployments.
210
+ */
211
+ egressDomains?: string[];
212
+ /**
213
+ * Secret names this agent expects at runtime (declarative documentation;
214
+ * values are never authored in files). `agentkit deploy` warns when a
215
+ * declared name is not set on the deployment — set values with
216
+ * `agentkit secrets set <slug> NAME`.
217
+ */
218
+ secretNames?: string[];
189
219
  }
190
220
 
191
221
  /**
@@ -1532,6 +1562,8 @@ export interface ResolvedAgent {
1532
1562
  * (`cwd` is absolute — relative paths resolved against the project root).
1533
1563
  */
1534
1564
  local?: AgentLocalOptions;
1565
+ /** Managed-hosting declarations from {@link AgentConfig.hosting}. */
1566
+ hosting?: AgentHostingOptions;
1535
1567
  tools: DiscoveredTool[];
1536
1568
  skills: DiscoveredSkill[];
1537
1569
  connections: DiscoveredConnection[];
@@ -1556,6 +1588,8 @@ export interface AgentProject {
1556
1588
  abs: DiscoveredAB[];
1557
1589
  /** Optional `agent/ab.config.ts` (`defineABConfig`). */
1558
1590
  abConfig?: ABConfigFile;
1591
+ /** Optional `agent/storage.ts` (`defineStorage`). */
1592
+ storage?: StorageDefinition;
1559
1593
  diagnostics: Diagnostic[];
1560
1594
  }
1561
1595
 
@@ -1579,6 +1613,8 @@ export interface AgentProjectInfo {
1579
1613
  cloud?: AgentCloudPublicInfo;
1580
1614
  /** Local harness options when authored (`cwd` absolute). */
1581
1615
  local?: { cwd?: string };
1616
+ /** Managed-hosting declarations when authored (egress allowlist, expected secrets). */
1617
+ hosting?: { egressDomains?: string[]; secretNames?: string[] };
1582
1618
  /**
1583
1619
  * The agent's own MCP surface (mounted at `<base>/v1/mcp`): other agents
1584
1620
  * and MCP clients delegate to this agent through these tools.
@@ -1613,12 +1649,14 @@ export interface AgentProjectInfo {
1613
1649
  hooks: string[];
1614
1650
  /** Live A/B experiment names (`defineAB` under `agent/ab`). */
1615
1651
  abs: string[];
1616
- /** Playground fold / persistence meta from `agent/ab.config.ts`. */
1652
+ /** Playground fold / durable-sink meta from `agent/ab.config.ts`. */
1617
1653
  abConfig?: {
1618
1654
  maxPlaygroundSessions?: number;
1619
1655
  durableSamples: boolean;
1620
1656
  durableSnapshots: boolean;
1621
1657
  };
1658
+ /** Project storage sink from `agent/storage.ts`, when authored. */
1659
+ storage?: { name?: string };
1622
1660
  diagnostics: Diagnostic[];
1623
1661
  }
1624
1662