@pipelex/sdk 0.44.0 → 0.46.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/version.js CHANGED
@@ -3,5 +3,5 @@
3
3
  * `User-Agent` without importing the public barrel. Kept in sync with
4
4
  * `package.json` — enforced by `tests/index.test.ts`.
5
5
  */
6
- export const SDK_VERSION = "0.44.0";
6
+ export const SDK_VERSION = "0.46.0";
7
7
  //# sourceMappingURL=version.js.map
@@ -89,8 +89,8 @@ A run request carries its method inline exactly one of three ways: `mthds_conten
89
89
 
90
90
  Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one client, so no runner/registry abstraction):
91
91
 
92
- - `client.ts` — `PipelexApiClient`: the `request()` pipeline (auth, base URL, timeouts/abort, problem-details parsing) and every route. `implements MTHDSProtocol<DictPipeOutput>` so the protocol-execution methods stay shaped like the standard.
93
- - `models.ts` — Dict-serialized concretes (`DictStuff` / `DictWorkingMemory` / `DictPipeOutput` / `DictRunResultExecute`), the `/v1/validate` surface (`PipelexValidationResult`, `ValidationErrorItem`, and the `SuggestedFix` / `FixOp` repair vocabulary its items carry — the standard's declarations, restated because `mthds` exports them only from its package root and pinned to it by a type-level test, see [errors.md](./errors.md#a-validation-item--validationerroritem)), the tools surface (`Diagnostic` / `LintResponse` / `FormatResponse`), and the crate-route request/response models — the shared `MthdsFileItem` / `CrateRequestBase` / `CrateInvalidReport` vocabulary plus the `/v1/resolve`, `/v1/codegen` and `/v1/pipe-io` shapes built on it, `GeneratedArtifact` among them.
92
+ - `client.ts` — `PipelexApiClient`: the `request()` pipeline (auth, base URL, timeouts/abort, problem-details parsing) and every route. It implements the standard's `MTHDSProtocol<DictPipeOutput>` for every route but `validate`, so the protocol-execution methods stay shaped like the standard: `validate` answers Pipelex's own verdict, `PipelexValidationResult`, because the hosted plane still sends the verdict that predates the protocol's problem items, where the standard's `ValidationResult` lists them.
93
+ - `models.ts` — Dict-serialized concretes (`DictStuff` / `DictWorkingMemory` / `DictPipeOutput` / `DictRunResultExecute`), the `/v1/validate` surface (`PipelexValidationResult`, whose two arms are declared whole rather than as narrowings of the standard's `ValidationResult`, `ValidationErrorItem`, and the `SuggestedFix` / `FixOp` repair vocabulary its items carry — the hosted plane's shapes, which predate the standard's problem items, their operation and safety vocabularies pinned to the standard's by a type-level test, see [errors.md](./errors.md#a-validation-item--validationerroritem)), the tools surface (`Diagnostic` / `LintResponse` / `FormatResponse`), and the crate-route request/response models — the shared `MthdsFileItem` / `CrateRequestBase` / `CrateInvalidReport` vocabulary plus the `/v1/resolve`, `/v1/codegen` and `/v1/pipe-io` shapes built on it, `GeneratedArtifact` among them.
94
94
  - `product-models.ts` — the snake_case wire shapes for the hosted-product management routes (profile, methods catalog, organizations, billing, API keys, onboarding, storage, runs list/detail/update). Each interface models only the fields the product actually consumes — no speculative mirrors.
95
95
  - `error-models.ts` — the error-report wire models: `RunErrorReport`, the runner's `ErrorReport` typed with every field it carries (with `UserAction`, `ProviderErrorMetadata` and `MigrationErrorBlock`), which is the one type for a failed run's report on `RunPublic.error`, `PipelineRun.error`, `RunHistoryItem.error`, the failed arm of `RunResultState` and `RunFailedError.error`; and `ProblemDetails` / `FieldError`, the typed members of a problem document that `ApiResponseError` exposes. Every field is optional and every interface keeps an index signature, because the runner owns the shape and adds fields without asking. It also holds the one function that checks a stored report, the internal `readRunErrorReport`, which every read handing a report back applies: each named field is kept only when it has its declared type, and members the SDK does not name are relayed as sent. It imports the `ValidationErrorItem` type and nothing else. See [errors.md](./errors.md#a-failed-run--runerrorreport).
96
96
  - `bundle-resources.ts` — the files a bundle's steps read, sent as the request's `resources`: the `BundleResources` and `BundleResourceBytes` types the barrel exports, the runner's path rule (`bundleResourcePathRefusal`) and canonical spelling, and `encodeBundleResources`, which checks every key before it reads any value and turns the bytes into standard base64 through `upload.ts`'s `bytesToBase64`. Internal apart from its types; the hook's bundle gatherer reuses its path rule. See [bundle-resources.md](./bundle-resources.md).
@@ -101,7 +101,7 @@ Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one cli
101
101
  - `runs.ts` — the run-lifecycle types (`RunStatus`, `RunRead`, `RunResults`, `RunResultState`) and the single poll loop (`pollUntilResult`). `RunResults.main_stuff` (the resolved main output content) is always present for a completed run: on the hosted path it is the `main_stuff.json` S3 artifact; on the bare-runner blocking path the SDK resolves it from the returned working memory via the response's `main_stuff_name`, so both paths deliver the same shape. Consumers read `main_stuff` directly. `working_memory` — every named stuff of the run, `{ root, aliases }`, typed `DictWorkingMemory` from `models.ts` — reads the same on both paths: the hosted path relays the `working_memory.json` artifact, and the blocking path lifts it off `pipe_output`, which still rides whole (blocking path only). `graph_spec` — the executed graph — also reaches the client on both paths: the hosted path relays the `graphspec.json` artifact, and the blocking path lifts it off `pipe_output` beside `graph_assembly_error`, the graph's twin of `usage_assembly_error`. That error field is declared ahead of the wire on the hosted side, where the platform's results body relays no such key and the field is therefore absent, reading `undefined` rather than `null`. A completed run that cannot deliver a main stuff throws `MissingMainStuffError` when the read asked for it. `runs.ts` also declares `RUN_RESULT_ARTIFACTS` / `RunResultArtifact`, the artifacts a results read can be narrowed to with `getRunResult`'s `artifacts` option; with a selection an unselected artifact is absent while a selected unwritten one is `null`. `runs.ts` also owns `TokensUsageRecord` — one client-facing usage record per inference call, carried by `RunResults.tokens_usages` on both paths. That is a mirror of a wire contract this SDK does not own (inference accounting is a Pipelex runtime extension rather than part of the MTHDS standard): every field is optional and the interface carries an index signature so pre-contract artifacts (relayed verbatim, never migrated) still type-check. See [`run-usage.md`](./run-usage.md) for the field reference, the cost/null semantics, and the old-artifact rules, and [`run-results.md`](./run-results.md) for the field-by-field account of `RunResults` — the durable handle, the output and how to read it, the run's working memory, the executed graph and how to render or keep it, and the produced files with their short-lived links.
102
102
  - `usage.ts` — `summarizeUsage`, the **pure** fold of a run's usage pair (`tokens_usages` / `usage_assembly_error`) into one `UsageSummary`: a `state` (`records`, `no_inference` or `unavailable`), the null-aware total cost with its `cost_partial` flag, the `input` and `output` token totals, the call count, the relayed assembly error, and a `by_pipe` rollup sorted by cost with the unattributed calls grouped under a `null` `pipe_code`. It applies the rules [`run-usage.md`](./run-usage.md#summarizing-a-run--summarizeusage) states, so that every consumer reads one computation rather than re-deriving those rules by hand. It imports types from `runs.ts` and nothing else: no client, no fetch, no `mthds/protocol`.
103
103
  - `execute-result.ts` — `PipelexExecuteResult`, a `DictRunResultExecute` subtype that adds a resolved `.main_stuff` accessor (dug out of `pipe_output`'s working memory via the response's `main_stuff_name`, throwing `MissingMainStuffError` if unlocatable). `execute()` returns it, giving blocking and durable results the **same output accessor** — `result.main_stuff` — so callers never branch on which path ran. The neutral `mthds` wire model leaves `main_stuff_name` in its extension index signature; this Pipelex-branded subtype declares it as a typed field (Pipelex owns that concept). `resultsFromExecute` (the public lift from that result onto `RunResults`, in the same module) reads that accessor too, keeping the resolution single-sourced; the `executeBlocking` fallback is its only caller inside the SDK, and a consumer driving `execute()` directly calls it by name.
104
- - `errors.ts` — the error verdict and the typed errors. The verdict is `ErrorDomain` (`input`, `config`, `runtime`: who can fix the failure) and `ErrorVerdict` (that domain and `retryable`), carried as two readonly own properties by the abstract `PipelexRequestError`, which refines the protocol-base `PipelineRequestError` (re-exported from `mthds/protocol`) and takes the verdict as a constructor argument, so every subclass declares its verdict where it is defined and one that forgets it does not compile; `errorVerdictOf` reads the pair structurally from anything a `catch` holds. The base also carries `runMayHaveStarted`, false unless a request that may create a run failed in a way that may have started one, and `runMayHaveStartedOf` reads it structurally the same way. Under that base sit `RequestArgumentError` (the client refusing a call's arguments before sending any request, `input` unless the site declares otherwise, as the invalid base URL declares `config`), the transport and run-lifecycle errors, `PagingNotTerminatingError`, the input-preparation family under `InputPreparationError`, and the artifact family under `ArtifactOperationError` (`ScopeUnavailableError`, `ArtifactFetchError`, `ArtifactAuthenticationError`, the last carrying the download verdict so far); both family bases take an optional verdict for the sites, and the consumer subclasses, whose verdict is not the family's. `ApiResponseError` carries every member of the problem document it was built from, its nested members checked one level down by the readers a stored report goes through, and also stands for a 2xx answer the SDK could not read — the branch fields `errorDomain` and `type`, `retryable`, `userAction`, `requestId` (the body's, else the `X-Request-ID` header's), the native `code` and `errorType`, the platform's `errors[]`, the inference members, and the decoded document whole as `problemDocument` — and resolves `retryable` and `errorDomain` from the document's values when they are valid and the fallback's otherwise, except that a valid `input` or `config` domain sent without `retryable` is not retryable. Its member names match `mthds`'s own `ApiResponseError` wherever the two overlap, and so do their types except the verdict, which this SDK narrows to always decided; a type-level test pins the shared `ProblemDetails` and `UserAction` because this package may not import `mthds/errors`. `RunFailedError` carries the run's `status` and its stored report as `error`, and takes its verdict from that report. It is not the whole error surface: `codegen-check.ts` exports `CodegenLockError`, which extends plain `Error` because nothing was requested over the wire, and declares the two verdict members itself. See [errors.md](./errors.md#the-verdict-every-error-carries).
104
+ - `errors.ts` — the error verdict and the typed errors. The verdict is `ErrorDomain` (`input`, `config`, `runtime`: who can fix the failure) and `ErrorVerdict` (that domain and `retryable`), carried as two readonly own properties by the abstract `PipelexRequestError`, which refines the protocol-base `PipelineRequestError` (re-exported from `mthds/protocol`) and takes the verdict as a constructor argument, so every subclass declares its verdict where it is defined and one that forgets it does not compile; `errorVerdictOf` reads the pair structurally from anything a `catch` holds. The base also carries `runMayHaveStarted`, false unless a request that may create a run failed in a way that may have started one, and `runMayHaveStartedOf` reads it structurally the same way. Under that base sit `RequestArgumentError` (the client refusing a call's arguments before sending any request, `input` unless the site declares otherwise, as the invalid base URL declares `config`), the transport and run-lifecycle errors, `PagingNotTerminatingError`, the input-preparation family under `InputPreparationError`, and the artifact family under `ArtifactOperationError` (`ScopeUnavailableError`, `ArtifactFetchError`, `ArtifactAuthenticationError`, the last carrying the download verdict so far); both family bases take an optional verdict for the sites, and the consumer subclasses, whose verdict is not the family's. `ApiResponseError` carries every member of the problem document it was built from, its nested members checked one level down by the readers a stored report goes through, and also stands for a 2xx answer the SDK could not read — the branch fields `errorDomain` and `type`, `retryable`, `userAction`, `requestId` (the body's, else the `X-Request-ID` header's), the native `code` and `errorType`, the platform's `errors[]`, the inference members, and the decoded document whole as `problemDocument` — and resolves `retryable` and `errorDomain` from the document's values when they are valid and the fallback's otherwise, except that a valid `input` or `config` domain sent without `retryable` is not retryable. Beside those hosted members it carries `problem`, the same answer read as the standard reads it (`readAnswer` from `mthds/protocol`), the problem `mthds`'s own `ApiResponseError` carries, so a caller handling both clients branches on `problem.code`; the platform's `code` and `errors[]` stay on the error, apart from the registry's `problem.code` and the problem items of `problem.errors`. `RunFailedError` carries the run's `status` and its stored report as `error`, and takes its verdict from that report. It is not the whole error surface: `codegen-check.ts` exports `CodegenLockError`, which extends plain `Error` because nothing was requested over the wire, and declares the two verdict members itself. See [errors.md](./errors.md#the-verdict-every-error-carries).
105
105
  - `error-verdicts.ts` — `fallbackVerdict`, the table an `ApiResponseError` falls back to for a member the server did not send: the HTTP status, the platform `code`, and whether the body is named, which decides a `404`. Internal and pure. `pipelex-sdk` (Python) must apply the same table: `tests/fixtures/error-verdicts.json`, the case file this package's suite drives, is the contract, and the Python suite drives its own code through the identical copy `make shared-files` writes.
106
106
  - `run-may-have-started.ts` — the **pure** reading of whether a failed request that may create a run may still have started one: `answerMayHaveStartedRun(status, code)` for an answer and `transportMayHaveStartedRun(cause)` for a transport failure, every failure counting unless it proves otherwise (a failure of the connection's set-up, a `501`, the platform's `503` coded `pipeline_start_unavailable`, or any status that refuses the request). The client marks the requests that may create a run, `start` and the blocking `execute`, with a `RunCreation` flag it threads through `requestRaw`, `readObjectAnswer` and its error helpers into the constructors of `ApiUnreachableError` and `ApiResponseError`, which apply the reading and carry the answer as `runMayHaveStarted`, forcing `retryable` false when it is true; nothing changes an error after it is thrown. The Python twin is `pipelex_sdk/run_may_have_started.py`. See [errors.md](./errors.md#a-failed-run-start--runmayhavestarted).
107
107
  - `user-agent.ts` — client identification per the spec `conformance/specs/client-identification.md`, in the `conformance` repository beside the specs' tests: the exported `AppInfo` option, its RFC 9110 token validation (a `TypeError` on a bad field, a `RangeError` past the 512-character ceiling), its rendering, the runtime detection (Node, Bun, Deno, or a browser, where no header is set) and `buildUserAgent`, which the client calls once at construction. The barrel re-exports `buildUserAgent`, `validateAppInfo`, `MAX_USER_AGENT_LENGTH` and the `RuntimeInfo` type, so a consumer's raw fetches send the same value. The client's private `requestHeaders` is the one place API request headers are built, so every route carries the value; the artifact fetch of a presigned link never goes through it. See [client-identification.md](./client-identification.md).
@@ -133,7 +133,7 @@ The hosted catalog/account routes the webapp drives. All ride the same `{base}/v
133
133
  - **User profile:** `getMe` (`GET /v1/me`).
