antpath 0.10.15 → 0.11.4

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.
Files changed (75) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +16 -8
  3. package/dist/_shared/blueprint.d.ts +93 -108
  4. package/dist/_shared/blueprint.js +144 -78
  5. package/dist/_shared/cleanup-policy.d.ts +2 -2
  6. package/dist/_shared/cleanup-policy.js +2 -5
  7. package/dist/_shared/http.d.ts +2 -2
  8. package/dist/_shared/index.d.ts +5 -1
  9. package/dist/_shared/index.js +5 -1
  10. package/dist/_shared/mcp-proxy-url.d.ts +55 -0
  11. package/dist/_shared/mcp-proxy-url.js +65 -0
  12. package/dist/_shared/operations.d.ts +7 -8
  13. package/dist/_shared/operations.js +14 -20
  14. package/dist/_shared/provider-proxy-url.d.ts +64 -0
  15. package/dist/_shared/provider-proxy-url.js +73 -0
  16. package/dist/_shared/proxy-validation.d.ts +1 -1
  17. package/dist/_shared/proxy-validation.js +2 -2
  18. package/dist/_shared/run-unit.d.ts +23 -36
  19. package/dist/_shared/run-unit.js +30 -46
  20. package/dist/_shared/runner-event.d.ts +120 -0
  21. package/dist/_shared/runner-event.js +193 -0
  22. package/dist/_shared/runner-job.d.ts +159 -0
  23. package/dist/_shared/runner-job.js +54 -0
  24. package/dist/_shared/runtime-manifest.d.ts +191 -0
  25. package/dist/_shared/runtime-manifest.js +221 -0
  26. package/dist/_shared/runtime-types.d.ts +7 -16
  27. package/dist/_shared/stable.d.ts +15 -10
  28. package/dist/_shared/stable.js +15 -10
  29. package/dist/_shared/submission.d.ts +221 -73
  30. package/dist/_shared/submission.js +442 -212
  31. package/dist/_shared/telemetry.d.ts +2 -2
  32. package/dist/_shared/telemetry.js +2 -2
  33. package/dist/_shared/template/index.d.ts +0 -1
  34. package/dist/_shared/template/index.js +0 -1
  35. package/dist/agents-md.d.ts +25 -67
  36. package/dist/agents-md.js +35 -121
  37. package/dist/agents-md.js.map +1 -1
  38. package/dist/asset-upload.d.ts +34 -0
  39. package/dist/asset-upload.js +34 -0
  40. package/dist/asset-upload.js.map +1 -1
  41. package/dist/blueprint.d.ts +3 -3
  42. package/dist/bundle.d.ts +2 -2
  43. package/dist/bundle.js +1 -1
  44. package/dist/cli.mjs +191 -100
  45. package/dist/cli.mjs.sha256 +1 -1
  46. package/dist/client.d.ts +56 -19
  47. package/dist/client.js +147 -125
  48. package/dist/client.js.map +1 -1
  49. package/dist/file.d.ts +28 -94
  50. package/dist/file.js +35 -175
  51. package/dist/file.js.map +1 -1
  52. package/dist/index.d.ts +5 -5
  53. package/dist/index.js +4 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/mcp-server.d.ts +10 -2
  56. package/dist/mcp-server.js +17 -2
  57. package/dist/mcp-server.js.map +1 -1
  58. package/dist/skill.d.ts +44 -214
  59. package/dist/skill.js +50 -284
  60. package/dist/skill.js.map +1 -1
  61. package/dist/version.d.ts +1 -1
  62. package/dist/version.js +1 -1
  63. package/dist/version.js.map +1 -1
  64. package/docs/cleanup.md +1 -1
  65. package/docs/credentials.md +2 -2
  66. package/docs/events.md +8 -8
  67. package/docs/outputs.md +2 -0
  68. package/docs/quickstart.md +18 -2
  69. package/docs/skills.md +1 -3
  70. package/docs/templates.md +6 -5
  71. package/package.json +3 -2
  72. package/dist/_shared/secrets.d.ts +0 -7
  73. package/dist/_shared/secrets.js +0 -20
  74. package/dist/_shared/template/mapper.d.ts +0 -11
  75. package/dist/_shared/template/mapper.js +0 -70
