@cursor/july 0.1.33 → 0.1.35

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 (171) hide show
  1. package/README.md +1 -1
  2. package/dist/artifacts.d.ts.map +1 -1
  3. package/dist/artifacts.js +0 -5
  4. package/dist/bin/agent-serve.d.ts +1 -0
  5. package/dist/bin/agent-serve.d.ts.map +1 -1
  6. package/dist/bin/agent-serve.js +32 -2
  7. package/dist/channels/github/types.d.ts +10 -7
  8. package/dist/channels/github/types.d.ts.map +1 -1
  9. package/dist/docs/404.html +2 -2
  10. package/dist/docs/ab.html +3 -3
  11. package/dist/docs/assets/{app.CgZqLyWL.js → app.D5Mv1T0U.js} +1 -1
  12. package/dist/docs/assets/chunks/@localSearchIndexroot.Cu7b6o1D.js +1 -0
  13. package/dist/docs/assets/chunks/{VPLocalSearchBox.C4KigoQn.js → VPLocalSearchBox.CMq_BQce.js} +1 -1
  14. package/dist/docs/assets/chunks/{theme.C_Ty4yT7.js → theme.C6D9UPLK.js} +2 -2
  15. package/dist/docs/assets/{example-agents_weather-agent.md.Dmrcphhl.js → example-agents_weather-agent.md.BADkPqxQ.js} +6 -4
  16. package/dist/docs/assets/example-agents_weather-agent.md.BADkPqxQ.lean.js +1 -0
  17. package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.js +9 -0
  18. package/dist/docs/assets/guides_cloud-runtime.md.CDJGvVC4.lean.js +1 -0
  19. package/dist/docs/building-with-agents.html +3 -3
  20. package/dist/docs/concepts.html +3 -3
  21. package/dist/docs/deployment.html +3 -3
  22. package/dist/docs/evals.html +3 -3
  23. package/dist/docs/example-agents/approval-buddy.html +3 -3
  24. package/dist/docs/example-agents/benny.html +3 -3
  25. package/dist/docs/example-agents/bugbot.html +3 -3
  26. package/dist/docs/example-agents/codebase-wiki.html +3 -3
  27. package/dist/docs/example-agents/codeowners-review.html +3 -3
  28. package/dist/docs/example-agents/concierge.html +3 -3
  29. package/dist/docs/example-agents/fsd.html +3 -3
  30. package/dist/docs/example-agents/index.html +3 -3
  31. package/dist/docs/example-agents/knowledge-base.html +3 -3
  32. package/dist/docs/example-agents/oncall.html +3 -3
  33. package/dist/docs/example-agents/security-reviewer.html +3 -3
  34. package/dist/docs/example-agents/slack-agent.html +3 -3
  35. package/dist/docs/example-agents/weather-agent.html +10 -8
  36. package/dist/docs/guides/agent-to-agent.html +3 -3
  37. package/dist/docs/guides/cloud-runtime.html +5 -5
  38. package/dist/docs/guides/github.html +3 -3
  39. package/dist/docs/guides/human-in-the-loop.html +3 -3
  40. package/dist/docs/guides/mcp-oauth.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/prompt.html +3 -3
  57. package/dist/docs/reference/schedules.html +3 -3
  58. package/dist/docs/reference/sessions.html +3 -3
  59. package/dist/docs/reference/skills.html +3 -3
  60. package/dist/docs/reference/subagents.html +3 -3
  61. package/dist/docs/reference/tools.html +3 -3
  62. package/dist/docs/scaffolding-agents.html +3 -3
  63. package/dist/docs/storage.html +3 -3
  64. package/dist/docs/troubleshooting.html +3 -3
  65. package/dist/files-backends/agent-store-presigned-url.d.ts +3 -11
  66. package/dist/files.d.ts +20 -0
  67. package/dist/files.d.ts.map +1 -1
  68. package/dist/files.js +20 -0
  69. package/dist/index.d.ts +2 -2
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +2 -2
  72. package/dist/internal/artifacts-store.d.ts +14 -6
  73. package/dist/internal/artifacts-store.d.ts.map +1 -1
  74. package/dist/internal/artifacts-store.js +160 -16
  75. package/dist/internal/cli-convert-automation.d.ts +39 -0
  76. package/dist/internal/cli-convert-automation.d.ts.map +1 -0
  77. package/dist/internal/cli-convert-automation.js +280 -0
  78. package/dist/internal/cli-deploy.d.ts +2 -0
  79. package/dist/internal/cli-deploy.d.ts.map +1 -1
  80. package/dist/internal/cli-deploy.js +1 -1
  81. package/dist/internal/cli-mcp-oauth.d.ts +23 -0
  82. package/dist/internal/cli-mcp-oauth.d.ts.map +1 -1
  83. package/dist/internal/cli-mcp-oauth.js +57 -33
  84. package/dist/internal/convert-automation/convert-workflow.d.ts +10 -0
  85. package/dist/internal/convert-automation/convert-workflow.d.ts.map +1 -0
  86. package/dist/internal/convert-automation/convert-workflow.js +998 -0
  87. package/dist/internal/convert-automation/index.d.ts +3 -0
  88. package/dist/internal/convert-automation/index.d.ts.map +1 -0
  89. package/dist/internal/convert-automation/index.js +1 -0
  90. package/dist/internal/convert-automation/slug.d.ts +11 -0
  91. package/dist/internal/convert-automation/slug.d.ts.map +1 -0
  92. package/dist/internal/convert-automation/slug.js +36 -0
  93. package/dist/internal/convert-automation/types.d.ts +191 -0
  94. package/dist/internal/convert-automation/types.d.ts.map +1 -0
  95. package/dist/internal/convert-automation/types.js +13 -0
  96. package/dist/internal/deploy-client.d.ts +31 -0
  97. package/dist/internal/deploy-client.d.ts.map +1 -1
  98. package/dist/internal/deploy-client.js +48 -0
  99. package/dist/internal/discovery.d.ts.map +1 -1
  100. package/dist/internal/discovery.js +3 -32
  101. package/dist/internal/host-kv.d.ts +6 -2
  102. package/dist/internal/host-kv.d.ts.map +1 -1
  103. package/dist/internal/init-project.d.ts +1 -0
  104. package/dist/internal/init-project.d.ts.map +1 -1
  105. package/dist/internal/init-project.js +1 -1
  106. package/dist/internal/mcp-endpoint.d.ts +6 -0
  107. package/dist/internal/mcp-endpoint.d.ts.map +1 -1
  108. package/dist/internal/mcp-endpoint.js +168 -0
  109. package/dist/internal/resolved-connections.d.ts +15 -0
  110. package/dist/internal/resolved-connections.d.ts.map +1 -1
  111. package/dist/internal/resolved-connections.js +12 -5
  112. package/dist/internal/server.d.ts.map +1 -1
  113. package/dist/internal/server.js +75 -7
  114. package/dist/internal/session-engine.d.ts +56 -3
  115. package/dist/internal/session-engine.d.ts.map +1 -1
  116. package/dist/internal/session-engine.js +262 -84
  117. package/dist/internal/storage-coordinator.d.ts +9 -16
  118. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  119. package/dist/internal/storage-coordinator.js +7 -52
  120. package/dist/internal/storage-roles.d.ts +78 -0
  121. package/dist/internal/storage-roles.d.ts.map +1 -0
  122. package/dist/internal/storage-roles.js +24 -0
  123. package/dist/internal/workspace.d.ts +36 -5
  124. package/dist/internal/workspace.d.ts.map +1 -1
  125. package/dist/internal/workspace.js +66 -0
  126. package/dist/memory.d.ts +22 -6
  127. package/dist/memory.d.ts.map +1 -1
  128. package/dist/memory.js +100 -8
  129. package/dist/playground/assets/{index-CVlCAcUx.js → index-D7OV8B_H.js} +50 -50
  130. package/dist/playground/assets/index-DOb96C0M.css +1 -0
  131. package/dist/playground/index.html +2 -2
  132. package/dist/storage.d.ts +0 -4
  133. package/dist/storage.d.ts.map +1 -1
  134. package/dist/storage.js +0 -4
  135. package/dist/types.d.ts +18 -0
  136. package/dist/types.d.ts.map +1 -1
  137. package/docs/example-agents/weather-agent.md +29 -11
  138. package/docs/guides/cloud-runtime.md +6 -4
  139. package/package.json +3 -1
  140. package/src/artifacts.ts +0 -6
  141. package/src/bin/agent-serve.ts +36 -2
  142. package/src/channels/github/types.ts +10 -7
  143. package/src/files.ts +27 -0
  144. package/src/index.ts +6 -1
  145. package/src/internal/artifacts-store.ts +185 -22
  146. package/src/internal/cli-convert-automation.ts +352 -0
  147. package/src/internal/cli-deploy.ts +1 -1
  148. package/src/internal/cli-mcp-oauth.ts +69 -31
  149. package/src/internal/convert-automation/convert-workflow.ts +1422 -0
  150. package/src/internal/convert-automation/index.ts +10 -0
  151. package/src/internal/convert-automation/slug.ts +38 -0
  152. package/src/internal/convert-automation/types.ts +199 -0
  153. package/src/internal/deploy-client.ts +78 -0
  154. package/src/internal/discovery.ts +3 -37
  155. package/src/internal/host-kv.ts +6 -2
  156. package/src/internal/init-project.ts +1 -1
  157. package/src/internal/mcp-endpoint.ts +243 -0
  158. package/src/internal/resolved-connections.ts +32 -6
  159. package/src/internal/server.ts +99 -1
  160. package/src/internal/session-engine.ts +345 -103
  161. package/src/internal/storage-coordinator.ts +15 -63
  162. package/src/internal/storage-roles.ts +86 -0
  163. package/src/internal/workspace.ts +81 -6
  164. package/src/memory.ts +126 -10
  165. package/src/storage.ts +0 -6
  166. package/src/types.ts +15 -0
  167. package/dist/docs/assets/chunks/@localSearchIndexroot.B1uHGxBZ.js +0 -1
  168. package/dist/docs/assets/example-agents_weather-agent.md.Dmrcphhl.lean.js +0 -1
  169. package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.js +0 -9
  170. package/dist/docs/assets/guides_cloud-runtime.md.BZ2GA7Es.lean.js +0 -1
  171. package/dist/playground/assets/index-BRhBbnd4.css +0 -1
