@pipelex/sdk 0.18.1 → 0.19.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/README.md CHANGED
@@ -77,6 +77,20 @@ try {
77
77
 
78
78
  The full client surface is documented in [`docs/architecture.md`](./docs/architecture.md).
79
79
 
80
+ ## Documentation
81
+
82
+ These pages ship inside the published package, so a reader who has only installed it opens them under `node_modules/@pipelex/sdk/docs/` — at the version being called, rather than whatever the repository's default branch says today. They are also browsable at [`Pipelex/pipelex-sdk-js/tree/main/docs`](https://github.com/Pipelex/pipelex-sdk-js/tree/main/docs), which is the address to give someone who has not installed the package.
83
+
84
+ | Page | What it covers |
85
+ | --- | --- |
86
+ | [`docs/architecture.md`](./docs/architecture.md) | The whole client surface: the request pipeline, every route, the typed errors |
87
+ | [`docs/run-results.md`](./docs/run-results.md) | Every field of `RunResults` — the run id as a durable handle, `main_stuff`, `working_memory`, `graph_spec`, the usage pair, produced files |
88
+ | [`docs/run-usage.md`](./docs/run-usage.md) | What a run consumed, record by record, and `summarizeUsage` which folds them into one reading |
89
+ | [`docs/artifact-download.md`](./docs/artifact-download.md) | Turning the `pipelex-storage://` references a run produced back into bytes: `collectArtifacts`, `resolveArtifacts`, `fetchArtifact`, `downloadArtifacts` |
90
+ | [`docs/input-preparation.md`](./docs/input-preparation.md) | The other direction — `uploadFile` and `prepareInputs`, which turn local files into references a run can take |
91
+ | [`docs/crate-routes.md`](./docs/crate-routes.md) | `resolve` and `codegen`, and the offline `runCodegenCheck` that guards a committed tree |
92
+ | [`docs/build-routes.md`](./docs/build-routes.md) | The `/v1/build/*` projections: `buildInputs`, `buildOutput`, `buildRunner` |
93
+
80
94
  ## Develop
81
95
 
82
96
  ```bash
package/dist/index.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * errors. Consumers should import everything they need from `@pipelex/sdk`.
8
8
  */
9
9
  /** Package version. Kept in sync with `package.json` — enforced by `tests/index.test.ts`. */
10
- export declare const SDK_VERSION = "0.18.1";
10
+ export declare const SDK_VERSION = "0.19.0";
11
11
  export * from "mthds/protocol";
12
12
  export { PipelexApiClient, DEFAULT_API_BASE_URL } from "./client.js";
13
13
  export type { MthdsFile, PipelexApiRunExtensions, PipelexHostedRunExtensions, PipelexRunOptions, PipelexStartOptions, ValidateMethodSelector, ValidateFilesOptions, PipelexApiClientOptions, } from "./client.js";
package/dist/index.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * errors. Consumers should import everything they need from `@pipelex/sdk`.
8
8
  */
9
9
  /** Package version. Kept in sync with `package.json` — enforced by `tests/index.test.ts`. */
10
- export const SDK_VERSION = "0.18.1";
10
+ export const SDK_VERSION = "0.19.0";
11
11
  // ── Pure MTHDS Protocol surface (re-exported from the `mthds/protocol` subpath) ──
12
12
  // The standard's interface, wire models, request/options surface, abstract domain
13
13
  // shapes, and the protocol-base `PipelineRequestError`. Re-exported so apps have a
package/dist/models.d.ts CHANGED
@@ -193,17 +193,21 @@ export interface PipelexValidationReport extends ValidationReport {
193
193
  /** Pipes the runtime may skip when an optional slot resolves absent. */
194
194
  liftable_pipes: LiftablePipeEntry[];
195
195
  /**
196
- * The qualified `pipe_ref` a caller gets by omitting the pipe selector on the run
197
- * and build routes — the bundle's declared `main_pipe` qualified by its domain and,
198
- * for a `method_ref` package, the manifest's `main_pipe` the way the runner's
199
- * `resolve_requested_pipe` reads it. `null` when the closure declares none or
200
- * several.
196
+ * The qualified ref of the pipe a selector-less run of THIS request would execute —
197
+ * the primary blueprint's declared `main_pipe` qualified by its domain and, for a
198
+ * `method_ref` package, the manifest's `main_pipe` qualified against the closure the
199
+ * way the runner's `resolve_requested_pipe` reads it. `null` when the server
200
+ * determined no entry pipe: no blueprint declares a `main_pipe`, or the manifest
201
+ * names a pipe the closure does not declare or declares in several domains. It
202
+ * carries the RUN default, so a closure whose domains each declare a `main_pipe`
203
+ * gets the first declaring blueprint's ref rather than `null` — that is the pipe
204
+ * `execute` would take.
201
205
  *
202
- * OPTIONAL because it is younger than the report: a server that predates it sends
203
- * nothing, and a consumer that needs a default (`prepareInputs`) reads it when
204
- * present and falls back to the opaque `bundle_blueprint.main_pipe`. It exists so
205
- * that descriptor consumers stop reading a blueprint this SDK types opaque on
206
- * purpose.
206
+ * Read it on PRESENCE, in three arms: a non-empty string is the default; a stated
207
+ * `null` is the server's verdict that there is none, and a consumer stops there
208
+ * rather than deriving one of its own; an ABSENT field means the server predates
209
+ * the field, and only then does a blueprint-derived default stand. That is the rule
210
+ * `prepareInputs` applies and the one `pipelex-mcp` states in its `SPEC.md`.
207
211
  */
208
212
  default_pipe_ref?: string | null;
209
213
  /** Best-effort execution graph of the main pipe; `null` with no `main_pipe` or on degrade. */
@@ -263,10 +263,21 @@ async function fetchSignature(client, selector) {
263
263
  return result;
264
264
  }
265
265
  /**
266
- * Pick the pipe whose descriptor guides the walk, in the order `docs/input-preparation.md`
267
- * documents: an explicit qualified `pipe_ref`, then the report's typed resolved
268
- * default, then the bundle's declared `main_pipe`, then the single pipe — else an
269
- * error naming the candidates.
266
+ * Pick the pipe whose descriptor guides the walk, in the order
267
+ * `docs/input-preparation.md` documents: an explicit qualified `pipe_ref`, then
268
+ * the report's resolved default — read on the field's PRESENCE, never on its
269
+ * truthiness — and, behind an ABSENT field only, the bundle's declared
270
+ * `main_pipe` then the single pipe.
271
+ *
272
+ * `default_pipe_ref` answers the very question this function asks: the qualified
273
+ * ref of the pipe a selector-less run of THIS request would execute. A stated
274
+ * `null` is that answer too — the server determined no entry pipe, and such a run
275
+ * is refused by the run route — so reading it as silence and falling through would
276
+ * prepare a pipe the run will not execute. Only a field the report does not carry
277
+ * at all (a runner predating it) leaves the blueprint and single-pipe arms
278
+ * standing. A JSON body cannot carry an own property holding `undefined`, and the
279
+ * parsed body is handed through untouched, so strict `=== undefined` is the whole
280
+ * absence test.
270
281
  */
271
282
  function selectPipeRef(report, inputForm, requested) {
272
283
  const refs = Object.keys(inputForm);
@@ -281,13 +292,30 @@ function selectPipeRef(report, inputForm, requested) {
281
292
  }
282
293
  return requested;
283
294
  }
284
- // The typed resolved default, when the runner serves it (manifest-aware for a
285
- // `method_ref` package, which is why it outranks the blueprint read below).
286
- const typedDefault = nonEmptyString(report.default_pipe_ref);
287
- if (typedDefault !== undefined && typedDefault in inputForm)
288
- return typedDefault;
289
- // `bundle_blueprint` is opaque transport in this SDK on purpose, so read it
290
- // defensively and fall through rather than trust it.
295
+ // The resolved default, when the runner serves the field at all (manifest-aware
296
+ // for a `method_ref` package, which is why it outranks the blueprint read below).
297
+ if (report.default_pipe_ref !== undefined) {
298
+ const statedDefault = nonEmptyString(report.default_pipe_ref);
299
+ if (statedDefault === undefined) {
300
+ // A stated `null` — or anything else that is not a non-empty string — is the
301
+ // server's verdict, not a gap: no entry pipe was determined, so a run naming
302
+ // no pipe would not resolve one either. Neither fallback stands behind it.
303
+ throw new InputPreparationError(`Cannot prepare inputs: the server determined no entry pipe for this method, so a run that ` +
304
+ `names no pipe would not resolve one (no \`main_pipe\` is declared, or the package manifest ` +
305
+ `names a pipe the closure does not declare or declares in several domains). Pass ` +
306
+ `\`pipe_ref\`. It declares: ${candidates}.`);
307
+ }
308
+ if (!(statedDefault in inputForm)) {
309
+ // The default and the descriptor come from one report keyed by one pipe set, so
310
+ // a miss is the report contradicting itself — falling through would silently
311
+ // prepare a different pipe than the one the run would execute.
312
+ throw new InputPreparationError(`Cannot prepare inputs: the validate report names "${statedDefault}" as the default pipe, but ` +
313
+ `its \`input_form\` descriptor does not describe it. Pass \`pipe_ref\`. It declares: ${candidates}.`);
314
+ }
315
+ return statedDefault;
316
+ }
317
+ // Behind an ABSENT field only. `bundle_blueprint` is opaque transport in this SDK
318
+ // on purpose, so read it defensively and fall through rather than trust it.
291
319
  const blueprintDefault = readBlueprintMainPipeRef(report.bundle_blueprint);