package/dist/client.d.ts CHANGED
@@ -1,17 +1,16 @@
1
- import { HttpClient, type AgentsMdRecord, type FetchLike, type FileRecord, type Output, type PlatformFlatRunSubmissionInput, type PlatformFlatSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type Run, type RunEvent, type RunUnit, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_shared/index.js";
1
+ import { HttpClient, type AgentsMdRecord, type FetchLike, type FileRecord, type Output, type PlatformRunSubmissionInput, type PlatformSubmission, type PlatformInlineSecrets, type PlatformProxyEndpoint, type PlatformProxyEndpointAuth, type Run, type RunEvent, type RunProvider, type RunUnit, type RuntimeKind, type RuntimeManifest, type SignedOutputLink, type Skill as SkillRecord, type WhoAmI } from "./_shared/index.js";
2
2
  import type { Blueprint } from "./blueprint.js";
3
3
  import { AgentsMd } from "./agents-md.js";
4
4
  import { File } from "./file.js";
5
5
  import { McpServer } from "./mcp-server.js";
6
6
  import { ProxyEndpoint } from "./proxy-endpoint.js";
7
7
  import { Skill } from "./skill.js";
8
- import type { ChunkedUploadClient } from "./skill.js";
9
8
  export interface AntpathClientOptions {
10
9
  /** Workspace-scoped SDK API token. */
11
10
  readonly apiToken: string;
12
11
  /**
13
- * Dashboard BFF root, e.g. `https://antpath.example.com`. Optional —
14
- * defaults to the canonical hosted URL (`https://www.antpath.ai`).
12
+ * API plane root, e.g. `https://antpath.example.com`. Optional —
13
+ * defaults to the canonical hosted URL (`https://api.antpath.ai`).
15
14
  * Self-hosted deployments override; the default lives in source so
16
15
  * the agent reading the SDK call site can see exactly where the call
17
16
  * goes.
@@ -27,7 +26,7 @@ export interface AntpathClientOptions {
27
26
  * - `model` / `system` / `prompt` — the agent's brief.
28
27
  * - `skills` — array of `Skill` instances. Each instance is one of:
29
28
  * workspace ref (`Skill.fromId` or the result of `.upload(client)`),
30
- * provider ref (`Skill.provider`), or unstaged transient
29
+ * provider ref (`Skill.provider`), or unstaged inline
31
30
  * (`Skill.fromFiles` / `Skill.fromPath`). Unstaged transient
32
31
  * skills cause `submitRun` to switch to multipart and upload the
33
32
  * bytes for this one run only.
@@ -45,6 +44,23 @@ export interface AntpathClientOptions {
45
44
  * if you want client-driven retry safety across process restarts.
46
45
  */
47
46
  export interface SubmitRunOptions {
47
+ /**
48
+ * Provider selector. Optional — defaults to
49
+ * {@link DEFAULT_RUN_PROVIDER} (`"anthropic"`). The call site must
50
+ * supply the matching `secrets.<provider>.apiKey` and MUST NOT
51
+ * supply any other provider's secret block. See
52
+ * `references/platform-rebuild-2026.md` (Customer interface).
53
+ */
54
+ readonly provider?: RunProvider;
55
+ /**
56
+ * Optional runtime opt-out. `"managed"` forces the Goose Managed
57
+ * runtime (Fly Machine + LiteLLM); `"native"` is only valid when
58
+ * `provider === "anthropic"` and routes through Anthropic's Managed
59
+ * Agents API. Omit to let the platform auto-route based on provider.
60
+ * The platform rejects features the chosen runtime cannot serve —
61
+ * see {@link selectRuntime}.
62
+ */
63
+ readonly runtime?: RuntimeKind;
48
64
  readonly model: string;
49
65
  readonly system?: string;
50
66
  readonly prompt: string | readonly string[];
@@ -52,9 +68,9 @@ export interface SubmitRunOptions {
52
68
  readonly agentsMd?: readonly AgentsMd[];
53
69
  readonly files?: readonly File[];
54
70
  readonly mcpServers?: readonly McpServer[];
55
- readonly environment?: PlatformFlatSubmission["environment"];
56
- readonly metadata?: PlatformFlatSubmission["metadata"];
57
- readonly cleanup?: PlatformFlatRunSubmissionInput["cleanup"];
71
+ readonly environment?: PlatformSubmission["environment"];
72
+ readonly metadata?: PlatformSubmission["metadata"];
73
+ readonly cleanup?: PlatformRunSubmissionInput["cleanup"];
58
74
  readonly proxyEndpoints?: readonly ProxyEndpoint[];
59
75
  /**
60
76
  * Container paths to capture as `output_objects` at session terminal.
@@ -71,6 +87,28 @@ export interface SubmitRunOptions {
71
87
  * `packages/sdk/docs/outputs.md` for the full contract.
72
88
  */
73
89
  readonly outputDirs?: readonly string[];
90
+ /**
91
+ * Override the Goose builtin extensions enabled inside the runner
92
+ * container. Each entry is a Goose builtin name passed to
93
+ * `goose run --with-builtin <NAME>`.
94
+ *
95
+ * - Omitted (default): the runner enables `["developer"]` which gives
96
+ * the agent `shell`, `write`, `edit`, and `tree` tools (bash, grep
97
+ * via shell, file read via shell or editor, file edit).
98
+ * - Empty array: the agent runs with zero builtin extensions —
99
+ * useful for pure-MCP setups where every tool comes from a
100
+ * submitted `mcpServers` entry.
101
+ * - Custom list: e.g. `["developer", "computercontroller"]` to add
102
+ * web search alongside the default shell/edit toolkit.
103
+ *
104
+ * Anthropic Native runs ignore this field (no Goose); the dispatcher
105
+ * persists it on the run snapshot for fidelity but the Anthropic
106
+ * Native adapter never reads it.
107
+ *
108
+ * Validation: each entry matches `/^[a-z][a-z0-9_-]{0,63}$/`, max 16
109
+ * entries, deduplicated server-side.
110
+ */
111
+ readonly builtins?: readonly string[];
74
112
  readonly secrets: PlatformInlineSecrets;
75
113
  readonly idempotencyKey?: string;
76
114
  readonly signal?: AbortSignal;
@@ -99,11 +137,18 @@ export interface WaitForRunOptions {
99
137
  * Lightweight reference to a submitted run. All read-back operations
100
138
  * delegate to the same `AntpathClient` and the same operations module
101
139
  * the CLI uses, so SDK and CLI never drift.
140
+ *
141
+ * `runtimeManifest` is the per-run, per-provider description of where
142
+ * antpath placed things inside the agent container, plus the merged
143
+ * env-var bag delivered via `RUNTIME.env` / `RUNTIME.json`. Populated
144
+ * on instances returned by `submitRun`; instances constructed by `id`
145
+ * elsewhere call `.get()` to populate the field via the GET response.
102
146
  */
103
147
  export declare class RunRef {
104
148
  #private;
105
149
  readonly runId: string;
106
- constructor(client: AntpathClient, runId: string);
150
+ readonly runtimeManifest?: RuntimeManifest;
151
+ constructor(client: AntpathClient, runId: string, runtimeManifest?: RuntimeManifest);
107
152
  get(): Promise<Run>;
108
153
  /** Convenience wrapper for `AntpathClient.getRunUnit`. */
109
154
  getUnit(): Promise<RunUnit>;
@@ -288,14 +333,6 @@ export declare class AntpathClient {
288
333
  readonly name: string;
289
334
  readonly bytes: Uint8Array;
290
335
  }): Promise<FileRecord>;
291
- /**
292
- * Internal: chunked-upload entry point for `Skill.upload` and
293
- * `File.upload` when the bundle exceeds the 6 MiB threshold.
294
- * Drives the three-step TUS flow: init → uploadChunked → finalize.
295
- * NOT part of the public API — the underscore is a "do not call
296
- * from user code" marker.
297
- */
298
- readonly _chunkedUpload: ChunkedUploadClient;
299
336
  /**
300
337
  * Submit a run and wait for it to reach a terminal state. Returns the
301
338
  * final `Run` record. For long-running flows, prefer `submitRun` +
@@ -311,7 +348,7 @@ export declare class AntpathClient {
311
348
  * before sending so credentials never enter the hashed submission or
312
349
  * the run snapshot.
313
350
  *
314
- * Unstaged transient skills (`Skill.fromFiles` / `Skill.fromPath`
351
+ * Unstaged inline skills (`Skill.fromFiles` / `Skill.fromPath`
315
352
  * without a prior `.upload`) are accepted: the SDK switches to a
316
353
  * multipart body that carries the canonical zip bytes alongside the
317
354
  * JSON submission. The dashboard BFF ingests each one through the
@@ -328,7 +365,7 @@ export declare class AntpathClient {
328
365
  * attempts, indexed events (inline + cursor for the tail), raw
329
366
  * provider-event Storage manifest, outputs, capture failures,
330
367
  * proxy-call audit, pinned workspace skills, provider skills,
331
- * transient skills. Backed by the same endpoint as `getRun` but
368
+ * inline skills. Backed by the same endpoint as `getRun` but
332
369
  * typed against the full wire shape — use this when you need
333
370
  * fields beyond `{id, status, timestamps, usage}`.
334
371
  */
package/dist/client.js CHANGED
@@ -1,4 +1,5 @@
1
- import { HttpClient, operations, reportTelemetryError } from "./_shared/index.js";
1
+ import { DEFAULT_RUN_PROVIDER, HttpClient, operations, reportTelemetryError } from "./_shared/index.js";
2
+ import { uploadAssetToR2 } from "./asset-upload.js";
2
3
  import { SDK_VERSION } from "./version.js";
3
4
  import { AgentsMd } from "./agents-md.js";
4
5
  import { File } from "./file.js";
@@ -9,13 +10,23 @@ import { Skill } from "./skill.js";
9
10
  * Lightweight reference to a submitted run. All read-back operations
10
11
  * delegate to the same `AntpathClient` and the same operations module
11
12
  * the CLI uses, so SDK and CLI never drift.
13
+ *
14
+ * `runtimeManifest` is the per-run, per-provider description of where
15
+ * antpath placed things inside the agent container, plus the merged
16
+ * env-var bag delivered via `RUNTIME.env` / `RUNTIME.json`. Populated
17
+ * on instances returned by `submitRun`; instances constructed by `id`
18
+ * elsewhere call `.get()` to populate the field via the GET response.
12
19
  */
13
20
  export class RunRef {
14
21
  runId;
22
+ runtimeManifest;
15
23
  #client;
16
- constructor(client, runId) {
24
+ constructor(client, runId, runtimeManifest) {
17
25
  this.#client = client;
18
26
  this.runId = runId;
27
+ if (runtimeManifest) {
28
+ this.runtimeManifest = runtimeManifest;
29
+ }
19
30
  }
20
31
  get() {
21
32
  return this.#client.getRun(this.runId);
@@ -259,17 +270,6 @@ export class AntpathClient {
259
270
  async _uploadFile(args) {
260
271
  return this.files._uploadFile(args);
261
272
  }
262
- /**
263
- * Internal: chunked-upload entry point for `Skill.upload` and
264
- * `File.upload` when the bundle exceeds the 6 MiB threshold.
265
- * Drives the three-step TUS flow: init → uploadChunked → finalize.
266
- * NOT part of the public API — the underscore is a "do not call
267
- * from user code" marker.
268
- */
269
- _chunkedUpload = {
270
- init: (args) => operations.initAssetUpload(this.#http, args),
271
- finalize: (args) => operations.finalizeAssetUpload(this.#http, args)
272
- };
273
273
  /**
274
274
  * Submit a run and wait for it to reach a terminal state. Returns the
275
275
  * final `Run` record. For long-running flows, prefer `submitRun` +
@@ -288,7 +288,7 @@ export class AntpathClient {
288
288
  * before sending so credentials never enter the hashed submission or
289
289
  * the run snapshot.
290
290
  *
291
- * Unstaged transient skills (`Skill.fromFiles` / `Skill.fromPath`
291
+ * Unstaged inline skills (`Skill.fromFiles` / `Skill.fromPath`
292
292
  * without a prior `.upload`) are accepted: the SDK switches to a
293
293
  * multipart body that carries the canonical zip bytes alongside the
294
294
  * JSON submission. The dashboard BFF ingests each one through the
@@ -302,8 +302,24 @@ export class AntpathClient {
302
302
  if (!options || typeof options !== "object") {
303
303
  throw new Error("AntpathClient.submitRun: options is required");
304
304
  }
305
- if (!options.secrets || !options.secrets.anthropic?.apiKey) {
306
- throw new Error("AntpathClient.submitRun: secrets.anthropic.apiKey is required");
305
+ const provider = options.provider ?? DEFAULT_RUN_PROVIDER;
306
+ if (!options.secrets) {
307
+ throw new Error("AntpathClient.submitRun: secrets is required");
308
+ }
309
+ // The matching provider's apiKey is required; every OTHER provider's
310
+ // secret block must be absent. The shared parser re-runs this check
311
+ // on the server; failing early here gives the caller a synchronous
312
+ // error before any network call.
313
+ const providerSecret = options.secrets[provider];
314
+ if (!providerSecret?.apiKey) {
315
+ throw new Error(`AntpathClient.submitRun: secrets.${provider}.apiKey is required`);
316
+ }
317
+ for (const other of ["anthropic", "deepseek", "openai", "gemini", "mistral"]) {
318
+ if (other === provider)
319
+ continue;
320
+ if (options.secrets[other] !== undefined) {
321
+ throw new Error(`AntpathClient.submitRun: secrets.${other} is not allowed when provider is ${provider}`);
322
+ }
307
323
  }
308
324
  if (typeof options.model !== "string" || !options.model) {
309
325
  throw new Error("AntpathClient.submitRun: model is required");
@@ -311,22 +327,25 @@ export class AntpathClient {
311
327
  const prompt = normalisePrompt(options.prompt);
312
328
  const { endpoints: proxyEndpointDeclarations, auth: proxyEndpointAuthFromInstances } = splitProxyEndpoints(options.proxyEndpoints ?? []);
313
329
  const mergedProxyAuth = mergeProxyEndpointAuth(proxyEndpointAuthFromInstances, options.secrets.proxyEndpointAuth ?? []);
314
- const { skillRefs, transientBundles } = prepareSkills(options.skills ?? []);
315
- const { agentsMdRefs, transientAgentsMdParts } = prepareAgentsMd(options.agentsMd ?? []);
316
- const { fileRefs, transientFileParts } = prepareFiles(options.files ?? []);
330
+ // Phase B + D: walk Skill / AgentsMd / File instances, materialize
331
+ // every draft (local bytes) to R2 BEFORE the submit round-trip.
332
+ // The wire shape carries only kind:"r2" / kind:"provider" refs.
333
+ const r2Skills = await materializeSkills(this.#http, options.skills ?? []);
334
+ const r2AgentsMd = await materializeAgentsMd(this.#http, options.agentsMd ?? []);
335
+ const r2Files = await materializeFiles(this.#http, options.files ?? []);
317
336
  const { submissionMcpServers, mergedMcpSecrets } = mergeMcpServers(options.mcpServers ?? [], options.secrets.mcpServers ?? []);
318
337
  const submission = {
319
338
  model: options.model,
320
339
  ...(options.system ? { system: options.system } : {}),
321
340
  prompt,
322
- skills: skillRefs,
323
- agentsMd: agentsMdRefs,
324
- files: fileRefs,
341
+ skills: r2Skills,
342
+ agentsMd: r2AgentsMd,
343
+ files: r2Files,
325
344
  // submissionMcpServers may contain workspace refs of the shape
326
345
  // {kind:"workspace", id:"mcp_..."}. The BFF runs
327
346
  // `resolveWorkspaceMcpRefsInSubmission` BEFORE the shared parser
328
347
  // and replaces them with the resolved {name, url}, so by the
329
- // time anything reads PlatformFlatSubmission post-parse the
348
+ // time anything reads PlatformSubmission post-parse the
330
349
  // shape matches McpServerRef. The cast acknowledges that the
331
350
  // SDK is producing pre-resolution wire input here.
332
351
  mcpServers: submissionMcpServers,
@@ -334,7 +353,11 @@ export class AntpathClient {
334
353
  ...(options.metadata ? { metadata: options.metadata } : {}),
335
354
  ...(options.outputDirs && options.outputDirs.length > 0
336
355
  ? { outputDirs: options.outputDirs }
337
- : {})
356
+ : {}),
357
+ // Pass-through `builtins` verbatim — including an empty array,
358
+ // which is the "disable all builtins" signal. Distinguish from
359
+ // omitted (default applies) via `!== undefined`.
360
+ ...(options.builtins !== undefined ? { builtins: options.builtins } : {})
338
361
  };
339
362
  const secrets = {
340
363
  ...options.secrets,
@@ -343,6 +366,15 @@ export class AntpathClient {
343
366
  };
344
367
  const request = {
345
368
  idempotencyKey: options.idempotencyKey ?? generateIdempotencyKey(),
369
+ // Always include `provider` on the wire so dashboard / proxy
370
+ // tooling never has to second-guess what the runtime saw. The
371
+ // shared parser still defaults to `anthropic` when callers omit
372
+ // the field entirely, but the SDK has resolved it by here.
373
+ provider,
374
+ // `runtime` is optional on the wire — absent means let the
375
+ // dispatcher auto-route. Only emit it when the caller asked for
376
+ // a specific runtime so the wire shape stays minimal.
377
+ ...(options.runtime ? { runtime: options.runtime } : {}),
346
378
  submission,
347
379
  ...(options.cleanup ? { cleanup: options.cleanup } : {}),
348
380
  secrets,
@@ -350,12 +382,11 @@ export class AntpathClient {
350
382
  ? { proxyEndpoints: proxyEndpointDeclarations }
351
383
  : {})
352
384
  };
353
- const isMultipart = transientBundles.length > 0 || transientAgentsMdParts.length > 0 || transientFileParts.length > 0;
385
+ // All inline refs were materialized to R2 above, so submitRun is
386
+ // always a plain JSON post. The multipart code path is gone.
354
387
  let run;
355
388
  try {
356
- run = isMultipart
357
- ? await operations.submitRunFlatMultipart(this.#http, request, transientBundles, transientAgentsMdParts, transientFileParts)
358
- : await operations.submitRunFlat(this.#http, request);
389
+ run = await operations.submitRun(this.#http, request);
359
390
  }
360
391
  catch (err) {
361
392
  // Fire-and-forget telemetry for unexpected submit failures so we
@@ -375,13 +406,12 @@ export class AntpathClient {
375
406
  status: typeof err.status === "number"
376
407
  ? err.status
377
408
  : undefined,
378
- message: err?.message?.slice(0, 200) ?? null,
379
- multipart: isMultipart
409
+ message: err?.message?.slice(0, 200) ?? null
380
410
  }
381
411
  });
382
412
  throw err;
383
413
  }
384
- return new RunRef(this, run.id);
414
+ return new RunRef(this, run.id, extractRuntimeManifest(run));
385
415
  }
386
416
  getRun(runId) {
387
417
  return operations.getRun(this.#http, runId);
@@ -391,7 +421,7 @@ export class AntpathClient {
391
421
  * attempts, indexed events (inline + cursor for the tail), raw
392
422
  * provider-event Storage manifest, outputs, capture failures,
393
423
  * proxy-call audit, pinned workspace skills, provider skills,
394
- * transient skills. Backed by the same endpoint as `getRun` but
424
+ * inline skills. Backed by the same endpoint as `getRun` but
395
425
  * typed against the full wire shape — use this when you need
396
426
  * fields beyond `{id, status, timestamps, usage}`.
397
427
  */
@@ -525,6 +555,20 @@ const TERMINAL_STATUSES = new Set([
525
555
  "canceled",
526
556
  "cleaned_up"
527
557
  ]);
558
+ /**
559
+ * Pull the runtime manifest off a submitRun / getRun response. The
560
+ * field is optional on the wire shape (older BFFs predate it); we
561
+ * return `undefined` when absent rather than fabricating a default,
562
+ * so callers can detect deployment skew explicitly.
563
+ */
564
+ function extractRuntimeManifest(run) {
565
+ if (!run || typeof run !== "object")
566
+ return undefined;
567
+ const value = run.runtimeManifest;
568
+ if (!value || typeof value !== "object" || Array.isArray(value))
569
+ return undefined;
570
+ return value;
571
+ }
528
572
  function isTerminal(status) {
529
573
  return typeof status === "string" && TERMINAL_STATUSES.has(status);
530
574
  }
@@ -572,145 +616,123 @@ function normalisePrompt(input) {
572
616
  * Walk the user-provided `Skill[]`, validating each instance and
573
617
  * producing:
574
618
  * - `skillRefs[]` — the wire entries for `submission.skills[]`, with
575
- * transient refs assigned positional slot ids (`transient-0`, …).
576
- * - `transientBundles[]` — the bytes for each transient skill,
619
+ * inline refs assigned positional slot ids (`transient-0`, …).
620
+ * - `inlineBundles[]` — the bytes for each inline skill,
577
621
  * parallel-indexed by slot.
578
622
  *
579
623
  * Throws on consumed Skills (the user kept a stale reference past an
580
624
  * `.upload(client)` call) with the same error message documented in
581
625
  * `Skill.upload` — that mistake is loud, not silent.
582
626
  */
583
- function prepareSkills(skills) {
584
- const skillRefs = [];
585
- const transientBundles = [];
586
- let transientIndex = 0;
627
+ /**
628
+ * Walk the user-provided Skill[], materialize every draft to R2,
629
+ * return the wire-shape refs. Provider skills pass through.
630
+ */
631
+ async function materializeSkills(http, skills) {
632
+ const out = [];
587
633
  for (let i = 0; i < skills.length; i++) {
588
634
  const entry = skills[i];
589
635
  if (!(entry instanceof Skill)) {
590
636
  throw new Error(`AntpathClient.submitRun: skills[${i}] must be a Skill instance`);
591
637
  }
592
638
  if (entry.isConsumed) {
593
- throw new Error(`AntpathClient.submitRun: skills[${i}] was already uploaded via skill.upload(client); ` +
594
- `use the returned Skill or Skill.fromId(record.id) for subsequent runs`);
639
+ throw new Error(`AntpathClient.submitRun: skills[${i}] was already consumed by a prior submitRun`);
595
640
  }
596
- if (entry.isUnstaged) {
597
- const bundle = entry._takeUnstagedBundle();
641
+ const ref = entry.ref;
642
+ if (ref.kind === "draft") {
643
+ const bundle = entry._takeDraftBundle();
598
644
  if (!bundle) {
599
- // Defence-in-depth: `isUnstaged` guarantees bytes exist.
600
- throw new Error(`AntpathClient.submitRun: skills[${i}] is unstaged but has no bytes (internal invariant violated)`);
645
+ throw new Error(`AntpathClient.submitRun: skills[${i}] is draft but has no bytes`);
601
646
  }
602
- const slot = `transient-${transientIndex++}`;
603
- skillRefs.push({
604
- kind: "transient",
605
- slot,
606
- name: bundle.name,
607
- contentHash: bundle.contentHash
608
- });
609
- // Anthropic filenames forbid `<>:"|?*\/` and control chars 0-31,
610
- // so the multipart filename uses hyphen separators. The 12-char
611
- // hash prefix is enough for janitor / orphan reconciliation;
612
- // the BFF stamps the full run-scoped filename once it knows the
613
- // run id.
614
- const shortHash = bundle.contentHash.replace(/^sha256:/, "").slice(0, 12);
615
- transientBundles.push({
616
- slot,
647
+ const uploaded = await uploadAssetToR2({
648
+ http,
617
649
  bytes: bundle.bytes,
618
- filename: `antpath-${slot}-${shortHash}.zip`
650
+ hash: bundle.contentHash
651
+ });
652
+ out.push({
653
+ kind: "r2",
654
+ path: uploaded.path,
655
+ hash: uploaded.hash,
656
+ sizeBytes: uploaded.sizeBytes,
657
+ name: bundle.name
619
658
  });
620
659
  continue;
621
660
  }
622
- skillRefs.push(entry.ref);
661
+ // Provider or r2 (re-used from a previous materialization).
662
+ out.push(ref);
623
663
  }
624
- return { skillRefs, transientBundles };
664
+ return out;
625
665
  }
626
- /**
627
- * Walk the user-provided `AgentsMd[]`, validating each instance and
628
- * producing:
629
- * - `agentsMdRefs[]` — the wire entries for `submission.agentsMd[]`,
630
- * with transient refs assigned positional slot ids
631
- * (`agentsmd-0`, `agentsmd-1`, …).
632
- * - `transientAgentsMdParts[]` — the content for each transient
633
- * AgentsMd, parallel-indexed by slot.
634
- */
635
- function prepareAgentsMd(agentsMds) {
636
- const agentsMdRefs = [];
637
- const transientAgentsMdParts = [];
638
- let transientIndex = 0;
666
+ /** Materialize draft AgentsMd[] to R2; pass-through any already-r2. */
667
+ async function materializeAgentsMd(http, agentsMds) {
668
+ const out = [];
639
669
  for (let i = 0; i < agentsMds.length; i++) {
640
670
  const entry = agentsMds[i];
641
671
  if (!(entry instanceof AgentsMd)) {
642
672
  throw new Error(`AntpathClient.submitRun: agentsMd[${i}] must be an AgentsMd instance`);
643
673
  }
644
674
  if (entry.isConsumed) {
645
- throw new Error(`AntpathClient.submitRun: agentsMd[${i}] was already uploaded via agentsMd.upload(client); ` +
646
- `use the returned AgentsMd or AgentsMd.fromId(record.id) for subsequent runs`);
675
+ throw new Error(`AntpathClient.submitRun: agentsMd[${i}] was already consumed by a prior submitRun`);
647
676
  }
648
- if (entry.isUnstaged) {
649
- const unstaged = entry._takeUnstagedContent();
650
- if (!unstaged) {
651
- throw new Error(`AntpathClient.submitRun: agentsMd[${i}] is unstaged but has no content (internal invariant violated)`);
677
+ const ref = entry.ref;
678
+ if (ref.kind === "draft") {
679
+ const bundle = entry._takeDraftBundle();
680
+ if (!bundle) {
681
+ throw new Error(`AntpathClient.submitRun: agentsMd[${i}] is draft but has no bytes`);
652
682
  }
653
- const slot = `agentsmd-${transientIndex++}`;
654
- agentsMdRefs.push({
655
- kind: "transient_agentsmd",
656
- slot,
657
- name: unstaged.name,
658
- contentHash: unstaged.contentHash
659
- });
660
- transientAgentsMdParts.push({
661
- slot,
662
- content: unstaged.content,
663
- filename: `${unstaged.name}.md`
683
+ const uploaded = await uploadAssetToR2({ http, bytes: bundle.bytes, hash: bundle.contentHash });
684
+ out.push({
685
+ kind: "r2",
686
+ path: uploaded.path,
687
+ hash: uploaded.hash,
688
+ sizeBytes: uploaded.sizeBytes,
689
+ name: bundle.name
664
690
  });
665
691
  continue;
666
692
  }
667
- agentsMdRefs.push(entry.ref);
693
+ out.push(ref);
668
694
  }
669
- return { agentsMdRefs, transientAgentsMdParts };
695
+ return out;
670
696
  }
671
- /**
672
- * Walk the user-provided `File[]`, validating each instance and producing:
673
- * - `fileRefs[]` — the wire entries for `submission.files[]`, with
674
- * transient refs assigned positional slot ids (`file-0`, `file-1`, …).
675
- * - `transientFileParts[]` — the bytes for each transient file, parallel-indexed.
676
- */
677
- function prepareFiles(files) {
678
- const fileRefs = [];
679
- const transientFileParts = [];
680
- let transientIndex = 0;
697
+ /** Materialize draft File[] to R2; pass-through any already-r2. */
698
+ async function materializeFiles(http, files) {
699
+ const out = [];
681
700
  for (let i = 0; i < files.length; i++) {
682
701
  const entry = files[i];
683
702
  if (!(entry instanceof File)) {
684
703
  throw new Error(`AntpathClient.submitRun: files[${i}] must be a File instance`);
685
704
  }
686
705
  if (entry.isConsumed) {
687
- throw new Error(`AntpathClient.submitRun: files[${i}] was already uploaded via file.upload(client); ` +
688
- `use the returned File or File.fromId(record.id) for subsequent runs`);
706
+ throw new Error(`AntpathClient.submitRun: files[${i}] was already consumed by a prior submitRun`);
689
707
  }
690
- if (entry.isUnstaged) {
691
- const bundle = entry._takeUnstagedBundle();
708
+ const ref = entry.ref;
709
+ if (ref.kind === "draft") {
710
+ const bundle = entry._takeDraftBundle();
692
711
  if (!bundle) {
693
- throw new Error(`AntpathClient.submitRun: files[${i}] is unstaged but has no bytes (internal invariant violated)`);
712
+ throw new Error(`AntpathClient.submitRun: files[${i}] is draft but has no bytes`);
694
713
  }
695
- const slot = `file-${transientIndex++}`;
696
- fileRefs.push({
697
- kind: "transient_file",
698
- slot,
699
- name: bundle.name,
700
- contentHash: bundle.contentHash,
701
- ...(bundle.mountPath ? { mountPath: bundle.mountPath } : {})
702
- });
703
- const shortHash = bundle.contentHash.replace(/^sha256:/, "").slice(0, 12);
704
- transientFileParts.push({
705
- slot,
706
- bytes: bundle.bytes,
707
- filename: `antpath-${slot}-${shortHash}.zip`
708
- });
714
+ const uploaded = await uploadAssetToR2({ http, bytes: bundle.bytes, hash: bundle.contentHash });
715
+ out.push(bundle.mountPath !== undefined
716
+ ? {
717
+ kind: "r2",
718
+ path: uploaded.path,
719
+ hash: uploaded.hash,
720
+ sizeBytes: uploaded.sizeBytes,
721
+ name: bundle.name,
722
+ mountPath: bundle.mountPath
723
+ }
724
+ : {
725
+ kind: "r2",
726
+ path: uploaded.path,
727
+ hash: uploaded.hash,
728
+ sizeBytes: uploaded.sizeBytes,
729
+ name: bundle.name
730
+ });
709
731
  continue;
710
732
  }
711
- fileRefs.push(entry.ref);
733
+ out.push(ref);
712
734
  }
713
- return { fileRefs, transientFileParts };
735
+ return out;
714
736
  }
715
737
  function mergeMcpServers(inputs, explicitSecrets) {
716
738
  const submissionMcpServers = [];