@@ -14,6 +14,13 @@
14
14
  * and A/B metrics go to their dedicated tables (`evals` / `abs`).
15
15
  * Reminder documents live on the control plane. Everything shares one
16
16
  * bounded, serialized queue.
17
+ *
18
+ * It does three unrelated jobs, declared separately as
19
+ * {@link FrameworkSessionStore}, {@link AuthorKvStore}, and
20
+ * {@link EvalAbStore} — see `storage-roles.ts` for why they differ and where
21
+ * each is headed. The queue stays shared: it is one bounded, serialized chain
22
+ * per agent, and splitting it would change backpressure behavior, which this
23
+ * seam deliberately does not touch.
17
24
  */
18
25
 
19
26
  import type { ABMetricSample } from "../ab.js";
@@ -30,6 +37,11 @@ import type { JsonValue, SessionEvent, SessionRecord } from "../types.js";
30
37
  import type { ABSnapshot } from "./ab-snapshot.js";
31
38
  import { describeError } from "./describe-error.js";
32
39
  import type { EvalRunStorage } from "./eval-run-store.js";
40
+ import type {
41
+ AuthorKvStore,
42
+ EvalAbStore,
43
+ FrameworkSessionStore,
44
+ } from "./storage-roles.js";
33
45
 
34
46
  /** Event types that close a unit of work — flush point for turn-end batching. */
