@agentstep/agent-sdk 0.1.0

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 (105) hide show
  1. package/package.json +45 -0
  2. package/src/auth/middleware.ts +38 -0
  3. package/src/backends/claude/args.ts +88 -0
  4. package/src/backends/claude/index.ts +193 -0
  5. package/src/backends/claude/permission-hook.ts +152 -0
  6. package/src/backends/claude/tool-bridge.ts +211 -0
  7. package/src/backends/claude/translator.ts +209 -0
  8. package/src/backends/claude/wrapper-script.ts +45 -0
  9. package/src/backends/codex/args.ts +69 -0
  10. package/src/backends/codex/auth.ts +35 -0
  11. package/src/backends/codex/index.ts +57 -0
  12. package/src/backends/codex/setup.ts +37 -0
  13. package/src/backends/codex/translator.ts +223 -0
  14. package/src/backends/codex/wrapper-script.ts +26 -0
  15. package/src/backends/factory/args.ts +45 -0
  16. package/src/backends/factory/auth.ts +30 -0
  17. package/src/backends/factory/index.ts +56 -0
  18. package/src/backends/factory/setup.ts +34 -0
  19. package/src/backends/factory/translator.ts +139 -0
  20. package/src/backends/factory/wrapper-script.ts +33 -0
  21. package/src/backends/gemini/args.ts +44 -0
  22. package/src/backends/gemini/auth.ts +30 -0
  23. package/src/backends/gemini/index.ts +53 -0
  24. package/src/backends/gemini/setup.ts +34 -0
  25. package/src/backends/gemini/translator.ts +139 -0
  26. package/src/backends/gemini/wrapper-script.ts +26 -0
  27. package/src/backends/opencode/args.ts +53 -0
  28. package/src/backends/opencode/auth.ts +53 -0
  29. package/src/backends/opencode/index.ts +70 -0
  30. package/src/backends/opencode/mcp.ts +67 -0
  31. package/src/backends/opencode/setup.ts +54 -0
  32. package/src/backends/opencode/translator.ts +168 -0
  33. package/src/backends/opencode/wrapper-script.ts +46 -0
  34. package/src/backends/registry.ts +38 -0
  35. package/src/backends/shared/ndjson.ts +29 -0
  36. package/src/backends/shared/translator-types.ts +69 -0
  37. package/src/backends/shared/wrap-prompt.ts +17 -0
  38. package/src/backends/types.ts +85 -0
  39. package/src/config/index.ts +95 -0
  40. package/src/db/agents.ts +185 -0
  41. package/src/db/api_keys.ts +78 -0
  42. package/src/db/batch.ts +142 -0
  43. package/src/db/client.ts +81 -0
  44. package/src/db/environments.ts +127 -0
  45. package/src/db/events.ts +208 -0
  46. package/src/db/memory.ts +143 -0
  47. package/src/db/migrations.ts +295 -0
  48. package/src/db/proxy.ts +37 -0
  49. package/src/db/sessions.ts +295 -0
  50. package/src/db/vaults.ts +110 -0
  51. package/src/errors.ts +53 -0
  52. package/src/handlers/agents.ts +194 -0
  53. package/src/handlers/batch.ts +41 -0
  54. package/src/handlers/docs.ts +87 -0
  55. package/src/handlers/environments.ts +154 -0
  56. package/src/handlers/events.ts +234 -0
  57. package/src/handlers/index.ts +12 -0
  58. package/src/handlers/memory.ts +141 -0
  59. package/src/handlers/openapi.ts +14 -0
  60. package/src/handlers/sessions.ts +223 -0
  61. package/src/handlers/stream.ts +76 -0
  62. package/src/handlers/threads.ts +26 -0
  63. package/src/handlers/ui/app.js +984 -0
  64. package/src/handlers/ui/index.html +112 -0
  65. package/src/handlers/ui/style.css +164 -0
  66. package/src/handlers/ui.ts +1281 -0
  67. package/src/handlers/vaults.ts +99 -0
  68. package/src/http.ts +35 -0
  69. package/src/index.ts +104 -0
  70. package/src/init.ts +227 -0
  71. package/src/openapi/registry.ts +8 -0
  72. package/src/openapi/schemas.ts +625 -0
  73. package/src/openapi/spec.ts +691 -0
  74. package/src/providers/apple.ts +220 -0
  75. package/src/providers/daytona.ts +217 -0
  76. package/src/providers/docker.ts +264 -0
  77. package/src/providers/e2b.ts +203 -0
  78. package/src/providers/fly.ts +276 -0
  79. package/src/providers/modal.ts +222 -0
  80. package/src/providers/podman.ts +206 -0
  81. package/src/providers/registry.ts +28 -0
  82. package/src/providers/shared.ts +11 -0
  83. package/src/providers/sprites.ts +55 -0
  84. package/src/providers/types.ts +73 -0
  85. package/src/providers/vercel.ts +208 -0
  86. package/src/proxy/forward.ts +111 -0
  87. package/src/queue/index.ts +111 -0
  88. package/src/sessions/actor.ts +53 -0
  89. package/src/sessions/bus.ts +155 -0
  90. package/src/sessions/driver.ts +818 -0
  91. package/src/sessions/grader.ts +120 -0
  92. package/src/sessions/interrupt.ts +14 -0
  93. package/src/sessions/sweeper.ts +136 -0
  94. package/src/sessions/threads.ts +126 -0
  95. package/src/sessions/tools.ts +50 -0
  96. package/src/shutdown.ts +78 -0
  97. package/src/sprite/client.ts +294 -0
  98. package/src/sprite/exec.ts +161 -0
  99. package/src/sprite/lifecycle.ts +339 -0
  100. package/src/sprite/pool.ts +65 -0
  101. package/src/sprite/setup.ts +159 -0
  102. package/src/state.ts +61 -0
  103. package/src/types.ts +339 -0
  104. package/src/util/clock.ts +7 -0
  105. package/src/util/ids.ts +11 -0
