@cursor/july 0.1.32 → 0.1.34
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/README.md +1 -1
- package/dist/artifacts.d.ts.map +1 -1
- package/dist/artifacts.js +0 -5
- package/dist/bin/agent-serve.d.ts +1 -0
- package/dist/bin/agent-serve.d.ts.map +1 -1
- package/dist/bin/agent-serve.js +32 -2
- package/dist/channels/github/types.d.ts +10 -7
- package/dist/channels/github/types.d.ts.map +1 -1
- package/dist/docs/404.html +2 -2
- package/dist/docs/ab.html +3 -3
- package/dist/docs/assets/{app.C2aVczmo.js → app.FPupl4SP.js} +1 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.WoYunhnT.js +1 -0
- package/dist/docs/assets/chunks/{VPLocalSearchBox.DLEzetbD.js → VPLocalSearchBox.Cd182Cu0.js} +1 -1
- package/dist/docs/assets/chunks/{theme.BkOH1oMp.js → theme.BEM3Okcd.js} +2 -2
- package/dist/docs/assets/{example-agents_weather-agent.md.Dmrcphhl.js → example-agents_weather-agent.md.BADkPqxQ.js} +6 -4
- package/dist/docs/assets/example-agents_weather-agent.md.BADkPqxQ.lean.js +1 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.js +9 -0
- package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.lean.js +1 -0
- package/dist/docs/building-with-agents.html +3 -3
- package/dist/docs/concepts.html +3 -3
- package/dist/docs/deployment.html +3 -3
- package/dist/docs/evals.html +3 -3
- package/dist/docs/example-agents/approval-buddy.html +3 -3
- package/dist/docs/example-agents/benny.html +3 -3
- package/dist/docs/example-agents/bugbot.html +3 -3
- package/dist/docs/example-agents/codebase-wiki.html +3 -3
- package/dist/docs/example-agents/codeowners-review.html +3 -3
- package/dist/docs/example-agents/concierge.html +3 -3
- package/dist/docs/example-agents/fsd.html +3 -3
- package/dist/docs/example-agents/index.html +3 -3
- package/dist/docs/example-agents/knowledge-base.html +3 -3
- package/dist/docs/example-agents/oncall.html +3 -3
- package/dist/docs/example-agents/security-reviewer.html +3 -3
- package/dist/docs/example-agents/slack-agent.html +3 -3
- package/dist/docs/example-agents/weather-agent.html +10 -8
- package/dist/docs/guides/agent-to-agent.html +3 -3
- package/dist/docs/guides/cloud-runtime.html +5 -5
- package/dist/docs/guides/github.html +3 -3
- package/dist/docs/guides/human-in-the-loop.html +3 -3
- package/dist/docs/guides/mcp-oauth.html +3 -3
- package/dist/docs/guides/slack.html +3 -3
- package/dist/docs/guides/webhooks.html +3 -3
- package/dist/docs/hashmap.json +1 -1
- package/dist/docs/hillclimbing.html +3 -3
- package/dist/docs/index.html +3 -3
- package/dist/docs/quickstart.html +3 -3
- package/dist/docs/reference/agent-config.html +3 -3
- package/dist/docs/reference/channels.html +3 -3
- package/dist/docs/reference/cli.html +3 -3
- package/dist/docs/reference/connections.html +3 -3
- package/dist/docs/reference/hooks.html +3 -3
- package/dist/docs/reference/http-api.html +3 -3
- package/dist/docs/reference/instructions.html +3 -3
- package/dist/docs/reference/playground.html +3 -3
- package/dist/docs/reference/project-layout.html +3 -3
- package/dist/docs/reference/prompt.html +3 -3
- package/dist/docs/reference/schedules.html +3 -3
- package/dist/docs/reference/sessions.html +3 -3
- package/dist/docs/reference/skills.html +3 -3
- package/dist/docs/reference/subagents.html +3 -3
- package/dist/docs/reference/tools.html +3 -3
- package/dist/docs/scaffolding-agents.html +3 -3
- package/dist/docs/storage.html +3 -3
- package/dist/docs/troubleshooting.html +3 -3
- package/dist/files-backends/agent-store-presigned-url.d.ts.map +1 -1
- package/dist/files-backends/agent-store-presigned-url.js +19 -810
- package/dist/files.d.ts +20 -0
- package/dist/files.d.ts.map +1 -1
- package/dist/files.js +20 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/internal/artifacts-store.d.ts +14 -6
- package/dist/internal/artifacts-store.d.ts.map +1 -1
- package/dist/internal/artifacts-store.js +160 -16
- package/dist/internal/cli-convert-automation.d.ts +39 -0
- package/dist/internal/cli-convert-automation.d.ts.map +1 -0
- package/dist/internal/cli-convert-automation.js +280 -0
- package/dist/internal/cli-deploy.d.ts +2 -0
- package/dist/internal/cli-deploy.d.ts.map +1 -1
- package/dist/internal/cli-deploy.js +1 -1
- package/dist/internal/cli-mcp-oauth.d.ts +23 -0
- package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
- package/dist/internal/cli-mcp-oauth.js +57 -33
- package/dist/internal/convert-automation/convert-workflow.d.ts +10 -0
- package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -0
- package/dist/internal/convert-automation/convert-workflow.js +998 -0
- package/dist/internal/convert-automation/index.d.ts +3 -0
- package/dist/internal/convert-automation/index.d.ts.map +1 -0
- package/dist/internal/convert-automation/index.js +1 -0
- package/dist/internal/convert-automation/slug.d.ts +11 -0
- package/dist/internal/convert-automation/slug.d.ts.map +1 -0
- package/dist/internal/convert-automation/slug.js +36 -0
- package/dist/internal/convert-automation/types.d.ts +191 -0
- package/dist/internal/convert-automation/types.d.ts.map +1 -0
- package/dist/internal/convert-automation/types.js +13 -0
- package/dist/internal/cursor-relay-core.d.ts +12 -0
- package/dist/internal/cursor-relay-core.d.ts.map +1 -1
- package/dist/internal/cursor-relay-core.js +42 -8
- package/dist/internal/deploy-client.d.ts +31 -0
- package/dist/internal/deploy-client.d.ts.map +1 -1
- package/dist/internal/deploy-client.js +48 -0
- package/dist/internal/discovery.d.ts.map +1 -1
- package/dist/internal/discovery.js +3 -32
- package/dist/internal/init-project.d.ts +1 -0
- package/dist/internal/init-project.d.ts.map +1 -1
- package/dist/internal/init-project.js +1 -1
- package/dist/internal/mcp-endpoint.d.ts +6 -0
- package/dist/internal/mcp-endpoint.d.ts.map +1 -1
- package/dist/internal/mcp-endpoint.js +168 -0
- package/dist/internal/resolved-connections.d.ts +15 -0
- package/dist/internal/resolved-connections.d.ts.map +1 -1
- package/dist/internal/resolved-connections.js +12 -5
- package/dist/internal/review-comments.d.ts +9 -1
- package/dist/internal/review-comments.d.ts.map +1 -1
- package/dist/internal/review-comments.js +76 -0
- package/dist/internal/server.d.ts.map +1 -1
- package/dist/internal/server.js +83 -8
- package/dist/internal/session-engine.d.ts +56 -3
- package/dist/internal/session-engine.d.ts.map +1 -1
- package/dist/internal/session-engine.js +252 -83
- package/dist/internal/storage-coordinator.d.ts +0 -15
- package/dist/internal/storage-coordinator.d.ts.map +1 -1
- package/dist/internal/storage-coordinator.js +0 -52
- package/dist/internal/workspace.d.ts +10 -5
- package/dist/internal/workspace.d.ts.map +1 -1
- package/dist/internal/workspace.js +13 -0
- package/dist/memory.d.ts +22 -6
- package/dist/memory.d.ts.map +1 -1
- package/dist/memory.js +100 -8
- package/dist/playground/assets/index-CDDWw0YX.js +85 -0
- package/dist/playground/assets/index-MVuNTd8v.css +1 -0
- package/dist/playground/index.html +2 -2
- package/dist/storage.d.ts +0 -4
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +0 -4
- package/dist/types.d.ts +18 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/example-agents/weather-agent.md +29 -11
- package/docs/guides/cloud-runtime.md +6 -4
- package/package.json +3 -22
- package/src/artifacts.ts +0 -6
- package/src/bin/agent-serve.ts +36 -2
- package/src/channels/github/types.ts +10 -7
- package/src/files-backends/agent-store-presigned-url.ts +7 -5
- package/src/files.ts +27 -0
- package/src/index.ts +6 -1
- package/src/internal/artifacts-store.ts +185 -22
- package/src/internal/cli-convert-automation.ts +352 -0
- package/src/internal/cli-deploy.ts +1 -1
- package/src/internal/cli-mcp-oauth.ts +69 -31
- package/src/internal/convert-automation/convert-workflow.ts +1422 -0
- package/src/internal/convert-automation/index.ts +10 -0
- package/src/internal/convert-automation/slug.ts +38 -0
- package/src/internal/convert-automation/types.ts +199 -0
- package/src/internal/cursor-relay-core.ts +62 -3
- package/src/internal/deploy-client.ts +78 -0
- package/src/internal/discovery.ts +3 -37
- package/src/internal/init-project.ts +1 -1
- package/src/internal/mcp-endpoint.ts +243 -0
- package/src/internal/resolved-connections.ts +32 -6
- package/src/internal/review-comments.ts +102 -2
- package/src/internal/server.ts +108 -2
- package/src/internal/session-engine.ts +329 -99
- package/src/internal/storage-coordinator.ts +0 -62
- package/src/internal/workspace.ts +19 -5
- package/src/memory.ts +126 -10
- package/src/storage.ts +0 -6
- package/src/types.ts +15 -0
- package/dist/channels/slack/post-update-delivery.d.ts +0 -85
- package/dist/channels/slack/post-update-delivery.d.ts.map +0 -1
- package/dist/docs/assets/chunks/@localSearchIndexroot.xgMnlUSO.js +0 -1
- package/dist/docs/assets/example-agents_weather-agent.md.Dmrcphhl.lean.js +0 -1
- package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.js +0 -9
- package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.lean.js +0 -1
- package/dist/internal/json-dir-store.d.ts +0 -32
- package/dist/internal/json-dir-store.d.ts.map +0 -1
- package/dist/internal/persistence-coordinator.d.ts +0 -127
- package/dist/internal/persistence-coordinator.d.ts.map +0 -1
- package/dist/multi-tenant.d.ts +0 -80
- package/dist/multi-tenant.d.ts.map +0 -1
- package/dist/multi-tenant.js +0 -69
- package/dist/persistence.d.ts +0 -184
- package/dist/persistence.d.ts.map +0 -1
- package/dist/playground/assets/index-50PKeJlG.css +0 -1
- package/dist/playground/assets/index-C0f2Wl1q.js +0 -85
package/src/bin/agent-serve.ts
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* agent-serve run [--dir .] [--message "..."] [--json|--text]
|
|
11
11
|
* agent-serve trajectory --events <file.ndjson>
|
|
12
12
|
* agent-serve init [dir] [--json]
|
|
13
|
+
* agent-serve convert-automation <url> [--dir <path>] [--json]
|
|
13
14
|
* agent-serve eval [--dir .] [evalId...]
|
|
14
15
|
* agent-serve validate [--dir .]
|
|
15
16
|
* agent-serve slack <subcommand>
|
|
@@ -69,7 +70,7 @@ const HELP = `${CLI} — serve a filesystem-defined Cursor agent over channels
|
|
|
69
70
|
Usage:
|
|
70
71
|
${CLI} serve [--dir <path>] [--port <n>] [--host <host>] [--dev]
|
|
71
72
|
[--mode single|multi] [--api-key <key>] [--no-schedules]
|
|
72
|
-
[--public-url <url>]
|
|
73
|
+
[--public-url <url>] [--cloud-tools-url <url>]
|
|
73
74
|
[--cursor-events --repo <owner/name>]...
|
|
74
75
|
${CLI} dev [path] [--port <n>] [--host <host>] …
|
|
75
76
|
Same flags as serve; implies --dev. Path defaults to
|
|
@@ -100,6 +101,7 @@ Usage:
|
|
|
100
101
|
${CLI} call <tool> [--input <json>] [--dir <path> | --url <baseUrl> | --prod]
|
|
101
102
|
[--session <id>] [--slug <slug>] [--team <id>]
|
|
102
103
|
${CLI} init [dir] [--json]
|
|
104
|
+
${CLI} convert-automation <url> [--dir <path>] [--json]
|
|
103
105
|
${CLI} install-skills [--print] [--json]
|
|
104
106
|
${CLI} mcp --prod [--slug <slug>] [--team <id>] [--dir <path>]
|
|
105
107
|
| --url <baseUrl> [--bearer-token <t>]
|
|
@@ -188,6 +190,17 @@ Commands:
|
|
|
188
190
|
run login when unsigned, then print next steps. --json is
|
|
189
191
|
machine-readable and never blocks on login. No directory:
|
|
190
192
|
print the setup guide.
|
|
193
|
+
convert-automation
|
|
194
|
+
Export a Cursor Automation into an agentkit project. <url> is
|
|
195
|
+
the dashboard URL (…/automations/<uuid> or
|
|
196
|
+
…/custom-agents/<uuid>) or a bare uuid. Fetches the definition
|
|
197
|
+
with your Cursor credentials, writes the converted files into
|
|
198
|
+
--dir (default ./<automation-name>), fills in the init scaffold
|
|
199
|
+
around them (existing files are kept), and installs npm
|
|
200
|
+
dependencies. MCP servers convert to Cursor-account connections
|
|
201
|
+
resolved at runtime (manage authorization in Cursor, dashboard
|
|
202
|
+
-> MCP). Review the project, then run \`validate\` / \`dev\`.
|
|
203
|
+
Warnings never fail the command.
|
|
191
204
|
install-skills
|
|
192
205
|
Install (or refresh) the Agent SDK skills for coding agents in
|
|
193
206
|
~/.cursor/skills/agentkit and print where they landed. \`init\`
|
|
@@ -299,6 +312,9 @@ Options:
|
|
|
299
312
|
(e.g. https://agent-serve.example.com). Required for peer
|
|
300
313
|
connections (defineConnection({ agent })) on cloud-runtime
|
|
301
314
|
turns; local turns always use loopback.
|
|
315
|
+
--cloud-tools-url serve: authenticated HTTP MCP URL for this deployment's
|
|
316
|
+
direct server-tool endpoint. Hosted AgentSDK deployments
|
|
317
|
+
configure this automatically.
|
|
302
318
|
--no-schedules Disable the cron runner outside dev mode.
|
|
303
319
|
--no-control-plane serve: do not start the bundled control plane (schedule
|
|
304
320
|
and reminder clocks). Cursor hosting passes this so the
|
|
@@ -358,7 +374,8 @@ Options:
|
|
|
358
374
|
--no-artifacts eval: do not write run artifacts.
|
|
359
375
|
--skip-report eval: ignore reporters from evals.config.ts / eval files.
|
|
360
376
|
--no-stream run/eval: silence live progress on stderr (JSON still prints).
|
|
361
|
-
--json JSON output (init/install-skills/
|
|
377
|
+
--json JSON output (init/convert-automation/install-skills/
|
|
378
|
+
info/chat/eval/logs/
|
|
362
379
|
sessions/session/github; run defaults to JSON). logs:
|
|
363
380
|
NDJSON progress/log events. session: trajectory JSON
|
|
364
381
|
(not raw events).
|
|
@@ -486,6 +503,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
486
503
|
name: { type: "string" },
|
|
487
504
|
env: { type: "string" },
|
|
488
505
|
"public-url": { type: "string" },
|
|
506
|
+
"cloud-tools-url": { type: "string" },
|
|
489
507
|
"mount-path": { type: "string" },
|
|
490
508
|
"base-url": { type: "string" },
|
|
491
509
|
print: { type: "boolean", default: false },
|
|
@@ -604,6 +622,21 @@ async function main(argv: string[]): Promise<number> {
|
|
|
604
622
|
});
|
|
605
623
|
}
|
|
606
624
|
|
|
625
|
+
case "convert-automation": {
|
|
626
|
+
const { cmdConvertAutomation } = await import(
|
|
627
|
+
"../internal/cli-convert-automation.js"
|
|
628
|
+
);
|
|
629
|
+
const dirFlagPassed =
|
|
630
|
+
rest.includes("--dir") || rest.some((arg) => arg.startsWith("--dir="));
|
|
631
|
+
return cmdConvertAutomation({
|
|
632
|
+
url: positionals[0],
|
|
633
|
+
// Default target is ./<automation-name-slug>, not the cwd.
|
|
634
|
+
dir: dirFlagPassed ? dir : undefined,
|
|
635
|
+
json: values.json,
|
|
636
|
+
apiKey: values["api-key"],
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
607
640
|
case "install-skills": {
|
|
608
641
|
const { cmdInstallSkills } = await import("../internal/cli-skills.js");
|
|
609
642
|
return cmdInstallSkills({
|
|
@@ -1170,6 +1203,7 @@ async function main(argv: string[]): Promise<number> {
|
|
|
1170
1203
|
playground: values["no-playground"] === true ? false : undefined,
|
|
1171
1204
|
docs: values["no-docs"] === true ? false : undefined,
|
|
1172
1205
|
publicUrl: values["public-url"],
|
|
1206
|
+
cloudToolsUrl: values["cloud-tools-url"],
|
|
1173
1207
|
cursorEvents:
|
|
1174
1208
|
values["cursor-events"] === true
|
|
1175
1209
|
? { repos: values.repo ?? [] }
|
|
@@ -404,10 +404,12 @@ export type GitHubStatusHook = (
|
|
|
404
404
|
) => GitHubDispatchResult;
|
|
405
405
|
|
|
406
406
|
/**
|
|
407
|
-
* Catch-all for
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
*
|
|
407
|
+
* Catch-all for webhook events with no typed hook (`push`,
|
|
408
|
+
* `pull_request_review`, `pull_request_review_thread`, …). Events that have
|
|
409
|
+
* a typed hook (`pull_request`, `issues`, `check_suite`, `check_run`,
|
|
410
|
+
* `workflow_run`, `status`, comments) NEVER reach this hook — when their
|
|
411
|
+
* typed hook is not declared, the delivery is skipped as `no_handler`
|
|
412
|
+
* instead of falling back here. Return `null` to ignore.
|
|
411
413
|
*/
|
|
412
414
|
export type GitHubEventHook = (
|
|
413
415
|
ctx: GitHubEventContext,
|
|
@@ -490,9 +492,10 @@ export interface GitHubChannelOptions {
|
|
|
490
492
|
/** Opt-in legacy commit `status` webhook handler. No default dispatch. */
|
|
491
493
|
onStatus?: GitHubStatusHook;
|
|
492
494
|
/**
|
|
493
|
-
* Catch-all webhook handler
|
|
494
|
-
*
|
|
495
|
-
*
|
|
495
|
+
* Catch-all webhook handler for events without a typed hook (`push`,
|
|
496
|
+
* `pull_request_review`, …). NOT a fallback: events with a typed hook are
|
|
497
|
+
* skipped as `no_handler` when that hook is undeclared, and never reach
|
|
498
|
+
* this one. Prefer typed hooks when the event has one.
|
|
496
499
|
*/
|
|
497
500
|
onEvent?: GitHubEventHook;
|
|
498
501
|
/**
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Re-export
|
|
2
|
+
* Re-export bundled into dist by `scripts/bundle-publish-shims.mjs`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Imports the `presigned-url` entry point rather than the package root: the
|
|
5
|
+
* root is a barrel over the whole library, and bundling through it inlines
|
|
6
|
+
* every module the barrel can reach (sync engine, local sqlite index, secure
|
|
7
|
+
* open) into the published tarball. This module has no imports of its own, so
|
|
8
|
+
* the bundle stays limited to the validator.
|
|
7
9
|
*/
|
|
8
|
-
export { createAgentStorePresignedUrlValidatorForBackend } from "@anysphere/agent-store-sync";
|
|
10
|
+
export { createAgentStorePresignedUrlValidatorForBackend } from "@anysphere/agent-store-sync/presigned-url";
|
package/src/files.ts
CHANGED
|
@@ -140,6 +140,33 @@ export const fileKeys = {
|
|
|
140
140
|
`${STORAGE_KEY_ROOT}/${agent}/files/sessions/${sessionId}/`,
|
|
141
141
|
} as const;
|
|
142
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Framework-owned keys for state that lives on the deployment's Agent Store
|
|
145
|
+
* but is not author `host.files` data: artifact rows and payloads, and the
|
|
146
|
+
* cross-session memory journal. Kept out of `files/` so author listings never
|
|
147
|
+
* see framework rows.
|
|
148
|
+
*/
|
|
149
|
+
export const agentStoreKeys = {
|
|
150
|
+
artifactRow: (agent: string, id: string): string =>
|
|
151
|
+
`${STORAGE_KEY_ROOT}/${agent}/artifacts/rows/${encodeURIComponent(id)}.json`,
|
|
152
|
+
artifactRowPrefix: (agent: string): string =>
|
|
153
|
+
`${STORAGE_KEY_ROOT}/${agent}/artifacts/rows/`,
|
|
154
|
+
artifactContent: (agent: string, id: string): string =>
|
|
155
|
+
`${STORAGE_KEY_ROOT}/${agent}/artifacts/content/${encodeURIComponent(id)}`,
|
|
156
|
+
memoryDir: (agent: string): string => `${STORAGE_KEY_ROOT}/${agent}/memory`,
|
|
157
|
+
memoryJournal: (agent: string): string =>
|
|
158
|
+
`${STORAGE_KEY_ROOT}/${agent}/memory/journal.jsonl`,
|
|
159
|
+
memoryJournalRotated: (agent: string, stamp: string): string =>
|
|
160
|
+
`${STORAGE_KEY_ROOT}/${agent}/memory/journal-${stamp}.jsonl`,
|
|
161
|
+
} as const;
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Deployment store FUSE mount on cloud-runtime VMs. Mirrors
|
|
165
|
+
* `AGENT_STORE_MOUNT_ROOT` / `AGENT_SERVE_DEPLOYMENT_STORE_MOUNT_NAME` in
|
|
166
|
+
* `@anysphere/constants` (not a dependency of this published package).
|
|
167
|
+
*/
|
|
168
|
+
export const DEPLOYMENT_STORE_MOUNT_PATH = "/cursor/stores/deployment";
|
|
169
|
+
|
|
143
170
|
/** Errors raised by the file surface. Thrown by sinks and by the framework. */
|
|
144
171
|
export class FileConflictError extends Error {
|
|
145
172
|
constructor(
|
package/src/index.ts
CHANGED
|
@@ -76,6 +76,7 @@ export {
|
|
|
76
76
|
MCP_CHANNEL_ID,
|
|
77
77
|
MCP_ENDPOINT_PATH,
|
|
78
78
|
mcpEndpointToolNames,
|
|
79
|
+
TOOL_MCP_ENDPOINT_PATH,
|
|
79
80
|
} from "./internal/mcp-endpoint.js";
|
|
80
81
|
export {
|
|
81
82
|
ReminderValidationError,
|
|
@@ -91,7 +92,11 @@ export {
|
|
|
91
92
|
UnknownToolError,
|
|
92
93
|
} from "./internal/session-engine.js";
|
|
93
94
|
export type { MemoryBackend, TurnMemoryRecord } from "./memory.js";
|
|
94
|
-
export {
|
|
95
|
+
export {
|
|
96
|
+
agentStoreMemoryBackend,
|
|
97
|
+
fileMemoryBackend,
|
|
98
|
+
memoryHook,
|
|
99
|
+
} from "./memory.js";
|
|
95
100
|
export type { PromptTag } from "./prompt.js";
|
|
96
101
|
export { prompt } from "./prompt.js";
|
|
97
102
|
export type {
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Durable artifact rows behind `ctx.artifacts`
|
|
2
|
+
* Durable artifact rows and payloads behind `ctx.artifacts`
|
|
3
|
+
* ({@link ArtifactsApi}).
|
|
3
4
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
5
|
+
* On Cursor-managed hosting rows and file payloads live on the deployment's
|
|
6
|
+
* Agent Store (the same store behind `ctx.host.files`, so they survive
|
|
7
|
+
* deploys and are visible on cloud VMs under the store mount); otherwise
|
|
8
|
+
* they land under `--state-root/artifacts` on local disk.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17,6 +19,11 @@ import {
|
|
|
17
19
|
} from "node:fs/promises";
|
|
18
20
|
import { join } from "node:path";
|
|
19
21
|
import { resolveArtifactsMax } from "../artifacts.js";
|
|
22
|
+
import { agentStoreKeys, type FileSink } from "../files.js";
|
|
23
|
+
import {
|
|
24
|
+
cursorHostedFiles,
|
|
25
|
+
isCursorHostedFilesAvailable,
|
|
26
|
+
} from "../files-backends/cursor-hosted.js";
|
|
20
27
|
import type {
|
|
21
28
|
ArtifactListFilter,
|
|
22
29
|
ArtifactRecord,
|
|
@@ -24,23 +31,30 @@ import type {
|
|
|
24
31
|
ArtifactTagInput,
|
|
25
32
|
JsonValue,
|
|
26
33
|
} from "../types.js";
|
|
27
|
-
import type { StorageCoordinator } from "./storage-coordinator.js";
|
|
28
34
|
|
|
29
|
-
// TODO(agent-stores): the KV backing store is migrating to Agent Stores.
|
|
30
|
-
// Swap only this adapter to the Agent Stores API when that lands; the
|
|
31
|
-
// engine, tool, HTTP, and playground layers must not change.
|
|
32
35
|
interface ArtifactRowStore {
|
|
33
36
|
get(id: string): Promise<ArtifactRecord | undefined>;
|
|
34
37
|
put(record: ArtifactRecord): Promise<void>;
|
|
35
38
|
delete(id: string): Promise<void>;
|
|
36
39
|
list(): Promise<ArtifactRecord[]>;
|
|
40
|
+
/** Row count without hydrating bodies — the per-insert cap check. */
|
|
41
|
+
count(): Promise<number>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** File/blob payload bytes, keyed by artifact id (metadata lives on the row). */
|
|
45
|
+
interface ArtifactContentStore {
|
|
46
|
+
get(id: string): Promise<Uint8Array | undefined>;
|
|
47
|
+
put(id: string, body: Uint8Array): Promise<void>;
|
|
48
|
+
delete(id: string): Promise<void>;
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
export interface CreateArtifactsStoreOptions {
|
|
40
52
|
stateRoot: string;
|
|
41
|
-
|
|
53
|
+
agentName: string;
|
|
42
54
|
/** `agent/artifacts.ts` config, when authored. */
|
|
43
55
|
config?: ArtifactsConfig;
|
|
56
|
+
/** Explicit sink override (tests). Omitted → hosted store or local disk. */
|
|
57
|
+
sink?: FileSink;
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
/** Kind used by freeform tags when no kinds are declared. */
|
|
@@ -54,16 +68,23 @@ export const ARTIFACT_DEFAULT_KIND = "artifact";
|
|
|
54
68
|
*/
|
|
55
69
|
export class ArtifactsStore {
|
|
56
70
|
private readonly rows: ArtifactRowStore;
|
|
71
|
+
private readonly content: ArtifactContentStore;
|
|
57
72
|
private readonly config: ArtifactsConfig | undefined;
|
|
58
73
|
private readonly max: number;
|
|
59
74
|
/** Serializes tags so read-modify-write upserts and eviction don't race. */
|
|
60
75
|
private tagChain: Promise<unknown> = Promise.resolve();
|
|
61
76
|
|
|
62
77
|
constructor(options: CreateArtifactsStoreOptions) {
|
|
63
|
-
|
|
64
|
-
options.
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
const sink =
|
|
79
|
+
options.sink ??
|
|
80
|
+
(isCursorHostedFilesAvailable() ? cursorHostedFiles() : undefined);
|
|
81
|
+
if (sink === undefined) {
|
|
82
|
+
this.rows = createFilesystemRowStore(options.stateRoot);
|
|
83
|
+
this.content = createFilesystemContentStore(options.stateRoot);
|
|
84
|
+
} else {
|
|
85
|
+
this.rows = createSinkRowStore(sink, options.agentName);
|
|
86
|
+
this.content = createSinkContentStore(sink, options.agentName);
|
|
87
|
+
}
|
|
67
88
|
this.config = options.config;
|
|
68
89
|
this.max = resolveArtifactsMax(options.config);
|
|
69
90
|
}
|
|
@@ -82,7 +103,9 @@ export class ArtifactsStore {
|
|
|
82
103
|
input.key === undefined
|
|
83
104
|
? `art_${randomUUID()}`
|
|
84
105
|
: artifactIdForKey(input.key);
|
|
85
|
-
|
|
106
|
+
// Fresh random ids can't collide; only keyed tags upsert.
|
|
107
|
+
const existing =
|
|
108
|
+
input.key === undefined ? undefined : await this.rows.get(id);
|
|
86
109
|
const now = new Date().toISOString();
|
|
87
110
|
const record: ArtifactRecord = {
|
|
88
111
|
id,
|
|
@@ -96,6 +119,25 @@ export class ArtifactsStore {
|
|
|
96
119
|
createdAt: existing?.createdAt ?? now,
|
|
97
120
|
updatedAt: now,
|
|
98
121
|
};
|
|
122
|
+
if (input.contents !== undefined) {
|
|
123
|
+
const body =
|
|
124
|
+
typeof input.contents === "string"
|
|
125
|
+
? Buffer.from(input.contents, "utf8")
|
|
126
|
+
: input.contents;
|
|
127
|
+
await this.content.put(id, body);
|
|
128
|
+
record.content = {
|
|
129
|
+
size: body.byteLength,
|
|
130
|
+
contentType: input.contentType,
|
|
131
|
+
};
|
|
132
|
+
} else if (existing?.content !== undefined) {
|
|
133
|
+
if (existing.sessionId === record.sessionId) {
|
|
134
|
+
record.content = existing.content;
|
|
135
|
+
} else {
|
|
136
|
+
// Keys are deployment-global: rebinding a keyed row to another
|
|
137
|
+
// session must not carry the prior session's payload with it.
|
|
138
|
+
await this.content.delete(id);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
99
141
|
await this.rows.put(record);
|
|
100
142
|
if (existing === undefined) {
|
|
101
143
|
await this.evictPastCap();
|
|
@@ -103,6 +145,15 @@ export class ArtifactsStore {
|
|
|
103
145
|
return record;
|
|
104
146
|
}
|
|
105
147
|
|
|
148
|
+
get(id: string): Promise<ArtifactRecord | undefined> {
|
|
149
|
+
return this.rows.get(id);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Payload bytes for one artifact; undefined when it carries none. */
|
|
153
|
+
getContent(id: string): Promise<Uint8Array | undefined> {
|
|
154
|
+
return this.content.get(id);
|
|
155
|
+
}
|
|
156
|
+
|
|
106
157
|
async list(filter?: ArtifactListFilter): Promise<ArtifactRecord[]> {
|
|
107
158
|
const records = await this.rows.list();
|
|
108
159
|
return records
|
|
@@ -152,6 +203,9 @@ export class ArtifactsStore {
|
|
|
152
203
|
}
|
|
153
204
|
|
|
154
205
|
private async evictPastCap(): Promise<void> {
|
|
206
|
+
if ((await this.rows.count()) <= this.max) {
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
155
209
|
const records = await this.rows.list();
|
|
156
210
|
if (records.length <= this.max) {
|
|
157
211
|
return;
|
|
@@ -159,6 +213,9 @@ export class ArtifactsStore {
|
|
|
159
213
|
const oldestFirst = records.sort((a, b) => byNewestUpdated(b, a));
|
|
160
214
|
for (const record of oldestFirst.slice(0, records.length - this.max)) {
|
|
161
215
|
await this.rows.delete(record.id);
|
|
216
|
+
if (record.content !== undefined) {
|
|
217
|
+
await this.content.delete(record.id);
|
|
218
|
+
}
|
|
162
219
|
}
|
|
163
220
|
}
|
|
164
221
|
}
|
|
@@ -206,23 +263,90 @@ function isArtifactRecord(
|
|
|
206
263
|
return true;
|
|
207
264
|
}
|
|
208
265
|
|
|
209
|
-
function
|
|
210
|
-
|
|
266
|
+
function parseRecordBytes(
|
|
267
|
+
bytes: Uint8Array | undefined
|
|
268
|
+
): ArtifactRecord | undefined {
|
|
269
|
+
if (bytes === undefined) {
|
|
270
|
+
return undefined;
|
|
271
|
+
}
|
|
272
|
+
let value: JsonValue;
|
|
273
|
+
try {
|
|
274
|
+
value = JSON.parse(Buffer.from(bytes).toString("utf8")) as JsonValue;
|
|
275
|
+
} catch {
|
|
276
|
+
// A corrupt row must not poison list().
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
return isArtifactRecord(value) ? value : undefined;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function sinkBody(
|
|
283
|
+
found: Awaited<ReturnType<FileSink["get"]>>
|
|
284
|
+
): Uint8Array | undefined {
|
|
285
|
+
if (found === undefined) {
|
|
286
|
+
return undefined;
|
|
287
|
+
}
|
|
288
|
+
return found instanceof Uint8Array ? found : found.body;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** Rows as one JSON file each on the deployment's Agent Store. */
|
|
292
|
+
function createSinkRowStore(
|
|
293
|
+
sink: FileSink,
|
|
294
|
+
agentName: string
|
|
211
295
|
): ArtifactRowStore {
|
|
296
|
+
const key = (id: string): string => agentStoreKeys.artifactRow(agentName, id);
|
|
212
297
|
return {
|
|
213
298
|
async get(id): Promise<ArtifactRecord | undefined> {
|
|
214
|
-
|
|
215
|
-
return isArtifactRecord(value) ? value : undefined;
|
|
299
|
+
return parseRecordBytes(sinkBody(await sink.get(key(id))));
|
|
216
300
|
},
|
|
217
301
|
async put(record): Promise<void> {
|
|
218
|
-
await
|
|
302
|
+
await sink.put(
|
|
303
|
+
key(record.id),
|
|
304
|
+
Buffer.from(JSON.stringify(record), "utf8")
|
|
305
|
+
);
|
|
219
306
|
},
|
|
220
307
|
async delete(id): Promise<void> {
|
|
221
|
-
await
|
|
308
|
+
await sink.delete(key(id));
|
|
222
309
|
},
|
|
223
310
|
async list(): Promise<ArtifactRecord[]> {
|
|
224
|
-
const
|
|
225
|
-
|
|
311
|
+
const entries = await sink.list(
|
|
312
|
+
agentStoreKeys.artifactRowPrefix(agentName)
|
|
313
|
+
);
|
|
314
|
+
const bodies = await Promise.all(
|
|
315
|
+
entries.map((entry) => sink.get(entry.key))
|
|
316
|
+
);
|
|
317
|
+
const records: ArtifactRecord[] = [];
|
|
318
|
+
for (const body of bodies) {
|
|
319
|
+
const record = parseRecordBytes(sinkBody(body));
|
|
320
|
+
if (record !== undefined) {
|
|
321
|
+
records.push(record);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
return records;
|
|
325
|
+
},
|
|
326
|
+
async count(): Promise<number> {
|
|
327
|
+
const entries = await sink.list(
|
|
328
|
+
agentStoreKeys.artifactRowPrefix(agentName)
|
|
329
|
+
);
|
|
330
|
+
return entries.length;
|
|
331
|
+
},
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function createSinkContentStore(
|
|
336
|
+
sink: FileSink,
|
|
337
|
+
agentName: string
|
|
338
|
+
): ArtifactContentStore {
|
|
339
|
+
const key = (id: string): string =>
|
|
340
|
+
agentStoreKeys.artifactContent(agentName, id);
|
|
341
|
+
return {
|
|
342
|
+
async get(id): Promise<Uint8Array | undefined> {
|
|
343
|
+
return sinkBody(await sink.get(key(id)));
|
|
344
|
+
},
|
|
345
|
+
async put(id, body): Promise<void> {
|
|
346
|
+
await sink.put(key(id), body);
|
|
347
|
+
},
|
|
348
|
+
async delete(id): Promise<void> {
|
|
349
|
+
await sink.delete(key(id));
|
|
226
350
|
},
|
|
227
351
|
};
|
|
228
352
|
}
|
|
@@ -286,5 +410,44 @@ function createFilesystemRowStore(stateRoot: string): ArtifactRowStore {
|
|
|
286
410
|
}
|
|
287
411
|
return records;
|
|
288
412
|
},
|
|
413
|
+
async count(): Promise<number> {
|
|
414
|
+
try {
|
|
415
|
+
const names = await readdir(dir);
|
|
416
|
+
return names.filter(
|
|
417
|
+
(name) => !name.startsWith(".") && name.endsWith(".json")
|
|
418
|
+
).length;
|
|
419
|
+
} catch (error) {
|
|
420
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
421
|
+
return 0;
|
|
422
|
+
}
|
|
423
|
+
throw error;
|
|
424
|
+
}
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function createFilesystemContentStore(stateRoot: string): ArtifactContentStore {
|
|
430
|
+
const dir = join(stateRoot, "artifacts", "content");
|
|
431
|
+
const filePath = (id: string): string => join(dir, encodeURIComponent(id));
|
|
432
|
+
return {
|
|
433
|
+
async get(id): Promise<Uint8Array | undefined> {
|
|
434
|
+
try {
|
|
435
|
+
return new Uint8Array(await readFile(filePath(id)));
|
|
436
|
+
} catch (error) {
|
|
437
|
+
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
|
438
|
+
return undefined;
|
|
439
|
+
}
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
},
|
|
443
|
+
async put(id, body): Promise<void> {
|
|
444
|
+
await mkdir(dir, { recursive: true });
|
|
445
|
+
const tmp = join(dir, `.tmp-${randomUUID()}`);
|
|
446
|
+
await writeFile(tmp, body);
|
|
447
|
+
await rename(tmp, filePath(id));
|
|
448
|
+
},
|
|
449
|
+
async delete(id): Promise<void> {
|
|
450
|
+
await rm(filePath(id), { force: true });
|
|
451
|
+
},
|
|
289
452
|
};
|
|
290
453
|
}
|