35
47
  const TURN_BOUNDARY_EVENTS: ReadonlySet<SessionEvent["type"]> = new Set([
@@ -85,7 +97,9 @@ interface SessionBuffer {
85
97
  debounceTimer?: ReturnType<typeof setTimeout>;
86
98
  }
87
99
 
88
- export class StorageCoordinator {
100
+ export class StorageCoordinator
101
+ implements FrameworkSessionStore, AuthorKvStore, EvalAbStore
102
+ {
89
103
  readonly policy: ResolvedStoragePolicy;
90
104
 
91
105
  private readonly definition: StorageDefinition;
@@ -458,68 +472,6 @@ export class StorageCoordinator {
458
472
  await this.definition.delete?.(key, this.context("policy"));
459
473
  }
460
474
 
461
- // ==========================================================================
462
- // Artifacts (`ctx.artifacts`)
463
- // ==========================================================================
464
-
465
- /**
466
- * Whether the sink can serve awaited artifact rows: `get` (upsert
467
- * read-back), `list` (listing + eviction), and `delete` (`max` cap
468
- * eviction) are all required — without `delete` the retention cap
469
- * could never evict, so rows fall back to the filesystem store.
470
- */
471
- get supportsArtifacts(): boolean {
472
- return (
473
- this.definition.get !== undefined &&
474
- this.definition.list !== undefined &&
475
- this.definition.delete !== undefined
476
- );
477
- }
478
-
479
- /** Awaited artifact-row read. Errors propagate (read-after-write). */
480
- async artifactGet(id: string): Promise<JsonValue | undefined> {
481
- const get = this.definition.get;
482
- if (get === undefined) {
483
- throw new Error("storage get is not configured");
484
- }
485
- const value = await get(
486
- storageKeys.artifact(this.agentName, id),
487
- this.context("restore")
488
- );
489
- return value ?? undefined;
490
- }
491
-
492
- /** Awaited artifact-row write (straight to the sink, like author KV). */
493
- async artifactPut(id: string, value: JsonValue): Promise<void> {
494
- await this.definition.put(
495
- storageKeys.artifact(this.agentName, id),
496
- value,
497
- this.context("policy")
498
- );
499
- }
500
-
501
- /** Awaited artifact-row delete. Errors propagate (cap eviction). */
502
- async artifactDelete(id: string): Promise<void> {
503
- const del = this.definition.delete;
504
- if (del === undefined) {
505
- throw new Error("storage delete is not configured");
506
- }
507
- await del(storageKeys.artifact(this.agentName, id), this.context("policy"));
508
- }
509
-
510
- /** All artifact-row values for this agent. Errors propagate. */
511
- async artifactList(): Promise<JsonValue[]> {
512
- const list = this.definition.list;
513
- if (list === undefined) {
514
- throw new Error("storage list is not configured");
515
- }
516
- const entries = await list(
517
- storageKeys.artifactPrefix(this.agentName),
518
- this.context("restore")
519
- );
520
- return Array.isArray(entries) ? entries.map((entry) => entry.value) : [];
521
- }
522
-
523
475
  // ==========================================================================
524
476
  // Evals (dedicated `evals` table)
525
477
  // ==========================================================================
@@ -0,0 +1,86 @@
1
+ /**
2
+ * The three unrelated jobs `defineStorage` currently does, named separately.
3
+ *
4
+ * {@link import("./storage-coordinator.js").StorageCoordinator} implements all
5
+ * three today over one sink and one delivery queue. Splitting the *types* is
6
+ * the seam that lets each job change independently:
7
+ *
8
+ * - {@link FrameworkSessionStore} is framework state. Session records, event
9
+ * chunks, and the continuation index are how a session survives the process,
10
+ * so they belong to Agent Serve, not to the agent. Today they persist only
11
+ * when the author writes `agent/storage.ts` — the defect this seam exists to
12
+ * let us fix, by giving the framework its own provider that is automatic and
13
+ * not author-configurable.
14
+ * - {@link AuthorKvStore} is author state: whatever `ctx.host.kv` was handed.
15
+ * It converges onto the author file plane, which is a different destination
16
+ * from the one above.
17
+ * - {@link EvalAbStore} is playground/experiment bookkeeping with its own
18
+ * dedicated tables, and follows neither.
19
+ *
20
+ * Consumers should depend on the narrowest role they use. That is what keeps a
21
+ * later change to framework persistence from silently reaching author KV, and
22
+ * it is why these are interfaces rather than a comment on one class.
23
+ */
24
+
25
+ import type { ABMetricSample } from "../ab.js";
26
+ import type { StorageContext } from "../storage.js";
27
+ import type { JsonValue, SessionEvent, SessionRecord } from "../types.js";
28
+ import type { ABSnapshot } from "./ab-snapshot.js";
29
+ import type { EvalRunStorage } from "./eval-run-store.js";
30
+
31
+ /**
32
+ * Framework-owned session durability: what has to be true for a session to
33
+ * outlive the process that served it.
34
+ *
35
+ * Writes are fire-and-forget by design — a throwing backend is logged and its
36
+ * write dropped, because storage must never stall or fail a turn. That is
37
+ * at-most-once, and it is the property Track F later has to tighten, since a
38
+ * dropped record is a session that cannot be restored.
39
+ */
40
+ export interface FrameworkSessionStore {
41
+ /** Durable session-record update; coalesced until the next flush. */
42
+ sessionRecord(record: SessionRecord): void;
43
+ /** One appended session event; batched per turn or debounce window. */
44
+ event(event: SessionEvent): void;
45
+ /** Marks an event delivered, for backends that track dispatch. */
46
+ eventDispatched(event: SessionEvent): void;
47
+ /** Flush everything buffered for one session (ordered: events, record). */
48
+ flushSession(sessionId: string, reason?: StorageContext["reason"]): void;
49
+
50
+ /** True when the backend can list every session in one bulk read. */
51
+ readonly canBulkRestore: boolean;
52
+ /** True when the backend is the Cursor-hosted one rather than an authored sink. */
53
+ readonly isCursorHosted: boolean;
54
+
55
+ listSessions(): Promise<SessionRecord[]>;
56
+ listSessionsFromSink(): Promise<SessionRecord[]>;
57
+ listSessionEvents(sessionId: string): Promise<SessionEvent[]>;
58
+ getSessionById(sessionId: string): Promise<SessionRecord | undefined>;
59
+ getSessionByContinuation(
60
+ channelId: string,
61
+ continuationKey: string
62
+ ): Promise<SessionRecord | undefined>;
63
+ }
64
+
65
+ /**
66
+ * Author-facing durable JSON behind `ctx.host.kv`.
67
+ *
68
+ * Deployment-wide on purpose: the point is state an author saves in one
69
+ * session and reads back in a later one, so unlike `ctx.host.files` there is
70
+ * no session scope to default to.
71
+ */
72
+ export interface AuthorKvStore {
73
+ /** False when no backend is configured; callers fall back to local disk. */
74
+ readonly supportsAuthorKv: boolean;
75
+ kvGet(authorKey: string): Promise<JsonValue | undefined>;
76
+ kvPut(authorKey: string, value: JsonValue): Promise<void>;
77
+ kvDelete(authorKey: string): Promise<void>;
78
+ }
79
+
80
+ /** Playground eval batches and live A/B metrics, on their own tables. */
81
+ export interface EvalAbStore {
82
+ evalRuns(): EvalRunStorage | undefined;
83
+ abSample(sample: ABMetricSample): void;
84
+ abSnapshot(snapshot: ABSnapshot): void;
85
+ getLatestAbSnapshot(): Promise<ABSnapshot | undefined>;
86
+ }
@@ -19,6 +19,7 @@ import {
19
19
  writeFile,
20
20
  } from "node:fs/promises";
21
21
  import { dirname, join, parse, relative, resolve, sep } from "node:path";
22
+ import { agentStoreKeys, DEPLOYMENT_STORE_MOUNT_PATH } from "../files.js";
22
23
  import { MEMORY_DIR_NAME } from "../memory.js";
23
24
  import type {
24
25
  DiscoveredSkill,
@@ -38,20 +39,41 @@ export interface MaterializeWorkspaceOptions {
38
39
  seed: boolean;
39
40
  /**
40
41
  * Agent state root. `<stateRoot>/memory/` (the durable cross-session
41
- * directory the memory hook journals into) is created and symlinked into
42
- * the workspace as `memory` so the agent reads it with plain file tools.
43
- *
44
- * TODO(agent-store): replace with the mounted AgentStore path so cloud
45
- * VMs see the same directory.
42
+ * directory the memory hook journals into on hosting a local mirror of
43
+ * the Agent Store journal) is created and symlinked into the workspace as
44
+ * `memory` so the agent reads it with plain file tools. Cloud VMs have no
45
+ * materialized workspace; they read the store mount directly (see
46
+ * {@link buildCloudMemorySection}).
46
47
  */
47
48
  stateRoot: string;
48
49
  }
49
50
 
51
+ /**
52
+ * First-turn preamble section for cloud-runtime sessions on hosted
53
+ * deployments: cloud VMs read the memory journal from the store mount.
54
+ */
55
+ export function buildCloudMemorySection(agentName: string): string {
56
+ const dir = `${DEPLOYMENT_STORE_MOUNT_PATH}/${agentStoreKeys.memoryDir(agentName)}`;
57
+ return [
58
+ "## Shared session memory",
59
+ "",
60
+ `The cross-session memory journal (the \`memory/\` directory your instructions may reference) lives at \`${dir}\` on this machine — read \`${dir}/journal.jsonl\` with your normal file tools.`,
61
+ ].join("\n");
62
+ }
63
+
50
64
  /** Relative path of an agent-side tool script inside the session workspace. */
51
65
  export function agentToolScriptPath(toolName: string): string {
52
66
  return `.agent-serve/tools/${toolName}.sh`;
53
67
  }
54
68
 
69
+ /**
70
+ * Reserved MCP connection name under which authored server tools are served
71
+ * to cloud turns (the session-bound tool MCP endpoint). Shared between the
72
+ * turn's MCP config and the first-prompt server-tool catalog so the model is
73
+ * told exactly where those tools live.
74
+ */
75
+ export const SERVER_TOOLS_MCP_SERVER_NAME = "agentsdk-tools";
76
+
55
77
  /**
56
78
  * Always-on identity line prepended when composing instructions. Sourced from
57
79
  * {@link ResolvedAgent.name} (`defineAgent({ name })`, else package/dir name)
@@ -143,16 +165,64 @@ export function buildAgentToolsCatalog(
143
165
  return `${parts.join("\n").trimEnd()}\n`;
144
166
  }
145
167
 
168
+ /**
169
+ * Markdown catalog of authored server tools for the cloud first prompt.
170
+ *
171
+ * Server tools execute on the serve host and reach cloud turns only as MCP
172
+ * tools on {@link SERVER_TOOLS_MCP_SERVER_NAME}. Instructions reference them
173
+ * by bare name, and the agent-tool catalog teaches a "recreate the script if
174
+ * missing" pattern — without this section a cloud model that cannot find a
175
+ * script for a server tool improvises a local substitute instead of calling
176
+ * the real tool, so its host-side effects silently never happen.
177
+ */
178
+ export function buildServerToolsCatalog(
179
+ tools: DiscoveredTool[],
180
+ options: { mcpServerName: string }
181
+ ): string {
182
+ const serverTools = tools.filter((tool) => tool.execution === "server");
183
+ if (serverTools.length === 0) {
184
+ return "";
185
+ }
186
+
187
+ const parts: string[] = [
188
+ "## Server tools",
189
+ "",
190
+ `These tools execute on the agent-serve host, not in this environment. They are available ONLY as MCP tools on the \`${options.mcpServerName}\` MCP server — list that server's tools and call them by name through your MCP tooling.`,
191
+ "There are no local scripts for these tools. NEVER create, edit, or run a local script or other substitute for them: a local re-implementation cannot produce their real effects on the host.",
192
+ "",
193
+ ];
194
+
195
+ for (const tool of serverTools) {
196
+ parts.push(`### \`${tool.name}\``);
197
+ parts.push("");
198
+ parts.push(tool.description.trim());
199
+ parts.push("");
200
+ parts.push("Input schema:");
201
+ parts.push("");
202
+ parts.push("```json");
203
+ parts.push(JSON.stringify(tool.inputSchema, null, 2));
204
+ parts.push("```");
205
+ parts.push("");
206
+ }
207
+
208
+ return `${parts.join("\n").trimEnd()}\n`;
209
+ }
210
+
146
211
  /**
147
212
  * The full AGENTS.md-equivalent context for an agent: instructions, identity
148
213
  * section, and the agent-side tool catalog. Written to the session workspace
149
214
  * as `AGENTS.md`, and inlined into the first turn's prompt when the harness
150
215
  * cannot discover that file (cloud runtime, or a local workspace the harness
151
216
  * will not treat as the project root — see {@link isNestedInGitRepo}).
217
+ *
218
+ * `serverToolsMcpName` is cloud-only: when the turn's MCP config carries the
219
+ * session-bound server-tool endpoint, the catalog names that server so the
220
+ * model calls the real tools instead of inventing local stand-ins. Local
221
+ * runtimes omit it — there the harness exposes server tools natively.
152
222
  */
153
223
  export function buildAgentsMdContent(
154
224
  agent: ResolvedAgent,
155
- options: { includeScripts: boolean }
225
+ options: { includeScripts: boolean; serverToolsMcpName?: string }
156
226
  ): string {
157
227
  return [
158
228
  agent.instructions?.trim() ?? "",
@@ -160,6 +230,11 @@ export function buildAgentsMdContent(
160
230
  buildAgentToolsCatalog(agent.tools, {
161
231
  includeScripts: options.includeScripts,
162
232
  }),
233
+ options.serverToolsMcpName === undefined
234
+ ? ""
235
+ : buildServerToolsCatalog(agent.tools, {
236
+ mcpServerName: options.serverToolsMcpName,
237
+ }),
163
238
  ]
164
239
  .filter((part) => part !== "")
165
240
  .join("\n\n");
package/src/memory.ts CHANGED
@@ -16,14 +16,21 @@
16
16
  * path — typically a server tool under `agent/tools/`, which can resolve
17
17
  * state via `ctx.stateRoot`.
18
18
  *
19
- * TODO(agent-store): once agent-serve can mount the agent's AgentStore
20
- * (durable S3-backed filesystem shared across serve hosts and cloud VMs),
21
- * ship an AgentStore-backed backend and make it the default. The journal
22
- * format is designed to survive that move unchanged.
19
+ * On Cursor-managed hosting the default is {@link agentStoreMemoryBackend}:
20
+ * the journal lives on the deployment's Agent Store (survives deploys,
21
+ * visible on cloud VMs under the store mount) and is mirrored to
22
+ * `<stateRoot>/memory/journal.jsonl` so the workspace symlink read path
23
+ * keeps working for local-runtime turns.
23
24
  */
24
25
 
25
- import { appendFile, mkdir, rename, stat } from "node:fs/promises";
26
+ import { randomUUID } from "node:crypto";
27
+ import { appendFile, mkdir, rename, stat, writeFile } from "node:fs/promises";
26
28
  import { join } from "node:path";
29
+ import { agentStoreKeys, FileConflictError, type FileSink } from "./files.js";
30
+ import {
31
+ cursorHostedFiles,
32
+ isCursorHostedFilesAvailable,
33
+ } from "./files-backends/cursor-hosted.js";
27
34
  import { defineHook } from "./hooks.js";
28
35
  import type { HookContext, HookDefinition, TurnUsage } from "./types.js";
29
36
 
@@ -48,11 +55,11 @@ export type TurnMemoryRecord = {
48
55
  usage?: TurnUsage;
49
56
  };
50
57
 
51
- /** Where turn records go. Receives the agent's state root per append. */
58
+ /** Where turn records go. Receives the agent's state root and name per append. */
52
59
  export interface MemoryBackend {
53
60
  appendTurn(
54
61
  record: TurnMemoryRecord,
55
- ctx: { stateRoot: string }
62
+ ctx: { stateRoot: string; agentName: string }
56
63
  ): Promise<void>;
57
64
  }
58
65
 
@@ -65,7 +72,7 @@ export interface FileMemoryBackendOptions {
65
72
  }
66
73
 
67
74
  /**
68
- * Default backend: append-only JSONL at `<stateRoot>/memory/journal.jsonl`
75
+ * Local default backend: append-only JSONL at `<stateRoot>/memory/journal.jsonl`
69
76
  * — the directory the framework symlinks into session workspaces. Rotated
70
77
  * segments stay alongside as `journal-<epoch-ms>.jsonl`.
71
78
  */
@@ -105,6 +112,112 @@ export function fileMemoryBackend(
105
112
  };
106
113
  }
107
114
 
115
+ export interface AgentStoreMemoryBackendOptions {
116
+ /** Sink override (tests). Defaults to the deployment's Agent Store. */
117
+ sink?: FileSink;
118
+ /** Rotation threshold, same meaning as {@link FileMemoryBackendOptions}. */
119
+ maxJournalBytes?: number;
120
+ }
121
+
122
+ /**
123
+ * Journal on the deployment's Agent Store — durable across deploys, visible
124
+ * on cloud VMs under the store mount. Appends are read-modify-write with
125
+ * etag preconditions (retried on a lost race), then mirrored to
126
+ * `<stateRoot>/memory/journal.jsonl` for the workspace symlink read path.
127
+ */
128
+ export function agentStoreMemoryBackend(
129
+ options: AgentStoreMemoryBackendOptions = {}
130
+ ): MemoryBackend {
131
+ const sink = options.sink ?? cursorHostedFiles();
132
+ const maxJournalBytes = options.maxJournalBytes ?? 5 * 1024 * 1024;
133
+ // Appends are chained per journal key (same shape as fileMemoryBackend);
134
+ // a failed append must not poison the chain for later turns.
135
+ const appendChains = new Map<string, Promise<void>>();
136
+
137
+ const append = async (
138
+ record: TurnMemoryRecord,
139
+ ctx: { stateRoot: string; agentName: string }
140
+ ): Promise<void> => {
141
+ const key = agentStoreKeys.memoryJournal(ctx.agentName);
142
+ const line = Buffer.from(`${JSON.stringify(record)}\n`, "utf8");
143
+ for (let attempt = 0; attempt < 3; attempt += 1) {
144
+ const current = await sink.get(key);
145
+ const currentBody =
146
+ current === undefined
147
+ ? new Uint8Array(0)
148
+ : current instanceof Uint8Array
149
+ ? current
150
+ : current.body;
151
+ const etag =
152
+ current === undefined || current instanceof Uint8Array
153
+ ? undefined
154
+ : current.etag;
155
+ let next: Uint8Array;
156
+ let rotatedStamp: string | undefined;
157
+ if (currentBody.byteLength >= maxJournalBytes) {
158
+ rotatedStamp = `${Date.now()}-${randomUUID().slice(0, 8)}`;
159
+ await sink.put(
160
+ agentStoreKeys.memoryJournalRotated(ctx.agentName, rotatedStamp),
161
+ currentBody,
162
+ { ifMatch: null }
163
+ );
164
+ next = line;
165
+ } else {
166
+ next = Buffer.concat([currentBody, line]);
167
+ }
168
+ try {
169
+ await sink.put(key, next, {
170
+ ifMatch: current === undefined ? null : etag,
171
+ });
172
+ } catch (error) {
173
+ if (error instanceof FileConflictError && attempt < 2) {
174
+ continue;
175
+ }
176
+ throw error;
177
+ }
178
+ await mirrorJournalLocally(ctx.stateRoot, next);
179
+ if (rotatedStamp !== undefined) {
180
+ // Rotated segments sit beside the live journal locally too, so the
181
+ // workspace symlink read path keeps pre-rotation history.
182
+ await writeFile(
183
+ join(ctx.stateRoot, MEMORY_DIR_NAME, `journal-${rotatedStamp}.jsonl`),
184
+ currentBody
185
+ );
186
+ }
187
+ return;
188
+ }
189
+ };
190
+
191
+ return {
192
+ async appendTurn(record, ctx) {
193
+ const key = agentStoreKeys.memoryJournal(ctx.agentName);
194
+ const prior = appendChains.get(key) ?? Promise.resolve();
195
+ const next = prior.catch(() => {}).then(() => append(record, ctx));
196
+ appendChains.set(key, next);
197
+ await next;
198
+ },
199
+ };
200
+ }
201
+
202
+ /** Atomic full rewrite of the local mirror; also hydrates a fresh pod after a deploy. */
203
+ async function mirrorJournalLocally(
204
+ stateRoot: string,
205
+ contents: Uint8Array
206
+ ): Promise<void> {
207
+ const dir = join(stateRoot, MEMORY_DIR_NAME);
208
+ await mkdir(dir, { recursive: true });
209
+ const tmp = join(dir, `.tmp-${randomUUID()}`);
210
+ await writeFile(tmp, contents);
211
+ await rename(tmp, join(dir, "journal.jsonl"));
212
+ }
213
+
214
+ /** Hosted deployments journal to the Agent Store; local dev to plain files. */
215
+ function defaultMemoryBackend(): MemoryBackend {
216
+ return isCursorHostedFilesAvailable()
217
+ ? agentStoreMemoryBackend()
218
+ : fileMemoryBackend();
219
+ }
220
+
108
221
  export interface MemoryHookOptions {
109
222
  /** Where records go. Defaults to {@link fileMemoryBackend}. */
110
223
  backend?: MemoryBackend;
@@ -126,7 +239,7 @@ function truncate(text: string, max: number): string {
126
239
  * ```
127
240
  */
128
241
  export function memoryHook(options: MemoryHookOptions = {}): HookDefinition {
129
- const backend = options.backend ?? fileMemoryBackend();
242
+ const backend = options.backend ?? defaultMemoryBackend();
130
243
  const maxTextLength = options.maxTextLength ?? 2000;
131
244
  // Inbound message per turn (truncated at receipt so large pastes are not
132
245
  // retained), keyed `<sessionId>/<turnId>` — `message.received` and the
@@ -167,7 +280,10 @@ export function memoryHook(options: MemoryHookOptions = {}): HookDefinition {
167
280
  if (usage !== undefined) {
168
281
  entry.usage = usage;
169
282
  }
170
- await backend.appendTurn(entry, { stateRoot: ctx.stateRoot });
283
+ await backend.appendTurn(entry, {
284
+ stateRoot: ctx.stateRoot,
285
+ agentName: ctx.agent.name,
286
+ });
171
287
  };
172
288
 
173
289
  const sweepSession = (ctx: HookContext): void => {
package/src/storage.ts CHANGED
@@ -276,7 +276,6 @@ export const STORAGE_KEY_ROOT = "agentkit/v1" as const;
276
276
  * | `agentkit/v1/{agent}/session-events/{sessionId}/{index}` | `SessionEvent[]` chunk (index = first event's index, zero-padded) |
277
277
  * | `agentkit/v1/{agent}/continuation/{channelId}/{token}` | `{ sessionId }` |
278
278
  * | `agentkit/v1/{agent}/kv/{key}` | Author JSON via {@link HostContext.kv} |
279
- * | `agentkit/v1/{agent}/artifacts/{id}` | `ArtifactRecord` via `ctx.artifacts` |
280
279
  *
281
280
  * Eval-run and A/B history do not flow through this KV scheme — they have
282
281
  * dedicated tables ({@link StorageConfig.evals} / {@link StorageConfig.abs}).
@@ -307,11 +306,6 @@ export const storageKeys = {
307
306
  kv: (agent: string, key: string): string =>
308
307
  `${STORAGE_KEY_ROOT}/${agent}/kv/${keySegment(key)}`,
309
308
  kvPrefix: (agent: string): string => `${STORAGE_KEY_ROOT}/${agent}/kv/`,
310
- /** Tagged artifact rows (see `ctx.artifacts` / `ArtifactsApi`). */
311
- artifact: (agent: string, id: string): string =>
312
- `${STORAGE_KEY_ROOT}/${agent}/artifacts/${keySegment(id)}`,
313
- artifactPrefix: (agent: string): string =>
314
- `${STORAGE_KEY_ROOT}/${agent}/artifacts/`,
315
309
  } as const;
316
310
 
317
311
  /**
package/src/types.ts CHANGED
@@ -1838,6 +1838,8 @@ export interface ArtifactRecord {
1838
1838
  key?: string;
1839
1839
  title?: string;
1840
1840
  data: JsonValue;
1841
+ /** Present when the artifact carries a file/blob payload (served at `GET /v1/artifacts/:id/content`). */
1842
+ content?: { size: number; contentType?: string };
1841
1843
  sessionId?: string;
1842
1844
  turnId?: string;
1843
1845
  source: ArtifactSource;
@@ -1853,6 +1855,13 @@ export interface ArtifactTagInput {
1853
1855
  title?: string;
1854
1856
  /** Upsert key: tagging the same key again replaces the row. */
1855
1857
  key?: string;
1858
+ /**
1859
+ * File/blob payload, served via `GET /v1/artifacts/:id/content`.
1860
+ * Re-tagging a keyed artifact without `contents` keeps the existing payload.
1861
+ */
1862
+ contents?: string | Uint8Array;
1863
+ /** MIME type reported when serving {@link contents}. */
1864
+ contentType?: string;
1856
1865
  /**
1857
1866
  * Session to attribute (and stream an `artifact.tagged` event to). On
1858
1867
  * session-bound facades ({@link ToolContext.artifacts},
@@ -2460,6 +2469,12 @@ export interface ServeOptions {
2460
2469
  * Local turns always use the loopback URL.
2461
2470
  */
2462
2471
  publicUrl?: string;
2472
+ /**
2473
+ * Authenticated HTTP MCP URL that proxies this hosted deployment's
2474
+ * `/v1/mcp/tools` endpoint. Cloud turns use it to call authored server tools
2475
+ * on the AgentSDK host. The resolved Cursor API key authenticates requests.
2476
+ */
2477
+ cloudToolsUrl?: string;
2463
2478
  /**
2464
2479
  * Pull GitHub webhook metadata from `/v0/scm-events` (Cursor account auth).
2465
2480
  * The stream is read as the host's Cursor user, so this requires a signed-in