@shardflux/sdk 0.6.1 → 0.6.2

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/http.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { RetryRecord } from './progress.js';
2
- export declare const SDK_VERSION = "0.6.1";
2
+ export declare const SDK_VERSION = "0.6.2";
3
3
  export interface RequestOptions {
4
4
  query?: Record<string, string | number | boolean | undefined | null>;
5
5
  json?: unknown;
package/dist/http.js CHANGED
@@ -6,7 +6,7 @@
6
6
  */
7
7
  import { ShardfluxApiError, ShardfluxProtocolError, isErrorBody } from "./errors.js";
8
8
  import { describeFailure } from "./progress.js";
9
- export const SDK_VERSION = '0.6.1';
9
+ export const SDK_VERSION = '0.6.2';
10
10
  export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
11
11
  /**
12
12
  * The fetch the SDK uses when none is given. On runtimes whose bundled undici is 8.x (Node 26) it sends
@@ -20,7 +20,8 @@ export interface LifecycleOptions {
20
20
  /**
21
21
  * Default (false): resolve once the change is requested; the returned operation is usually still `queued`.
22
22
  * `true` or WaitOptions: resolve once it has finished (the operation `succeeded`); throws OperationFailedError when it
23
- * fails and OperationTimeoutError after `timeoutMs` (default 5 minutes; the operation continues server side).
23
+ * fails and OperationTimeoutError after `timeoutMs` (default 5 minutes; the operation continues server side, a start
24
+ * waiting for capacity until its deadline, then it fails with `capacity_unavailable`, retryable).
24
25
  */
25
26
  wait?: boolean | WaitOptions;
26
27
  /** Progress events of this call (phases, retries, and `done` with its timing). */
@@ -104,6 +104,8 @@ interface EventBase {
104
104
  }
105
105
  /**
106
106
  * - `phase`: a phase began (live progress: "capacity_pending: no_ready_host"). `tool` phases come from tool calls.
107
+ * A `capacity_pending` phase carries `deadlineAt` (0.6.2+, when the API reports it): when the start gives up waiting
108
+ * for a host and fails with `capacity_unavailable` (retryable; nothing was started).
107
109
  * - `retry`: a request is retried after a transient failure (also emitted for tool calls, action `tool`).
108
110
  * - `done`: the call ended, successfully or not, with its full timing.
109
111
  */
@@ -111,6 +113,7 @@ export type ProgressEvent = (EventBase & {
111
113
  type: 'phase';
112
114
  phase: LifecyclePhase;
113
115
  reason: string | null;
116
+ deadlineAt?: string;
114
117
  }) | (EventBase & {
115
118
  type: 'retry';
116
119
  retry: RetryRecord;
@@ -123,6 +126,12 @@ export type ProgressListener = (event: ProgressEvent) => void;
123
126
  export declare function emitTo(listeners: ReadonlyArray<ProgressListener | undefined>, event: ProgressEvent): void;
124
127
  /** Combines listeners (client-level and per call) into one; undefined when there are none. */
125
128
  export declare function combineListeners(...listeners: Array<ProgressListener | undefined>): ProgressListener | undefined;
129
+ /**
130
+ * When a start waiting in `capacity_pending` gives up: the operation's `error.details.deadline_at` (RFC 3339). A start no
131
+ * host admits by then fails with `capacity_unavailable` (retryable; nothing was started). Null in any other state, or
132
+ * from an API that does not report it.
133
+ */
134
+ export declare function capacityDeadlineOf(op: Operation): string | null;
126
135
  /** Server timing from an operation as GET /v1/operations/{id} returns it. */
127
136
  export declare function serverTiming(op: Operation): ServerTiming;
128
137
  /** Why a request failed, in one short phrase (for retry records). */
@@ -139,8 +148,11 @@ export declare class Trace {
139
148
  /** Milliseconds since the call began. */
140
149
  now(): number;
141
150
  get finished(): LifecycleTiming | null;
142
- /** Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase. */
143
- phase(phase: LifecyclePhase, reason?: string | null): void;
151
+ /**
152
+ * Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase.
153
+ * `deadlineAt`: when a `capacity_pending` start gives up (added to the event only).
154
+ */
155
+ phase(phase: LifecyclePhase, reason?: string | null, deadlineAt?: string | null): void;
144
156
  /** Runs `fn` as a phase that may overlap others (open() reads the view and issues the token together). */
145
157
  span<T>(phase: LifecyclePhase, fn: () => Promise<T>, reason?: string | null): Promise<T>;
146
158
  /** Records an operation snapshot: its ids, the observed state as a phase, and the server timing. */
package/dist/progress.js CHANGED
@@ -30,6 +30,17 @@ function diffMs(from, to) {
30
30
  }
31
31
  const str = (v) => (typeof v === 'string' && v.length > 0 ? v : null);
32
32
  const num = (v) => (typeof v === 'number' && Number.isFinite(v) ? v : null);
33
+ /**
34
+ * When a start waiting in `capacity_pending` gives up: the operation's `error.details.deadline_at` (RFC 3339). A start no
35
+ * host admits by then fails with `capacity_unavailable` (retryable; nothing was started). Null in any other state, or
36
+ * from an API that does not report it.
37
+ */
38
+ export function capacityDeadlineOf(op) {
39
+ if (op.state !== 'capacity_pending')
40
+ return null;
41
+ const details = op.error?.details;
42
+ return typeof details === 'object' && details !== null ? str(details.deadline_at) : null;
43
+ }
33
44
  /** Server timing from an operation as GET /v1/operations/{id} returns it. */
34
45
  export function serverTiming(op) {
35
46
  const r = (op.result ?? {});
@@ -113,8 +124,11 @@ export class Trace {
113
124
  this.#current.durationMs = round(at - this.#current.startMs);
114
125
  this.#current = null;
115
126
  }
116
- /** Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase. */
117
- phase(phase, reason = null) {
127
+ /**
128
+ * Enters a sequential phase (closing the current one). The same phase and reason again is not a new phase.
129
+ * `deadlineAt`: when a `capacity_pending` start gives up (added to the event only).
130
+ */
131
+ phase(phase, reason = null, deadlineAt = null) {
118
132
  if (this.#timing)
119
133
  return;
120
134
  if (this.#current && this.#current.phase === phase && this.#current.reason === reason)
@@ -123,7 +137,7 @@ export class Trace {
123
137
  this.#close(at);
124
138
  this.#current = { phase, reason, operationId: this.operationId, startMs: at, durationMs: 0 };
125
139
  this.#phases.push(this.#current);
126
- this.#emit({ ...this.#base(), type: 'phase', phase, reason });
140
+ this.#emit({ ...this.#base(), type: 'phase', phase, reason, ...(deadlineAt ? { deadlineAt } : {}) });
127
141
  }
128
142
  /** Runs `fn` as a phase that may overlap others (open() reads the view and issues the token together). */
129
143
  async span(phase, fn, reason = null) {
@@ -145,7 +159,7 @@ export class Trace {
145
159
  this.workspaceId ??= op.workspace_id;
146
160
  this.#server = serverTiming(op);
147
161
  if (!TERMINAL.has(op.state))
148
- this.phase(op.state, op.state_reason ?? null);
162
+ this.phase(op.state, op.state_reason ?? null, capacityDeadlineOf(op));
149
163
  }
150
164
  retry(r) {
151
165
  if (this.#timing)
@@ -154,7 +154,9 @@ export declare class Workspace {
154
154
  * (`timeoutMs`, default 120 000 ms) covers every step. Throws OperationFailedError when the resume/open fails,
155
155
  * OperationTimeoutError (naming the operation, its state and reason) when it is still queued, running or
156
156
  * capacity_pending at the deadline, and any other API error (a conflict other than already_running /
157
- * operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`.
157
+ * operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`. A resume no
158
+ * host admits within 15 minutes fails with `capacity_unavailable` (OperationFailedError, `retryable`: the workspace
159
+ * stays suspended with its state; try again later).
158
160
  */
159
161
  wake(opts?: WakeOptions): Promise<boolean>;
160
162
  /** Tools granted by the most recent token (null before one was issued). */
package/dist/workspace.js CHANGED
@@ -217,7 +217,9 @@ export class Workspace {
217
217
  * (`timeoutMs`, default 120 000 ms) covers every step. Throws OperationFailedError when the resume/open fails,
218
218
  * OperationTimeoutError (naming the operation, its state and reason) when it is still queued, running or
219
219
  * capacity_pending at the deadline, and any other API error (a conflict other than already_running /
220
- * operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`.
220
+ * operation_in_progress) at once. Cell calls use it automatically when they meet `workspace_not_running`. A resume no
221
+ * host admits within 15 minutes fails with `capacity_unavailable` (OperationFailedError, `retryable`: the workspace
222
+ * stays suspended with its state; try again later).
221
223
  */
222
224
  async wake(opts = {}) {
223
225
  const trace = new Trace('wake', combineListeners(this.#ctx.onProgress, this.#tracked(opts).onProgress), { workspaceId: this.id });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/sdk",
3
- "version": "0.6.1",
3
+ "version": "0.6.2",
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",