@cursor/july 0.1.6 → 0.1.8

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 (179) hide show
  1. package/dist/bin/agent-serve.js +73 -19
  2. package/dist/channels/slack/channel-watch.d.ts +6 -1
  3. package/dist/channels/slack/channel-watch.d.ts.map +1 -1
  4. package/dist/channels/slack/channel-watch.js +22 -1
  5. package/dist/channels/slack/cursor-account.d.ts +4 -0
  6. package/dist/channels/slack/cursor-account.d.ts.map +1 -1
  7. package/dist/channels/slack/cursor-account.js +12 -2
  8. package/dist/channels/slack/defaults.d.ts +10 -5
  9. package/dist/channels/slack/defaults.d.ts.map +1 -1
  10. package/dist/channels/slack/defaults.js +39 -17
  11. package/dist/channels/slack/dispatch.d.ts +18 -0
  12. package/dist/channels/slack/dispatch.d.ts.map +1 -1
  13. package/dist/channels/slack/dispatch.js +141 -38
  14. package/dist/channels/slack/inbound.d.ts +13 -3
  15. package/dist/channels/slack/inbound.d.ts.map +1 -1
  16. package/dist/channels/slack/inbound.js +17 -9
  17. package/dist/channels/slack/index.d.ts +4 -0
  18. package/dist/channels/slack/index.d.ts.map +1 -1
  19. package/dist/channels/slack/index.js +4 -0
  20. package/dist/channels/slack/live-delivery.d.ts.map +1 -1
  21. package/dist/channels/slack/live-delivery.js +1 -0
  22. package/dist/channels/slack/manifest.d.ts.map +1 -1
  23. package/dist/channels/slack/manifest.js +3 -0
  24. package/dist/channels/slack/nudge-carrier.d.ts +19 -0
  25. package/dist/channels/slack/nudge-carrier.d.ts.map +1 -0
  26. package/dist/channels/slack/nudge-carrier.js +166 -0
  27. package/dist/channels/slack/nudge-store.d.ts +61 -0
  28. package/dist/channels/slack/nudge-store.d.ts.map +1 -0
  29. package/dist/channels/slack/nudge-store.js +266 -0
  30. package/dist/channels/slack/slack-channel.d.ts.map +1 -1
  31. package/dist/channels/slack/slack-channel.js +74 -25
  32. package/dist/channels/slack/stop.d.ts +14 -0
  33. package/dist/channels/slack/stop.d.ts.map +1 -0
  34. package/dist/channels/slack/stop.js +22 -0
  35. package/dist/channels/slack/thread-context.d.ts +81 -0
  36. package/dist/channels/slack/thread-context.d.ts.map +1 -0
  37. package/dist/channels/slack/thread-context.js +470 -0
  38. package/dist/channels/slack/types.d.ts +27 -0
  39. package/dist/channels/slack/types.d.ts.map +1 -1
  40. package/dist/docs/404.html +2 -2
  41. package/dist/docs/ab.html +4 -4
  42. package/dist/docs/assets/{app.DMboGKUD.js → app.BP8mAVMW.js} +1 -1
  43. package/dist/docs/assets/chunks/@localSearchIndexroot.DevOabKJ.js +1 -0
  44. package/dist/docs/assets/chunks/{VPLocalSearchBox.DkEqEnJU.js → VPLocalSearchBox.1FXIZbz8.js} +1 -1
  45. package/dist/docs/assets/chunks/{theme.ASugdMNm.js → theme.Dm1R02v9.js} +2 -2
  46. package/dist/docs/assets/example-agents_index.md.CwtWu5ix.js +2 -0
  47. package/dist/docs/assets/{example-agents_index.md.CRqJlnIf.lean.js → example-agents_index.md.CwtWu5ix.lean.js} +1 -1
  48. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.js +10 -0
  49. package/dist/docs/assets/example-agents_oncall.md.BG_sUMly.lean.js +1 -0
  50. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.js → guides_slack.md.FWale6Ip.js} +4 -3
  51. package/dist/docs/assets/{guides_slack.md.CCwqHvSV.lean.js → guides_slack.md.FWale6Ip.lean.js} +1 -1
  52. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.js → reference_sessions.md.DLd6mvbv.js} +2 -2
  53. package/dist/docs/assets/{reference_sessions.md.DdjWabnN.lean.js → reference_sessions.md.DLd6mvbv.lean.js} +1 -1
  54. package/dist/docs/assets/{storage.md.CVnInNiN.js → storage.md.Cu3MwM2a.js} +3 -2
  55. package/dist/docs/assets/{storage.md.CVnInNiN.lean.js → storage.md.Cu3MwM2a.lean.js} +1 -1
  56. package/dist/docs/building-with-agents.html +4 -4
  57. package/dist/docs/concepts.html +4 -4
  58. package/dist/docs/deployment.html +4 -4
  59. package/dist/docs/evals.html +4 -4
  60. package/dist/docs/example-agents/approval-buddy.html +4 -4
  61. package/dist/docs/example-agents/benny.html +5 -5
  62. package/dist/docs/example-agents/bugbot.html +5 -5
  63. package/dist/docs/example-agents/codebase-wiki.html +4 -4
  64. package/dist/docs/example-agents/codeowners-review.html +4 -4
  65. package/dist/docs/example-agents/concierge.html +4 -4
  66. package/dist/docs/example-agents/fsd.html +4 -4
  67. package/dist/docs/example-agents/index.html +6 -6
  68. package/dist/docs/example-agents/knowledge-base.html +4 -4
  69. package/dist/docs/example-agents/oncall.html +34 -0
  70. package/dist/docs/example-agents/security-reviewer.html +4 -4
  71. package/dist/docs/example-agents/slack-agent.html +4 -4
  72. package/dist/docs/example-agents/weather-agent.html +4 -4
  73. package/dist/docs/guides/agent-to-agent.html +4 -4
  74. package/dist/docs/guides/cloud-runtime.html +4 -4
  75. package/dist/docs/guides/github.html +4 -4
  76. package/dist/docs/guides/human-in-the-loop.html +4 -4
  77. package/dist/docs/guides/slack.html +7 -6
  78. package/dist/docs/guides/webhooks.html +4 -4
  79. package/dist/docs/hashmap.json +1 -1
  80. package/dist/docs/hillclimbing.html +4 -4
  81. package/dist/docs/index.html +4 -4
  82. package/dist/docs/quickstart.html +4 -4
  83. package/dist/docs/reference/agent-config.html +4 -4
  84. package/dist/docs/reference/channels.html +4 -4
  85. package/dist/docs/reference/cli.html +4 -4
  86. package/dist/docs/reference/connections.html +4 -4
  87. package/dist/docs/reference/hooks.html +4 -4
  88. package/dist/docs/reference/http-api.html +4 -4
  89. package/dist/docs/reference/instructions.html +4 -4
  90. package/dist/docs/reference/playground.html +4 -4
  91. package/dist/docs/reference/project-layout.html +4 -4
  92. package/dist/docs/reference/schedules.html +4 -4
  93. package/dist/docs/reference/sessions.html +5 -5
  94. package/dist/docs/reference/skills.html +4 -4
  95. package/dist/docs/reference/subagents.html +4 -4
  96. package/dist/docs/reference/tools.html +4 -4
  97. package/dist/docs/scaffolding-agents.html +4 -4
  98. package/dist/docs/storage.html +7 -6
  99. package/dist/docs/troubleshooting.html +4 -4
  100. package/dist/evals.d.ts +2 -2
  101. package/dist/internal/cli-ax.d.ts +23 -10
  102. package/dist/internal/cli-ax.d.ts.map +1 -1
  103. package/dist/internal/cli-ax.js +358 -55
  104. package/dist/internal/cli-mcp.d.ts +64 -0
  105. package/dist/internal/cli-mcp.d.ts.map +1 -0
  106. package/dist/internal/cli-mcp.js +570 -0
  107. package/dist/internal/distribution.d.ts.map +1 -1
  108. package/dist/internal/distribution.js +2 -0
  109. package/dist/internal/eval-run-store.d.ts +2 -0
  110. package/dist/internal/eval-run-store.d.ts.map +1 -1
  111. package/dist/internal/eval-run-store.js +4 -4
  112. package/dist/internal/evals-client.d.ts +96 -0
  113. package/dist/internal/evals-client.d.ts.map +1 -0
  114. package/dist/internal/evals-client.js +262 -0
  115. package/dist/internal/host-kv.d.ts +17 -0
  116. package/dist/internal/host-kv.d.ts.map +1 -0
  117. package/dist/internal/host-kv.js +77 -0
  118. package/dist/internal/init-project.js +3 -3
  119. package/dist/internal/server.d.ts.map +1 -1
  120. package/dist/internal/server.js +19 -2
  121. package/dist/internal/session-engine.d.ts +42 -7
  122. package/dist/internal/session-engine.d.ts.map +1 -1
  123. package/dist/internal/session-engine.js +247 -51
  124. package/dist/internal/storage-coordinator.d.ts +18 -1
  125. package/dist/internal/storage-coordinator.d.ts.map +1 -1
  126. package/dist/internal/storage-coordinator.js +43 -0
  127. package/dist/playground/assets/index-Bpr4Th4M.js +319 -0
  128. package/dist/playground/index.html +1 -1
  129. package/dist/storage-backends/file-kv.d.ts +42 -0
  130. package/dist/storage-backends/file-kv.d.ts.map +1 -0
  131. package/dist/storage-backends/file-kv.js +190 -0
  132. package/dist/storage-backends/postgres-kv.d.ts +77 -0
  133. package/dist/storage-backends/postgres-kv.d.ts.map +1 -0
  134. package/dist/storage-backends/postgres-kv.js +180 -0
  135. package/dist/storage.d.ts +10 -1
  136. package/dist/storage.d.ts.map +1 -1
  137. package/dist/storage.js +10 -1
  138. package/dist/types.d.ts +79 -0
  139. package/dist/types.d.ts.map +1 -1
  140. package/docs/example-agents/index.md +14 -9
  141. package/docs/example-agents/oncall.md +216 -0
  142. package/docs/guides/slack.md +11 -2
  143. package/docs/reference/sessions.md +13 -4
  144. package/docs/storage.md +27 -0
  145. package/package.json +15 -1
  146. package/skills/framework-map/SKILL.md +6 -4
  147. package/src/bin/agent-serve.ts +79 -18
  148. package/src/channels/slack/channel-watch.ts +28 -1
  149. package/src/channels/slack/cursor-account.ts +20 -0
  150. package/src/channels/slack/defaults.ts +51 -20
  151. package/src/channels/slack/dispatch.ts +215 -40
  152. package/src/channels/slack/inbound.ts +30 -10
  153. package/src/channels/slack/index.ts +4 -0
  154. package/src/channels/slack/live-delivery.ts +1 -0
  155. package/src/channels/slack/manifest.ts +3 -0
  156. package/src/channels/slack/nudge-carrier.ts +182 -0
  157. package/src/channels/slack/nudge-store.ts +308 -0
  158. package/src/channels/slack/slack-channel.ts +106 -31
  159. package/src/channels/slack/stop.ts +25 -0
  160. package/src/channels/slack/thread-context.ts +572 -0
  161. package/src/channels/slack/types.ts +27 -0
  162. package/src/evals.ts +2 -2
  163. package/src/internal/cli-ax.ts +466 -75
  164. package/src/internal/cli-mcp.ts +708 -0
  165. package/src/internal/distribution.ts +2 -0
  166. package/src/internal/eval-run-store.ts +5 -0
  167. package/src/internal/evals-client.ts +431 -0
  168. package/src/internal/host-kv.ts +85 -0
  169. package/src/internal/init-project.ts +3 -3
  170. package/src/internal/server.ts +43 -0
  171. package/src/internal/session-engine.ts +351 -50
  172. package/src/internal/storage-coordinator.ts +41 -0
  173. package/src/storage-backends/file-kv.ts +221 -0
  174. package/src/storage-backends/postgres-kv.ts +248 -0
  175. package/src/storage.ts +11 -1
  176. package/src/types.ts +83 -0
  177. package/dist/docs/assets/chunks/@localSearchIndexroot.O6eP7QZr.js +0 -1
  178. package/dist/docs/assets/example-agents_index.md.CRqJlnIf.js +0 -2
  179. package/dist/playground/assets/index-mux9aj5P.js +0 -319
