@cursor/july 0.1.8 → 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 (119) hide show
  1. package/dist/bin/agent-serve.js +0 -0
  2. package/dist/channels/slack/post-update-delivery.d.ts +85 -0
  3. package/dist/channels/slack/post-update-delivery.d.ts.map +1 -0
  4. package/dist/channels/slack/post-update-delivery.js +318 -0
  5. package/dist/docs/404.html +2 -2
  6. package/dist/docs/ab.html +3 -3
  7. package/dist/docs/assets/{app.BP8mAVMW.js → app.Ciosf24M.js} +1 -1
  8. package/dist/docs/assets/chunks/@localSearchIndexroot.D154Detj.js +1 -0
  9. package/dist/docs/assets/chunks/{VPLocalSearchBox.1FXIZbz8.js → VPLocalSearchBox.BcRDly7n.js} +1 -1
  10. package/dist/docs/assets/chunks/{theme.Dm1R02v9.js → theme.CMpbjdfx.js} +2 -2
  11. package/dist/docs/assets/{deployment.md.BtfEsc9S.js → deployment.md.DC5-ImcM.js} +1 -1
  12. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.js → example-agents_approval-buddy.md.DFGBYLcc.js} +2 -2
  13. package/dist/docs/assets/{example-agents_approval-buddy.md.8R5phXb5.lean.js → example-agents_approval-buddy.md.DFGBYLcc.lean.js} +1 -1
  14. package/dist/docs/assets/{example-agents_index.md.CwtWu5ix.js → example-agents_index.md.BPXjUtRB.js} +2 -2
  15. package/dist/docs/assets/{example-agents_index.md.CwtWu5ix.lean.js → example-agents_index.md.BPXjUtRB.lean.js} +1 -1
  16. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.js → example-agents_security-reviewer.md.CE9v-aKm.js} +2 -2
  17. package/dist/docs/assets/{example-agents_security-reviewer.md.D2rtwDTO.lean.js → example-agents_security-reviewer.md.CE9v-aKm.lean.js} +1 -1
  18. package/dist/docs/assets/{storage.md.Cu3MwM2a.js → storage.md.DRTdnFvd.js} +7 -1
  19. package/dist/docs/assets/{storage.md.Cu3MwM2a.lean.js → storage.md.DRTdnFvd.lean.js} +1 -1
  20. package/dist/docs/building-with-agents.html +3 -3
  21. package/dist/docs/concepts.html +3 -3
  22. package/dist/docs/deployment.html +5 -5
  23. package/dist/docs/evals.html +3 -3
  24. package/dist/docs/example-agents/approval-buddy.html +5 -5
  25. package/dist/docs/example-agents/benny.html +3 -3
  26. package/dist/docs/example-agents/bugbot.html +3 -3
  27. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  28. package/dist/docs/example-agents/codeowners-review.html +3 -3
  29. package/dist/docs/example-agents/concierge.html +3 -3
  30. package/dist/docs/example-agents/fsd.html +3 -3
  31. package/dist/docs/example-agents/index.html +5 -5
  32. package/dist/docs/example-agents/knowledge-base.html +3 -3
  33. package/dist/docs/example-agents/oncall.html +3 -3
  34. package/dist/docs/example-agents/security-reviewer.html +6 -6
  35. package/dist/docs/example-agents/slack-agent.html +3 -3
  36. package/dist/docs/example-agents/weather-agent.html +3 -3
  37. package/dist/docs/guides/agent-to-agent.html +3 -3
  38. package/dist/docs/guides/cloud-runtime.html +3 -3
  39. package/dist/docs/guides/github.html +3 -3
  40. package/dist/docs/guides/human-in-the-loop.html +3 -3
  41. package/dist/docs/guides/slack.html +3 -3
  42. package/dist/docs/guides/webhooks.html +3 -3
  43. package/dist/docs/hashmap.json +1 -1
  44. package/dist/docs/hillclimbing.html +3 -3
  45. package/dist/docs/index.html +3 -3
  46. package/dist/docs/quickstart.html +3 -3
  47. package/dist/docs/reference/agent-config.html +3 -3
  48. package/dist/docs/reference/channels.html +3 -3
  49. package/dist/docs/reference/cli.html +3 -3
  50. package/dist/docs/reference/connections.html +3 -3
  51. package/dist/docs/reference/hooks.html +3 -3
  52. package/dist/docs/reference/http-api.html +3 -3
  53. package/dist/docs/reference/instructions.html +3 -3
  54. package/dist/docs/reference/playground.html +3 -3
  55. package/dist/docs/reference/project-layout.html +3 -3
  56. package/dist/docs/reference/schedules.html +3 -3
  57. package/dist/docs/reference/sessions.html +3 -3
  58. package/dist/docs/reference/skills.html +3 -3
  59. package/dist/docs/reference/subagents.html +3 -3
  60. package/dist/docs/reference/tools.html +3 -3
  61. package/dist/docs/scaffolding-agents.html +3 -3
  62. package/dist/docs/storage.html +11 -5
  63. package/dist/docs/troubleshooting.html +3 -3
  64. package/dist/internal/cli-deploy.d.ts.map +1 -1
  65. package/dist/internal/cli-deploy.js +45 -18
  66. package/dist/internal/deploy-client.d.ts +7 -1
  67. package/dist/internal/deploy-client.d.ts.map +1 -1
  68. package/dist/internal/deploy-client.js +3 -0
  69. package/dist/internal/deploy-source.d.ts +3 -0
  70. package/dist/internal/deploy-source.d.ts.map +1 -1
  71. package/dist/internal/deploy-source.js +25 -7
  72. package/dist/internal/discovery.js +1 -1
  73. package/dist/internal/distribution.d.ts.map +1 -1
  74. package/dist/internal/distribution.js +1 -0
  75. package/dist/internal/hosting.d.ts +12 -2
  76. package/dist/internal/hosting.d.ts.map +1 -1
  77. package/dist/internal/hosting.js +21 -4
  78. package/dist/internal/json-dir-store.d.ts +32 -0
  79. package/dist/internal/json-dir-store.d.ts.map +1 -0
  80. package/dist/internal/json-dir-store.js +100 -0
  81. package/dist/internal/logs-client.d.ts +16 -2
  82. package/dist/internal/logs-client.d.ts.map +1 -1
  83. package/dist/internal/logs-client.js +51 -16
  84. package/dist/internal/session-engine.d.ts.map +1 -1
  85. package/dist/internal/session-engine.js +29 -15
  86. package/dist/internal/workspace.d.ts +19 -0
  87. package/dist/internal/workspace.d.ts.map +1 -1
  88. package/dist/internal/workspace.js +47 -13
  89. package/dist/playground/assets/index-C0-QEG4O.js +86 -0
  90. package/dist/playground/assets/index-z3JY9O2x.css +1 -0
  91. package/dist/playground/index.html +2 -2
  92. package/dist/storage-backends/cursor-hosted.d.ts +65 -0
  93. package/dist/storage-backends/cursor-hosted.d.ts.map +1 -0
  94. package/dist/storage-backends/cursor-hosted.js +203 -0
  95. package/dist/storage-backends/postgres-kv.d.ts +5 -0
  96. package/dist/storage-backends/postgres-kv.d.ts.map +1 -1
  97. package/dist/storage-backends/postgres-kv.js +5 -0
  98. package/docs/.vitepress/cache/deps/package.json +3 -0
  99. package/docs/deployment.md +7 -3
  100. package/docs/example-agents/approval-buddy.md +1 -1
  101. package/docs/example-agents/index.md +1 -1
  102. package/docs/example-agents/security-reviewer.md +5 -5
  103. package/docs/storage.md +18 -4
  104. package/package.json +8 -1
  105. package/src/internal/cli-deploy.ts +58 -20
  106. package/src/internal/deploy-client.ts +11 -0
  107. package/src/internal/deploy-source.ts +24 -6
  108. package/src/internal/discovery.ts +1 -1
  109. package/src/internal/distribution.ts +1 -0
  110. package/src/internal/hosting.ts +24 -4
  111. package/src/internal/logs-client.ts +67 -21
  112. package/src/internal/session-engine.ts +28 -11
  113. package/src/internal/workspace.ts +48 -11
  114. package/src/storage-backends/cursor-hosted.ts +246 -0
  115. package/src/storage-backends/postgres-kv.ts +5 -0
  116. package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +0 -1
  117. package/dist/playground/assets/index-7GJIUFQj.css +0 -1
  118. package/dist/playground/assets/index-Bpr4Th4M.js +0 -319
  119. /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-Bpr4Th4M.js"></script>
