@cursor/july 0.1.24 → 0.1.26

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 (205) hide show
  1. package/dist/bin/agent-serve.js +0 -0
  2. package/dist/channels/github/instrument.d.ts +20 -0
  3. package/dist/channels/github/instrument.d.ts.map +1 -0
  4. package/dist/docs/404.html +1 -1
  5. package/dist/docs/ab.html +2 -2
  6. package/dist/docs/assets/{app.CvSGaAxk.js → app.DYcC9FY-.js} +1 -1
  7. package/dist/docs/assets/chunks/@localSearchIndexroot.BQTzJjR_.js +1 -0
  8. package/dist/docs/assets/chunks/{VPLocalSearchBox.J1jJbCvs.js → VPLocalSearchBox.o4N_knTV.js} +1 -1
  9. package/dist/docs/assets/chunks/{theme.C7vfzr7h.js → theme.DQ-njyo0.js} +2 -2
  10. package/dist/docs/building-with-agents.html +2 -2
  11. package/dist/docs/concepts.html +2 -2
  12. package/dist/docs/deployment.html +2 -2
  13. package/dist/docs/evals.html +2 -2
  14. package/dist/docs/example-agents/approval-buddy.html +2 -2
  15. package/dist/docs/example-agents/benny.html +2 -2
  16. package/dist/docs/example-agents/bugbot.html +2 -2
  17. package/dist/docs/example-agents/codebase-wiki.html +2 -2
  18. package/dist/docs/example-agents/codeowners-review.html +2 -2
  19. package/dist/docs/example-agents/concierge.html +2 -2
  20. package/dist/docs/example-agents/fsd.html +2 -2
  21. package/dist/docs/example-agents/index.html +2 -2
  22. package/dist/docs/example-agents/knowledge-base.html +2 -2
  23. package/dist/docs/example-agents/oncall.html +2 -2
  24. package/dist/docs/example-agents/security-reviewer.html +2 -2
  25. package/dist/docs/example-agents/slack-agent.html +2 -2
  26. package/dist/docs/example-agents/weather-agent.html +2 -2
  27. package/dist/docs/guides/agent-to-agent.html +2 -2
  28. package/dist/docs/guides/cloud-runtime.html +2 -2
  29. package/dist/docs/guides/github.html +2 -2
  30. package/dist/docs/guides/human-in-the-loop.html +2 -2
  31. package/dist/docs/guides/mcp-oauth.html +2 -2
  32. package/dist/docs/guides/slack.html +2 -2
  33. package/dist/docs/guides/webhooks.html +2 -2
  34. package/dist/docs/hillclimbing.html +2 -2
  35. package/dist/docs/index.html +2 -2
  36. package/dist/docs/quickstart.html +2 -2
  37. package/dist/docs/reference/agent-config.html +2 -2
  38. package/dist/docs/reference/channels.html +2 -2
  39. package/dist/docs/reference/cli.html +2 -2
  40. package/dist/docs/reference/connections.html +2 -2
  41. package/dist/docs/reference/hooks.html +2 -2
  42. package/dist/docs/reference/http-api.html +2 -2
  43. package/dist/docs/reference/instructions.html +2 -2
  44. package/dist/docs/reference/playground.html +2 -2
  45. package/dist/docs/reference/project-layout.html +2 -2
  46. package/dist/docs/reference/prompt.html +2 -2
  47. package/dist/docs/reference/schedules.html +2 -2
  48. package/dist/docs/reference/sessions.html +2 -2
  49. package/dist/docs/reference/skills.html +2 -2
  50. package/dist/docs/reference/subagents.html +2 -2
  51. package/dist/docs/reference/tools.html +2 -2
  52. package/dist/docs/scaffolding-agents.html +2 -2
  53. package/dist/docs/storage.html +2 -2
  54. package/dist/docs/troubleshooting.html +2 -2
  55. package/dist/internal/json-dir-store.d.ts +32 -0
  56. package/dist/internal/json-dir-store.d.ts.map +1 -0
  57. package/dist/internal/json-dir-store.js +100 -0
  58. package/dist/playground/assets/index-CjOQ4hN9.css +1 -0
  59. package/dist/playground/assets/{index-DTG9OsPV.js → index-DqXdAFGa.js} +41 -41
  60. package/dist/playground/index.html +2 -2
  61. package/package.json +31 -24
  62. package/src/artifacts.ts +78 -0
  63. package/src/bin/agent-serve.version.test.ts +64 -0
  64. package/src/channels/github/api.test.ts +64 -0
  65. package/src/channels/github/auth.test.ts +105 -0
  66. package/src/channels/github/cursor-account.test.ts +204 -0
  67. package/src/channels/github/forward.test.ts +457 -0
  68. package/src/channels/github/github-channel.ts +19 -2
  69. package/src/channels/github/github.test.ts +985 -0
  70. package/src/channels/github/replay.test.ts +179 -0
  71. package/src/channels/github/types.ts +6 -0
  72. package/src/channels/slack/api.post-message.test.ts +148 -0
  73. package/src/channels/slack/api.ts +3 -1
  74. package/src/channels/slack/approvals.test.ts +328 -0
  75. package/src/channels/slack/block-actions.test.ts +469 -0
  76. package/src/channels/slack/bot-mentions.test.ts +267 -0
  77. package/src/channels/slack/channel-watch.test.ts +363 -0
  78. package/src/channels/slack/cursor-account.test.ts +301 -0
  79. package/src/channels/slack/cursor-account.ts +3 -1
  80. package/src/channels/slack/defaults.final-post.test.ts +182 -0
  81. package/src/channels/slack/dispatch.test.ts +875 -0
  82. package/src/channels/slack/dispatch.ts +12 -1
  83. package/src/channels/slack/eval-directive.test.ts +273 -0
  84. package/src/channels/slack/interactive.ts +3 -0
  85. package/src/channels/slack/message-body.test.ts +54 -0
  86. package/src/channels/slack/nudge-store.test.ts +143 -0
  87. package/src/channels/slack/slack-channel.ts +6 -0
  88. package/src/channels/slack/slack.test.ts +391 -0
  89. package/src/channels/slack/stop.test.ts +23 -0
  90. package/src/channels/slack/thread-context.test.ts +202 -0
  91. package/src/channels/slack/types.ts +6 -0
  92. package/src/evals/assertions.test.ts +617 -0
  93. package/src/evals/assertions.ts +37 -1
  94. package/src/evals/expect.test.ts +144 -0
  95. package/src/evals/judge.test.ts +181 -0
  96. package/src/evals/loaders.test.ts +132 -0
  97. package/src/evals/matchers.test.ts +95 -0
  98. package/src/evals/reporters.test.ts +303 -0
  99. package/src/evals/run-facts.test.ts +259 -0
  100. package/src/evals.ts +1 -0
  101. package/src/index.ts +1 -0
  102. package/src/internal/ab-snapshot.test.ts +325 -0
  103. package/src/internal/approval-gate.test.ts +49 -0
  104. package/src/internal/approvals.integration.test.ts +383 -0
  105. package/src/internal/artifacts-store.test.ts +283 -0
  106. package/src/internal/artifacts-store.ts +290 -0
  107. package/src/internal/authored-loaders.test.ts +31 -0
  108. package/src/internal/builtin-tools/artifacts.test.ts +247 -0
  109. package/src/internal/builtin-tools/artifacts.ts +74 -0
  110. package/src/internal/builtin-tools/index.ts +21 -0
  111. package/src/internal/builtin-tools/reminders.test.ts +201 -0
  112. package/src/internal/channel-route-schema.test.ts +294 -0
  113. package/src/internal/chat-attach.test.ts +262 -0
  114. package/src/internal/cli-deploy.test.ts +1991 -0
  115. package/src/internal/cli-docs.test.ts +161 -0
  116. package/src/internal/cli-mcp.test.ts +789 -0
  117. package/src/internal/cli-skills.test.ts +133 -0
  118. package/src/internal/cli-slack.test.ts +1654 -0
  119. package/src/internal/cli-slack.ts +13 -4
  120. package/src/internal/cloud-merge.test.ts +74 -0
  121. package/src/internal/cron.test.ts +22 -0
  122. package/src/internal/cursor/account-mcp.test.ts +807 -0
  123. package/src/internal/cursor/backend-client.test.ts +591 -0
  124. package/src/internal/cursor/credentials.test.ts +351 -0
  125. package/src/internal/cursor/github-credentials.test.ts +136 -0
  126. package/src/internal/cursor-account-mcp-auth.test.ts +310 -0
  127. package/src/internal/cursor-account.integration.test.ts +441 -0
  128. package/src/internal/cursor-event-relay.test.ts +746 -0
  129. package/src/internal/cursor-github-credentials.integration.test.ts +271 -0
  130. package/src/internal/cursor-slack-relay.test.ts +525 -0
  131. package/src/internal/deploy-source.test.ts +111 -0
  132. package/src/internal/discovery.artifact-tool.test.ts +136 -0
  133. package/src/internal/discovery.artifacts.test.ts +119 -0
  134. package/src/internal/discovery.builtin-tools.test.ts +94 -0
  135. package/src/internal/discovery.concurrency.test.ts +60 -0
  136. package/src/internal/discovery.cursor-account.test.ts +133 -0
  137. package/src/internal/discovery.cwd.test.ts +83 -0
  138. package/src/internal/discovery.hosting.test.ts +80 -0
  139. package/src/internal/discovery.identity.test.ts +44 -0
  140. package/src/internal/discovery.ts +102 -1
  141. package/src/internal/distribution.ts +1 -0
  142. package/src/internal/docs-site.test.ts +66 -0
  143. package/src/internal/duration.test.ts +29 -0
  144. package/src/internal/eval-judge-model.test.ts +187 -0
  145. package/src/internal/eval-run-store.cancel.test.ts +142 -0
  146. package/src/internal/eval-run-store.storage.test.ts +211 -0
  147. package/src/internal/eval-runner.http.test.ts +403 -0
  148. package/src/internal/eval-runner.run.test.ts +928 -0
  149. package/src/internal/evals-client.test.ts +307 -0
  150. package/src/internal/event-mapper.test.ts +243 -0
  151. package/src/internal/github-fanout.test.ts +213 -0
  152. package/src/internal/handleAgentServeTrigger.test.ts +185 -0
  153. package/src/internal/host-kv.test.ts +82 -0
  154. package/src/internal/host-platforms.test.ts +126 -0
  155. package/src/internal/http-channel.test.ts +402 -0
  156. package/src/internal/init-project.test.ts +270 -0
  157. package/src/internal/install-cursor-skills.test.ts +262 -0
  158. package/src/internal/local-env.test.ts +120 -0
  159. package/src/internal/log-ring.test.ts +31 -0
  160. package/src/internal/logs-client.test.ts +350 -0
  161. package/src/internal/mcp-endpoint.test.ts +436 -0
  162. package/src/internal/mcp-host.test.ts +298 -0
  163. package/src/internal/mcp-oauth.test.ts +148 -0
  164. package/src/internal/net.test.ts +17 -0
  165. package/src/internal/peer-connections.test.ts +128 -0
  166. package/src/internal/peer-mcp.integration.test.ts +289 -0
  167. package/src/internal/playground/toolchain.test.ts +53 -0
  168. package/src/internal/playground-cli.test.ts +187 -0
  169. package/src/internal/playground-proxy.test.ts +376 -0
  170. package/src/internal/prompt-context.integration.test.ts +232 -0
  171. package/src/internal/prompt-context.test.ts +127 -0
  172. package/src/internal/reminder-runner.test.ts +432 -0
  173. package/src/internal/reminder-runner.ts +1 -0
  174. package/src/internal/reminder-store.test.ts +53 -0
  175. package/src/internal/request-headers.test.ts +27 -0
  176. package/src/internal/resolve-prod-target.test.ts +787 -0
  177. package/src/internal/resolved-connections.test.ts +295 -0
  178. package/src/internal/router.test.ts +57 -0
  179. package/src/internal/schedule-runner.ts +1 -0
  180. package/src/internal/sdk-runner.test.ts +290 -0
  181. package/src/internal/server.artifacts.test.ts +334 -0
  182. package/src/internal/server.ts +45 -0
  183. package/src/internal/session-engine.artifacts.test.ts +293 -0
  184. package/src/internal/session-engine.coalesce.test.ts +169 -0
  185. package/src/internal/session-engine.concurrency.test.ts +250 -0
  186. package/src/internal/session-engine.host-oauth-mcp.test.ts +110 -0
  187. package/src/internal/session-engine.interrupt.test.ts +577 -0
  188. package/src/internal/session-engine.storage.test.ts +547 -0
  189. package/src/internal/session-engine.ts +129 -1
  190. package/src/internal/session-urls.test.ts +28 -0
  191. package/src/internal/sessions-client.test.ts +518 -0
  192. package/src/internal/storage-coordinator.test.ts +517 -0
  193. package/src/internal/storage-coordinator.ts +62 -0
  194. package/src/internal/tool-call.test.ts +458 -0
  195. package/src/internal/tool-result.test.ts +52 -0
  196. package/src/internal/trajectory.approvals.test.ts +83 -0
  197. package/src/internal/trajectory.subagents.test.ts +198 -0
  198. package/src/internal/turn-governor.test.ts +137 -0
  199. package/src/internal/update-check.test.ts +485 -0
  200. package/src/internal/workspace.test.ts +207 -0
  201. package/src/storage-backends/cursor-hosted.test.ts +121 -0
  202. package/src/storage.ts +6 -0
  203. package/src/types.ts +152 -2
  204. package/dist/docs/assets/chunks/@localSearchIndexroot.CL2Y0Zmh.js +0 -1
  205. package/dist/playground/assets/index-CidizGZv.css +0 -1
