antpath 0.10.14 → 0.11.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.
Files changed (77) 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 +7 -1
  9. package/dist/_shared/index.js +6 -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 +55 -8
  13. package/dist/_shared/operations.js +163 -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/sse.d.ts +74 -0
  28. package/dist/_shared/sse.js +0 -0
  29. package/dist/_shared/stable.d.ts +15 -10
  30. package/dist/_shared/stable.js +15 -10
  31. package/dist/_shared/submission.d.ts +199 -73
  32. package/dist/_shared/submission.js +409 -210
  33. package/dist/_shared/telemetry.d.ts +2 -2
  34. package/dist/_shared/telemetry.js +2 -2
  35. package/dist/_shared/template/index.d.ts +0 -1
  36. package/dist/_shared/template/index.js +0 -1
  37. package/dist/agents-md.d.ts +25 -67
  38. package/dist/agents-md.js +35 -121
  39. package/dist/agents-md.js.map +1 -1
  40. package/dist/asset-upload.d.ts +34 -0
  41. package/dist/asset-upload.js +34 -0
  42. package/dist/asset-upload.js.map +1 -1
  43. package/dist/blueprint.d.ts +3 -3
  44. package/dist/bundle.d.ts +2 -2
  45. package/dist/bundle.js +1 -1
  46. package/dist/cli.mjs +559 -105
  47. package/dist/cli.mjs.sha256 +1 -1
  48. package/dist/client.d.ts +53 -22
  49. package/dist/client.js +196 -130
  50. package/dist/client.js.map +1 -1
  51. package/dist/file.d.ts +28 -94
  52. package/dist/file.js +35 -175
  53. package/dist/file.js.map +1 -1
  54. package/dist/index.d.ts +5 -5
  55. package/dist/index.js +4 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/mcp-server.d.ts +10 -2
  58. package/dist/mcp-server.js +17 -2
  59. package/dist/mcp-server.js.map +1 -1
  60. package/dist/skill.d.ts +44 -214
  61. package/dist/skill.js +50 -284
  62. package/dist/skill.js.map +1 -1
  63. package/dist/version.d.ts +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/version.js.map +1 -1
  66. package/docs/cleanup.md +1 -1
  67. package/docs/credentials.md +2 -2
  68. package/docs/events.md +8 -8
  69. package/docs/outputs.md +2 -0
  70. package/docs/quickstart.md +18 -2
  71. package/docs/skills.md +1 -3
  72. package/docs/templates.md +6 -5
  73. package/package.json +2 -1
  74. package/dist/_shared/secrets.d.ts +0 -7
  75. package/dist/_shared/secrets.js +0 -20
  76. package/dist/_shared/template/mapper.d.ts +0 -11
  77. package/dist/_shared/template/mapper.js +0 -70
@@ -1 +1 @@
1
- e4711068466806825d0e897c6266a8cb2141e584de6ee4f0116be17a16a185b2 cli.mjs
1
+ ba35805b91e9b19eeeead9f80278140797287bc4d14e6618b842354cb2cb3208 cli.mjs
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.
@@ -76,8 +92,19 @@ export interface SubmitRunOptions {
76
92
  readonly signal?: AbortSignal;
77
93
  }
78
94
  export interface StreamEventsOptions {
95
+ /** Polling interval in ms — used by the `polling` transport and as
96
+ * the fallback when SSE disconnects. Default 1000. */
79
97
  readonly intervalMs?: number;
80
98
  readonly signal?: AbortSignal;
99
+ /**
100
+ * Transport selector. `"auto"` (default) opens an SSE connection;
101
+ * on disconnect (server-side error, network blip, missing endpoint
102
+ * on older deployments) it falls back to polling for the remaining
103
+ * lifetime of the run. `"sse"` forces SSE and surfaces disconnects
104
+ * as iterator completion. `"polling"` keeps the historical poll
105
+ * loop — useful for environments without streaming support.
106
+ */
107
+ readonly transport?: "auto" | "sse" | "polling";
81
108
  }