134
134
  - **Methods catalog:** `listMethods`, `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod`, `listMethodVersions`, `getMethodVersion`, `deleteMethod`. **A saved method has a draft and immutable published versions.** Its content fields (`mthds`, `python`, `input_data`) are its draft, which `writeDraft` (`PUT /v1/methods/{id}/draft`) replaces without validating it; `updated_at` is the draft's token, which moves on every draft write and nothing else, and `draft_digest` the SHA-256 of its two file sets. A draft write may carry `expected_updated_at`, the token the caller last saw, and is then a compare-and-swap: a draft that moved since is a `409` `method_update_conflict` and nothing is written. `renameMethod` (`PATCH /v1/methods/{id}`) changes the name alone and moves no token. `publishMethod` (`POST /v1/methods/{id}/publish`) takes the required `expected_draft_updated_at` and answers a `MethodPublishResult` discriminated on `outcome` — `published` with the new version, `unchanged` with the existing latest version when the draft's digest equals it, `refused` with a `reason` (`invalid` or `not_runnable`), a `message` and the runner's `validation` verdict — every arm carrying the `method`; the SDK reads the nested method as `getMethod` does, and an answer whose `outcome` is none of these is an answer it cannot read. A publish that produced no verdict is thrown rather than answered: only a draft that differs from the latest version reaches the runner, and a runner that cannot be reached, answers unusably or does not finish within the platform's deadline is a `502` or `503` with nothing written, so a retry is safe, and a retry of a publish that landed while its answer was lost answers `unchanged` with the version it wrote. A method is stored as one item that keeps room for the summary a publish adds, so a draft that saves can always be published: a creation or a draft write is a `413` `payload_too_large` when it would grow the method past that room, below the store's item limit, and a rename is the same `413` for a name so long it would leave the method too large to publish. A version is numbered from 1, never reused, and holds the draft's files as they stood, its `source_digest`, its `crate_fingerprint` (`null` when the runner could not resolve the bundle in memory), the runner's version, and when and by whom it was published; `listMethodVersions` pages the summaries newest first (`MethodVersionPage`, `{items, nextCursor}`, a `limit` from 1 to 100, 20 by default, and a `422` outside that range), and `getMethodVersion` reads one with its sources, its `python` parsed as a method's is. A method read carries `latest_version` and `latest_published`, both `null` until the first publish, so the publish state is derived: never published, draft unchanged (`draft_digest === latest_published.source_digest`), or draft ahead. The method routes take a bare id and address the method itself, never one of its versions; the platform does not parse a suffix there, so `mt_x@3` would answer a `404` that reads as a method that does not exist, and each route refuses a suffixed id with `RequestArgumentError` before anything is sent, naming `parseMethodSelector` and `getMethodVersion`. The whole-method `updateMethod` is gone, replaced by the draft write and the rename. **`deleteMethod` is asynchronous and its return type says so:** `DELETE /v1/methods/{id}` answers `202` with a `MethodDeletionAccepted` (`method_id`, `deletion_state`, `deletion_job_id`) the moment the platform has claimed the method and terminated its in-flight workflows — the rest of the cascade (runs, events, S3 objects) is enqueued. A resolved promise means "accepted", never "gone"; completion is the row disappearing from `listMethods`, not any field of that body. The claim is a conditional write, so a double-clicked delete is a `409 method_being_deleted` rather than a second cascade over the same runs, and an unknown or foreign-org id is a `404`. The erasure deletes the method's versions with the rest. (This doc previously stated the SDK had no `delete` because the platform had no such route — the route has always existed; the claim was simply false.) `listMethods` returns **one page**, not the whole catalog — a `MethodPage` (`{items, nextCursor}`) served from a narrow index projection, so its cost is the page size rather than the size of the catalog. It used to return every method with its full `.mthds` bundle attached, which hit DynamoDB's 1 MB page cap at a couple hundred methods and came back truncated with no error and no flag, so methods silently vanished from the UI. Rows are therefore `MethodSummary`, **not** `MethodData`: no `mthds`, no `python`, no `updated_at`, because none of them is in the projection and putting `mthds` back is what restored the truncation. Reach for `getMethod(id)` when you need a method's source. `query.q` is applied server-side across the whole catalog, not over the page — filtering one page client-side would be searching 50 rows of 10,000 and calling it a search. `query.published` filters the same way, sent as `published=true` (the methods with a published version) or `published=false` (those never published), and combines with `q`. Each row carries the method's publish state, so a list tells its methods apart without reading each one: `latest_version` and `latest_published` (a `MethodVersionSummary`), both `null` when the method was never published, and `draft_digest`, which is `null` on a row that never stored one until its next draft write, unlike on `MethodData`, because the list reads an index that holds no bundle to digest; all three are optional, absent from a platform that does not serve them. Never published is `latest_version === null`, the draft unchanged is `draft_digest === latest_published.source_digest`, and the draft ahead is two digests that differ. Both filters run after the index read, so **a filtered page can come back empty and still carry a `nextCursor`**: an empty page is not the end of the list, and only a `null` cursor is. `iterateMethods` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const m of client.iterateMethods())`); it keeps going **past empty pages**, since `{items: [], nextCursor: "…"}` means "nothing matched in the slice I just read, keep going". A server that keeps minting fresh cursors forever stops it at a page ceiling far past any real catalog with a `PagingNotTerminatingError` carrying `pageLimit`, never a silently truncated stream; `iterateRuns` shares the same loop. It gives up only when the server stops advancing its cursor, and **throws** rather than returning a partial answer; like `iterateRuns` it is deliberately not a `listAllMethods(): Promise<MethodSummary[]>`, because an all-at-once helper needs a page cap and a cap means silently returning a truncated list. `MethodSummary.deletion_state` rides the list rows: a method mid-erasure stays listed so the UI can render it as "Deleting…", while `getMethod` refuses it with a 409. Full read responses (`MethodData`, from `getMethod`) carry `org_id`, `created_by_user_id`, a server-derived `description` (recomputed from the draft's top-level `description` on every draft write), the draft token and digest and the latest version's summary on top of the write contract's `name` / `mthds` / `input_data`. `getMethodClosure` is a client-side semantic layer over `getMethod` (not a route) that parses the draft's polymorphic `mthds` source into a runnable, provenance-labelled closure — the draft, not what a bare id runs — see [input-preparation.md](./input-preparation.md#getmethodclosure--the-explicit-expansion-utility).
135
135
  - **Method examples:** `createMethodExample`, `listMethodExamples`, `iterateMethodExamples`, `getMethodExample`, `updateMethodExample`, `recordMethodExampleLatestRun`, `deleteMethodExample`, on `/v1/methods/{id}/examples`. An example is a named set of inputs saved on a method, shared across the organization, with an `input_revision` that rises each time its inputs change and the `latest_run_id` of those inputs, which an inputs edit clears. `recordMethodExampleLatestRun` records a run against the revision whose inputs it used and answers a `MethodExampleLatestRunResult` discriminated on `outcome`, `recorded` or `stale`, each arm carrying the `example`: a run that outlasted an edit is a verdict, never thrown, and an answer off the union is one the SDK cannot read; one helper, `readVerdict`, reads this verdict and `publishMethod`'s, the example checked once, by the reader every example goes through. Every answer carrying an example is read with `parseExactJson` and every request body written with `stringifyExactJson`, so an integer past `Number.MAX_SAFE_INTEGER` in the inputs reads as a `bigint` and goes back digit for digit, to an edit or as a run's inputs; `3.0` still reads as `3`, and inputs holding one sent back unchanged are an edit for the platform, which compares inputs as canonical JSON. Every example the client hands back, the page rows and the verdict's `example` included, is checked by `readMethodExample` against the fields the Python SDK's model requires, with the strict types it holds them to, an example breaking one being an answer the SDK cannot read. The list is paged oldest first by an opaque cursor, and a page may be short while `nextCursor` is set; `iterateMethodExamples` follows it through the loop `iterateMethods` and `iterateRuns` share, and refuses a method id at the call rather than at the first pull. Every method takes a bare catalog id, and the client refuses an example id that is empty, `.` or `..` or holds a lone surrogate, a create without its name or inputs or whose `run_id` is given but empty, an edit naming neither `name` nor `inputs`, and a latest-run write without a run id or a positive integer revision before sending. See [method-examples.md](./method-examples.md).
136
- - **Organizations:** `listMemberships`, `createOrganization`, `renameOrganization` (`PATCH`). The org "switch" is a WorkOS-session op (`/api/active-org`), not a `/v1` route — out of SDK scope.
136
+ - **Organizations:** `listMemberships`, `createOrganization`, `renameOrganization` (`PATCH`). Each membership carries the caller's `role_in_org`, an `OrgRole` (`admin`, `member` or `runner`) read open: a role a later platform adds arrives as its raw string, so a `switch` over it says what an unknown role does. The role is for display; what it may do is the platform's to decide, and a call refused to the caller's role is an `ApiResponseError`. The org "switch" is a WorkOS-session op (`/api/active-org`), not a `/v1` route — out of SDK scope.
137
137
  - **Billing:** `getSubscription`, `listPlans`, `listInvoices`, `createCheckout`, `changePlan` (409 `conflict` ⇒ no subscription), `getBillingPortal` (409 `conflict` ⇒ no subscription).
138
138
  - **Pipelex API keys (`plx_sk_…`):** `listPipelexApiKeys`, `createPipelexApiKey` (409 `pipelex_api_key_limit_reached`), `revokePipelexApiKey`, `rotatePipelexApiKey`. The plaintext `api_key` is returned once, on create/rotate.
139
139
  - **Onboarding:** `submitOnboarding`.
package/docs/errors.md CHANGED
@@ -28,7 +28,7 @@ try {
28
28
  }
29
29
  ```