@@ -0,0 +1,121 @@
1
+ import { afterEach, describe, expect, it, vi } from "vitest";
2
+
3
+ import { defineStorage, type StorageContext } from "../storage.js";
4
+ import {
5
+ AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR,
6
+ AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR,
7
+ CURSOR_HOSTED_STORAGE,
8
+ CURSOR_HOSTED_STORAGE_ENV_VARS,
9
+ cursorHostedStorage,
10
+ isCursorHostedStorage,
11
+ } from "./cursor-hosted.js";
12
+
13
+ const storageCtx: StorageContext = {
14
+ agentName: "test-agent",
15
+ projectRoot: "/tmp/test-agent",
16
+ reason: "policy",
17
+ };
18
+
19
+ describe("cursorHostedStorage", () => {
20
+ afterEach(() => {
21
+ vi.unstubAllEnvs();
22
+ });
23
+
24
+ it("exports the platform-managed env names", () => {
25
+ expect(AGENT_SERVE_HOSTED_STORAGE_DATABASE_URL_ENV_VAR).toBe(
26
+ "BUGBOTDB_URL"
27
+ );
28
+ expect(AGENT_SERVE_DEPLOYMENT_ID_ENV_VAR).toBe("AGENT_SERVE_DEPLOYMENT_ID");
29
+ expect(CURSOR_HOSTED_STORAGE_ENV_VARS).toEqual([
30
+ "BUGBOTDB_URL",
31
+ "AGENT_SERVE_DEPLOYMENT_ID",
32
+ ]);
33
+ });
34
+
35
+ it("requires CURSOR_API_KEY", async () => {
36
+ vi.stubEnv("CURSOR_API_KEY", "");
37
+ const methods = cursorHostedStorage({
38
+ baseUrl: "https://api.cursor.com",
39
+ });
40
+ await expect(methods.put("k", 1, storageCtx)).rejects.toThrow(
41
+ /CURSOR_API_KEY/
42
+ );
43
+ });
44
+
45
+ it("calls the control-plane storage proxy with the pod credential", async () => {
46
+ const fetchMock = vi.fn(
47
+ async (input: RequestInfo | URL, init?: RequestInit) => {
48
+ expect(String(input)).toBe(
49
+ "https://api.cursor.com/internal/agent-serve/storage/kv/put"
50
+ );
51
+ expect(init?.method).toBe("POST");
52
+ expect(init?.headers).toMatchObject({
53
+ Authorization: "Bearer key_pod",
54
+ "Content-Type": "application/json",
55
+ });
56
+ expect(JSON.parse(String(init?.body))).toEqual({
57
+ key: "session/1",
58
+ value: { ok: true },
59
+ });
60
+ return new Response(JSON.stringify({ ok: true }), { status: 200 });
61
+ }
62
+ );
63
+
64
+ const methods = cursorHostedStorage({
65
+ baseUrl: "https://api.cursor.com",
66
+ apiKey: "key_pod",
67
+ fetch: fetchMock as typeof fetch,
68
+ });
69
+
70
+ await methods.put("session/1", { ok: true }, storageCtx);
71
+ expect(fetchMock).toHaveBeenCalledOnce();
72
+ });
73
+
74
+ it("maps get misses to undefined", async () => {
75
+ const methods = cursorHostedStorage({
76
+ baseUrl: "https://api.cursor.com",
77
+ apiKey: "key_pod",
78
+ fetch: (async () =>
79
+ new Response(JSON.stringify({ found: false, value: null }), {
80
+ status: 200,
81
+ })) as typeof fetch,
82
+ });
83
+ await expect(methods.get("missing", storageCtx)).resolves.toBeUndefined();
84
+ });
85
+
86
+ it("exposes evals and abs surfaces", () => {
87
+ const methods = cursorHostedStorage({
88
+ baseUrl: "https://api.cursor.com",
89
+ apiKey: "key_pod",
90
+ fetch: (async () =>
91
+ new Response(JSON.stringify({ ok: true }), {
92
+ status: 200,
93
+ })) as typeof fetch,
94
+ });
95
+ expect(typeof methods.evals.put).toBe("function");
96
+ expect(typeof methods.abs.putSample).toBe("function");
97
+ expect(typeof methods.abs.getSnapshot).toBe("function");
98
+ });
99
+
100
+ it("marks the sink so defineStorage preserves cursor-hosted detection", () => {
101
+ const methods = cursorHostedStorage({
102
+ baseUrl: "https://api.cursor.com",
103
+ apiKey: "key_pod",
104
+ fetch: (async () =>
105
+ new Response(JSON.stringify({ ok: true }), {
106
+ status: 200,
107
+ })) as typeof fetch,
108
+ });
109
+ expect(methods[CURSOR_HOSTED_STORAGE]).toBe(true);
110
+ expect(isCursorHostedStorage(methods)).toBe(true);
111
+ expect(
112
+ isCursorHostedStorage(
113
+ defineStorage({
114
+ name: "cursor-hosted",
115
+ ...methods,
116
+ })
117
+ )
118
+ ).toBe(true);
119
+ expect(isCursorHostedStorage(defineStorage({ put: () => {} }))).toBe(false);
120
+ });
121
+ });
package/src/storage.ts CHANGED
@@ -277,6 +277,7 @@ export const STORAGE_KEY_ROOT = "agentkit/v1" as const;
277
277
  * | `agentkit/v1/{agent}/continuation/{channelId}/{token}` | `{ sessionId }` |
