@shardflux/sdk 0.9.0 → 0.10.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.
@@ -723,9 +723,22 @@ export interface paths {
723
723
  * none is known; in the future while a keepalive or a running command is
724
724
  * active). A running exec command counts as work until min(its start +
725
725
  * `IDLE_COMMAND_MAX_SECONDS`, its start + `timeout_ms`), and its end is
726
- * activity (contracts §20.6). `suspend_at` = `idle_since` + `idle_minutes` when
726
+ * activity (contracts §20.6): an end seen without a client call is
727
+ * recorded in `last_attach_at`, which is work but not a tool call.
728
+ * `suspend_at` = `idle_since` + `idle_minutes` when
727
729
  * the policy is `suspend_after` and automatic suspend is enabled, else
728
730
  * null; the idle loop acts within `2 x ACTIVITY_FLUSH_MS` after it.
731
+ * `suspend_request` is the pending suspend request of the application
732
+ * API's `POST /v1/workspaces/{id}/suspend-when-idle` while it is valid:
733
+ * the workspace is running, `last_tool_at` is not later than
734
+ * `requested_at` + 0.5 s and `running_since` not later than
735
+ * `requested_at` (a tool call or a resume after the request cancels it
736
+ * for good), else null. A valid request applies under every policy
737
+ * (`never` included) unless automatic suspend was disabled after
738
+ * repeated failures; `suspend_at` is then the earlier of the policy's
739
+ * time and max(`idle_since`, `requested_at`) + `after_seconds`, so an
740
+ * attached stream, a keepalive or a running command defers it until
741
+ * `after_seconds` after it ends (contracts §20.6).
729
742
  * Activity lags tool traffic by up to `ACTIVITY_FLUSH_MS`. Any
730
743
  * tool granted by the token authorizes the call; the tool gate does not
731
744
  * apply and the call is not tool activity.
