@sealant/api-contracts-next 0.0.0-next.0 → 0.39.0-next.682

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 (40) hide show
  1. package/LICENSE +202 -0
  2. package/dist/core-api/access-tokens.d.ts +126 -0
  3. package/dist/core-api/access-tokens.js +72 -0
  4. package/dist/core-api/budgets.d.ts +15 -0
  5. package/dist/core-api/budgets.js +13 -0
  6. package/dist/core-api/connected-accounts.d.ts +226 -0
  7. package/dist/core-api/connected-accounts.js +128 -0
  8. package/dist/core-api/control-plane.d.ts +1318 -0
  9. package/dist/core-api/control-plane.js +31 -0
  10. package/dist/core-api/github.d.ts +261 -0
  11. package/dist/core-api/github.js +153 -0
  12. package/dist/core-api/inference.d.ts +223 -0
  13. package/dist/core-api/inference.js +139 -0
  14. package/dist/core-api/oci-names.d.ts +21 -0
  15. package/dist/core-api/oci-names.js +44 -0
  16. package/dist/core-api/packages.d.ts +179 -0
  17. package/dist/core-api/packages.js +60 -0
  18. package/dist/core-api/profiles.d.ts +188 -0
  19. package/dist/core-api/profiles.js +88 -0
  20. package/dist/core-api/record-events.d.ts +209 -0
  21. package/dist/core-api/record-events.js +193 -0
  22. package/dist/core-api/registries.d.ts +106 -0
  23. package/dist/core-api/registries.js +85 -0
  24. package/dist/core-api/runs.d.ts +516 -0
  25. package/dist/core-api/runs.js +284 -0
  26. package/dist/core-api/sessions.d.ts +390 -0
  27. package/dist/core-api/sessions.js +264 -0
  28. package/dist/core-api/ssh-keys.d.ts +120 -0
  29. package/dist/core-api/ssh-keys.js +108 -0
  30. package/dist/core-api/system.d.ts +51 -0
  31. package/dist/core-api/system.js +41 -0
  32. package/dist/core-api/users.d.ts +71 -0
  33. package/dist/core-api/users.js +48 -0
  34. package/dist/core-api/workspaces.d.ts +1880 -0
  35. package/dist/core-api/workspaces.js +980 -0
  36. package/dist/index.d.ts +18 -0
  37. package/dist/index.js +18 -0
  38. package/dist/workspace-environment.d.ts +124 -0
  39. package/dist/workspace-environment.js +259 -0
  40. package/package.json +31 -9