278
278
  * | `agentkit/v1/{agent}/reminder/{reminderId}` | `ReminderRecord` |
279
279
  * | `agentkit/v1/{agent}/kv/{key}` | Author JSON via {@link HostContext.kv} |
280
+ * | `agentkit/v1/{agent}/artifacts/{id}` | `ArtifactRecord` via `ctx.artifacts` |
280
281
  *
281
282
  * Eval-run and A/B history do not flow through this KV scheme — they have
282
283
  * dedicated tables ({@link StorageConfig.evals} / {@link StorageConfig.abs}).
@@ -311,6 +312,11 @@ export const storageKeys = {
311
312
  kv: (agent: string, key: string): string =>
312
313
  `${STORAGE_KEY_ROOT}/${agent}/kv/${keySegment(key)}`,
313
314
  kvPrefix: (agent: string): string => `${STORAGE_KEY_ROOT}/${agent}/kv/`,
315
+ /** Tagged artifact rows (see `ctx.artifacts` / `ArtifactsApi`). */
316
+ artifact: (agent: string, id: string): string =>
317
+ `${STORAGE_KEY_ROOT}/${agent}/artifacts/${keySegment(id)}`,
318
+ artifactPrefix: (agent: string): string =>
319
+ `${STORAGE_KEY_ROOT}/${agent}/artifacts/`,
314
320
  } as const;