@@ -827,6 +840,7 @@ export interface components {
827
840
  env?: {
828
841
  [key: string]: string;
829
842
  };
843
+ /** @description Absolute working directory; omitted or empty starts in the default (/home/user). A relative path is refused, not resolved: 422 validation_failed, details.reason invalid_cwd, details.field cwd, the message naming the absolute path it likely means. A cwd that is not a directory ends the session failed_to_start (processful) or the execution failed with details.reason exec_failed_to_start (file-first). */
830
844
  cwd?: string;
831
845
  user?: string;
832
846
  /** @description Written to stdin, which is then closed (max 1 MiB decoded). */
@@ -939,6 +953,7 @@ export interface components {
939
953
  started_at?: string | null;
940
954
  /** Format: date-time */
941
955
  ended_at?: string | null;
956
+ /** @description Why the session ended without an exit code. For failed_to_start the command never ran and this names the cause, e.g. `working directory "/home/user/app" is not a directory` or `executable "foo" not found in PATH`. */
942
957
  error?: string;
943
958
  };
944
959
  OutputEvent: {
@@ -982,6 +997,7 @@ export interface components {
982
997
  env?: {
983
998
  [key: string]: string;
984
999
  };
1000
+ /** @description Absolute working directory; omitted or empty starts in the default (/home/user). A relative path is refused: 422 validation_failed, details.reason invalid_cwd, details.field cwd. */
985
1001
  cwd?: string;
986
1002
  user?: string;
987
1003
  /** @default 24 */
@@ -1348,6 +1364,25 @@ export interface components {
1348
1364
  [key: string]: unknown;
1349
1365
  } | null;
1350
1366
  };
1367
+ /**
1368
+ * @description A pending suspend request (application API `POST /v1/workspaces/{id}/suspend-when-idle`):
1369
+ * once the workspace has been idle for `after_seconds` after `requested_at`, suspend it.
1370
+ */
1371
+ IdleSuspendRequest: {
1372
+ /**
1373
+ * Format: date-time
1374
+ * @description When the request was made (database clock); a newer request replaces it.
1375
+ */
1376
+ requested_at: string;
1377
+ after_seconds: number;
1378
+ /**
1379
+ * Format: date-time
1380
+ * @description requested_at + after_seconds, the earliest suspend (the idle loop acts within
1381
+ * 2 x ACTIVITY_FLUSH_MS after it). A work signal still active then defers it;
1382
+ * suspend_at is the projection.
1383
+ */
1384
+ not_before: string;
1385
+ };
1351
1386
  IdleStatus: {
1352
1387
  policy: components["schemas"]["IdlePolicy"];
1353
1388
  /** @description Workspace observed_state (contracts §9). */
@@ -1365,6 +1400,8 @@ export interface components {
1365
1400
  /** Format: date-time */
1366
1401
  suspend_at: string | null;
1367
1402
  auto_suspend: components["schemas"]["IdleAutoSuspend"];
1403
+ /** @description The pending suspend request while it is valid, else null. */
1404
+ suspend_request: components["schemas"]["IdleSuspendRequest"] | null;
1368
1405
  };
1369
1406
  };
1370
1407
  responses: {
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.9.0";
2
+ export declare const SDK_VERSION = "0.10.0";
3
3
  export interface RequestOptions {
4
4
  query?: Record<string, string | number | boolean | undefined | null>;
5
5
  json?: unknown;
package/dist/http.js CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import { ShardfluxApiError, ShardfluxProtocolError, apiError, isErrorBody } from "./errors.js";
10
10
  import { describeFailure } from "./progress.js";
11
- export const SDK_VERSION = '0.9.0';
11
+ export const SDK_VERSION = '0.10.0';
12
12
  export const defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
13
13
  /**
14
14
  * The fetch the SDK uses when none is given. On runtimes whose bundled undici is 8.x (Node 26) it sends
package/dist/index.d.ts CHANGED
@@ -15,14 +15,14 @@
15
15
  export type { components, operations, paths } from './generated/app-api.js';
16
16
  export type { components as CellComponents, paths as CellPaths } from './generated/cell-api.js';
17
17
  export { BillingApi, Shardflux, WorkspacesApi, fetchBillingCatalog, pickByKey } from './client.js';
18
- export type { AgentSession, BillingCatalog, BillingSubscription, Caps, CheckoutSession, DiskLayout, Entitlements, FindByKeyOptions, ForkTarget, Invoice, InvoicePage, LifetimeFilter, ListParams, Me, OpenParams, OpenResponse, Operation, Page, PortalSession, PurposeFilter, ResetWorkspaceBody, ResumeAnswer, ResumeRequestOptions, ResumeResponse, ShardfluxOptions, UpdatePolicy, WaitOptions, WorkspaceInputs, WorkspaceLifetime, WorkspaceOrigin, WorkspacePurpose, WorkspaceView, } from './client.js';
18
+ export type { AgentSession, BillingCatalog, BillingSubscription, Caps, CheckoutSession, DiskLayout, Entitlements, FindByKeyOptions, ForkTarget, Invoice, InvoicePage, LifetimeFilter, ListParams, Me, OpenParams, OpenResponse, Operation, Page, PortalSession, PurposeFilter, ResetWorkspaceBody, ResumeAnswer, ResumeRequestOptions, ResumeResponse, ShardfluxOptions, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResponse, SuspendWhenIdleResult, UpdatePolicy, WaitOptions, WorkspaceInputs, WorkspaceLifetime, WorkspaceOrigin, WorkspacePurpose, WorkspaceView, } from './client.js';
19
19
  export type { FinishedOperation, LifecycleOptions, ResumeOptions, WaitedLifecycleOptions, WaitedResumeOptions } from './lifecycle.js';
20
20
  export { formatTiming } from './progress.js';
21
21
  export type { LifecycleAction, LifecyclePhase, LifecycleTiming, ProgressEvent, ProgressListener, RetryRecord, ServerTiming, TimingOutcome, TimingPhase, } from './progress.js';
22
22
  export { FEEDBACK_CATEGORIES, FEEDBACK_MESSAGE_MAX_LENGTH } from './feedback.js';
23
23
  export type { AccountFeedbackParams, FeedbackCategory, FeedbackContext, FeedbackReceipt, SendFeedbackParams } from './feedback.js';
24
24
  export { UsageApi } from './usage.js';
25
- export type { Grants, Spend, SpendPolicy, UsageEstimate, UsageMeter, UsageSeries, UsageSeriesParams, UsageSummary } from './usage.js';
25
+ export type { Grants, Spend, SpendCap, SpendPolicy, UsageEstimate, UsageMeter, UsageSeries, UsageSeriesParams, UsageSummary } from './usage.js';
26
26
  export { TemplateBuildTimeoutError, TemplateBuildsApi, TemplateDraftApi, TemplatePackagesApi, TemplateUploadError, TemplateUploadsApi, TemplateVersionTestInstancesApi, TemplateVersionsApi, TemplatesApi, buildSettled, saveAsTemplateBody, } from './templates.js';
27
27
  export { TemplateFileError, packDirectory, parseTemplateText, readTemplateFile } from './template-file.js';
28
28
  export type { PackedFile, YamlParser } from './template-file.js';
@@ -50,10 +50,10 @@ export type { AnthropicToolDefinition, JsonSchema, OpenAIChatToolDefinition, Ope
50
50
  export { CaptureError, ToolCallCapture, captureTool } from './capture.js';
51
51
  export type { CallLike, CallRef, CaptureCall, CaptureErrorKind, CaptureEvent, CaptureFlushResult, CapturePart, CaptureSelector, CaptureSource, CaptureStats, CaptureStatus, DropReason, ToolCallCaptureOptions, WrapOptions, } from './capture.js';
52
52
  export type { AiSdkAdapter, AiSdkToolEndEvent, AnthropicAdapter, ClaudeAdapter, ClaudeCaptureHooks, ClaudeHookCallback, ClaudeHookMatcher, LangChainAdapter, LangChainToolHandler, MastraAdapter, MastraAfterToolCallContext, McpAdapter, OpenAIAgentsAdapter, } from './capture-adapters.js';
53
- export { NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from './errors.js';
53
+ export { ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from './errors.js';
54
54
  export type { AppErrorCode, CellErrorCode, ErrorCode, ErrorReason, KnownErrorReason, WorkspaceMode } from './errors.js';
55
55
  export { SDK_VERSION } from './http.js';
56
56
  export { API_KEY_TOOL_PERMISSIONS, AccountApiKeysApi, AccountAuditApi, AccountAuthApi, AccountBillingApi, AccountExportsApi, AccountInvitationsApi, AccountMembersApi, AccountOrganizationsApi, AccountProjectsApi, AccountTemplatesApi, AccountTotpApi, AccountUserApi, CheckoutTimeoutError, OrganizationExportsApi, SESSION_TOKEN_PATTERN, ShardfluxAccount, isSessionToken, parseEmailToken, } from './account.js';
57
- export type { AccountClientOptions, AccountDeletion, AccountDeletionCanceled, AccountDeletionScheduled, ApiKey, ApiKeyPage, ApiKeyToolPermission, AuditExportParams, AuthSessionInfo, AuthSessionPage, AuthSessionState, AuthUser, BillingInvoicePage, BillingPortalSession, CheckoutStatus, CreateApiKeyParams, CreatedApiKey, DataExport, EmailChangeConfirmResult, EmailChangeResult, Invitation, InvitationPage, LoginResult, Member, MemberPage, MemberRole, MfaChallengeResult, Organization, OrganizationDeletion, OrganizationDeletionResult, OrganizationPage, OrganizationRole, OrganizationWorkspacePage, OrganizationWorkspacesParams, PageParams, PasswordChangeResult, PasswordResetConfirmResult, PasswordResetRequestResult, Project, ProjectPage, RecoveryCodes, RegisterResult, ResendVerificationResult, SessionTokenUpdate, ShardfluxAccountOptions, StepUpResult, TotpConfirmResult, TotpDisableResult, TotpEnrollment, VerifyEmailResult, WaitForCheckoutOptions, } from './account.js';
57
+ export type { AccountClientOptions, AccountDeletion, AccountDeletionCanceled, AccountDeletionScheduled, ApiKey, ApiKeyPage, ApiKeyToolPermission, AuditExportParams, AuthSessionInfo, AuthSessionPage, AuthSessionState, AuthUser, BillingInvoicePage, BillingPortalSession, CheckoutStatus, CreateApiKeyParams, CreatedApiKey, DataExport, EmailChangeConfirmResult, EmailChangeResult, Invitation, InvitationPage, LoginResult, Member, MemberPage, MemberRole, MfaChallengeResult, Organization, OrganizationDeletion, OrganizationDeletionResult, OrganizationPage, OrganizationRole, OrganizationWorkspacePage, OrganizationWorkspacesParams, PageParams, PasswordChangeResult, PasswordResetConfirmResult, PasswordResetRequestResult, Project, ProjectPage, RecoveryCodes, RegisterResult, ResendVerificationResult, SessionTokenUpdate, ShardfluxAccountOptions, SpendPolicyUpdate, StepUpResult, TotpConfirmResult, TotpDisableResult, TotpEnrollment, VerifyEmailResult, WaitForCheckoutOptions, } from './account.js';
58
58
  export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from './version-check.js';
59
59
  export type { CheckClientVersionOptions, ClientEcosystem, ClientVersionEntry, ClientVersionStatus, ClientVersionStatusKind, ClientVersions, VersionCheckIdentity, VersionCheckOption, } from './version-check.js';
package/dist/index.js CHANGED
@@ -15,7 +15,7 @@ export { EXECUTION_ID, ExecutionResult, newExecutionId } from "./executions.js";
15
15
  export { ToolTokenManager } from "./tokens.js";
16
16
  export { ToolArgumentError, executeToolCall, toAnthropicTools, toOpenAITools, validateArgs, workspaceTools } from "./tools.js";
17
17
  export { CaptureError, ToolCallCapture, captureTool } from "./capture.js";
18
- export { NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from "./errors.js";
18
+ export { ExecStartError, NotSupportedForModeError, OperationFailedError, OperationTimeoutError, ShardfluxApiError, ShardfluxProtocolError, TreeRevisionMismatchError } from "./errors.js";
19
19
  export { SDK_VERSION } from "./http.js";
20
20
  export { API_KEY_TOOL_PERMISSIONS, AccountApiKeysApi, AccountAuditApi, AccountAuthApi, AccountBillingApi, AccountExportsApi, AccountInvitationsApi, AccountMembersApi, AccountOrganizationsApi, AccountProjectsApi, AccountTemplatesApi, AccountTotpApi, AccountUserApi, CheckoutTimeoutError, OrganizationExportsApi, SESSION_TOKEN_PATTERN, ShardfluxAccount, isSessionToken, parseEmailToken, } from "./account.js";
21
21
  export { checkClientVersion, clientVersionStatus, compareVersions, versionCheckDisabledByEnv } from "./version-check.js";
package/dist/tools.js CHANGED
@@ -11,6 +11,7 @@
11
11
  * providers' formats and executeToolCall dispatches a model's tool call.
12
12
  */
13
13
  import { CAPTURE_BARRIER } from "./cell.js";
14
+ import { ExecStartError } from "./errors.js";
14
15
  import { newExecutionId } from "./executions.js";
15
16
  export class ToolArgumentError extends Error {
16
17
  tool;
@@ -160,13 +161,31 @@ export function workspaceTools(workspace, opts = {}) {
160
161
  description: 'Run a shell command in the persistent remote workspace (Linux; bash -lc) and return its exit code, stdout and stderr. Files, installed packages and background processes persist between calls.',
161
162
  parameters: execParameters,
162
163
  run: async (a, o) => {
163
- const r = await cell().exec.run(['bash', '-lc', String(a.command)], {
164
- ...(typeof a.cwd === 'string' ? { cwd: a.cwd } : opts.defaultCwd ? { cwd: opts.defaultCwd } : {}),
165
- timeoutMs: typeof a.timeout_ms === 'number' ? a.timeout_ms : 600_000,
166
- ...(typeof a.stdin === 'string' ? { stdin: a.stdin } : {}),
167
- maxOutputBytes: max,
168
- ...(o.signal ? { signal: o.signal } : {}),
169
- });
164
+ let r;
165
+ try {
166
+ r = await cell().exec.run(['bash', '-lc', String(a.command)], {
167
+ ...(typeof a.cwd === 'string' ? { cwd: a.cwd } : opts.defaultCwd ? { cwd: opts.defaultCwd } : {}),
168
+ timeoutMs: typeof a.timeout_ms === 'number' ? a.timeout_ms : 600_000,
169
+ ...(typeof a.stdin === 'string' ? { stdin: a.stdin } : {}),
170
+ maxOutputBytes: max,
171
+ ...(o.signal ? { signal: o.signal } : {}),
172
+ });
173
+ }
174
+ catch (e) {
175
+ // Nothing ran (e.g. a cwd that is not a directory): the model gets the reason, as for an execution.
176
+ if (!(e instanceof ExecStartError))
177
+ throw e;
178
+ return {
179
+ exit_code: null,
180
+ term_signal: null,
181
+ timed_out: false,
182
+ stdout: '',
183
+ stderr: '',
184
+ truncated: false,
185
+ session_id: e.sessionId,
186
+ error: { code: e.code, message: e.message, reason: e.reason },
187
+ };
188
+ }
170
189
  return { exit_code: r.exitCode, term_signal: r.termSignal, timed_out: r.timedOut, stdout: r.stdout, stderr: r.stderr, truncated: r.truncated, session_id: r.sessionId };
171
190
  },
172
191
  },
package/dist/usage.d.ts CHANGED
@@ -2,13 +2,21 @@
2
2
  * Usage, allowances, estimates, grants and spend (Phase 9 application API).
3
3
  *
4
4
  * const s = await cloud.usage.summary(orgId);
5
- * if (s.allowance_exhausted) ... // opens/resumes answer 402 allowance_exhausted
5
+ * if (s.allowance_exhausted) ... // opens/resumes answer 402 allowance_exhausted, details.reason = s.exhausted_reason
6
+ * s.spend_cap.state // opt-in overage (0.10.0): 'accruing', 'warning', 'reached', ...
6
7
  *
7
8
  * Every response carries `measurement.measured_through` (usage is complete up to it) and both raw
8
9
  * (fractional) and billable (whole-unit) quantities. API keys read organization totals but only
9
10
  * their own project's workspaces.
11
+ *
12
+ * Opt-in overage (0.10.0): an owner or billing member can turn on overage with a spend cap in the console. While it is
13
+ * on, a CPU-hours or RAM GiB-hours allowance past `included` is in cap state `overage` (starts are admitted and the
14
+ * usage past it is charged on the next invoice) until the charges reach the cap. `summary()`, `spend()` and
15
+ * `estimate()` report it as `spend_cap` (`SpendCap`: state, cap, charges, lines per allowance, projected date);
16
+ * `spendPolicy()` returns the settings. An API key only reads them: owners and billing members change them in the
17
+ * console or with a user session (`ShardfluxAccount.billing.setSpendPolicy`).
10
18
  */
11
- import type { operations } from './generated/app-api.js';
19
+ import type { components, operations } from './generated/app-api.js';
12
20
  import type { ClientContext } from './client.js';
13
21
  type JsonOf<R> = R extends {
14
22
  content: {
@@ -27,6 +35,13 @@ export type Grants = Ok<operations['getV1OrganizationsOrganizationIdGrants']>;
27
35
  export type Spend = Ok<operations['getV1OrganizationsOrganizationIdSpend']>;
28
36
  export type SpendPolicy = Ok<operations['getV1OrganizationsOrganizationIdSpendPolicy']>;
29
37
  export type UsageMeter = UsageSummary['meters'][number]['meter'];
38
+ /**
39
+ * Opt-in overage this period (0.10.0), on `summary()`, `spend()` and `estimate()`: `state` (`unavailable`, `off`,
40
+ * `paused`, `within_allowance`, `accruing`, `warning`, `reached`), the configured and effective cap, `charges_minor`
41
+ * and `remaining_minor` (minor units of `currency`), `percent_of_cap`, `lines` (units past each allowance, billed units,
42
+ * rate and amount) and `projected_reached_at`.
43
+ */
44
+ export type SpendCap = components['schemas']['SpendCap'];
30
45
  export interface UsageSeriesParams {
31
46
  /** RFC 3339; defaults to the current period's start. */
32
47
  from?: string;
@@ -39,22 +54,37 @@ export interface UsageSeriesParams {
39
54
  export declare class UsageApi {
40
55
  #private;
41
56
  constructor(ctx: () => ClientContext);
42
- /** Current-period usage per meter, allowances with enforcement and cap state, measurement freshness. */
57
+ /**
58
+ * Current-period usage per meter, allowances with enforcement and cap state (`overage` while opt-in overage covers
59
+ * usage past a CPU-hours or RAM GiB-hours allowance), measurement freshness, `exhausted_reason` (the 402
60
+ * allowance_exhausted reason while starts are refused: `allowance_used`, `overage_paused`, `spend_cap_reached`) and
61
+ * `spend_cap` (0.10.0).
62
+ */
43
63
  summary(organizationId: string): Promise<UsageSummary>;
44
64
  /** Time series from the ledger (hour: <= 31 days, day: <= 400 days per request). */
45
65
  series(organizationId: string, params?: UsageSeriesParams): Promise<UsageSeries>;
46
66
  /** Usage of one workspace. */
47
67
  workspace(workspaceId: string, params?: Omit<UsageSeriesParams, 'workspaceId' | 'projectId'>): Promise<UsageSeries>;
48
- /** Subscription fee, usage charges (0 while overage is disabled) and projected allowance use. */
68
+ /**
69
+ * Subscription fee, overage charges so far (`usage_charges_minor`, per meter in `usage_lines`), the estimated total,
70
+ * projected allowance use and `spend_cap` (0.10.0). Charges are 0 while overage is off or not on the plan.
71
+ */
49
72
  estimate(organizationId: string): Promise<UsageEstimate>;
50
73
  /** Quotas, per-workspace ceilings/reservations/grants and the compute budget leases granted to the cell. */
51
74
  grants(organizationId: string, params?: {
52
75
  limit?: number;
53
76
  cursor?: string;
54
77
  }): Promise<Grants>;
55
- /** Spend policy, charges this period, cap state and enforcement (leases, overshoot bound). */
78
+ /**
79
+ * Spend policy, overage charges this period (`usage_charges_minor`), cap state (`overage` past an allowance under the
80
+ * cap), `exhausted_reason`, enforcement (leases, overshoot bound) and `spend_cap` (0.10.0).
81
+ */
56
82
  spend(organizationId: string): Promise<Spend>;
57
- /** Usage alert thresholds (changing them is an owner/billing browser action). */
83
+ /**
84
+ * Usage alert thresholds and the overage settings (0.10.0): `overage_available`, `overage_enabled`, `overage_state`
85
+ * (`unavailable`, `off`, `on`, `paused`), `spend_cap_minor` with its bounds, `rates` and `currency`. Read-only for API
86
+ * keys: owners and billing members change them in the console or with `ShardfluxAccount.billing.setSpendPolicy`.
87
+ */
58
88
  spendPolicy(organizationId: string): Promise<SpendPolicy>;
59
89
  }
60
90
  export {};
package/dist/usage.js CHANGED
@@ -7,7 +7,12 @@ export class UsageApi {
7
7
  const c = this.#ctx();
8
8
  return c.http.json('GET', path, query ? { query } : {}, c.authorization);
9
9
  }
10
- /** Current-period usage per meter, allowances with enforcement and cap state, measurement freshness. */
10
+ /**
11
+ * Current-period usage per meter, allowances with enforcement and cap state (`overage` while opt-in overage covers
12
+ * usage past a CPU-hours or RAM GiB-hours allowance), measurement freshness, `exhausted_reason` (the 402
13
+ * allowance_exhausted reason while starts are refused: `allowance_used`, `overage_paused`, `spend_cap_reached`) and
14
+ * `spend_cap` (0.10.0).
15
+ */
11
16
  summary(organizationId) {
12
17
  return this.#get(`/v1/organizations/${encodeURIComponent(organizationId)}/usage/summary`);
13
18
  }
@@ -26,7 +31,10 @@ export class UsageApi {
26
31
  workspace(workspaceId, params = {}) {
27
32
  return this.#get(`/v1/workspaces/${encodeURIComponent(workspaceId)}/usage`, { from: params.from, to: params.to, granularity: params.granularity, meter: params.meter });
28
33
  }
29
- /** Subscription fee, usage charges (0 while overage is disabled) and projected allowance use. */
34
+ /**
35
+ * Subscription fee, overage charges so far (`usage_charges_minor`, per meter in `usage_lines`), the estimated total,
36
+ * projected allowance use and `spend_cap` (0.10.0). Charges are 0 while overage is off or not on the plan.
37
+ */
30
38
  estimate(organizationId) {
31
39
  return this.#get(`/v1/organizations/${encodeURIComponent(organizationId)}/usage/estimate`);
32
40
  }
@@ -34,11 +42,18 @@ export class UsageApi {
34
42
  grants(organizationId, params = {}) {
35
43
  return this.#get(`/v1/organizations/${encodeURIComponent(organizationId)}/grants`, { limit: params.limit, cursor: params.cursor });
36
44
  }
37
- /** Spend policy, charges this period, cap state and enforcement (leases, overshoot bound). */
45
+ /**
46
+ * Spend policy, overage charges this period (`usage_charges_minor`), cap state (`overage` past an allowance under the
47
+ * cap), `exhausted_reason`, enforcement (leases, overshoot bound) and `spend_cap` (0.10.0).
48
+ */
38
49
  spend(organizationId) {
39
50
  return this.#get(`/v1/organizations/${encodeURIComponent(organizationId)}/spend`);
40
51
  }
41
- /** Usage alert thresholds (changing them is an owner/billing browser action). */
52
+ /**
53
+ * Usage alert thresholds and the overage settings (0.10.0): `overage_available`, `overage_enabled`, `overage_state`
54
+ * (`unavailable`, `off`, `on`, `paused`), `spend_cap_minor` with its bounds, `rates` and `currency`. Read-only for API
55
+ * keys: owners and billing members change them in the console or with `ShardfluxAccount.billing.setSpendPolicy`.
56
+ */
42
57
  spendPolicy(organizationId) {
43
58
  return this.#get(`/v1/organizations/${encodeURIComponent(organizationId)}/spend-policy`);
44
59
  }
@@ -2,7 +2,7 @@
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, WaitOptions, WorkspaceLifetime, WorkspaceOrigin, WorkspacePurpose, WorkspaceView } from './client.js';
5
+ import type { ClientContext, DiskLayout, ForkTarget, Operation, SuspendRequest, SuspendWhenIdleOptions, SuspendWhenIdleResult, WaitOptions, WorkspaceLifetime, 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';
@@ -100,6 +100,12 @@ export declare class Workspace {
100
100
  * an older API. A failed startup leaves the workspace running for inspection; the next open runs the failed step again.
101
101
  */
102
102
  get startup(): WorkspaceStartup | null;
103
+ /**
104
+ * The pending suspend-when-idle request as of the last view (0.10.0; `idle.suspend_request`): `{requested_at,
105
+ * after_seconds, not_before}`, or null when there is none, when the workspace is not running, or once a tool call or
106
+ * a resume after the request cancelled it. `refresh()` reads it again.
107
+ */
108
+ get suspendRequest(): SuspendRequest | null;
103
109
  /** The raw view (GET /v1/workspaces/{id}). */
104
110
  get data(): WorkspaceView;
105
111
  /**
@@ -145,6 +151,22 @@ export declare class Workspace {
145
151
  */
146
152
  suspend(opts: WaitedLifecycleOptions): Promise<FinishedOperation>;
147
153
  suspend(opts?: LifecycleOptions): Promise<Operation>;
154
+ /**
155
+ * Suspends the workspace once it has been idle for `afterSeconds` (30..3600; 0.9.0): call it when your agent's turn
156
+ * ends, so the workspace stops using RAM soon after instead of waiting out its idle policy. A command still running,
157
+ * an attached stream or a keepalive postpones the suspend until `afterSeconds` after it ends; the next tool call (the
158
+ * next turn) or a resume cancels it. Resolves with the recorded `suspendRequest` (also `workspace.suspendRequest`), or
159
+ * with the suspend already in progress as `operation`. See WorkspacesApi.suspendWhenIdle for the errors. A file-first
160
+ * workspace (never suspended) is refused locally with NotSupportedForModeError.
161
+ *
162
+ * const { suspendRequest } = await workspace.suspendWhenIdle({ afterSeconds: 60 });
163
+ */
164
+ suspendWhenIdle(opts: SuspendWhenIdleOptions): Promise<SuspendWhenIdleResult>;
165
+ /**
166
+ * Cancels a pending suspend-when-idle request (0.10.0; idempotent, in any state) and refreshes this handle's view.
167
+ * A suspend the request already started is not undone (it shows as `activeOperation`).
168
+ */
169
+ cancelSuspendWhenIdle(): Promise<this>;
148
170
  /**
149
171
  * Resumes a suspended workspace. Resolves when the resume is REQUESTED; with `{ wait: true }`, once the workspace runs.
150
172
  * Tool calls wake a suspended workspace by themselves, so this is rarely needed. With `wait` (0.9.0) it is one held
package/dist/workspace.js CHANGED
@@ -132,6 +132,14 @@ export class Workspace {
132
132
  get startup() {
133
133
  return this.#view.startup ?? null;
134
134
  }
135
+ /**
136
+ * The pending suspend-when-idle request as of the last view (0.10.0; `idle.suspend_request`): `{requested_at,
137
+ * after_seconds, not_before}`, or null when there is none, when the workspace is not running, or once a tool call or
138
+ * a resume after the request cancelled it. `refresh()` reads it again.
139
+ */
140
+ get suspendRequest() {
141
+ return this.#view.idle?.suspend_request ?? null;
142
+ }
135
143
  /** The raw view (GET /v1/workspaces/{id}). */
136
144
  get data() {
137
145
  return this.#view;
@@ -193,6 +201,32 @@ export class Workspace {
193
201
  return Promise.reject(refusal);
194
202
  return this.#ctx.workspaces.suspend(this.id, { ...this.#tracked(opts), [AFTER_WAIT]: this.#refreshAfterWait() });
195
203
  }
204
+ /**
205
+ * Suspends the workspace once it has been idle for `afterSeconds` (30..3600; 0.9.0): call it when your agent's turn
206
+ * ends, so the workspace stops using RAM soon after instead of waiting out its idle policy. A command still running,
207
+ * an attached stream or a keepalive postpones the suspend until `afterSeconds` after it ends; the next tool call (the
208
+ * next turn) or a resume cancels it. Resolves with the recorded `suspendRequest` (also `workspace.suspendRequest`), or
209
+ * with the suspend already in progress as `operation`. See WorkspacesApi.suspendWhenIdle for the errors. A file-first
210
+ * workspace (never suspended) is refused locally with NotSupportedForModeError.
211
+ *
212
+ * const { suspendRequest } = await workspace.suspendWhenIdle({ afterSeconds: 60 });
213
+ */
214
+ async suspendWhenIdle(opts) {
215
+ const refusal = this.#needsVm('suspend_when_idle');
216
+ if (refusal)
217
+ throw refusal;
218
+ const out = await this.#ctx.workspaces.suspendWhenIdle(this.id, opts);
219
+ this.#view = out.workspace.data;
220
+ return { ...out, workspace: this };
221
+ }
222
+ /**
223
+ * Cancels a pending suspend-when-idle request (0.10.0; idempotent, in any state) and refreshes this handle's view.
224
+ * A suspend the request already started is not undone (it shows as `activeOperation`).
225
+ */
226
+ async cancelSuspendWhenIdle() {
227
+ this.#view = (await this.#ctx.workspaces.cancelSuspendWhenIdle(this.id)).data;
228
+ return this;
229
+ }
196
230
  resume(opts = {}) {
197
231
  const refusal = this.#needsVm('resume');
198
232
  if (refusal)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shardflux/sdk",
3
- "version": "0.9.0",
3
+ "version": "0.10.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",