@@ -47,6 +47,8 @@ export interface StartEvalRunInput {
47
47
  tags?: string[];
48
48
  bearerToken?: string;
49
49
  verbose?: boolean;
50
+ /** Per-case timeout override (same semantics as CLI `--timeout-ms`). */
51
+ timeoutMs?: number;
50
52
  }
51
53
 
52
54
  export type EvalListConfig = {
@@ -190,6 +192,7 @@ export class EvalRunStore {
190
192
  ? {}
191
193
  : { bearerToken: input.bearerToken }),
192
194
  ...(input.verbose === undefined ? {} : { verbose: input.verbose }),
195
+ ...(input.timeoutMs === undefined ? {} : { timeoutMs: input.timeoutMs }),
193
196
  });
194
197
 
195
198
  return snapshot;
@@ -316,6 +319,7 @@ export class EvalRunStore {
316
319
  config: Partial<EvalConfigFile>;
317
320
  bearerToken?: string;
318
321
  verbose?: boolean;
322
+ timeoutMs?: number;
319
323
  }
320
324
  ): Promise<void> {
321
325
  if (this.runs.get(runId) === undefined) {
@@ -330,6 +334,7 @@ export class EvalRunStore {
330
334
  ? {}
331
335
  : { bearerToken: opts.bearerToken }),
332
336
  ...(opts.verbose === undefined ? {} : { verbose: opts.verbose }),
337
+ ...(opts.timeoutMs === undefined ? {} : { timeoutMs: opts.timeoutMs }),
333
338
  onCaseStart: (id) => {
334
339
  const run = this.runs.get(runId);
335
340
  if (run === undefined) {
@@ -0,0 +1,431 @@
1
+ /**
2
+ * HTTP client for playground eval batches (`/v1/dev/evals/runs`).
3
+ *
4
+ * Used by `agentkit eval --prod` (or `--url`) so the batch runs on the serve
5
+ * process and is mirrored to `defineStorage` when an `evals` table is
6
+ * configured. Without those flags, eval keeps the in-process ephemeral path
7
+ * in `cli-ax.ts`.
8
+ */
9
+
10
+ import type { EvalRunSnapshot } from "../evals.js";
11
+ import { matchesEvalIdFilter } from "../evals.js";
12
+ import type { EvalRunResult } from "./eval-runner.js";
13
+ import { mergeRequestHeaders, type RequestAuth } from "./request-headers.js";
14
+
15
+ const DEFAULT_POLL_MS = 1_000;
16
+
17
+ export type RemoteEvalErrorCode =
18
+ | "no_matching_evals"
19
+ | "eval_run_in_progress"
20
+ | "unknown_eval_run"
21
+ | "start_failed"
22
+ | "poll_failed"
23
+ | "aborted";
24
+
25
+ export class RemoteEvalError extends Error {
26
+ constructor(
27
+ message: string,
28
+ readonly status?: number,
29
+ readonly code?: RemoteEvalErrorCode,
30
+ readonly activeRunId?: string
31
+ ) {
32
+ super(message);
33
+ this.name = "RemoteEvalError";
34
+ }
35
+ }
36
+
37
+ export interface RemoteEvalListItem {
38
+ id: string;
39
+ fileId: string;
40
+ description?: string;
41
+ tags?: string[];
42
+ }
43
+
44
+ export interface RunRemoteEvalsOptions extends RequestAuth {
45
+ baseUrl: string;
46
+ filterIds?: string[];
47
+ tags?: string[];
48
+ /** Per-case timeout override forwarded to the serve process. */
49
+ timeoutMs?: number;
50
+ /** Ask the serve harness to emit verbose case logs (server-side). */
51
+ verbose?: boolean;
52
+ fetchImpl?: typeof fetch;
53
+ /** Poll interval while the batch is `running`. */
54
+ pollMs?: number;
55
+ /** Abort the wait (not the remote batch). */
56
+ signal?: AbortSignal;
57
+ /** Called each time the polled snapshot changes. */
58
+ onSnapshot?: (snap: EvalRunSnapshot) => void;
59
+ }
60
+
61
+ /**
62
+ * Start a batch on the target serve via `POST /v1/dev/evals/runs`, poll until
63
+ * terminal, and return CLI-shaped case results.
64
+ */
65
+ export async function runRemoteEvals(
66
+ options: RunRemoteEvalsOptions
67
+ ): Promise<{ snapshot: EvalRunSnapshot; results: EvalRunResult[] }> {
68
+ const baseUrl = options.baseUrl.replace(/\/$/, "");
69
+ const fetchImpl = options.fetchImpl ?? fetch;
70
+ const auth: RequestAuth = {
71
+ bearerToken: options.bearerToken,
72
+ headers: options.headers,
73
+ };
74
+
75
+ const started = await startRemoteEvalRun({
76
+ baseUrl,
77
+ auth,
78
+ fetchImpl,
79
+ filterIds: options.filterIds,
80
+ tags: options.tags,
81
+ timeoutMs: options.timeoutMs,
82
+ verbose: options.verbose,
83
+ signal: options.signal,
84
+ });
85
+ options.onSnapshot?.(started);
86
+
87
+ const snapshot = await pollRemoteEvalRun({
88
+ baseUrl,
89
+ runId: started.runId,
90
+ auth,
91
+ fetchImpl,
92
+ pollMs: options.pollMs ?? DEFAULT_POLL_MS,
93
+ signal: options.signal,
94
+ onSnapshot: options.onSnapshot,
95
+ });
96
+
97
+ return { snapshot, results: snapshotToEvalRunResults(snapshot) };
98
+ }
99
+
100
+ /** List datapoints from `GET /v1/dev/evals` (same surface a remote run uses). */
101
+ export async function listRemoteEvals(args: {
102
+ baseUrl: string;
103
+ auth?: RequestAuth;
104
+ fetchImpl?: typeof fetch;
105
+ filterIds?: string[];
106
+ tags?: string[];
107
+ signal?: AbortSignal;
108
+ }): Promise<RemoteEvalListItem[]> {
109
+ const baseUrl = args.baseUrl.replace(/\/$/, "");
110
+ const fetchImpl = args.fetchImpl ?? fetch;
111
+ let resp: Response;
112
+ try {
113
+ resp = await fetchImpl(`${baseUrl}/v1/dev/evals`, {
114
+ headers: mergeRequestHeaders(args.auth),
115
+ signal: args.signal,
116
+ });
117
+ } catch (error) {
118
+ throw new RemoteEvalError(
119
+ error instanceof Error ? error.message : "Could not list evals.",
120
+ undefined,
121
+ "start_failed"
122
+ );
123
+ }
124
+ if (!resp.ok) {
125
+ throw new RemoteEvalError(
126
+ `Could not list evals (HTTP ${resp.status}).`,
127
+ resp.status,
128
+ "start_failed"
129
+ );
130
+ }
131
+ const body = (await resp.json()) as {
132
+ evals?: Array<{
133
+ id?: unknown;
134
+ fileId?: unknown;
135
+ description?: unknown;
136
+ tags?: unknown;
137
+ }>;
138
+ };
139
+ const listed: RemoteEvalListItem[] = [];
140
+ for (const row of body.evals ?? []) {
141
+ if (typeof row.id !== "string" || row.id === "") {
142
+ continue;
143
+ }
144
+ if (typeof row.fileId !== "string" || row.fileId === "") {
145
+ continue;
146
+ }
147
+ const tags = Array.isArray(row.tags)
148
+ ? row.tags.filter((t): t is string => typeof t === "string")
149
+ : undefined;
150
+ listed.push({
151
+ id: row.id,
152
+ fileId: row.fileId,
153
+ ...(typeof row.description === "string"
154
+ ? { description: row.description }
155
+ : {}),
156
+ ...(tags === undefined || tags.length === 0 ? {} : { tags }),
157
+ });
158
+ }
159
+
160
+ let selected = listed;
161
+ if (args.filterIds !== undefined && args.filterIds.length > 0) {
162
+ selected = selected.filter((e) =>
163
+ args.filterIds!.some((f) => matchesEvalIdFilter(e.id, f))
164
+ );
165
+ }
166
+ if (args.tags !== undefined && args.tags.length > 0) {
167
+ selected = selected.filter((e) =>
168
+ (e.tags ?? []).some((t) => args.tags!.includes(t))
169
+ );
170
+ }
171
+ return selected;
172
+ }
173
+
174
+ export async function startRemoteEvalRun(args: {
175
+ baseUrl: string;
176
+ auth?: RequestAuth;
177
+ fetchImpl?: typeof fetch;
178
+ filterIds?: string[];
179
+ tags?: string[];
180
+ timeoutMs?: number;
181
+ verbose?: boolean;
182
+ signal?: AbortSignal;
183
+ }): Promise<EvalRunSnapshot> {
184
+ const baseUrl = args.baseUrl.replace(/\/$/, "");
185
+ const fetchImpl = args.fetchImpl ?? fetch;
186
+ const body: {
187
+ filterIds?: string[];
188
+ tags?: string[];
189
+ timeoutMs?: number;
190
+ verbose?: boolean;
191
+ } = {};
192
+ if (args.filterIds !== undefined && args.filterIds.length > 0) {
193
+ body.filterIds = args.filterIds;
194
+ }
195
+ if (args.tags !== undefined && args.tags.length > 0) {
196
+ body.tags = args.tags;
197
+ }
198
+ if (args.timeoutMs !== undefined) {
199
+ body.timeoutMs = args.timeoutMs;
200
+ }
201
+ if (args.verbose === true) {
202
+ body.verbose = true;
203
+ }
204
+
205
+ let resp: Response;
206
+ try {
207
+ resp = await fetchImpl(`${baseUrl}/v1/dev/evals/runs`, {
208
+ method: "POST",
209
+ headers: {
210
+ "content-type": "application/json",
211
+ ...mergeRequestHeaders(args.auth),
212
+ },
213
+ body: JSON.stringify(body),
214
+ signal: args.signal,
215
+ });
216
+ } catch (error) {
217
+ throw new RemoteEvalError(
218
+ error instanceof Error ? error.message : "Could not start eval run.",
219
+ undefined,
220
+ "start_failed"
221
+ );
222
+ }
223
+
224
+ const json = (await resp.json().catch(() => ({}))) as {
225
+ ok?: boolean;
226
+ error?: string;
227
+ message?: string;
228
+ activeRunId?: string;
229
+ runId?: string;
230
+ };
231
+
232
+ if (json.error === "no_matching_evals") {
233
+ throw new RemoteEvalError(
234
+ "No matching evals found.",
235
+ 404,
236
+ "no_matching_evals"
237
+ );
238
+ }
239
+ if (resp.status === 409 || json.error === "eval_run_in_progress") {
240
+ throw new RemoteEvalError(
241
+ json.activeRunId !== undefined && json.activeRunId !== ""
242
+ ? `An eval run is already in progress (${json.activeRunId}).`
243
+ : "An eval run is already in progress.",
244
+ 409,
245
+ "eval_run_in_progress",
246
+ typeof json.activeRunId === "string" ? json.activeRunId : undefined
247
+ );
248
+ }
249
+ if (!resp.ok) {
250
+ const detail =
251
+ typeof json.message === "string" && json.message !== ""
252
+ ? json.message
253
+ : typeof json.error === "string" && json.error !== ""
254
+ ? json.error
255
+ : `HTTP ${resp.status}`;
256
+ throw new RemoteEvalError(
257
+ `Could not start eval run: ${detail}`,
258
+ resp.status,
259
+ "start_failed"
260
+ );
261
+ }
262
+ if (typeof json.runId !== "string" || json.runId === "") {
263
+ throw new RemoteEvalError(
264
+ "Eval start response missing runId.",
265
+ undefined,
266
+ "start_failed"
267
+ );
268
+ }
269
+ return json as EvalRunSnapshot;
270
+ }
271
+
272
+ /** One-shot `GET /v1/dev/evals/runs/:runId` (no polling). */
273
+ export async function getRemoteEvalRun(args: {
274
+ baseUrl: string;
275
+ runId: string;
276
+ auth?: RequestAuth;
277
+ fetchImpl?: typeof fetch;
278
+ signal?: AbortSignal;
279
+ }): Promise<EvalRunSnapshot> {
280
+ const baseUrl = args.baseUrl.replace(/\/$/, "");
281
+ const fetchImpl = args.fetchImpl ?? fetch;
282
+ let resp: Response;
283
+ try {
284
+ resp = await fetchImpl(
285
+ `${baseUrl}/v1/dev/evals/runs/${encodeURIComponent(args.runId)}`,
286
+ {
287
+ headers: mergeRequestHeaders(args.auth),
288
+ signal: args.signal,
289
+ }
290
+ );
291
+ } catch (error) {
292
+ throw new RemoteEvalError(
293
+ error instanceof Error ? error.message : "Could not fetch eval run.",
294
+ undefined,
295
+ "poll_failed"
296
+ );
297
+ }
298
+
299
+ if (resp.status === 404) {
300
+ throw new RemoteEvalError(
301
+ `Unknown eval run ${args.runId}.`,
302
+ 404,
303
+ "unknown_eval_run"
304
+ );
305
+ }
306
+ if (!resp.ok) {
307
+ throw new RemoteEvalError(
308
+ `Could not fetch eval run (HTTP ${resp.status}).`,
309
+ resp.status,
310
+ "poll_failed"
311
+ );
312
+ }
313
+ return (await resp.json()) as EvalRunSnapshot;
314
+ }
315
+
316
+ /** Recent batches from `GET /v1/dev/evals/runs`. */
317
+ export async function listRemoteEvalRuns(args: {
318
+ baseUrl: string;
319
+ auth?: RequestAuth;
320
+ fetchImpl?: typeof fetch;
321
+ signal?: AbortSignal;
322
+ }): Promise<{ runs: EvalRunSnapshot[]; activeRunId?: string }> {
323
+ const baseUrl = args.baseUrl.replace(/\/$/, "");
324
+ const fetchImpl = args.fetchImpl ?? fetch;
325
+ let resp: Response;
326
+ try {
327
+ resp = await fetchImpl(`${baseUrl}/v1/dev/evals/runs`, {
328
+ headers: mergeRequestHeaders(args.auth),
329
+ signal: args.signal,
330
+ });
331
+ } catch (error) {
332
+ throw new RemoteEvalError(
333
+ error instanceof Error ? error.message : "Could not list eval runs.",
334
+ undefined,
335
+ "poll_failed"
336
+ );
337
+ }
338
+ if (!resp.ok) {
339
+ throw new RemoteEvalError(
340
+ `Could not list eval runs (HTTP ${resp.status}).`,
341
+ resp.status,
342
+ "poll_failed"
343
+ );
344
+ }
345
+ const body = (await resp.json()) as {
346
+ runs?: unknown;
347
+ activeRunId?: unknown;
348
+ };
349
+ const runs = Array.isArray(body.runs)
350
+ ? (body.runs.filter(
351
+ (r): r is EvalRunSnapshot =>
352
+ r !== null &&
353
+ typeof r === "object" &&
354
+ typeof (r as EvalRunSnapshot).runId === "string"
355
+ ) as EvalRunSnapshot[])
356
+ : [];
357
+ return {
358
+ runs,
359
+ ...(typeof body.activeRunId === "string" && body.activeRunId !== ""
360
+ ? { activeRunId: body.activeRunId }
361
+ : {}),
362
+ };
363
+ }
364
+
365
+ export async function pollRemoteEvalRun(args: {
366
+ baseUrl: string;
367
+ runId: string;
368
+ auth?: RequestAuth;
369
+ fetchImpl?: typeof fetch;
370
+ pollMs?: number;
371
+ signal?: AbortSignal;
372
+ onSnapshot?: (snap: EvalRunSnapshot) => void;
373
+ }): Promise<EvalRunSnapshot> {
374
+ const pollMs = args.pollMs ?? DEFAULT_POLL_MS;
375
+ let lastJson = "";
376
+
377
+ for (;;) {
378
+ if (args.signal?.aborted === true) {
379
+ throw new RemoteEvalError("Eval wait aborted.", undefined, "aborted");
380
+ }
381
+
382
+ const snap = await getRemoteEvalRun(args);
383
+ const serialized = JSON.stringify(snap);
384
+ if (serialized !== lastJson) {
385
+ lastJson = serialized;
386
+ args.onSnapshot?.(snap);
387
+ }
388
+ if (snap.status !== "running") {
389
+ return snap;
390
+ }
391
+ await sleep(pollMs, args.signal);
392
+ }
393
+ }
394
+
395
+ export function snapshotToEvalRunResults(
396
+ snapshot: EvalRunSnapshot
397
+ ): EvalRunResult[] {
398
+ return snapshot.cases.map((c) => ({
399
+ id: c.id,
400
+ path: c.fileId,
401
+ ...(c.description === undefined ? {} : { description: c.description }),
402
+ ok: c.ok === true,
403
+ assertions: c.assertions ?? [],
404
+ ...(c.sessionId === undefined ? {} : { sessionId: c.sessionId }),
405
+ inputs: c.inputs ?? [],
406
+ ...(c.finalText === undefined ? {} : { finalText: c.finalText }),
407
+ ...(c.tools === undefined ? {} : { tools: c.tools }),
408
+ ...(c.toolCalls === undefined ? {} : { toolCalls: c.toolCalls }),
409
+ ...(c.error === undefined ? {} : { error: c.error }),
410
+ logs: c.logs ?? [],
411
+ durationMs: c.durationMs ?? 0,
412
+ }));
413
+ }
414
+
415
+ function sleep(ms: number, signal?: AbortSignal): Promise<void> {
416
+ return new Promise((resolve, reject) => {
417
+ if (signal?.aborted === true) {
418
+ reject(new RemoteEvalError("Eval wait aborted.", undefined, "aborted"));
419
+ return;
420
+ }
421
+ const timer = setTimeout(() => {
422
+ signal?.removeEventListener("abort", onAbort);
423
+ resolve();
424
+ }, ms);
425
+ const onAbort = (): void => {
426
+ clearTimeout(timer);
427
+ reject(new RemoteEvalError("Eval wait aborted.", undefined, "aborted"));
428
+ };
429
+ signal?.addEventListener("abort", onAbort, { once: true });
430
+ });
431
+ }
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Author-facing durable JSON KV for {@link HostContext.kv}.
3
+ *
4
+ * When `defineStorage` is authored and exposes `get`, values live in that
5
+ * sink under {@link storageKeys.kv}. Otherwise they land as one JSON file
6
+ * per key under `--state-root/kv` (local dogfood / no durable plug-in).
7
+ */
8
+
9
+ import { randomUUID } from "node:crypto";
10
+ import { mkdir, readFile, rename, rm, writeFile } from "node:fs/promises";
11
+ import { join } from "node:path";
12
+ import { storageKeys } from "../storage.js";
13
+ import type { HostKvApi, JsonValue } from "../types.js";
14
+ import type { StorageCoordinator } from "./storage-coordinator.js";
15
+
16
+ export interface CreateHostKvApiOptions {
17
+ agentName: string;
18
+ stateRoot: string;
19
+ storage?: StorageCoordinator;
20
+ }
21
+
22
+ /** Build the host KV API for one agent process. */
23
+ export function createHostKvApi(options: CreateHostKvApiOptions): HostKvApi {
24
+ const storage = options.storage;
25
+ if (storage?.supportsAuthorKv === true) {
26
+ return {
27
+ get: (key) => storage.kvGet(normalizeAuthorKey(key)),
28
+ put: (key, value) => storage.kvPut(normalizeAuthorKey(key), value),
29
+ delete: (key) => storage.kvDelete(normalizeAuthorKey(key)),
30
+ };
31
+ }
32
+ return createFilesystemKvApi({
33
+ agentName: options.agentName,
34
+ stateRoot: options.stateRoot,
35
+ });
36
+ }
37
+
38
+ function createFilesystemKvApi(options: {
39
+ agentName: string;
40
+ stateRoot: string;
41
+ }): HostKvApi {
42
+ const dir = join(options.stateRoot, "kv");
43
+ const filePath = (authorKey: string): string =>
44
+ join(
45
+ dir,
46
+ `${encodeURIComponent(storageKeys.kv(options.agentName, authorKey))}.json`
47
+ );
48
+
49
+ return {
50
+ async get(key): Promise<JsonValue | undefined> {
51
+ const authorKey = normalizeAuthorKey(key);
52
+ try {
53
+ return JSON.parse(
54
+ await readFile(filePath(authorKey), "utf8")
55
+ ) as JsonValue;
56
+ } catch (error) {
57
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") {
58
+ return undefined;
59
+ }
60
+ throw error;
61
+ }
62
+ },
63
+
64
+ async put(key, value): Promise<void> {
65
+ const authorKey = normalizeAuthorKey(key);
66
+ await mkdir(dir, { recursive: true });
67
+ const target = filePath(authorKey);
68
+ const tmp = join(dir, `.tmp-${randomUUID()}`);
69
+ await writeFile(tmp, JSON.stringify(value), "utf8");
70
+ await rename(tmp, target);
71
+ },
72
+
73
+ async delete(key): Promise<void> {
74
+ const authorKey = normalizeAuthorKey(key);
75
+ await rm(filePath(authorKey), { force: true });
76
+ },
77
+ };
78
+ }
79
+
80
+ function normalizeAuthorKey(key: string): string {
81
+ if (typeof key !== "string" || key.trim() === "") {
82
+ throw new Error("host.kv key must be a non-empty string");
83
+ }
84
+ return key;
85
+ }
@@ -433,15 +433,15 @@ Recommended loop once the project exists:
433
433
  1. Edit agent/ + tools under agent/tools/
434
434
  2. ${CLI_COMMAND_NAME} info --dir . --json
435
435
  3. ${CLI_COMMAND_NAME} run --dir . --message "..."
436
- 4. ${CLI_COMMAND_NAME} eval --local --dir .
436
+ 4. ${CLI_COMMAND_NAME} eval --dir .
437
437
 
438
438
  Evals:
439
439
  Create evals/**/*.eval.ts using defineEval from ${PACKAGE_NAME}/evals
440
440
  Case id is the path: evals/weather/nyc.eval.ts → weather/nyc
441
441
  Required: evals/evals.config.ts with defineEvalConfig({ maxConcurrency })
442
442
  (hard-capped at 200 due to model provider request limits)
443
- Run: ${CLI_COMMAND_NAME} eval --local [--json] [evalId...]
444
- (omit --local to run against the hosted deployment)
443
+ Run: ${CLI_COMMAND_NAME} eval [--json] [evalId...]
444
+ (pass --prod to run against the hosted deployment)
445
445
 
446
446
  See AGENTS.md in the ${PACKAGE_NAME} package for the full loop.
447
447
  `);
@@ -423,6 +423,17 @@ export async function startServer(
423
423
  },
424
424
  resolveApproval: (sessionId, callId, decision, auth) =>
425
425
  engine.resolveApproval(sessionId, callId, decision, auth),
426
+ hasContinuationSession: (continuationToken) =>
427
+ engine.hasContinuationSession(channel.id, continuationToken),
428
+ isContinuationBusy: (continuationToken) =>
429
+ engine.isContinuationBusy(channel.id, continuationToken),
430
+ getContinuationLastBotMessageTs: (continuationToken) =>
431
+ engine.getContinuationLastBotMessageTs(
432
+ channel.id,
433
+ continuationToken
434
+ ),
435
+ interruptContinuation: (continuationToken) =>
436
+ engine.interruptContinuation(channel.id, continuationToken),
426
437
  logger,
427
438
  };
428
439
  try {
@@ -1082,6 +1093,8 @@ function mountAgentRoutes(
1082
1093
  const body = await readJsonObject(request);
1083
1094
  const filterIds = asStringArray(body?.filterIds);
1084
1095
  const tags = asStringArray(body?.tags);
1096
+ const timeoutMs = asPositiveInt(body?.timeoutMs);
1097
+ const verbose = body?.verbose === true;
1085
1098
  const authHeader = request.headers.get("authorization");
1086
1099
  const bearerToken = authHeader?.toLowerCase().startsWith("bearer ")
1087
1100
  ? authHeader.slice("bearer ".length).trim()
@@ -1093,6 +1106,8 @@ function mountAgentRoutes(
1093
1106
  ...(bearerToken === undefined || bearerToken === ""
1094
1107
  ? {}
1095
1108
  : { bearerToken }),
1109
+ ...(timeoutMs === undefined ? {} : { timeoutMs }),
1110
+ ...(verbose ? { verbose: true } : {}),
1096
1111
  });
1097
1112
  return jsonResponse(snap, { status: 202 });
1098
1113
  } catch (error) {
@@ -1620,6 +1635,26 @@ async function startCursorSlackRelay(input: {
1620
1635
  envelope,
1621
1636
  send: args.send,
1622
1637
  host: args.host,
1638
+ hasContinuationSession: (continuationToken) =>
1639
+ target.runtime.engine.hasContinuationSession(
1640
+ target.channelId,
1641
+ continuationToken
1642
+ ),
1643
+ isContinuationBusy: (continuationToken) =>
1644
+ target.runtime.engine.isContinuationBusy(
1645
+ target.channelId,
1646
+ continuationToken
1647
+ ),
1648
+ getContinuationLastBotMessageTs: (continuationToken) =>
1649
+ target.runtime.engine.getContinuationLastBotMessageTs(
1650
+ target.channelId,
1651
+ continuationToken
1652
+ ),
1653
+ interruptContinuation: (continuationToken) =>
1654
+ target.runtime.engine.interruptContinuation(
1655
+ target.channelId,
1656
+ continuationToken
1657
+ ),
1623
1658
  });
1624
1659
  },
1625
1660
  });
@@ -1896,6 +1931,14 @@ function asStringArray(value: unknown): string[] | undefined {
1896
1931
  return out.length === 0 ? undefined : out;
1897
1932
  }
1898
1933
 
1934
+ function asPositiveInt(value: unknown): number | undefined {
1935
+ if (typeof value !== "number" || !Number.isFinite(value)) {
1936
+ return undefined;
1937
+ }
1938
+ const n = Math.floor(value);
1939
+ return n > 0 ? n : undefined;
1940
+ }
1941
+
1899
1942
  async function assertReminderReadable(input: {
1900
1943
  engine: SessionEngine;
1901
1944
  reminder: ReminderInfo;