@opencode/protocol 0.0.0-reserved → 2.0.1
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.
- package/dist/api.d.ts +56 -0
- package/dist/api.js +79 -0
- package/dist/client.d.ts +64 -0
- package/dist/client.js +50 -0
- package/dist/errors.d.ts +153 -0
- package/dist/errors.js +122 -0
- package/dist/groups/agent.d.ts +194 -0
- package/dist/groups/agent.js +30 -0
- package/dist/groups/command.d.ts +15 -0
- package/dist/groups/command.js +20 -0
- package/dist/groups/config.d.ts +9 -0
- package/dist/groups/config.js +16 -0
- package/dist/groups/credential.d.ts +32 -0
- package/dist/groups/credential.js +40 -0
- package/dist/groups/debug.d.ts +17 -0
- package/dist/groups/debug.js +23 -0
- package/dist/groups/event.d.ts +14643 -0
- package/dist/groups/event.js +51 -0
- package/dist/groups/form.d.ts +781 -0
- package/dist/groups/form.js +95 -0
- package/dist/groups/fs.d.ts +35 -0
- package/dist/groups/fs.js +53 -0
- package/dist/groups/generate.d.ts +33 -0
- package/dist/groups/generate.js +23 -0
- package/dist/groups/health.d.ts +15 -0
- package/dist/groups/health.js +20 -0
- package/dist/groups/integration.d.ts +1154 -0
- package/dist/groups/integration.js +149 -0
- package/dist/groups/location.d.ts +16 -0
- package/dist/groups/location.js +34 -0
- package/dist/groups/mcp.d.ts +82 -0
- package/dist/groups/mcp.js +78 -0
- package/dist/groups/message.d.ts +734 -0
- package/dist/groups/message.js +51 -0
- package/dist/groups/migration.d.ts +28 -0
- package/dist/groups/migration.js +21 -0
- package/dist/groups/model.d.ts +314 -0
- package/dist/groups/model.js +33 -0
- package/dist/groups/permission.d.ts +167 -0
- package/dist/groups/permission.js +109 -0
- package/dist/groups/persistent-pty.d.ts +249 -0
- package/dist/groups/persistent-pty.js +91 -0
- package/dist/groups/plugin.d.ts +91 -0
- package/dist/groups/plugin.js +55 -0
- package/dist/groups/project.d.ts +90 -0
- package/dist/groups/project.js +39 -0
- package/dist/groups/provider.d.ts +156 -0
- package/dist/groups/provider.js +34 -0
- package/dist/groups/pty.d.ts +147 -0
- package/dist/groups/pty.js +113 -0
- package/dist/groups/reference.d.ts +29 -0
- package/dist/groups/reference.js +20 -0
- package/dist/groups/rpc.d.ts +22 -0
- package/dist/groups/rpc.js +24 -0
- package/dist/groups/server.d.ts +5 -0
- package/dist/groups/server.js +11 -0
- package/dist/groups/session.d.ts +10794 -0
- package/dist/groups/session.js +549 -0
- package/dist/groups/shell.d.ts +152 -0
- package/dist/groups/shell.js +82 -0
- package/dist/groups/skill.d.ts +20 -0
- package/dist/groups/skill.js +20 -0
- package/dist/groups/vcs.d.ts +70 -0
- package/dist/groups/vcs.js +76 -0
- package/dist/groups/websearch.d.ts +28 -0
- package/dist/groups/websearch.js +34 -0
- package/dist/groups/workspace.d.ts +22 -0
- package/dist/groups/workspace.js +27 -0
- package/dist/groups/worktree.d.ts +47 -0
- package/dist/groups/worktree.js +61 -0
- package/dist/middleware/authorization.d.ts +13 -0
- package/dist/middleware/authorization.js +6 -0
- package/dist/middleware/schema-error.d.ts +13 -0
- package/dist/middleware/schema-error.js +4 -0
- package/dist/simulation.d.ts +1801 -0
- package/dist/simulation.js +487 -0
- package/package.json +36 -6
- package/README.md +0 -5
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import { SessionMessage } from "@opencode/schema/session-message";
|
|
2
|
+
import { SessionTransfer } from "@opencode/schema/session-transfer";
|
|
3
|
+
import { SessionInbox } from "@opencode/schema/session-inbox";
|
|
4
|
+
import { PromptInput } from "@opencode/schema/prompt-input";
|
|
5
|
+
import { Session } from "@opencode/schema/session";
|
|
6
|
+
import { SessionStats } from "@opencode/schema/session-stats";
|
|
7
|
+
import { InstructionEntry } from "@opencode/schema/instruction-entry";
|
|
8
|
+
import { Project } from "@opencode/schema/project";
|
|
9
|
+
import { AbsolutePath, NonNegativeInt, PositiveInt, RelativePath, statics } from "@opencode/schema/schema";
|
|
10
|
+
import { Event } from "@opencode/schema/event";
|
|
11
|
+
import { Workspace } from "@opencode/schema/workspace";
|
|
12
|
+
import { Context, Effect, Encoding, Result, Schema, SchemaGetter, Struct } from "effect";
|
|
13
|
+
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";
|
|
15
|
+
import { Agent } from "@opencode/schema/agent";
|
|
16
|
+
import { Skill } from "@opencode/schema/skill";
|
|
17
|
+
import { Model } from "@opencode/schema/model";
|
|
18
|
+
import { Permission } from "@opencode/schema/permission";
|
|
19
|
+
import { Location } from "@opencode/schema/location";
|
|
20
|
+
import { SessionEvent } from "@opencode/schema/session-event";
|
|
21
|
+
import { EventLog } from "@opencode/schema/event-log";
|
|
22
|
+
const ParentIDFilter = Schema.Union([
|
|
23
|
+
Session.ID,
|
|
24
|
+
Schema.Null.pipe(Schema.encodeTo(Schema.Literal("null"), {
|
|
25
|
+
decode: SchemaGetter.transform(() => null),
|
|
26
|
+
encode: SchemaGetter.transform(() => "null"),
|
|
27
|
+
})),
|
|
28
|
+
]).annotate({
|
|
29
|
+
description: "Filter by parent session. Use null to return only root sessions.",
|
|
30
|
+
});
|
|
31
|
+
const SessionsQueryFields = {
|
|
32
|
+
workspace: Workspace.ID.pipe(Schema.optional),
|
|
33
|
+
limit: Schema.NumberFromString.pipe(Schema.decodeTo(PositiveInt), Schema.optional).annotate({
|
|
34
|
+
description: "Maximum number of sessions to return. Defaults to the newest 50 sessions.",
|
|
35
|
+
}),
|
|
36
|
+
order: Schema.optional(Schema.Union([Schema.Literal("asc"), Schema.Literal("desc")])).annotate({
|
|
37
|
+
description: "Session order for the first page. Use desc for newest first or asc for oldest first.",
|
|
38
|
+
}),
|
|
39
|
+
search: Schema.optional(Schema.String),
|
|
40
|
+
parentID: ParentIDFilter.pipe(Schema.optional),
|
|
41
|
+
};
|
|
42
|
+
const SessionsDirectoryQuery = Schema.Struct({
|
|
43
|
+
...SessionsQueryFields,
|
|
44
|
+
directory: AbsolutePath,
|
|
45
|
+
});
|
|
46
|
+
const SessionsProjectQuery = Schema.Struct({
|
|
47
|
+
...SessionsQueryFields,
|
|
48
|
+
project: Project.ID,
|
|
49
|
+
subpath: RelativePath.pipe(Schema.optional),
|
|
50
|
+
});
|
|
51
|
+
const SessionsAllQuery = Schema.Struct(SessionsQueryFields);
|
|
52
|
+
const withCursor = (schema) => schema.mapFields((fields) => ({
|
|
53
|
+
...Struct.omit(fields, ["limit"]),
|
|
54
|
+
anchor: Session.ListAnchor,
|
|
55
|
+
}));
|
|
56
|
+
const SessionsCursorInput = Schema.Union([
|
|
57
|
+
withCursor(SessionsDirectoryQuery),
|
|
58
|
+
withCursor(SessionsProjectQuery),
|
|
59
|
+
withCursor(SessionsAllQuery),
|
|
60
|
+
]);
|
|
61
|
+
const SessionsCursorJson = Schema.fromJsonString(SessionsCursorInput);
|
|
62
|
+
const encodeSessionsCursor = Schema.encodeSync(SessionsCursorJson);
|
|
63
|
+
const decodeSessionsCursor = Schema.decodeUnknownEffect(SessionsCursorJson);
|
|
64
|
+
const invalidCursor = "Invalid cursor";
|
|
65
|
+
export const SessionsCursor = Schema.String.pipe(Schema.brand("SessionsCursor"), statics((schema) => {
|
|
66
|
+
const make = schema.make.bind(schema);
|
|
67
|
+
return {
|
|
68
|
+
make: (input) => make(Encoding.encodeBase64Url(encodeSessionsCursor(input))),
|
|
69
|
+
parse: (input) => Effect.suspend(() => {
|
|
70
|
+
const result = Encoding.decodeBase64UrlString(input);
|
|
71
|
+
return Result.isFailure(result)
|
|
72
|
+
? Effect.fail(invalidCursor)
|
|
73
|
+
: decodeSessionsCursor(result.success).pipe(Effect.mapError(() => invalidCursor));
|
|
74
|
+
}),
|
|
75
|
+
};
|
|
76
|
+
}));
|
|
77
|
+
const SessionActive = Schema.Struct({
|
|
78
|
+
type: Schema.Literal("running"),
|
|
79
|
+
}).annotate({ identifier: "SessionActive" });
|
|
80
|
+
const BooleanFromString = Schema.Literals(["true", "false"]).pipe(Schema.decodeTo(Schema.Boolean, {
|
|
81
|
+
decode: SchemaGetter.transform((value) => value === "true"),
|
|
82
|
+
encode: SchemaGetter.transform((value) => (value ? "true" : "false")),
|
|
83
|
+
}));
|
|
84
|
+
const SessionsQueryCursor = SessionsCursor.annotate({
|
|
85
|
+
description: "Opaque pagination cursor returned as cursor.previous or cursor.next in the previous response.",
|
|
86
|
+
});
|
|
87
|
+
export const SessionsQuery = Schema.Struct({
|
|
88
|
+
...SessionsQueryFields,
|
|
89
|
+
directory: AbsolutePath.pipe(Schema.optional),
|
|
90
|
+
project: Project.ID.pipe(Schema.optional),
|
|
91
|
+
subpath: RelativePath.pipe(Schema.optional),
|
|
92
|
+
cursor: SessionsQueryCursor.pipe(Schema.optional),
|
|
93
|
+
}).annotate({ identifier: "SessionsQuery" });
|
|
94
|
+
export const makeSessionGroup = (sessionLocationMiddleware) => HttpApiGroup.make("server.session")
|
|
95
|
+
.add(HttpApiEndpoint.get("session.list", "/api/session", {
|
|
96
|
+
query: SessionsQuery,
|
|
97
|
+
success: Schema.Struct({
|
|
98
|
+
data: Schema.Array(Session.Info),
|
|
99
|
+
cursor: Schema.Struct({
|
|
100
|
+
previous: SessionsCursor.pipe(Schema.optional),
|
|
101
|
+
next: SessionsCursor.pipe(Schema.optional),
|
|
102
|
+
}),
|
|
103
|
+
}).annotate({ identifier: "SessionsResponse" }),
|
|
104
|
+
error: [InvalidCursorError, InvalidRequestError],
|
|
105
|
+
}).annotateMerge(OpenApi.annotations({
|
|
106
|
+
identifier: "v2.session.list",
|
|
107
|
+
summary: "List sessions",
|
|
108
|
+
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.",
|
|
109
|
+
})))
|
|
110
|
+
.add(HttpApiEndpoint.get("session.stats", "/api/session/stats", {
|
|
111
|
+
query: Schema.Struct({
|
|
112
|
+
from: Schema.NumberFromString.pipe(Schema.optional),
|
|
113
|
+
to: Schema.NumberFromString.pipe(Schema.optional),
|
|
114
|
+
project: Project.ID.pipe(Schema.optional),
|
|
115
|
+
timezone: Schema.String.pipe(Schema.optional),
|
|
116
|
+
tools: SessionStats.ToolMode.pipe(Schema.optional),
|
|
117
|
+
}),
|
|
118
|
+
success: Schema.Struct({ data: SessionStats.Info }),
|
|
119
|
+
error: InvalidRequestError,
|
|
120
|
+
}).annotateMerge(OpenApi.annotations({
|
|
121
|
+
identifier: "v2.session.stats",
|
|
122
|
+
summary: "Get session statistics",
|
|
123
|
+
description: "Aggregate local session activity, usage, and tool reliability for a time range.",
|
|
124
|
+
})))
|
|
125
|
+
.add(HttpApiEndpoint.post("session.create", "/api/session", {
|
|
126
|
+
payload: Schema.Struct({
|
|
127
|
+
id: Session.ID.pipe(Schema.optional),
|
|
128
|
+
title: Schema.String.pipe(Schema.optional),
|
|
129
|
+
agent: Agent.ID.pipe(Schema.optional),
|
|
130
|
+
model: Model.Ref.pipe(Schema.optional),
|
|
131
|
+
location: Location.Ref.pipe(Schema.optional),
|
|
132
|
+
metadata: Session.Metadata.pipe(Schema.optional),
|
|
133
|
+
permissions: Permission.Ruleset.pipe(Schema.optional),
|
|
134
|
+
}),
|
|
135
|
+
success: Schema.Struct({ data: Session.Info }),
|
|
136
|
+
}).annotateMerge(OpenApi.annotations({
|
|
137
|
+
identifier: "v2.session.create",
|
|
138
|
+
summary: "Create session",
|
|
139
|
+
description: "Create a session at the requested location.",
|
|
140
|
+
})))
|
|
141
|
+
.add(HttpApiEndpoint.post("session.import", "/api/session/import", {
|
|
142
|
+
payload: Schema.Struct({
|
|
143
|
+
...SessionTransfer.Data.fields,
|
|
144
|
+
location: Location.Ref.pipe(Schema.optional),
|
|
145
|
+
}),
|
|
146
|
+
success: Schema.Struct({ data: Session.Info }),
|
|
147
|
+
error: [ConflictError, SessionNotFoundError],
|
|
148
|
+
}).annotateMerge(OpenApi.annotations({
|
|
149
|
+
identifier: "v2.session.import",
|
|
150
|
+
summary: "Import session",
|
|
151
|
+
description: "Import a projected session transcript at the requested location. If parentID is supplied, the parent session must already exist; import parents before children.",
|
|
152
|
+
})))
|
|
153
|
+
.add(HttpApiEndpoint.get("session.export", "/api/session/:sessionID/export", {
|
|
154
|
+
params: { sessionID: Session.ID },
|
|
155
|
+
query: Schema.Struct({ sanitize: BooleanFromString.pipe(Schema.optional) }),
|
|
156
|
+
success: Schema.Struct({ data: SessionTransfer.Data }),
|
|
157
|
+
error: [SessionNotFoundError, UnknownError],
|
|
158
|
+
}).annotateMerge(OpenApi.annotations({
|
|
159
|
+
identifier: "v2.session.export",
|
|
160
|
+
summary: "Export session",
|
|
161
|
+
description: "Export a complete projected session transcript.",
|
|
162
|
+
})))
|
|
163
|
+
.add(HttpApiEndpoint.get("session.active", "/api/session/active", {
|
|
164
|
+
success: Schema.Struct({ data: Schema.Record(Session.ID, SessionActive) }),
|
|
165
|
+
}).annotateMerge(OpenApi.annotations({
|
|
166
|
+
identifier: "v2.session.active",
|
|
167
|
+
summary: "List active sessions",
|
|
168
|
+
description: "Retrieve foreground Session drains currently owned by this OpenCode process. Sessions absent from the result are inactive.",
|
|
169
|
+
})))
|
|
170
|
+
.add(HttpApiEndpoint.get("session.get", "/api/session/:sessionID", {
|
|
171
|
+
params: { sessionID: Session.ID },
|
|
172
|
+
success: Schema.Struct({ data: Session.Info }),
|
|
173
|
+
error: SessionNotFoundError,
|
|
174
|
+
}).annotateMerge(OpenApi.annotations({
|
|
175
|
+
identifier: "v2.session.get",
|
|
176
|
+
summary: "Get session",
|
|
177
|
+
description: "Retrieve a session by ID.",
|
|
178
|
+
})))
|
|
179
|
+
.add(HttpApiEndpoint.delete("session.remove", "/api/session/:sessionID", {
|
|
180
|
+
params: { sessionID: Session.ID },
|
|
181
|
+
success: HttpApiSchema.NoContent,
|
|
182
|
+
error: SessionNotFoundError,
|
|
183
|
+
}).annotateMerge(OpenApi.annotations({
|
|
184
|
+
identifier: "v2.session.remove",
|
|
185
|
+
summary: "Delete session",
|
|
186
|
+
description: "Delete a session and its child sessions.",
|
|
187
|
+
})))
|
|
188
|
+
.add(HttpApiEndpoint.post("session.fork", "/api/session/:sessionID/fork", {
|
|
189
|
+
params: { sessionID: Session.ID },
|
|
190
|
+
payload: Schema.Struct({ boundary: Session.ForkRequestBoundary }),
|
|
191
|
+
success: Schema.Struct({ data: Session.Info }),
|
|
192
|
+
error: [SessionNotFoundError, MessageNotFoundError, InvalidRequestError],
|
|
193
|
+
})
|
|
194
|
+
.middleware(sessionLocationMiddleware)
|
|
195
|
+
.annotateMerge(OpenApi.annotations({
|
|
196
|
+
identifier: "v2.session.fork",
|
|
197
|
+
summary: "Fork session",
|
|
198
|
+
description: "Create a child session by copying projected history through or before a message boundary.",
|
|
199
|
+
})))
|
|
200
|
+
.add(HttpApiEndpoint.post("session.switchAgent", "/api/session/:sessionID/agent", {
|
|
201
|
+
params: { sessionID: Session.ID },
|
|
202
|
+
payload: Schema.Struct({ agent: Agent.ID }),
|
|
203
|
+
success: HttpApiSchema.NoContent,
|
|
204
|
+
error: SessionNotFoundError,
|
|
205
|
+
})
|
|
206
|
+
.middleware(sessionLocationMiddleware)
|
|
207
|
+
.annotateMerge(OpenApi.annotations({
|
|
208
|
+
identifier: "v2.session.switchAgent",
|
|
209
|
+
summary: "Switch session agent",
|
|
210
|
+
description: "Switch the agent used by subsequent provider turns.",
|
|
211
|
+
})))
|
|
212
|
+
.add(HttpApiEndpoint.post("session.switchModel", "/api/session/:sessionID/model", {
|
|
213
|
+
params: { sessionID: Session.ID },
|
|
214
|
+
payload: Schema.Struct({ model: Model.Ref }),
|
|
215
|
+
success: HttpApiSchema.NoContent,
|
|
216
|
+
error: SessionNotFoundError,
|
|
217
|
+
})
|
|
218
|
+
.middleware(sessionLocationMiddleware)
|
|
219
|
+
.annotateMerge(OpenApi.annotations({
|
|
220
|
+
identifier: "v2.session.switchModel",
|
|
221
|
+
summary: "Switch session model",
|
|
222
|
+
description: "Switch the model used by subsequent provider turns.",
|
|
223
|
+
})))
|
|
224
|
+
.add(HttpApiEndpoint.post("session.rename", "/api/session/:sessionID/rename", {
|
|
225
|
+
params: { sessionID: Session.ID },
|
|
226
|
+
payload: Schema.Struct({ title: Schema.String }),
|
|
227
|
+
success: HttpApiSchema.NoContent,
|
|
228
|
+
error: SessionNotFoundError,
|
|
229
|
+
})
|
|
230
|
+
.middleware(sessionLocationMiddleware)
|
|
231
|
+
.annotateMerge(OpenApi.annotations({
|
|
232
|
+
identifier: "v2.session.rename",
|
|
233
|
+
summary: "Rename session",
|
|
234
|
+
description: "Update the session title.",
|
|
235
|
+
})))
|
|
236
|
+
.add(HttpApiEndpoint.post("session.move", "/api/session/:sessionID/move", {
|
|
237
|
+
params: { sessionID: Session.ID },
|
|
238
|
+
payload: Schema.Struct({ ...Location.Ref.fields, delivery: SessionInbox.Delivery.pipe(Schema.optional) }),
|
|
239
|
+
success: HttpApiSchema.NoContent,
|
|
240
|
+
error: [SessionNotFoundError, InvalidRequestError],
|
|
241
|
+
}).annotateMerge(OpenApi.annotations({
|
|
242
|
+
identifier: "v2.session.move",
|
|
243
|
+
summary: "Move session",
|
|
244
|
+
description: "Move a session to another project directory, optionally transferring local changes.",
|
|
245
|
+
})))
|
|
246
|
+
.add(HttpApiEndpoint.post("session.prompt", "/api/session/:sessionID/prompt", {
|
|
247
|
+
params: { sessionID: Session.ID },
|
|
248
|
+
payload: Schema.Struct({
|
|
249
|
+
id: SessionMessage.ID.pipe(Schema.optional),
|
|
250
|
+
...PromptInput.Prompt.fields,
|
|
251
|
+
metadata: SessionInbox.UserPayload.fields.metadata,
|
|
252
|
+
delivery: SessionInbox.Delivery.pipe(Schema.optional),
|
|
253
|
+
resume: Schema.Boolean.pipe(Schema.optional),
|
|
254
|
+
}),
|
|
255
|
+
success: Schema.Struct({ data: SessionInbox.User }),
|
|
256
|
+
error: [ConflictError, InvalidRequestError, SessionNotFoundError],
|
|
257
|
+
})
|
|
258
|
+
.middleware(sessionLocationMiddleware)
|
|
259
|
+
.annotateMerge(OpenApi.annotations({
|
|
260
|
+
identifier: "v2.session.prompt",
|
|
261
|
+
summary: "Send message",
|
|
262
|
+
description: "Durably admit one session input and schedule agent-loop execution unless resume is false.",
|
|
263
|
+
})))
|
|
264
|
+
.add(HttpApiEndpoint.post("session.command", "/api/session/:sessionID/command", {
|
|
265
|
+
params: { sessionID: Session.ID },
|
|
266
|
+
payload: Schema.Struct({
|
|
267
|
+
command: Schema.String,
|
|
268
|
+
...PromptInput.Prompt.fields,
|
|
269
|
+
delivery: SessionInbox.Delivery.pipe(Schema.optional),
|
|
270
|
+
}),
|
|
271
|
+
success: HttpApiSchema.NoContent,
|
|
272
|
+
error: [SessionNotFoundError, CommandNotFoundError, CommandExecutionError],
|
|
273
|
+
})
|
|
274
|
+
.middleware(sessionLocationMiddleware)
|
|
275
|
+
.annotateMerge(OpenApi.annotations({
|
|
276
|
+
identifier: "v2.session.command",
|
|
277
|
+
summary: "Run command",
|
|
278
|
+
description: "Execute a slash command callback immediately.",
|
|
279
|
+
})))
|
|
280
|
+
.add(HttpApiEndpoint.post("session.skill", "/api/session/:sessionID/skill", {
|
|
281
|
+
params: { sessionID: Session.ID },
|
|
282
|
+
payload: Schema.Struct({
|
|
283
|
+
id: SessionMessage.ID.pipe(Schema.optional),
|
|
284
|
+
skill: Skill.ID,
|
|
285
|
+
resume: Schema.Boolean.pipe(Schema.optional),
|
|
286
|
+
}),
|
|
287
|
+
success: HttpApiSchema.NoContent,
|
|
288
|
+
error: [SessionNotFoundError, SkillNotFoundError],
|
|
289
|
+
})
|
|
290
|
+
.middleware(sessionLocationMiddleware)
|
|
291
|
+
.annotateMerge(OpenApi.annotations({
|
|
292
|
+
identifier: "v2.session.skill",
|
|
293
|
+
summary: "Activate skill",
|
|
294
|
+
description: "Activate a skill for a session by appending a skill message and resuming execution.",
|
|
295
|
+
})))
|
|
296
|
+
.add(HttpApiEndpoint.post("session.synthetic", "/api/session/:sessionID/synthetic", {
|
|
297
|
+
params: { sessionID: Session.ID },
|
|
298
|
+
payload: Schema.Struct({
|
|
299
|
+
id: SessionMessage.ID.pipe(Schema.optional),
|
|
300
|
+
text: Schema.String,
|
|
301
|
+
description: Schema.String.pipe(Schema.optional),
|
|
302
|
+
metadata: SessionMessage.Synthetic.fields.metadata,
|
|
303
|
+
delivery: SessionInbox.Delivery.pipe(Schema.optional),
|
|
304
|
+
resume: Schema.Boolean.pipe(Schema.optional),
|
|
305
|
+
}),
|
|
306
|
+
success: Schema.Struct({ data: SessionInbox.Synthetic }),
|
|
307
|
+
error: [ConflictError, SessionNotFoundError],
|
|
308
|
+
})
|
|
309
|
+
.middleware(sessionLocationMiddleware)
|
|
310
|
+
.annotateMerge(OpenApi.annotations({
|
|
311
|
+
identifier: "v2.session.synthetic",
|
|
312
|
+
summary: "Add synthetic message",
|
|
313
|
+
description: "Durably admit synthetic session input and schedule execution unless resume is false.",
|
|
314
|
+
})))
|
|
315
|
+
.add(HttpApiEndpoint.post("session.shell", "/api/session/:sessionID/shell", {
|
|
316
|
+
params: { sessionID: Session.ID },
|
|
317
|
+
payload: Schema.Struct({
|
|
318
|
+
id: Event.ID.pipe(Schema.optional),
|
|
319
|
+
command: Schema.String,
|
|
320
|
+
}),
|
|
321
|
+
success: HttpApiSchema.NoContent,
|
|
322
|
+
error: SessionNotFoundError,
|
|
323
|
+
})
|
|
324
|
+
.middleware(sessionLocationMiddleware)
|
|
325
|
+
.annotateMerge(OpenApi.annotations({
|
|
326
|
+
identifier: "v2.session.shell",
|
|
327
|
+
summary: "Run shell command",
|
|
328
|
+
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.",
|
|
329
|
+
})))
|
|
330
|
+
.add(HttpApiEndpoint.post("session.compact", "/api/session/:sessionID/compact", {
|
|
331
|
+
params: { sessionID: Session.ID },
|
|
332
|
+
payload: Schema.Struct({
|
|
333
|
+
id: SessionMessage.ID.pipe(Schema.optional),
|
|
334
|
+
delivery: SessionInbox.Delivery.pipe(Schema.optional),
|
|
335
|
+
}),
|
|
336
|
+
success: Schema.Struct({ data: SessionInbox.Compaction }),
|
|
337
|
+
error: [ConflictError, SessionNotFoundError],
|
|
338
|
+
})
|
|
339
|
+
.middleware(sessionLocationMiddleware)
|
|
340
|
+
.annotateMerge(OpenApi.annotations({
|
|
341
|
+
identifier: "v2.session.compact",
|
|
342
|
+
summary: "Compact session",
|
|
343
|
+
description: "Durably admit a session compaction request. Steers by default: it runs at the next step boundary instead of waiting behind queued prompts.",
|
|
344
|
+
})))
|
|
345
|
+
.add(HttpApiEndpoint.post("session.wait", "/api/session/:sessionID/wait", {
|
|
346
|
+
params: { sessionID: Session.ID },
|
|
347
|
+
success: HttpApiSchema.NoContent,
|
|
348
|
+
error: [SessionNotFoundError, ServiceUnavailableError],
|
|
349
|
+
})
|
|
350
|
+
.middleware(sessionLocationMiddleware)
|
|
351
|
+
.annotateMerge(OpenApi.annotations({
|
|
352
|
+
identifier: "v2.session.wait",
|
|
353
|
+
summary: "Wait for session",
|
|
354
|
+
description: "Wait for a session agent loop to become idle.",
|
|
355
|
+
})))
|
|
356
|
+
.add(HttpApiEndpoint.post("session.revert.stage", "/api/session/:sessionID/revert/stage", {
|
|
357
|
+
params: { sessionID: Session.ID },
|
|
358
|
+
payload: Schema.Struct({ messageID: SessionMessage.ID, files: Schema.Boolean.pipe(Schema.optional) }),
|
|
359
|
+
success: Schema.Struct({ data: Session.Revert }),
|
|
360
|
+
error: [MessageNotFoundError, SessionNotFoundError, SessionBusyError, UnknownError],
|
|
361
|
+
})
|
|
362
|
+
.middleware(sessionLocationMiddleware)
|
|
363
|
+
.annotateMerge(OpenApi.annotations({
|
|
364
|
+
identifier: "v2.session.revert.stage",
|
|
365
|
+
summary: "Stage session revert",
|
|
366
|
+
description: "Stage or move a reversible session boundary and optionally apply its file changes.",
|
|
367
|
+
})))
|
|
368
|
+
.add(HttpApiEndpoint.post("session.revert.clear", "/api/session/:sessionID/revert/clear", {
|
|
369
|
+
params: { sessionID: Session.ID },
|
|
370
|
+
success: HttpApiSchema.NoContent,
|
|
371
|
+
error: [SessionNotFoundError, SessionBusyError, UnknownError],
|
|
372
|
+
})
|
|
373
|
+
.middleware(sessionLocationMiddleware)
|
|
374
|
+
.annotateMerge(OpenApi.annotations({ identifier: "v2.session.revert.clear", summary: "Clear staged revert" })))
|
|
375
|
+
.add(HttpApiEndpoint.post("session.revert.commit", "/api/session/:sessionID/revert/commit", {
|
|
376
|
+
params: { sessionID: Session.ID },
|
|
377
|
+
success: HttpApiSchema.NoContent,
|
|
378
|
+
error: [SessionNotFoundError, SessionBusyError],
|
|
379
|
+
})
|
|
380
|
+
.middleware(sessionLocationMiddleware)
|
|
381
|
+
.annotateMerge(OpenApi.annotations({ identifier: "v2.session.revert.commit", summary: "Commit staged revert" })))
|
|
382
|
+
.add(HttpApiEndpoint.get("session.context", "/api/session/:sessionID/context", {
|
|
383
|
+
params: { sessionID: Session.ID },
|
|
384
|
+
success: Schema.Struct({ data: Schema.Array(SessionMessage.Info) }),
|
|
385
|
+
error: [SessionNotFoundError, UnknownError],
|
|
386
|
+
}).annotateMerge(OpenApi.annotations({
|
|
387
|
+
identifier: "v2.session.context",
|
|
388
|
+
summary: "Get session context",
|
|
389
|
+
description: "Retrieve the active context messages for a session (all messages after the last compaction).",
|
|
390
|
+
})))
|
|
391
|
+
.add(HttpApiEndpoint.get("session.inbox.list", "/api/session/:sessionID/inbox", {
|
|
392
|
+
params: { sessionID: Session.ID },
|
|
393
|
+
success: Schema.Struct({ data: Schema.Array(SessionInbox.Info) }),
|
|
394
|
+
error: SessionNotFoundError,
|
|
395
|
+
}).annotateMerge(OpenApi.annotations({
|
|
396
|
+
identifier: "v2.session.inbox.list",
|
|
397
|
+
summary: "List session inbox",
|
|
398
|
+
description: "List durable enqueued session work not yet delivered, ordered by enqueue sequence. Includes user, synthetic, compaction, and move items.",
|
|
399
|
+
})))
|
|
400
|
+
.add(HttpApiEndpoint.delete("session.inbox.cancel", "/api/session/:sessionID/inbox/:inboxID", {
|
|
401
|
+
params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
|
|
402
|
+
success: HttpApiSchema.NoContent,
|
|
403
|
+
error: [ConflictError, SessionNotFoundError],
|
|
404
|
+
}).annotateMerge(OpenApi.annotations({
|
|
405
|
+
identifier: "v2.session.inbox.cancel",
|
|
406
|
+
summary: "Cancel inbox input",
|
|
407
|
+
description: "Cancel an inbox item that has not yet been delivered.",
|
|
408
|
+
})))
|
|
409
|
+
.add(HttpApiEndpoint.post("session.inbox.steer", "/api/session/:sessionID/inbox/:inboxID/steer", {
|
|
410
|
+
params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
|
|
411
|
+
success: HttpApiSchema.NoContent,
|
|
412
|
+
error: [ConflictError, SessionNotFoundError],
|
|
413
|
+
}).annotateMerge(OpenApi.annotations({
|
|
414
|
+
identifier: "v2.session.inbox.steer",
|
|
415
|
+
summary: "Steer queued item",
|
|
416
|
+
description: "Change a queued inbox item to steer delivery and wake session execution.",
|
|
417
|
+
})))
|
|
418
|
+
.add(HttpApiEndpoint.post("session.inbox.queue", "/api/session/:sessionID/inbox/:inboxID/queue", {
|
|
419
|
+
params: { sessionID: Session.ID, inboxID: SessionMessage.ID },
|
|
420
|
+
success: HttpApiSchema.NoContent,
|
|
421
|
+
error: [ConflictError, SessionNotFoundError],
|
|
422
|
+
}).annotateMerge(OpenApi.annotations({
|
|
423
|
+
identifier: "v2.session.inbox.queue",
|
|
424
|
+
summary: "Queue steered item",
|
|
425
|
+
description: "Change a steered inbox item to queued delivery.",
|
|
426
|
+
})))
|
|
427
|
+
.add(HttpApiEndpoint.get("session.instructions.entry.list", "/api/session/:sessionID/instructions/entries", {
|
|
428
|
+
params: { sessionID: Session.ID },
|
|
429
|
+
success: Schema.Struct({ data: Schema.Array(InstructionEntry.Info) }),
|
|
430
|
+
error: SessionNotFoundError,
|
|
431
|
+
})
|
|
432
|
+
.middleware(sessionLocationMiddleware)
|
|
433
|
+
.annotateMerge(OpenApi.annotations({
|
|
434
|
+
identifier: "v2.session.instructions.entry.list",
|
|
435
|
+
summary: "List instruction entries",
|
|
436
|
+
description: "List API-managed instruction entries attached to the session.",
|
|
437
|
+
})))
|
|
438
|
+
.add(HttpApiEndpoint.put("session.instructions.entry.put", "/api/session/:sessionID/instructions/entries/:key", {
|
|
439
|
+
params: { sessionID: Session.ID, key: InstructionEntry.Key },
|
|
440
|
+
payload: Schema.Struct({ value: Schema.Json }),
|
|
441
|
+
success: HttpApiSchema.NoContent,
|
|
442
|
+
error: [SessionNotFoundError, InstructionEntry.ValueTooLargeError],
|
|
443
|
+
})
|
|
444
|
+
.middleware(sessionLocationMiddleware)
|
|
445
|
+
.annotateMerge(OpenApi.annotations({
|
|
446
|
+
identifier: "v2.session.instructions.entry.put",
|
|
447
|
+
summary: "Put instruction entry",
|
|
448
|
+
description: "Attach or replace one durable instruction entry. Changes announce as updates at the next step boundary.",
|
|
449
|
+
})))
|
|
450
|
+
.add(HttpApiEndpoint.delete("session.instructions.entry.remove", "/api/session/:sessionID/instructions/entries/:key", {
|
|
451
|
+
params: { sessionID: Session.ID, key: InstructionEntry.Key },
|
|
452
|
+
success: HttpApiSchema.NoContent,
|
|
453
|
+
error: SessionNotFoundError,
|
|
454
|
+
})
|
|
455
|
+
.middleware(sessionLocationMiddleware)
|
|
456
|
+
.annotateMerge(OpenApi.annotations({
|
|
457
|
+
identifier: "v2.session.instructions.entry.remove",
|
|
458
|
+
summary: "Remove instruction entry",
|
|
459
|
+
description: "Remove one instruction entry; the removal is announced to the model at the next step boundary.",
|
|
460
|
+
})))
|
|
461
|
+
.add(HttpApiEndpoint.post("session.generate", "/api/session/:sessionID/generate", {
|
|
462
|
+
params: { sessionID: Session.ID },
|
|
463
|
+
payload: Schema.Struct({ prompt: Schema.String }),
|
|
464
|
+
success: Schema.Struct({
|
|
465
|
+
data: Schema.Struct({ text: Schema.String }),
|
|
466
|
+
}).annotate({ identifier: "SessionGenerateResponse" }),
|
|
467
|
+
error: [SessionNotFoundError, ServiceUnavailableError],
|
|
468
|
+
})
|
|
469
|
+
.middleware(sessionLocationMiddleware)
|
|
470
|
+
.annotateMerge(OpenApi.annotations({
|
|
471
|
+
identifier: "v2.session.generate",
|
|
472
|
+
summary: "Generate text from session context",
|
|
473
|
+
description: "Generate transient text from the current session context without mutating session history.",
|
|
474
|
+
})))
|
|
475
|
+
.add(HttpApiEndpoint.get("session.log", "/api/experimental/session/:sessionID/log", {
|
|
476
|
+
params: { sessionID: Session.ID },
|
|
477
|
+
query: {
|
|
478
|
+
after: Schema.NumberFromString.pipe(Schema.decodeTo(Event.Seq), Schema.optional),
|
|
479
|
+
follow: BooleanFromString.pipe(Schema.optional),
|
|
480
|
+
},
|
|
481
|
+
success: HttpApiSchema.StreamSse({
|
|
482
|
+
data: Schema.Union([SessionEvent.Durable, EventLog.Synced]).annotate({ identifier: "SessionLogItem" }),
|
|
483
|
+
}),
|
|
484
|
+
error: SessionNotFoundError,
|
|
485
|
+
}).annotateMerge(OpenApi.annotations({
|
|
486
|
+
identifier: "v2.session.log",
|
|
487
|
+
summary: "Read the session log",
|
|
488
|
+
description: "Experimental durable session event log. Reads events after an exclusive aggregate sequence and continues with live events when follow=true.",
|
|
489
|
+
})))
|
|
490
|
+
.add(HttpApiEndpoint.post("session.interrupt", "/api/session/:sessionID/interrupt", {
|
|
491
|
+
params: { sessionID: Session.ID },
|
|
492
|
+
query: { continue: BooleanFromString.pipe(Schema.optional) },
|
|
493
|
+
success: Schema.Struct({
|
|
494
|
+
interrupted: Schema.Boolean.annotate({
|
|
495
|
+
description: "Whether an active execution owned by this OpenCode process was interrupted.",
|
|
496
|
+
}),
|
|
497
|
+
}).annotate({ identifier: "SessionInterruptResponse" }),
|
|
498
|
+
error: SessionNotFoundError,
|
|
499
|
+
})
|
|
500
|
+
.middleware(sessionLocationMiddleware)
|
|
501
|
+
.annotateMerge(OpenApi.annotations({
|
|
502
|
+
identifier: "v2.session.interrupt",
|
|
503
|
+
summary: "Interrupt session execution",
|
|
504
|
+
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.",
|
|
505
|
+
})))
|
|
506
|
+
.add(HttpApiEndpoint.post("session.background", "/api/session/:sessionID/background", {
|
|
507
|
+
params: { sessionID: Session.ID },
|
|
508
|
+
success: HttpApiSchema.NoContent,
|
|
509
|
+
error: SessionNotFoundError,
|
|
510
|
+
})
|
|
511
|
+
.middleware(sessionLocationMiddleware)
|
|
512
|
+
.annotateMerge(OpenApi.annotations({
|
|
513
|
+
identifier: "v2.session.background",
|
|
514
|
+
summary: "Background blocking session tools",
|
|
515
|
+
description: "Move active foreground backgroundable tools for this session into background observation. Idle requests are a no-op.",
|
|
516
|
+
})))
|
|
517
|
+
.add(HttpApiEndpoint.get("session.message", "/api/session/:sessionID/message/:messageID", {
|
|
518
|
+
params: { sessionID: Session.ID, messageID: SessionMessage.ID },
|
|
519
|
+
success: Schema.Struct({ data: SessionMessage.Info }),
|
|
520
|
+
error: [SessionNotFoundError, MessageNotFoundError],
|
|
521
|
+
}).annotateMerge(OpenApi.annotations({
|
|
522
|
+
identifier: "v2.session.message",
|
|
523
|
+
summary: "Get session message",
|
|
524
|
+
description: "Retrieve one projected message owned by the Session.",
|
|
525
|
+
})))
|
|
526
|
+
.add(HttpApiEndpoint.put("session.environment", "/api/session/:sessionID/environment", {
|
|
527
|
+
params: { sessionID: Session.ID },
|
|
528
|
+
payload: Schema.Struct({ variables: Schema.Record(Schema.String, Schema.String) }),
|
|
529
|
+
success: HttpApiSchema.NoContent,
|
|
530
|
+
error: SessionNotFoundError,
|
|
531
|
+
}).annotateMerge(OpenApi.annotations({
|
|
532
|
+
identifier: "v2.session.environment",
|
|
533
|
+
summary: "Set session environment",
|
|
534
|
+
description: "Replace the process environment used by local shell commands for this session.",
|
|
535
|
+
})))
|
|
536
|
+
.add(HttpApiEndpoint.post("session.view", "/api/session/:sessionID/view", {
|
|
537
|
+
params: { sessionID: Session.ID },
|
|
538
|
+
payload: Schema.Struct({ idle: NonNegativeInt }),
|
|
539
|
+
success: HttpApiSchema.NoContent,
|
|
540
|
+
error: SessionNotFoundError,
|
|
541
|
+
}).annotateMerge(OpenApi.annotations({
|
|
542
|
+
identifier: "v2.session.view",
|
|
543
|
+
summary: "View session",
|
|
544
|
+
description: "Mark the idle transition observed by the viewer as viewed.",
|
|
545
|
+
})))
|
|
546
|
+
.annotateMerge(OpenApi.annotations({
|
|
547
|
+
title: "session",
|
|
548
|
+
description: "Experimental session routes.",
|
|
549
|
+
}));
|