12
- <link rel="stylesheet" crossorigin href="./assets/index-7GJIUFQj.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
+ }
@@ -9,6 +9,11 @@
9
9
  * agent_serve_ab(deployment_id, agent, kind, ref, value JSONB, updated_at)
10
10
  * PRIMARY KEY (deployment_id, agent, kind, ref)
11
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
+ *
12
17
  * `postgresStorage` implements the whole `defineStorage` surface as plain
13
18
  * SQL: the four KV methods against the last-write-wins `agent_serve_kv`
14
19
  * table (sessions, event chunks, continuations, reminders), the `evals`
@@ -1 +1 @@
1
- {"version":3,"file":"postgres-kv.d.ts","sourceRoot":"","sources":["../../src/storage-backends/postgres-kv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;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"}
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"}
@@ -9,6 +9,11 @@
9
9
  * agent_serve_ab(deployment_id, agent, kind, ref, value JSONB, updated_at)
10
10
  * PRIMARY KEY (deployment_id, agent, kind, ref)
11
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
+ *
12
17
  * `postgresStorage` implements the whole `defineStorage` surface as plain
13
18
  * SQL: the four KV methods against the last-write-wins `agent_serve_kv`
14
19
  * table (sessions, event chunks, continuations, reminders), the `evals`
@@ -0,0 +1,3 @@
1
+ {
2
+ "type": "module"
3
+ }
@@ -127,9 +127,13 @@ shell arguments.
127
127
  ### Choose durable storage
128
128
 
129
129
  Hosted filesystem state can reset during a deploy or runtime
130
- replacement. Use [`defineStorage`](./storage.md) to preserve supported
131
- records across deployments. Self-host with a persistent `--state-root`
132
- when the complete filesystem must survive.
130
+ replacement. Prefer
131
+ [`cursorHostedStorage`](./storage.md) (`@cursor/july/storage/cursor-hosted`)
132
+ so durable records land in Cursor's Bugbot `agent_serve_*` tables through
133
+ a control-plane HTTP proxy (pod credential auth — no database URL in the
134
+ engine). Do not put `BUGBOTDB_URL` or `AGENT_SERVE_DEPLOYMENT_ID` in
135
+ `hosting.secretNames`. Self-host with your own `defineStorage` backend or
136
+ a persistent `--state-root` when the complete filesystem must survive.
133
137
 
134
138
  ### Use the hosted agent
135
139
 
@@ -69,7 +69,7 @@ approving the PR.
69
69
  | Server tools | [`agent/tools/`](../../examples/approval-buddy/agent/tools/) | Prepare evidence, approve, list buddies, and search GIFs. |
70
70
  | Deterministic policy | [`agent/lib/approve.ts`](../../examples/approval-buddy/agent/lib/approve.ts), [`agent/lib/buddies.ts`](../../examples/approval-buddy/agent/lib/buddies.ts) | Own the roster and live eligibility checks. |
71
71
  | Review subagents | [`agent/subagents/`](../../examples/approval-buddy/agent/subagents/) | Run deep audit and code-quality passes over the same evidence. |
72
- | Storage | [`agent/storage.ts`](../../examples/approval-buddy/agent/storage.ts) | Persist sessions and events through a custom durable adapter. |
72
+ | Storage | [`agent/storage.ts`](../../examples/approval-buddy/agent/storage.ts) | Persist sessions and events with `cursorHostedStorage` (Bugbot `agent_serve_*`). |
73
73
  | Evals and unit tests | [`evals/`](../../examples/approval-buddy/evals/), [`agent/lib/`](../../examples/approval-buddy/agent/lib/) | Protect routing, output contracts, policy, and GitHub behavior. |
74
74
 
75
75
  There are no authored skills, MCP connections, schedules, reminders, hooks,
@@ -72,7 +72,7 @@ Several examples need more:
72
72
  - GitHub examples require access to the target repository. Codebase wiki and
73
73
  Codeowners review call the host `gh` CLI for PR data; the codeowners
74
74
  fixtures run without network.
75
- - Approval Buddy and Security Reviewer define custom durable session storage.
75
+ - Example agents use `cursorHostedStorage` (`agent/storage.ts`) for Cursor-hosted session storage (control-plane proxy).
76
76
  - Remote PR coordinator starts remote agent sessions and needs access to its
77
77
  workflow backend.
78
78
 
@@ -71,7 +71,7 @@ status from the finding count even when the review posting result says
71
71
  | Progress channel | [`agent/channels/asr-progress.ts`](../../examples/security-reviewer/agent/channels/asr-progress.ts) | Serve live reviewer and triage state by `runId`. |
72
72
  | Playground renderer | [`agent/playground/tools/run_reviewers.tsx`](../../examples/security-reviewer/agent/playground/tools/run_reviewers.tsx) | Replace the generic tool chip with live module rows. |
73
73
  | SDK review pipeline | [`agent/lib/asr/`](../../examples/security-reviewer/agent/lib/asr/), [`review-stages.ts`](../../examples/security-reviewer/agent/lib/review-stages.ts) | Select modules, call model workers, triage, deduplicate, and write artifacts. |
74
- | Storage | [`agent/storage.ts`](../../examples/security-reviewer/agent/storage.ts) | Persist framework sessions through a custom adapter with lazy restore. |
74
+ | Storage | [`agent/storage.ts`](../../examples/security-reviewer/agent/storage.ts) | Persist framework sessions with `cursorHostedStorage` (lazy restore). |
75
75
  | A/B | [`agent/ab.ts`](../../examples/security-reviewer/agent/ab.ts), [`agent/ab.config.ts`](../../examples/security-reviewer/agent/ab.config.ts) | Compare all-severity versus high-only GitHub comments. |
76
76
  | Eval | [`evals/`](../../examples/security-reviewer/evals/) | Check stage-tool presence against a pinned sample. |
77
77
 
@@ -190,10 +190,10 @@ findings, accounting, and audit events.
190
190
 
191
191
  ## Separate session storage from review artifacts
192
192
 
193
- `defineStorage` sends agentkit session and event records to a custom durable
194
- store. Security Reviewer sets `restore: "off"` so startup doesn't load old
195
- review sessions in bulk. A continuation lookup can still fetch a needed
196
- session.
193
+ `defineStorage` + `cursorHostedStorage` sends agentkit session and event records
194
+ to Cursor-hosted Bugbot storage through the control-plane proxy. Security
195
+ Reviewer sets `restore: "off"` so startup doesn't load old review sessions in
196
+ bulk. A continuation lookup can still fetch a needed session.
197
197
 
198
198
  The staged review files are separate from session storage. Session-store
199
199
  durability doesn't preserve those files. All stages for one `runId` must see
package/docs/storage.md CHANGED
@@ -113,13 +113,27 @@ propagate errors — unlike session mirrors, which are at-most-once.
113
113
 
114
114
  Without `agent/storage.ts`, `host.kv` falls back to files under
115
115
  `--state-root/kv`. That is fine for local dogfood; it does **not**
116
- survive replacing the host. For prod, author `defineStorage` (for example
117
- `@cursor/july/storage/postgres-kv`) so both sessions and author KV share
118
- the durable backend.
116
+ survive replacing the host. For Cursor-managed hosting, prefer
117
+ `@cursor/july/storage/cursor-hosted` so sessions and author KV share the
118
+ platform Bugbot tables through a control-plane HTTP proxy (authenticated
119
+ as the deployment pod credential — engines never receive a database URL).
120
+ For self-hosted Postgres you own, use `@cursor/july/storage/postgres-kv`
121
+ and supply `BUGBOTDB_URL` / `AGENT_SERVE_DEPLOYMENT_ID` yourself.
122
+
123
+ ```ts
124
+ // agent/storage.ts — Cursor-managed hosting
125
+ import { defineStorage } from "@cursor/july/storage";
126
+ import { cursorHostedStorage } from "@cursor/july/storage/cursor-hosted";
127
+
128
+ export default defineStorage({
129
+ ...cursorHostedStorage(),
130
+ });
131
+ ```
119
132
 
120
133
  Built-in helpers:
121
134
 
122
135
  | Import | Backend |
123
136
  | --- | --- |
124
137
  | `@cursor/july/storage/file-kv` | File-per-key under `.agent-serve/kv` |
125
- | `@cursor/july/storage/postgres-kv` | `agent_serve_*` tables (`BUGBOTDB_URL`) |
138
+ | `@cursor/july/storage/cursor-hosted` | Platform Bugbot `agent_serve_*` via control-plane proxy |
139
+ | `@cursor/july/storage/postgres-kv` | Same tables; you supply `BUGBOTDB_URL` / `AGENT_SERVE_DEPLOYMENT_ID` |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cursor/july",
3
- "version": "0.1.8",
3
+ "version": "0.1.9",
4
4
  "description": "(early alpha) Filesystem-first framework for defining Cursor agents as markdown and TypeScript and serving them over channels with the Cursor SDK.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "repository": {
@@ -138,6 +138,13 @@
138
138
  "import": "./dist/storage-backends/postgres-kv.js",
139
139
  "default": "./dist/storage-backends/postgres-kv.js"
140
140
  },