@@ -0,0 +1,691 @@
1
+ /**
2
+ * Build the OpenAPI 3.1 document for the managed-agents Managed Agents API.
3
+ *
4
+ * Import `schemas` first (side-effect: it registers named schemas with the
5
+ * singleton registry). Then register every route operation, then run the
6
+ * generator.
7
+ */
8
+ import { z } from "zod";
9
+ import { OpenApiGeneratorV31 } from "@asteasolutions/zod-to-openapi";
10
+ import { registry } from "./registry";
11
+ import {
12
+ AgentSchema,
13
+ AgentListResponseSchema,
14
+ AgentDeletedResponseSchema,
15
+ CreateAgentRequestSchema,
16
+ UpdateAgentRequestSchema,
17
+ EnvironmentSchema,
18
+ EnvironmentListResponseSchema,
19
+ EnvironmentDeletedResponseSchema,
20
+ CreateEnvironmentRequestSchema,
21
+ SessionSchema,
22
+ SessionStatusSchema,
23
+ SessionListResponseSchema,
24
+ SessionDeletedResponseSchema,
25
+ CreateSessionRequestSchema,
26
+ UpdateSessionRequestSchema,
27
+ EventListResponseSchema,
28
+ UserEventBatchRequestSchema,
29
+ UserEventAppendResponseSchema,
30
+ ErrorEnvelopeSchema,
31
+ VaultSchema,
32
+ VaultEntrySchema,
33
+ VaultListResponseSchema,
34
+ VaultEntryListResponseSchema,
35
+ CreateVaultRequestSchema,
36
+ SetVaultEntryRequestSchema,
37
+ VaultDeletedResponseSchema,
38
+ VaultEntryDeletedResponseSchema,
39
+ BatchRequestSchema,
40
+ BatchResponseSchema,
41
+ } from "./schemas";
42
+
43
+ // Security scheme: the Managed Agents spec uses `x-api-key` header auth
44
+ // (with optional `Authorization: Bearer` fallback — we document the primary).
45
+ registry.registerComponent("securitySchemes", "ApiKey", {
46
+ type: "apiKey",
47
+ in: "header",
48
+ name: "x-api-key",
49
+ });
50
+
51
+ // Shared error responses — every authed route can emit these
52
+ const ErrorResponses = {
53
+ 400: { description: "Bad request", content: { "application/json": { schema: ErrorEnvelopeSchema } } },
54
+ 401: {
55
+ description: "Missing or invalid API key",
56
+ content: { "application/json": { schema: ErrorEnvelopeSchema } },
57
+ },
58
+ 404: {
59
+ description: "Resource not found",
60
+ content: { "application/json": { schema: ErrorEnvelopeSchema } },
61
+ },
62
+ 409: {
63
+ description: "Conflict (e.g. environment has attached sessions)",
64
+ content: { "application/json": { schema: ErrorEnvelopeSchema } },
65
+ },
66
+ 500: {
67
+ description: "Server error",
68
+ content: { "application/json": { schema: ErrorEnvelopeSchema } },
69
+ },
70
+ };
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // /v1/agents
74
+ // ---------------------------------------------------------------------------
75
+
76
+ registry.registerPath({
77
+ method: "post",
78
+ path: "/v1/agents",
79
+ tags: ["Agents"],
80
+ summary: "Create an agent",
81
+ security: [{ ApiKey: [] }],
82
+ request: {
83
+ body: {
84
+ required: true,
85
+ content: { "application/json": { schema: CreateAgentRequestSchema } },
86
+ },
87
+ },
88
+ responses: {
89
+ 201: {
90
+ description: "Agent created",
91
+ content: { "application/json": { schema: AgentSchema } },
92
+ },
93
+ ...ErrorResponses,
94
+ },
95
+ });
96
+
97
+ registry.registerPath({
98
+ method: "get",
99
+ path: "/v1/agents",
100
+ tags: ["Agents"],
101
+ summary: "List agents",
102
+ security: [{ ApiKey: [] }],
103
+ request: {
104
+ query: z.object({
105
+ limit: z.coerce.number().int().min(1).max(100).optional(),
106
+ order: z.enum(["asc", "desc"]).optional(),
107
+ page: z.string().optional().describe("ULID cursor for pagination"),
108
+ include_archived: z.enum(["true", "false"]).optional(),
109
+ }),
110
+ },
111
+ responses: {
112
+ 200: {
113
+ description: "A page of agents",
114
+ content: { "application/json": { schema: AgentListResponseSchema } },
115
+ },
116
+ ...ErrorResponses,
117
+ },
118
+ });
119
+
120
+ registry.registerPath({
121
+ method: "get",
122
+ path: "/v1/agents/{id}",
123
+ tags: ["Agents"],
124
+ summary: "Retrieve an agent (optionally a specific version)",
125
+ security: [{ ApiKey: [] }],
126
+ request: {
127
+ params: z.object({ id: z.string() }),
128
+ query: z.object({ version: z.coerce.number().int().optional() }),
129
+ },
130
+ responses: {
131
+ 200: {
132
+ description: "Agent retrieved",
133
+ content: { "application/json": { schema: AgentSchema } },
134
+ },
135
+ ...ErrorResponses,
136
+ },
137
+ });
138
+
139
+ registry.registerPath({
140
+ method: "post",
141
+ path: "/v1/agents/{id}",
142
+ tags: ["Agents"],
143
+ summary: "Update an agent (creates a new version)",
144
+ security: [{ ApiKey: [] }],
145
+ request: {
146
+ params: z.object({ id: z.string() }),
147
+ body: {
148
+ required: true,
149
+ content: { "application/json": { schema: UpdateAgentRequestSchema } },
150
+ },
151
+ },
152
+ responses: {
153
+ 200: {
154
+ description: "Agent updated",
155
+ content: { "application/json": { schema: AgentSchema } },
156
+ },
157
+ ...ErrorResponses,
158
+ },
159
+ });
160
+
161
+ registry.registerPath({
162
+ method: "delete",
163
+ path: "/v1/agents/{id}",
164
+ tags: ["Agents"],
165
+ summary: "Archive an agent",
166
+ security: [{ ApiKey: [] }],
167
+ request: { params: z.object({ id: z.string() }) },
168
+ responses: {
169
+ 200: {
170
+ description: "Agent archived",
171
+ content: { "application/json": { schema: AgentDeletedResponseSchema } },
172
+ },
173
+ ...ErrorResponses,
174
+ },
175
+ });
176
+
177
+ // ---------------------------------------------------------------------------
178
+ // /v1/environments
179
+ // ---------------------------------------------------------------------------
180
+
181
+ registry.registerPath({
182
+ method: "post",
183
+ path: "/v1/environments",
184
+ tags: ["Environments"],
185
+ summary: "Create an environment",
186
+ description:
187
+ "Returns immediately with `state: \"preparing\"`. Poll `GET /v1/environments/{id}` until `state` becomes `ready` before creating a session against it.",
188
+ security: [{ ApiKey: [] }],
189
+ request: {
190
+ body: {
191
+ required: true,
192
+ content: { "application/json": { schema: CreateEnvironmentRequestSchema } },
193
+ },
194
+ },
195
+ responses: {
196
+ 201: {
197
+ description: "Environment created; async setup in progress",
198
+ content: { "application/json": { schema: EnvironmentSchema } },
199
+ },
200
+ ...ErrorResponses,
201
+ },
202
+ });
203
+
204
+ registry.registerPath({
205
+ method: "get",
206
+ path: "/v1/environments",
207
+ tags: ["Environments"],
208
+ summary: "List environments",
209
+ security: [{ ApiKey: [] }],
210
+ request: {
211
+ query: z.object({
212
+ limit: z.coerce.number().int().min(1).max(100).optional(),
213
+ order: z.enum(["asc", "desc"]).optional(),
214
+ page: z.string().optional(),
215
+ include_archived: z.enum(["true", "false"]).optional(),
216
+ }),
217
+ },
218
+ responses: {
219
+ 200: {
220
+ description: "A page of environments",
221
+ content: { "application/json": { schema: EnvironmentListResponseSchema } },
222
+ },
223
+ ...ErrorResponses,
224
+ },
225
+ });
226
+
227
+ registry.registerPath({
228
+ method: "get",
229
+ path: "/v1/environments/{id}",
230
+ tags: ["Environments"],
231
+ summary: "Retrieve an environment",
232
+ security: [{ ApiKey: [] }],
233
+ request: { params: z.object({ id: z.string() }) },
234
+ responses: {
235
+ 200: {
236
+ description: "Environment retrieved",
237
+ content: { "application/json": { schema: EnvironmentSchema } },
238
+ },
239
+ ...ErrorResponses,
240
+ },
241
+ });
242
+
243
+ registry.registerPath({
244
+ method: "delete",
245
+ path: "/v1/environments/{id}",
246
+ tags: ["Environments"],
247
+ summary: "Delete an environment",
248
+ description:
249
+ "Returns 409 if the environment has non-terminated sessions still attached.",
250
+ security: [{ ApiKey: [] }],
251
+ request: { params: z.object({ id: z.string() }) },
252
+ responses: {
253
+ 200: {
254
+ description: "Environment deleted",
255
+ content: { "application/json": { schema: EnvironmentDeletedResponseSchema } },
256
+ },
257
+ ...ErrorResponses,
258
+ },
259
+ });
260
+
261
+ registry.registerPath({
262
+ method: "post",
263
+ path: "/v1/environments/{id}/archive",
264
+ tags: ["Environments"],
265
+ summary: "Archive an environment",
266
+ description:
267
+ "Returns 409 if the environment has non-terminated sessions still attached (same guard as DELETE).",
268
+ security: [{ ApiKey: [] }],
269
+ request: { params: z.object({ id: z.string() }) },
270
+ responses: {
271
+ 200: {
272
+ description: "Environment archived",
273
+ content: { "application/json": { schema: EnvironmentSchema } },
274
+ },
275
+ ...ErrorResponses,
276
+ },
277
+ });
278
+
279
+ // ---------------------------------------------------------------------------
280
+ // /v1/sessions
281
+ // ---------------------------------------------------------------------------
282
+
283
+ registry.registerPath({
284
+ method: "post",
285
+ path: "/v1/sessions",
286
+ tags: ["Sessions"],
287
+ summary: "Create a session",
288
+ description:
289
+ "Creates a session idle and pinned to the specified agent version + environment. A sprite is NOT allocated until the first user.message event.",
290
+ security: [{ ApiKey: [] }],
291
+ request: {
292
+ body: {
293
+ required: true,
294
+ content: { "application/json": { schema: CreateSessionRequestSchema } },
295
+ },
296
+ },
297
+ responses: {
298
+ 201: {
299
+ description: "Session created",
300
+ content: { "application/json": { schema: SessionSchema } },
301
+ },
302
+ ...ErrorResponses,
303
+ },
304
+ });
305
+
306
+ registry.registerPath({
307
+ method: "get",
308
+ path: "/v1/sessions",
309
+ tags: ["Sessions"],
310
+ summary: "List sessions",
311
+ security: [{ ApiKey: [] }],
312
+ request: {
313
+ query: z.object({
314
+ limit: z.coerce.number().int().min(1).max(100).optional(),
315
+ order: z.enum(["asc", "desc"]).optional(),
316
+ page: z.string().optional().describe("ULID cursor for pagination"),
317
+ include_archived: z.enum(["true", "false"]).optional(),
318
+ agent_id: z.string().optional(),
319
+ agent_version: z.coerce.number().int().optional(),
320
+ environment_id: z.string().optional(),
321
+ status: SessionStatusSchema.optional(),
322
+ "created_at[gt]": z.string().optional(),
323
+ "created_at[gte]": z.string().optional(),
324
+ "created_at[lt]": z.string().optional(),
325
+ "created_at[lte]": z.string().optional(),
326
+ }),
327
+ },
328
+ responses: {
329
+ 200: {
330
+ description: "A page of sessions",
331
+ content: { "application/json": { schema: SessionListResponseSchema } },
332
+ },
333
+ ...ErrorResponses,
334
+ },
335
+ });
336
+
337
+ registry.registerPath({
338
+ method: "get",
339
+ path: "/v1/sessions/{id}",
340
+ tags: ["Sessions"],
341
+ summary: "Retrieve a session",
342
+ security: [{ ApiKey: [] }],
343
+ request: { params: z.object({ id: z.string() }) },
344
+ responses: {
345
+ 200: {
346
+ description: "Session retrieved",
347
+ content: { "application/json": { schema: SessionSchema } },
348
+ },
349
+ ...ErrorResponses,
350
+ },
351
+ });
352
+
353
+ registry.registerPath({
354
+ method: "post",
355
+ path: "/v1/sessions/{id}",
356
+ tags: ["Sessions"],
357
+ summary: "Update session title / metadata",
358
+ security: [{ ApiKey: [] }],
359
+ request: {
360
+ params: z.object({ id: z.string() }),
361
+ body: {
362
+ required: true,
363
+ content: { "application/json": { schema: UpdateSessionRequestSchema } },
364
+ },
365
+ },
366
+ responses: {
367
+ 200: {
368
+ description: "Session updated",
369
+ content: { "application/json": { schema: SessionSchema } },
370
+ },
371
+ ...ErrorResponses,
372
+ },
373
+ });
374
+
375
+ registry.registerPath({
376
+ method: "delete",
377
+ path: "/v1/sessions/{id}",
378
+ tags: ["Sessions"],
379
+ summary: "Delete (terminate) a session",
380
+ description:
381
+ "Interrupts any in-flight turn, releases the sprite, and flips the session to `terminated` with `stop_reason:\"deleted\"`.",
382
+ security: [{ ApiKey: [] }],
383
+ request: { params: z.object({ id: z.string() }) },
384
+ responses: {
385
+ 200: {
386
+ description: "Session deleted",
387
+ content: { "application/json": { schema: SessionDeletedResponseSchema } },
388
+ },
389
+ ...ErrorResponses,
390
+ },
391
+ });
392
+
393
+ registry.registerPath({
394
+ method: "post",
395
+ path: "/v1/sessions/{id}/archive",
396
+ tags: ["Sessions"],
397
+ summary: "Archive a session",
398
+ security: [{ ApiKey: [] }],
399
+ request: { params: z.object({ id: z.string() }) },
400
+ responses: {
401
+ 200: {
402
+ description: "Session archived",
403
+ content: { "application/json": { schema: SessionSchema } },
404
+ },
405
+ ...ErrorResponses,
406
+ },
407
+ });
408
+
409
+ registry.registerPath({
410
+ method: "post",
411
+ path: "/v1/sessions/{id}/events",
412
+ tags: ["Events"],
413
+ summary: "Append user events to a session",
414
+ description:
415
+ "Atomically appends a batch of user events. `user.message` events buffered while a turn is running are drained as a subsequent turn. Supports `user.message`, `user.interrupt`, `user.custom_tool_result`, `user.tool_confirmation` (requires confirmation_mode), and `user.define_outcome` (triggers grader loop).",
416
+ security: [{ ApiKey: [] }],
417
+ request: {
418
+ params: z.object({ id: z.string() }),
419
+ headers: z.object({
420
+ "idempotency-key": z
421
+ .string()
422
+ .optional()
423
+ .describe("Per-event key is built as `<idempotency-key>:<batch index>`."),
424
+ }),
425
+ body: {
426
+ required: true,
427
+ content: { "application/json": { schema: UserEventBatchRequestSchema } },
428
+ },
429
+ },
430
+ responses: {
431
+ 200: {
432
+ description: "Events appended",
433
+ content: { "application/json": { schema: UserEventAppendResponseSchema } },
434
+ },
435
+ ...ErrorResponses,
436
+ },
437
+ });
438
+
439
+ registry.registerPath({
440
+ method: "get",
441
+ path: "/v1/sessions/{id}/events",
442
+ tags: ["Events"],
443
+ summary: "List a session's event history",
444
+ security: [{ ApiKey: [] }],
445
+ request: {
446
+ params: z.object({ id: z.string() }),
447
+ query: z.object({
448
+ limit: z.coerce.number().int().min(1).max(500).optional(),
449
+ order: z.enum(["asc", "desc"]).optional(),
450
+ after_seq: z.coerce.number().int().optional(),
451
+ }),
452
+ },
453
+ responses: {
454
+ 200: {
455
+ description: "A page of events",
456
+ content: { "application/json": { schema: EventListResponseSchema } },
457
+ },
458
+ ...ErrorResponses,
459
+ },
460
+ });
461
+
462
+ registry.registerPath({
463
+ method: "get",
464
+ path: "/v1/sessions/{id}/stream",
465
+ tags: ["Events"],
466
+ summary: "Stream a session's events via Server-Sent Events",
467
+ description:
468
+ "Long-lived `text/event-stream` response. Each emitted event sets an `id:` field with the monotonic `seq`, so `EventSource` clients resume automatically via `Last-Event-ID`. A `data: {\"type\":\"ping\"}` keepalive is written every 15 seconds. On reconnect, the server backfills events with `seq > Last-Event-ID` from the DB before tailing live.",
469
+ security: [{ ApiKey: [] }],
470
+ request: {
471
+ params: z.object({ id: z.string() }),
472
+ headers: z.object({
473
+ "last-event-id": z
474
+ .string()
475
+ .optional()
476
+ .describe("EventSource auto-populates this on reconnect; falls back to ?after_seq"),
477
+ }),
478
+ query: z.object({
479
+ after_seq: z.coerce.number().int().optional(),
480
+ }),
481
+ },
482
+ responses: {
483
+ 200: {
484
+ description: "SSE stream opened",
485
+ content: {
486
+ "text/event-stream": {
487
+ schema: z
488
+ .string()
489
+ .openapi({
490
+ description:
491
+ "Each frame is `id: <seq>\\nevent: <type>\\ndata: <json>\\n\\n`. JSON shape conforms to ManagedEvent.",
492
+ }),
493
+ },
494
+ },
495
+ },
496
+ ...ErrorResponses,
497
+ },
498
+ });
499
+
500
+ // ---------------------------------------------------------------------------
501
+ // /v1/vaults
502
+ // ---------------------------------------------------------------------------
503
+
504
+ registry.registerPath({
505
+ method: "post",
506
+ path: "/v1/vaults",
507
+ tags: ["Vaults"],
508
+ summary: "Create a vault",
509
+ security: [{ ApiKey: [] }],
510
+ request: {
511
+ body: {
512
+ required: true,
513
+ content: { "application/json": { schema: CreateVaultRequestSchema } },
514
+ },
515
+ },
516
+ responses: {
517
+ 201: {
518
+ description: "Vault created",
519
+ content: { "application/json": { schema: VaultSchema } },
520
+ },
521
+ ...ErrorResponses,
522
+ },
523
+ });
524
+
525
+ registry.registerPath({
526
+ method: "get",
527
+ path: "/v1/vaults",
528
+ tags: ["Vaults"],
529
+ summary: "List vaults",
530
+ security: [{ ApiKey: [] }],
531
+ request: {
532
+ query: z.object({
533
+ agent_id: z.string().optional().describe("Filter by agent ID"),
534
+ }),
535
+ },
536
+ responses: {
537
+ 200: {
538
+ description: "A list of vaults",
539
+ content: { "application/json": { schema: VaultListResponseSchema } },
540
+ },
541
+ ...ErrorResponses,
542
+ },
543
+ });
544
+
545
+ registry.registerPath({
546
+ method: "get",
547
+ path: "/v1/vaults/{id}",
548
+ tags: ["Vaults"],
549
+ summary: "Retrieve a vault",
550
+ security: [{ ApiKey: [] }],
551
+ request: { params: z.object({ id: z.string() }) },
552
+ responses: {
553
+ 200: {
554
+ description: "Vault retrieved",
555
+ content: { "application/json": { schema: VaultSchema } },
556
+ },
557
+ ...ErrorResponses,
558
+ },
559
+ });
560
+
561
+ registry.registerPath({
562
+ method: "delete",
563
+ path: "/v1/vaults/{id}",
564
+ tags: ["Vaults"],
565
+ summary: "Delete a vault and all its entries",
566
+ security: [{ ApiKey: [] }],
567
+ request: { params: z.object({ id: z.string() }) },
568
+ responses: {
569
+ 200: {
570
+ description: "Vault deleted",
571
+ content: { "application/json": { schema: VaultDeletedResponseSchema } },
572
+ },
573
+ ...ErrorResponses,
574
+ },
575
+ });
576
+
577
+ registry.registerPath({
578
+ method: "get",
579
+ path: "/v1/vaults/{id}/entries",
580
+ tags: ["Vaults"],
581
+ summary: "List vault entries",
582
+ security: [{ ApiKey: [] }],
583
+ request: { params: z.object({ id: z.string() }) },
584
+ responses: {
585
+ 200: {
586
+ description: "A list of vault entries",
587
+ content: { "application/json": { schema: VaultEntryListResponseSchema } },
588
+ },
589
+ ...ErrorResponses,
590
+ },
591
+ });
592
+
593
+ registry.registerPath({
594
+ method: "get",
595
+ path: "/v1/vaults/{id}/entries/{key}",
596
+ tags: ["Vaults"],
597
+ summary: "Get a vault entry",
598
+ security: [{ ApiKey: [] }],
599
+ request: { params: z.object({ id: z.string(), key: z.string() }) },
600
+ responses: {
601
+ 200: {
602
+ description: "Vault entry retrieved",
603
+ content: { "application/json": { schema: VaultEntrySchema } },
604
+ },
605
+ ...ErrorResponses,
606
+ },
607
+ });
608
+
609
+ registry.registerPath({
610
+ method: "put",
611
+ path: "/v1/vaults/{id}/entries/{key}",
612
+ tags: ["Vaults"],
613
+ summary: "Set a vault entry",
614
+ security: [{ ApiKey: [] }],
615
+ request: {
616
+ params: z.object({ id: z.string(), key: z.string() }),
617
+ body: {
618
+ required: true,
619
+ content: { "application/json": { schema: SetVaultEntryRequestSchema } },
620
+ },
621
+ },
622
+ responses: {
623
+ 200: {
624
+ description: "Entry set",
625
+ content: { "application/json": { schema: VaultEntrySchema } },
626
+ },
627
+ ...ErrorResponses,
628
+ },
629
+ });
630
+
631
+ registry.registerPath({
632
+ method: "delete",
633
+ path: "/v1/vaults/{id}/entries/{key}",
634
+ tags: ["Vaults"],
635
+ summary: "Delete a vault entry",
636
+ security: [{ ApiKey: [] }],
637
+ request: { params: z.object({ id: z.string(), key: z.string() }) },
638
+ responses: {
639
+ 200: {
640
+ description: "Entry deleted",
641
+ content: { "application/json": { schema: VaultEntryDeletedResponseSchema } },
642
+ },
643
+ ...ErrorResponses,
644
+ },
645
+ });
646
+
647
+ // ---------------------------------------------------------------------------
648
+ // /v1/batch
649
+ // ---------------------------------------------------------------------------
650
+
651
+ registry.registerPath({
652
+ method: "post",
653
+ path: "/v1/batch",
654
+ tags: ["Batch"],
655
+ summary: "Execute multiple operations atomically",
656
+ description:
657
+ "Wraps all operations in a single SQLite transaction. On any error, the transaction rolls back and the error is returned.",
658
+ security: [{ ApiKey: [] }],
659
+ request: {
660
+ body: {
661
+ required: true,
662
+ content: { "application/json": { schema: BatchRequestSchema } },
663
+ },
664
+ },
665
+ responses: {
666
+ 200: {
667
+ description: "All operations succeeded",
668
+ content: { "application/json": { schema: BatchResponseSchema } },
669
+ },
670
+ ...ErrorResponses,
671
+ },
672
+ });
673
+
674
+ // ---------------------------------------------------------------------------
675
+ // Generate the final document
676
+ // ---------------------------------------------------------------------------
677
+
678
+ export function buildOpenApiDocument(opts: { serverUrl: string }): unknown {
679
+ const generator = new OpenApiGeneratorV31(registry.definitions);
680
+ return generator.generateDocument({
681
+ openapi: "3.1.0",
682
+ info: {
683
+ title: "AgentStep Gateway",
684
+ version: "0.1.0",
685
+ description:
686
+ "Open-source, drop-in replacement for the Claude Managed Agents API. Self-hosted agent gateway with 6 agent harnesses and 9 sandbox providers. Use with `@agentstep/agent-sdk` or the official Anthropic SDK — just change the baseURL.",
687
+ },
688
+ servers: [{ url: opts.serverUrl, description: "This host" }],
689
+ security: [{ ApiKey: [] }],
690
+ });
691
+ }