315
321
 
316
322
  /**
package/src/types.ts CHANGED
@@ -45,7 +45,8 @@ export type DefinitionKind =
45
45
  | "hook"
46
46
  | "eval"
47
47
  | "ab"
48
- | "storage";
48
+ | "storage"
49
+ | "artifacts";
49
50
 
50
51
  export interface BrandedDefinition<K extends DefinitionKind> {
51
52
  readonly __agentServe: K;
@@ -516,6 +517,11 @@ export interface ToolContext {
516
517
  channelId: string,
517
518
  sessionId: string
518
519
  ): Promise<ChannelSession | null>;
520
+ /**
521
+ * Durable artifacts, bound to this tool call's session: `tag` auto-fills
522
+ * `sessionId` (and `turnId` when known).
523
+ */
524
+ artifacts: ArtifactsApi;
519
525
  }
520
526
 
521
527
  /**
@@ -922,6 +928,18 @@ export type SessionEventPayload =
922
928
  data: { callId: string; name?: string; description?: string };
923
929
  }
924
930
  | { type: "subagent.completed"; data: { callId: string; name?: string } }
931
+ | {
932
+ /** An artifact was tagged for this session (see {@link ArtifactsApi.tag}). */
933
+ type: "artifact.tagged";
934
+ data: {
935
+ id: string;
936
+ kind: string;
937
+ key?: string;
938
+ title?: string;
939
+ data: JsonValue;
940
+ source: ArtifactSource;
941
+ };
942
+ }
925
943
  | {
926
944
  type: "turn.completed";
927
945
  data: { result?: string; usage?: TurnUsage };
@@ -1292,6 +1310,11 @@ export interface ChannelHandlerArgs {
1292
1310
  request: Request,
1293
1311
  sessionId: string
1294
1312
  ): { playgroundUrl: string; traceUrl: string };
1313
+ /**
1314
+ * Durable artifacts (unbound — channel handlers have no ambient session;
1315
+ * pass `sessionId` in `tag` input to attribute one).
1316
+ */
1317
+ artifacts: ArtifactsApi;
1295
1318
  }
