@opencode/protocol 2.0.3 → 2.0.5

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 (69) hide show
  1. package/dist/api.d.ts +4 -6
  2. package/dist/api.js +4 -8
  3. package/dist/client.d.ts +0 -2
  4. package/dist/client.js +0 -2
  5. package/dist/errors.d.ts +20 -0
  6. package/dist/errors.js +17 -0
  7. package/dist/groups/agent.d.ts +6 -5
  8. package/dist/groups/agent.js +2 -2
  9. package/dist/groups/command.d.ts +3 -3
  10. package/dist/groups/command.js +1 -1
  11. package/dist/groups/config.d.ts +5 -13
  12. package/dist/groups/config.js +11 -18
  13. package/dist/groups/credential.d.ts +3 -18
  14. package/dist/groups/credential.js +3 -10
  15. package/dist/groups/debug.d.ts +0 -8
  16. package/dist/groups/debug.js +3 -3
  17. package/dist/groups/event.d.ts +99 -74
  18. package/dist/groups/event.js +3 -3
  19. package/dist/groups/form.d.ts +6 -654
  20. package/dist/groups/form.js +6 -83
  21. package/dist/groups/fs.d.ts +6 -6
  22. package/dist/groups/fs.js +3 -3
  23. package/dist/groups/generate.d.ts +1 -1
  24. package/dist/groups/generate.js +2 -2
  25. package/dist/groups/integration.d.ts +27 -27
  26. package/dist/groups/integration.js +19 -16
  27. package/dist/groups/location.d.ts +10 -4
  28. package/dist/groups/location.js +2 -3
  29. package/dist/groups/mcp.d.ts +11 -13
  30. package/dist/groups/mcp.js +10 -10
  31. package/dist/groups/message.d.ts +698 -40
  32. package/dist/groups/message.js +26 -3
  33. package/dist/groups/migration.js +1 -1
  34. package/dist/groups/model.d.ts +12 -9
  35. package/dist/groups/model.js +2 -2
  36. package/dist/groups/permission.d.ts +9 -16
  37. package/dist/groups/permission.js +8 -20
  38. package/dist/groups/persistent-pty.js +1 -1
  39. package/dist/groups/plugin.d.ts +7 -12
  40. package/dist/groups/plugin.js +3 -13
  41. package/dist/groups/project.d.ts +1 -14
  42. package/dist/groups/project.js +2 -13
  43. package/dist/groups/provider.d.ts +8 -7
  44. package/dist/groups/provider.js +2 -2
  45. package/dist/groups/pty.d.ts +15 -12
  46. package/dist/groups/pty.js +8 -8
  47. package/dist/groups/reference.d.ts +8 -12
  48. package/dist/groups/reference.js +16 -3
  49. package/dist/groups/rpc.d.ts +0 -1
  50. package/dist/groups/rpc.js +1 -1
  51. package/dist/groups/server.d.ts +9 -1
  52. package/dist/groups/server.js +11 -5
  53. package/dist/groups/session.d.ts +1085 -793
  54. package/dist/groups/session.js +172 -94
  55. package/dist/groups/shell.d.ts +13 -43
  56. package/dist/groups/shell.js +6 -24
  57. package/dist/groups/skill.d.ts +4 -5
  58. package/dist/groups/skill.js +1 -1
  59. package/dist/groups/vcs.d.ts +17 -12
  60. package/dist/groups/vcs.js +6 -6
  61. package/dist/groups/websearch.d.ts +6 -5
  62. package/dist/groups/websearch.js +2 -2
  63. package/dist/groups/worktree.d.ts +17 -23
  64. package/dist/groups/worktree.js +21 -30
  65. package/package.json +2 -2
  66. package/dist/groups/health.d.ts +0 -15
  67. package/dist/groups/health.js +0 -20
  68. package/dist/groups/workspace.d.ts +0 -22
  69. package/dist/groups/workspace.js +0 -27
@@ -1,17 +1,15 @@
1
1
  import { SessionMessage } from "@opencode/schema/session-message";
2
- import { SessionTransfer } from "@opencode/schema/session-transfer";
3
2
  import { SessionInbox } from "@opencode/schema/session-inbox";
4
3
  import { PromptInput } from "@opencode/schema/prompt-input";
5
4
  import { Session } from "@opencode/schema/session";
6
5
  import { SessionStats } from "@opencode/schema/session-stats";
7
6
  import { InstructionEntry } from "@opencode/schema/instruction-entry";
8
7
  import { Project } from "@opencode/schema/project";
9
- import { AbsolutePath, NonNegativeInt, PositiveInt, RelativePath, statics } from "@opencode/schema/schema";
8
+ import { AbsolutePath, DateTimeUtcFromMillis, NonNegativeInt, PositiveInt, RelativePath, statics, } from "@opencode/schema/schema";
10
9
  import { Event } from "@opencode/schema/event";
11
- import { Workspace } from "@opencode/schema/workspace";
12
10
  import { Context, Effect, Encoding, Result, Schema, SchemaGetter, Struct } from "effect";