@@ -0,0 +1,390 @@
1
+ /**
2
+ * Interactive session wire contracts — first-class PTY sessions over a live workspace.
3
+ *
4
+ * A session is a daemon-owned PTY (it survives control-connection drops); the control plane holds
5
+ * the durable row and drives every verb over short-lived per-daemon-request connections, so any
6
+ * API instance can serve any session. Output is DURABLE and SEQUENCE-KEYED: the session's run
7
+ * record ingests the PTY byte stream (redacted, byte-exact), and the output endpoints serve it by
8
+ * sequence range — which is what makes detach/reattach and byte-exact history replay work. The
9
+ * live tail is served as SSE by `GET /v1/sessions/:sessionId/output/stream` (implemented as a raw
10
+ * streaming route on the same server, outside this schema-derived contract).
11
+ *
12
+ * AUTHORIZATION: the session surface enforces scoped bearer tokens when one is presented —
13
+ * `session:read` (status/output), `session:input` (input/resize/signal), `workspace:exec`
14
+ * (create/close). Without a bearer token the pre-auth owner model applies unchanged.
15
+ */
16
+ import { Schema } from "effect";
17
+ import { HttpApiEndpoint, HttpApiGroup } from "effect/unstable/httpapi";
18
+ export declare const sessionStatusSchema: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
19
+ export type SessionStatusWire = typeof sessionStatusSchema.Type;
20
+ export declare const sessionAuthorizationHeadersSchema: Schema.Struct<{
21
+ readonly authorization: Schema.optional<Schema.String>;
22
+ }>;
23
+ export type SessionAuthorizationHeaders = typeof sessionAuthorizationHeadersSchema.Type;
24
+ /**
25
+ * How a session's leader is wired. `pty` (default) allocates a pseudoterminal — interactive
26
+ * shells and TUIs. `pipe` gives the leader plain stdio pipes and no tty — the shape for processes
27
+ * that speak a byte protocol over stdin/stdout (JSON-RPC / NDJSON servers): stdout is the recorded,
28
+ * attachable output, stderr is recorded as diagnostics only, input feeds stdin, and resize is
29
+ * rejected.
30
+ */
31
+ export declare const sessionModeSchema: Schema.Literals<readonly ["pty", "pipe"]>;
32
+ export type SessionMode = typeof sessionModeSchema.Type;
33
+ export declare const createSessionRequestSchema: Schema.Struct<{
34
+ readonly workspaceId: Schema.String;
35
+ readonly ownerUserId: Schema.String;
36
+ /** argv[0] is the program the session runs; the rest its arguments. */
37
+ readonly argv: Schema.$Array<Schema.String>;
38
+ /** Working directory inside the workspace (defaults to the workspace working directory). */
39
+ readonly cwd: Schema.optional<Schema.String>;
40
+ /** Extra environment for the PTY process (values are NOT secrets — use credentials for those). */
41
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
42
+ readonly cols: Schema.optional<Schema.Int>;
43
+ readonly rows: Schema.optional<Schema.Int>;
44
+ readonly term: Schema.optional<Schema.String>;
45
+ /** Leader wiring; defaults to `pty`. `cols`/`rows`/`term` are ignored for `pipe`. */
46
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
47
+ /** Opaque caller correlation bag: stored verbatim, echoed on reads, no platform semantics. */
48
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
49
+ }>;
50
+ export type CreateSessionRequest = typeof createSessionRequestSchema.Type;
51
+ export declare const sessionSchema: Schema.Struct<{
52
+ readonly sessionId: Schema.String;
53
+ readonly workspaceId: Schema.String;
54
+ /** The interactive run recording this session; its record is the durable evidence. */
55
+ readonly runId: Schema.String;
56
+ readonly ownerUserId: Schema.String;
57
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
58
+ readonly argv: Schema.$Array<Schema.String>;
59
+ readonly cwd: Schema.optional<Schema.String>;
60
+ readonly cols: Schema.Number;
61
+ readonly rows: Schema.Number;
62
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
63
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
64
+ readonly exitCode: Schema.optional<Schema.Number>;
65
+ readonly exitSignal: Schema.optional<Schema.Number>;
66
+ readonly errorMessage: Schema.optional<Schema.String>;
67
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
68
+ /**
69
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
70
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
71
+ * where a previous reader stopped.
72
+ */
73
+ readonly outputHighWater: Schema.String;
74
+ readonly createdAt: Schema.String;
75
+ readonly endedAt: Schema.optional<Schema.String>;
76
+ }>;
77
+ export type SessionWire = typeof sessionSchema.Type;
78
+ export declare const listSessionsQuerySchema: Schema.Struct<{
79
+ readonly ownerUserId: Schema.String;
80
+ readonly workspaceId: Schema.optional<Schema.String>;
81
+ readonly status: Schema.optional<Schema.Literals<readonly ["starting", "running", "exited", "failed"]>>;
82
+ readonly limit: Schema.optional<Schema.String>;
83
+ }>;
84
+ export type ListSessionsQuery = typeof listSessionsQuerySchema.Type;
85
+ export declare const listSessionsResponseSchema: Schema.Struct<{
86
+ readonly items: Schema.$Array<Schema.Struct<{
87
+ readonly sessionId: Schema.String;
88
+ readonly workspaceId: Schema.String;
89
+ /** The interactive run recording this session; its record is the durable evidence. */
90
+ readonly runId: Schema.String;
91
+ readonly ownerUserId: Schema.String;
92
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
93
+ readonly argv: Schema.$Array<Schema.String>;
94
+ readonly cwd: Schema.optional<Schema.String>;
95
+ readonly cols: Schema.Number;
96
+ readonly rows: Schema.Number;
97
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
98
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
99
+ readonly exitCode: Schema.optional<Schema.Number>;
100
+ readonly exitSignal: Schema.optional<Schema.Number>;
101
+ readonly errorMessage: Schema.optional<Schema.String>;
102
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
103
+ /**
104
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
105
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
106
+ * where a previous reader stopped.
107
+ */
108
+ readonly outputHighWater: Schema.String;
109
+ readonly createdAt: Schema.String;
110
+ readonly endedAt: Schema.optional<Schema.String>;
111
+ }>>;
112
+ }>;
113
+ export type ListSessionsResponse = typeof listSessionsResponseSchema.Type;
114
+ export declare const sessionInputRequestSchema: Schema.Struct<{
115
+ readonly ownerUserId: Schema.optional<Schema.String>;
116
+ /** Base64-encoded keystrokes (bytes, not text — binary-safe). */
117
+ readonly dataBase64: Schema.String;
118
+ }>;
119
+ export type SessionInputRequest = typeof sessionInputRequestSchema.Type;
120
+ export declare const sessionResizeRequestSchema: Schema.Struct<{
121
+ readonly ownerUserId: Schema.optional<Schema.String>;
122
+ readonly cols: Schema.Int;
123
+ readonly rows: Schema.Int;
124
+ }>;
125
+ export type SessionResizeRequest = typeof sessionResizeRequestSchema.Type;
126
+ export declare const sessionSignalRequestSchema: Schema.Struct<{
127
+ readonly ownerUserId: Schema.optional<Schema.String>;
128
+ /** POSIX signal number (e.g. 2 = SIGINT, 15 = SIGTERM). */
129
+ readonly signal: Schema.Int;
130
+ }>;
131
+ export type SessionSignalRequest = typeof sessionSignalRequestSchema.Type;
132
+ export declare const closeSessionRequestSchema: Schema.Struct<{
133
+ readonly ownerUserId: Schema.optional<Schema.String>;
134
+ }>;
135
+ export type CloseSessionRequest = typeof closeSessionRequestSchema.Type;
136
+ /**
137
+ * Byte-exact session output by sequence range. `from` is INCLUSIVE (omit = from the beginning);
138
+ * chunks carry their sequence so a disconnected reader resumes with `lastSequence + 1`. Output is
139
+ * as-recorded: redacted upstream by the daemon, PTY output stream only.
140
+ */
141
+ export declare const getSessionOutputQuerySchema: Schema.Struct<{
142
+ readonly ownerUserId: Schema.optional<Schema.String>;
143
+ readonly from: Schema.optional<Schema.String>;
144
+ readonly limit: Schema.optional<Schema.String>;
145
+ }>;
146
+ export type GetSessionOutputQuery = typeof getSessionOutputQuerySchema.Type;
147
+ export declare const sessionOutputChunkSchema: Schema.Struct<{
148
+ readonly sequence: Schema.String;
149
+ /** Base64-encoded PTY output bytes, exactly as recorded. */
150
+ readonly dataBase64: Schema.String;
151
+ }>;
152
+ export type SessionOutputChunk = typeof sessionOutputChunkSchema.Type;
153
+ export declare const sessionOutputResponseSchema: Schema.Struct<{
154
+ readonly sessionId: Schema.String;
155
+ readonly chunks: Schema.$Array<Schema.Struct<{
156
+ readonly sequence: Schema.String;
157
+ /** Base64-encoded PTY output bytes, exactly as recorded. */
158
+ readonly dataBase64: Schema.String;
159
+ }>>;
160
+ /** The cursor to pass as `from` to continue after this page. */
161
+ readonly nextFrom: Schema.String;
162
+ /** Session status at read time, so pollers can stop when the session settles. */
163
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
164
+ }>;
165
+ export type SessionOutputResponse = typeof sessionOutputResponseSchema.Type;
166
+ declare const SessionBadRequestError_base: Schema.Class<SessionBadRequestError, Schema.TaggedStruct<"SessionBadRequestError", {
167
+ readonly message: Schema.String;
168
+ }>, import("effect/Cause").YieldableError>;
169
+ export declare class SessionBadRequestError extends SessionBadRequestError_base {
170
+ }
171
+ declare const SessionUnauthorizedError_base: Schema.Class<SessionUnauthorizedError, Schema.TaggedStruct<"SessionUnauthorizedError", {
172
+ readonly message: Schema.String;
173
+ }>, import("effect/Cause").YieldableError>;
174
+ export declare class SessionUnauthorizedError extends SessionUnauthorizedError_base {
175
+ }
176
+ declare const SessionForbiddenError_base: Schema.Class<SessionForbiddenError, Schema.TaggedStruct<"SessionForbiddenError", {
177
+ readonly message: Schema.String;
178
+ }>, import("effect/Cause").YieldableError>;
179
+ export declare class SessionForbiddenError extends SessionForbiddenError_base {
180
+ }
181
+ declare const SessionNotFoundError_base: Schema.Class<SessionNotFoundError, Schema.TaggedStruct<"SessionNotFoundError", {
182
+ readonly message: Schema.String;
183
+ }>, import("effect/Cause").YieldableError>;
184
+ export declare class SessionNotFoundError extends SessionNotFoundError_base {
185
+ }
186
+ declare const SessionConflictError_base: Schema.Class<SessionConflictError, Schema.TaggedStruct<"SessionConflictError", {
187
+ readonly message: Schema.String;
188
+ }>, import("effect/Cause").YieldableError>;
189
+ export declare class SessionConflictError extends SessionConflictError_base {
190
+ }
191
+ declare const SessionBadGatewayError_base: Schema.Class<SessionBadGatewayError, Schema.TaggedStruct<"SessionBadGatewayError", {
192
+ readonly message: Schema.String;
193
+ }>, import("effect/Cause").YieldableError>;
194
+ export declare class SessionBadGatewayError extends SessionBadGatewayError_base {
195
+ }
196
+ declare const SessionInternalServerError_base: Schema.Class<SessionInternalServerError, Schema.TaggedStruct<"SessionInternalServerError", {
197
+ readonly message: Schema.String;
198
+ }>, import("effect/Cause").YieldableError>;
199
+ export declare class SessionInternalServerError extends SessionInternalServerError_base {
200
+ }
201
+ export declare const SessionsGroup: HttpApiGroup.HttpApiGroup<"sessions", HttpApiEndpoint.HttpApiEndpoint<"closeSession", "POST", "/:sessionId/close", HttpApiEndpoint.StringTree<Schema.Struct<{
202
+ readonly sessionId: Schema.String;
203
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
204
+ readonly ownerUserId: Schema.optional<Schema.String>;
205
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
206
+ readonly authorization: Schema.optional<Schema.String>;
207
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
208
+ readonly sessionId: Schema.String;
209
+ readonly workspaceId: Schema.String;
210
+ /** The interactive run recording this session; its record is the durable evidence. */
211
+ readonly runId: Schema.String;
212
+ readonly ownerUserId: Schema.String;
213
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
214
+ readonly argv: Schema.$Array<Schema.String>;
215
+ readonly cwd: Schema.optional<Schema.String>;
216
+ readonly cols: Schema.Number;
217
+ readonly rows: Schema.Number;
218
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
219
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
220
+ readonly exitCode: Schema.optional<Schema.Number>;
221
+ readonly exitSignal: Schema.optional<Schema.Number>;
222
+ readonly errorMessage: Schema.optional<Schema.String>;
223
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
224
+ /**
225
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
226
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
227
+ * where a previous reader stopped.
228
+ */
229
+ readonly outputHighWater: Schema.String;
230
+ readonly createdAt: Schema.String;
231
+ readonly endedAt: Schema.optional<Schema.String>;
232
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionBadGatewayError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"createSession", "POST", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
233
+ readonly workspaceId: Schema.String;
234
+ readonly ownerUserId: Schema.String;
235
+ /** argv[0] is the program the session runs; the rest its arguments. */
236
+ readonly argv: Schema.$Array<Schema.String>;
237
+ /** Working directory inside the workspace (defaults to the workspace working directory). */
238
+ readonly cwd: Schema.optional<Schema.String>;
239
+ /** Extra environment for the PTY process (values are NOT secrets — use credentials for those). */
240
+ readonly env: Schema.optional<Schema.$Record<Schema.String, Schema.String>>;
241
+ readonly cols: Schema.optional<Schema.Int>;
242
+ readonly rows: Schema.optional<Schema.Int>;
243
+ readonly term: Schema.optional<Schema.String>;
244
+ /** Leader wiring; defaults to `pty`. `cols`/`rows`/`term` are ignored for `pipe`. */
245
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
246
+ /** Opaque caller correlation bag: stored verbatim, echoed on reads, no platform semantics. */
247
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
248
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
249
+ readonly authorization: Schema.optional<Schema.String>;
250
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
251
+ readonly sessionId: Schema.String;
252
+ readonly workspaceId: Schema.String;
253
+ /** The interactive run recording this session; its record is the durable evidence. */
254
+ readonly runId: Schema.String;
255
+ readonly ownerUserId: Schema.String;
256
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
257
+ readonly argv: Schema.$Array<Schema.String>;
258
+ readonly cwd: Schema.optional<Schema.String>;
259
+ readonly cols: Schema.Number;
260
+ readonly rows: Schema.Number;
261
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
262
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
263
+ readonly exitCode: Schema.optional<Schema.Number>;
264
+ readonly exitSignal: Schema.optional<Schema.Number>;
265
+ readonly errorMessage: Schema.optional<Schema.String>;
266
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
267
+ /**
268
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
269
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
270
+ * where a previous reader stopped.
271
+ */
272
+ readonly outputHighWater: Schema.String;
273
+ readonly createdAt: Schema.String;
274
+ readonly endedAt: Schema.optional<Schema.String>;
275
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionConflictError | typeof SessionBadGatewayError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getSession", "GET", "/:sessionId", HttpApiEndpoint.StringTree<Schema.Struct<{
276
+ readonly sessionId: Schema.String;
277
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
278
+ readonly ownerUserId: Schema.optional<Schema.String>;
279
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<Schema.Struct<{
280
+ readonly authorization: Schema.optional<Schema.String>;
281
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
282
+ readonly sessionId: Schema.String;
283
+ readonly workspaceId: Schema.String;
284
+ /** The interactive run recording this session; its record is the durable evidence. */
285
+ readonly runId: Schema.String;
286
+ readonly ownerUserId: Schema.String;
287
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
288
+ readonly argv: Schema.$Array<Schema.String>;
289
+ readonly cwd: Schema.optional<Schema.String>;
290
+ readonly cols: Schema.Number;
291
+ readonly rows: Schema.Number;
292
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
293
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
294
+ readonly exitCode: Schema.optional<Schema.Number>;
295
+ readonly exitSignal: Schema.optional<Schema.Number>;
296
+ readonly errorMessage: Schema.optional<Schema.String>;
297
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
298
+ /**
299
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
300
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
301
+ * where a previous reader stopped.
302
+ */
303
+ readonly outputHighWater: Schema.String;
304
+ readonly createdAt: Schema.String;
305
+ readonly endedAt: Schema.optional<Schema.String>;
306
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"getSessionOutput", "GET", "/:sessionId/output", HttpApiEndpoint.StringTree<Schema.Struct<{
307
+ readonly sessionId: Schema.String;
308
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
309
+ readonly ownerUserId: Schema.optional<Schema.String>;
310
+ readonly from: Schema.optional<Schema.String>;
311
+ readonly limit: Schema.optional<Schema.String>;
312
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<Schema.Struct<{
313
+ readonly authorization: Schema.optional<Schema.String>;
314
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
315
+ readonly sessionId: Schema.String;
316
+ readonly chunks: Schema.$Array<Schema.Struct<{
317
+ readonly sequence: Schema.String;
318
+ /** Base64-encoded PTY output bytes, exactly as recorded. */
319
+ readonly dataBase64: Schema.String;
320
+ }>>;
321
+ /** The cursor to pass as `from` to continue after this page. */
322
+ readonly nextFrom: Schema.String;
323
+ /** Session status at read time, so pollers can stop when the session settles. */
324
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
325
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"listSessions", "GET", "/", HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<Schema.Struct<{
326
+ readonly ownerUserId: Schema.String;
327
+ readonly workspaceId: Schema.optional<Schema.String>;
328
+ readonly status: Schema.optional<Schema.Literals<readonly ["starting", "running", "exited", "failed"]>>;
329
+ readonly limit: Schema.optional<Schema.String>;
330
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.StringTree<Schema.Struct<{
331
+ readonly authorization: Schema.optional<Schema.String>;
332
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
333
+ readonly items: Schema.$Array<Schema.Struct<{
334
+ readonly sessionId: Schema.String;
335
+ readonly workspaceId: Schema.String;
336
+ /** The interactive run recording this session; its record is the durable evidence. */
337
+ readonly runId: Schema.String;
338
+ readonly ownerUserId: Schema.String;
339
+ readonly status: Schema.Literals<readonly ["starting", "running", "exited", "failed"]>;
340
+ readonly argv: Schema.$Array<Schema.String>;
341
+ readonly cwd: Schema.optional<Schema.String>;
342
+ readonly cols: Schema.Number;
343
+ readonly rows: Schema.Number;
344
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
345
+ readonly mode: Schema.optional<Schema.Literals<readonly ["pty", "pipe"]>>;
346
+ readonly exitCode: Schema.optional<Schema.Number>;
347
+ readonly exitSignal: Schema.optional<Schema.Number>;
348
+ readonly errorMessage: Schema.optional<Schema.String>;
349
+ readonly metadata: Schema.optional<Schema.$Record<Schema.String, Schema.Unknown>>;
350
+ /**
351
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
352
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
353
+ * where a previous reader stopped.
354
+ */
355
+ readonly outputHighWater: Schema.String;
356
+ readonly createdAt: Schema.String;
357
+ readonly endedAt: Schema.optional<Schema.String>;
358
+ }>>;
359
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"resizeSession", "POST", "/:sessionId/resize", HttpApiEndpoint.StringTree<Schema.Struct<{
360
+ readonly sessionId: Schema.String;
361
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
362
+ readonly ownerUserId: Schema.optional<Schema.String>;
363
+ readonly cols: Schema.Int;
364
+ readonly rows: Schema.Int;
365
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
366
+ readonly authorization: Schema.optional<Schema.String>;
367
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
368
+ readonly ok: Schema.Boolean;
369
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionConflictError | typeof SessionBadGatewayError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"sendSessionInput", "POST", "/:sessionId/input", HttpApiEndpoint.StringTree<Schema.Struct<{
370
+ readonly sessionId: Schema.String;
371
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
372
+ readonly ownerUserId: Schema.optional<Schema.String>;
373
+ /** Base64-encoded keystrokes (bytes, not text — binary-safe). */
374
+ readonly dataBase64: Schema.String;
375
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
376
+ readonly authorization: Schema.optional<Schema.String>;
377
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
378
+ readonly ok: Schema.Boolean;
379
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionConflictError | typeof SessionBadGatewayError | typeof SessionInternalServerError>, never, never> | HttpApiEndpoint.HttpApiEndpoint<"signalSession", "POST", "/:sessionId/signal", HttpApiEndpoint.StringTree<Schema.Struct<{
380
+ readonly sessionId: Schema.String;
381
+ }>>, HttpApiEndpoint.StringTree<never>, HttpApiEndpoint.Json<Schema.Struct<{
382
+ readonly ownerUserId: Schema.optional<Schema.String>;
383
+ /** POSIX signal number (e.g. 2 = SIGINT, 15 = SIGTERM). */
384
+ readonly signal: Schema.Int;
385
+ }>>, HttpApiEndpoint.StringTree<Schema.Struct<{
386
+ readonly authorization: Schema.optional<Schema.String>;
387
+ }>>, HttpApiEndpoint.Json<Schema.Struct<{
388
+ readonly ok: Schema.Boolean;
389
+ }>>, HttpApiEndpoint.Json<typeof SessionBadRequestError | typeof SessionUnauthorizedError | typeof SessionForbiddenError | typeof SessionNotFoundError | typeof SessionConflictError | typeof SessionBadGatewayError | typeof SessionInternalServerError>, never, never>, false>;
390
+ export {};
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Interactive session wire contracts — first-class PTY sessions over a live workspace.
3
+ *
4
+ * A session is a daemon-owned PTY (it survives control-connection drops); the control plane holds
5
+ * the durable row and drives every verb over short-lived per-daemon-request connections, so any
6
+ * API instance can serve any session. Output is DURABLE and SEQUENCE-KEYED: the session's run
7
+ * record ingests the PTY byte stream (redacted, byte-exact), and the output endpoints serve it by
8
+ * sequence range — which is what makes detach/reattach and byte-exact history replay work. The
9
+ * live tail is served as SSE by `GET /v1/sessions/:sessionId/output/stream` (implemented as a raw
10
+ * streaming route on the same server, outside this schema-derived contract).
11
+ *
12
+ * AUTHORIZATION: the session surface enforces scoped bearer tokens when one is presented —
13
+ * `session:read` (status/output), `session:input` (input/resize/signal), `workspace:exec`
14
+ * (create/close). Without a bearer token the pre-auth owner model applies unchanged.
15
+ */
16
+ import { Schema } from "effect";
17
+ import { HttpApiEndpoint, HttpApiGroup, HttpApiSchema, OpenApi } from "effect/unstable/httpapi";
18
+ const NonEmptyString = Schema.String.check(Schema.isNonEmpty(), Schema.isTrimmed());
19
+ export const sessionStatusSchema = Schema.Literals(["starting", "running", "exited", "failed"]);
20
+ export const sessionAuthorizationHeadersSchema = Schema.Struct({
21
+ authorization: Schema.optional(Schema.String),
22
+ });
23
+ /**
24
+ * How a session's leader is wired. `pty` (default) allocates a pseudoterminal — interactive
25
+ * shells and TUIs. `pipe` gives the leader plain stdio pipes and no tty — the shape for processes
26
+ * that speak a byte protocol over stdin/stdout (JSON-RPC / NDJSON servers): stdout is the recorded,
27
+ * attachable output, stderr is recorded as diagnostics only, input feeds stdin, and resize is
28
+ * rejected.
29
+ */
30
+ export const sessionModeSchema = Schema.Literals(["pty", "pipe"]);
31
+ export const createSessionRequestSchema = Schema.Struct({
32
+ workspaceId: NonEmptyString,
33
+ ownerUserId: NonEmptyString,
34
+ /** argv[0] is the program the session runs; the rest its arguments. */
35
+ argv: Schema.Array(NonEmptyString).check(Schema.isNonEmpty(), Schema.isMaxLength(64)),
36
+ /** Working directory inside the workspace (defaults to the workspace working directory). */
37
+ cwd: Schema.optional(NonEmptyString),
38
+ /** Extra environment for the PTY process (values are NOT secrets — use credentials for those). */
39
+ env: Schema.optional(Schema.Record(Schema.String, Schema.String)),
40
+ cols: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
41
+ rows: Schema.optional(Schema.Int.check(Schema.isGreaterThan(0))),
42
+ term: Schema.optional(NonEmptyString),
43
+ /** Leader wiring; defaults to `pty`. `cols`/`rows`/`term` are ignored for `pipe`. */
44
+ mode: Schema.optional(sessionModeSchema),
45
+ /** Opaque caller correlation bag: stored verbatim, echoed on reads, no platform semantics. */
46
+ metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
47
+ });
48
+ export const sessionSchema = Schema.Struct({
49
+ sessionId: NonEmptyString,
50
+ workspaceId: NonEmptyString,
51
+ /** The interactive run recording this session; its record is the durable evidence. */
52
+ runId: NonEmptyString,
53
+ ownerUserId: NonEmptyString,
54
+ status: sessionStatusSchema,
55
+ argv: Schema.Array(Schema.String),
56
+ cwd: Schema.optional(NonEmptyString),
57
+ cols: Schema.Number,
58
+ rows: Schema.Number,
59
+ /** Leader wiring. Absent on servers from before pipe mode shipped, which means `pty`. */
60
+ mode: Schema.optional(sessionModeSchema),
61
+ exitCode: Schema.optional(Schema.Number),
62
+ exitSignal: Schema.optional(Schema.Number),
63
+ errorMessage: Schema.optional(Schema.String),
64
+ metadata: Schema.optional(Schema.Record(Schema.String, Schema.Unknown)),
65
+ /**
66
+ * Highest ingested output sequence for this session's run (decimal string), or "0" when nothing
67
+ * has been ingested yet — the resume cursor: `output?from=<highWater + 1>` continues exactly
68
+ * where a previous reader stopped.
69
+ */
70
+ outputHighWater: NonEmptyString,
71
+ createdAt: Schema.String,
72
+ endedAt: Schema.optional(Schema.String),
73
+ });
74
+ export const listSessionsQuerySchema = Schema.Struct({
75
+ ownerUserId: NonEmptyString,
76
+ workspaceId: Schema.optional(NonEmptyString),
77
+ status: Schema.optional(sessionStatusSchema),
78
+ limit: Schema.optional(NonEmptyString),
79
+ });
80
+ export const listSessionsResponseSchema = Schema.Struct({
81
+ items: Schema.Array(sessionSchema),
82
+ });
83
+ export const sessionInputRequestSchema = Schema.Struct({
84
+ ownerUserId: Schema.optional(NonEmptyString),
85
+ /** Base64-encoded keystrokes (bytes, not text — binary-safe). */
86
+ dataBase64: NonEmptyString,
87
+ });
88
+ export const sessionResizeRequestSchema = Schema.Struct({
89
+ ownerUserId: Schema.optional(NonEmptyString),
90
+ cols: Schema.Int.check(Schema.isGreaterThan(0)),
91
+ rows: Schema.Int.check(Schema.isGreaterThan(0)),
92
+ });
93
+ export const sessionSignalRequestSchema = Schema.Struct({
94
+ ownerUserId: Schema.optional(NonEmptyString),
95
+ /** POSIX signal number (e.g. 2 = SIGINT, 15 = SIGTERM). */
96
+ signal: Schema.Int.check(Schema.isGreaterThan(0)),
97
+ });
98
+ export const closeSessionRequestSchema = Schema.Struct({
99
+ ownerUserId: Schema.optional(NonEmptyString),
100
+ });
101
+ /**
102
+ * Byte-exact session output by sequence range. `from` is INCLUSIVE (omit = from the beginning);
103
+ * chunks carry their sequence so a disconnected reader resumes with `lastSequence + 1`. Output is
104
+ * as-recorded: redacted upstream by the daemon, PTY output stream only.
105
+ */
106
+ export const getSessionOutputQuerySchema = Schema.Struct({
107
+ ownerUserId: Schema.optional(NonEmptyString),
108
+ from: Schema.optional(NonEmptyString),
109
+ limit: Schema.optional(NonEmptyString),
110
+ });
111
+ export const sessionOutputChunkSchema = Schema.Struct({
112
+ sequence: NonEmptyString, // decimal-string uint64
113
+ /** Base64-encoded PTY output bytes, exactly as recorded. */
114
+ dataBase64: Schema.String,
115
+ });
116
+ export const sessionOutputResponseSchema = Schema.Struct({
117
+ sessionId: NonEmptyString,
118
+ chunks: Schema.Array(sessionOutputChunkSchema),
119
+ /** The cursor to pass as `from` to continue after this page. */
120
+ nextFrom: NonEmptyString,
121
+ /** Session status at read time, so pollers can stop when the session settles. */
122
+ status: sessionStatusSchema,
123
+ });
124
+ export class SessionBadRequestError extends Schema.TaggedErrorClass()("SessionBadRequestError", { message: Schema.String }, { httpApiStatus: 400 }) {
125
+ }
126
+ export class SessionUnauthorizedError extends Schema.TaggedErrorClass()("SessionUnauthorizedError", { message: Schema.String }, { httpApiStatus: 401 }) {
127
+ }
128
+ export class SessionForbiddenError extends Schema.TaggedErrorClass()("SessionForbiddenError", { message: Schema.String }, { httpApiStatus: 403 }) {
129
+ }
130
+ export class SessionNotFoundError extends Schema.TaggedErrorClass()("SessionNotFoundError", { message: Schema.String }, { httpApiStatus: 404 }) {
131
+ }
132
+ export class SessionConflictError extends Schema.TaggedErrorClass()("SessionConflictError", { message: Schema.String }, { httpApiStatus: 409 }) {
133
+ }
134
+ export class SessionBadGatewayError extends Schema.TaggedErrorClass()("SessionBadGatewayError", { message: Schema.String }, { httpApiStatus: 502 }) {
135
+ }
136
+ export class SessionInternalServerError extends Schema.TaggedErrorClass()("SessionInternalServerError", { message: Schema.String }, { httpApiStatus: 500 }) {
137
+ }
138
+ const sessionIdParams = Schema.Struct({ sessionId: NonEmptyString });
139
+ export const SessionsGroup = HttpApiGroup.make("sessions")
140
+ .add(
141
+ // Scope: workspace:exec. Opens the daemon PTY, creates the interactive run + session rows.
142
+ HttpApiEndpoint.post("createSession", "/", {
143
+ headers: sessionAuthorizationHeadersSchema,
144
+ payload: createSessionRequestSchema,
145
+ success: sessionSchema.pipe(HttpApiSchema.status(201)),
146
+ error: [
147
+ SessionBadRequestError,
148
+ SessionUnauthorizedError,
149
+ SessionForbiddenError,
150
+ SessionNotFoundError,
151
+ SessionConflictError,
152
+ SessionBadGatewayError,
153
+ SessionInternalServerError,
154
+ ],
155
+ }))
156
+ .add(HttpApiEndpoint.get("listSessions", "/", {
157
+ headers: sessionAuthorizationHeadersSchema,
158
+ query: listSessionsQuerySchema,
159
+ success: listSessionsResponseSchema,
160
+ error: [
161
+ SessionBadRequestError,
162
+ SessionUnauthorizedError,
163
+ SessionForbiddenError,
164
+ SessionInternalServerError,
165
+ ],
166
+ }))
167
+ .add(
168
+ // Scope: session:read. Status includes the resume cursor (outputHighWater).
169
+ HttpApiEndpoint.get("getSession", "/:sessionId", {
170
+ params: sessionIdParams,
171
+ headers: sessionAuthorizationHeadersSchema,
172
+ query: Schema.Struct({ ownerUserId: Schema.optional(NonEmptyString) }),
173
+ success: sessionSchema,
174
+ error: [
175
+ SessionBadRequestError,
176
+ SessionUnauthorizedError,
177
+ SessionForbiddenError,
178
+ SessionNotFoundError,
179
+ SessionInternalServerError,
180
+ ],
181
+ }))
182
+ .add(
183
+ // Scope: session:read. Byte-exact recorded output by sequence range (history + poll tail).
184
+ HttpApiEndpoint.get("getSessionOutput", "/:sessionId/output", {
185
+ params: sessionIdParams,
186
+ headers: sessionAuthorizationHeadersSchema,
187
+ query: getSessionOutputQuerySchema,
188
+ success: sessionOutputResponseSchema,
189
+ error: [
190
+ SessionBadRequestError,
191
+ SessionUnauthorizedError,
192
+ SessionForbiddenError,
193
+ SessionNotFoundError,
194
+ SessionInternalServerError,
195
+ ],
196
+ }))
197
+ .add(
198
+ // Scope: session:input.
199
+ HttpApiEndpoint.post("sendSessionInput", "/:sessionId/input", {
200
+ params: sessionIdParams,
201
+ headers: sessionAuthorizationHeadersSchema,
202
+ payload: sessionInputRequestSchema,
203
+ success: Schema.Struct({ ok: Schema.Boolean }),
204
+ error: [
205
+ SessionBadRequestError,
206
+ SessionUnauthorizedError,
207
+ SessionForbiddenError,
208
+ SessionNotFoundError,
209
+ SessionConflictError,
210
+ SessionBadGatewayError,
211
+ SessionInternalServerError,
212
+ ],
213
+ }))
214
+ .add(
215
+ // Scope: session:input.
216
+ HttpApiEndpoint.post("resizeSession", "/:sessionId/resize", {
217
+ params: sessionIdParams,
218
+ headers: sessionAuthorizationHeadersSchema,
219
+ payload: sessionResizeRequestSchema,
220
+ success: Schema.Struct({ ok: Schema.Boolean }),
221
+ error: [
222
+ SessionBadRequestError,
223
+ SessionUnauthorizedError,
224
+ SessionForbiddenError,
225
+ SessionNotFoundError,
226
+ SessionConflictError,
227
+ SessionBadGatewayError,
228
+ SessionInternalServerError,
229
+ ],
230
+ }))
231
+ .add(
232
+ // Scope: session:input.
233
+ HttpApiEndpoint.post("signalSession", "/:sessionId/signal", {
234
+ params: sessionIdParams,
235
+ headers: sessionAuthorizationHeadersSchema,
236
+ payload: sessionSignalRequestSchema,
237
+ success: Schema.Struct({ ok: Schema.Boolean }),
238
+ error: [
239
+ SessionBadRequestError,
240
+ SessionUnauthorizedError,
241
+ SessionForbiddenError,
242
+ SessionNotFoundError,
243
+ SessionConflictError,
244
+ SessionBadGatewayError,
245
+ SessionInternalServerError,
246
+ ],
247
+ }))
248
+ .add(
249
+ // Scope: workspace:exec (closing a terminal is a control action, like opening one).
250
+ HttpApiEndpoint.post("closeSession", "/:sessionId/close", {
251
+ params: sessionIdParams,
252
+ headers: sessionAuthorizationHeadersSchema,
253
+ payload: closeSessionRequestSchema,
254
+ success: sessionSchema.pipe(HttpApiSchema.status(202)),
255
+ error: [
256
+ SessionBadRequestError,
257
+ SessionUnauthorizedError,
258
+ SessionForbiddenError,
259
+ SessionNotFoundError,
260
+ SessionBadGatewayError,
261
+ SessionInternalServerError,
262
+ ],
263
+ }))
264
+ .annotate(OpenApi.Description, "Interactive PTY sessions: durable, reattachable, sequence-keyed output.");