@shardflux/sdk 0.11.1 → 0.13.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.
@@ -2,12 +2,12 @@
2
2
  * A workspace handle: the latest view from the application API plus managed
3
3
  * tool tokens and cell clients (one per agent label / tool set).
4
4
  */
5
- import type { ClientContext, DiskLayout, ForkTarget, Operation, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResult, WaitOptions, WorkspaceLifetime, WorkspaceOrigin, WorkspacePurpose, WorkspaceView } from './client.js';
5
+ import type { AllocationMode, ClientContext, IdlePolicy, DiskLayout, ForkTarget, Operation, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResult, WaitOptions, WorkspaceLifetime, WorkspaceMemory, WorkspaceOrigin, WorkspacePurpose, WorkspaceView } from './client.js';
6
6
  import { CAPTURE_BARRIER, CellClient } from './cell.js';
7
7
  import { ToolCallCapture } from './capture.js';
8
8
  import type { ToolCallCaptureOptions } from './capture.js';
9
9
  import type { WorkspaceMode } from './errors.js';
10
- import type { FinishedOperation, LifecycleOptions, ResumeOptions, WaitedLifecycleOptions, WaitedResumeOptions } from './lifecycle.js';
10
+ import type { FinishedOperation, ForkOptions, LifecycleOptions, ResumeOptions, SuspendOptions, WaitedForkOptions, WaitedLifecycleOptions, WaitedResumeOptions, WaitedSuspendOptions } from './lifecycle.js';
11
11
  import { Trace } from './progress.js';
12
12
  import type { LifecycleTiming, ProgressListener } from './progress.js';
13
13
  import { WorkspaceSecrets } from './secrets.js';