30
30
 
31
- `errorVerdictOf` checks the shape rather than the class: it returns the pair from any `Error` carrying a boolean `retryable` and a known `errorDomain`, and `undefined` otherwise. So it reads an error raised by another copy of the SDK installed beside yours, a subclass of your own, and an `mthds` `ApiResponseError` whose runner sent both members. Every class also exposes the two members directly, and they derive from the abstract `PipelexRequestError`, itself a `PipelineRequestError`, so `instanceof PipelineRequestError` still matches all of them.
31
+ `errorVerdictOf` checks the shape rather than the class: it returns the pair from any `Error` carrying a boolean `retryable` and a known `errorDomain`, and `undefined` otherwise. So it reads an error raised by another copy of the SDK installed beside yours and a subclass of your own. An error of the standard's own client, `mthds`, carries its verdict on its problem instead, as `domain` and `retry` in the standard's vocabulary, and `errorVerdictOf` reads it as that problem's verdict in this SDK's vocabulary, whatever the runner's body held beside it. `problemVerdict` does the translation for any problem of the standard: the standard's domain maps onto this SDK's by who acts — `input` and `method` read as `input`, `account` and `operator` as `config`, `runtime` as `runtime` — and only a `retry` of `yes` is retryable, since an outcome that is unknown may already have taken effect. A domain or a retry verdict the standard's reader does not know reads by its own fallbacks (`problemDomain`, `problemRetry`), and the Python SDK applies the same mapping. Every class also exposes the two members directly, and they derive from the abstract `PipelexRequestError`, itself a `PipelineRequestError`, so `instanceof PipelineRequestError` still matches all of them.
32
32
 