141
+ "./storage/cursor-hosted": {
142
+ "anysphere-source": "./src/storage-backends/cursor-hosted.ts",
143
+ "bun": "./src/storage-backends/cursor-hosted.ts",
144
+ "types": "./dist/storage-backends/cursor-hosted.d.ts",
145
+ "import": "./dist/storage-backends/cursor-hosted.js",
146
+ "default": "./dist/storage-backends/cursor-hosted.js"
147
+ },
141
148
  "./package.json": {
142
149
  "anysphere-source": "./package.json",
143
150
  "bun": "./package.json",
@@ -40,9 +40,11 @@ import {
40
40
  } from "./discovery.js";
41
41
  import { CLI_COMMAND_NAME as CLI } from "./distribution.js";
42
42
  import {
43
+ isReservedSecretName,
43
44
  isValidCursorEventRepo,
44
45
  validateSettableSecretName,
45
46
  } from "./hosting.js";
47
+ import { deployProgressLabel, sanitizeCustomerError } from "./logs-client.js";
46
48
  import { stderrPalette } from "./terminal-style.js";
47
49
 
48
50
  const DEFAULT_POLL_INTERVAL_MS = 3_000;
@@ -554,6 +556,24 @@ async function deployOne(
554
556
  );
555
557
  return 1;
556
558
  }