292
320
  if (blueprintDefault !== undefined && blueprintDefault in inputForm)
293
321
  return blueprintDefault;
@@ -1 +1 @@
1
- {"version":3,"file":"prepare-inputs.js","sourceRoot":"","sources":["../src/prepare-inputs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAQH,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAQpD,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,sBAAsB,GAAG,oBAAoB,CAAC;AACpD,MAAM,WAAW,GAAG,eAAe,CAAC;AAgEpC,2FAA2F;AAC3F,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,MAAM,CAAC,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,iBAAiB,CAAC;AACrE,CAAC;AAED,+EAA+E;AAC/E,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,OAAO,OAAO,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;AAClD,CAAC;AAED;;;;GAIG;AACH,SAAS,aAAa,CAAC,IAAa;IAClC,OAAO,aAAa,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,CAAC;AAC9C,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,KAAc;IACxC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAChC,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,SAAS,IAAI,KAAK,IAAI,SAAS,IAAI,KAAK,CAAC;AACvE,CAAC;AAED,0DAA0D;AAC1D,SAAS,aAAa,CAAC,OAAe;IACpC,MAAM,KAAK,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QACd,MAAM,IAAI,qBAAqB,CAC7B,4CAA4C,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CACpE,CAAC;IACJ,CAAC;IACD,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,gBAAgB;IACxD,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IACzC,MAAM,QAAQ,GAAG,UAAU,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,WAAW,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,0BAA0B,CAAC;IACvE,gFAAgF;IAChF,kFAAkF;IAClF,iFAAiF;IACjF,IAAI,CAAC;QACH,IAAI,QAAQ,EAAE,CAAC;YACb,0EAA0E;YAC1E,8EAA8E;YAC9E,mFAAmF;YACnF,gFAAgF;YAChF,6DAA6D;YAC7D,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;QACxD,CAAC;QACD,MAAM,IAAI,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC;QACzC,OAAO,EAAE,KAAK,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,WAAW,EAAE,CAAC;IAChE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,qBAAqB,CAC7B,+BAA+B,QAAQ,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,0BAA0B,MAAM,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EACpH,EAAE,KAAK,EAAE,CACV,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CAAC,MAAc;IACnC,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,KAAK,IAAI,CAAC;QAAE,KAAK,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/F,OAAO,KAAK,CAAC;AACf,CAAC;AAED,mGAAmG;AACnG,SAAS,aAAa,CAAC,GAAmB,EAAE,MAAe;IACzD,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,OAAO,GAAG,eAAe,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC7C,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,KAAK,UAAU,eAAe,CAAC,GAAmB,EAAE,MAAe;IACjE,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;QAC/B,IAAI,MAAM,CAAC,UAAU,CAAC,sBAAsB,CAAC;YAAE,OAAO,MAAM,CAAC,CAAC,mBAAmB;QACjF,IAAI,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,CAAC,+BAA+B;QAC5E,IAAI,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;YAC/B,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;YACrD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;YACpE,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACzB,OAAO,MAAM,CAAC,GAAG,CAAC;QACpB,CAAC;QACD,iFAAiF;QACjF,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC,GAAG,CAAC;IACpB,CAAC;IACD,IAAI,MAAM,YAAY,IAAI,IAAI,MAAM,YAAY,WAAW,IAAI,MAAM,YAAY,UAAU,EAAE,CAAC;QAC5F,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC,GAAG,CAAC;IACpB,CAAC;IACD,kFAAkF;IAClF,oFAAoF;IACpF,gEAAgE;IAChE,MAAM,IAAI,qBAAqB,CAC7B,uGAAuG;QACrG,kFAAkF,OAAO,MAAM,GAAG,CACrG,CAAC;AACJ,CAAC;AAED,qGAAqG;AACrG,KAAK,UAAU,mBAAmB,CAAC,GAAmB,EAAE,WAAoB;IAC1E,IAAI,aAAa,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC;QAC3D,OAAO,EAAE,GAAG,WAAW,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;IAC3C,CAAC;IACD,MAAM,QAAQ,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACvD,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,KAAK,UAAU,WAAW,CACxB,GAAmB,EACnB,IAAmB,EACnB,WAAoB;IAEpB,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;QAClB,KAAK,UAAU,CAAC;QAChB,KAAK,OAAO;YACV,OAAO,mBAAmB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC/C,KAAK,QAAQ,CAAC,CAAC,CAAC;YACd,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC;gBAAE,OAAO,WAAW,CAAC;YACpD,MAAM,MAAM,GAA4B,EAAE,GAAG,WAAW,EAAE,CAAC;YAC3D,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;gBAChC,IAAI,MAAM,CAAC,MAAM,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3C,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC9E,CAAC;YACH,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,KAAK,MAAM,CAAC,CAAC,CAAC;YACZ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,WAAW,CAAC;gBAAE,OAAO,WAAW,CAAC;YACpD,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QACzF,CAAC;QACD;YACE,OAAO,WAAW,CAAC;IACvB,CAAC;AACH,CAAC;AAKD;;;;;;;GAOG;AACH,SAAS,eAAe,CAAC,OAA6B;IACpD,MAAM,GAAG,GAAG,OAA6C,CAAC;IAC1D,MAAM,KAAK,GACT,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC;QACpD,CAAC,CAAE,GAAG,CAAC,OAAO,CAAqB;QACnC,CAAC,CAAC,SAAS,CAAC;IAChB,MAAM,SAAS,GAAG,cAAc,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC;IAElD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,IAAI,KAAK,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IAC/C,IAAI,SAAS,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,SAAS;QAAE,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAEtD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,qBAAqB,CAC7B,4FAA4F;YAC1F,0FAA0F;YAC1F,cAAc,CACjB,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,qBAAqB,CAC7B,0BAA0B,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,8CAA8C;YACzF,kDAAkD,CACrD,CAAC;IACJ,CAAC;IACD,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC1C,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,CAAC;IAC9D,OAAO,EAAE,SAAS,EAAE,QAAkB,EAAE,CAAC;AAC3C,CAAC;AAED;;;;;;;;;GASG;AACH,KAAK,UAAU,cAAc,CAC3B,MAA4B,EAC5B,QAA0B;IAE1B,IAAI,MAA+B,CAAC;IACpC,IAAI,OAAO,IAAI,QAAQ,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5D,8EAA8E;QAC9E,mEAAmE;QACnE,MAAM,YAAY,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC;QAC9E,MAAM,OAAO,GAAG,YAAY;YAC1B,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,IAAI,iBAAiB,KAAK,GAAG,CAAC,QAAQ,CAAC;YACxF,CAAC,CAAC,SAAS,CAAC;QACd,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IACrF,CAAC;SAAM,CAAC;QACN,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IACvF,CAAC;IAED,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;QACrB,MAAM,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,OAAO,CAAC;QACrE,MAAM,IAAI,qBAAqB,CAC7B,iEAAiE,KAAK,EAAE,CACzE,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CACpB,MAA+B,EAC/B,SAAoB,EACpB,SAA6B;IAE7B,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;IACpC,MAAM,UAAU,GAAG,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,wCAAwC,CAAC;IAEhG,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,qBAAqB,CAC7B,6FAA6F;gBAC3F,IAAI,SAAS,2BAA2B,UAAU,GAAG,CACxD,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,CAAC,SAAS,IAAI,SAAS,CAAC,EAAE,CAAC;YAC9B,MAAM,IAAI,qBAAqB,CAC7B,uDAAuD,SAAS,mBAAmB,UAAU,GAAG,CACjG,CAAC;QACJ,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,8EAA8E;IAC9E,4EAA4E;IAC5E,MAAM,YAAY,GAAG,cAAc,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC7D,IAAI,YAAY,KAAK,SAAS,IAAI,YAAY,IAAI,SAAS;QAAE,OAAO,YAAY,CAAC;IAEjF,4EAA4E;IAC5E,qDAAqD;IACrD,MAAM,gBAAgB,GAAG,wBAAwB,CAAC,MAAM,CAAC,gBAAgB,CAAC,CAAC;IAC3E,IAAI,gBAAgB,KAAK,SAAS,IAAI,gBAAgB,IAAI,SAAS;QAAE,OAAO,gBAAgB,CAAC;IAE7F,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC,CAAC,CAAW,CAAC;IAEhD,MAAM,IAAI,qBAAqB,CAC7B,kGAAkG;QAChG,gBAAgB,UAAU,GAAG,CAChC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,SAAS,wBAAwB,CAAC,SAAkB;IAClD,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAChD,MAAM,QAAQ,GAAG,cAAc,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,CAAC;IACxD,IAAI,QAAQ,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC7C,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,QAAQ,CAAC;IAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnD,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAC;AACpE,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,MAA4B,EAC5B,OAA6B;IAE7B,MAAM,QAAQ,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACtD,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC;IACpC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;QAC5B,6EAA6E;QAC7E,gCAAgC;QAChC,MAAM,IAAI,qBAAqB,CAC7B,gGAAgG;YAC9F,4FAA4F;YAC5F,8CAA8C,CACjD,CAAC;IACJ,CAAC;IACD,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,cAAc,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnF,MAAM,UAAU,GAAG,SAAS,CAAC,OAAO,CAA4B,CAAC;IAEjE,MAAM,QAAQ,GAAG,IAAI,GAAG,CACtB,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CACtD,CAAC;IAEF,MAAM,GAAG,GAAmB,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;IACtE,MAAM,SAAS,GAA4B,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IACjE,KAAK,MAAM,CAAC,IAAI,EAAE,WAAW,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,SAAS,CAAC,2DAA2D;QACvE,CAAC;QACD,IAAI,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;YACpC,2EAA2E;YAC3E,mEAAmE;YACnE,0EAA0E;YAC1E,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,OAAO,CAAC,CAAC;YAClE,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QACxD,CAAC;aAAM,CAAC;YACN,SAAS,CAAC,IAAI,CAAC,GAAG,MAAM,WAAW,CAAC,GAAG,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;QAC/D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"prepare-inputs.js","sourceRoot":"","sources":["../src/prepare-inputs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,128 @@
1
+ # Architecture — `@pipelex/sdk`
2
+
3
+ ## Purpose
4
+
5
+ `@pipelex/sdk` is the Pipelex-branded product client for the hosted Pipelex API. It is the single import source for Node consumers (the webapp is the proving ground) that need to:
6
+
7
+ - run methods against the hosted runner (`execute` / `start`, run lifecycle, results),
8
+ - validate `.mthds` content (`validate`, returning the Pipelex `PipelexValidationResult` envelope),
9
+ - read the model deck and version handshake (`models` / `version`),
10
+ - and reach the Pipelex product surface (user profile, methods catalog, organizations, billing, API keys, gateway key, onboarding, storage).
11
+
12
+ ## Dependency direction (one-way)
13
+
14
+ ```
15
+ @pipelex/sdk ──imports──► mthds/protocol (pure MTHDS wire types)
16
+ ▲
17
+ └── consumers (pipelex-app, ...)
18
+ ```
19
+
20
+ - `@pipelex/sdk` depends on `mthds`, and only through the published **`mthds/protocol`** subpath. It never deep-imports `mthds` internals, and `mthds` never imports from `@pipelex/sdk`. `dependency-cruiser` enforces both directions.
21
+ - The client re-implements the official MTHDS protocol routes using MTHDS _types_; it owns its own `request()` pipeline (auth, base URL, retries/timeouts/abort, problem-details, observability). It does not delegate to `mthds`'s protocol client. Pinning route shapes to `mthds/protocol` types keeps them from silently diverging from the standard.
22
+
23
+ ## Brand boundary
24
+
25
+ - **MTHDS-branded (lives in `mthds`):** protocol wire models, the standard `ValidationReport` / `ValidationResult` shapes, `.mthds` utilities, the `MthdsProtocolClient`.
26
+ - **Pipelex-branded (lives here):** `PipelexApiClient`, run store / durable lifecycle, build helpers, `PipelexValidationResult`, hosted-product typed errors, API keys / orgs / billing / methods catalog / storage / onboarding.
27
+ - Inside a Pipelex envelope, field names stay neutral (`bundle_blueprint`, `pipe_io_contracts`, `input_form`, `liftable_pipes`, `graph_spec`) — Pipelex branding is reserved for genuinely runtime-specific envelopes.
28
+
29
+ ## Module format & packaging
30
+
31
+ The package is ESM-only (`"type": "module"`, `dist/index.js` built by `tsc` with `module: NodeNext`) — there is no separate CJS build. CJS consumers can still load it: the `exports` map declares a `default` condition alongside `import`, both pointing at the same ESM `dist/index.js`, so `require("@pipelex/sdk")` resolves through Node's `require(ESM)` support rather than a transpiled dual build. That support needs Node 20.19+ or 22.12+ (unflagged); `engines.node` is pinned to `>=22.12.0` to match the workspace's Node 22 floor. This only works because `dist/` has no top-level await — if that ever changes, `require()` of the module will throw and a real dual ESM+CJS build (or a `require`-only CJS entrypoint) would be needed instead.
32
+
33
+ Condition order in `exports` matters: `types` is listed first (TS/Node resolution convention — the type-checker looks at the first matching condition), then `import`, then `default` as the CJS fallback.
34
+
35
+ ## Wire conventions
36
+
37
+ - Base URL from env (default `https://api.pipelex.com`); every route under `/v1/*`.
38
+ - Auth: `Authorization: Bearer <token>`; organization resolved from the JWT `org_id` claim, not a header.
39
+ - A diagnostic verdict (e.g. `/v1/validate`) is a `200` discriminated on a body field (`is_valid`); non-2xx is reserved for "no verdict could be produced" (request-shape `422`, auth `401`/`403`, server `5xx`), carried as RFC 7807 `problem+json` and mapped to typed product errors.
40
+
41
+ ### Standard artifacts on the validate report
42
+
43
+ `PipelexValidationReport` is a Pipelex envelope that carries artifacts the MTHDS standard owns, and it names their types by importing them: `pipe_io_contracts` is `PipeIOContracts` and `input_form` is `InputForm`, both from `mthds/protocol`, where the standard declares them as the recommended extension fields of a validation report. The SDK restates nothing about their shape, and a consumer importing from `@pipelex/sdk` reaches the node types too (`InputFormTopLevelField`, `PipeInputContract`, the `FieldKind` vocabulary) through the barrel's `export * from "mthds/protocol"`.
44
+
45
+ That supersedes the earlier ruling which kept both fields opaque as `Record<string, unknown>`. The ruling was defending a real principle — this SDK does not own these types, and a second declaration here would be free to drift from the one the server emits — and the principle is unchanged. What changed is that the standard's TypeScript client now declares them, so importing enforces the same boundary the opaque typing was reaching for, and enforces it at compile time instead of leaving every consumer to cast. `bundle_blueprint` and `graph_spec` stay opaque for exactly the original reason: their canonical schemas live in the runtime's blueprint models and in `@pipelex/mthds-ui`'s `GraphSpec`, and neither has a declaration in the standard for this SDK to import. Each narrows the day the standard declares one.
46
+
47
+ ### Run sources
48
+
49
+ A run request carries its method inline exactly one of three ways: `mthds_contents` text, a `files` bundle map, or a `bundle_b64` zip. Those three are *encodings* of a bundle the caller ships, and they are mutually exclusive. Two selectors sit beside them on separate axes: the Pipelex-API `method_ref` (an address the RUNNER resolves by git fetch — a complete run source that pairs with nothing; see [Pipelex API run extensions](#pipelex-api-run-extensions-method_ref) below) and the hosted `method_id` (a catalog id the PLATFORM resolves — legal on its own, and legal alongside one inline encoding, where the inline source runs and the id rides along as run-history linkage; see [Hosted run extensions](#hosted-run-extensions-method_id) below). The rules — and crucially, _where they live_ — are:
50
+
51
+ - **Exclusivity is enforced from the standard, not restated here.** `assertExclusiveRunSources` and `hasBundlePayload` are imported from `mthds/protocol`; they are pure predicates over `RunRequest`, so this client and the MTHDS runners cannot drift on which combinations they reject. A second local copy would be free to disagree about what is legal. The `method_ref` pairings are the one local addition (`assertMethodRefPairsWithNothing` in `client.ts`), because the protocol does not know the field — the wording of its errors mirrors the server's own validator.
52
+ - **Exclusivity keys off presence, the run-source precondition keys off runnability.** Supplying `files: {}` _and_ `bundle_b64` is rejected — the caller expressed two encodings — while an empty encoding on its own carries no method and so is neither shipped on the wire nor counted as "something to run".
53
+ - **`files` / `bundle_b64` / `bundleMain` / `method_ref` / `method_id` are reserved keys on `extra`.** `extra` merges last into the body, so smuggling a run source through it would overwrite the validated fields and bypass the exclusivity check; `buildExtensions` rejects any reserved key — the named run sources, the two method selectors, _and_ the client-only `bundleMain` hint. `buildExtensions` also strips prototype-pollution keys (`__proto__` / `constructor` / `prototype`), which `JSON.parse`-sourced `extra` can carry, so the client never emits a pollution gadget on the wire.
54
+ - **`bundleMain` is client-only and never serialized.** It is an entrypoint hint for a _local_ runner; the request builders name wire fields explicitly, and it is reserved on `extra`, so it cannot leak onto the wire by either path.
55
+
56
+ ### Pipelex API run extensions (`method_ref`)
57
+
58
+ `method_ref` is the layer-2 sibling of the hosted `method_id`: a globally resolvable address (`github.com/<owner>/<repo>[/<selector>][@<tag>]`) that any pipelex-api deployment resolves itself — git fetch at the tag, package located by manifest identity — with no catalog anywhere. It lives in its own `PipelexApiRunExtensions` interface rather than in `PipelexHostedRunExtensions`, because the two selectors belong to different layers: an address is meaningful against a bare runner; a catalog id is not. Served by pipelex-api >= 0.21.0; on `api.pipelex.com` availability follows the platform deploy that forwards it.
59
+
60
+ - **A complete run source, so it pairs with nothing.** The fetched package carries its `.mthds` and its entry pipe. The client rejects `method_ref` beside inline `mthds_contents`, beside a bundle encoding, and beside `method_id` — mirroring the server's 422s, with no linkage exception (an address run has its own provenance and needs no linkage id). `pipe_code` beside it is legal: it overrides the manifest's `main_pipe`.
61
+ - **Provenance comes back typed.** A `method_ref` run's start ack (`PipelexRunResultStart`) and execute result (`PipelexExecuteResult`) carry `method_provenance` — `{address, tag, commit_sha}`, the SHA being what keeps the run explainable when a tag moves.
62
+ - **It rides the blocking fallback too**, like `method_id` — a bare runner reached through `startAndWaitForResult`'s degrade runs the same fetched package.
63
+ - **An empty string is treated as absent**, same as `method_id`.
64
+
65
+ ### Hosted run extensions (`method_id`)
66
+
67
+ `RunOptions` / `StartOptions` come from `mthds/protocol` and stay pure. This SDK's run methods take `PipelexRunOptions` / `PipelexStartOptions` — those types intersected with `PipelexApiRunExtensions` (above) and `PipelexHostedRunExtensions`, which is where the hosted API's own run arguments live. Today the hosted interface is one field, `method_id`, and the reasoning generalizes to every hosted-only argument that follows:
68
+
69
+ - **It is a named option, not an `extra` entry.** Typing its own platform's arguments is the one job a layer-3 client exists to do; `extra` stays the escape hatch for extensions this client does not know about (a third-party server, or a newer version of ours). That split, and the reserved-keys guard that enforces it, are normative for every client in the layered stack: a client types the arguments of the platform it is built for, and only the layer that owns an argument may validate or reject it. It was previously passed as `extra: { method_id }`, which worked but documented nothing and validated nothing.
70
+ - **The guard is per layer.** `method_id` is reserved on `extra` _here_ and must never become reserved in `mthds` / `mthds-python`: a protocol client talking to another vendor's server has no business rejecting that vendor's arguments.
71
+ - **Pure pass-through — nothing is expanded client-side.** The platform resolves the id against the org's catalog. The same posture holds across the tooling routes: `validate` / `resolve` / `codegen` take a typed `method_id` the platform resolves server-side (see the surface list below), and so does `prepareInputs`, which composes a `validate` of its own rather than a route call. `/v1/build/*` is the last family with no by-id form — it takes `files` only, and a stored method is expanded explicitly with `getMethodClosure`, which stays public as the local expansion utility.
72
+ - **Alone it is a run source; alongside an inline source it is linkage.** With no inline source the platform resolves and runs the stored method. With one, the inline source is what RUNS (precedence) and the id is recorded as run-history linkage on the Run row — the index key `GET /v1/runs?method_id=` queries. Rejecting the combination is not an option: it once orphaned every unsaved-bundle run from its method. The client therefore counts `method_id` towards its "something to run" precondition, and never treats it as an exclusivity violation.
73
+ - **It rides the blocking fallback too.** `startAndWaitForResult` degrades to `POST /v1/execute` against a runner with no run store, and the selector goes with it — so a bare `pipelex-api` answers a `422` that names the key (its Rule 4 diagnosis: "a hosted-only selector must be sent to the hosted API") instead of the client silently dropping it and running something else.
74
+ - **An empty string is treated as absent.** `method_id: ""` selects no method and links nothing, so it is not sent and does not satisfy the precondition.
75
+
76
+ ## Module layout
77
+
78
+ Flat `src/` (mirrors `mthds-js`'s `runners/api` flatness — the SDK has one client, so no runner/registry abstraction):
79
+
80
+ - `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.
81
+ - `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 tools surface (`Diagnostic` / `LintResponse` / `FormatResponse`), and the crate-family request/response models — the shared `CrateRequestBase` / `CrateInvalidReport` / `GeneratedArtifact` vocabulary plus the `/v1/build/*`, `/v1/resolve` and `/v1/codegen` shapes built on it.
82
+ - `product-models.ts` — the snake_case wire shapes for the hosted-product management routes (profile, methods catalog, organizations, billing, API keys, gateway key, onboarding, storage, runs list/detail/update). Each interface models only the fields the product actually consumes — no speculative mirrors.
83
+ - `method-source.ts` — `methodSourceToContents`, the canonical parser turning a stored method's polymorphic `mthds` source (raw single-bundle text XOR a JSON `{name, content}[]` file array) into a flat list of file contents. A verbatim port of the platform's `_method_source_to_contents` so the SDK and runtime read a stored source identically; exported from the barrel and consumed by `getMethodClosure`. See [input-preparation.md](./input-preparation.md#getmethodclosure--the-explicit-expansion-utility).
84
+ - `codegen-check.ts` — `runCodegenCheck`, the **pure** offline drift check over a committed codegen tree: the stamp grammar, the `codegen.lock` parser, the hash, and the drift taxonomy, ported from pipelex's `codegen/check.py` so a verdict computed here equals the one `pipelex codegen check` computes over the same bytes (the drift `detail` sentences included, verbatim). It touches neither the client nor `mthds/protocol` — its only import is `smol-toml` — and it hashes through WebCrypto rather than `node:crypto` so it adds no Node builtin to the barrel's import graph (`upload.ts` still names `node:fs/promises`, so a browser-targeting bundler must mark `node:*` external). It raises `CodegenLockError`, which extends `Error` rather than the protocol base. No filesystem, no fetch, no API key — the caller walks its own tree and hands in the text. See [crate-routes.md](./crate-routes.md#the-offline-check--runcodegencheck).
85
+ - `artifacts.ts` — the artifact stack, the download twin of `prepareInputs`, in layers: `collectArtifacts` (a pure, exported walk for `pipelex-storage://` references), `resolveArtifacts` (the bulk resolve route over a whole list, chunked at its bound), `fetchArtifact` (one reference as a bounded `Response`) and `downloadArtifacts` (Node-only, a run's produced files saved under a directory as a produced verdict), plus `artifactFilename`, the filename derivation the MCP server used to own. The three network operations are client methods over a small structural seam (`ArtifactCapableClient`: the raw `resolveStorageUrls` call and `getRunResult`); the module's static import graph stays free of Node builtins, because `node:fs/promises`, `node:path` and `undici` are loaded by dynamic import behind `isNodeRuntime()` — a browser-targeting bundler still has to mark `node:*` and `undici` external, as it already must for `upload.ts`. `undici` is a runtime dependency for one reason: the fetch runs on an `Agent` carrying `headersTimeout` and `bodyTimeout`, the per-stall bounds an `AbortSignal` budget does not give, and Node exposes its bundled undici's `fetch` but not its `Agent`. See [artifact-download.md](./artifact-download.md).
86
+ - `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`. `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.
87
+ - `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`.
88
+ - `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). `mapRunResultToRunResults` reads that accessor too, keeping the resolution single-sourced.
89
+ - `errors.ts` — the transport and run-lifecycle typed errors, the input-preparation family under `InputPreparationError`, and the artifact family under `ArtifactOperationError` (`ScopeUnavailableError`, `ArtifactFetchError`, `ArtifactAuthenticationError`, the last carrying the download verdict so far); all derive from the protocol-base `PipelineRequestError` (re-exported from `mthds/protocol`) except `ClientAuthenticationError`. It is not the whole error surface: `codegen-check.ts` exports `CodegenLockError`, which extends plain `Error` because nothing was requested over the wire.
90
+ - `index.ts` — the public barrel: `export * from "mthds/protocol"` (single import source for the standard surface) plus the client, models, run types, the standalone pure helpers (`runCodegenCheck`, `summarizeUsage`, `collectArtifacts`, `artifactFilename`), and errors.
91
+
92
+ ## Client surface (current)
93
+
94
+ - **Protocol execution:** `execute` (returns a `PipelexExecuteResult` with a resolved `.main_stuff`), `start`, `validate` (returns `PipelexValidationResult`), `validateFiles`, `models`, `version`.
95
+ - **Two independent opt-in lists on `validate`.** `render` asks the server to render a *view format* and adds a `rendered_<format>` field; `views` asks for a *structured view* and adds a same-named top-level field — today only `input_form`, the per-pipe input-form descriptors. Each resolves its own tokens against its own supported set and unknown tokens are lenient-ignored on both, never a `422`. The client always asks for `render: ["markdown"]` but sends `views` only when a caller names a token, because an opt-in view exists precisely so the default response stays byte-identical for the consumers that discard it. `pipelex-api` gates `input_form` on the token as of 0.18.0; a 0.17.0 runner resolved no token and emitted the field on every valid verdict, which is why `PipelexValidationReport.input_form` is typed optional rather than required — the one typing that reads a response from either runner correctly.
96
+ - **Method-bundle transport (pipelex-api extension):** `execute` / `start` / the blocking fallback accept the whole method — the `.mthds` plus its `funcs/*.py`, `structures/*.py`, `requirements.txt` — instead of only the inline `mthds_contents` text, so custom PipeFunc Python travels with the method. Two equivalent, mutually exclusive encodings: `files` (a `{ relativePath: text }` map) and `bundle_b64` (the same bundle as a base64 zip). A bundle is self-contained, so it satisfies the "something to run" precondition on its own — neither `pipe_code` nor `mthds_contents` is required beside it. See [run-sources](#run-sources) above.
97
+ - **Method selectors on the run methods:** `execute` / `start` / `startAndWaitForResult` take the Pipelex API's `method_ref` (an address the runner resolves; provenance comes back typed as `method_provenance`) and the platform's `method_id` as **typed named options** (`PipelexRunOptions` / `PipelexStartOptions`), never `extra` entries. See [Pipelex API run extensions](#pipelex-api-run-extensions-method_ref) and [hosted run extensions](#hosted-run-extensions-method_id) above.
98
+ - **Method selectors on `validate`:** the first argument is inline contents (`string[]`) or a `ValidateMethodSelector` — `{ method_ref }` (runner-resolved by address) or `{ method_id }` (hosted, platform-resolved) — under the strict tooling XOR: exactly one selector, no linkage exception, `mthds_sources` legal only beside inline contents. A selector-resolution failure is a non-2xx `ApiResponseError`, never an `is_valid: false` verdict.
99
+ - **Build helpers (`/v1/build/*`):** `buildInputs`, `buildOutput`, `buildRunner`, `concept`, `pipeSpec`. The three projections share one closure envelope (`files: [{content, source?}]` XOR a `method_ref` — the address form server-resolved, the registry form still `501`) plus an optional **qualified** `pipe_ref` that defaults to the closure's `main_pipe`, and each returns a **discriminated 200 verdict** (`is_valid`) rather than a bare payload — same discipline as `validate`. The build routes take **no `method_id`** (`method_id?: never` on `BuildRequestBase`): the hosted tooling selector deliberately excludes them, so a stored method is expanded first with `getMethodClosure`. **Nothing inside this SDK calls them** — `prepareInputs` reads its signature from the input-form descriptor on the validate report — so they exist for external consumers only, and are retired with the rest of `/v1/build/*` once those consumers project the template client-side. See [build-routes.md](./build-routes.md).
100
+ - **Crate routes (`/v1/resolve`, `/v1/codegen`):** `resolve` emits the **normalized library crate** (the MTHDS standard's Library Crate Format, fingerprint riding inside the payload); `codegen` projects that crate through two explicit axes — `kind` (`types`) × `target` (`ts-zod` / `python-pydantic` / `python-structures`) — into stamped artifacts plus their `codegen.lock`. They share the build routes' closure envelope and its **discriminated 200 verdict** (`CrateInvalidReport` is literally the same invalid arm), and additionally take the hosted `method_id` (`PipelexHostedToolingExtensions`) as a third closure form under the strict three-way XOR — a pass-through the platform resolves, meaningless against a bare runner. Writing the artifacts and lock verbatim reproduces a local `pipelex codegen types` run byte-for-byte, so the offline `pipelex codegen check` passes — the SDK stays transport-only and does not write files. Verifying such a tree afterwards is `runCodegenCheck`, a standalone pure helper rather than a client method, because it needs no server at all. See [crate-routes.md](./crate-routes.md).
101
+ - **Tools (`/v1/lint`, `/v1/format`):** `lint`, `format` — single-file static diagnostics and canonical formatting, served by any pipelex-api runner (no inference, no bundle load). Both are **diagnostic endpoints** like `validate`: a malformed `.mthds` file is a produced verdict on a **200** carrying `diagnostics[]`, never a thrown error — `format` additionally echoes the content back unchanged with `changed: false`. Only a no-verdict condition is non-2xx: a malformed body, **malformed formatter `options`** (e.g. a non-numeric `column_width` ⇒ 422), auth, or a server fault, all mapped to the typed `ApiResponseError`. `lint` is the cheap static check (syntax / schema / semantic); it does not replace `validate`, which loads the bundle, resolves across files, and dry-runs the pipes.
102
+ - **Durable run lifecycle (hosted extension — NOT protocol):** `getRunStatus`, `getRunResult`, `waitForResult`, `startAndWaitForResult` (handshakes `/v1/version`, takes the durable start+poll path on a hosted deployment, and self-heals to the blocking `execute` against a bare runner). Reading what a completed run consumed is `summarizeUsage(results)`, a standalone pure helper rather than a client method, because it folds the usage pair the results already carry and needs no server. See [run-usage.md](./run-usage.md#summarizing-a-run--summarizeusage).
103
+ - **Health:** `health` (origin-level `/health`).
104
+
105
+ ### Product routes (hosted management surface)
106
+
107
+ The hosted catalog/account routes the webapp drives. All ride the same `{base}/v1/*` + `Authorization: Bearer` + org-from-JWT contract as the protocol routes, go through the shared `requestProduct<T>` helper (30s management timeout, empty-body tolerant), and map a non-2xx RFC 9457 `problem+json` to a typed `ApiResponseError`. Consumers branch on the **structured `code`** (`conflict`, `not_found`, `pipelex_api_key_limit_reached`, …), never the HTTP status.
108
+
109
+ - **User profile:** `getMe` (`GET /v1/me`).
110
+ - **Methods catalog:** `listMethods`, `getMethod`, `createMethod`, `updateMethod` (rename = changed `name`), `deleteMethod`. **`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 conflict` rather than a second cascade over the same runs, and an unknown or foreign-org id is a `404`. (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. `iterateMethods` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const m of client.iterateMethods())`); unlike `iterateRuns` it must keep going **past empty pages**, because `q` is a post-read filter over a bounded index slice per request, so `{items: [], nextCursor: "…"}` means "nothing matched in the slice I just read, keep going" — the difference is the server, not the client. 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`, and a server-derived `description` (parsed read-side from the bundle's top-level `description`) 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 stored method's polymorphic `mthds` source into a runnable, provenance-labelled closure — see [input-preparation.md](./input-preparation.md#getmethodclosure--the-explicit-expansion-utility).
111
+ - **Organizations:** `listMemberships`, `createOrganization`, `renameOrganization` (`PATCH`). The org "switch" is a WorkOS-session op (`/api/active-org`), not a `/v1` route — out of SDK scope.
112
+ - **Billing:** `getSubscription`, `listPlans`, `listInvoices`, `createCheckout`, `changePlan` (409 `conflict` ⇒ no subscription), `getBillingPortal` (409 `conflict` ⇒ no subscription).
113
+ - **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.
114
+ - **Gateway API key (LLM inference key):** `createGatewayApiKey` (the JSON body is always sent, even with `promo_code: null`), `getGatewayApiKey`.
115
+ - **Onboarding:** `submitOnboarding`.
116
+ - **Storage:** `resolveStorageUrl`, `resolveStorageUrls` (`POST /v1/resolve-storage-url/bulk` — the single route applied to a list of at most 100 references, one verdict per reference in request order, a refused reference a value on its item; a deployment without the route answers a `404` `ApiResponseError`), `upload` (JSON base64 — the multipart hop is browser→BFF only). The higher-level `uploadFile` / `prepareInputs` preparation surface is built on top of `upload`; `prepareInputs` additionally composes one `validate` call with `views: ["input_form"]` for the signature it walks, and takes the method as inline `files`, a `method_ref` or a `method_id` like every other method-taking operation — see [input-preparation.md](./input-preparation.md). The download twin — `collectArtifacts`, `resolveArtifacts`, `fetchArtifact`, `downloadArtifacts` — is built on top of `resolveStorageUrls` and `getRunResult`, never on the embedded `public_url`, and is documented on [artifact-download.md](./artifact-download.md); downloading is a separate explicit gesture, and no run call takes a `download` option.
117
+ - **Runs list/detail/update:** `listRuns` (by `method_id`) returns **one page**, not the whole history — a `RunPage` (`{items, nextCursor}`) served from a time-ordered index, so its cost is the page size rather than the size of the history. `iterateRuns` is the async iterator that follows the cursor for callers who genuinely want everything (`for await (const run of client.iterateRuns(id))`); it is deliberately not a `listAllRuns(): Promise<PipelineRun[]>`, because an all-at-once helper needs a page cap and a cap means silently returning a truncated list. Both accept `createdFrom` / `createdTo`, applied server-side as index key conditions — these are **instants, not days** (ISO-8601 with a UTC offset; a naive timestamp is a 400), because only the caller knows which timezone's day it means. `getRunDetail` (`GET /v1/runs/{id}`) is the only call returning `mthds_contents` — what the run actually executed — and `inputs`; it is kept off the status read that pollers hit every few seconds and off the list, where the bundle would be multiplied by the page size. `updateRun` is the admin/manual status patch. The status/results/start lifecycle routes live in the run-lifecycle section above.
118
+
119
+ ## Hook bundle (`dist-hooks/check.mjs`)
120
+
121
+ `src/hooks/` holds the post-edit `.mthds` hook pipeline (local lint/format via `@pipelex/tools-wasm`, then a `POST /v1/validate` verdict through this SDK), bundled into a single dependency-free ESM file by `npm run build:hook`. The one specifier left external is `undici`, reached only by the artifact fetch path through a dynamic import the hook never executes, so the bundle does not inline an HTTP client it cannot use. **Distribution is source-checkout vendoring, not npm:** `pipelex-plugins` runs `make vendor-hook`, which builds `dist-hooks/check.mjs` in a sibling checkout of this repo and copies it in as a static hook asset — the published `@pipelex/sdk` tarball deliberately does not ship the bundle (it inlines the ~4 MB WASM engine, and no npm consumer of the SDK's library API uses it). `dist-hooks/` is gitignored; the consumer repo's own checks gate a stale vendored copy.
122
+
123
+ ## Testing
124
+
125
+ Two layers, split by whether a server is in the loop:
126
+
127
+ - **Unit suites (`tests/*.test.ts`, `make test`):** mock the fetch boundary (`vi.spyOn(globalThis, "fetch")`) and pin the client's wire behavior — URLs, request bodies, status→error mapping, poll semantics. These run everywhere, including CI. The codegen-check suite is the exception that proves the rule: it has no server to mock, so it runs against **vendored real codegen output** under `tests/fixtures/codegen/` — artifacts and lock emitted by an actual `pipelex codegen types` run, committed beside the `.mthds` they came from. A hand-written stamp would only prove the port agrees with itself. The artifacts are vendored with a trailing `.txt` and their real names restored at load time, because `format:check`, `lint`, and `typecheck:test` all glob `tests/**/*.ts` — and a single reformatted byte would invalidate the very hash the fixture exists to pin.
128
+ - **E2E suites (`tests/e2e/*.e2e.ts`, `make test-e2e`):** no mocks — they drive a **live pipelex-api server** (`PIPELEX_E2E_BASE_URL`, default `http://localhost:8081`) and verify the diagnostic-endpoint contract end-to-end with correct and malformed MTHDS bundles: clean content lints with zero diagnostics and formats to itself (idempotently), malformed content comes back as a 200 verdict (`diagnostics[]` on lint; `diagnostics[]` plus the content echoed back unchanged with `changed: false` on format; `is_valid: false` + `validation_errors[]` on validate), and malformed formatter options surface as a 422 `ApiResponseError`. The crate suite adds the case only a live server can settle: the `kind` / `target` vocabulary is a mirror of a Python `StrEnum` in another repo, so nothing but a real call proves the two still agree — and it cross-checks that `codegen`'s `crate_fingerprint` equals the `fingerprint` `resolve` returns for the same closure. It then runs `runCodegenCheck` over the artifacts and lock the server just emitted — verbatim first (current, with the lock header's fingerprint matching the response's), then mutated in memory once per drift category, for a `.ts` target and a `.py` one. The unit suite pins the port against vendored bytes; only this pins it against the stamp and lock bytes the server writes **today**, which is the pairing that matters when a consumer commits the server's artifacts and gates CI on the SDK's verdict about them. The distinct `.e2e.ts` suffix + `vitest.e2e.config.ts` keep them out of the default `vitest run`, so `make test` / CI never require a server. An unreachable server is an error, never a silent skip, and it is reported once: `make test-e2e` preflights the target with a single `curl` and bails with a one-line message, instead of letting all three suites fail in `beforeAll` and print a socket-error dump each. The per-suite `beforeAll` guard stays as the backstop for a direct `npm run test:e2e` run. Both probe **`/v1/version`**, deliberately: a bare runner serves the origin-level `/health` and a hosted origin does not (it answers `/v1/health` instead), so `/v1/version` is the one unauthenticated route both answer — probing anything else reports a perfectly reachable hosted origin as down. The target itself is resolved once, in the Makefile, and exported along with `PIPELEX_API_KEY`, so the URL the preflight checks is always the URL the suites call: the shell environment (or a `make test-e2e PIPELEX_E2E_BASE_URL=...` override) wins, then `.env`, then `http://localhost:8081`. The run echoes the resolved target before it starts, since `.env` can now supply it invisibly. Note that a hosted origin cannot run the whole suite today — `lint` and `format` are not exposed there (gateway `403`), while the protocol, build and crate routes all answer — so a fully green run means a local runner. The artifact suite is the mirror image: it uploads a file with `prepareInputs`, runs a pass-through and brings the file back down with `downloadArtifacts` over `working_memory`, bytes equal, so it needs a hosted platform carrying the upload, run-lifecycle and bulk resolve routes and a key for it, and it fails honestly rather than skipping against a bare runner.
@@ -0,0 +1,165 @@
1
+ # Artifact download (`collectArtifacts` / `resolveArtifacts` / `fetchArtifact` / `downloadArtifacts`)
2
+
3
+ > **Status: implemented** (`src/artifacts.ts`). This is the download twin of [input preparation](./input-preparation.md): where `prepareInputs` turns local files into `pipelex-storage://` references before a run, these four operations turn the references a run produced back into bytes on disk, or into a bounded stream, afterwards. They are layered so that each is usable without the next.
4
+ >
5
+ > **They need a platform that serves the bulk resolve route.** Everything below `collectArtifacts` mints its links through `POST /v1/resolve-storage-url/bulk`, a hosted-platform route. The public bare runner (`pipelex-api`) has no resolve route at all, single or bulk, and a hosted deployment that predates the route answers a `404`; in both cases the operation throws the existing `ApiResponseError` and nothing is downloaded. `resolveStorageUrl`, the single-reference primitive, stays for the callers that have one link to mint.
6
+
7
+ ## Why this exists
8
+
9
+ A run that produces an image, a PDF or a document does not embed the bytes in its results. The content carries the file's durable reference — a `pipelex-storage://` URI in its `url` field — beside a `public_url` the storage provider signed when the run wrote the file. That signed link is short-lived and must not be stored, so every consumer that wanted the file had to walk the result for references, mint a fresh link per reference, and stream each link to disk within sensible bounds. The MCP server did exactly that for itself; a method app needs the same walk to display a picture; a CLI needs it to save a report. `downloadArtifacts` makes it one explicit operation, and the layers under it make each step reusable on its own.
10
+
11
+ Downloading is **explicit and separate from running**, the input-preparation rule in reverse: `execute` / `start` never silently upload, and `startAndWaitForResult` never silently downloads. There is no `download` option on any run call. A download is its own gesture, inspectable and repeatable — by run id, days after the run.
12
+
13
+ Nothing here reads the embedded `public_url`. Every link is minted fresh by the platform, which is what makes a download work long after the embedded link died, and what keeps the tenant boundary where the platform enforces it.
14
+
15
+ ## The four operations
16
+
17
+ ### `collectArtifacts(value)` — the pure walk
18
+
19
+ ```ts
20
+ import { collectArtifacts } from "@pipelex/sdk";
21
+
22
+ const uris = collectArtifacts(results.main_stuff);
23
+ // ["pipelex-storage://org/runs/01J…/outputs/illustration.png", …]
24
+ ```
25
+
26
+ Walks any JSON-shaped value and returns every string that **is** a `pipelex-storage://` reference — the whole string, scheme first, with something after the scheme. A string that merely contains a reference does not count; the bare scheme does not count; nothing else is looked at. The result is deduplicated and kept in discovery order. It is a contract rather than a heuristic, because the scheme is unambiguous: the runtime serializes a produced file as content carrying its reference in `url`, and nothing else on the wire starts that way.
27
+
28
+ It is pure — no network, no key — and exported standalone, so a consumer can count or list a result's files without resolving any of them.
29
+
30
+ ### `resolveArtifacts(uris)` — fresh links for a whole list
31
+
32
+ ```ts
33
+ const resolved = await client.resolveArtifacts(uris);
34
+ for (const item of resolved) {
35
+ if (item.error === null) {
36
+ // item.url is fetchable now; item.expires_at says until when; item.content_type may be null
37
+ } else {
38
+ // item.error is { code, detail } — the route's own per-reference refusal
39
+ }
40
+ }
41
+ ```
42
+
43
+ One call to the platform's bulk route for the whole list, chunked at the route's bound of 100 references per request (`BULK_RESOLVE_MAX_URIS`), answering one `ResolvedArtifact` per reference **in request order, duplicates included**. A resolved item carries `url`, `expires_at` (UTC, ISO 8601) and `content_type` (`string | null` — the platform's guess from the reference's extension, null when it has none) with `error: null`; a refused item carries `error` as `{ code, detail }` with the three link fields null. The codes are the route's: `invalid_storage_uri` for a malformed reference and `forbidden` for one belonging to another organization. A consumer branches on `error`, never on an HTTP status, because the request is a `200` whenever every reference got a verdict.
44
+
45
+ Only what is not about a reference throws: the route's whole-request refusals (a caller with no organization, a request over the bound or with an unknown field, a signing failure, a deployment without the route) as `ApiResponseError`, and an unreachable host as `ApiUnreachableError`. An empty list resolves to an empty list with no request made. A link lives about fifteen minutes; resolve close to the moment of use.
46
+
47
+ This is where every browser-side or server-rendering consumer stops: it mints links on the server and hands each one to a client that uses it immediately. `resolveStorageUrls` is the raw wire call underneath it (one request, at most the bound), the way `upload` sits under `uploadFile`.
48
+
49
+ ### `fetchArtifact(uri)` — one bounded stream
50
+
51
+ ```ts
52
+ // A same-origin proxy: the API key stays on the server, the browser gets the bytes.
53
+ export async function GET(request: Request, { params }: { params: { uri: string } }) {
54
+ const upstream = await client.fetchArtifact(params.uri, { signal: request.signal });
55
+ return new Response(upstream.body, {
56
+ status: upstream.status,
57
+ headers: {
58
+ "Content-Type": upstream.headers.get("content-type") ?? "application/octet-stream",
59
+ "X-Content-Type-Options": "nosniff",
60
+ "Content-Disposition": "inline",
61
+ "Cache-Control": "private, max-age=60",
62
+ },
63
+ });
64
+ }
65
+ ```
66
+
67
+ Resolves the reference fresh and returns the object store's response as a bounded `Response`. The bounds:
68
+
69
+ - **A timeout** covering the connection, the headers and the whole body (`timeoutMs`, default 120 s). On Node the fetch also runs on an `undici` dispatcher carrying `headersTimeout` and `bodyTimeout` at the same value, the per-stall bounds an `AbortSignal` alone does not give.
70
+ - **Redirects refused** (`redirect: "manual"`): a presigned link has no reason to redirect, and one that does is refused rather than followed.
71
+ - **The byte cap enforced mid-stream** (`maxBytes`, default 1 GiB): a declared `Content-Length` over the cap is refused before a byte is read, and a body that crosses the cap while streaming errors the returned stream — never buffered.
72
+ - **No credentials forwarded**: the request carries no headers of ours. The link's authorization is in its query string, and nothing else may ride along to the store.
73
+ - **Plain `http:` refused** unless `allowHttp: true`. A general-purpose library does not fetch over plain http silently; the local compose stack's object store hands out such links, and that is what the option opts into.
74
+
75
+ It is **header-neutral**: the status, status text and headers are the store's own. The one change is that a `Content-Encoding` the fetch already decoded is dropped with the encoded `Content-Length`, since the body handed on is the decoded bytes. A proxy relaying it therefore owns the response hygiene — `X-Content-Type-Options: nosniff`, a sandboxing CSP on the asset response, a controlled `Content-Disposition`, private caching — and must set them itself, as the example does.
76
+
77
+ Only a `2xx` is returned. Anything else throws an `ArtifactFetchError` whose `code` says why, in the same closed vocabulary the download verdict uses per item: the route's `invalid_storage_uri` / `forbidden`, then `unsupported_url` (not a URL, not http(s), or carrying credentials), `plain_http_refused`, `redirect_refused`, `store_refused` (a 401/403 from the store — the link is freshly minted, so this is the store refusing a fresh signature, not an expired link), `not_found` (404/410), `store_error` (any other non-2xx, with `status`), `too_large`, `timeout` and `network`. The caller's abort propagates as-is, and the body reader is cancelled with it. `downloadArtifacts` and a proxy share this boundary: the download is the same fetch followed by a write.
78
+
79
+ ### `downloadArtifacts({ run_id | results, dir, … })` — the files on disk
80
+
81
+ ```ts
82
+ const verdict = await client.downloadArtifacts({
83
+ run_id: "01J…", // or: results: <a RunResults in hand>
84
+ dir: "./out/01J…",
85
+ scope: "working_memory", // default "main_stuff"
86
+ });
87
+
88
+ console.log(verdict.saved_paths);
89
+ if (!verdict.all_saved) {
90
+ for (const artifact of verdict.artifacts) {
91
+ if (artifact.error !== null)
92
+ console.warn(artifact.uri, artifact.error.code, artifact.error.detail);
93
+ }
94
+ }
95
+ ```
96
+
97
+ **Node-only**, like the path-string arm of `uploadFile`: it writes to a filesystem, and says so with an `ArtifactOperationError` anywhere else.
98
+
99
+ **Where it reads from.** Exactly one of `run_id` and `results`. A `run_id` re-reads the results through `getRunResult`, so a completed run is downloadable days later from its id alone; a `RunResults` already in hand is read as it is, with no request. `scope` picks the artifact walked for references: `main_stuff` (the default) is the run's output, and `working_memory` is the opt-in that also brings down the echoed inputs and every intermediate stuff — it is read off the parsed results body, whether or not `RunResults` declares the field.
100
+
101
+ **How it downloads.** The whole set is resolved through the bulk route ahead of the workers, then a bounded number of workers (`concurrency`, default 4) each take the next reference and fetch it → open its file with `wx` → stream the body in. Resolution is just-in-time where it matters: a link that has expired by the time its worker reaches it — a large set downloaded a few at a time can outlive the fifteen-minute link — is resolved again for that reference alone, so no fetch ever runs on a stale signature.
102
+
103
+ **Filenames.** Each file is named by `artifactFilename` (exported): the last segment of the storage key, reduced to `[A-Za-z0-9._-]` with leading dots stripped so it can never name anything outside `dir`, capped in length with the extension preserved, given an extension from the content type when the key has none, and falling back to `artifact-N`. Files are **never overwritten**: a name already on disk gets a numeric suffix (`report-1.pdf`, `report-2.pdf`), through exclusive creation rather than an exists-check, so two workers cannot race for one name. `dir` is created if missing.
104
+
105
+ **Cleanup.** A failed or aborted download unlinks its partial file; nothing truncated is ever left under a final name.
106
+
107
+ **The verdict.** A produced verdict, one entry per reference in discovery order:
108
+
109
+ ```ts
110
+ {
111
+ scope: "main_stuff" | "working_memory",
112
+ artifacts: [
113
+ { uri, path, content_type, size, error: null }, // saved — path is absolute
114
+ { uri, path: null, content_type, size: null, error: { code, detail } }, // not saved
115
+ ],
116
+ saved_paths: [ /* the absolute paths of the saved ones, same order */ ],
117
+ all_saved: boolean, // every reference saved; vacuously true for an empty walk
118
+ aborted?: true, // present only when the caller's signal aborted the call
119
+ }
120
+ ```
121
+
122
+ `artifacts.length` is the count of references walked, errors included. An empty walk over a present scope — an output that references no stored file — is a verdict with empty lists and `all_saved: true`, not an error, and it touches neither the network nor the disk. `content_type` is the platform's guess from the reference, known before the fetch, on both arms.
123
+
124
+ Per-item `error.code` is the fetch vocabulary above plus the download's own: `resolve_failed` (an expired link could not be re-resolved, for a reason that is not the credential), `total_limit_exceeded` (the item that would take the call past `maxTotalBytes`, and, once the files already saved leave no room, every item not yet started, whose `detail` says it was skipped; an item refused only because files still in flight hold the room stops nothing else, since one of them may yet fail and give it back), `write_failed` (the file could not be created, written or closed) and `aborted` (in flight or not yet started when the signal fired).
125
+
126
+ **What it throws.** Only conditions with no verdict, all typed:
127
+
128
+ - `RunStillRunningError` (with the retry hint) or `RunFailedError` — a `run_id` naming a run that has not completed;
129
+ - `ScopeUnavailableError` — the requested scope's artifact is `null` or missing from the body (`scope` and `runId` on the error). Reading by `run_id`, a null `main_stuff` is already `MissingMainStuffError` from `getRunResult`;
130
+ - `ArtifactAuthenticationError` — the resolve route refused the credential (`401` / `403`), on the first resolve or on a re-resolve part-way through. It carries `verdict`, the result as it stood: the refusal stops the workers taking new items but lets the fetches already running finish, since they are on presigned links that do not carry the credential, so every file saved is real and listed and the rest are marked `aborted` with a detail naming the credential failure;
131
+ - `ArtifactOperationError` — outside Node, an unusable `dir`, both selectors or neither, or nonsense bounds;
132
+ - and the transport and lifecycle errors of the reads it makes, unchanged: `ApiResponseError` for a deployment without the bulk route, `RunLifecycleUnavailableError` for a bare runner asked by `run_id`, `ApiUnreachableError`.
133
+
134
+ Everything else that can go wrong with one reference is that reference's `error`.
135
+
136
+ **Options and defaults.**
137
+
138
+ | Option | Default | What it bounds |
139
+ | --------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
140
+ | `scope` | `"main_stuff"` | the artifact walked for references |
141
+ | `concurrency` | `4` | artifacts in flight at once |
142
+ | `maxBytes` | 1 GiB | one file, from `Content-Length` and again mid-stream |
143
+ | `timeoutMs` | 120 s | one file's whole exchange, plus the dispatcher's per-stall timeouts |
144
+ | `maxTotalBytes` | 4 GiB | the bytes the whole call saves, a file in flight counting its declared length |
145
+ | `allowHttp` | `false` | whether a plain `http:` link is fetched |
146
+ | `signal` | — | cancels the call: in-flight fetches are cancelled and their partial files unlinked, the rest are marked, and the verdict comes back with `aborted: true` |
147
+
148
+ The per-file defaults are the MCP server's, which this replaces; the caps are accident guards against filling a disk from a runaway output, not judgments about artifact size.
149
+
150
+ ## Round trip
151
+
152
+ The two directions compose. A file uploaded by `prepareInputs` is echoed in the run's working memory under the same reference, so a pass-through run brings it back byte for byte:
153
+
154
+ ```ts
155
+ const prepared = await client.prepareInputs({ files, inputs: { doc: "./brief.pdf", note: "hi" } });
156
+ const results = await client.startAndWaitForResult({ files, pipe_code, inputs: prepared.inputs });
157
+ const verdict = await client.downloadArtifacts({
158
+ run_id: results.pipeline_run_id,
159
+ dir: "./out",
160
+ scope: "working_memory",
161
+ });
162
+ // verdict.artifacts finds prepared.uploads[0].uri among the saved files
163
+ ```
164
+
165
+ That is also the live e2e leg (`tests/e2e/artifacts.e2e.ts`), which needs a platform carrying the bulk route.