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
@@ -12,12 +12,6 @@ import { iterateSse } from "./sse.js";
12
12
  * `references/development-principles.md` (Agent-first surface design,
13
13
  * Concrete rule 3).
14
14
  */
15
- export async function submitRun(http, request) {
16
- return http.request("/api/runs", {
17
- method: "POST",
18
- body: JSON.stringify(request)
19
- });
20
- }
21
15
  export async function getRun(http, runId) {
22
16
  const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
23
17
  return hasRun(result) ? result.run : result;
@@ -27,7 +21,7 @@ export async function getRun(http, runId) {
27
21
  * parsed submission inputs, attempts, indexed events (with
28
22
  * pagination cursor for large runs), raw-event Storage manifest,
29
23
  * outputs, capture failures, proxy-call audit, pinned skills,
30
- * provider skills, transient skills.
24
+ * provider skills, inline skills.
31
25
  *
32
26
  * Backed by the same `GET /api/runs/:runId` endpoint that
33
27
  * `getRun` calls; this variant just narrows the return type to
@@ -222,22 +216,22 @@ export async function downloadRunArchive(http, runId) {
222
216
  return response;
223
217
  }
224
218
  // ===========================================================================
225
- // Flat (Skill / McpServer / Blueprint) operations
219
+ // Run submission operations (Skill / McpServer / Blueprint composition)
226
220
  // ===========================================================================
227
- export async function submitRunFlat(http, request) {
221
+ export async function submitRun(http, request) {
228
222
  return http.request("/api/runs", {
229
223
  method: "POST",
230
224
  body: JSON.stringify(request)
231
225
  });
232
226
  }
233
227
  /**
234
- * Multipart variant of `submitRunFlat` for runs that carry transient
228
+ * Multipart variant of `submitRun` for runs that carry transient
235
229
  * (per-run) skill bundles and/or transient AgentsMd content.
236
230
  *
237
231
  * The JSON submission travels as the `submission` part; each
238
- * `TransientSkillRef.slot` in `request.submission.skills` MUST be
232
+ * `InlineSkillRef.slot` in `request.submission.skills` MUST be
239
233
  * mirrored by exactly one `skill:<slot>` part with the bundle bytes.
240
- * Each `TransientAgentsMdRef.slot` in `request.submission.agentsMd`
234
+ * Each `InlineAgentsMdRef.slot` in `request.submission.agentsMd`
241
235
  * MUST be mirrored by exactly one `agentsmd:<slot>` part with the
242
236
  * markdown text.
243
237
  *
@@ -246,12 +240,12 @@ export async function submitRunFlat(http, request) {
246
240
  *
247
241
  * At least one of `bundles` or `agentsMdParts` must be non-empty.
248
242
  */
249
- export async function submitRunFlatMultipart(http, request, bundles, agentsMdParts, fileParts) {
243
+ export async function submitRunMultipart(http, request, bundles, agentsMdParts, fileParts) {
250
244
  const hasBundles = Array.isArray(bundles) && bundles.length > 0;
251
245
  const hasAgentsMd = Array.isArray(agentsMdParts) && agentsMdParts.length > 0;
252
246
  const hasFiles = Array.isArray(fileParts) && fileParts.length > 0;
253
247
  if (!hasBundles && !hasAgentsMd && !hasFiles) {
254
- throw new Error("submitRunFlatMultipart: bundles, agentsMdParts, or fileParts must be non-empty");
248
+ throw new Error("submitRunMultipart: bundles, agentsMdParts, or fileParts must be non-empty");
255
249
  }
256
250
  const form = new FormData();
257
251
  // Submission rides as a typed JSON Blob so the BFF reads
@@ -261,10 +255,10 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
261
255
  const seen = new Set();
262
256
  for (const bundle of bundles) {
263
257
  if (typeof bundle.slot !== "string" || !bundle.slot) {
264
- throw new Error("submitRunFlatMultipart: each bundle must have a non-empty slot id");
258
+ throw new Error("submitRunMultipart: each bundle must have a non-empty slot id");
265
259
  }
266
260
  if (seen.has(bundle.slot)) {
267
- throw new Error(`submitRunFlatMultipart: duplicate transient skill slot "${bundle.slot}"`);
261
+ throw new Error(`submitRunMultipart: duplicate inline skill slot "${bundle.slot}"`);
268
262
  }
269
263
  seen.add(bundle.slot);
270
264
  const blob = toBlob(bundle.bytes, "application/zip");
@@ -272,11 +266,11 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
272
266
  }
273
267
  for (const part of agentsMdParts ?? []) {
274
268
  if (typeof part.slot !== "string" || !part.slot) {
275
- throw new Error("submitRunFlatMultipart: each agentsMd part must have a non-empty slot id");
269
+ throw new Error("submitRunMultipart: each agentsMd part must have a non-empty slot id");
276
270
  }
277
271
  const partKey = `agentsmd:${part.slot}`;
278
272
  if (seen.has(partKey)) {
279
- throw new Error(`submitRunFlatMultipart: duplicate agentsMd slot "${part.slot}"`);
273
+ throw new Error(`submitRunMultipart: duplicate agentsMd slot "${part.slot}"`);
280
274
  }
281
275
  seen.add(partKey);
282
276
  const blob = new Blob([part.content], { type: "text/plain" });
@@ -284,11 +278,11 @@ export async function submitRunFlatMultipart(http, request, bundles, agentsMdPar
284
278
  }
285
279
  for (const part of fileParts ?? []) {
286
280
  if (typeof part.slot !== "string" || !part.slot) {
287
- throw new Error("submitRunFlatMultipart: each file part must have a non-empty slot id");
281
+ throw new Error("submitRunMultipart: each file part must have a non-empty slot id");
288
282
  }
289
283
  const partKey = `file:${part.slot}`;
290
284
  if (seen.has(partKey)) {
291
- throw new Error(`submitRunFlatMultipart: duplicate file slot "${part.slot}"`);
285
+ throw new Error(`submitRunMultipart: duplicate file slot "${part.slot}"`);
292
286
  }
293
287
  seen.add(partKey);
294
288
  const blob = toBlob(part.bytes, "application/zip");
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Per-run Antpath provider proxy URL + bearer minting types.
3
+ *
4
+ * The provider proxy is the second of Antpath's two
5
+ * runtime-independent proxies (the other is the MCP proxy in
6
+ * `./mcp-proxy-url.ts`). It sits between the self-hosted runner and
7
+ * the customer's provider API — today that's DeepSeek's
8
+ * Anthropic-compatible endpoint, but the route is namespaced so
9
+ * additional providers slot in without a wire-format change.
10
+ *
11
+ * The runner container sees only `ANTHROPIC_BASE_URL=<proxy-url>` and
12
+ * `ANTHROPIC_AUTH_TOKEN=<scoped-bearer>`; the real provider key + base
13
+ * URL stay in the run Vault bundle. See
14
+ * `references/deepseek-runtime-mcp-plan.md` (Provider proxy +
15
+ * Security requirements).
16
+ */
17
+ import type { RunProvider } from "./submission.js";
18
+ /**
19
+ * Platform-reserved key under which the per-run provider proxy bearer
20
+ * is folded into the Vault bundle. Distinct from
21
+ * `__antpath_proxy_token` (customer-declared HTTP proxy) and
22
+ * `__antpath_mcp_proxy_token` (MCP proxy) so the three capabilities
23
+ * are revoked independently. The shared `parseInlineSecrets` rejects
24
+ * any inbound submission whose `secrets` carries a key with the
25
+ * `__antpath_` prefix; only the BFF mutates this after the parser.
26
+ */
27
+ export declare const PROVIDER_PROXY_BEARER_BUNDLE_KEY = "__antpath_provider_proxy_token";
28
+ /**
29
+ * Output of the per-run provider proxy bearer mint. Same primitive
30
+ * (high-entropy random + peppered SHA-256) as the customer-proxy +
31
+ * MCP-proxy mints; persisted in dedicated columns / Vault keys so
32
+ * the three capabilities don't share a fate.
33
+ */
34
+ export interface MintedProviderProxyBearer {
35
+ readonly plaintext: string;
36
+ readonly hash: string;
37
+ readonly expiresAt: Date;
38
+ }
39
+ /**
40
+ * DeepSeek's documented Anthropic-compatibility endpoint. Used by the
41
+ * provider proxy as the default upstream when the customer does not
42
+ * supply `secrets.deepseek.baseUrl`. Subject to DeepSeek's own
43
+ * upstream changes — see `references/deepseek-runtime-mcp-plan.md`
44
+ * (Source facts to re-check).
45
+ */
46
+ export declare const DEEPSEEK_ANTHROPIC_COMPAT_DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic";
47
+ /**
48
+ * Canonical URL path of the per-run provider proxy endpoint. The BFF
49
+ * route lives at `/api/runs/[runId]/provider-proxy/[providerName]`;
50
+ * the runner / runtime receives the absolute URL from
51
+ * {@link buildProviderProxyUrl}.
52
+ */
53
+ export declare function providerProxyPath(runId: string, provider: RunProvider): string;
54
+ /**
55
+ * Build the absolute per-run, per-provider proxy URL. `baseUrl` is the
56
+ * configured Antpath API plane root (e.g. `https://api.antpath.ai`);
57
+ * the function strips any trailing slash and appends the canonical
58
+ * provider proxy path.
59
+ */
60
+ export declare function buildProviderProxyUrl(args: {
61
+ readonly baseUrl: string;
62
+ readonly runId: string;
63
+ readonly provider: RunProvider;
64
+ }): string;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Per-run Antpath provider proxy URL + bearer minting types.
3
+ *
4
+ * The provider proxy is the second of Antpath's two
5
+ * runtime-independent proxies (the other is the MCP proxy in
6
+ * `./mcp-proxy-url.ts`). It sits between the self-hosted runner and
7
+ * the customer's provider API — today that's DeepSeek's
8
+ * Anthropic-compatible endpoint, but the route is namespaced so
9
+ * additional providers slot in without a wire-format change.
10
+ *
11
+ * The runner container sees only `ANTHROPIC_BASE_URL=<proxy-url>` and
12
+ * `ANTHROPIC_AUTH_TOKEN=<scoped-bearer>`; the real provider key + base
13
+ * URL stay in the run Vault bundle. See
14
+ * `references/deepseek-runtime-mcp-plan.md` (Provider proxy +
15
+ * Security requirements).
16
+ */
17
+ /**
18
+ * Platform-reserved key under which the per-run provider proxy bearer
19
+ * is folded into the Vault bundle. Distinct from
20
+ * `__antpath_proxy_token` (customer-declared HTTP proxy) and
21
+ * `__antpath_mcp_proxy_token` (MCP proxy) so the three capabilities
22
+ * are revoked independently. The shared `parseInlineSecrets` rejects
23
+ * any inbound submission whose `secrets` carries a key with the
24
+ * `__antpath_` prefix; only the BFF mutates this after the parser.
25
+ */
26
+ export const PROVIDER_PROXY_BEARER_BUNDLE_KEY = "__antpath_provider_proxy_token";
27
+ /**
28
+ * DeepSeek's documented Anthropic-compatibility endpoint. Used by the
29
+ * provider proxy as the default upstream when the customer does not
30
+ * supply `secrets.deepseek.baseUrl`. Subject to DeepSeek's own
31
+ * upstream changes — see `references/deepseek-runtime-mcp-plan.md`
32
+ * (Source facts to re-check).
33
+ */
34
+ export const DEEPSEEK_ANTHROPIC_COMPAT_DEFAULT_BASE_URL = "https://api.deepseek.com/anthropic";
35
+ /**
36
+ * Canonical URL path of the per-run provider proxy endpoint. The BFF
37
+ * route lives at `/api/runs/[runId]/provider-proxy/[providerName]`;
38
+ * the runner / runtime receives the absolute URL from
39
+ * {@link buildProviderProxyUrl}.
40
+ */
41
+ export function providerProxyPath(runId, provider) {
42
+ if (!runId || typeof runId !== "string") {
43
+ throw new Error("providerProxyPath: runId must be a non-empty string");
44
+ }
45
+ if (provider !== "anthropic" && provider !== "deepseek") {
46
+ throw new Error(`providerProxyPath: unsupported provider ${JSON.stringify(provider)}`);
47
+ }
48
+ return `/api/runs/${encodeURIComponent(runId)}/provider-proxy/${provider}`;
49
+ }
50
+ /**
51
+ * Build the absolute per-run, per-provider proxy URL. `baseUrl` is the
52
+ * configured Antpath API plane root (e.g. `https://api.antpath.ai`);
53
+ * the function strips any trailing slash and appends the canonical
54
+ * provider proxy path.
55
+ */
56
+ export function buildProviderProxyUrl(args) {
57
+ if (!args.baseUrl || typeof args.baseUrl !== "string") {
58
+ throw new Error("buildProviderProxyUrl: baseUrl must be a non-empty string");
59
+ }
60
+ let parsedBase;
61
+ try {
62
+ parsedBase = new URL(args.baseUrl);
63
+ }
64
+ catch {
65
+ throw new Error(`buildProviderProxyUrl: baseUrl is not a valid URL: ${args.baseUrl}`);
66
+ }
67
+ if (parsedBase.protocol !== "https:" && parsedBase.protocol !== "http:") {
68
+ throw new Error(`buildProviderProxyUrl: baseUrl protocol must be http or https (got ${parsedBase.protocol})`);
69
+ }
70
+ const trimmedBase = `${parsedBase.origin}${parsedBase.pathname.replace(/\/+$/, "")}`;
71
+ return `${trimmedBase}${providerProxyPath(args.runId, args.provider)}`;
72
+ }
73
+ //# sourceMappingURL=provider-proxy-url.js.map
@@ -14,6 +14,6 @@ export declare function validateProxyAuth(endpoints: readonly PlatformProxyEndpo
14
14
  * caller is hand-rolling networking.
15
15
  */
16
16
  export declare function buildPlatformAllowedHosts(input: {
17
- readonly dashboardBaseUrl: string;
17
+ readonly baseUrl: string;
18
18
  readonly extraHosts?: readonly string[];
19
19
  }): readonly string[];
@@ -37,10 +37,10 @@ export function validateProxyAuth(endpoints, auth) {
37
37
  export function buildPlatformAllowedHosts(input) {
38
38
  const result = [];
39
39
  try {
40
- result.push(new URL(input.dashboardBaseUrl).host);
40
+ result.push(new URL(input.baseUrl).host);
41
41
  }
42
42
  catch {
43
- throw new Error("buildPlatformAllowedHosts: dashboardBaseUrl must be an absolute URL");
43
+ throw new Error("buildPlatformAllowedHosts: baseUrl must be an absolute URL");
44
44
  }
45
45
  for (const host of input.extraHosts ?? []) {
46
46
  if (!result.includes(host))
@@ -19,36 +19,16 @@
19
19
  * `rawEventPages` (manifest only; bytes downloaded out-of-band so the
20
20
  * detail response stays bounded). The archive zip carries the bytes.
21
21
  */
22
- import type { JsonValue, PlatformCleanupPolicy, PlatformFlatSubmission, PlatformProxyEndpoint, PlatformTemplateSubmission } from "./submission.js";
22
+ import type { JsonValue, PlatformCleanupPolicy, PlatformSubmission, PlatformProxyEndpoint } from "./submission.js";
23
23
  /**
24
- * Parsed view of `runs.template_snapshot` jsonb. Two historical shapes
25
- * coexist in storage:
26
- *
27
- * - `kind: "flat"` (current): {kind:"flat", submission, cleanup?}
28
- * written by `insertRunWithSkillSnapshots` for all new runs.
29
- * - legacy template-shaped: {template, executionPayloadSecretId,
30
- * variables?, cleanup?} written by `insertRun` before the flat
31
- * pivot.
32
- *
33
- * The parser tolerates both and returns a discriminated union so
34
- * consumers branch mechanically. New work targets `kind: "flat"`.
35
- *
36
- * Note on legacy redaction: historic template-shaped snapshots have
37
- * `system` and `messages` replaced with `"[redacted]"` by
38
- * `redactTemplateForMetadata`. That redaction predates the raw-event /
39
- * raw-input policy update and we do not retroactively un-redact data we
40
- * never stored. Flat-shape snapshots are verbatim.
24
+ * Parsed view of `runs.template_snapshot` jsonb. Stored shape is
25
+ * `{kind:"submission", submission, cleanup?}` written by
26
+ * `insertRunWithSkillSnapshots` for all runs.
41
27
  */
42
- export type RunUnitSubmission = RunUnitFlatSubmission | RunUnitTemplateSubmission;
28
+ export type RunUnitSubmission = RunUnitFlatSubmission;
43
29
  export interface RunUnitFlatSubmission {
44
- readonly kind: "flat";
45
- readonly submission: PlatformFlatSubmission;
46
- readonly cleanup?: PlatformCleanupPolicy;
47
- }
48
- export interface RunUnitTemplateSubmission {
49
- readonly kind: "template";
50
- readonly template: PlatformTemplateSubmission;
51
- readonly variables?: Record<string, JsonValue>;
30
+ readonly kind: "submission";
31
+ readonly submission: PlatformSubmission;
52
32
  readonly cleanup?: PlatformCleanupPolicy;
53
33
  }
54
34
  export interface RunUnitAttempt {
@@ -157,7 +137,7 @@ export interface RunUnitProviderSkill {
157
137
  readonly skillId: string;
158
138
  readonly version?: string;
159
139
  }
160
- export interface RunUnitTransientSkill {
140
+ export interface RunUnitInlineSkill {
161
141
  readonly id: string;
162
142
  readonly slotId: string;
163
143
  readonly skillName: string;
@@ -190,17 +170,24 @@ export interface RunUnit {
190
170
  readonly proxyCalls: RunUnitProxyCallPage;
191
171
  readonly skillSnapshots: readonly RunUnitSkillSnapshot[];
192
172
  readonly providerSkills: readonly RunUnitProviderSkill[];
193
- readonly transientSkills: readonly RunUnitTransientSkill[];
173
+ readonly inlineSkills: readonly RunUnitInlineSkill[];
174
+ /**
175
+ * Per-run, per-provider runtime manifest — derived from the validated
176
+ * submission + the chosen provider (`buildRuntimeManifest`). Tells
177
+ * SDK consumers where antpath placed things in-container and what
178
+ * env vars the agent will see. Undefined on responses from BFFs
179
+ * that predate Phase 2 of the runtime-environment rollout.
180
+ */
181
+ readonly runtimeManifest?: import("./runtime-manifest.js").RuntimeManifest;
194
182
  }
195
183
  /**
196
- * Parse a `runs.template_snapshot` jsonb payload into the typed
197
- * discriminated union. Tolerates both flat and legacy template shapes;
198
- * never throws on minor unknown keys so we can forward-compat with
199
- * worker-side enrichment.
184
+ * Parse a `runs.template_snapshot` jsonb payload into the typed flat
185
+ * submission. Never throws on minor unknown keys so we can
186
+ * forward-compat with worker-side enrichment.
200
187
  *
201
188
  * Returns a typed shape even for malformed snapshots — the worst case
202
- * is `{kind: "flat", submission: {model: "", ...}}` with empty defaults
203
- * — because the dashboard must still render *something* for a buggy
204
- * historical row rather than 500ing the whole detail page.
189
+ * is `{kind: "submission", submission: {model: "", ...}}` with empty
190
+ * defaults — because the dashboard must still render *something* for a
191
+ * buggy historical row rather than 500ing the whole detail page.
205
192
  */
206
193
  export declare function parseRunUnitSubmission(input: unknown): RunUnitSubmission;
@@ -24,29 +24,25 @@ import { parseMcpServerRef, parseSkillRef } from "./blueprint.js";
24
24
  // Submission parser
25
25
  // ---------------------------------------------------------------------------
26
26
  /**
27
- * Parse a `runs.template_snapshot` jsonb payload into the typed
28
- * discriminated union. Tolerates both flat and legacy template shapes;
29
- * never throws on minor unknown keys so we can forward-compat with
30
- * worker-side enrichment.
27
+ * Parse a `runs.template_snapshot` jsonb payload into the typed flat
28
+ * submission. Never throws on minor unknown keys so we can
29
+ * forward-compat with worker-side enrichment.
31
30
  *
32
31
  * Returns a typed shape even for malformed snapshots — the worst case
33
- * is `{kind: "flat", submission: {model: "", ...}}` with empty defaults
34
- * — because the dashboard must still render *something* for a buggy
35
- * historical row rather than 500ing the whole detail page.
32
+ * is `{kind: "submission", submission: {model: "", ...}}` with empty
33
+ * defaults — because the dashboard must still render *something* for a
34
+ * buggy historical row rather than 500ing the whole detail page.
36
35
  */
37
36
  export function parseRunUnitSubmission(input) {
38
37
  if (!input || typeof input !== "object" || Array.isArray(input)) {
39
38
  return fallbackFlat();
40
39
  }
41
40
  const value = input;
42
- if (value.kind === "flat") {
41
+ if (value.kind === "submission") {
43
42
  return parseFlatProjection(value);
44
43
  }
45
- if (isRecord(value.template)) {
46
- return parseTemplateProjection(value);
47
- }
48
- // Snapshot exists but matches neither shape — surface as an empty
49
- // flat submission so consumers can still render lifecycle bits.
44
+ // Snapshot exists but does not match the flat shape — surface as an
45
+ // empty flat submission so consumers can still render lifecycle bits.
50
46
  return fallbackFlat();
51
47
  }
52
48
  function parseFlatProjection(value) {
@@ -69,36 +65,14 @@ function parseFlatProjection(value) {
69
65
  : {})
70
66
  };
71
67
  return {
72
- kind: "flat",
68
+ kind: "submission",
73
69
  submission,
74
70
  ...(cleanup ? { cleanup } : {})
75
71
  };
76
72
  }
77
- function parseTemplateProjection(value) {
78
- const templateRaw = value.template;
79
- const template = {
80
- name: typeof templateRaw.name === "string" ? templateRaw.name : "",
81
- model: typeof templateRaw.model === "string" ? templateRaw.model : "",
82
- templateHash: typeof templateRaw.templateHash === "string" ? templateRaw.templateHash : "",
83
- messages: toStringArray(templateRaw.messages),
84
- ...(typeof templateRaw.system === "string" ? { system: templateRaw.system } : {}),
85
- ...(isJsonRecord(templateRaw.metadata) ? { metadata: templateRaw.metadata } : {}),
86
- ...(parseEnvironment(templateRaw.environment)
87
- ? { environment: parseEnvironment(templateRaw.environment) }
88
- : {})
89
- };
90
- const variables = isJsonRecord(value.variables) ? value.variables : undefined;
91
- const cleanup = parseCleanup(value.cleanup);
92
- return {
93
- kind: "template",
94
- template,
95
- ...(variables ? { variables } : {}),
96
- ...(cleanup ? { cleanup } : {})
97
- };
98
- }
99
73
  function fallbackFlat() {
100
74
  return {
101
- kind: "flat",
75
+ kind: "submission",
102
76
  submission: {
103
77
  model: "",
104
78
  prompt: [],
@@ -140,7 +114,7 @@ function toSkillRefArray(value) {
140
114
  const out = [];
141
115
  for (let i = 0; i < value.length; i++) {
142
116
  try {
143
- out.push(parseSkillRef(value[i], `submission.skills[${i}]`, { allowTransient: true }));
117
+ out.push(parseSkillRef(value[i], `submission.skills[${i}]`));
144
118
  }
145
119
  catch {
146
120
  // Skip malformed entries rather than failing the whole detail
@@ -195,7 +169,22 @@ function parseEnvironment(value) {
195
169
  env.packages = pkgs;
196
170
  }
197
171
  }
198
- return env.networking || env.packages
172
+ // Lenient pass-through for envVars stored in already-validated
173
+ // snapshots. The strict parser in submission.ts enforces shape /
174
+ // size / reserved-prefix rules at submission time; here we just
175
+ // accept whatever shape was persisted.
176
+ if (isRecord(value.envVars)) {
177
+ const out = {};
178
+ for (const [k, v] of Object.entries(value.envVars)) {
179
+ if (typeof v === "string") {
180
+ out[k] = v;
181
+ }
182
+ }
183
+ if (Object.keys(out).length > 0) {
184
+ env.envVars = out;
185
+ }
186
+ }
187
+ return env.networking || env.packages || env.envVars
199
188
  ? env
200
189
  : undefined;
201
190
  }
@@ -204,14 +193,9 @@ function parseCleanup(value) {
204
193
  return undefined;
205
194
  }
206
195
  const session = value.session;
207
- const claudeSession = value.claudeSession;
208
- const out = {};
209
196
  if (session === "retain" || session === "delete") {
210
- out.session = session;
211
- }
212
- if (claudeSession === "retain" || claudeSession === "delete") {
213
- out.claudeSession = claudeSession;
197
+ return { session };
214
198
  }
215
- return out.session || out.claudeSession ? out : undefined;
199
+ return undefined;
216
200
  }
217
201
  //# sourceMappingURL=run-unit.js.map
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Unified runner event schema. Both runtimes feed the same shape into
3
+ * the api.antpath.ai event pipeline:
4
+ *
5
+ * - **Anthropic Native** — the `AnthropicSseInbox` Durable Object
6
+ * consumes Anthropic's `/v1/messages/stream` SSE stream and uses
7
+ * the Anthropic adapter to translate each event into one or more
8
+ * `RunnerEvent`s.
9
+ * - **Goose Managed** — the per-run runner image POSTs batches of
10
+ * NDJSON events to `/runs/{id}/runner/events`; the Goose adapter
11
+ * translates each event into one or more `RunnerEvent`s.
12
+ *
13
+ * The downstream subscribers (dashboard, SDK `streamEvents`, observable
14
+ * spans) never see the runtime-specific wire shapes — they only see
15
+ * `RunnerEvent`s. This is the cutover boundary that makes the dual
16
+ * runtime invisible to customers.
17
+ *
18
+ * See `references/platform-rebuild-2026.md` (Wire contracts →
19
+ * runner-event).
20
+ */
21
+ import type { JsonValue } from "./submission.js";
22
+ /**
23
+ * Schema version. Bump when the shape of `RunnerEvent`, its kind set,
24
+ * or the batch envelope changes. Subscribers gate on this so a future
25
+ * v2 wire shape can land behind a feature flag.
26
+ */
27
+ export declare const RUNNER_EVENT_VERSION: 1;
28
+ /**
29
+ * The set of event kinds emitted into the unified stream. Adapters
30
+ * fold runtime-specific events into one of these — anything that
31
+ * doesn't fit is mapped to `notification` so the data is captured
32
+ * even when no UI handler exists yet.
33
+ *
34
+ * - `runtime_started` — either runtime announced "ready" (Fly
35
+ * machine running goose; Anthropic session
36
+ * accepted the first turn).
37
+ * - `assistant_text` — model text delta.
38
+ * - `tool_request` — model emitted a tool_use / function call.
39
+ * - `tool_response` — tool result delivered back to the model.
40
+ * - `skill_loaded` — a skill was loaded (Anthropic Skills API
41
+ * ref OR a workspace folder mount).
42
+ * - `file_uploaded` — a file became available to the agent
43
+ * (Files API id OR workspace path).
44
+ * - `notification` — runtime/extension notification; catch-all
45
+ * for diagnostic data.
46
+ * - `stream_error` — stream-level error (non-fatal). Subscribers
47
+ * may surface this as a UI warning; the run
48
+ * continues unless `runtime_terminal` follows.
49
+ * - `runtime_terminal` — the run reached a terminal state. The
50
+ * adapter MUST emit exactly one of these per
51
+ * run; subscribers gate on it for end-of-stream.
52
+ */
53
+ export declare const RUNNER_EVENT_KINDS: readonly ["runtime_started", "assistant_text", "tool_request", "tool_response", "skill_loaded", "file_uploaded", "notification", "stream_error", "runtime_terminal"];
54
+ export type RunnerEventKind = (typeof RUNNER_EVENT_KINDS)[number];
55
+ /**
56
+ * One event in the unified stream. `seq` is monotonically increasing
57
+ * within a single run (the adapter is responsible for assigning seqs
58
+ * — no two events with the same `seq` for the same `runId`); `tMs` is
59
+ * a millisecond-resolution timestamp that is also monotonically
60
+ * non-decreasing within a single run (an event's `tMs` is never less
61
+ * than the previous event's `tMs`). `data` carries the runtime- and
62
+ * kind-specific payload, JSON-typed so the batch round-trips through
63
+ * the database column without ad-hoc serialization.
64
+ */
65
+ export interface RunnerEvent {
66
+ readonly seq: number;
67
+ readonly tMs: number;
68
+ readonly kind: RunnerEventKind;
69
+ readonly data: Readonly<Record<string, JsonValue>>;
70
+ }
71
+ /**
72
+ * Batch envelope. The runner ships one or more events per POST so the
73
+ * inbox writes them atomically. `events` MUST be sorted by `seq`
74
+ * ascending; api.antpath.ai rejects malformed batches before touching
75
+ * Postgres.
76
+ */
77
+ export interface RunnerEventBatch {
78
+ readonly v: typeof RUNNER_EVENT_VERSION;
79
+ readonly runId: string;
80
+ readonly events: readonly RunnerEvent[];
81
+ }
82
+ /**
83
+ * Maximum number of events per batch. Bounds the size of the body
84
+ * api.antpath.ai accepts and the size of the Durable Object write.
85
+ * Larger streams are split into multiple batches; the runner is
86
+ * responsible for chunking.
87
+ */
88
+ export declare const RUNNER_EVENT_BATCH_MAX_EVENTS: 256;
89
+ /**
90
+ * Validation outcome for an inbound batch. The `ok` branch returns
91
+ * the parsed batch with frozen events; the `error` branch returns the
92
+ * code + a short human message suitable for an HTTP 400 body. Codes
93
+ * are stable strings — the dashboard / SDK may branch on them.
94
+ */
95
+ export type RunnerEventBatchValidation = {
96
+ readonly ok: true;
97
+ readonly batch: RunnerEventBatch;
98
+ } | {
99
+ readonly ok: false;
100
+ readonly code: RunnerEventBatchValidationCode;
101
+ readonly message: string;
102
+ };
103
+ export declare const RUNNER_EVENT_BATCH_VALIDATION_CODES: readonly ["invalid_envelope", "version_mismatch", "missing_run_id", "empty_batch", "batch_too_large", "invalid_event", "seq_not_monotonic", "t_ms_not_monotonic"];
104
+ export type RunnerEventBatchValidationCode = (typeof RUNNER_EVENT_BATCH_VALIDATION_CODES)[number];
105
+ /**
106
+ * Parse + validate an inbound runner event batch (untrusted input).
107
+ * Used at the api.antpath.ai ingress so adapters never have to
108
+ * re-check the wire shape, and used by tests to assert the contract.
109
+ *
110
+ * Successful validation guarantees:
111
+ * - top-level envelope matches {@link RunnerEventBatch}
112
+ * - `v === RUNNER_EVENT_VERSION`
113
+ * - `runId` is a non-empty string
114
+ * - `events` is a non-empty array of at most
115
+ * {@link RUNNER_EVENT_BATCH_MAX_EVENTS} entries
116
+ * - each event is a {@link RunnerEvent} with a known `kind`
117
+ * - `seq` is strictly increasing across the batch
118
+ * - `tMs` is non-decreasing across the batch
119
+ */
120
+ export declare function validateRunnerEventBatch(input: unknown): RunnerEventBatchValidation;