@@ -82,6 +82,13 @@ export declare class Workspace {
82
82
  get grants(): WorkspaceView['grants'];
83
83
  get ceilings(): WorkspaceView['ceilings'];
84
84
  get template(): WorkspaceView['template'];
85
+ /**
86
+ * The template version whose immutable paths the workspace has mounted (0.13.0; `template.immutable_version`): the
87
+ * template's newest published version as of the workspace's last cold boot or resume, which can be newer than
88
+ * `template.version`. Null when the workspace mounts none (its version declares no immutable paths, it has not started
89
+ * since, or an older API).
90
+ */
91
+ get immutableVersion(): number | null;
85
92
  get pendingReason(): string | null;
86
93
  get activeOperation(): WorkspaceView['active_operation'];
87
94
  /** persistent or session; immutable. A view without the field (older API) is persistent. */
@@ -108,6 +115,14 @@ export declare class Workspace {
108
115
  * a resume after the request cancelled it. `refresh()` reads it again.
109
116
  */
110
117
  get suspendRequest(): SuspendRequest | null;
118
+ /**
119
+ * Memory of the workspace as of the last view (0.13.0): `{allocation_mode, promised_mib, held_mib,
120
+ * plugged_mib}`. An elastic workspace holds `held_mib` while idle and is grown towards `promised_mib` when a command
121
+ * needs it; `plugged_mib` is what is plugged above the floor now (null without a live allocation). Null on an older API.
122
+ */
123
+ get memory(): WorkspaceMemory | null;
124
+ /** `fixed` or `elastic` (0.13.0): the running VM's layout, else the next start's. Fixed on an older API. */
125
+ get allocationMode(): AllocationMode;
111
126
  /** The raw view (GET /v1/workspaces/{id}). */
112
127
  get data(): WorkspaceView;
113
128
  /**
@@ -135,11 +150,20 @@ export declare class Workspace {
135
150
  get secrets(): WorkspaceSecrets;
136
151
  /** The workspace's text inputs `{NAME: value}` (0.7.0; secret inputs are bound secrets, never listed here). */
137
152
  inputs(): Promise<Record<string, string>>;
153
+ get labels(): Record<string, string>;
154
+ setLabels(labels: Record<string, string>): Promise<this>;
155
+ setIdlePolicy(policy: IdlePolicy | null): Promise<this>;
156
+ idle(signal?: AbortSignal): ReturnType<CellClient['idle']>;
157
+ keepalive(seconds: number, signal?: AbortSignal): ReturnType<CellClient['keepalive']>;
138
158
  refresh(): Promise<this>;
139
- /** Waits for the active operation (if any) and refreshes. */
159
+ /**
160
+ * Waits for the active operation (if any) and refreshes. A suspend-when-idle that found the workspace active is
161
+ * canceled `workspace_active` (0.12.0+: `OperationFailedError.workspaceActive`); the workspace keeps running, so this
162
+ * resolves.
163
+ */
140
164
  waitUntilReady(opts?: WaitOptions): Promise<this>;
141
165
  /**
142
- * Deletes the workspace (tool access ends at once; keys are never reused). Resolves when the delete is REQUESTED;
166
+ * Deletes the workspace (tool access ends at once; the key can be reused after deletion finishes). Resolves when the delete is REQUESTED;
143
167
  * with `{ wait: true }`, once it has FINISHED.
144
168
  */
145
169
  delete(opts: WaitedLifecycleOptions): Promise<FinishedOperation>;
@@ -147,12 +171,15 @@ export declare class Workspace {
147
171
  /**
148
172
  * Suspends the workspace: memory and processes are checkpointed, compute stops. Resolves when the suspend is
149
173
  * REQUESTED (the operation is usually still `queued`, and the workspace still running); pass `{ wait: true }` to
150
- * resolve once it has FINISHED, with `workspace.state` then `suspended`.
174
+ * resolve once it has FINISHED, with `workspace.state` then `suspended`. That is as soon as the workspace is sealed on
175
+ * its host, typically in a few hundred ms. `result.durable` (also `lastTiming.server.durable`, 0.12.0+) turns true
176
+ * when the copy lands in durable storage, typically within a second; `{ durable: true }` resolves only then.
151
177
  *
152
178
  * await workspace.suspend({ wait: true });
179
+ * await workspace.suspend({ durable: true }); // 0.12.0+: also wait for the durable copy
153
180
  */
154
- suspend(opts: WaitedLifecycleOptions): Promise<FinishedOperation>;
155
- suspend(opts?: LifecycleOptions): Promise<Operation>;
181
+ suspend(opts: WaitedSuspendOptions): Promise<FinishedOperation>;
182
+ suspend(opts?: SuspendOptions): Promise<Operation>;
156
183
  /**
157
184
  * Suspends the workspace once it has been idle for `afterSeconds` (0..3600, 0 = as soon as it is idle; 0.9.0): call it when your agent's turn
158
185
  * ends, so the workspace stops using RAM soon after instead of waiting out its idle policy. A command still running,
@@ -176,7 +203,9 @@ export declare class Workspace {
176
203
  * those given to open()), so `cell()` calls with that label and tools start at once. A workspace that is already
177
204
  * running is ShardfluxApiError 409 `conflict` (`already_running`). The finished operation's `result.memory_restored`
178
205
  * (also `lastTiming.server.memoryRestored`, 0.11.0+) is false when the resume booted the saved disk instead
179
- * (`resume_path` `cold_boot`): files kept, processes restarted.
206
+ * (`resume_path` `cold_boot`): files kept, processes restarted. `result.lost_suspend` (also
207
+ * `lastTiming.server.lostSuspend`, `lostSuspendOf(op)`, 0.12.0+) names a suspend this resume could not restore and the
208
+ * checkpoint it restored instead (see the lifecycle reference).
180
209
  */
181
210
  resume(opts: WaitedResumeOptions): Promise<FinishedOperation>;
182
211
  resume(opts?: ResumeOptions): Promise<Operation>;
@@ -191,11 +220,11 @@ export declare class Workspace {
191
220
  * Forks into a new key. Resolves when the fork is REQUESTED (the copy's handle is returned at once); with
192
221
  * `{ wait: true }`, once the copy exists, with its handle refreshed.
193
222
  */
194
- fork(target: ForkTarget, opts: WaitedLifecycleOptions): Promise<{
223
+ fork(target: ForkTarget, opts: WaitedForkOptions): Promise<{
195
224
  operation: FinishedOperation;
196
225
  workspace: Workspace;
197
226
  }>;
198
- fork(target: ForkTarget, opts?: LifecycleOptions): Promise<{
227
+ fork(target: ForkTarget, opts?: ForkOptions): Promise<{
199
228
  operation: Operation;
200
229
  workspace: Workspace;
201
230
  }>;
package/dist/workspace.js CHANGED
@@ -96,6 +96,15 @@ export class Workspace {
96
96
  get template() {
97
97
  return this.#view.template;
98
98
  }
99
+ /**
100
+ * The template version whose immutable paths the workspace has mounted (0.13.0; `template.immutable_version`): the
101
+ * template's newest published version as of the workspace's last cold boot or resume, which can be newer than
102
+ * `template.version`. Null when the workspace mounts none (its version declares no immutable paths, it has not started
103
+ * since, or an older API).
104
+ */
105
+ get immutableVersion() {
106
+ return this.#view.template.immutable_version ?? null;
107
+ }
99
108
  get pendingReason() {
100
109
  return this.#view.pending_reason;
101
110
  }
@@ -142,6 +151,18 @@ export class Workspace {
142
151
  get suspendRequest() {
143
152
  return this.#view.idle?.suspend_request ?? null;
144
153
  }
154
+ /**
155
+ * Memory of the workspace as of the last view (0.13.0): `{allocation_mode, promised_mib, held_mib,
156
+ * plugged_mib}`. An elastic workspace holds `held_mib` while idle and is grown towards `promised_mib` when a command
157
+ * needs it; `plugged_mib` is what is plugged above the floor now (null without a live allocation). Null on an older API.
158
+ */
159
+ get memory() {
160
+ return this.#view.memory ?? null;
161
+ }
162
+ /** `fixed` or `elastic` (0.13.0): the running VM's layout, else the next start's. Fixed on an older API. */
163
+ get allocationMode() {
164
+ return this.#view.memory?.allocation_mode ?? this.#view.caps?.allocation_mode ?? 'fixed';
165
+ }
145
166
  /** The raw view (GET /v1/workspaces/{id}). */
146
167
  get data() {
147
168
  return this.#view;
@@ -181,17 +202,39 @@ export class Workspace {
181
202
  inputs() {
182
203
  return this.#ctx.workspaces.inputs(this.id);
183
204
  }
205
+ get labels() { return { ...this.#view.labels }; }
206
+ async setLabels(labels) {
207
+ this.#view = (await this.#ctx.workspaces.setLabels(this.id, labels)).data;
208
+ return this;
209
+ }
210
+ async setIdlePolicy(policy) {
211
+ this.#view = (await this.#ctx.workspaces.setIdlePolicy(this.id, policy)).data;
212
+ return this;
213
+ }
214
+ idle(signal) { return this.cell().idle(signal); }
215
+ keepalive(seconds, signal) { return this.cell().keepalive(seconds, signal); }
184
216
  async refresh() {
185
217
  this.#view = await this.#ctx.http.json('GET', `/v1/workspaces/${encodeURIComponent(this.id)}`, {}, this.#ctx.authorization);
186
218
  if (this.#view.mode === 'file_first' && typeof this.#view.tree_revision === 'number')
187
219
  this.#noteTreeRevision(this.#view.tree_revision);
188
220
  return this;
189
221
  }
190
- /** Waits for the active operation (if any) and refreshes. */
222
+ /**
223
+ * Waits for the active operation (if any) and refreshes. A suspend-when-idle that found the workspace active is
224
+ * canceled `workspace_active` (0.12.0+: `OperationFailedError.workspaceActive`); the workspace keeps running, so this
225
+ * resolves.
226
+ */
191
227
  async waitUntilReady(opts = {}) {
192
228
  const op = this.#view.active_operation;
193
- if (op)
194
- await this.#ctx.workspaces.waitForOperation(op.id, opts);
229
+ if (op) {
230
+ try {
231
+ await this.#ctx.workspaces.waitForOperation(op.id, opts);
232
+ }
233
+ catch (e) {
234
+ if (!(e instanceof OperationFailedError) || !e.workspaceActive)
235
+ throw e;
236
+ }
237
+ }
195
238
  return this.refresh();
196
239
  }
197
240
  delete(opts = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/sdk",
3
- "version": "0.11.1",
3
+ "version": "0.13.0",
4
4
  "type": "module",
5
5
  "description": "Shardflux TypeScript SDK: open persistent agent workspaces by key and give your agent workspace tools (exec, files, processes, PTY, git, browser).",
6
6
  "license": "Apache-2.0",
@@ -71,6 +71,9 @@
71
71
  "yaml": "2.9.1",
72
72
  "zod": "4.6.5"
73
73
  },
74
+ "dependencies": {
75
+ "undici": "8.10.2"
76
+ },
74
77
  "scripts": {
75
78
  "build": "node scripts/build.mjs",
76
79
  "generate": "openapi-typescript ../contracts/openapi/app-api.json -o src/generated/app-api.ts && openapi-typescript ../contracts/openapi/cell-api.yaml --default-non-nullable false -o src/generated/cell-api.ts",