13
11
  import { HttpApiEndpoint, HttpApiGroup, HttpApiMiddleware, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
14
- import { ConflictError, CommandExecutionError, CommandNotFoundError, InvalidCursorError, InvalidRequestError, MessageNotFoundError, ServiceUnavailableError, SessionBusyError, SessionNotFoundError, SkillNotFoundError, UnknownError, } from "../errors.js";
12
+ import { ConflictError, CommandExecutionError, CommandNotFoundError, FormAlreadySettledError, FormInvalidAnswerError, FormNotFoundError, InvalidCursorError, InvalidRequestError, MessageNotFoundError, ServiceUnavailableError, SessionBusyError, SessionNotFoundError, SkillNotFoundError, UnknownError, } from "../errors.js";
15
13
  import { Agent } from "@opencode/schema/agent";
16
14
  import { Skill } from "@opencode/schema/skill";
17
15
  import { Model } from "@opencode/schema/model";
@@ -20,6 +18,8 @@ import { Location } from "@opencode/schema/location";
20
18
  import { SessionEvent } from "@opencode/schema/session-event";
21
19
  import { EventLog } from "@opencode/schema/event-log";
22
20
  import { FileDiff } from "@opencode/schema/file-diff";
21
+ import { Form } from "@opencode/schema/form";
22
+ import { PublicSessionMessage } from "./message.js";
23
23
  const ParentIDFilter = Schema.Union([
24
24
  Session.ID,
25
25
  Schema.Null.pipe(Schema.encodeTo(Schema.Literal("null"), {
@@ -30,7 +30,6 @@ const ParentIDFilter = Schema.Union([
30
30
  description: "Filter by parent session. Use null to return only root sessions.",
31
31
  });
32
32
  const SessionsQueryFields = {
33
- workspace: Workspace.ID.pipe(Schema.optional),
34
33
  limit: Schema.NumberFromString.pipe(Schema.decodeTo(PositiveInt), Schema.optional).annotate({
35
34
  description: "Maximum number of sessions to return. Defaults to the newest 50 sessions.",
36
35
  }),
@@ -78,6 +77,34 @@ export const SessionsCursor = Schema.String.pipe(Schema.brand("SessionsCursor"),
78
77
  const SessionActive = Schema.Struct({
79
78
  type: Schema.Literal("running"),
80
79
  }).annotate({ identifier: "SessionActive" });
80
+ const PublicSessionInfo = Schema.Struct({
81
+ ...Struct.omit(Session.Info.fields, ["location"]),
82
+ location: Location.PublicRef,
83
+ }).annotate({ identifier: "Session.Info" });
84
+ const PublicSessionTransfer = Schema.Struct({
85
+ info: PublicSessionInfo,
86
+ messages: Schema.Array(PublicSessionMessage),
87
+ }).annotate({ identifier: "SessionTransfer.Data" });
88
+ const PublicMovePayload = Schema.Struct({
89
+ ...Struct.omit(SessionInbox.MovePayload.fields, ["location"]),
90
+ location: Location.PublicRef,
91
+ }).annotate({ identifier: "Session.Inbox.MovePayload" });
92
+ const PublicMove = Schema.Struct({
93
+ ...Struct.omit(SessionInbox.Move.fields, ["payload"]),
94
+ payload: PublicMovePayload,
95
+ }).annotate({ identifier: "Session.Inbox.Move" });
96
+ const PublicInboxInfo = Schema.Union([
97
+ SessionInbox.User,
98
+ SessionInbox.Synthetic,
99
+ SessionInbox.Compaction,
100
+ PublicMove,
101
+ ]).annotate({ identifier: "Session.Inbox.Info" });
102
+ const FormCreatePayload = Schema.Struct({
103
+ id: Form.ID.pipe(Schema.optional),
104
+ title: Form.Info.fields.title,
105
+ metadata: Form.Info.fields.metadata,
106
+ fields: Form.Info.fields.fields,
107
+ }).annotate({ identifier: "Form.CreatePayload" });
81
108
  const BooleanFromString = Schema.Literals(["true", "false"]).pipe(Schema.decodeTo(Schema.Boolean, {
82
109
  decode: SchemaGetter.transform((value) => value === "true"),
83
110
  encode: SchemaGetter.transform((value) => (value ? "true" : "false")),
@@ -92,11 +119,11 @@ export const SessionsQuery = Schema.Struct({
92
119
  subpath: RelativePath.pipe(Schema.optional),
93
120
  cursor: SessionsQueryCursor.pipe(Schema.optional),
94
121
  }).annotate({ identifier: "SessionsQuery" });
95
- export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make("server.session")
122
+ export const makeSessionGroup = (sessionLocationMiddleware, formLocationMiddleware) => HttpApiGroup.make("server.session")
96
123
  .add(HttpApiEndpoint.get("session.list", "/api/session", {
97
124
  query: SessionsQuery,
98
125
  success: Schema.Struct({
99
- data: Schema.Array(Session.Info),
126
+ data: Schema.Array(PublicSessionInfo),
100
127
  cursor: Schema.Struct({
101
128
  previous: SessionsCursor.pipe(Schema.optional),
102
129
  next: SessionsCursor.pipe(Schema.optional),
@@ -104,11 +131,11 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
104
131
  }).annotate({ identifier: "SessionsResponse" }),
105
132
  error: [InvalidCursorError, InvalidRequestError],
106
133
  }).annotateMerge(OpenApi.annotations({
107
- identifier: "v2.session.list",
134
+ identifier: "session.list",
108
135
  summary: "List sessions",
109
136
  description: "Retrieve sessions in the requested order. Items keep that order across pages; use cursor.next or cursor.previous to move through the ordered list.",
110
137
  })))
111
- .add(HttpApiEndpoint.get("session.stats", "/api/session/stats", {
138
+ .add(HttpApiEndpoint.get("session.stats", "/api/experimental/session/stats", {
112
139
  query: Schema.Struct({
113
140
  from: Schema.NumberFromString.pipe(Schema.optional),
114
141
  to: Schema.NumberFromString.pipe(Schema.optional),
@@ -119,7 +146,7 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
119
146
  success: Schema.Struct({ data: SessionStats.Info }),
120
147
  error: InvalidRequestError,
121
148
  }).annotateMerge(OpenApi.annotations({
122
- identifier: "v2.session.stats",
149
+ identifier: "experimental.session.stats",
123
150
  summary: "Get session statistics",
124
151
  description: "Aggregate local session activity, usage, and tool reliability for a time range.",
125
152
  })))
@@ -129,51 +156,51 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
129
156
  title: Schema.String.pipe(Schema.optional),
130
157
  agent: Agent.ID.pipe(Schema.optional),
131
158
  model: Model.Ref.pipe(Schema.optional),
132
- location: Location.Ref.pipe(Schema.optional),
159
+ location: Location.PublicRef.pipe(Schema.optional),
133
160
  metadata: Session.Metadata.pipe(Schema.optional),
134
161
  permissions: Permission.Ruleset.pipe(Schema.optional),
135
162
  }),
136
- success: Schema.Struct({ data: Session.Info }),
163
+ success: Schema.Struct({ data: PublicSessionInfo }),
137
164
  }).annotateMerge(OpenApi.annotations({
138
- identifier: "v2.session.create",
165
+ identifier: "session.create",
139
166
  summary: "Create session",
140
167
  description: "Create a session at the requested location.",
141
168
  })))
142
- .add(HttpApiEndpoint.post("session.import", "/api/session/import", {
169
+ .add(HttpApiEndpoint.post("session.import", "/api/experimental/session/import", {
143
170
  payload: Schema.Struct({
144
- ...SessionTransfer.Data.fields,
145
- location: Location.Ref.pipe(Schema.optional),
171
+ ...PublicSessionTransfer.fields,
172
+ location: Location.PublicRef.pipe(Schema.optional),
146
173
  }),
147
- success: Schema.Struct({ data: Session.Info }),
174
+ success: Schema.Struct({ data: PublicSessionInfo }),
148
175
  error: [ConflictError, SessionNotFoundError],
149
176
  }).annotateMerge(OpenApi.annotations({
150
- identifier: "v2.session.import",
177
+ identifier: "experimental.session.import",
151
178
  summary: "Import session",
152
179
  description: "Import a projected session transcript at the requested location. If parentID is supplied, the parent session must already exist; import parents before children.",
153
180
  })))
154
- .add(HttpApiEndpoint.get("session.export", "/api/session/:sessionID/export", {
181
+ .add(HttpApiEndpoint.get("session.export", "/api/experimental/session/:sessionID/export", {
155
182
  params: { sessionID: Session.ID },
156
183
  query: Schema.Struct({ sanitize: BooleanFromString.pipe(Schema.optional) }),
157
- success: Schema.Struct({ data: SessionTransfer.Data }),
184
+ success: Schema.Struct({ data: PublicSessionTransfer }),
158
185
  error: [SessionNotFoundError, UnknownError],
159
186
  }).annotateMerge(OpenApi.annotations({
160
- identifier: "v2.session.export",
187
+ identifier: "experimental.session.export",
161
188
  summary: "Export session",
162
189
  description: "Export a complete projected session transcript.",
163
190
  })))
164
191
  .add(HttpApiEndpoint.get("session.active", "/api/session/active", {
165
192
  success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive) }),
166
193
  }).annotateMerge(OpenApi.annotations({
167
- identifier: "v2.session.active",
194
+ identifier: "session.active",
168
195
  summary: "List active sessions",
169
196
  description: "Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive.",
170
197
  })))
171
198
  .add(HttpApiEndpoint.get("session.get", "/api/session/:sessionID", {
172
199
  params: { sessionID: Session.ID },
173
- success: Schema.Struct({ data: Session.Info }),
200
+ success: Schema.Struct({ data: PublicSessionInfo }),
174
201
  error: SessionNotFoundError,
175
202
  }).annotateMerge(OpenApi.annotations({
176
- identifier: "v2.session.get",
203
+ identifier: "session.get",
177
204
  summary: "Get session",
178
205
  description: "Retrieve a session by ID.",
179
206
  })))
@@ -182,21 +209,21 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
182
209
  success: HttpApiSchema.NoContent,
183
210
  error: SessionNotFoundError,
184
211
  }).annotateMerge(OpenApi.annotations({
185
- identifier: "v2.session.remove",
212
+ identifier: "session.remove",
186
213
  summary: "Delete session",
187
214
  description: "Delete a session and its child sessions.",
188
215
  })))
189
216
  .add(HttpApiEndpoint.post("session.fork", "/api/session/:sessionID/fork", {
190
217
  params: { sessionID: Session.ID },
191
- payload: Schema.Struct({ boundary: Session.ForkRequestBoundary }),
192
- success: Schema.Struct({ data: Session.Info }),
218
+ payload: Schema.Struct({ before: SessionMessage.ID.pipe(Schema.optional) }),
219
+ success: Schema.Struct({ data: PublicSessionInfo }),
193
220
  error: [SessionNotFoundError, MessageNotFoundError, InvalidRequestError],
194
221
  })
195
222
  .middleware(sessionLocationMiddleware)
196
223
  .annotateMerge(OpenApi.annotations({
197
- identifier: "v2.session.fork",
224
+ identifier: "session.fork",
198
225
  summary: "Fork session",
199
- description: "Create a child session by copying projected history through or before a message boundary.",
226
+ description: "Create a child session by copying projected history before a message. Omit before to copy the full history.",
200
227
  })))
201
228
  .add(HttpApiEndpoint.post("session.switchAgent", "/api/session/:sessionID/agent", {
202
229
  params: { sessionID: Session.ID },
@@ -206,7 +233,7 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
206
233
  })
207
234
  .middleware(sessionLocationMiddleware)
208
235
  .annotateMerge(OpenApi.annotations({
209
- identifier: "v2.session.switchAgent",
236
+ identifier: "session.switchAgent",
210
237
  summary: "Switch session agent",
211
238
  description: "Switch the agent used by subsequent provider turns.",
212
239
  })))
@@ -218,31 +245,34 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
218
245
  })
219
246
  .middleware(sessionLocationMiddleware)
220
247
  .annotateMerge(OpenApi.annotations({
221
- identifier: "v2.session.switchModel",
248
+ identifier: "session.switchModel",
222
249
  summary: "Switch session model",
223
250
  description: "Switch the model used by subsequent provider turns.",
224
251
  })))
225
- .add(HttpApiEndpoint.post("session.rename", "/api/session/:sessionID/rename", {
252
+ .add(HttpApiEndpoint.patch("session.update", "/api/session/:sessionID", {
226
253
  params: { sessionID: Session.ID },
227
- payload: Schema.Struct({ title: Schema.String }),
254
+ payload: Schema.Struct({
255
+ title: Schema.String.pipe(Schema.optional),
256
+ permissions: Permission.Ruleset.pipe(Schema.optional),
257
+ }),
228
258
  success: HttpApiSchema.NoContent,
229
259
  error: SessionNotFoundError,
230
260
  })
231
261
  .middleware(sessionLocationMiddleware)
232
262
  .annotateMerge(OpenApi.annotations({
233
- identifier: "v2.session.rename",
234
- summary: "Rename session",
235
- description: "Update the session title.",
263
+ identifier: "session.update",
264
+ summary: "Update session",
265
+ description: "Update mutable session properties.",
236
266
  })))
237
267
  .add(HttpApiEndpoint.post("session.move", "/api/session/:sessionID/move", {
238
268
  params: { sessionID: Session.ID },
239
- payload: Schema.Struct({ ...Location.Ref.fields, delivery: SessionInbox.Delivery.pipe(Schema.optional) }),
269
+ payload: Schema.Struct({ ...Location.PublicRef.fields, delivery: SessionInbox.Delivery.pipe(Schema.optional) }),
240
270
  success: HttpApiSchema.NoContent,
241
271
  error: [SessionNotFoundError, InvalidRequestError],
242
272
  }).annotateMerge(OpenApi.annotations({
243
- identifier: "v2.session.move",
273
+ identifier: "session.move",
244
274
  summary: "Move session",
245
- description: "Move a session to another project directory, optionally transferring local changes.",
275
+ description: "Move a session to another project directory at the requested delivery boundary.",
246
276
  })))
247
277
  .add(HttpApiEndpoint.post("session.prompt", "/api/session/:sessionID/prompt", {
248
278
  params: { sessionID: Session.ID },
@@ -258,14 +288,14 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
258
288
  })
259
289
  .middleware(sessionLocationMiddleware)
260
290
  .annotateMerge(OpenApi.annotations({
261
- identifier: "v2.session.prompt",
291
+ identifier: "session.prompt",
262
292
  summary: "Send message",
263
293
  description: "Durably admit one session input and schedule agent-loop execution unless resume is false.",
264
294
  })))
265
295
  .add(HttpApiEndpoint.post("session.command", "/api/session/:sessionID/command", {
266
296
  params: { sessionID: Session.ID },
267
297
  payload: Schema.Struct({
268
- command: Schema.String,
298
+ name: Schema.String,
269
299
  ...PromptInput.Prompt.fields,
270
300
  delivery: SessionInbox.Delivery.pipe(Schema.optional),
271
301
  }),
@@ -274,15 +304,14 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
274
304
  })
275
305
  .middleware(sessionLocationMiddleware)
276
306
  .annotateMerge(OpenApi.annotations({
277
- identifier: "v2.session.command",
307
+ identifier: "session.command",
278
308
  summary: "Run command",
279
309
  description: "Execute a slash command callback immediately.",
280
310
  })))
281
- .add(HttpApiEndpoint.post("session.skill", "/api/session/:sessionID/skill", {
311
+ .add(HttpApiEndpoint.post("session.skill", "/api/experimental/session/:sessionID/skill", {
282
312
  params: { sessionID: Session.ID },
283
313
  payload: Schema.Struct({
284
- id: SessionMessage.ID.pipe(Schema.optional),
285
- skill: Skill.ID,
314
+ id: Skill.ID,
286
315
  resume: Schema.Boolean.pipe(Schema.optional),
287
316
  }),
288
317
  success: HttpApiSchema.NoContent,
@@ -290,7 +319,7 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
290
319
  })
291
320
  .middleware(sessionLocationMiddleware)
292
321
  .annotateMerge(OpenApi.annotations({
293
- identifier: "v2.session.skill",
322
+ identifier: "experimental.session.skill",
294
323
  summary: "Activate skill",
295
324
  description: "Activate a skill for a session by appending a skill message and resuming execution.",
296
325
  })))
@@ -309,14 +338,14 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
309
338
  })
310
339
  .middleware(sessionLocationMiddleware)
311
340
  .annotateMerge(OpenApi.annotations({
312
- identifier: "v2.session.synthetic",
341
+ identifier: "session.synthetic",
313
342
  summary: "Add synthetic message",
314
343
  description: "Durably admit synthetic session input and schedule execution unless resume is false.",
315
344
  })))
316
345
  .add(HttpApiEndpoint.post("session.shell", "/api/session/:sessionID/shell", {
317
346
  params: { sessionID: Session.ID },
318
347
  payload: Schema.Struct({
319
- id: Event.ID.pipe(Schema.optional),
348
+ id: SessionMessage.ID.pipe(Schema.optional),
320
349
  command: Schema.String,
321
350
  }),
322
351
  success: HttpApiSchema.NoContent,
@@ -324,7 +353,7 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
324
353
  })
325
354
  .middleware(sessionLocationMiddleware)
326
355
  .annotateMerge(OpenApi.annotations({
327
- identifier: "v2.session.shell",
356
+ identifier: "session.shell",
328
357
  summary: "Run shell command",
329
358
  description: "Execute one shell command in the session's working directory. Emits a shell.started event before execution and a shell.ended event with the merged output after.",
330
359
  })))
@@ -339,18 +368,18 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
339
368
  })
340
369
  .middleware(sessionLocationMiddleware)
341
370
  .annotateMerge(OpenApi.annotations({
342
- identifier: "v2.session.compact",
371
+ identifier: "session.compact",
343
372
  summary: "Compact session",
344
373
  description: "Durably admit a session compaction request. Steers by default: it runs at the next step boundary instead of waiting behind queued prompts.",
345
374
  })))
346
- .add(HttpApiEndpoint.post("session.wait", "/api/session/:sessionID/wait", {
375
+ .add(HttpApiEndpoint.post("session.wait", "/api/experimental/session/:sessionID/wait", {
347
376
  params: { sessionID: Session.ID },
348
377
  success: HttpApiSchema.NoContent,
349
378
  error: [SessionNotFoundError, ServiceUnavailableError],
350
379
  })
351
380
  .middleware(sessionLocationMiddleware)
352
381
  .annotateMerge(OpenApi.annotations({
353
- identifier: "v2.session.wait",
382
+ identifier: "experimental.session.wait",
354
383
  summary: "Wait for session",
355
384
  description: "Wait for a session agent loop to become idle.",
356
385
  })))
@@ -362,30 +391,30 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
362
391
  })
363
392
  .middleware(sessionLocationMiddleware)
364
393
  .annotateMerge(OpenApi.annotations({
365
- identifier: "v2.session.revert.stage",
394
+ identifier: "session.revert.stage",
366
395
  summary: "Stage session revert",
367
396
  description: "Stage or move a reversible session boundary and optionally apply its file changes.",
368
397
  })))
369
- .add(HttpApiEndpoint.post("session.revert.clear", "/api/session/:sessionID/revert/clear", {
398
+ .add(HttpApiEndpoint.delete("session.revert.clear", "/api/session/:sessionID/revert", {
370
399
  params: { sessionID: Session.ID },
371
400
  success: HttpApiSchema.NoContent,
372
401
  error: [SessionNotFoundError, SessionBusyError, UnknownError],
373
402
  })
374
403
  .middleware(sessionLocationMiddleware)
375
- .annotateMerge(OpenApi.annotations({ identifier: "v2.session.revert.clear", summary: "Clear staged revert" })))
404
+ .annotateMerge(OpenApi.annotations({ identifier: "session.revert.clear", summary: "Clear staged revert" })))
376
405
  .add(HttpApiEndpoint.post("session.revert.commit", "/api/session/:sessionID/revert/commit", {
377
406
  params: { sessionID: Session.ID },
378
407
  success: HttpApiSchema.NoContent,
379
408
  error: [SessionNotFoundError, SessionBusyError],
380
409
  })
381
410
  .middleware(sessionLocationMiddleware)
382
- .annotateMerge(OpenApi.annotations({ identifier: "v2.session.revert.commit", summary: "Commit staged revert" })))
411
+ .annotateMerge(OpenApi.annotations({ identifier: "session.revert.commit", summary: "Commit staged revert" })))
383
412
  .add(HttpApiEndpoint.get("session.context", "/api/session/:sessionID/context", {
384
413
  params: { sessionID: Session.ID },
385
- success: Schema.Struct({ data: Schema.Array(SessionMessage.Info) }),
414
+ success: Schema.Struct({ data: Schema.Array(PublicSessionMessage) }),
386
415
  error: [SessionNotFoundError, UnknownError],
387
416
  }).annotateMerge(OpenApi.annotations({
388
- identifier: "v2.session.context",
417
+ identifier: "session.context",
389
418
  summary: "Get session context",
390
419
  description: "Retrieve the active context messages for a session (all messages after the last compaction).",
391
420
  })))
@@ -405,58 +434,50 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
405
434
  success: Schema.Struct({ data: Schema.Array(FileDiff.Info) }),
406
435
  error: [InvalidRequestError, MessageNotFoundError, SessionNotFoundError, UnknownError],
407
436
  }).annotateMerge(OpenApi.annotations({
408
- identifier: "v2.session.diff",
437
+ identifier: "session.diff",
409
438
  summary: "Diff session turns",
410
439
  description: "Structured per-file diffs of the files a turn changed. A turn runs from the first prompt after the session was last idle until its next idle marker, so prompts steered in while it was busy belong to the same turn; `to` extends the range through a later turn. Compares the range's first recorded snapshot with its last; a step still running in the active session compares against the working copy. Ranges that span a location change are rejected. In sessions without any idle marker, a prompt's turn spans until the next user message.",
411
440
  })))
412
441
  .add(HttpApiEndpoint.get("session.inbox.list", "/api/session/:sessionID/inbox", {
413
442
  params: { sessionID: Session.ID },
414
- success: Schema.Struct({ data: Schema.Array(SessionInbox.Info) }),
443
+ success: Schema.Struct({ data: Schema.Array(PublicInboxInfo) }),
415
444
  error: SessionNotFoundError,
416
445
  }).annotateMerge(OpenApi.annotations({
417
- identifier: "v2.session.inbox.list",
446
+ identifier: "session.inbox.list",
418
447
  summary: "List session inbox",
419
448
  description: "List durable enqueued session work not yet delivered, ordered by enqueue sequence. Includes user, synthetic, compaction, and move items.",
420
449
  })))
421
450
  .add(HttpApiEndpoint.delete("session.inbox.cancel", "/api/session/:sessionID/inbox/:inboxID", {
422
451
  params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
423
452
  success: HttpApiSchema.NoContent,
424
- error: [ConflictError, SessionNotFoundError],
453
+ error: SessionNotFoundError,
425
454
  }).annotateMerge(OpenApi.annotations({
426
- identifier: "v2.session.inbox.cancel",
455
+ identifier: "session.inbox.cancel",
427
456
  summary: "Cancel inbox input",
428
- description: "Cancel an inbox item that has not yet been delivered.",
457
+ description: "Cancel an inbox item that has not yet been delivered. Unavailable items are a no-op.",
429
458
  })))
430
- .add(HttpApiEndpoint.post("session.inbox.steer", "/api/session/:sessionID/inbox/:inboxID/steer", {
459
+ .add(HttpApiEndpoint.patch("session.inbox.update", "/api/session/:sessionID/inbox/:inboxID", {
431
460
  params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
461
+ payload: Schema.Struct({ delivery: SessionInbox.Delivery }),
432
462
  success: HttpApiSchema.NoContent,
433
463
  error: [ConflictError, SessionNotFoundError],
434
464
  }).annotateMerge(OpenApi.annotations({
435
- identifier: "v2.session.inbox.steer",
436
- summary: "Steer queued item",
437
- description: "Change a queued inbox item to steer delivery and wake session execution.",
465
+ identifier: "session.inbox.update",
466
+ summary: "Update inbox item",
467
+ description: "Change a pending inbox item's delivery mode. Steering wakes session execution.",
438
468
  })))
439
- .add(HttpApiEndpoint.post("session.inbox.queue", "/api/session/:sessionID/inbox/:inboxID/queue", {
440
- params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
441
- success: HttpApiSchema.NoContent,
442
- error: [ConflictError, SessionNotFoundError],
443
- }).annotateMerge(OpenApi.annotations({
444
- identifier: "v2.session.inbox.queue",
445
- summary: "Queue steered item",
446
- description: "Change a steered inbox item to queued delivery.",
447
- })))
448
- .add(HttpApiEndpoint.get("session.instructions.entry.list", "/api/session/:sessionID/instructions/entries", {
469
+ .add(HttpApiEndpoint.get("session.instructions.entry.list", "/api/experimental/session/:sessionID/instructions/entries", {
449
470
  params: { sessionID: Session.ID },
450
471
  success: Schema.Struct({ data: Schema.Array(InstructionEntry.Info) }),
451
472
  error: SessionNotFoundError,
452
473
  })
453
474
  .middleware(sessionLocationMiddleware)
454
475
  .annotateMerge(OpenApi.annotations({
455
- identifier: "v2.session.instructions.entry.list",
476
+ identifier: "experimental.session.instructions.entry.list",
456
477
  summary: "List instruction entries",
457
478
  description: "List API-managed instruction entries attached to the session.",
458
479
  })))
459
- .add(HttpApiEndpoint.put("session.instructions.entry.put", "/api/session/:sessionID/instructions/entries/:key", {
480
+ .add(HttpApiEndpoint.put("session.instructions.entry.put", "/api/experimental/session/:sessionID/instructions/entries/:key", {
460
481
  params: { sessionID: Session.ID, key: InstructionEntry.Key },
461
482
  payload: Schema.Struct({ value: Schema.Json }),
462
483
  success: HttpApiSchema.NoContent,
@@ -464,18 +485,18 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
464
485
  })
465
486
  .middleware(sessionLocationMiddleware)
466
487
  .annotateMerge(OpenApi.annotations({
467
- identifier: "v2.session.instructions.entry.put",
488
+ identifier: "experimental.session.instructions.entry.put",
468
489
  summary: "Put instruction entry",
469
490
  description: "Attach or replace one durable instruction entry. Changes announce as updates at the next step boundary.",
470
491
  })))
471
- .add(HttpApiEndpoint.delete("session.instructions.entry.remove", "/api/session/:sessionID/instructions/entries/:key", {
492
+ .add(HttpApiEndpoint.delete("session.instructions.entry.remove", "/api/experimental/session/:sessionID/instructions/entries/:key", {
472
493
  params: { sessionID: Session.ID, key: InstructionEntry.Key },
473
494
  success: HttpApiSchema.NoContent,
474
495
  error: SessionNotFoundError,
475
496
  })
476
497
  .middleware(sessionLocationMiddleware)
477
498
  .annotateMerge(OpenApi.annotations({
478
- identifier: "v2.session.instructions.entry.remove",
499
+ identifier: "experimental.session.instructions.entry.remove",
479
500
  summary: "Remove instruction entry",
480
501
  description: "Remove one instruction entry; the removal is announced to the model at the next step boundary.",
481
502
  })))
@@ -489,7 +510,7 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
489
510
  })
490
511
  .middleware(sessionLocationMiddleware)
491
512
  .annotateMerge(OpenApi.annotations({
492
- identifier: "v2.session.generate",
513
+ identifier: "session.generate",
493
514
  summary: "Generate text from session context",
494
515
  description: "Generate transient text from the current session context without mutating session history.",
495
516
  })))
@@ -504,13 +525,13 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
504
525
  }),
505
526
  error: SessionNotFoundError,
506
527
  }).annotateMerge(OpenApi.annotations({
507
- identifier: "v2.session.log",
528
+ identifier: "session.log",
508
529
  summary: "Read the session log",
509
530
  description: "Experimental durable session event log. Reads events after an exclusive aggregate sequence and continues with live events when follow=true.",
510
531
  })))
511
532
  .add(HttpApiEndpoint.post("session.interrupt", "/api/session/:sessionID/interrupt", {
512
533
  params: { sessionID: Session.ID },
513
- query: { continue: BooleanFromString.pipe(Schema.optional) },
534
+ query: { resume: BooleanFromString.pipe(Schema.optional) },
514
535
  success: Schema.Struct({
515
536
  interrupted: Schema.Boolean.annotate({
516
537
  description: "Whether an active execution owned by this OpenCode process was interrupted.",
@@ -520,9 +541,9 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
520
541
  })
521
542
  .middleware(sessionLocationMiddleware)
522
543
  .annotateMerge(OpenApi.annotations({
523
- identifier: "v2.session.interrupt",
544
+ identifier: "session.interrupt",
524
545
  summary: "Interrupt session execution",
525
- description: "Interrupt active execution owned by this OpenCode process. Returns interrupted=true when an active execution was interrupted and false for the idle no-op. When continue=true, execution resumes pending steering input and next-in-line control items (manual compaction, moves) while queued prompts remain parked.",
546
+ description: "Interrupt active execution owned by this OpenCode process. Returns interrupted=true when an active execution was interrupted and false for the idle no-op. When resume=true, execution resumes pending steering input and next-in-line control items (manual compaction, moves) while queued prompts remain parked.",
526
547
  })))
527
548
  .add(HttpApiEndpoint.post("session.background", "/api/session/:sessionID/background", {
528
549
  params: { sessionID: Session.ID },
@@ -531,18 +552,75 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
531
552
  })
532
553
  .middleware(sessionLocationMiddleware)
533
554
  .annotateMerge(OpenApi.annotations({
534
- identifier: "v2.session.background",
555
+ identifier: "session.background",
535
556
  summary: "Background blocking session tools",
536
557
  description: "Move active foreground backgroundable tools for this session into background observation. Idle requests are a no-op.",
537
558
  })))
538
559
  .add(HttpApiEndpoint.get("session.message", "/api/session/:sessionID/message/:messageID", {
539
560
  params: { sessionID: Session.ID, messageID: SessionMessage.ID },
540
- success: Schema.Struct({ data: SessionMessage.Info }),
561
+ success: Schema.Struct({ data: PublicSessionMessage }),
541
562
  error: [SessionNotFoundError, MessageNotFoundError],
542
563
  }).annotateMerge(OpenApi.annotations({
543
- identifier: "v2.session.message",
564
+ identifier: "session.message.get",
544
565
  summary: "Get session message",
545
566
  description: "Retrieve one projected message owned by the Session.",
567
+ })))
568
+ .add(HttpApiEndpoint.get("session.form.list", "/api/session/:sessionID/form", {
569
+ params: { sessionID: Schema.String },
570
+ success: Schema.Struct({ data: Schema.Array(Form.Info) }),
571
+ error: SessionNotFoundError,
572
+ })
573
+ .middleware(formLocationMiddleware)
574
+ .annotateMerge(OpenApi.annotations({
575
+ identifier: "session.form.list",
576
+ summary: "List session forms",
577
+ description: "Retrieve pending forms for a session.",
578
+ })))
579
+ .add(HttpApiEndpoint.post("session.form.create", "/api/session/:sessionID/form", {
580
+ params: { sessionID: Schema.String },
581
+ payload: FormCreatePayload,
582
+ success: Schema.Struct({ data: Form.Info }),
583
+ error: [SessionNotFoundError, ConflictError, InvalidRequestError],
584
+ })
585
+ .middleware(formLocationMiddleware)
586
+ .annotateMerge(OpenApi.annotations({
587
+ identifier: "session.form.create",
588
+ summary: "Create session form",
589
+ description: "Create a form for a session.",
590
+ })))
591
+ .add(HttpApiEndpoint.get("session.form.get", "/api/session/:sessionID/form/:formID", {
592
+ params: { sessionID: Schema.String, formID: Form.ID },
593
+ success: Schema.Struct({ data: Form.Detail }),
594
+ error: [SessionNotFoundError, FormNotFoundError],
595
+ })
596
+ .middleware(formLocationMiddleware)
597
+ .annotateMerge(OpenApi.annotations({
598
+ identifier: "session.form.get",
599
+ summary: "Get session form",
600
+ description: "Retrieve a form and its current state for a session.",
601
+ })))
602
+ .add(HttpApiEndpoint.post("session.form.reply", "/api/session/:sessionID/form/:formID/reply", {
603
+ params: { sessionID: Schema.String, formID: Form.ID },
604
+ payload: Form.Reply,
605
+ success: HttpApiSchema.NoContent,
606
+ error: [SessionNotFoundError, FormAlreadySettledError, FormInvalidAnswerError, FormNotFoundError],
607
+ })
608
+ .middleware(formLocationMiddleware)
609
+ .annotateMerge(OpenApi.annotations({
610
+ identifier: "session.form.reply",
611
+ summary: "Reply to form",
612
+ description: "Submit an answer to a pending form.",
613
+ })))
614
+ .add(HttpApiEndpoint.delete("session.form.cancel", "/api/session/:sessionID/form/:formID", {
615
+ params: { sessionID: Schema.String, formID: Form.ID },
616
+ success: HttpApiSchema.NoContent,
617
+ error: [SessionNotFoundError, FormAlreadySettledError, FormNotFoundError],
618
+ })
619
+ .middleware(formLocationMiddleware)
620
+ .annotateMerge(OpenApi.annotations({
621
+ identifier: "session.form.cancel",
622
+ summary: "Cancel form",
623
+ description: "Cancel a pending form.",
546
624
  })))
547
625
  .add(HttpApiEndpoint.put("session.environment", "/api/session/:sessionID/environment", {
548
626
  params: { sessionID: Session.ID },
@@ -550,17 +628,17 @@ export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make
550
628
  success: HttpApiSchema.NoContent,
551
629
  error: SessionNotFoundError,
552
630
  }).annotateMerge(OpenApi.annotations({
553
- identifier: "v2.session.environment",
631
+ identifier: "session.environment",
554
632
  summary: "Set session environment",
555
633
  description: "Replace the process environment used by local shell commands for this session.",
556
634
  })))
557
635
  .add(HttpApiEndpoint.post("session.view", "/api/session/:sessionID/view", {
558
636
  params: { sessionID: Session.ID },
559
- payload: Schema.Struct({ idle: NonNegativeInt }),
637
+ payload: Schema.Struct({ idle: DateTimeUtcFromMillis }),
560
638
  success: HttpApiSchema.NoContent,
561
639
  error: SessionNotFoundError,
562
640
  }).annotateMerge(OpenApi.annotations({
563
- identifier: "v2.session.view",
641
+ identifier: "session.view",
564
642
  summary: "View session",
565
643
  description: "Mark the idle transition observed by the viewer as viewed.",
566
644
  })))