@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/LICENSE +201 -0
- package/README.md +84 -0
- package/dist/auth.d.ts +51 -0
- package/dist/auth.js +64 -0
- package/dist/client.d.ts +105 -0
- package/dist/client.js +107 -0
- package/dist/errors.d.ts +47 -0
- package/dist/errors.js +83 -0
- package/dist/evaluation.d.ts +253 -0
- package/dist/evaluation.js +455 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.js +24 -0
- package/dist/refusals.d.ts +116 -0
- package/dist/refusals.js +125 -0
- package/dist/result.d.ts +38 -0
- package/dist/result.js +40 -0
- package/dist/validate.d.ts +58 -0
- package/dist/validate.js +103 -0
- package/package.json +42 -0
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
|
+
}
|