1296
1319
 
1297
1320
  export type ChannelRouteHandler = (
@@ -1482,6 +1505,12 @@ export interface ChannelStartArgs {
1482
1505
  * should prefer this over `console.*` so CLI/eval hosts can redirect.
1483
1506
  */
1484
1507
  logger: (line: string) => void;
1508
+ /**
1509
+ * Durable artifacts (unbound, same facade as
1510
+ * {@link ChannelHandlerArgs.artifacts}) — for host pipelines the channel
1511
+ * drives outside any route handler.
1512
+ */
1513
+ artifacts: ArtifactsApi;
1485
1514
  }
1486
1515
 
1487
1516
  /**
@@ -1528,7 +1557,7 @@ export interface ChannelConfig<TState = JsonValue> {
1528
1557
  */
1529
1558
  receive?: (
1530
1559
  input: ReceiveInput,
1531
- args: { send: SendMessageFn }
1560
+ args: { send: SendMessageFn; artifacts: ArtifactsApi }
1532
1561
  // biome-ignore lint/suspicious/noConfusingVoidType: hooks may return a session, nothing, or an async nothing
1533
1562
  ) => Promise<ChannelSession | undefined | void> | ChannelSession | void;
1534
1563
  /** Stream-event handlers for sessions owned by this channel. */
@@ -1575,6 +1604,11 @@ export interface HookContext {
1575
1604
  * memory journal) write here.
1576
1605
  */
1577
1606
  stateRoot: string;
1607
+ /**
1608
+ * Durable artifacts, bound to this hook's session: `tag` auto-fills
1609
+ * `sessionId` (and `turnId` when known).
1610
+ */
1611
+ artifacts: ArtifactsApi;
1578
1612
  }