559
+ // Repo-backed fields without a resolvable https origin become a static
560
+ // deploy body the control plane rejects. Fail here with a fix hint —
561
+ // common when origin is an unrecognized SSH shape (pass --repo).
562
+ if (
563
+ gitRepoUrl === undefined &&
564
+ (gitRef !== undefined ||
565
+ agentPath !== undefined ||
566
+ (options.cursorEventsRepos !== undefined &&
567
+ options.cursorEventsRepos.length > 0) ||
568
+ (options.allowDomains !== undefined && options.allowDomains.length > 0))
569
+ ) {
570
+ ctx.err(
571
+ `Could not resolve an https:// git repository URL for this deploy.\n` +
572
+ ` Pass --repo https://github.com/<owner>/<repo> (inferred from origin when possible).\n` +
573
+ ` Or omit --ref/--path/--cursor-events-repo/--allow-domain for a static engine.\n`
574
+ );
575
+ return 1;
576
+ }
557
577
  for (const repo of options.cursorEventsRepos ?? []) {
558
578
  if (!isValidCursorEventRepo(repo)) {
559
579
  ctx.err(`Invalid --cursor-events-repo: ${repo} (expected owner/name)\n`);
@@ -659,13 +679,11 @@ async function deployOne(
659
679
  }
660
680
 
661
681
  if (detail.status !== "running") {
662
- ctx.err(
663
- `Deployment ${slug} ended ${detail.status}${
664
- detail.lastError === null || detail.lastError === undefined
665
- ? ""
666
- : `: ${detail.lastError}`
667
- }\n`
668
- );
682
+ const errorDetail =
683
+ detail.lastError === null || detail.lastError === undefined
684
+ ? ""
685
+ : `: ${sanitizeCustomerError(detail.lastError)}`;
686
+ ctx.err(`Deployment ${slug} ended ${detail.status}${errorDetail}\n`);
669
687
  return 1;
670
688
  }
671
689
  ctx.out(
@@ -709,7 +727,11 @@ function warnMissingSecrets(
709
727
  return;
710
728
  }
711
729
  const setNames = new Set(set ?? []);
712
- const missing = declared.filter((name) => !setNames.has(name));
730
+ // Platform-managed names (CURSOR_*, hosted storage) are injected by the
731
+ // control plane and never appear in secrets list — do not warn on them.
732
+ const missing = declared.filter(
733
+ (name) => !setNames.has(name) && !isReservedSecretName(name)
734
+ );
713
735
  if (missing.length === 0) {
714
736
  return;
715
737
  }
@@ -793,11 +815,23 @@ export async function cmdDeploymentStatus(
793
815
  const lines = [
794
816
  `${detail.slug} (team ${detail.teamId})`,
795
817
  ` status: ${detail.status}`,
818
+ ];
819
+ if (detail.progress !== undefined) {
820
+ lines.push(` progress: ${detail.progress}`);
821
+ }
822
+ if (
823
+ detail.statusMessage !== null &&
824
+ detail.statusMessage !== undefined &&
825
+ detail.statusMessage.trim() !== ""
826
+ ) {
827
+ lines.push(` step: ${detail.statusMessage}`);
828
+ }
829
+ lines.push(
796
830
  ` generation: ${detail.generation}`,
797
831
  ` kind: ${detail.deploymentKind}`,
798
832
  ` alias: ${aliasUrl}`,
799
- ` playground: ${aliasUrl}/playground`,
800
- ];
833
+ ` playground: ${aliasUrl}/playground`
834
+ );
801
835
  if (detail.gitRepoUrl !== null && detail.gitRepoUrl !== undefined) {
802
836
  lines.push(` repo: ${detail.gitRepoUrl} @ ${detail.gitRef ?? ""}`);
803
837
  }
@@ -835,7 +869,7 @@ export async function cmdDeploymentStatus(
835
869
  );
836
870
  }
837
871
  if (detail.lastError !== null && detail.lastError !== undefined) {
838
- lines.push(` last error: ${detail.lastError}`);
872
+ lines.push(` last error: ${sanitizeCustomerError(detail.lastError)}`);
839
873
  }
840
874
  if (detail.desiredUpdatedAt !== undefined) {
841
875
  lines.push(` updated: ${detail.desiredUpdatedAt}`);
@@ -1229,7 +1263,9 @@ function createLineReader(
1229
1263
  /**
1230
1264
  * Poll deployment status every `pollIntervalMs` until it lands in
1231
1265
  * `terminal` (single-line progress on a TTY, one line per transition
1232
- * otherwise). Throws DeployApiError on request failures or timeout.
1266
+ * otherwise). Prefers customer `statusMessage` / `progress` over raw
1267
+ * status so create → checkout → start is visible. Throws DeployApiError
1268
+ * on request failures or timeout.
1233
1269
  */
1234
1270
  async function pollUntil(
1235
1271
  ctx: DeployCliContext,
@@ -1238,21 +1274,22 @@ async function pollUntil(
1238
1274
  ): Promise<DeploymentDetail> {
1239
1275
  const startedAt = Date.now();
1240
1276
  const tty = process.stderr.isTTY === true;
1241
- let lastStatus: DeploymentStatus | undefined;
1277
+ let lastLabel: string | undefined;
1242
1278
  let sawProgress = false;
1243
1279
 
1244
- const report = (status: DeploymentStatus): void => {
1280
+ const report = (detail: DeploymentDetail): void => {
1245
1281
  if (ctx.json) {
1246
1282
  return;
1247
1283
  }
1284
+ const label = deployProgressLabel(detail);
1248
1285
  const elapsed = Math.round((Date.now() - startedAt) / 1000);
1249
1286
  if (tty) {
1250
- ctx.err(`\r\x1b[2K${slug}: ${status} (${elapsed}s)`);
1287
+ ctx.err(`\r\x1b[2K${slug}: ${label} (${elapsed}s)`);
1251
1288
  sawProgress = true;
1252
- } else if (status !== lastStatus) {
1253
- ctx.err(`${slug}: ${status}\n`);
1289
+ } else if (label !== lastLabel) {
1290
+ ctx.err(`${slug}: ${label}\n`);
1254
1291
  }
1255
- lastStatus = status;
1292
+ lastLabel = label;
1256
1293
  };
1257
1294
  const finishProgressLine = (): void => {
1258
1295
  if (tty && sawProgress) {
@@ -1267,15 +1304,16 @@ async function pollUntil(
1267
1304
  finishProgressLine();
1268
1305
  throw error;
1269
1306
  });
1270
- report(detail.status);
1307
+ report(detail);
1271
1308
  if (terminal.has(detail.status)) {
1272
1309
  finishProgressLine();
1273
1310
  return detail;
1274
1311
  }
1275
1312
  if (Date.now() - startedAt >= ctx.timeoutMs) {
1276
1313
  finishProgressLine();
1314
+ const last = deployProgressLabel(detail);
1277
1315
  throw new DeployApiError(
1278
- `Timed out after ${Math.round(ctx.timeoutMs / 1000)}s waiting for ${slug} (last status: ${detail.status}). It may still converge — check \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.`,
1316
+ `Timed out after ${Math.round(ctx.timeoutMs / 1000)}s waiting for ${slug} (last: ${last}). It may still converge — check \`${CLI} deployment ${slug} --team ${ctx.teamId}\`.`,
1279
1317
  0
1280
1318
  );
1281
1319
  }
@@ -31,7 +31,9 @@ export type DeploymentStatus =
31
31
  export type DeploymentProgress =
32
32
  | "queued"
33
33
  | "preparing"
34
+ | "creating"
34
35
  | "building"
36
+ | "starting"
35
37
  | "finalizing"
36
38
  | "running"
37
39
  | "failed"
@@ -135,6 +137,12 @@ export interface Deployment {
135
137
  * backends; older responses omit it.
136
138
  */
137
139
  progress?: DeploymentProgress;
140
+ /**
141
+ * Live step while the deploy converges (e.g. "Checking out source…").
142
+ * Null when running / stopped or when only a failure is on the row.
143
+ * Present on newer backends; older responses omit it.
144
+ */
145
+ statusMessage?: string | null;
138
146
  generation: number;
139
147
  observedGeneration?: number | null;
140
148
  engineGeneration?: number | null;
@@ -218,7 +226,9 @@ const deploymentSchema = z.object({
218
226
  .enum([
219
227
  "queued",
220
228
  "preparing",
229
+ "creating",
221
230
  "building",
231
+ "starting",
222
232
  "finalizing",
223
233
  "running",
224
234
  "failed",
@@ -227,6 +237,7 @@ const deploymentSchema = z.object({
227
237
  "degraded",
228
238
  ])
229
239
  .optional(),
240
+ statusMessage: z.string().nullable().optional(),
230
241
  generation: z.number(),
231
242
  observedGeneration: z.number().nullable().optional(),
232
243
  engineGeneration: z.number().nullable().optional(),