@o11y-one/sdk 0.0.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/errors.js ADDED
@@ -0,0 +1,83 @@
1
+ /**
2
+ * The O11y One error taxonomy, as the SDK must render it.
3
+ *
4
+ * From lane 50A's note: `UNAUTHENTICATED` vs `PERMISSION_DENIED` is the whole
5
+ * reason this layer exists. "Your token is invalid" and "your token is fine but
6
+ * this capability is not shipped / not granted" are different problems with
7
+ * different fixes, and collapsing them into "auth error" is the failure mode
8
+ * this file prevents.
9
+ */
10
+ import { Code, ConnectError } from "@connectrpc/connect";
11
+ const CANONICAL_SCOPES = [
12
+ "eval:read",
13
+ "run:execute",
14
+ "dataset:write",
15
+ "lease:submit",
16
+ "platform-annotation:write",
17
+ ];
18
+ export const MACHINE_SCOPES = CANONICAL_SCOPES;
19
+ /**
20
+ * Classify a thrown error into the taxonomy above.
21
+ *
22
+ * Non-Connect errors pass through as `unclassified` rather than being forced
23
+ * into a bucket — a DNS failure is not an auth failure and pretending otherwise
24
+ * sends people down the wrong path.
25
+ */
26
+ export function classify(err) {
27
+ if (!(err instanceof ConnectError)) {
28
+ return {
29
+ disposition: "unclassified",
30
+ code: undefined,
31
+ message: err instanceof Error ? err.message : String(err),
32
+ missingScope: undefined,
33
+ retryable: false,
34
+ };
35
+ }
36
+ switch (err.code) {
37
+ case Code.Unauthenticated:
38
+ return {
39
+ disposition: "reauthenticate",
40
+ code: err.code,
41
+ message: err.rawMessage,
42
+ missingScope: undefined,
43
+ retryable: false,
44
+ };
45
+ case Code.PermissionDenied: {
46
+ // The server names the missing canonical scope in the message when the
47
+ // cause is a scope gap. When it does not, the cause is the other
48
+ // PERMISSION_DENIED case: a machine credential on a non-machine surface.
49
+ const named = MACHINE_SCOPES.find((s) => err.rawMessage.includes(s));
50
+ return {
51
+ disposition: "insufficient-scope",
52
+ code: err.code,
53
+ message: err.rawMessage,
54
+ missingScope: named,
55
+ retryable: false,
56
+ };
57
+ }
58
+ case Code.InvalidArgument:
59
+ return {
60
+ disposition: "version-skew",
61
+ code: err.code,
62
+ message: err.rawMessage,
63
+ missingScope: undefined,
64
+ retryable: false,
65
+ };
66
+ case Code.Unavailable:
67
+ return {
68
+ disposition: "retry-with-backoff",
69
+ code: err.code,
70
+ message: err.rawMessage,
71
+ missingScope: undefined,
72
+ retryable: true,
73
+ };
74
+ default:
75
+ return {
76
+ disposition: "unclassified",
77
+ code: err.code,
78
+ message: err.rawMessage,
79
+ missingScope: undefined,
80
+ retryable: false,
81
+ };
82
+ }
83
+ }
@@ -0,0 +1,253 @@
1
+ /**
2
+ * The agentic-evaluation journey surface: thin, typed, validated wrappers over
3
+ * the generated `AgenticEvaluationService` client.
4
+ *
5
+ * Every method here is one of two things and nothing else:
6
+ * - a request SHAPED and VALIDATED before it leaves (validate.ts), or
7
+ * - a response whose refusal is DECODED to a typed value (refusals.ts) rather
8
+ * than thrown away.
9
+ *
10
+ * No server logic is reimplemented. The backend does the work; this class binds
11
+ * to its verbs, guards their inputs, and names their outcomes. The generated
12
+ * client is reachable as `.raw` for any verb this surface has not lifted.
13
+ */
14
+ import type { Client } from "@connectrpc/connect";
15
+ import type { Timestamp } from "@bufbuild/protobuf/wkt";
16
+ import { AgenticEvaluationService, PlatformAnnotationKindV1, type CreateEvaluationRunResponse, type PreviewEvaluationRunResponse, type GetCallerPrincipalResponse, type MachinePrincipalV1, type MachinePrincipalWithCredentialsV1, type MachineCredentialV1, type ExternalCaseLeaseV1, type ExternalCaseOutputV1, type LeasedEvaluationCaseV1, type CreateEvaluationDatasetVersionResponse, type CaptureEvaluationCaseResponse, type PublishDatasetCaseDraftsResponse, type RecordPlatformAnnotationResponse, type PlatformAnnotationV1, type PlatformAnnotationAttributeV1, type PlatformAnnotationLinkV1, type EvaluationCaptureSpanSourceV1, type EvaluationCaptureCellSourceV1, type DatasetFieldMappingV1 } from "@o11y-one/api-agentic/o11y_one/agentic/v1/evaluation_pb";
17
+ import type { O11yClient } from "./client.js";
18
+ import type { MachineScope } from "./errors.js";
19
+ import { type Result } from "./result.js";
20
+ import { type LaunchRejection, type CaptureRefusal, type DatasetVersionRejection, type AnnotationRejection, type LeaseRefusal, type CursorResync, type SubmissionOutcome } from "./refusals.js";
21
+ /** Canonical scope strings for a set of wire discriminants; UNSPECIFIED drops. */
22
+ export declare function scopesToCanonical(values: readonly number[]): MachineScope[];
23
+ /**
24
+ * The plaintext credential returned exactly once by `createMachineCredential`.
25
+ *
26
+ * It is a DISTINCT type the caller must consume deliberately: `reveal()` hands
27
+ * over the string, and every accidental-logging path — `toString`,
28
+ * `toJSON`, Node's `util.inspect` — returns a redaction instead. The server
29
+ * keeps no read-back copy, so this object is the only place the material ever
30
+ * exists; the redaction is there so a stray `console.log(result)` cannot leak
31
+ * it into a CI log.
32
+ */
33
+ export declare class OneTimeCredential {
34
+ #private;
35
+ /** The credential-id this token belongs to, safe to log. */
36
+ readonly credentialId: string;
37
+ constructor(plaintext: string, credentialId: string);
38
+ /** Hand over the plaintext. Store it in your secret manager; never log it. */
39
+ reveal(): string;
40
+ toString(): string;
41
+ toJSON(): string;
42
+ }
43
+ /** Who the credential authenticates as, and what it may do — before any run. */
44
+ export interface CallerIdentity {
45
+ readonly tenantId: string;
46
+ readonly orgId: string;
47
+ /** Absent for a browser session; present for a machine credential. */
48
+ readonly machinePrincipalId: string | undefined;
49
+ /** The credential id that authorized this request; absent for a session. */
50
+ readonly credentialId: string | undefined;
51
+ /** The credential's scopes, as canonical strings. */
52
+ readonly scopes: readonly MachineScope[];
53
+ /** True when the credential carries `scope`. The pre-run gate `doctor` needs. */
54
+ readonly hasScope: (scope: MachineScope) => boolean;
55
+ readonly raw: GetCallerPrincipalResponse;
56
+ }
57
+ /** The outcome of a lease attempt that was not refused. */
58
+ export interface LeaseOutcome {
59
+ /** The fenced hold, or absent when nothing was claimable. */
60
+ readonly lease: ExternalCaseLeaseV1 | undefined;
61
+ readonly cases: readonly LeasedEvaluationCaseV1[];
62
+ readonly remainingUnleasedCaseCount: number;
63
+ /**
64
+ * True when the lease is absent AND nothing remains: the honest "you are
65
+ * done" answer, distinct from "held by another runtime" (lease absent,
66
+ * remaining > 0).
67
+ */
68
+ readonly exhausted: boolean;
69
+ }
70
+ export declare class AgenticEvaluationClient {
71
+ readonly raw: Client<typeof AgenticEvaluationService>;
72
+ constructor(client: O11yClient);
73
+ /**
74
+ * Answer "is my credential valid, and which scopes does it carry?" BEFORE a
75
+ * run. An UNAUTHENTICATED / PERMISSION_DENIED here is a thrown `ConnectError`
76
+ * the caller renders with `classify()`; a success carries the scope set that
77
+ * turns a minute-40 failure into a minute-0 one.
78
+ */
79
+ whoami(): Promise<CallerIdentity>;
80
+ createMachinePrincipal(args: {
81
+ displayName: string;
82
+ description?: string;
83
+ scopes: readonly MachineScope[];
84
+ }): Promise<MachinePrincipalV1>;
85
+ listMachinePrincipals(args?: {
86
+ pageSize?: number;
87
+ pageToken?: string;
88
+ }): Promise<{
89
+ principals: MachinePrincipalWithCredentialsV1[];
90
+ nextPageToken: string | undefined;
91
+ }>;
92
+ /**
93
+ * Mint a credential. The plaintext comes back once, wrapped in a
94
+ * {@link OneTimeCredential} the caller must `reveal()` to read and can never
95
+ * accidentally log.
96
+ */
97
+ createMachineCredential(args: {
98
+ machinePrincipalId: string;
99
+ description?: string;
100
+ expiresAt?: Date;
101
+ }): Promise<{
102
+ credential: MachineCredentialV1;
103
+ token: OneTimeCredential;
104
+ }>;
105
+ revokeMachineCredential(args: {
106
+ credentialId: string;
107
+ revokeReason?: string;
108
+ }): Promise<MachineCredentialV1>;
109
+ revokeMachinePrincipal(args: {
110
+ machinePrincipalId: string;
111
+ revokeReason?: string;
112
+ }): Promise<{
113
+ principal: MachinePrincipalV1;
114
+ revokedCredentials: MachineCredentialV1[];
115
+ }>;
116
+ /**
117
+ * Resolve and freeze what a launch WOULD do: the preview token, its digest
118
+ * and expiry, the frozen manifest, the estimates and any blockers. Launch
119
+ * from the token this returns.
120
+ *
121
+ * NOTE: the wire contract launches from a definition (+ optional revision)
122
+ * through a preview token; there is no inline-draft launch path in the proto
123
+ * at this capability. See l1-notes.
124
+ */
125
+ previewRun(args: {
126
+ definitionId: string;
127
+ revisionId?: string;
128
+ }): Promise<PreviewEvaluationRunResponse>;
129
+ /**
130
+ * Launch a run from an accepted preview token. Returns `err(LaunchRejection)`
131
+ * when a binding moved under the token (the refusal rides in `Status.details`
132
+ * on a FAILED_PRECONDITION); any other transport error rethrows.
133
+ */
134
+ launchRun(args: {
135
+ definitionId: string;
136
+ idempotencyKey: string;
137
+ previewToken: string;
138
+ }): Promise<Result<CreateEvaluationRunResponse, LaunchRejection>>;
139
+ /**
140
+ * Claim a bounded set of prepared cases. `err(LeaseRefusal)` when the server
141
+ * refuses on a 200 (`SCOPE_MISSING`, `RUN_NOT_EXECUTABLE`, …). `ok` otherwise
142
+ * — and an `ok` with no lease is not a failure: check `exhausted`.
143
+ */
144
+ leaseCases(args: {
145
+ evaluationRunId: string;
146
+ candidateKey: string;
147
+ runtimeKey: string;
148
+ maxCases?: number;
149
+ leaseSeconds?: number;
150
+ }): Promise<Result<LeaseOutcome, LeaseRefusal>>;
151
+ /** Heartbeat a held lease. `err(LeaseRefusal)` when the fence was lost. */
152
+ renewLease(args: {
153
+ evaluationRunId: string;
154
+ leaseId: string;
155
+ leaseToken: string;
156
+ leaseSeconds?: number;
157
+ }): Promise<Result<ExternalCaseLeaseV1, LeaseRefusal>>;
158
+ /**
159
+ * Submit a batch of case outputs — the recorded-output upload and the
160
+ * external-loop submit are the same verb. Returns a {@link SubmissionOutcome}
161
+ * that partitions the batch: which coordinates landed, which were duplicates,
162
+ * which were rejected and why — never an all-or-nothing throw. A whole-request
163
+ * refusal (fence lost) appears as `outcome.refusal`, with no per-case answers.
164
+ *
165
+ * Idempotent by lease identity and coordinate: a resubmit of the same batch
166
+ * reports the landed cases as `ALREADY_SUBMITTED`.
167
+ */
168
+ submitCaseOutputs(args: {
169
+ evaluationRunId: string;
170
+ leaseId: string;
171
+ leaseToken: string;
172
+ outputs: readonly ExternalCaseOutputV1[];
173
+ idempotencyKey: string;
174
+ }): Promise<SubmissionOutcome>;
175
+ /** Release a held lease early. `err(LeaseRefusal)` when the fence was lost. */
176
+ releaseLease(args: {
177
+ evaluationRunId: string;
178
+ leaseId: string;
179
+ leaseToken: string;
180
+ }): Promise<Result<number, LeaseRefusal>>;
181
+ /**
182
+ * Create a dataset version from EXACTLY ONE ingest source: raw JSONL bytes, or
183
+ * a finalized capture draft by id. `err(DatasetVersionRejection)` on a typed
184
+ * ingest refusal (rides in `Status.details`); any other error rethrows.
185
+ */
186
+ createDatasetVersion(args: {
187
+ datasetCollectionId?: string;
188
+ collectionName?: string;
189
+ label?: string;
190
+ jsonl?: Uint8Array;
191
+ draftId?: string;
192
+ recordedOutputFieldPath?: string;
193
+ expectedDraftVersion?: bigint;
194
+ idempotencyKey: string;
195
+ }): Promise<Result<CreateEvaluationDatasetVersionResponse, DatasetVersionRejection>>;
196
+ /**
197
+ * Append one case to a collection's open draft, from EXACTLY ONE source: a
198
+ * trace span or a matrix cell. `err(CaptureRefusal)` on a typed capture
199
+ * refusal (rides in `Status.details`).
200
+ */
201
+ captureCase(args: {
202
+ datasetCollectionId: string;
203
+ draftId?: string;
204
+ span?: EvaluationCaptureSpanSourceV1;
205
+ cell?: EvaluationCaptureCellSourceV1;
206
+ fieldMappings?: readonly DatasetFieldMappingV1[];
207
+ recordedOutputFieldPath?: string;
208
+ baseDatasetVersionId?: string;
209
+ expectedContentDigest?: string;
210
+ idempotencyKey: string;
211
+ }): Promise<Result<CaptureEvaluationCaseResponse, CaptureRefusal>>;
212
+ /**
213
+ * Publish a reviewed changeset. A digest mismatch is a FAILED_PRECONDITION
214
+ * transport error (never a silent rebase) and is thrown for `classify()`.
215
+ */
216
+ publishDatasetDrafts(args: {
217
+ changesetId: string;
218
+ previewDigest: string;
219
+ label?: string;
220
+ idempotencyKey: string;
221
+ }): Promise<PublishDatasetCaseDraftsResponse>;
222
+ /**
223
+ * Record a deployment marker, custom event, or time-range highlight.
224
+ * `err(AnnotationRejection)` on a typed rejection (rides in `Status.details`).
225
+ * A `startAt`/`endAt` are `Date`s; omit `endAt` for an instant.
226
+ */
227
+ recordAnnotation(args: {
228
+ kind: PlatformAnnotationKindV1;
229
+ title: string;
230
+ startAt?: Date;
231
+ endAt?: Date;
232
+ attributes?: readonly PlatformAnnotationAttributeV1[];
233
+ links?: readonly PlatformAnnotationLinkV1[];
234
+ idempotencyKey: string;
235
+ }): Promise<Result<RecordPlatformAnnotationResponse, AnnotationRejection>>;
236
+ /**
237
+ * List annotations overlapping a window. `resync`, when present, is a typed
238
+ * directive that the caller's cursor cannot be honoured and the listing must
239
+ * be restarted — it arrives with an empty page, never as a transport error.
240
+ */
241
+ listAnnotations(args: {
242
+ windowStart: Date | Timestamp;
243
+ windowEnd: Date | Timestamp;
244
+ kinds?: readonly PlatformAnnotationKindV1[];
245
+ limit?: number;
246
+ pageToken?: string;
247
+ }): Promise<{
248
+ annotations: PlatformAnnotationV1[];
249
+ nextPageToken: string | undefined;
250
+ hasMore: boolean;
251
+ resync: CursorResync | undefined;
252
+ }>;
253
+ }