82
109
  export interface WaitForRunOptions {
83
110
  readonly intervalMs?: number;
@@ -88,11 +115,18 @@ export interface WaitForRunOptions {
88
115
  * Lightweight reference to a submitted run. All read-back operations
89
116
  * delegate to the same `AntpathClient` and the same operations module
90
117
  * the CLI uses, so SDK and CLI never drift.
118
+ *
119
+ * `runtimeManifest` is the per-run, per-provider description of where
120
+ * antpath placed things inside the agent container, plus the merged
121
+ * env-var bag delivered via `RUNTIME.env` / `RUNTIME.json`. Populated
122
+ * on instances returned by `submitRun`; instances constructed by `id`
123
+ * elsewhere call `.get()` to populate the field via the GET response.
91
124
  */
92
125
  export declare class RunRef {
93
126
  #private;
94
127
  readonly runId: string;
95
- constructor(client: AntpathClient, runId: string);
128
+ readonly runtimeManifest?: RuntimeManifest;
129
+ constructor(client: AntpathClient, runId: string, runtimeManifest?: RuntimeManifest);
96
130
  get(): Promise<Run>;
97
131
  /** Convenience wrapper for `AntpathClient.getRunUnit`. */
98
132
  getUnit(): Promise<RunUnit>;
@@ -277,14 +311,6 @@ export declare class AntpathClient {
277
311
  readonly name: string;
278
312
  readonly bytes: Uint8Array;
279
313
  }): Promise<FileRecord>;
280
- /**
281
- * Internal: chunked-upload entry point for `Skill.upload` and
282
- * `File.upload` when the bundle exceeds the 6 MiB threshold.
283
- * Drives the three-step TUS flow: init → uploadChunked → finalize.
284
- * NOT part of the public API — the underscore is a "do not call
285
- * from user code" marker.
286
- */
287
- readonly _chunkedUpload: ChunkedUploadClient;
288
314
  /**
289
315
  * Submit a run and wait for it to reach a terminal state. Returns the
290
316
  * final `Run` record. For long-running flows, prefer `submitRun` +
@@ -300,7 +326,7 @@ export declare class AntpathClient {
300
326
  * before sending so credentials never enter the hashed submission or
301
327
  * the run snapshot.
302
328
  *
303
- * Unstaged transient skills (`Skill.fromFiles` / `Skill.fromPath`
329
+ * Unstaged inline skills (`Skill.fromFiles` / `Skill.fromPath`
304
330
  * without a prior `.upload`) are accepted: the SDK switches to a
305
331
  * multipart body that carries the canonical zip bytes alongside the
306
332
  * JSON submission. The dashboard BFF ingests each one through the
@@ -317,16 +343,21 @@ export declare class AntpathClient {
317
343
  * attempts, indexed events (inline + cursor for the tail), raw
318
344
  * provider-event Storage manifest, outputs, capture failures,
319
345
  * proxy-call audit, pinned workspace skills, provider skills,
320
- * transient skills. Backed by the same endpoint as `getRun` but
346
+ * inline skills. Backed by the same endpoint as `getRun` but
321
347
  * typed against the full wire shape — use this when you need
322
348
  * fields beyond `{id, status, timestamps, usage}`.
323
349
  */
324
350
  getRunUnit(runId: string): Promise<RunUnit>;
325
351
  listEvents(runId: string): Promise<readonly RunEvent[]>;
326
352
  /**
327
- * Poll the events endpoint and yield new events as they arrive. Stops
328
- * when the run reaches a terminal state, when the signal is aborted,
329
- * or when the caller breaks out of the iterator.
353
+ * Yield run events as they arrive. The default transport is SSE
354
+ * (`/api/runs/:id/events/stream`); on disconnect it falls back to
355
+ * polling for the remaining lifetime of the run, so older
356
+ * deployments without the SSE endpoint and transient network blips
357
+ * both keep working.
358
+ *
359
+ * Stops when the run reaches a terminal state, when the signal is
360
+ * aborted, or when the caller breaks out of the iterator.
330
361
  */
331
362
  streamEvents(runId: string, options?: StreamEventsOptions): AsyncIterable<RunEvent>;
332
363
  /**
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,
@@ -343,6 +362,15 @@ export class AntpathClient {
343
362
  };
344
363
  const request = {
345
364
  idempotencyKey: options.idempotencyKey ?? generateIdempotencyKey(),
365
+ // Always include `provider` on the wire so dashboard / proxy
366
+ // tooling never has to second-guess what the runtime saw. The
367
+ // shared parser still defaults to `anthropic` when callers omit
368
+ // the field entirely, but the SDK has resolved it by here.
369
+ provider,
370
+ // `runtime` is optional on the wire — absent means let the
371
+ // dispatcher auto-route. Only emit it when the caller asked for
372
+ // a specific runtime so the wire shape stays minimal.
373
+ ...(options.runtime ? { runtime: options.runtime } : {}),
346
374
  submission,
347
375
  ...(options.cleanup ? { cleanup: options.cleanup } : {}),
348
376
  secrets,
@@ -350,12 +378,11 @@ export class AntpathClient {
350
378
  ? { proxyEndpoints: proxyEndpointDeclarations }
351
379
  : {})
352
380
  };
353
- const isMultipart = transientBundles.length > 0 || transientAgentsMdParts.length > 0 || transientFileParts.length > 0;
381
+ // All inline refs were materialized to R2 above, so submitRun is
382
+ // always a plain JSON post. The multipart code path is gone.
354
383
  let run;
355
384
  try {
356
- run = isMultipart
357
- ? await operations.submitRunFlatMultipart(this.#http, request, transientBundles, transientAgentsMdParts, transientFileParts)
358
- : await operations.submitRunFlat(this.#http, request);
385
+ run = await operations.submitRun(this.#http, request);
359
386
  }
360
387
  catch (err) {
361
388
  // Fire-and-forget telemetry for unexpected submit failures so we
@@ -375,13 +402,12 @@ export class AntpathClient {
375
402
  status: typeof err.status === "number"
376
403
  ? err.status
377
404
  : undefined,
378
- message: err?.message?.slice(0, 200) ?? null,
379
- multipart: isMultipart
405
+ message: err?.message?.slice(0, 200) ?? null
380
406
  }
381
407
  });
382
408
  throw err;
383
409
  }
384
- return new RunRef(this, run.id);
410
+ return new RunRef(this, run.id, extractRuntimeManifest(run));
385
411
  }
386
412
  getRun(runId) {
387
413
  return operations.getRun(this.#http, runId);
@@ -391,7 +417,7 @@ export class AntpathClient {
391
417
  * attempts, indexed events (inline + cursor for the tail), raw
392
418
  * provider-event Storage manifest, outputs, capture failures,
393
419
  * proxy-call audit, pinned workspace skills, provider skills,
394
- * transient skills. Backed by the same endpoint as `getRun` but
420
+ * inline skills. Backed by the same endpoint as `getRun` but
395
421
  * typed against the full wire shape — use this when you need
396
422
  * fields beyond `{id, status, timestamps, usage}`.
397
423
  */
@@ -402,19 +428,67 @@ export class AntpathClient {
402
428
  return operations.listRunEvents(this.#http, runId);
403
429
  }
404
430
  /**
405
- * Poll the events endpoint and yield new events as they arrive. Stops
406
- * when the run reaches a terminal state, when the signal is aborted,
407
- * or when the caller breaks out of the iterator.
431
+ * Yield run events as they arrive. The default transport is SSE
432
+ * (`/api/runs/:id/events/stream`); on disconnect it falls back to
433
+ * polling for the remaining lifetime of the run, so older
434
+ * deployments without the SSE endpoint and transient network blips
435
+ * both keep working.
436
+ *
437
+ * Stops when the run reaches a terminal state, when the signal is
438
+ * aborted, or when the caller breaks out of the iterator.
408
439
  */
409
440
  async *streamEvents(runId, options = {}) {
410
- const intervalMs = options.intervalMs ?? 1_000;
441
+ const transport = options.transport ?? "auto";
411
442
  const signal = options.signal;
412
443
  const seenIds = new Set();
444
+ let cursor;
445
+ if (transport !== "polling") {
446
+ while (!signal?.aborted) {
447
+ const outcome = yield* this.#streamEventsSseRound(runId, {
448
+ cursor: cursor,
449
+ signal: signal,
450
+ seenIds
451
+ });
452
+ cursor = outcome.nextCursor;
453
+ if (outcome.kind === "terminal" || outcome.kind === "aborted")
454
+ return;
455
+ if (transport === "sse")
456
+ return; // disconnects surface as completion
457
+ // "auto" — fall through to polling for the rest of the run.
458
+ break;
459
+ }
460
+ }
461
+ if (signal?.aborted)
462
+ return;
463
+ yield* this.#streamEventsPolling(runId, { ...options, seenIds });
464
+ }
465
+ async *#streamEventsSseRound(runId, opts) {
466
+ const streamOpts = {};
467
+ if (opts.cursor !== undefined)
468
+ streamOpts.cursor = opts.cursor;
469
+ if (opts.signal !== undefined)
470
+ streamOpts.signal = opts.signal;
471
+ const iter = operations.streamRunEventsSse(this.#http, runId, streamOpts);
472
+ while (true) {
473
+ const next = await iter.next();
474
+ if (next.done) {
475
+ return next.value;
476
+ }
477
+ const event = next.value;
478
+ if (!opts.seenIds.has(event.id)) {
479
+ opts.seenIds.add(event.id);
480
+ yield event;
481
+ }
482
+ }
483
+ }
484
+ async *#streamEventsPolling(runId, options) {
485
+ const intervalMs = options.intervalMs ?? 1_000;
486
+ const signal = options.signal;
413
487
  while (!signal?.aborted) {
414
488
  const events = await this.listEvents(runId);
415
489
  for (const event of events) {
416
- if (!seenIds.has(event.id)) {
417
- seenIds.add(event.id);
490
+ if (!options.seenIds.has(event.id)) {
491
+ options.seenIds.add(event.id);
418
492
  yield event;
419
493
  }
420
494
  }
@@ -477,6 +551,20 @@ const TERMINAL_STATUSES = new Set([
477
551
  "canceled",
478
552
  "cleaned_up"
479
553
  ]);
554
+ /**
555
+ * Pull the runtime manifest off a submitRun / getRun response. The
556
+ * field is optional on the wire shape (older BFFs predate it); we
557
+ * return `undefined` when absent rather than fabricating a default,
558
+ * so callers can detect deployment skew explicitly.
559
+ */
560
+ function extractRuntimeManifest(run) {
561
+ if (!run || typeof run !== "object")
562
+ return undefined;
563
+ const value = run.runtimeManifest;
564
+ if (!value || typeof value !== "object" || Array.isArray(value))
565
+ return undefined;
566
+ return value;
567
+ }
480
568
  function isTerminal(status) {
481
569
  return typeof status === "string" && TERMINAL_STATUSES.has(status);
482
570
  }
@@ -524,145 +612,123 @@ function normalisePrompt(input) {
524
612
  * Walk the user-provided `Skill[]`, validating each instance and
525
613
  * producing:
526
614
  * - `skillRefs[]` — the wire entries for `submission.skills[]`, with
527
- * transient refs assigned positional slot ids (`transient-0`, …).
528
- * - `transientBundles[]` — the bytes for each transient skill,
615
+ * inline refs assigned positional slot ids (`transient-0`, …).
616
+ * - `inlineBundles[]` — the bytes for each inline skill,
529
617
  * parallel-indexed by slot.
530
618
  *
531
619
  * Throws on consumed Skills (the user kept a stale reference past an
532
620
  * `.upload(client)` call) with the same error message documented in
533
621
  * `Skill.upload` — that mistake is loud, not silent.
534
622
  */
535
- function prepareSkills(skills) {
536
- const skillRefs = [];
537
- const transientBundles = [];
538
- let transientIndex = 0;
623
+ /**
624
+ * Walk the user-provided Skill[], materialize every draft to R2,
625
+ * return the wire-shape refs. Provider skills pass through.
626
+ */
627
+ async function materializeSkills(http, skills) {
628
+ const out = [];
539
629
  for (let i = 0; i < skills.length; i++) {
540
630
  const entry = skills[i];
541
631
  if (!(entry instanceof Skill)) {
542
632
  throw new Error(`AntpathClient.submitRun: skills[${i}] must be a Skill instance`);
543
633
  }
544
634
  if (entry.isConsumed) {
545
- throw new Error(`AntpathClient.submitRun: skills[${i}] was already uploaded via skill.upload(client); ` +
546
- `use the returned Skill or Skill.fromId(record.id) for subsequent runs`);
635
+ throw new Error(`AntpathClient.submitRun: skills[${i}] was already consumed by a prior submitRun`);
547
636
  }
548
- if (entry.isUnstaged) {
549
- const bundle = entry._takeUnstagedBundle();
637
+ const ref = entry.ref;
638
+ if (ref.kind === "draft") {
639
+ const bundle = entry._takeDraftBundle();
550
640
  if (!bundle) {
551
- // Defence-in-depth: `isUnstaged` guarantees bytes exist.
552
- throw new Error(`AntpathClient.submitRun: skills[${i}] is unstaged but has no bytes (internal invariant violated)`);
641
+ throw new Error(`AntpathClient.submitRun: skills[${i}] is draft but has no bytes`);
553
642
  }
554
- const slot = `transient-${transientIndex++}`;
555
- skillRefs.push({
556
- kind: "transient",
557
- slot,
558
- name: bundle.name,
559
- contentHash: bundle.contentHash
560
- });
561
- // Anthropic filenames forbid `<>:"|?*\/` and control chars 0-31,
562
- // so the multipart filename uses hyphen separators. The 12-char
563
- // hash prefix is enough for janitor / orphan reconciliation;
564
- // the BFF stamps the full run-scoped filename once it knows the
565
- // run id.
566
- const shortHash = bundle.contentHash.replace(/^sha256:/, "").slice(0, 12);
567
- transientBundles.push({
568
- slot,
643
+ const uploaded = await uploadAssetToR2({
644
+ http,
569
645
  bytes: bundle.bytes,
570
- filename: `antpath-${slot}-${shortHash}.zip`
646
+ hash: bundle.contentHash
647
+ });
648
+ out.push({
649
+ kind: "r2",
650
+ path: uploaded.path,
651
+ hash: uploaded.hash,
652
+ sizeBytes: uploaded.sizeBytes,
653
+ name: bundle.name
571
654
  });
572
655
  continue;
573
656
  }
574
- skillRefs.push(entry.ref);
657
+ // Provider or r2 (re-used from a previous materialization).
658
+ out.push(ref);
575
659
  }
576
- return { skillRefs, transientBundles };
660
+ return out;
577
661
  }
578
- /**
579
- * Walk the user-provided `AgentsMd[]`, validating each instance and
580
- * producing:
581
- * - `agentsMdRefs[]` — the wire entries for `submission.agentsMd[]`,
582
- * with transient refs assigned positional slot ids
583
- * (`agentsmd-0`, `agentsmd-1`, …).
584
- * - `transientAgentsMdParts[]` — the content for each transient
585
- * AgentsMd, parallel-indexed by slot.
586
- */
587
- function prepareAgentsMd(agentsMds) {
588
- const agentsMdRefs = [];
589
- const transientAgentsMdParts = [];
590
- let transientIndex = 0;
662
+ /** Materialize draft AgentsMd[] to R2; pass-through any already-r2. */
663
+ async function materializeAgentsMd(http, agentsMds) {
664
+ const out = [];
591
665
  for (let i = 0; i < agentsMds.length; i++) {
592
666
  const entry = agentsMds[i];
593
667
  if (!(entry instanceof AgentsMd)) {
594
668
  throw new Error(`AntpathClient.submitRun: agentsMd[${i}] must be an AgentsMd instance`);
595
669
  }
596
670
  if (entry.isConsumed) {
597
- throw new Error(`AntpathClient.submitRun: agentsMd[${i}] was already uploaded via agentsMd.upload(client); ` +
598
- `use the returned AgentsMd or AgentsMd.fromId(record.id) for subsequent runs`);
671
+ throw new Error(`AntpathClient.submitRun: agentsMd[${i}] was already consumed by a prior submitRun`);
599
672
  }
600
- if (entry.isUnstaged) {
601
- const unstaged = entry._takeUnstagedContent();
602
- if (!unstaged) {
603
- throw new Error(`AntpathClient.submitRun: agentsMd[${i}] is unstaged but has no content (internal invariant violated)`);
673
+ const ref = entry.ref;
674
+ if (ref.kind === "draft") {
675
+ const bundle = entry._takeDraftBundle();
676
+ if (!bundle) {
677
+ throw new Error(`AntpathClient.submitRun: agentsMd[${i}] is draft but has no bytes`);
604
678
  }
605
- const slot = `agentsmd-${transientIndex++}`;
606
- agentsMdRefs.push({
607
- kind: "transient_agentsmd",
608
- slot,
609
- name: unstaged.name,
610
- contentHash: unstaged.contentHash
611
- });
612
- transientAgentsMdParts.push({
613
- slot,
614
- content: unstaged.content,
615
- filename: `${unstaged.name}.md`
679
+ const uploaded = await uploadAssetToR2({ http, bytes: bundle.bytes, hash: bundle.contentHash });
680
+ out.push({
681
+ kind: "r2",
682
+ path: uploaded.path,
683
+ hash: uploaded.hash,
684
+ sizeBytes: uploaded.sizeBytes,
685
+ name: bundle.name
616
686
  });
617
687
  continue;
618
688
  }
619
- agentsMdRefs.push(entry.ref);
689
+ out.push(ref);
620
690
  }
621
- return { agentsMdRefs, transientAgentsMdParts };
691
+ return out;
622
692
  }
623
- /**
624
- * Walk the user-provided `File[]`, validating each instance and producing:
625
- * - `fileRefs[]` — the wire entries for `submission.files[]`, with
626
- * transient refs assigned positional slot ids (`file-0`, `file-1`, …).
627
- * - `transientFileParts[]` — the bytes for each transient file, parallel-indexed.
628
- */
629
- function prepareFiles(files) {
630
- const fileRefs = [];
631
- const transientFileParts = [];
632
- let transientIndex = 0;
693
+ /** Materialize draft File[] to R2; pass-through any already-r2. */
694
+ async function materializeFiles(http, files) {
695
+ const out = [];
633
696
  for (let i = 0; i < files.length; i++) {
634
697
  const entry = files[i];
635
698
  if (!(entry instanceof File)) {
636
699
  throw new Error(`AntpathClient.submitRun: files[${i}] must be a File instance`);
637
700
  }
638
701
  if (entry.isConsumed) {
639
- throw new Error(`AntpathClient.submitRun: files[${i}] was already uploaded via file.upload(client); ` +
640
- `use the returned File or File.fromId(record.id) for subsequent runs`);
702
+ throw new Error(`AntpathClient.submitRun: files[${i}] was already consumed by a prior submitRun`);
641
703
  }
642
- if (entry.isUnstaged) {
643
- const bundle = entry._takeUnstagedBundle();
704
+ const ref = entry.ref;
705
+ if (ref.kind === "draft") {
706
+ const bundle = entry._takeDraftBundle();
644
707
  if (!bundle) {
645
- throw new Error(`AntpathClient.submitRun: files[${i}] is unstaged but has no bytes (internal invariant violated)`);
708
+ throw new Error(`AntpathClient.submitRun: files[${i}] is draft but has no bytes`);
646
709
  }
647
- const slot = `file-${transientIndex++}`;
648
- fileRefs.push({
649
- kind: "transient_file",
650
- slot,
651
- name: bundle.name,
652
- contentHash: bundle.contentHash,
653
- ...(bundle.mountPath ? { mountPath: bundle.mountPath } : {})
654
- });
655
- const shortHash = bundle.contentHash.replace(/^sha256:/, "").slice(0, 12);
656
- transientFileParts.push({
657
- slot,
658
- bytes: bundle.bytes,
659
- filename: `antpath-${slot}-${shortHash}.zip`
660
- });
710
+ const uploaded = await uploadAssetToR2({ http, bytes: bundle.bytes, hash: bundle.contentHash });
711
+ out.push(bundle.mountPath !== undefined
712
+ ? {
713
+ kind: "r2",
714
+ path: uploaded.path,
715
+ hash: uploaded.hash,
716
+ sizeBytes: uploaded.sizeBytes,
717
+ name: bundle.name,
718
+ mountPath: bundle.mountPath
719
+ }
720
+ : {
721
+ kind: "r2",
722
+ path: uploaded.path,
723
+ hash: uploaded.hash,
724
+ sizeBytes: uploaded.sizeBytes,
725
+ name: bundle.name
726
+ });
661
727
  continue;
662
728
  }
663
- fileRefs.push(entry.ref);
729
+ out.push(ref);
664
730
  }
665
- return { fileRefs, transientFileParts };
731
+ return out;
666
732
  }
667
733
  function mergeMcpServers(inputs, explicitSecrets) {
668
734
  const submissionMcpServers = [];