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