@cursor/july 0.1.114 → 0.2.1
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.
- package/dist/channels/slack/channel-watch.d.ts +19 -3
- package/dist/channels/slack/channel-watch.d.ts.map +1 -1
- package/dist/channels/slack/channel-watch.js +48 -9
- package/dist/channels/slack/inbound.d.ts +7 -0
- package/dist/channels/slack/inbound.d.ts.map +1 -1
- package/dist/channels/slack/inbound.js +23 -0
- package/dist/channels/slack/slack-channel.d.ts.map +1 -1
- package/dist/channels/slack/slack-channel.js +87 -8
- package/dist/channels/slack/types.d.ts +11 -12
- package/dist/channels/slack/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/assets/{app.BqkJwOZ-.js → app.D23Y-7Tp.js} +4 -4
- package/dist/docs/assets/chunks/@localSearchIndexroot.CzCCM7N8.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.BJAi2KiV.js → VPLocalSearchBox.CWBeTFRZ.js} +1 -1
- package/dist/docs/assets/chunks/{arc.BZpXTgvV.js → arc.DSF2O3pm.js} +1 -1
- package/dist/docs/assets/chunks/{architectureDiagram-Q4EWVU46.WYI-7F-Y.js → architectureDiagram-Q4EWVU46.J52Wzbkg.js} +1 -1
- package/dist/docs/assets/chunks/{baseUniq.CZaUPpg0.js → baseUniq.CQS3LPCt.js} +1 -1
- package/dist/docs/assets/chunks/{blockDiagram-DXYQGD6D.D6UES2pD.js → blockDiagram-DXYQGD6D.Dw339Gr5.js} +1 -1
- package/dist/docs/assets/chunks/{c4Diagram-AHTNJAMY.cwebIe4i.js → c4Diagram-AHTNJAMY.BUdtOaRZ.js} +1 -1
- package/dist/docs/assets/chunks/channel.Bfu4df88.js +1 -0
- package/dist/docs/assets/chunks/{chunk-4BX2VUAB.fVyFnjxg.js → chunk-4BX2VUAB.CuOrkEqk.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-4TB4RGXK.BanufG1c.js → chunk-4TB4RGXK.BJNBcY7U.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-55IACEB6.VaSMz5-2.js → chunk-55IACEB6.VJK5LAm_.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-EDXVE4YY.CN2diZOM.js → chunk-EDXVE4YY.BYYLihvj.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-FMBD7UC4.g4ivypu3.js → chunk-FMBD7UC4.CmoW8BXP.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-OYMX7WX6.GZXKn9JJ.js → chunk-OYMX7WX6.DTGY4C-M.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-QZHKN3VN.itXxJZCd.js → chunk-QZHKN3VN.Cg5n67vl.js} +1 -1
- package/dist/docs/assets/chunks/{chunk-YZCP3GAM.-rw2GfvX.js → chunk-YZCP3GAM.C3GR_ia5.js} +1 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.DdfgtaWs.js +1 -0
- package/dist/docs/assets/chunks/clone.rkmfti6d.js +1 -0
- package/dist/docs/assets/chunks/{cose-bilkent-S5V4N54A.CmaI5br0.js → cose-bilkent-S5V4N54A.BTRG8N3b.js} +1 -1
- package/dist/docs/assets/chunks/{dagre-KV5264BT.4wY9S4Kt.js → dagre-KV5264BT.Bob_bp_p.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-5BDNPKRD.Pc3c0u9W.js → diagram-5BDNPKRD.ggPcs9uO.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-G4DWMVQ6.CYrWz-nj.js → diagram-G4DWMVQ6.BP0qyJkp.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-MMDJMWI5.Bgj5hukb.js → diagram-MMDJMWI5.B0X24UKr.js} +1 -1
- package/dist/docs/assets/chunks/{diagram-TYMM5635.DGMEXalS.js → diagram-TYMM5635.B4rXHFVt.js} +1 -1
- package/dist/docs/assets/chunks/{erDiagram-SMLLAGMA.GepTV9Im.js → erDiagram-SMLLAGMA._55Rt9oX.js} +1 -1
- package/dist/docs/assets/chunks/{flowDiagram-DWJPFMVM.DVKywg3j.js → flowDiagram-DWJPFMVM.DGP4XvR5.js} +1 -1
- package/dist/docs/assets/chunks/{ganttDiagram-T4ZO3ILL.C7qt9Mlo.js → ganttDiagram-T4ZO3ILL.BtXtkL4E.js} +1 -1
- package/dist/docs/assets/chunks/{gitGraphDiagram-UUTBAWPF.U30_r82P.js → gitGraphDiagram-UUTBAWPF.B9cPWblK.js} +1 -1
- package/dist/docs/assets/chunks/{graph.CyyMyAWv.js → graph.D8HzNexS.js} +1 -1
- package/dist/docs/assets/chunks/{infoDiagram-42DDH7IO.Dn9ACW3y.js → infoDiagram-42DDH7IO.Bw7CQUpi.js} +1 -1
- package/dist/docs/assets/chunks/{ishikawaDiagram-UXIWVN3A.DlIdIGOA.js → ishikawaDiagram-UXIWVN3A.MwkzF6nQ.js} +1 -1
- package/dist/docs/assets/chunks/{journeyDiagram-VCZTEJTY.DZj4vy4E.js → journeyDiagram-VCZTEJTY.DIGFF-3C.js} +1 -1
- package/dist/docs/assets/chunks/{kanban-definition-6JOO6SKY.Dl63eMUV.js → kanban-definition-6JOO6SKY.DhYef2BN.js} +1 -1
- package/dist/docs/assets/chunks/{layout.BLHZLWPH.js → layout.C0XUxuPi.js} +1 -1
- package/dist/docs/assets/chunks/{linear.aXKGKaNw.js → linear.BwNPpZex.js} +1 -1
- package/dist/docs/assets/chunks/{min.zWnFcpcc.js → min.CwAQdL7z.js} +1 -1
- package/dist/docs/assets/chunks/{mindmap-definition-QFDTVHPH.Qs4MQBea.js → mindmap-definition-QFDTVHPH.pWsSVLsP.js} +1 -1
- package/dist/docs/assets/chunks/{pieDiagram-DEJITSTG.BmPHgsk7.js → pieDiagram-DEJITSTG.BDJ3FbBy.js} +1 -1
- package/dist/docs/assets/chunks/{quadrantDiagram-34T5L4WZ.D5MQ3gwA.js → quadrantDiagram-34T5L4WZ.Co80izyB.js} +1 -1
- package/dist/docs/assets/chunks/{requirementDiagram-MS252O5E.CkdUFrO7.js → requirementDiagram-MS252O5E.JveKw4yx.js} +1 -1
- package/dist/docs/assets/chunks/{sankeyDiagram-XADWPNL6.KZrljrAV.js → sankeyDiagram-XADWPNL6.B0A7adPi.js} +1 -1
- package/dist/docs/assets/chunks/{sequenceDiagram-FGHM5R23.XMoEW-Lx.js → sequenceDiagram-FGHM5R23.d6JZ5Hre.js} +1 -1
- package/dist/docs/assets/chunks/{stateDiagram-FHFEXIEX.BmTzePLj.js → stateDiagram-FHFEXIEX.DWnL0NQl.js} +1 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.ZEetPk0E.js +1 -0
- package/dist/docs/assets/chunks/{theme.BfQzpxsg.js → theme.MJTLx0hh.js} +2 -2
- package/dist/docs/assets/chunks/{timeline-definition-GMOUNBTQ.Dug0oamp.js → timeline-definition-GMOUNBTQ.CFS7Ai4c.js} +1 -1
- package/dist/docs/assets/chunks/{vennDiagram-DHZGUBPP.BOTHrEFu.js → vennDiagram-DHZGUBPP.CwSlnjCf.js} +1 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.3gurI8YA.js +162 -0
- package/dist/docs/assets/chunks/{wardleyDiagram-NUSXRM2D.CoXKdfi6.js → wardleyDiagram-NUSXRM2D.B_8mvtjh.js} +1 -1
- package/dist/docs/assets/chunks/{xychartDiagram-5P7HB3ND.DXoSCjAW.js → xychartDiagram-5P7HB3ND.DtjU5H85.js} +1 -1
- package/dist/docs/assets/{guides_slack.md.Bo96y42E.js → guides_slack.md.Bjw2r2gL.js} +5 -5
- package/dist/docs/assets/{guides_slack.md.Bo96y42E.lean.js → guides_slack.md.Bjw2r2gL.lean.js} +1 -1
- package/dist/docs/assets/reference_cli.md.BvnQM8wd.js +97 -0
- package/dist/docs/assets/reference_cli.md.BvnQM8wd.lean.js +1 -0
- package/dist/docs/building-with-agents.html +35 -35
- package/dist/docs/deployment.html +35 -35
- package/dist/docs/evals.html +35 -35
- package/dist/docs/guides/agent-to-agent.html +35 -35
- package/dist/docs/guides/bitbucket.html +35 -35
- package/dist/docs/guides/cloud-agents.html +35 -35
- package/dist/docs/guides/convert-automation.html +35 -35
- package/dist/docs/guides/github.html +35 -35
- package/dist/docs/guides/gitlab.html +35 -35
- package/dist/docs/guides/grokbot-agents.html +35 -35
- package/dist/docs/guides/hooks.html +35 -35
- package/dist/docs/guides/improve.html +35 -35
- package/dist/docs/guides/jev.html +35 -35
- package/dist/docs/guides/mcp-oauth.html +35 -35
- package/dist/docs/guides/opentelemetry.html +35 -35
- package/dist/docs/guides/slack.html +39 -39
- package/dist/docs/guides/slack.md +16 -1
- package/dist/docs/guides/webhooks.html +35 -35
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +35 -35
- package/dist/docs/index.html +35 -35
- package/dist/docs/llms-full.txt +699 -752
- package/dist/docs/llms.txt +1 -1
- package/dist/docs/quickstart.html +35 -35
- package/dist/docs/reference/agent-config.html +35 -35
- package/dist/docs/reference/artifacts.html +35 -35
- package/dist/docs/reference/channels.html +35 -35
- package/dist/docs/reference/cli.html +127 -125
- package/dist/docs/reference/cli.md +685 -753
- package/dist/docs/reference/connections.html +35 -35
- package/dist/docs/reference/evals.html +35 -35
- package/dist/docs/reference/extensions.html +35 -35
- package/dist/docs/reference/hooks.html +35 -35
- package/dist/docs/reference/http-api.html +35 -35
- package/dist/docs/reference/instructions.html +35 -35
- package/dist/docs/reference/playground.html +35 -35
- package/dist/docs/reference/project-layout.html +35 -35
- package/dist/docs/reference/prompt.html +35 -35
- package/dist/docs/reference/schedules.html +35 -35
- package/dist/docs/reference/sessions.html +35 -35
- package/dist/docs/reference/skills.html +35 -35
- package/dist/docs/reference/subagents.html +35 -35
- package/dist/docs/reference/tools.html +35 -35
- package/dist/docs/templates/agentic-owners.html +35 -35
- package/dist/docs/templates/pr-autofixer.html +35 -35
- package/dist/docs/templates/security-reviewer.html +35 -35
- package/dist/docs/templates/thermo-quality-review.html +35 -35
- package/dist/docs/templates/thermo-review.html +35 -35
- package/dist/docs/templates/triage.html +35 -35
- package/dist/docs/troubleshooting.html +35 -35
- package/dist/files-backends/cursor-hosted.d.ts +11 -17
- package/dist/files-backends/cursor-hosted.d.ts.map +1 -1
- package/dist/files-backends/cursor-hosted.js +13 -41
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/internal/artifacts-store.d.ts +11 -0
- package/dist/internal/artifacts-store.d.ts.map +1 -1
- package/dist/internal/artifacts-store.js +113 -18
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +9 -1
- package/dist/internal/cursor/cursor-api-transport.d.ts +37 -0
- package/dist/internal/cursor/cursor-api-transport.d.ts.map +1 -0
- package/dist/internal/cursor/cursor-api-transport.js +44 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts +21 -0
- package/dist/internal/cursor/hosted-store-secrets.d.ts.map +1 -1
- package/dist/internal/cursor/hosted-store-secrets.js +26 -0
- package/dist/internal/cursor/store-api-client.d.ts +82 -0
- package/dist/internal/cursor/store-api-client.d.ts.map +1 -0
- package/dist/internal/cursor/store-api-client.js +227 -0
- package/dist/internal/deploy-client.d.ts +6 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +3 -0
- package/dist/internal/deploy-manifest.d.ts +8 -0
- package/dist/internal/deploy-manifest.d.ts.map +1 -1
- package/dist/internal/deploy-manifest.js +7 -1
- package/dist/internal/discovery/agent-config.d.ts +3 -1
- package/dist/internal/discovery/agent-config.d.ts.map +1 -1
- package/dist/internal/discovery/agent-config.js +7 -4
- package/dist/internal/framework-storage-selection.d.ts +1 -1
- package/dist/internal/framework-storage-selection.d.ts.map +1 -1
- package/dist/internal/platform-timers.d.ts.map +1 -1
- package/dist/internal/platform-timers.js +20 -2
- package/dist/internal/reminder-runner.d.ts +79 -1
- package/dist/internal/reminder-runner.d.ts.map +1 -1
- package/dist/internal/reminder-runner.js +287 -46
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +7 -0
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +13 -15
- package/dist/internal/store-api-protocol.d.ts +134 -0
- package/dist/internal/store-api-protocol.d.ts.map +1 -0
- package/dist/internal/store-api-protocol.js +126 -0
- package/dist/memory.d.ts +49 -10
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +193 -50
- package/dist/playground/assets/{index-CrMWlgUU.js → index-Cs0MKsv4.js} +30 -30
- package/dist/playground/assets/index-DLwnR9ys.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/reminders.d.ts +1 -1
- package/dist/reminders.d.ts.map +1 -1
- package/dist/types.d.ts +44 -10
- package/dist/types.d.ts.map +1 -1
- package/docs/guides/slack.md +16 -1
- package/docs/reference/cli.md +686 -754
- package/package.json +1 -1
- package/skills/setup-slack/SKILL.md +1 -1
- package/src/channels/slack/channel-watch.ts +57 -8
- package/src/channels/slack/inbound.ts +24 -0
- package/src/channels/slack/slack-channel.ts +127 -4
- package/src/channels/slack/types.ts +11 -12
- package/src/files-backends/cursor-hosted.ts +31 -68
- package/src/index.ts +1 -0
- package/src/internal/artifacts-store.ts +131 -25
- package/src/internal/cli-deploy.ts +12 -1
- package/src/internal/cursor/cursor-api-transport.ts +73 -0
- package/src/internal/cursor/hosted-store-secrets.ts +35 -0
- package/src/internal/cursor/store-api-client.ts +360 -0
- package/src/internal/deploy-client.ts +9 -0
- package/src/internal/deploy-manifest.ts +15 -0
- package/src/internal/discovery/agent-config.ts +8 -6
- package/src/internal/framework-storage-selection.ts +1 -1
- package/src/internal/platform-timers.ts +24 -2
- package/src/internal/reminder-runner.ts +454 -66
- package/src/internal/server.ts +8 -0
- package/src/internal/session-engine.ts +17 -22
- package/src/internal/store-api-protocol.ts +222 -0
- package/src/memory.ts +240 -59
- package/src/reminders.ts +1 -0
- package/src/types.ts +47 -10
- package/dist/docs/assets/chunks/@localSearchIndexroot.BnSgidYE.js +0 -1
- package/dist/docs/assets/chunks/channel.DdM5EfNW.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-6PBFFD2Q.CjfGHeg2.js +0 -1
- package/dist/docs/assets/chunks/classDiagram-v2-HSJHXN6E.CjfGHeg2.js +0 -1
- package/dist/docs/assets/chunks/clone.wSOICb_f.js +0 -1
- package/dist/docs/assets/chunks/stateDiagram-v2-QKLJ7IA2.Cu5X28zZ.js +0 -1
- package/dist/docs/assets/chunks/wardley-RL74JXVD.DXy2i1LS.js +0 -162
- package/dist/docs/assets/reference_cli.md.DLWDz9ij.js +0 -95
- package/dist/docs/assets/reference_cli.md.DLWDz9ij.lean.js +0 -1
- package/dist/playground/assets/index-C61EWMBK.css +0 -1
|
@@ -31,14 +31,34 @@ import type {
|
|
|
31
31
|
ArtifactTagInput,
|
|
32
32
|
JsonValue,
|
|
33
33
|
} from "../types.js";
|
|
34
|
+
import { mapBounded } from "./bounded.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Row-body hydration fan-out cap. `list()` fetches one object per row; on
|
|
38
|
+
* the Agent Store each fetch is a control-plane request, and an unbounded
|
|
39
|
+
* `Promise.all` over ~1k rows is a fleet-visible read burst (the 2026-09-18
|
|
40
|
+
* Agent Store read storm was this exact fan-out, run once per insert by the
|
|
41
|
+
* pre-metadata eviction path).
|
|
42
|
+
*/
|
|
43
|
+
const ROW_HYDRATION_CONCURRENCY = 8;
|
|
44
|
+
|
|
45
|
+
/** One row's identity + modification metadata, without its body. */
|
|
46
|
+
interface ArtifactRowMeta {
|
|
47
|
+
id: string;
|
|
48
|
+
/** Last-modified epoch ms, when the backing store tracks one. */
|
|
49
|
+
mtimeMs?: number;
|
|
50
|
+
}
|
|
34
51
|
|
|
35
52
|
interface ArtifactRowStore {
|
|
36
53
|
get(id: string): Promise<ArtifactRecord | undefined>;
|
|
37
54
|
put(record: ArtifactRecord): Promise<void>;
|
|
38
55
|
delete(id: string): Promise<void>;
|
|
39
56
|
list(): Promise<ArtifactRecord[]>;
|
|
40
|
-
/**
|
|
41
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Row ids + modification metadata without hydrating bodies — the
|
|
59
|
+
* per-insert cap check and (when mtimes are tracked) the eviction order.
|
|
60
|
+
*/
|
|
61
|
+
listMeta(): Promise<ArtifactRowMeta[]>;
|
|
42
62
|
}
|
|
43
63
|
|
|
44
64
|
/** File/blob payload bytes, keyed by artifact id (metadata lives on the row). */
|
|
@@ -202,21 +222,54 @@ export class ArtifactsStore {
|
|
|
202
222
|
return { kind, data: input.data };
|
|
203
223
|
}
|
|
204
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Trim to the retention cap from listing metadata alone. Past the cap
|
|
227
|
+
* this runs on every new-row insert, so it must not hydrate row bodies:
|
|
228
|
+
* on the Agent Store that is one read per row (~`max` reads per insert,
|
|
229
|
+
* forever, since eviction trims right back to the cap) — the 2026-09-18
|
|
230
|
+
* read storm on the security-reviewer deployment. The store listing
|
|
231
|
+
* carries last-modified metadata, and a row is rewritten on every upsert,
|
|
232
|
+
* so listing mtime orders rows the same way their `updatedAt` does.
|
|
233
|
+
*/
|
|
205
234
|
private async evictPastCap(): Promise<void> {
|
|
206
|
-
|
|
235
|
+
const metas = await this.rows.listMeta();
|
|
236
|
+
const excess = metas.length - this.max;
|
|
237
|
+
if (excess <= 0) {
|
|
207
238
|
return;
|
|
208
239
|
}
|
|
209
|
-
const
|
|
210
|
-
|
|
211
|
-
|
|
240
|
+
for (const id of await this.oldestRowIds(metas, excess)) {
|
|
241
|
+
await this.rows.delete(id);
|
|
242
|
+
// Without the row body there is no `content` flag to consult; delete
|
|
243
|
+
// the payload unconditionally — absent payloads delete as a no-op.
|
|
244
|
+
await this.content.delete(id);
|
|
212
245
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** The `excess` oldest row ids, cheapest available ordering first. */
|
|
249
|
+
private async oldestRowIds(
|
|
250
|
+
metas: ArtifactRowMeta[],
|
|
251
|
+
excess: number
|
|
252
|
+
): Promise<string[]> {
|
|
253
|
+
if (metas.some(meta => meta.mtimeMs !== undefined)) {
|
|
254
|
+
// A store that tracks mtimes must never regress to per-insert
|
|
255
|
+
// hydration. The rare entry missing one (anomalous on such a store)
|
|
256
|
+
// sorts oldest via the comparator's `?? 0` and gets evicted first —
|
|
257
|
+
// evicting an anomaly beats re-reading every row on every insert.
|
|
258
|
+
return metas
|
|
259
|
+
.sort(byOldestModified)
|
|
260
|
+
.slice(0, excess)
|
|
261
|
+
.map(meta => meta.id);
|
|
219
262
|
}
|
|
263
|
+
// A store that tracks no modification times at all (the local
|
|
264
|
+
// filesystem store, deliberately) leaves only the rows' own
|
|
265
|
+
// `updatedAt`: hydrate — bounded — and order exactly. Corrupt rows
|
|
266
|
+
// drop out of `list()`, so recompute the excess against the rows that
|
|
267
|
+
// actually parsed.
|
|
268
|
+
const records = await this.rows.list();
|
|
269
|
+
return records
|
|
270
|
+
.sort((a, b) => byNewestUpdated(b, a))
|
|
271
|
+
.slice(0, Math.max(0, records.length - this.max))
|
|
272
|
+
.map(record => record.id);
|
|
220
273
|
}
|
|
221
274
|
}
|
|
222
275
|
|
|
@@ -228,6 +281,18 @@ export function artifactIdForKey(key: string): string {
|
|
|
228
281
|
return `art_${createHash("sha256").update(key, "utf8").digest("hex").slice(0, 32)}`;
|
|
229
282
|
}
|
|
230
283
|
|
|
284
|
+
/**
|
|
285
|
+
* Oldest-modified first. Store mtimes can be coarse (S3 rounds to the
|
|
286
|
+
* second), so equal stamps tie-break by id — deterministic, and within a
|
|
287
|
+
* same-stamp cohort any victim is equally past the retention cap.
|
|
288
|
+
*/
|
|
289
|
+
function byOldestModified(a: ArtifactRowMeta, b: ArtifactRowMeta): number {
|
|
290
|
+
if (a.mtimeMs !== b.mtimeMs) {
|
|
291
|
+
return (a.mtimeMs ?? 0) - (b.mtimeMs ?? 0);
|
|
292
|
+
}
|
|
293
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
294
|
+
}
|
|
295
|
+
|
|
231
296
|
function byNewestUpdated(a: ArtifactRecord, b: ArtifactRecord): number {
|
|
232
297
|
if (a.updatedAt !== b.updatedAt) {
|
|
233
298
|
return a.updatedAt < b.updatedAt ? 1 : -1;
|
|
@@ -311,8 +376,10 @@ function createSinkRowStore(
|
|
|
311
376
|
const entries = await sink.list(
|
|
312
377
|
agentStoreKeys.artifactRowPrefix(agentName)
|
|
313
378
|
);
|
|
314
|
-
const bodies = await
|
|
315
|
-
entries
|
|
379
|
+
const bodies = await mapBounded(
|
|
380
|
+
entries,
|
|
381
|
+
ROW_HYDRATION_CONCURRENCY,
|
|
382
|
+
entry => sink.get(entry.key)
|
|
316
383
|
);
|
|
317
384
|
const records: ArtifactRecord[] = [];
|
|
318
385
|
for (const body of bodies) {
|
|
@@ -323,15 +390,42 @@ function createSinkRowStore(
|
|
|
323
390
|
}
|
|
324
391
|
return records;
|
|
325
392
|
},
|
|
326
|
-
async
|
|
327
|
-
const
|
|
328
|
-
|
|
329
|
-
)
|
|
330
|
-
|
|
393
|
+
async listMeta(): Promise<ArtifactRowMeta[]> {
|
|
394
|
+
const prefix = agentStoreKeys.artifactRowPrefix(agentName);
|
|
395
|
+
const metas: ArtifactRowMeta[] = [];
|
|
396
|
+
for (const entry of await sink.list(prefix)) {
|
|
397
|
+
const id = rowIdFromName(entry.key.slice(prefix.length));
|
|
398
|
+
if (id !== undefined) {
|
|
399
|
+
metas.push({ id, mtimeMs: entry.mtimeMs });
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
return metas;
|
|
331
403
|
},
|
|
332
404
|
};
|
|
333
405
|
}
|
|
334
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Invert the row naming scheme (`encodeURIComponent(id) + ".json"`).
|
|
409
|
+
* Foreign keys under the prefix — no `.json` suffix, malformed escapes,
|
|
410
|
+
* non-canonical encodings — read as absent rather than throwing. The
|
|
411
|
+
* round-trip check matters for eviction: a name that decodes but does not
|
|
412
|
+
* re-encode to itself (`a+b.json`, `foo bar.json`) would count toward the
|
|
413
|
+
* cap yet be undeletable through `key(id)`, leaving a phantom victim that
|
|
414
|
+
* every subsequent insert re-selects and fails to remove.
|
|
415
|
+
*/
|
|
416
|
+
function rowIdFromName(name: string): string | undefined {
|
|
417
|
+
if (!name.endsWith(".json") || name.includes("/")) {
|
|
418
|
+
return undefined;
|
|
419
|
+
}
|
|
420
|
+
const encoded = name.slice(0, -".json".length);
|
|
421
|
+
try {
|
|
422
|
+
const id = decodeURIComponent(encoded);
|
|
423
|
+
return encodeURIComponent(id) === encoded ? id : undefined;
|
|
424
|
+
} catch {
|
|
425
|
+
return undefined;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
335
429
|
function createSinkContentStore(
|
|
336
430
|
sink: FileSink,
|
|
337
431
|
agentName: string
|
|
@@ -410,18 +504,30 @@ function createFilesystemRowStore(stateRoot: string): ArtifactRowStore {
|
|
|
410
504
|
}
|
|
411
505
|
return records;
|
|
412
506
|
},
|
|
413
|
-
async
|
|
507
|
+
async listMeta(): Promise<ArtifactRowMeta[]> {
|
|
508
|
+
// No mtimes on purpose: local rows are cheap to hydrate, and the
|
|
509
|
+
// rows' own `updatedAt` orders eviction exactly, immune to coarse
|
|
510
|
+
// filesystem timestamp granularity.
|
|
511
|
+
let names: string[];
|
|
414
512
|
try {
|
|
415
|
-
|
|
416
|
-
return names.filter(
|
|
417
|
-
name => !name.startsWith(".") && name.endsWith(".json")
|
|
418
|
-
).length;
|
|
513
|
+
names = await readdir(dir);
|
|
419
514
|
} catch (error) {
|
|
420
515
|
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
421
|
-
return
|
|
516
|
+
return [];
|
|
422
517
|
}
|
|
423
518
|
throw error;
|
|
424
519
|
}
|
|
520
|
+
const metas: ArtifactRowMeta[] = [];
|
|
521
|
+
for (const name of names) {
|
|
522
|
+
if (name.startsWith(".")) {
|
|
523
|
+
continue;
|
|
524
|
+
}
|
|
525
|
+
const id = rowIdFromName(name);
|
|
526
|
+
if (id !== undefined) {
|
|
527
|
+
metas.push({ id });
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return metas;
|
|
425
531
|
},
|
|
426
532
|
};
|
|
427
533
|
}
|
|
@@ -1658,11 +1658,18 @@ export async function cmdStop(
|
|
|
1658
1658
|
}
|
|
1659
1659
|
const cancellation =
|
|
1660
1660
|
"cancellation" in accepted ? accepted.cancellation : undefined;
|
|
1661
|
+
// The backend escalates a publisher's stop of a multi-tenant app to the
|
|
1662
|
+
// application-level suspension; say so, and how to undo it.
|
|
1663
|
+
const stoppedGlobally = "scope" in accepted && accepted.scope === "global";
|
|
1661
1664
|
if (ctx.noWait) {
|
|
1662
1665
|
if (ctx.json) {
|
|
1663
1666
|
ctx.out(`${JSON.stringify(accepted, null, 2)}\n`);
|
|
1664
1667
|
} else {
|
|
1665
|
-
ctx.out(
|
|
1668
|
+
ctx.out(
|
|
1669
|
+
stoppedGlobally
|
|
1670
|
+
? `Stop accepted for ${slug} across all tenants (status: ${accepted.status}).\n`
|
|
1671
|
+
: `Stop accepted for ${slug} (status: ${accepted.status}).\n`
|
|
1672
|
+
);
|
|
1666
1673
|
}
|
|
1667
1674
|
return cancellation?.failed === 0 || cancellation === undefined ? 0 : 1;
|
|
1668
1675
|
}
|
|
@@ -1681,6 +1688,10 @@ export async function cmdStop(
|
|
|
1681
1688
|
}
|
|
1682
1689
|
if (ctx.json) {
|
|
1683
1690
|
ctx.out(`${JSON.stringify(detail, null, 2)}\n`);
|
|
1691
|
+
} else if (stoppedGlobally) {
|
|
1692
|
+
ctx.out(
|
|
1693
|
+
`Stopped ${slug} across all tenants (team ${ctx.teamId}); resume with \`${CLI} start ${slug} --global\`.\n`
|
|
1694
|
+
);
|
|
1684
1695
|
} else {
|
|
1685
1696
|
ctx.out(`Stopped ${slug} (team ${ctx.teamId}).\n`);
|
|
1686
1697
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one resolution of "which Cursor control plane, as whom" for hosted
|
|
3
|
+
* transports. The verb-lane files sink (`files-backends/cursor-hosted.ts`)
|
|
4
|
+
* and the domain-lane store API client (`store-api-client.ts`) both build on
|
|
5
|
+
* these, so the pod credential and base URL resolve identically on every
|
|
6
|
+
* wire — one place to change, no drift between lanes.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Same default as the hosted storage backend, inlined so these entrypoints
|
|
11
|
+
* do not import CLI code.
|
|
12
|
+
*/
|
|
13
|
+
export const DEFAULT_CURSOR_EXTERNAL_API_URL = "https://api.cursor.com";
|
|
14
|
+
|
|
15
|
+
export interface CursorApiTransportOptions {
|
|
16
|
+
/** Control-plane base URL (default: `CURSOR_API_BASE_URL`, else api.cursor.com). */
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
/** Bearer credential (default: `CURSOR_API_KEY`). */
|
|
19
|
+
apiKey?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Lazy credential, re-resolved on every call and preferred over
|
|
22
|
+
* {@link apiKey} — hosted freeze snapshots listen unsigned and bind the
|
|
23
|
+
* key file after serve start. Kept separate so `apiKey` stays a plain
|
|
24
|
+
* string.
|
|
25
|
+
*/
|
|
26
|
+
resolveApiKey?: () => string | undefined;
|
|
27
|
+
/** Test seam; defaults to global fetch. */
|
|
28
|
+
fetch?: typeof fetch;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function trimTrailingSlash(raw: string): string {
|
|
32
|
+
const trimmed = raw.trim();
|
|
33
|
+
return trimmed.endsWith("/") ? trimmed.slice(0, -1) : trimmed;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function resolveCursorApiBaseUrl(
|
|
37
|
+
options: CursorApiTransportOptions
|
|
38
|
+
): string {
|
|
39
|
+
return trimTrailingSlash(
|
|
40
|
+
options.baseUrl ??
|
|
41
|
+
process.env.CURSOR_API_BASE_URL ??
|
|
42
|
+
DEFAULT_CURSOR_EXTERNAL_API_URL
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The pod credential is the whole authorization on every hosted wire: the
|
|
48
|
+
* API resolves the caller's deployment from it, so there is no second,
|
|
49
|
+
* narrower credential to manage. Fails loudly rather than defaulting —
|
|
50
|
+
* calling the control plane as nobody is worse than not calling.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveCursorApiKey(
|
|
53
|
+
options: CursorApiTransportOptions
|
|
54
|
+
): string {
|
|
55
|
+
const configured = options.resolveApiKey?.() ?? options.apiKey;
|
|
56
|
+
const key = (configured ?? process.env.CURSOR_API_KEY)?.trim();
|
|
57
|
+
if (key === undefined || key === "") {
|
|
58
|
+
throw new Error(
|
|
59
|
+
"Cursor hosted transport requires CURSOR_API_KEY (deployment pod credential on Cursor hosting)"
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return key;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function resolveCursorApiFetch(
|
|
66
|
+
options: CursorApiTransportOptions
|
|
67
|
+
): typeof fetch {
|
|
68
|
+
const impl = options.fetch ?? globalThis.fetch;
|
|
69
|
+
if (typeof impl !== "function") {
|
|
70
|
+
throw new Error("Cursor hosted transport requires fetch");
|
|
71
|
+
}
|
|
72
|
+
return impl;
|
|
73
|
+
}
|
|
@@ -26,6 +26,41 @@ export const DEFAULT_AGENT_SERVE_STORE_PROTOCOL_FILE =
|
|
|
26
26
|
*/
|
|
27
27
|
export const AGENT_SERVE_FACTORY_FLUSH_PROTOCOL = "factory-flush-v1";
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Pin value additionally enabling the domain-call lane
|
|
31
|
+
* (`POST /internal/agentsdk/store/api/<call>`), where the server owns the
|
|
32
|
+
* storage pattern for framework state — starting with `memory.append`. A
|
|
33
|
+
* strict superset of {@link AGENT_SERVE_FACTORY_FLUSH_PROTOCOL}: verbs keep
|
|
34
|
+
* riding the factory flush wire under this pin too. The same fail-safe
|
|
35
|
+
* applies in both directions — an unknown value keeps today's behavior, and
|
|
36
|
+
* re-pinning to `factory-flush-v1` turns the domain lane back off without a
|
|
37
|
+
* client change.
|
|
38
|
+
*/
|
|
39
|
+
export const AGENT_SERVE_FACTORY_API_PROTOCOL = "factory-api-v1";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Does this pin select the factory flush wire for the store verbs? True for
|
|
43
|
+
* the flush pin and for the domain-API superset pin.
|
|
44
|
+
*/
|
|
45
|
+
export function storeProtocolEnablesFactoryFlush(
|
|
46
|
+
protocol: string | undefined
|
|
47
|
+
): boolean {
|
|
48
|
+
return (
|
|
49
|
+
protocol === AGENT_SERVE_FACTORY_FLUSH_PROTOCOL ||
|
|
50
|
+
protocol === AGENT_SERVE_FACTORY_API_PROTOCOL
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Does this pin enable the domain-call lane (server-owned storage patterns)?
|
|
56
|
+
* Only its exact value does; everything else keeps the client-side patterns.
|
|
57
|
+
*/
|
|
58
|
+
export function storeProtocolEnablesStoreApi(
|
|
59
|
+
protocol: string | undefined
|
|
60
|
+
): boolean {
|
|
61
|
+
return protocol === AGENT_SERVE_FACTORY_API_PROTOCOL;
|
|
62
|
+
}
|
|
63
|
+
|
|
29
64
|
const STORE_SOURCE_ID_ENV = "AGENT_SERVE_STORE_SOURCE_ID";
|
|
30
65
|
const FRAMEWORK_STORE_SOURCE_ID_ENV = "AGENT_SERVE_FRAMEWORK_STORE_SOURCE_ID";
|
|
31
66
|
const STORE_SOURCE_ID_FILE_ENV = "AGENT_SERVE_STORE_SOURCE_ID_FILE";
|