33
33
  **"Retryable" means a retry can succeed, and never one that could start a second paid run.** The verdict says whether asking again can plausibly succeed, and on most routes it says nothing about whether the first attempt had an effect. A request that may create a run, a `start` or a blocking `execute`, is the exception: when it failed in a way that may have started a run anyway, the error's `runMayHaveStarted` is `true` and its `retryable` is `false`, whatever the server sent and whatever the status says, since asking again could start the run twice. [A failed run start](#a-failed-run-start--runmayhavestarted) says when that is and what to tell a person.
34
34
 
@@ -259,7 +259,7 @@ So `caller_facing_message` is how a consumer tells the author's copy from the pl
259
259
 
260
260
  Every non-2xx answer from a `/v1` route is a problem document, and the SDK throws an `ApiResponseError` whose fields are its members. Each field is `undefined` when the document did not carry it, and a member of the wrong type (a numeric `type`, a `retryable` of `"no"`, a `user_action` without a `detail`) reads as absent rather than as a wrong value. The nested members are checked one level down, exactly as a [stored report's](#a-failed-run--runerrorreport) are: `providerMetadata` and `migration` keep each named field only when it has its type, `validationErrors` keeps only the items that are objects with a string `category` and `message`, and each item of `errors` keeps its `field`, `code` and `detail` only when they are strings; members the SDK does not name are relayed as sent. The verdict is the exception: `errorDomain` and `retryable` are always decided, the server's value when it sent a valid one and otherwise [as the verdict section says](#a-refused-requests-verdict).
261
261
 
262
- **An answer the SDK could not read is an `ApiResponseError` too.** A `2xx` whose body is not JSON, such as a gateway's HTML page or an empty body where the route answers one, is no result the SDK can hand back, so every route throws an `ApiResponseError` for it instead of letting the runtime's `SyntaxError` escape. So is a `2xx` read exactly, such as a run's results or a method example, that holds a number past `Number.MAX_SAFE_INTEGER` on a runtime whose `JSON.parse` hands a reviver no source text, which could only read it rounded; its `cause` is the reader's `ExactJsonUnsupportedError` ([exact-integers.md](./exact-integers.md)). So is a `2xx` whose body is JSON but not the object the route answers — `null`, a number, a string, an array — on every protocol, extension and run-lifecycle route, on `health()`, and on the product routes whose answer the SDK or its caller reads before anything else (`listMethods`, `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod`, `listMethodVersions`, `getMethodVersion`, the method example routes, `listRuns`, `getRunDetail`, `upload`, `requestUploadGrant`, `resolveStorageUrls`), instead of the `TypeError` that reading it would raise. So is an object missing the member those reads depend on: a page of `listMethods`, `listMethodVersions`, `listMethodExamples` or `listRuns` whose `items` is not an array or whose `next_cursor` is neither a string nor `null`, which the iterators could not walk or would follow forever; a publish result whose `outcome` is not `published`, `unchanged` or `refused`, or whose `method` is not an object, and a latest-run result whose `outcome` is not `recorded` or `stale`, or whose `example` is not an object, which a caller branching on `outcome` could not read; a method example, as `createMethodExample`, `getMethodExample` and `updateMethodExample` answer it, as a row of `listMethodExamples` and as a latest-run result's `example`, that misses a field the Python SDK's model of an example requires or holds one of another type, its message naming the field; an `upload` answer with no non-empty string `uri`, which names no stored file; and a stored method or a published version whose `mthds` field is neither a string nor absent (a `null` one is still no source, which `getMethodClosure` reports as `EmptyMethodSourceError`) or whose `python` field is not the serialized `[{ name, content }]` list, as `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod` and `getMethodVersion` answer it: that is server data no change to the call fixes, not an argument the caller passed. Its `status` is the `2xx`, `responseBody` the raw text, `cause` what made the answer unreadable (the parse failure, or `mthds`'s refusal of the `python` field; nothing for a body that parsed to something the route does not answer), `problemDocument` and the problem members are `undefined`, and its verdict is the fallback's for a status no refusal carries: `runtime`, not retryable. On `start` and `execute` its `runMayHaveStarted` is `true`, since the server accepted the request; that includes a start acknowledgement whose `pipeline_run_id` is not a string, and an `execute` `202` that names no run, which leave the caller a run it could not follow, and an `execute` answer that is not the completed run: one whose `pipeline_run_id` is not a string, or whose `pipe_output` carries no string `pipeline_run_id` or no `working_memory` with `root` and `aliases` objects, the members the Python SDK's model of the answer requires. A product route that answers a `2xx` with no body at all, such as `revokePipelexApiKey`, still resolves to `undefined`, and the other product routes hand their decoded body back as it came.
262
+ **An answer the SDK could not read is an `ApiResponseError` too.** A `2xx` whose body is not JSON, such as a gateway's HTML page or an empty body where the route answers one, is no result the SDK can hand back, so every route throws an `ApiResponseError` for it instead of letting the runtime's `SyntaxError` escape. Every body is read by the rules the protocol reads a problem body by (`parseJsonBody` in `mthds/protocol`, as `pipelex-sdk` reads it), so JSON nested deeper than `MAX_BODY_NESTING`, or holding a number past the range of a double, such as `1e999` or an integer that long, which `JSON.parse` reads as `Infinity`, is a body that is not JSON too; a refusal whose body is such JSON carries no hosted member and no `problemDocument`. So is a `2xx` read exactly, such as a run's results or a method example, that holds a number past `Number.MAX_SAFE_INTEGER` on a runtime whose `JSON.parse` hands a reviver no source text, which could only read it rounded; its `cause` is the reader's `ExactJsonUnsupportedError` ([exact-integers.md](./exact-integers.md)). So is a `2xx` whose body is JSON but not the object the route answers — `null`, a number, a string, an array — on every protocol, extension and run-lifecycle route, on `health()`, and on the product routes whose answer the SDK or its caller reads before anything else (`listMethods`, `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod`, `listMethodVersions`, `getMethodVersion`, the method example routes, `listRuns`, `getRunDetail`, `upload`, `requestUploadGrant`, `resolveStorageUrls`), instead of the `TypeError` that reading it would raise. So is an object missing the member those reads depend on: a page of `listMethods`, `listMethodVersions`, `listMethodExamples` or `listRuns` whose `items` is not an array or whose `next_cursor` is neither a string nor `null`, which the iterators could not walk or would follow forever; a publish result whose `outcome` is not `published`, `unchanged` or `refused`, or whose `method` is not an object, and a latest-run result whose `outcome` is not `recorded` or `stale`, or whose `example` is not an object, which a caller branching on `outcome` could not read; a method example, as `createMethodExample`, `getMethodExample` and `updateMethodExample` answer it, as a row of `listMethodExamples` and as a latest-run result's `example`, that misses a field the Python SDK's model of an example requires or holds one of another type, its message naming the field; an `upload` answer with no non-empty string `uri`, which names no stored file; and a stored method or a published version whose `mthds` field is neither a string nor absent (a `null` one is still no source, which `getMethodClosure` reports as `EmptyMethodSourceError`) or whose `python` field is not the serialized `[{ name, content }]` list, as `getMethod`, `createMethod`, `writeDraft`, `renameMethod`, `publishMethod` and `getMethodVersion` answer it: that is server data no change to the call fixes, not an argument the caller passed. Its `status` is the `2xx`, `responseBody` the raw text, `cause` what made the answer unreadable (the parse failure, or `mthds`'s refusal of the `python` field; nothing for a body that parsed to something the route does not answer, nor for JSON the protocol refuses), `problemDocument` and the hosted problem members are `undefined`, `problem` is the standard's reading of such an answer (`client.response_unreadable`, or `client.outcome_unknown` on `start` and `execute`), and its verdict is the fallback's for a status no refusal carries: `runtime`, not retryable. On `start` and `execute` its `runMayHaveStarted` is `true`, since the server accepted the request; that includes a start acknowledgement whose `pipeline_run_id` is not a string, and an `execute` `202` that names no run, which leave the caller a run it could not follow, and an `execute` answer that is not the completed run: one whose `pipeline_run_id` is not a string, or whose `pipe_output` carries no string `pipeline_run_id` or no `working_memory` with `root` and `aliases` objects, the members the Python SDK's model of the answer requires. A product route that answers a `2xx` with no body at all, such as `revokePipelexApiKey`, still resolves to `undefined`, and the other product routes hand their decoded body back as it came.
263
263
 
264
264
  | Field | Wire member | Meaning |
265
265
  |---|---|---|
@@ -280,6 +280,7 @@ Every non-2xx answer from a `/v1` route is a problem document, and the SDK throw
280
280
  | `errors` | `errors` | The platform's field-level failures, `{ field, code, detail }` each. |
281
281
  | `problemDocument` | the whole body | The decoded document, every member named or not; `undefined` when the body was not a JSON object. The record of what the server sent, verdict members included. |
282
282
  | `status`, `statusText`, `responseBody` | the transport | The HTTP status, its text and the raw body. |
283
+ | `problem` | the answer, read by the standard | The same answer as the MTHDS standard reads it, the problem `mthds`'s own `ApiResponseError` carries (see below). |
283
284
 
284
285
  **Branch on `errorDomain` and `type`, never on the HTTP status or the message.** That is the rule the hosted-envelope spec sets across surfaces: `errorDomain` tells the caller's own mistake from a fault it cannot fix, and `type` names the error class with a URI that stays the same on every occurrence, and both mean the same on the runner and the platform. `code` and `errorType` are each surface's finer native code, and a client of one surface may branch on that surface's code too. On the platform `type` is `https://pipelex.com/errors/<code>`, derived from `code` one to one, so branching on either reads the same answer; the platform may add codes, so a code the caller does not know gets its generic handling. A refusal the platform relays from the runner, such as a run route's refusal to run an invalid method, carries the runner's `type` and `errorType` and no `code`, so a caller branching on `code` reads its absence as a refusal the runner authored and branches on `type` or `errorType` there. Where the platform sends no `error_domain`, as its Lambda-backed routes do not yet, the fallback reads `errorDomain` from the status, so the branch below reads the same whichever decided it:
285
286
 
@@ -300,7 +301,7 @@ try {
300
301
  }
301
302
  ```
302
303
 
303
- The members `mthds`'s own `ApiResponseError` carries (`type`, `title`, `instance`, `requestId`, `errorDomain`, `retryable`, `userAction`) have the same names here, and the same types except the verdict: the standard's client leaves `errorDomain` an open string and `retryable` optional, where this SDK decides both. A type-level test pins `ProblemDetails`, the members as parsed, to the standard client's declaration, so a consumer reads one vocabulary whichever client raised the error. The rest are the Pipelex members the standard's client leaves to this SDK.
304
+ **`problem` is the standard's reading of the same answer.** The MTHDS standard's own client, `mthds`, reads every answer that is not a route's result as one problem under a registry code, and `problem` is that reading, the problem `mthds`'s `ApiResponseError` carries, typed as the standard's `Problem`: the body as received when it is a core problem document, otherwise the problem the standard reads the answer as by its status (`readAnswer` in `mthds/protocol`), and `client.response_unreadable` for a `2xx` the SDK could not read. On `start` and `execute`, `runMayHaveStarted` decides: wherever a run may have started, which is a `2xx` the SDK could not read, a `408` and every `5xx` but a `501` and a `503` carrying `pipeline_start_unavailable`, it is `client.outcome_unknown`, naming the run when the body names one, such as the platform's `bad_gateway` for a start whose acknowledgement it lost, even when the body is a core problem document, since one inviting a retry would contradict `runMayHaveStarted`; wherever none did, it is the reading of an answer to a request that creates no run, `runtime.unavailable` for that `503` and `client.response_unreadable` for that `501`. A caller handling this SDK's errors and the standard client's alike branches on `problem.code`. The hosted plane's problem documents predate the core registry, so for them `problem` says little, and every member of the table above is read from the hosted document as it always was. The two never share a slot: `code` is the platform's code beside the registry's `problem.code`, and `errors` the platform's field-level failures beside the problem items of `problem.errors`. The error itself stays a `PipelineRequestError` rather than the standard's `ProblemError`, whose `code` is the registry's, so that `code` keeps meaning the platform's.
304
305
 
305
306
  ### The method codes
306
307
 
@@ -343,6 +344,6 @@ if (!report.is_valid) {
343
344
  }
344
345
  ```
345
346
 
346
- **The shape is `mthds`'s.** The standard's client declares the same item and the same fix vocabulary (`SuggestedFix`, `FixOp` and its arms, `FixSafety`, `FixOpKind`, `FixValue`), member for member under the same names, and this SDK exports every one of those names, so a consumer reads one vocabulary whichever client handed it the item. `@pipelex/sdk` imports `mthds` only through `mthds/protocol`, and `mthds` exports these declarations from its package root, so they are restated in `src/models.ts` and a type-level test pins them to the standard's: a member added on one side and not the other fails `make check`. The one difference the test allows is `template_file` and `template_location`, which the runner's item carries and the `mthds` this SDK depends on does not declare yet; the test fails the day `mthds` declares them, so the allowance goes then.
347
+ **The shape is the hosted plane's.** It is the item the runner sends today, which predates the standard's problem items: from protocol 2.0.0 a validate verdict lists its faults as problem items under a registry `code`, which the hosted plane does not send yet, so this SDK declares the item it receives, in `src/models.ts`, and it is not the standard's. Its fix shares the standard's operation kinds (`FixOpKind`), safeties (`FixSafety`) and written values (`FixValue`, which this SDK also calls `TomlValue`), and a type-level test pins those to the standard's, so a kind added on one side and not the other fails `make check`. The standard's own `SuggestedFix` names its file `file` rather than `source` and is open to members it does not declare.
347
348
 
348
- **An unset member can be `null`.** That is the one way this declaration differs from the standard's. The verdict's valid arm, which carries the advisory `warnings[]`, sends an unset member as an explicit `null`, where every other channel leaves the key out, so every optional member is typed `| null`. Read a member with a truthiness check or `??`, never with `=== undefined`.
349
+ **An unset member can be `null`.** The verdict's valid arm, which carries the advisory `warnings[]`, sends an unset member as an explicit `null`, where every other channel leaves the key out, so every optional member is typed `| null`. Read a member with a truthiness check or `??`, never with `=== undefined`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pipelex/sdk",
3
- "version": "0.44.0",
3
+ "version": "0.46.0",
4
4
  "description": "TypeScript SDK for the Pipelex hosted API — execute methods, manage runs, and call the product surface.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -74,7 +74,7 @@
74
74
  "vitest": "^4.0.18"
75
75
  },
76
76
  "dependencies": {
77
- "mthds": "^0.30.0",
77
+ "mthds": "^0.32.1",
78
78
  "smol-toml": "^1.6.0",
79
79
  "undici": "^7.29.1"
80
80
  }