@sealant/sdk 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/types.d.ts CHANGED
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * This is the fluent object model the marketing site commits to verbatim:
5
5
  *
6
- * const sandbox = await sealant.sandboxes.create({ repository, harness: opencode() })
7
- * const run = await sandbox.harness.run("Round invoice totals once, after applying the discount.")
6
+ * const workspace = await sealant.workspaces.create({ repository, harness: opencode() })
7
+ * const run = await workspace.harness.run("Round invoice totals once, after applying the discount.")
8
8
  * await run.record.replay()
9
9
  *
10
10
  * Design rule (load-bearing): these public types are HAND-WRITTEN and DECOUPLED from the Effect-core
@@ -30,7 +30,7 @@ export interface SealantConfig {
30
30
  }
31
31
  /** The harnesses with first-class integrations baked into the platform today. */
32
32
  export type HarnessId = "opencode" | "codex" | "claude-code";
33
- /** A single one-shot command to invoke a harness against a prompt inside the sandbox. */
33
+ /** A single one-shot command to invoke a harness against a prompt inside the workspace. */
34
34
  export interface HarnessRunCommand {
35
35
  /** The executable to run (e.g. `"opencode"`). */
36
36
  readonly executable: string;
@@ -56,19 +56,19 @@ export interface Harness {
56
56
  /** Optional launch command for an interactive session (defaults to the executable). */
57
57
  readonly launchCommand?: string;
58
58
  }
59
- /** Lifecycle status of a sandbox. `stopped`/`expired` arrive with lifecycle close-out (Phase 3). */
60
- export type SandboxStatus = "queued" | "running" | "ready" | "failed" | "cancelled";
61
- /** A coarse lifecycle event observed while a sandbox is being provisioned. */
62
- export interface SandboxEvent {
59
+ /** Lifecycle status of a workspace. `stopped`/`expired` arrive with lifecycle close-out (Phase 3). */
60
+ export type WorkspaceStatus = "queued" | "running" | "ready" | "failed" | "cancelled";
61
+ /** A coarse lifecycle event observed while a workspace is being provisioned. */
62
+ export interface WorkspaceEvent {
63
63
  readonly type: string;
64
64
  readonly occurredAt: string;
65
65
  readonly message?: string;
66
66
  }
67
- /** The supported sandbox OS families (maps to the blueprint target). */
68
- export type SandboxOs = "fedora" | "arch" | "nix";
67
+ /** The supported workspace OS families (maps to the blueprint target). */
68
+ export type WorkspaceOs = "fedora" | "arch" | "nix";
69
69
  /**
70
- * Connected-account credentials to attach to a sandbox at creation time, per provider — so the
71
- * harness inside the sandbox authenticates as the caller's own Claude / Codex / GitHub identity
70
+ * Connected-account credentials to attach to a workspace at creation time, per provider — so the
71
+ * harness inside the workspace authenticates as the caller's own Claude / Codex / GitHub identity
72
72
  * instead of running unauthenticated.
73
73
  *
74
74
  * For each provider: `true` means "my default account" (the one named `"default"`), and a `string`
@@ -80,7 +80,7 @@ export type SandboxOs = "fedora" | "arch" | "nix";
80
80
  * `auth.json` contents, and any other secret material never do. The control plane resolves references
81
81
  * to encrypted credentials server-side and injects them at launch.
82
82
  */
83
- export interface SandboxCredentialsOptions {
83
+ export interface WorkspaceCredentialsOptions {
84
84
  /** Profile id whose per-provider account bindings apply first. */
85
85
  readonly profile?: string;
86
86
  /** `true` for the caller's default Claude account, or a string naming a specific one. */
@@ -91,46 +91,72 @@ export interface SandboxCredentialsOptions {
91
91
  readonly github?: boolean | string;
92
92
  }
93
93
  export interface CreateOptions {
94
- /** Source git repository to build the sandbox around (e.g. `"github.com/acme/billing-service"`). */
94
+ /** Source git repository to build the workspace around (e.g. `"github.com/acme/billing-service"`). */
95
95
  readonly repository: string;
96
- /** The harness to run inside the sandbox. */
96
+ /** The harness to run inside the workspace. */
97
97
  readonly harness: Harness;
98
98
  /** Git ref to check out (defaults to the repository's default branch). */
99
99
  readonly ref?: string;
100
- /** Human-friendly name for the sandbox. */
100
+ /** Human-friendly name for the workspace. */
101
101
  readonly name?: string;
102
- /** OS family for the sandbox image. */
103
- readonly os?: SandboxOs;
104
- /** Extra OS packages to install in the sandbox. */
102
+ /** OS family for the workspace image. */
103
+ readonly os?: WorkspaceOs;
104
+ /** Extra OS packages to install in the workspace. */
105
105
  readonly packages?: readonly string[];
106
- /** When true (default), resolve only once the sandbox runtime is live. */
106
+ /** When true (default), resolve only once the workspace runtime is live. */
107
107
  readonly wait?: boolean;
108
108
  /** Observe provisioning events as they happen. */
109
- readonly onEvent?: (event: SandboxEvent) => void;
110
- /** Connected-account credentials to attach to the sandbox (see `SandboxCredentialsOptions`). */
111
- readonly credentials?: SandboxCredentialsOptions;
109
+ readonly onEvent?: (event: WorkspaceEvent) => void;
110
+ /** Connected-account credentials to attach to the workspace (see `WorkspaceCredentialsOptions`). */
111
+ readonly credentials?: WorkspaceCredentialsOptions;
112
112
  }
113
113
  export interface ListOptions {
114
- readonly status?: SandboxStatus;
114
+ readonly status?: WorkspaceStatus;
115
115
  readonly limit?: number;
116
116
  }
117
+ /** Options for a deterministic `workspace.exec()`. */
118
+ export interface WorkspaceExecOptions {
119
+ /** Working directory inside the workspace (defaults to the repository root). */
120
+ readonly cwd?: string;
121
+ }
122
+ /**
123
+ * The settled result of a deterministic `workspace.exec()`. The exit code is a check DATUM — a
124
+ * nonzero exit resolves normally (that's the point: `base fails` is a recorded fact, not an error).
125
+ * `exec()` rejects only when the execution machinery itself broke, i.e. when the exit code cannot
126
+ * be trusted.
127
+ */
128
+ export interface WorkspaceExecResult {
129
+ /** Exit code of the executed command. */
130
+ readonly exitCode: number;
131
+ /** Everything the command wrote to stdout, decoded as UTF-8. */
132
+ readonly stdout: string;
133
+ /** Everything the command wrote to stderr, decoded as UTF-8. */
134
+ readonly stderr: string;
135
+ /** The run this exec was recorded as — its `record` is the durable, replayable evidence. */
136
+ readonly run: Run;
137
+ }
117
138
  /** A live, disposable development environment around a real repository. */
118
- export interface Sandbox {
139
+ export interface Workspace {
119
140
  readonly id: string;
120
141
  readonly name: string;
121
142
  /** Current lifecycle status. */
122
- status(): Promise<SandboxStatus>;
123
- /** Resolves once the sandbox runtime is live and ready to accept a run. */
143
+ status(): Promise<WorkspaceStatus>;
144
+ /** Resolves once the workspace runtime is live and ready to accept a run. */
124
145
  ready(): Promise<this>;
125
- /** Run a harness in this sandbox. */
146
+ /** Run a harness in this workspace. */
126
147
  readonly harness: HarnessRunner;
148
+ /**
149
+ * Execute one command deterministically in the workspace — no agent in the loop — recorded into a
150
+ * run record like any other process. `argv[0]` is the executable, the rest its arguments.
151
+ */
152
+ exec(argv: readonly string[], options?: WorkspaceExecOptions): Promise<WorkspaceExecResult>;
127
153
  /** Lifecycle events as an async stream. */
128
- events(): AsyncIterable<SandboxEvent>;
129
- /** Stop the sandbox now (Phase 3). */
154
+ events(): AsyncIterable<WorkspaceEvent>;
155
+ /** Stop the workspace now (Phase 3). */
130
156
  stop(): Promise<void>;
131
- /** Restart the sandbox into a fresh runtime (Phase 3). */
132
- restart(): Promise<Sandbox>;
133
- /** Schedule the sandbox to expire (Phase 3). */
157
+ /** Restart the workspace into a fresh runtime (Phase 3). */
158
+ restart(): Promise<Workspace>;
159
+ /** Schedule the workspace to expire (Phase 3). */
134
160
  expire(options?: {
135
161
  readonly in?: string;
136
162
  }): Promise<void>;
@@ -144,13 +170,13 @@ export interface RunOptions {
144
170
  export interface SessionOptions {
145
171
  readonly signal?: AbortSignal;
146
172
  }
147
- /** Runs a harness in a sandbox, one-shot or interactive. */
173
+ /** Runs a harness in a workspace, one-shot or interactive. */
148
174
  export interface HarnessRunner {
149
175
  /** BLOCKING: resolves once the harness has terminally completed; `result`/`changes` are settled. */
150
176
  run(prompt: string, options?: RunOptions): Promise<Run>;
151
177
  /** NON-BLOCKING: returns a live handle immediately for streaming via `run.record.stream()`. */
152
178
  start(prompt: string, options?: RunOptions): Promise<Run>;
153
- /** Interactive session reusing the live sandbox (Phase 3). */
179
+ /** Interactive session reusing the live workspace (Phase 3). */
154
180
  session(options?: SessionOptions): Promise<InteractiveSession>;
155
181
  }
156
182
  export type RunOutcome = "completed" | "failed";
@@ -185,13 +211,171 @@ export interface RunArtifacts {
185
211
  list(): Promise<readonly ArtifactRef[]>;
186
212
  get(name: string): Promise<Uint8Array>;
187
213
  }
188
- /** A single ordered entry in the execution record's timeline. */
189
- export interface TimelineEntry {
214
+ /** The runtime daemon's lifecycle state changed. `state` is a numeric `RuntimeState`. */
215
+ export interface RuntimeStateChangedEvent {
216
+ readonly state: number;
217
+ readonly reason?: string | undefined;
218
+ }
219
+ /** Periodic runtime liveness signal. `state` is a numeric `RuntimeState`. */
220
+ export interface RuntimeHeartbeatEvent {
221
+ readonly state: number;
222
+ }
223
+ /** A supervised process began executing. */
224
+ export interface ProcessStartedEvent {
225
+ readonly pid: number;
226
+ readonly pgid: number;
227
+ readonly pidfd: boolean;
228
+ readonly executable: string;
229
+ readonly args: readonly string[];
230
+ readonly cwd: string;
231
+ /** Wall clock at start, microseconds (decimal string). */
232
+ readonly startedAt: string;
233
+ }
234
+ /** A supervised process ended. `reason` is a numeric `ExitReason`. */
235
+ export interface ProcessExitedEvent {
236
+ readonly exitCode?: number | undefined;
237
+ readonly signal?: number | undefined;
238
+ readonly reason: number;
239
+ /** Wall-clock duration, microseconds (decimal string). */
240
+ readonly durationMicros: string;
241
+ }
242
+ /**
243
+ * A run of process output. Raw bytes live in the artifact store (fetch byte-exact text via
244
+ * `record.scrollback()`); the event carries counts and a content hash. `stream` is a numeric
245
+ * `StreamKind` (stdout = 2, stderr = 3).
246
+ */
247
+ export interface IoChunkEvent {
248
+ readonly stream: number;
249
+ readonly byteCount: string;
250
+ readonly streamOffset: string;
251
+ readonly contentAlgo?: string | undefined;
252
+ readonly contentHash?: string | undefined;
253
+ readonly transform?: {
254
+ readonly redacted: boolean;
255
+ readonly truncated: boolean;
256
+ readonly coalesced: boolean;
257
+ readonly originalByteCount?: string | undefined;
258
+ } | undefined;
259
+ }
260
+ /** The runtime dropped events under pressure. `priority` is a numeric `EventPriority`. */
261
+ export interface TelemetryDroppedEvent {
262
+ readonly reason: string;
263
+ readonly count: string;
264
+ readonly priority: number;
265
+ }
266
+ /** Filesystem entry metadata attached to a change. `fileType` is a numeric `FileType`. */
267
+ export interface FileEntryData {
268
+ readonly path: string;
269
+ readonly fileType: number;
270
+ readonly size: string;
271
+ readonly mtimeMicros: string;
272
+ readonly mode: number;
273
+ readonly hash?: string | undefined;
274
+ readonly symlinkTarget?: string | undefined;
275
+ }
276
+ /** A watched file changed. `kind` is a numeric `FileChangeKind`. */
277
+ export interface FileChangeEvent {
278
+ readonly kind: number;
279
+ readonly path: string;
280
+ readonly renameFrom?: string | undefined;
281
+ readonly entry?: FileEntryData | undefined;
282
+ readonly certain: boolean;
283
+ }
284
+ /** The file watcher overflowed — changes under `root` may have been missed. */
285
+ export interface FileWatchOverflowEvent {
286
+ readonly root: string;
287
+ }
288
+ /** A filesystem snapshot pass finished. */
289
+ export interface FileSnapshotCompletedEvent {
290
+ readonly root: string;
291
+ readonly fileCount: string;
292
+ }
293
+ /** Aggregate before/after diff counts became available. */
294
+ export interface FileDiffAvailableEvent {
295
+ readonly added: string;
296
+ readonly modified: string;
297
+ readonly deleted: string;
298
+ readonly renamed: string;
299
+ }
300
+ /** An outbound network request the run made. `scheme` is a numeric `NetworkScheme`. */
301
+ export interface NetworkRequestEvent {
302
+ readonly scheme: number;
303
+ readonly method?: string | undefined;
304
+ readonly host: string;
305
+ readonly port: number;
306
+ readonly path?: string | undefined;
307
+ readonly status?: number | undefined;
308
+ readonly bytesSent: string;
309
+ readonly bytesReceived: string;
310
+ readonly durationMicros: string;
311
+ }
312
+ /** A network source the run touched — the raw material of a "sources the agent opened" trail. */
313
+ export interface NetworkSourceObservedEvent {
314
+ readonly host: string;
315
+ readonly resolvedIps: readonly string[];
316
+ readonly port: number;
317
+ readonly scheme?: number | undefined;
318
+ readonly method?: string | undefined;
319
+ readonly path?: string | undefined;
320
+ readonly status?: number | undefined;
321
+ }
322
+ /** Fields shared by every timeline entry, independent of its kind. */
323
+ export interface TimelineEntryBase {
190
324
  readonly sequence: bigint;
191
- readonly kind: string;
192
325
  readonly occurredAt: string;
193
- readonly data: unknown;
326
+ /** One-line human summary of the event. */
327
+ readonly summary: string;
328
+ /** Correlation id of the producing process, when attributable. */
329
+ readonly processId?: string | undefined;
194
330
  }
331
+ /**
332
+ * A single ordered entry in the execution record's timeline, DISCRIMINATED by `kind`: switch on it
333
+ * and `data` narrows to the event's typed payload. The `"unknown"` case is the forward-compatibility
334
+ * path — it carries kinds newer than this SDK (or payloads that failed their schema) with the wire
335
+ * kind preserved in `rawKind` and the payload verbatim in `data`.
336
+ */
337
+ export type TimelineEntry = (TimelineEntryBase & {
338
+ readonly kind: "runtimeStateChanged";
339
+ readonly data: RuntimeStateChangedEvent;
340
+ }) | (TimelineEntryBase & {
341
+ readonly kind: "runtimeHeartbeat";
342
+ readonly data: RuntimeHeartbeatEvent;
343
+ }) | (TimelineEntryBase & {
344
+ readonly kind: "processStarted";
345
+ readonly data: ProcessStartedEvent;
346
+ }) | (TimelineEntryBase & {
347
+ readonly kind: "processExited";
348
+ readonly data: ProcessExitedEvent;
349
+ }) | (TimelineEntryBase & {
350
+ readonly kind: "ioChunk";
351
+ readonly data: IoChunkEvent;
352
+ }) | (TimelineEntryBase & {
353
+ readonly kind: "telemetryDropped";
354
+ readonly data: TelemetryDroppedEvent;
355
+ }) | (TimelineEntryBase & {
356
+ readonly kind: "fileChange";
357
+ readonly data: FileChangeEvent;
358
+ }) | (TimelineEntryBase & {
359
+ readonly kind: "fileWatchOverflow";
360
+ readonly data: FileWatchOverflowEvent;
361
+ }) | (TimelineEntryBase & {
362
+ readonly kind: "fileSnapshotCompleted";
363
+ readonly data: FileSnapshotCompletedEvent;
364
+ }) | (TimelineEntryBase & {
365
+ readonly kind: "fileDiffAvailable";
366
+ readonly data: FileDiffAvailableEvent;
367
+ }) | (TimelineEntryBase & {
368
+ readonly kind: "networkRequest";
369
+ readonly data: NetworkRequestEvent;
370
+ }) | (TimelineEntryBase & {
371
+ readonly kind: "networkSourceObserved";
372
+ readonly data: NetworkSourceObservedEvent;
373
+ }) | (TimelineEntryBase & {
374
+ readonly kind: "unknown";
375
+ /** The kind as received on the wire — set when this SDK version doesn't model it. */
376
+ readonly rawKind: string;
377
+ readonly data: unknown;
378
+ });
195
379
  /** A re-fold of the record up to some point — scrubable by sequence. */
196
380
  export interface RunReplay {
197
381
  readonly entries: readonly TimelineEntry[];
@@ -283,9 +467,96 @@ export interface Run {
283
467
  /** Resolves once the run has terminally completed (no-op if already settled). */
284
468
  wait(): Promise<Run>;
285
469
  }
286
- /** An interactive harness session over the live sandbox (Phase 3). */
470
+ /** An interactive harness session over the live workspace (Phase 3). */
287
471
  export interface InteractiveSession {
288
472
  send(input: string): Promise<void>;
289
473
  output(): AsyncIterable<Uint8Array>;
290
474
  close(): Promise<void>;
291
475
  }
476
+ /**
477
+ * Connected-account selection for inference — the same reference shape as workspace creation,
478
+ * minus GitHub (not a model provider). `true` means "my default account"; a string names one.
479
+ * Only claude accounts are supported today; a codex selection is rejected until Codex inference
480
+ * ships. SECURITY: only account references cross this surface — never token material.
481
+ */
482
+ export interface InferenceCredentialsOptions {
483
+ /** Profile id whose claude binding applies when `claude` is not set explicitly. */
484
+ readonly profile?: string;
485
+ /** `true` for the caller's default claude account, or a string naming a specific one. */
486
+ readonly claude?: boolean | string;
487
+ /** Reserved — rejected until Codex inference ships. */
488
+ readonly codex?: boolean | string;
489
+ }
490
+ /** A caller-defined tool the model may call. `inputSchema` is a JSON Schema object, verbatim. */
491
+ export interface InferenceToolDefinition {
492
+ readonly name: string;
493
+ readonly description?: string;
494
+ readonly inputSchema: unknown;
495
+ }
496
+ /** A tool call the model made. Execute it YOUR side, then respond with an `InferenceToolResult`. */
497
+ export interface InferenceToolCall {
498
+ readonly toolCallId: string;
499
+ readonly name: string;
500
+ readonly input: unknown;
501
+ }
502
+ /** Your result for one tool call, keyed by its `toolCallId`. */
503
+ export interface InferenceToolResult {
504
+ readonly toolCallId: string;
505
+ readonly content: string;
506
+ readonly isError?: boolean;
507
+ }
508
+ /** The assistant turn: the final text (with parsed `json` when requested) or pending tool calls. */
509
+ export type InferenceTurn = {
510
+ readonly type: "text";
511
+ readonly text: string;
512
+ readonly json?: unknown;
513
+ } | {
514
+ readonly type: "toolCalls";
515
+ readonly calls: readonly InferenceToolCall[];
516
+ };
517
+ export interface InferenceUsage {
518
+ readonly inputTokens: number;
519
+ readonly outputTokens: number;
520
+ }
521
+ export interface InferenceResponse {
522
+ /** Continuation handle for the tool loop (held in memory server-side; expires after idle). */
523
+ readonly sessionId: string;
524
+ readonly turn: InferenceTurn;
525
+ /** Usage for the exchange, present on the final text turn. */
526
+ readonly usage?: InferenceUsage;
527
+ }
528
+ /** Starts a new inference exchange on a connected account. */
529
+ export interface InferenceRespondOptions {
530
+ readonly prompt: string;
531
+ readonly system?: string;
532
+ readonly model?: string;
533
+ /** Upper bound on agentic turns within the exchange (server default 16). */
534
+ readonly maxTurns?: number;
535
+ readonly tools?: readonly InferenceToolDefinition[];
536
+ /** Structured output: reply as JSON (schema-constrained when `schema` is given). */
537
+ readonly responseFormat?: {
538
+ readonly type: "json";
539
+ readonly schema?: unknown;
540
+ };
541
+ readonly credentials: InferenceCredentialsOptions;
542
+ }
543
+ /** Continues an exchange by posting the results of the previous turn's tool calls. */
544
+ export interface InferenceContinueOptions {
545
+ readonly sessionId: string;
546
+ readonly toolResults: readonly InferenceToolResult[];
547
+ }
548
+ /**
549
+ * Inference on connected accounts. The model call runs SERVER-SIDE through the official agent SDKs
550
+ * on the resolved account's credential (never raw model-API calls); the tool loop is CALLER-
551
+ * EXECUTED — a `toolCalls` turn parks server-side until you `respond()` with the results:
552
+ *
553
+ * let response = await sealant.inference.respond({ prompt, tools, credentials: { claude: true } })
554
+ * while (response.turn.type === "toolCalls") {
555
+ * const toolResults = await runTools(response.turn.calls)
556
+ * response = await sealant.inference.respond({ sessionId: response.sessionId, toolResults })
557
+ * }
558
+ * response.turn.text
559
+ */
560
+ export interface InferenceNamespace {
561
+ respond(options: InferenceRespondOptions | InferenceContinueOptions): Promise<InferenceResponse>;
562
+ }
package/dist/types.js CHANGED
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * This is the fluent object model the marketing site commits to verbatim:
5
5
  *
6
- * const sandbox = await sealant.sandboxes.create({ repository, harness: opencode() })
7
- * const run = await sandbox.harness.run("Round invoice totals once, after applying the discount.")
6
+ * const workspace = await sealant.workspaces.create({ repository, harness: opencode() })
7
+ * const run = await workspace.harness.run("Round invoice totals once, after applying the discount.")
8
8
  * await run.record.replay()
9
9
  *
10
10
  * Design rule (load-bearing): these public types are HAND-WRITTEN and DECOUPLED from the Effect-core
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@sealant/sdk",
3
- "version": "0.3.1",
4
- "description": "The fluent public SDK for Sealant — create a sandbox, run a harness, replay the record.",
3
+ "version": "0.5.0",
4
+ "description": "The fluent public SDK for Sealant — create a workspace, run a harness, replay the record.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",
@@ -16,6 +16,10 @@
16
16
  ".": {
17
17
  "types": "./dist/index.d.ts",
18
18
  "import": "./dist/index.js"
19
+ },
20
+ "./effect": {
21
+ "types": "./dist/effect/index.d.ts",
22
+ "import": "./dist/effect/index.js"
19
23
  }
20
24
  },
21
25
  "publishConfig": {
@@ -23,7 +27,7 @@
23
27
  },
24
28
  "dependencies": {
25
29
  "effect": "^4.0.0-beta.85",
26
- "@sealant/api-contracts": "^0.3.1"
30
+ "@sealant/api-contracts": "^0.5.0"
27
31
  },
28
32
  "devDependencies": {
29
33
  "@effect/vitest": "^4.0.0-beta.85",