antpath 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_shared/index.d.ts +1 -0
- package/dist/_shared/index.js +1 -0
- package/dist/_shared/operations.d.ts +47 -0
- package/dist/_shared/operations.js +58 -0
- package/dist/_shared/proxy-protocol.d.ts +10 -2
- package/dist/_shared/proxy-protocol.js +3 -2
- package/dist/_shared/run-unit.d.ts +206 -0
- package/dist/_shared/run-unit.js +213 -0
- package/dist/_shared/runtime-types.d.ts +19 -0
- package/dist/_shared/submission.d.ts +21 -0
- package/dist/_shared/submission.js +93 -3
- package/dist/cli.mjs +120 -35
- package/dist/cli.mjs.sha256 +1 -1
- package/dist/client.d.ts +82 -1
- package/dist/client.js +83 -1
- package/dist/client.js.map +1 -1
- package/dist/proxy-endpoint.d.ts +26 -7
- package/dist/proxy-endpoint.js +19 -3
- package/dist/proxy-endpoint.js.map +1 -1
- package/dist/skill.d.ts +41 -0
- package/dist/skill.js +52 -0
- package/dist/skill.js.map +1 -1
- package/docs/credentials.md +34 -0
- package/docs/events.md +7 -0
- package/docs/mcp.md +28 -0
- package/docs/outputs.md +92 -12
- package/docs/quickstart.md +37 -0
- package/docs/skills.md +49 -0
- package/package.json +1 -1
package/dist/_shared/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./proxy-protocol.js";
|
|
|
6
6
|
export * from "./secrets.js";
|
|
7
7
|
export * from "./status.js";
|
|
8
8
|
export * from "./submission.js";
|
|
9
|
+
export * from "./run-unit.js";
|
|
9
10
|
export * from "./stable.js";
|
|
10
11
|
export * from "./sdk-secrets.js";
|
|
11
12
|
export * from "./sdk-errors.js";
|
package/dist/_shared/index.js
CHANGED
|
@@ -6,6 +6,7 @@ export * from "./proxy-protocol.js";
|
|
|
6
6
|
export * from "./secrets.js";
|
|
7
7
|
export * from "./status.js";
|
|
8
8
|
export * from "./submission.js";
|
|
9
|
+
export * from "./run-unit.js";
|
|
9
10
|
// SDK + CLI shared surface — moved here so the published `antpath` SDK
|
|
10
11
|
// and the in-container `antpath` CLI consume the SAME implementation.
|
|
11
12
|
export * from "./stable.js";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { HttpClient } from "./http.js";
|
|
2
|
+
import type { RunUnit } from "./run-unit.js";
|
|
2
3
|
import type { Output, Run, RunEvent, SignedOutputLink, Skill, WhoAmI } from "./runtime-types.js";
|
|
3
4
|
import type { PlatformFlatRunSubmissionInput, PlatformRunSubmissionInput } from "./submission.js";
|
|
4
5
|
/**
|
|
@@ -16,12 +17,38 @@ import type { PlatformFlatRunSubmissionInput, PlatformRunSubmissionInput } from
|
|
|
16
17
|
*/
|
|
17
18
|
export declare function submitRun(http: HttpClient, request: PlatformRunSubmissionInput): Promise<Run>;
|
|
18
19
|
export declare function getRun(http: HttpClient, runId: string): Promise<Run>;
|
|
20
|
+
/**
|
|
21
|
+
* Strongly-typed accessor for the full self-contained run unit:
|
|
22
|
+
* parsed submission inputs, attempts, indexed events (with
|
|
23
|
+
* pagination cursor for large runs), raw-event Storage manifest,
|
|
24
|
+
* outputs, capture failures, proxy-call audit, pinned skills,
|
|
25
|
+
* provider skills, transient skills.
|
|
26
|
+
*
|
|
27
|
+
* Backed by the same `GET /api/runs/:runId` endpoint that
|
|
28
|
+
* `getRun` calls; this variant just narrows the return type to
|
|
29
|
+
* the documented wire shape. Prefer this for new code; `getRun`
|
|
30
|
+
* stays for callers that only need the loose record.
|
|
31
|
+
*/
|
|
32
|
+
export declare function getRunUnit(http: HttpClient, runId: string): Promise<RunUnit>;
|
|
19
33
|
export declare function listRunEvents(http: HttpClient, runId: string): Promise<readonly RunEvent[]>;
|
|
20
34
|
export declare function listOutputs(http: HttpClient, runId: string): Promise<readonly Output[]>;
|
|
21
35
|
export declare function createOutputLink(http: HttpClient, runId: string, outputId: string): Promise<SignedOutputLink>;
|
|
22
36
|
export declare function cancelRun(http: HttpClient, runId: string): Promise<void>;
|
|
23
37
|
export declare function deleteRun(http: HttpClient, runId: string): Promise<void>;
|
|
24
38
|
export declare function whoami(http: HttpClient): Promise<WhoAmI>;
|
|
39
|
+
/**
|
|
40
|
+
* Stream the per-run archive zip from the BFF. Returns the raw
|
|
41
|
+
* `Response` so callers can pipe the body to disk without buffering
|
|
42
|
+
* the whole archive in memory.
|
|
43
|
+
*
|
|
44
|
+
* The archive lifecycle contract lives in
|
|
45
|
+
* `apps/dashboard/src/server/run-archive.ts` and
|
|
46
|
+
* `packages/sdk/docs/outputs.md`. Pre-session runs reject with HTTP
|
|
47
|
+
* 409 `run_not_started`; mid-session and terminal both produce the
|
|
48
|
+
* same archive layout and differ only in `manifest.json`'s `source`
|
|
49
|
+
* + `partial` fields.
|
|
50
|
+
*/
|
|
51
|
+
export declare function downloadRunArchive(http: HttpClient, runId: string): Promise<Response>;
|
|
25
52
|
export declare function submitRunFlat(http: HttpClient, request: PlatformFlatRunSubmissionInput): Promise<Run>;
|
|
26
53
|
/**
|
|
27
54
|
* Multipart variant of `submitRunFlat` for runs that carry transient
|
|
@@ -59,3 +86,23 @@ export declare function createSkillBundle(http: HttpClient, args: {
|
|
|
59
86
|
export declare function listSkills(http: HttpClient): Promise<readonly Skill[]>;
|
|
60
87
|
export declare function getSkill(http: HttpClient, skillId: string): Promise<Skill>;
|
|
61
88
|
export declare function deleteSkill(http: HttpClient, skillId: string): Promise<void>;
|
|
89
|
+
/**
|
|
90
|
+
* Lookup a live workspace skill by `(name, contentHash)`. Returns the
|
|
91
|
+
* matching `Skill` record or null when no live row carries that hash.
|
|
92
|
+
*
|
|
93
|
+
* `contentHash` is the wire format `sha256:<hex>` as returned by
|
|
94
|
+
* `hashSkillBundle`. This powers `Skill.uploadIfChanged` — the SDK
|
|
95
|
+
* computes the hash locally and calls this function to skip the upload
|
|
96
|
+
* when the bytes already exist.
|
|
97
|
+
*/
|
|
98
|
+
export declare function findSkillByHash(http: HttpClient, args: {
|
|
99
|
+
readonly name: string;
|
|
100
|
+
readonly contentHash: string;
|
|
101
|
+
}): Promise<Skill | null>;
|
|
102
|
+
/**
|
|
103
|
+
* Lookup a live workspace skill by `name`. Returns the matching `Skill`
|
|
104
|
+
* record or null when no live row carries that name. Implemented as a
|
|
105
|
+
* list-and-filter on the existing `/api/skills` endpoint — the
|
|
106
|
+
* indexed by-hash route is reserved for `uploadIfChanged`.
|
|
107
|
+
*/
|
|
108
|
+
export declare function findSkillByName(http: HttpClient, name: string): Promise<Skill | null>;
|
|
@@ -21,6 +21,21 @@ export async function getRun(http, runId) {
|
|
|
21
21
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
22
22
|
return hasRun(result) ? result.run : result;
|
|
23
23
|
}
|
|
24
|
+
/**
|
|
25
|
+
* Strongly-typed accessor for the full self-contained run unit:
|
|
26
|
+
* parsed submission inputs, attempts, indexed events (with
|
|
27
|
+
* pagination cursor for large runs), raw-event Storage manifest,
|
|
28
|
+
* outputs, capture failures, proxy-call audit, pinned skills,
|
|
29
|
+
* provider skills, transient skills.
|
|
30
|
+
*
|
|
31
|
+
* Backed by the same `GET /api/runs/:runId` endpoint that
|
|
32
|
+
* `getRun` calls; this variant just narrows the return type to
|
|
33
|
+
* the documented wire shape. Prefer this for new code; `getRun`
|
|
34
|
+
* stays for callers that only need the loose record.
|
|
35
|
+
*/
|
|
36
|
+
export async function getRunUnit(http, runId) {
|
|
37
|
+
return http.request(`/api/runs/${encodeURIComponent(runId)}`);
|
|
38
|
+
}
|
|
24
39
|
export async function listRunEvents(http, runId) {
|
|
25
40
|
const result = await http.request(`/api/runs/${encodeURIComponent(runId)}/events`);
|
|
26
41
|
return result.events;
|
|
@@ -41,6 +56,22 @@ export async function deleteRun(http, runId) {
|
|
|
41
56
|
export async function whoami(http) {
|
|
42
57
|
return http.request("/api/whoami");
|
|
43
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Stream the per-run archive zip from the BFF. Returns the raw
|
|
61
|
+
* `Response` so callers can pipe the body to disk without buffering
|
|
62
|
+
* the whole archive in memory.
|
|
63
|
+
*
|
|
64
|
+
* The archive lifecycle contract lives in
|
|
65
|
+
* `apps/dashboard/src/server/run-archive.ts` and
|
|
66
|
+
* `packages/sdk/docs/outputs.md`. Pre-session runs reject with HTTP
|
|
67
|
+
* 409 `run_not_started`; mid-session and terminal both produce the
|
|
68
|
+
* same archive layout and differ only in `manifest.json`'s `source`
|
|
69
|
+
* + `partial` fields.
|
|
70
|
+
*/
|
|
71
|
+
export async function downloadRunArchive(http, runId) {
|
|
72
|
+
const { response } = await http.download(`/api/runs/${encodeURIComponent(runId)}/download`);
|
|
73
|
+
return response;
|
|
74
|
+
}
|
|
44
75
|
// ===========================================================================
|
|
45
76
|
// Flat (Skill / McpServer / Blueprint) operations
|
|
46
77
|
// ===========================================================================
|
|
@@ -125,6 +156,33 @@ export async function deleteSkill(http, skillId) {
|
|
|
125
156
|
method: "DELETE"
|
|
126
157
|
});
|
|
127
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Lookup a live workspace skill by `(name, contentHash)`. Returns the
|
|
161
|
+
* matching `Skill` record or null when no live row carries that hash.
|
|
162
|
+
*
|
|
163
|
+
* `contentHash` is the wire format `sha256:<hex>` as returned by
|
|
164
|
+
* `hashSkillBundle`. This powers `Skill.uploadIfChanged` — the SDK
|
|
165
|
+
* computes the hash locally and calls this function to skip the upload
|
|
166
|
+
* when the bytes already exist.
|
|
167
|
+
*/
|
|
168
|
+
export async function findSkillByHash(http, args) {
|
|
169
|
+
const params = new URLSearchParams({
|
|
170
|
+
name: args.name,
|
|
171
|
+
content_hash: args.contentHash
|
|
172
|
+
});
|
|
173
|
+
const result = await http.request(`/api/skills/by-hash?${params.toString()}`);
|
|
174
|
+
return result.skill ?? null;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Lookup a live workspace skill by `name`. Returns the matching `Skill`
|
|
178
|
+
* record or null when no live row carries that name. Implemented as a
|
|
179
|
+
* list-and-filter on the existing `/api/skills` endpoint — the
|
|
180
|
+
* indexed by-hash route is reserved for `uploadIfChanged`.
|
|
181
|
+
*/
|
|
182
|
+
export async function findSkillByName(http, name) {
|
|
183
|
+
const skills = await listSkills(http);
|
|
184
|
+
return skills.find((skill) => skill.name === name) ?? null;
|
|
185
|
+
}
|
|
128
186
|
function unwrapSkill(result) {
|
|
129
187
|
if (result && typeof result === "object" && "skill" in result) {
|
|
130
188
|
return result.skill;
|
|
@@ -67,8 +67,16 @@ export interface ProxyIndexEntry {
|
|
|
67
67
|
* The actual auth value lives in the run's Vault bundle under
|
|
68
68
|
* `secrets.proxyEndpointAuth[i].value` and is never reflected back
|
|
69
69
|
* into the container or index file.
|
|
70
|
+
*
|
|
71
|
+
* The `none` variant declares an upstream that takes no auth (public
|
|
72
|
+
* APIs like Wikimedia Commons or NASA Images). It still routes through
|
|
73
|
+
* the proxy for unified egress, audit, and budget enforcement, but
|
|
74
|
+
* carries no `proxyEndpointAuth[]` entry and the BFF injects no
|
|
75
|
+
* header or query value.
|
|
70
76
|
*/
|
|
71
77
|
export type ProxyAuthShape = {
|
|
78
|
+
readonly type: "none";
|
|
79
|
+
} | {
|
|
72
80
|
readonly type: "bearer";
|
|
73
81
|
} | {
|
|
74
82
|
readonly type: "basic";
|
|
@@ -82,7 +90,7 @@ export type ProxyAuthShape = {
|
|
|
82
90
|
export type ProxyAuthType = ProxyAuthShape["type"];
|
|
83
91
|
/**
|
|
84
92
|
* Header name (lowercase) that an upstream auth shape uses as its
|
|
85
|
-
* carrier. Returns `undefined` for query-based auth.
|
|
93
|
+
* carrier. Returns `undefined` for query-based and keyless auth.
|
|
86
94
|
*
|
|
87
95
|
* Used by the submission parser to forbid `allowHeaders` from listing
|
|
88
96
|
* the auth header (avoids leaks via caller-supplied headers), and by
|
|
@@ -92,7 +100,7 @@ export type ProxyAuthType = ProxyAuthShape["type"];
|
|
|
92
100
|
export declare function authShapeHeaderName(shape: ProxyAuthShape): string | undefined;
|
|
93
101
|
/**
|
|
94
102
|
* Query-string key that an upstream query-based auth shape uses as its
|
|
95
|
-
* carrier. Returns `undefined` for non-query shapes.
|
|
103
|
+
* carrier. Returns `undefined` for non-query shapes (including "none").
|
|
96
104
|
*/
|
|
97
105
|
export declare function authShapeQueryName(shape: ProxyAuthShape): string | undefined;
|
|
98
106
|
/**
|
|
@@ -66,7 +66,7 @@ export const PROXY_ERROR_CODES = [
|
|
|
66
66
|
];
|
|
67
67
|
/**
|
|
68
68
|
* Header name (lowercase) that an upstream auth shape uses as its
|
|
69
|
-
* carrier. Returns `undefined` for query-based auth.
|
|
69
|
+
* carrier. Returns `undefined` for query-based and keyless auth.
|
|
70
70
|
*
|
|
71
71
|
* Used by the submission parser to forbid `allowHeaders` from listing
|
|
72
72
|
* the auth header (avoids leaks via caller-supplied headers), and by
|
|
@@ -81,12 +81,13 @@ export function authShapeHeaderName(shape) {
|
|
|
81
81
|
case "header":
|
|
82
82
|
return shape.name.toLowerCase();
|
|
83
83
|
case "query":
|
|
84
|
+
case "none":
|
|
84
85
|
return undefined;
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
/**
|
|
88
89
|
* Query-string key that an upstream query-based auth shape uses as its
|
|
89
|
-
* carrier. Returns `undefined` for non-query shapes.
|
|
90
|
+
* carrier. Returns `undefined` for non-query shapes (including "none").
|
|
90
91
|
*/
|
|
91
92
|
export function authShapeQueryName(shape) {
|
|
92
93
|
return shape.type === "query" ? shape.name : undefined;
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunUnit — the self-contained read shape of a run.
|
|
3
|
+
*
|
|
4
|
+
* One canonical struct that captures every non-secret artifact persisted
|
|
5
|
+
* for a single run: parsed submission inputs, status/lifecycle, attempts,
|
|
6
|
+
* indexed events, raw-event Storage manifest, outputs (+ capture
|
|
7
|
+
* failures), proxy-call audit log, pinned workspace skills, provider
|
|
8
|
+
* built-in skills, and transient (Anthropic Files) skill records.
|
|
9
|
+
*
|
|
10
|
+
* Wire contract for `GET /api/runs/:runId`, the per-run archive's
|
|
11
|
+
* `run.json`/`submission.json`/`caps.json`, and the SDK/CLI
|
|
12
|
+
* `client.runs.get(runId)` return type.
|
|
13
|
+
*
|
|
14
|
+
* Immutability: every field here is read-only. Edit endpoints do not
|
|
15
|
+
* exist by design — see `references/architecture-decisions.md` (Runs).
|
|
16
|
+
*
|
|
17
|
+
* Raw event payloads are not embedded in this struct. They live in
|
|
18
|
+
* private Supabase Storage as gzipped JSONL pages and are listed via
|
|
19
|
+
* `rawEventPages` (manifest only; bytes downloaded out-of-band so the
|
|
20
|
+
* detail response stays bounded). The archive zip carries the bytes.
|
|
21
|
+
*/
|
|
22
|
+
import type { JsonValue, PlatformCleanupPolicy, PlatformFlatSubmission, PlatformProxyEndpoint, PlatformTemplateSubmission } from "./submission.js";
|
|
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.
|
|
41
|
+
*/
|
|
42
|
+
export type RunUnitSubmission = RunUnitFlatSubmission | RunUnitTemplateSubmission;
|
|
43
|
+
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>;
|
|
52
|
+
readonly cleanup?: PlatformCleanupPolicy;
|
|
53
|
+
}
|
|
54
|
+
export interface RunUnitAttempt {
|
|
55
|
+
readonly id: string;
|
|
56
|
+
readonly attemptNumber: number;
|
|
57
|
+
readonly status: string;
|
|
58
|
+
readonly providerSessionId?: string;
|
|
59
|
+
readonly errorClass?: string;
|
|
60
|
+
readonly errorCode?: string;
|
|
61
|
+
readonly errorMessage?: string;
|
|
62
|
+
readonly startedAt?: string;
|
|
63
|
+
readonly terminalAt?: string;
|
|
64
|
+
readonly createdAt: string;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Indexed event metadata (dedupe-key + summary). Raw payload bytes for
|
|
68
|
+
* each event are NOT here — they ride in `rawEventPages`. This struct
|
|
69
|
+
* stays small so the detail response is bounded.
|
|
70
|
+
*/
|
|
71
|
+
export interface RunUnitEvent {
|
|
72
|
+
readonly id: string;
|
|
73
|
+
readonly attemptId?: string;
|
|
74
|
+
readonly providerEventId?: string;
|
|
75
|
+
readonly type: string;
|
|
76
|
+
readonly summary?: string;
|
|
77
|
+
readonly occurredAt?: string;
|
|
78
|
+
readonly processedAt: string;
|
|
79
|
+
readonly usageDelta?: Record<string, JsonValue>;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Inline slice of events plus an optional cursor for the tail. Most
|
|
83
|
+
* runs fit entirely inline; long-running ones overflow and the
|
|
84
|
+
* consumer paginates via `GET /api/runs/:runId/events?cursor=...`.
|
|
85
|
+
*/
|
|
86
|
+
export interface RunUnitEventPage {
|
|
87
|
+
readonly entries: readonly RunUnitEvent[];
|
|
88
|
+
readonly totalCount: number;
|
|
89
|
+
readonly truncated: boolean;
|
|
90
|
+
readonly nextCursor?: string;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* One gzipped JSONL page of raw provider events captured to Storage.
|
|
94
|
+
* Bytes are downloaded via signed URL or surfaced inside the per-run
|
|
95
|
+
* archive zip. `storagePath` is bucket-relative; the BFF turns it
|
|
96
|
+
* into a signed URL for clients.
|
|
97
|
+
*/
|
|
98
|
+
export interface RunUnitRawEventPage {
|
|
99
|
+
readonly attempt: number;
|
|
100
|
+
readonly page: number;
|
|
101
|
+
readonly byteSize: number;
|
|
102
|
+
readonly eventCount: number;
|
|
103
|
+
readonly storagePath: string;
|
|
104
|
+
readonly contentEncoding: "gzip";
|
|
105
|
+
readonly createdAt: string;
|
|
106
|
+
}
|
|
107
|
+
export interface RunUnitOutput {
|
|
108
|
+
readonly id: string;
|
|
109
|
+
readonly fileName: string;
|
|
110
|
+
readonly byteSize: number;
|
|
111
|
+
readonly contentType?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface RunUnitOutputCaptureFailure {
|
|
114
|
+
readonly id: string;
|
|
115
|
+
readonly providerFileId?: string;
|
|
116
|
+
readonly filename?: string;
|
|
117
|
+
readonly byteSize?: number;
|
|
118
|
+
readonly reason: string;
|
|
119
|
+
readonly errorMessage?: string;
|
|
120
|
+
readonly createdAt: string;
|
|
121
|
+
}
|
|
122
|
+
export interface RunUnitProxyCall {
|
|
123
|
+
readonly id: string;
|
|
124
|
+
readonly endpointName: string;
|
|
125
|
+
readonly method: string;
|
|
126
|
+
readonly requestPathRedacted: string | null;
|
|
127
|
+
readonly requestByteSize: number;
|
|
128
|
+
readonly responseStatus: number | null;
|
|
129
|
+
readonly responseByteSize: number;
|
|
130
|
+
readonly outcome: string;
|
|
131
|
+
readonly errorClass: string | null;
|
|
132
|
+
readonly startedAt: string;
|
|
133
|
+
readonly finishedAt: string | null;
|
|
134
|
+
readonly durationMs: number | null;
|
|
135
|
+
}
|
|
136
|
+
export interface RunUnitProxyCallPage {
|
|
137
|
+
readonly entries: readonly RunUnitProxyCall[];
|
|
138
|
+
readonly totalCount: number;
|
|
139
|
+
readonly truncated: boolean;
|
|
140
|
+
readonly nextCursor?: string;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Workspace skill bundle pinned at submission. `liveSkillId` is `null`
|
|
144
|
+
* when the corresponding `skill_bundles` row has been soft-deleted —
|
|
145
|
+
* the UI uses that to render a tombstoned link.
|
|
146
|
+
*/
|
|
147
|
+
export interface RunUnitSkillSnapshot {
|
|
148
|
+
readonly skillId: string;
|
|
149
|
+
readonly name: string;
|
|
150
|
+
readonly hash: string;
|
|
151
|
+
readonly sizeBytes: number;
|
|
152
|
+
readonly fileCount: number;
|
|
153
|
+
readonly liveSkillId: string | null;
|
|
154
|
+
}
|
|
155
|
+
export interface RunUnitProviderSkill {
|
|
156
|
+
readonly vendor: string;
|
|
157
|
+
readonly skillId: string;
|
|
158
|
+
readonly version?: string;
|
|
159
|
+
}
|
|
160
|
+
export interface RunUnitTransientSkill {
|
|
161
|
+
readonly id: string;
|
|
162
|
+
readonly slotId: string;
|
|
163
|
+
readonly skillName: string;
|
|
164
|
+
readonly contentHash: string;
|
|
165
|
+
readonly anthropicFileId: string | null;
|
|
166
|
+
readonly status: string;
|
|
167
|
+
readonly createdAt: string;
|
|
168
|
+
readonly updatedAt: string;
|
|
169
|
+
}
|
|
170
|
+
export interface RunUnit {
|
|
171
|
+
readonly id: string;
|
|
172
|
+
readonly workspaceId: string;
|
|
173
|
+
readonly status: string;
|
|
174
|
+
readonly lifecyclePhase?: string;
|
|
175
|
+
readonly cleanupStatus: string;
|
|
176
|
+
readonly createdAt: string;
|
|
177
|
+
readonly updatedAt: string;
|
|
178
|
+
readonly startedAt?: string;
|
|
179
|
+
readonly terminalAt?: string;
|
|
180
|
+
readonly deletedAt?: string;
|
|
181
|
+
readonly attemptCount: number;
|
|
182
|
+
readonly submission: RunUnitSubmission;
|
|
183
|
+
readonly capsSnapshot?: Record<string, JsonValue>;
|
|
184
|
+
readonly proxyEndpointsSnapshot?: readonly PlatformProxyEndpoint[];
|
|
185
|
+
readonly attempts: readonly RunUnitAttempt[];
|
|
186
|
+
readonly events: RunUnitEventPage;
|
|
187
|
+
readonly rawEventPages: readonly RunUnitRawEventPage[];
|
|
188
|
+
readonly outputs: readonly RunUnitOutput[];
|
|
189
|
+
readonly outputCaptureFailures: readonly RunUnitOutputCaptureFailure[];
|
|
190
|
+
readonly proxyCalls: RunUnitProxyCallPage;
|
|
191
|
+
readonly skillSnapshots: readonly RunUnitSkillSnapshot[];
|
|
192
|
+
readonly providerSkills: readonly RunUnitProviderSkill[];
|
|
193
|
+
readonly transientSkills: readonly RunUnitTransientSkill[];
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
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.
|
|
200
|
+
*
|
|
201
|
+
* 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.
|
|
205
|
+
*/
|
|
206
|
+
export declare function parseRunUnitSubmission(input: unknown): RunUnitSubmission;
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RunUnit — the self-contained read shape of a run.
|
|
3
|
+
*
|
|
4
|
+
* One canonical struct that captures every non-secret artifact persisted
|
|
5
|
+
* for a single run: parsed submission inputs, status/lifecycle, attempts,
|
|
6
|
+
* indexed events, raw-event Storage manifest, outputs (+ capture
|
|
7
|
+
* failures), proxy-call audit log, pinned workspace skills, provider
|
|
8
|
+
* built-in skills, and transient (Anthropic Files) skill records.
|
|
9
|
+
*
|
|
10
|
+
* Wire contract for `GET /api/runs/:runId`, the per-run archive's
|
|
11
|
+
* `run.json`/`submission.json`/`caps.json`, and the SDK/CLI
|
|
12
|
+
* `client.runs.get(runId)` return type.
|
|
13
|
+
*
|
|
14
|
+
* Immutability: every field here is read-only. Edit endpoints do not
|
|
15
|
+
* exist by design — see `references/architecture-decisions.md` (Runs).
|
|
16
|
+
*
|
|
17
|
+
* Raw event payloads are not embedded in this struct. They live in
|
|
18
|
+
* private Supabase Storage as gzipped JSONL pages and are listed via
|
|
19
|
+
* `rawEventPages` (manifest only; bytes downloaded out-of-band so the
|
|
20
|
+
* detail response stays bounded). The archive zip carries the bytes.
|
|
21
|
+
*/
|
|
22
|
+
import { parseMcpServerRef, parseSkillRef } from "./blueprint.js";
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Submission parser
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
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.
|
|
31
|
+
*
|
|
32
|
+
* 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.
|
|
36
|
+
*/
|
|
37
|
+
export function parseRunUnitSubmission(input) {
|
|
38
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
39
|
+
return fallbackFlat();
|
|
40
|
+
}
|
|
41
|
+
const value = input;
|
|
42
|
+
if (value.kind === "flat") {
|
|
43
|
+
return parseFlatProjection(value);
|
|
44
|
+
}
|
|
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.
|
|
50
|
+
return fallbackFlat();
|
|
51
|
+
}
|
|
52
|
+
function parseFlatProjection(value) {
|
|
53
|
+
const submissionRaw = isRecord(value.submission) ? value.submission : {};
|
|
54
|
+
const cleanup = parseCleanup(value.cleanup);
|
|
55
|
+
const submission = {
|
|
56
|
+
model: typeof submissionRaw.model === "string" ? submissionRaw.model : "",
|
|
57
|
+
...(typeof submissionRaw.system === "string" ? { system: submissionRaw.system } : {}),
|
|
58
|
+
prompt: toStringArray(submissionRaw.prompt),
|
|
59
|
+
skills: toSkillRefArray(submissionRaw.skills),
|
|
60
|
+
mcpServers: toMcpServerRefArray(submissionRaw.mcpServers),
|
|
61
|
+
...(parseEnvironment(submissionRaw.environment)
|
|
62
|
+
? { environment: parseEnvironment(submissionRaw.environment) }
|
|
63
|
+
: {}),
|
|
64
|
+
...(isJsonRecord(submissionRaw.metadata) ? { metadata: submissionRaw.metadata } : {}),
|
|
65
|
+
...(toOptionalStringArray(submissionRaw.outputDirs)
|
|
66
|
+
? { outputDirs: toOptionalStringArray(submissionRaw.outputDirs) }
|
|
67
|
+
: {})
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
kind: "flat",
|
|
71
|
+
submission,
|
|
72
|
+
...(cleanup ? { cleanup } : {})
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function parseTemplateProjection(value) {
|
|
76
|
+
const templateRaw = value.template;
|
|
77
|
+
const template = {
|
|
78
|
+
name: typeof templateRaw.name === "string" ? templateRaw.name : "",
|
|
79
|
+
model: typeof templateRaw.model === "string" ? templateRaw.model : "",
|
|
80
|
+
templateHash: typeof templateRaw.templateHash === "string" ? templateRaw.templateHash : "",
|
|
81
|
+
messages: toStringArray(templateRaw.messages),
|
|
82
|
+
...(typeof templateRaw.system === "string" ? { system: templateRaw.system } : {}),
|
|
83
|
+
...(isJsonRecord(templateRaw.metadata) ? { metadata: templateRaw.metadata } : {}),
|
|
84
|
+
...(parseEnvironment(templateRaw.environment)
|
|
85
|
+
? { environment: parseEnvironment(templateRaw.environment) }
|
|
86
|
+
: {})
|
|
87
|
+
};
|
|
88
|
+
const variables = isJsonRecord(value.variables) ? value.variables : undefined;
|
|
89
|
+
const cleanup = parseCleanup(value.cleanup);
|
|
90
|
+
return {
|
|
91
|
+
kind: "template",
|
|
92
|
+
template,
|
|
93
|
+
...(variables ? { variables } : {}),
|
|
94
|
+
...(cleanup ? { cleanup } : {})
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function fallbackFlat() {
|
|
98
|
+
return {
|
|
99
|
+
kind: "flat",
|
|
100
|
+
submission: {
|
|
101
|
+
model: "",
|
|
102
|
+
prompt: [],
|
|
103
|
+
skills: [],
|
|
104
|
+
mcpServers: []
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Coercion helpers — deliberately lenient. We never throw on a single
|
|
110
|
+
// malformed sub-field; we collapse it to a safe default and keep going
|
|
111
|
+
// so a dashboard read of a malformed snapshot still surfaces the rest.
|
|
112
|
+
// ---------------------------------------------------------------------------
|
|
113
|
+
function isRecord(value) {
|
|
114
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
115
|
+
}
|
|
116
|
+
function isJsonRecord(value) {
|
|
117
|
+
return isRecord(value);
|
|
118
|
+
}
|
|
119
|
+
function toStringArray(value) {
|
|
120
|
+
if (!Array.isArray(value)) {
|
|
121
|
+
return [];
|
|
122
|
+
}
|
|
123
|
+
return value.filter((item) => typeof item === "string");
|
|
124
|
+
}
|
|
125
|
+
function toOptionalStringArray(value) {
|
|
126
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
127
|
+
return undefined;
|
|
128
|
+
}
|
|
129
|
+
const filtered = value.filter((item) => typeof item === "string");
|
|
130
|
+
return filtered.length === 0 ? undefined : filtered;
|
|
131
|
+
}
|
|
132
|
+
function toSkillRefArray(value) {
|
|
133
|
+
if (!Array.isArray(value)) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
const out = [];
|
|
137
|
+
for (let i = 0; i < value.length; i++) {
|
|
138
|
+
try {
|
|
139
|
+
out.push(parseSkillRef(value[i], `submission.skills[${i}]`, { allowTransient: true }));
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
// Skip malformed entries rather than failing the whole detail
|
|
143
|
+
// read. Worker-side enrichment may add fields we don't recognise.
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return out;
|
|
147
|
+
}
|
|
148
|
+
function toMcpServerRefArray(value) {
|
|
149
|
+
if (!Array.isArray(value)) {
|
|
150
|
+
return [];
|
|
151
|
+
}
|
|
152
|
+
const out = [];
|
|
153
|
+
for (let i = 0; i < value.length; i++) {
|
|
154
|
+
try {
|
|
155
|
+
out.push(parseMcpServerRef(value[i], `submission.mcpServers[${i}]`));
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
// ignore malformed
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return out;
|
|
162
|
+
}
|
|
163
|
+
function parseEnvironment(value) {
|
|
164
|
+
if (!isRecord(value)) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const env = {};
|
|
168
|
+
if (isRecord(value.networking)) {
|
|
169
|
+
const mode = value.networking.mode;
|
|
170
|
+
const allowedHosts = value.networking.allowedHosts;
|
|
171
|
+
if (mode === "limited" || mode === "open") {
|
|
172
|
+
env.networking = {
|
|
173
|
+
mode,
|
|
174
|
+
...(Array.isArray(allowedHosts)
|
|
175
|
+
? { allowedHosts: toStringArray(allowedHosts) }
|
|
176
|
+
: {})
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (Array.isArray(value.packages)) {
|
|
181
|
+
const pkgs = value.packages
|
|
182
|
+
.filter(isRecord)
|
|
183
|
+
.map((p) => {
|
|
184
|
+
const r = p;
|
|
185
|
+
return typeof r.name === "string"
|
|
186
|
+
? { name: r.name, ...(typeof r.version === "string" ? { version: r.version } : {}) }
|
|
187
|
+
: null;
|
|
188
|
+
})
|
|
189
|
+
.filter((p) => p !== null);
|
|
190
|
+
if (pkgs.length > 0) {
|
|
191
|
+
env.packages = pkgs;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return env.networking || env.packages
|
|
195
|
+
? env
|
|
196
|
+
: undefined;
|
|
197
|
+
}
|
|
198
|
+
function parseCleanup(value) {
|
|
199
|
+
if (!isRecord(value)) {
|
|
200
|
+
return undefined;
|
|
201
|
+
}
|
|
202
|
+
const session = value.session;
|
|
203
|
+
const claudeSession = value.claudeSession;
|
|
204
|
+
const out = {};
|
|
205
|
+
if (session === "retain" || session === "delete") {
|
|
206
|
+
out.session = session;
|
|
207
|
+
}
|
|
208
|
+
if (claudeSession === "retain" || claudeSession === "delete") {
|
|
209
|
+
out.claudeSession = claudeSession;
|
|
210
|
+
}
|
|
211
|
+
return out.session || out.claudeSession ? out : undefined;
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=run-unit.js.map
|
|
@@ -71,6 +71,25 @@ export interface WhoAmI {
|
|
|
71
71
|
readonly tokenId?: string;
|
|
72
72
|
readonly tokenName?: string | null;
|
|
73
73
|
readonly scopes?: readonly string[];
|
|
74
|
+
/**
|
|
75
|
+
* Workspace-level caps the BFF will enforce on subsequent calls.
|
|
76
|
+
* Surfaced so consumers (e.g. broll's app-side admission gate) can
|
|
77
|
+
* decide whether to keep their own gate or rely on platform headers.
|
|
78
|
+
* All fields optional — older BFFs may omit. Numbers are concrete
|
|
79
|
+
* snapshots at the time of the `whoami` call.
|
|
80
|
+
*/
|
|
81
|
+
readonly caps?: {
|
|
82
|
+
/** Token-bucket cap on POST /api/runs per minute, per workspace. */
|
|
83
|
+
readonly runSubmitPerMinute?: number;
|
|
84
|
+
/** Hard cap on concurrent non-terminal runs the workspace may hold. */
|
|
85
|
+
readonly maxConcurrentRuns?: number;
|
|
86
|
+
/** Storage cap (bytes) on captured output objects, workspace-wide. */
|
|
87
|
+
readonly storageCapBytes?: number;
|
|
88
|
+
/** Current captured-output usage in bytes. */
|
|
89
|
+
readonly storageUsedBytes?: number;
|
|
90
|
+
/** Wall-clock ceiling on a single run before forced termination. */
|
|
91
|
+
readonly maxRunDurationMs?: number;
|
|
92
|
+
};
|
|
74
93
|
readonly [key: string]: unknown;
|
|
75
94
|
}
|
|
76
95
|
/**
|