1579
1613
 
1580
1614
  export type HookHandler<TEvent extends SessionEvent = SessionEvent> = (
@@ -1595,6 +1629,107 @@ export interface HookConfig {
1595
1629
 
1596
1630
  export type HookDefinition = HookConfig & BrandedDefinition<"hook">;
1597
1631
 
1632
+ // ============================================================================
1633
+ // Artifacts (agent/artifacts.ts)
1634
+ // ============================================================================
1635
+
1636
+ /** One declared artifact kind (see {@link ArtifactsConfig.kinds}). */
1637
+ export interface ArtifactKindConfig {
1638
+ /**
1639
+ * What this kind holds. Also the model-facing prompt for the
1640
+ * `tag_artifact` built-in tool when {@link ArtifactsConfig.agentTool}
1641
+ * is enabled.
1642
+ */
1643
+ description: string;
1644
+ /**
1645
+ * Optional Zod schema; `tag` validates the payload against it and
1646
+ * persists the parsed output (defaults and coercions applied), so
1647
+ * the schema's output must stay JSON-serializable.
1648
+ */
1649
+ schema?: z.ZodType;
1650
+ }
1651
+
1652
+ /** Input to `defineArtifacts` (authored at `agent/artifacts.ts`). */
1653
+ export interface ArtifactsConfig {
1654
+ /**
1655
+ * Declared artifact kinds. With kinds declared, `tag` only accepts
1656
+ * these; with none, any kind string is accepted freeform.
1657
+ */
1658
+ kinds?: Record<string, ArtifactKindConfig>;
1659
+ /**
1660
+ * Expose the model-facing `tag_artifact` built-in tool generated from
1661
+ * {@link kinds}. Requires at least one declared kind.
1662
+ */
1663
+ agentTool?: boolean;
1664
+ /**
1665
+ * Retention cap (default 1000). On insert past the cap, the
1666
+ * oldest-updated artifact is evicted.
1667
+ */
1668
+ max?: number;
1669
+ }
1670
+
1671
+ export type ArtifactsDefinition = ArtifactsConfig &
1672
+ BrandedDefinition<"artifacts">;
1673
+
1674
+ /** Who tagged an artifact: host code, or the model via `tag_artifact`. */
1675
+ export type ArtifactSource = "host" | "model";
1676
+
1677
+ /** One durable tagged artifact. */
1678
+ export interface ArtifactRecord {
1679
+ /** Stable id: derived from {@link key} when present, else random. */
1680
+ id: string;
1681
+ kind: string;
1682
+ /** Author-chosen upsert key (same key ⇒ same id ⇒ replaces the row). */
1683
+ key?: string;
1684
+ title?: string;
1685
+ data: JsonValue;
1686
+ sessionId?: string;
1687
+ turnId?: string;
1688
+ source: ArtifactSource;
1689
+ createdAt: string;
1690
+ updatedAt: string;
1691
+ }
1692
+
1693
+ /** Input to {@link ArtifactsApi.tag}. */
1694
+ export interface ArtifactTagInput {
1695
+ /** Defaults to `"artifact"` when no kinds are declared. */
1696
+ kind?: string;
1697
+ data: JsonValue;
1698
+ title?: string;
1699
+ /** Upsert key: tagging the same key again replaces the row. */
1700
+ key?: string;
1701
+ /**
1702
+ * Session to attribute (and stream an `artifact.tagged` event to). On
1703
+ * session-bound facades ({@link ToolContext.artifacts},
1704
+ * {@link HookContext.artifacts}) this is auto-filled and a different
1705
+ * value is rejected; unbound facades
1706
+ * ({@link ChannelHandlerArgs.artifacts}) accept it explicitly.
1707
+ */
1708
+ sessionId?: string;
1709
+ turnId?: string;
1710
+ /** Defaults to `"host"`. */
1711
+ source?: ArtifactSource;
1712
+ }
1713
+
1714
+ /** Filter for {@link ArtifactsApi.list}. */
1715
+ export interface ArtifactListFilter {
1716
+ kind?: string;
1717
+ sessionId?: string;
1718
+ }
1719
+
1720
+ /**
1721
+ * The artifacts API, attached as `ctx.artifacts` on tools
1722
+ * ({@link ToolContext}), hooks ({@link HookContext}), and channel handlers
1723
+ * ({@link ChannelHandlerArgs}). Tool and hook facades are session-bound:
1724
+ * `tag` auto-fills the session; the channel facade is unbound.
1725
+ */
1726
+ export interface ArtifactsApi {
1727
+ /** Tag (upsert) one artifact. See {@link ArtifactTagInput}. */
1728
+ tag(input: ArtifactTagInput): Promise<ArtifactRecord>;
1729
+ /** Tagged artifacts, newest-updated first. */
1730
+ list(filter?: ArtifactListFilter): Promise<ArtifactRecord[]>;
1731
+ }
1732
+
1598
1733
  // ============================================================================
1599
1734
  // Schedules (agent/schedules/*)
1600
1735
  // ============================================================================
@@ -1613,6 +1748,11 @@ export interface ScheduleHandlerArgs {
1613
1748
  appAuth: AuthContext;
1614
1749
  /** Shared host services (MCP / GitHub / Slack). Same as tool `ctx.host`. */
1615
1750
  host: HostContext;
1751
+ /**
1752
+ * Durable artifacts (unbound — schedule handlers have no ambient session;
1753
+ * pass `sessionId` explicitly to attribute).
1754
+ */
1755
+ artifacts: ArtifactsApi;
1616
1756
  }
1617
1757
 
1618
1758
  export interface ScheduleConfig {
@@ -1666,6 +1806,12 @@ export interface ReminderFireContext extends ReminderUntilContext {
1666
1806
  /** Wake the reminder's already-bound session (target is implicit). */
1667
1807
  followup(args: { message: string }): Promise<ChannelSession>;
1668
1808
  cancel(reason?: string): Promise<void>;
1809
+ /**
1810
+ * Durable artifacts (unbound — the reminder is bound to a continuation
1811
+ * token, not a resolved session; pass `sessionId` in `tag` input to
1812
+ * attribute one).
1813
+ */
1814
+ artifacts: ArtifactsApi;
1669
1815
  }
1670
1816
 
1671
1817
  /**
@@ -1919,6 +2065,8 @@ export interface AgentProject {
1919
2065
  abConfig?: ABConfigFile;
1920
2066
  /** Optional `agent/storage.ts` (`defineStorage`). */
1921
2067
  storage?: StorageDefinition;
2068
+ /** Optional `agent/artifacts.ts` (`defineArtifacts`). */
2069
+ artifacts?: ArtifactsDefinition;
1922
2070
  diagnostics: Diagnostic[];
1923
2071
  }
1924
2072
 
@@ -1988,6 +2136,8 @@ export interface AgentProjectInfo {
1988
2136
  };
1989
2137
  /** Project storage sink from `agent/storage.ts`, when authored. */
1990
2138
  storage?: { name?: string };
2139
+ /** Declared artifact kinds from `agent/artifacts.ts`, when authored. */
2140
+ artifacts?: { kinds: string[]; agentTool: boolean; max: number };
1991
2141
  diagnostics: Diagnostic[];
1992
2142
  }
1993
2143