@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.
@@ -0,0 +1,455 @@
1
+ import { timestampFromDate } from "@bufbuild/protobuf/wkt";
2
+ import { AgenticEvaluationService, MachinePrincipalScopeV1, PlatformAnnotationKindV1, EvaluationLaunchRejectionV1Schema, EvaluationCaptureRefusalV1Schema, EvaluationDatasetVersionRejectionV1Schema, PlatformAnnotationRejectionV1Schema, } from "@o11y-one/api-agentic/o11y_one/agentic/v1/evaluation_pb";
3
+ import { ok, err } from "./result.js";
4
+ import { findDetail, toLaunchRejection, toCaptureRefusal, toDatasetVersionRejection, toAnnotationRejection, toLeaseRefusal, toCaseAck, toCursorResync, } from "./refusals.js";
5
+ import { requireNonEmpty, requireIdempotencyKey, requireExactlyOne, rejectUnspecified, } from "./validate.js";
6
+ // --- scope vocabulary bridge ------------------------------------------------
7
+ // The five canonical scope strings (errors.ts, frozen taxonomy) <-> the proto's
8
+ // numeric MachinePrincipalScopeV1. Callers speak strings; the wire speaks the
9
+ // enum; neither leaks into the other.
10
+ const SCOPE_TO_ENUM = {
11
+ "eval:read": MachinePrincipalScopeV1.EVAL_READ,
12
+ "run:execute": MachinePrincipalScopeV1.RUN_EXECUTE,
13
+ "dataset:write": MachinePrincipalScopeV1.DATASET_WRITE,
14
+ "lease:submit": MachinePrincipalScopeV1.LEASE_SUBMIT,
15
+ "platform-annotation:write": MachinePrincipalScopeV1.PLATFORM_ANNOTATION_WRITE,
16
+ };
17
+ const ENUM_TO_SCOPE = {
18
+ [MachinePrincipalScopeV1.EVAL_READ]: "eval:read",
19
+ [MachinePrincipalScopeV1.RUN_EXECUTE]: "run:execute",
20
+ [MachinePrincipalScopeV1.DATASET_WRITE]: "dataset:write",
21
+ [MachinePrincipalScopeV1.LEASE_SUBMIT]: "lease:submit",
22
+ [MachinePrincipalScopeV1.PLATFORM_ANNOTATION_WRITE]: "platform-annotation:write",
23
+ };
24
+ /** Canonical scope strings for a set of wire discriminants; UNSPECIFIED drops. */
25
+ export function scopesToCanonical(values) {
26
+ const out = [];
27
+ for (const v of values) {
28
+ const s = ENUM_TO_SCOPE[v];
29
+ if (s !== undefined) {
30
+ out.push(s);
31
+ }
32
+ }
33
+ return out;
34
+ }
35
+ // --- one-time credential material -------------------------------------------
36
+ /**
37
+ * The plaintext credential returned exactly once by `createMachineCredential`.
38
+ *
39
+ * It is a DISTINCT type the caller must consume deliberately: `reveal()` hands
40
+ * over the string, and every accidental-logging path — `toString`,
41
+ * `toJSON`, Node's `util.inspect` — returns a redaction instead. The server
42
+ * keeps no read-back copy, so this object is the only place the material ever
43
+ * exists; the redaction is there so a stray `console.log(result)` cannot leak
44
+ * it into a CI log.
45
+ */
46
+ export class OneTimeCredential {
47
+ #plaintext;
48
+ /** The credential-id this token belongs to, safe to log. */
49
+ credentialId;
50
+ constructor(plaintext, credentialId) {
51
+ this.#plaintext = plaintext;
52
+ this.credentialId = credentialId;
53
+ }
54
+ /** Hand over the plaintext. Store it in your secret manager; never log it. */
55
+ reveal() {
56
+ return this.#plaintext;
57
+ }
58
+ toString() {
59
+ return "[o11y one-time credential — redacted; call reveal()]";
60
+ }
61
+ toJSON() {
62
+ return "[o11y one-time credential — redacted]";
63
+ }
64
+ [Symbol.for("nodejs.util.inspect.custom")]() {
65
+ return this.toString();
66
+ }
67
+ }
68
+ // --- the client -------------------------------------------------------------
69
+ export class AgenticEvaluationClient {
70
+ raw;
71
+ constructor(client) {
72
+ this.raw = client.service(AgenticEvaluationService);
73
+ }
74
+ // -- 1. identity ----------------------------------------------------------
75
+ /**
76
+ * Answer "is my credential valid, and which scopes does it carry?" BEFORE a
77
+ * run. An UNAUTHENTICATED / PERMISSION_DENIED here is a thrown `ConnectError`
78
+ * the caller renders with `classify()`; a success carries the scope set that
79
+ * turns a minute-40 failure into a minute-0 one.
80
+ */
81
+ async whoami() {
82
+ const res = await this.raw.getCallerPrincipal({});
83
+ const scopes = scopesToCanonical(res.machinePrincipal?.scopes ?? []);
84
+ const scopeSet = new Set(scopes);
85
+ return {
86
+ tenantId: res.tenantId,
87
+ orgId: res.orgId,
88
+ machinePrincipalId: res.machinePrincipal?.machinePrincipalId,
89
+ credentialId: res.credential?.credentialId,
90
+ scopes,
91
+ hasScope: (scope) => scopeSet.has(scope),
92
+ raw: res,
93
+ };
94
+ }
95
+ // -- 2. machine credentials ----------------------------------------------
96
+ async createMachinePrincipal(args) {
97
+ requireNonEmpty("displayName", args.displayName);
98
+ const scopes = args.scopes.map((s) => SCOPE_TO_ENUM[s]);
99
+ rejectUnspecified("scopes", scopes);
100
+ const res = await this.raw.createMachinePrincipal({
101
+ displayName: args.displayName,
102
+ ...(args.description !== undefined ? { description: args.description } : {}),
103
+ scopes,
104
+ });
105
+ if (res.principal === undefined) {
106
+ throw new Error("createMachinePrincipal: server returned no principal");
107
+ }
108
+ return res.principal;
109
+ }
110
+ async listMachinePrincipals(args) {
111
+ const res = await this.raw.listMachinePrincipals({
112
+ ...(args?.pageSize !== undefined ? { pageSize: args.pageSize } : {}),
113
+ ...(args?.pageToken !== undefined ? { pageToken: args.pageToken } : {}),
114
+ });
115
+ return { principals: res.principals, nextPageToken: res.nextPageToken };
116
+ }
117
+ /**
118
+ * Mint a credential. The plaintext comes back once, wrapped in a
119
+ * {@link OneTimeCredential} the caller must `reveal()` to read and can never
120
+ * accidentally log.
121
+ */
122
+ async createMachineCredential(args) {
123
+ requireNonEmpty("machinePrincipalId", args.machinePrincipalId);
124
+ const res = await this.raw.createMachineCredential({
125
+ machinePrincipalId: args.machinePrincipalId,
126
+ ...(args.description !== undefined ? { description: args.description } : {}),
127
+ ...(args.expiresAt !== undefined ? { expiresAt: timestampFromDate(args.expiresAt) } : {}),
128
+ });
129
+ if (res.credential === undefined) {
130
+ throw new Error("createMachineCredential: server returned no credential metadata");
131
+ }
132
+ return {
133
+ credential: res.credential,
134
+ token: new OneTimeCredential(res.plaintextTokenOnce, res.credential.credentialId),
135
+ };
136
+ }
137
+ async revokeMachineCredential(args) {
138
+ requireNonEmpty("credentialId", args.credentialId);
139
+ const res = await this.raw.revokeMachineCredential({
140
+ credentialId: args.credentialId,
141
+ ...(args.revokeReason !== undefined ? { revokeReason: args.revokeReason } : {}),
142
+ });
143
+ if (res.credential === undefined) {
144
+ throw new Error("revokeMachineCredential: server returned no credential");
145
+ }
146
+ return res.credential;
147
+ }
148
+ async revokeMachinePrincipal(args) {
149
+ requireNonEmpty("machinePrincipalId", args.machinePrincipalId);
150
+ const res = await this.raw.revokeMachinePrincipal({
151
+ machinePrincipalId: args.machinePrincipalId,
152
+ ...(args.revokeReason !== undefined ? { revokeReason: args.revokeReason } : {}),
153
+ });
154
+ if (res.principal === undefined) {
155
+ throw new Error("revokeMachinePrincipal: server returned no principal");
156
+ }
157
+ return { principal: res.principal, revokedCredentials: res.revokedCredentials };
158
+ }
159
+ // -- 3. run submission ----------------------------------------------------
160
+ /**
161
+ * Resolve and freeze what a launch WOULD do: the preview token, its digest
162
+ * and expiry, the frozen manifest, the estimates and any blockers. Launch
163
+ * from the token this returns.
164
+ *
165
+ * NOTE: the wire contract launches from a definition (+ optional revision)
166
+ * through a preview token; there is no inline-draft launch path in the proto
167
+ * at this capability. See l1-notes.
168
+ */
169
+ async previewRun(args) {
170
+ requireNonEmpty("definitionId", args.definitionId);
171
+ return await this.raw.previewEvaluationRun({
172
+ definitionId: args.definitionId,
173
+ ...(args.revisionId !== undefined ? { revisionId: args.revisionId } : {}),
174
+ });
175
+ }
176
+ /**
177
+ * Launch a run from an accepted preview token. Returns `err(LaunchRejection)`
178
+ * when a binding moved under the token (the refusal rides in `Status.details`
179
+ * on a FAILED_PRECONDITION); any other transport error rethrows.
180
+ */
181
+ async launchRun(args) {
182
+ requireNonEmpty("definitionId", args.definitionId);
183
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
184
+ requireNonEmpty("previewToken", args.previewToken);
185
+ try {
186
+ const res = await this.raw.createEvaluationRun({
187
+ definitionId: args.definitionId,
188
+ idempotencyKey: args.idempotencyKey,
189
+ previewToken: args.previewToken,
190
+ });
191
+ return ok(res);
192
+ }
193
+ catch (e) {
194
+ const detail = findDetail(e, EvaluationLaunchRejectionV1Schema);
195
+ if (detail !== undefined) {
196
+ return err(toLaunchRejection(detail));
197
+ }
198
+ throw e;
199
+ }
200
+ }
201
+ // -- 4/5. externally-executed loop: lease -> renew -> submit -> release ----
202
+ /**
203
+ * Claim a bounded set of prepared cases. `err(LeaseRefusal)` when the server
204
+ * refuses on a 200 (`SCOPE_MISSING`, `RUN_NOT_EXECUTABLE`, …). `ok` otherwise
205
+ * — and an `ok` with no lease is not a failure: check `exhausted`.
206
+ */
207
+ async leaseCases(args) {
208
+ requireNonEmpty("evaluationRunId", args.evaluationRunId);
209
+ requireNonEmpty("candidateKey", args.candidateKey);
210
+ requireNonEmpty("runtimeKey", args.runtimeKey);
211
+ const res = await this.raw.leaseEvaluationCases({
212
+ evaluationRunId: args.evaluationRunId,
213
+ candidateKey: args.candidateKey,
214
+ runtimeKey: args.runtimeKey,
215
+ maxCases: args.maxCases ?? 0,
216
+ leaseSeconds: args.leaseSeconds ?? 0,
217
+ });
218
+ if (res.refusal !== undefined) {
219
+ return err(toLeaseRefusal(res.refusal));
220
+ }
221
+ return ok({
222
+ lease: res.lease,
223
+ cases: res.cases,
224
+ remainingUnleasedCaseCount: res.remainingUnleasedCaseCount,
225
+ exhausted: res.lease === undefined && res.remainingUnleasedCaseCount === 0,
226
+ });
227
+ }
228
+ /** Heartbeat a held lease. `err(LeaseRefusal)` when the fence was lost. */
229
+ async renewLease(args) {
230
+ requireNonEmpty("evaluationRunId", args.evaluationRunId);
231
+ requireNonEmpty("leaseId", args.leaseId);
232
+ requireNonEmpty("leaseToken", args.leaseToken);
233
+ const res = await this.raw.renewEvaluationCaseLease({
234
+ evaluationRunId: args.evaluationRunId,
235
+ leaseId: args.leaseId,
236
+ leaseToken: args.leaseToken,
237
+ leaseSeconds: args.leaseSeconds ?? 0,
238
+ });
239
+ if (res.refusal !== undefined) {
240
+ return err(toLeaseRefusal(res.refusal));
241
+ }
242
+ if (res.lease === undefined) {
243
+ throw new Error("renewEvaluationCaseLease: neither lease nor refusal was returned");
244
+ }
245
+ return ok(res.lease);
246
+ }
247
+ /**
248
+ * Submit a batch of case outputs — the recorded-output upload and the
249
+ * external-loop submit are the same verb. Returns a {@link SubmissionOutcome}
250
+ * that partitions the batch: which coordinates landed, which were duplicates,
251
+ * which were rejected and why — never an all-or-nothing throw. A whole-request
252
+ * refusal (fence lost) appears as `outcome.refusal`, with no per-case answers.
253
+ *
254
+ * Idempotent by lease identity and coordinate: a resubmit of the same batch
255
+ * reports the landed cases as `ALREADY_SUBMITTED`.
256
+ */
257
+ async submitCaseOutputs(args) {
258
+ requireNonEmpty("evaluationRunId", args.evaluationRunId);
259
+ requireNonEmpty("leaseId", args.leaseId);
260
+ requireNonEmpty("leaseToken", args.leaseToken);
261
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
262
+ if (args.outputs.length === 0) {
263
+ requireNonEmpty("outputs", "");
264
+ }
265
+ const res = await this.raw.submitEvaluationCaseOutputs({
266
+ evaluationRunId: args.evaluationRunId,
267
+ leaseId: args.leaseId,
268
+ leaseToken: args.leaseToken,
269
+ outputs: args.outputs,
270
+ idempotencyKey: args.idempotencyKey,
271
+ });
272
+ const acks = res.acks.map(toCaseAck);
273
+ return {
274
+ acceptedCount: res.acceptedCount,
275
+ alreadySubmittedCount: res.alreadySubmittedCount,
276
+ rejectedCount: res.rejectedCount,
277
+ acks,
278
+ accepted: acks.filter((a) => a.kind === "ACCEPTED"),
279
+ alreadySubmitted: acks.filter((a) => a.kind === "ALREADY_SUBMITTED"),
280
+ rejected: acks.filter((a) => a.kind === "REJECTED"),
281
+ remainingLeasedCaseCount: res.remainingLeasedCaseCount,
282
+ refusal: res.refusal !== undefined ? toLeaseRefusal(res.refusal) : undefined,
283
+ };
284
+ }
285
+ /** Release a held lease early. `err(LeaseRefusal)` when the fence was lost. */
286
+ async releaseLease(args) {
287
+ requireNonEmpty("evaluationRunId", args.evaluationRunId);
288
+ requireNonEmpty("leaseId", args.leaseId);
289
+ requireNonEmpty("leaseToken", args.leaseToken);
290
+ const res = await this.raw.releaseEvaluationCaseLease({
291
+ evaluationRunId: args.evaluationRunId,
292
+ leaseId: args.leaseId,
293
+ leaseToken: args.leaseToken,
294
+ });
295
+ if (res.refusal !== undefined) {
296
+ return err(toLeaseRefusal(res.refusal));
297
+ }
298
+ return ok(res.releasedCaseCount);
299
+ }
300
+ // -- 6. dataset drafts: create / append / publish -------------------------
301
+ /**
302
+ * Create a dataset version from EXACTLY ONE ingest source: raw JSONL bytes, or
303
+ * a finalized capture draft by id. `err(DatasetVersionRejection)` on a typed
304
+ * ingest refusal (rides in `Status.details`); any other error rethrows.
305
+ */
306
+ async createDatasetVersion(args) {
307
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
308
+ requireExactlyOne("ingest", [
309
+ { name: "jsonl", present: args.jsonl !== undefined },
310
+ { name: "draftId", present: args.draftId !== undefined },
311
+ ]);
312
+ const ingest = args.jsonl !== undefined
313
+ ? { case: "jsonl", value: args.jsonl }
314
+ : { case: "draftId", value: args.draftId };
315
+ try {
316
+ const res = await this.raw.createEvaluationDatasetVersion({
317
+ ...(args.datasetCollectionId !== undefined
318
+ ? { datasetCollectionId: args.datasetCollectionId }
319
+ : {}),
320
+ ...(args.collectionName !== undefined ? { collectionName: args.collectionName } : {}),
321
+ ...(args.label !== undefined ? { label: args.label } : {}),
322
+ ingest,
323
+ ...(args.recordedOutputFieldPath !== undefined
324
+ ? { recordedOutputFieldPath: args.recordedOutputFieldPath }
325
+ : {}),
326
+ ...(args.expectedDraftVersion !== undefined
327
+ ? { expectedDraftVersion: args.expectedDraftVersion }
328
+ : {}),
329
+ idempotencyKey: args.idempotencyKey,
330
+ });
331
+ return ok(res);
332
+ }
333
+ catch (e) {
334
+ const detail = findDetail(e, EvaluationDatasetVersionRejectionV1Schema);
335
+ if (detail !== undefined) {
336
+ return err(toDatasetVersionRejection(detail));
337
+ }
338
+ throw e;
339
+ }
340
+ }
341
+ /**
342
+ * Append one case to a collection's open draft, from EXACTLY ONE source: a
343
+ * trace span or a matrix cell. `err(CaptureRefusal)` on a typed capture
344
+ * refusal (rides in `Status.details`).
345
+ */
346
+ async captureCase(args) {
347
+ requireNonEmpty("datasetCollectionId", args.datasetCollectionId);
348
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
349
+ requireExactlyOne("source", [
350
+ { name: "span", present: args.span !== undefined },
351
+ { name: "cell", present: args.cell !== undefined },
352
+ ]);
353
+ const source = args.span !== undefined
354
+ ? { case: "span", value: args.span }
355
+ : { case: "cell", value: args.cell };
356
+ try {
357
+ const res = await this.raw.captureEvaluationCase({
358
+ datasetCollectionId: args.datasetCollectionId,
359
+ ...(args.draftId !== undefined ? { draftId: args.draftId } : {}),
360
+ source,
361
+ fieldMappings: (args.fieldMappings ?? []),
362
+ ...(args.recordedOutputFieldPath !== undefined
363
+ ? { recordedOutputFieldPath: args.recordedOutputFieldPath }
364
+ : {}),
365
+ ...(args.baseDatasetVersionId !== undefined
366
+ ? { baseDatasetVersionId: args.baseDatasetVersionId }
367
+ : {}),
368
+ ...(args.expectedContentDigest !== undefined
369
+ ? { expectedContentDigest: args.expectedContentDigest }
370
+ : {}),
371
+ idempotencyKey: args.idempotencyKey,
372
+ });
373
+ return ok(res);
374
+ }
375
+ catch (e) {
376
+ const detail = findDetail(e, EvaluationCaptureRefusalV1Schema);
377
+ if (detail !== undefined) {
378
+ return err(toCaptureRefusal(detail));
379
+ }
380
+ throw e;
381
+ }
382
+ }
383
+ /**
384
+ * Publish a reviewed changeset. A digest mismatch is a FAILED_PRECONDITION
385
+ * transport error (never a silent rebase) and is thrown for `classify()`.
386
+ */
387
+ async publishDatasetDrafts(args) {
388
+ requireNonEmpty("changesetId", args.changesetId);
389
+ requireNonEmpty("previewDigest", args.previewDigest);
390
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
391
+ return await this.raw.publishDatasetCaseDrafts({
392
+ changesetId: args.changesetId,
393
+ previewDigest: args.previewDigest,
394
+ ...(args.label !== undefined ? { label: args.label } : {}),
395
+ idempotencyKey: args.idempotencyKey,
396
+ });
397
+ }
398
+ // -- 7. annotations -------------------------------------------------------
399
+ /**
400
+ * Record a deployment marker, custom event, or time-range highlight.
401
+ * `err(AnnotationRejection)` on a typed rejection (rides in `Status.details`).
402
+ * A `startAt`/`endAt` are `Date`s; omit `endAt` for an instant.
403
+ */
404
+ async recordAnnotation(args) {
405
+ requireNonEmpty("title", args.title);
406
+ requireIdempotencyKey("idempotencyKey", args.idempotencyKey);
407
+ rejectUnspecified("kind", [args.kind]);
408
+ try {
409
+ const res = await this.raw.recordPlatformAnnotation({
410
+ kind: args.kind,
411
+ title: args.title,
412
+ ...(args.startAt !== undefined ? { startAt: timestampFromDate(args.startAt) } : {}),
413
+ ...(args.endAt !== undefined ? { endAt: timestampFromDate(args.endAt) } : {}),
414
+ attributes: (args.attributes ?? []),
415
+ links: (args.links ?? []),
416
+ idempotencyKey: args.idempotencyKey,
417
+ });
418
+ return ok(res);
419
+ }
420
+ catch (e) {
421
+ const detail = findDetail(e, PlatformAnnotationRejectionV1Schema);
422
+ if (detail !== undefined) {
423
+ return err(toAnnotationRejection(detail));
424
+ }
425
+ throw e;
426
+ }
427
+ }
428
+ /**
429
+ * List annotations overlapping a window. `resync`, when present, is a typed
430
+ * directive that the caller's cursor cannot be honoured and the listing must
431
+ * be restarted — it arrives with an empty page, never as a transport error.
432
+ */
433
+ async listAnnotations(args) {
434
+ const windowStart = args.windowStart instanceof Date ? timestampFromDate(args.windowStart) : args.windowStart;
435
+ const windowEnd = args.windowEnd instanceof Date ? timestampFromDate(args.windowEnd) : args.windowEnd;
436
+ if (args.kinds !== undefined) {
437
+ rejectUnspecified("kinds", args.kinds);
438
+ }
439
+ const res = await this.raw.listPlatformAnnotations({
440
+ windowStart,
441
+ windowEnd,
442
+ kinds: (args.kinds ?? []),
443
+ page: {
444
+ ...(args.limit !== undefined ? { limit: args.limit } : {}),
445
+ ...(args.pageToken !== undefined ? { pageToken: args.pageToken } : {}),
446
+ },
447
+ });
448
+ return {
449
+ annotations: res.annotations,
450
+ nextPageToken: res.page?.nextPageToken,
451
+ hasMore: res.page?.hasMore ?? false,
452
+ resync: res.resync !== undefined ? toCursorResync(res.resync) : undefined,
453
+ };
454
+ }
455
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * `@o11y-one/sdk` — hand-written client layer over the generated `@o11y-one/api-agentic`.
3
+ *
4
+ * The foundation:
5
+ * - transport construction (client.ts)
6
+ * - credential injection and its local validation (auth.ts)
7
+ * - the failure taxonomy an SDK exists to render (errors.ts)
8
+ *
9
+ * The agentic-evaluation journey surface (evaluation.ts) binds the code-first
10
+ * loop's verbs — identity, machine credentials, run submission, the
11
+ * externally-executed lease loop, dataset drafts, annotations — as thin, typed,
12
+ * validated wrappers. Its refusals are TYPED VALUES (refusals.ts) returned in a
13
+ * `Result` (result.ts), not thrown-away errors; its inputs are guarded
14
+ * synchronously (validate.ts) before a wire call. The generated client stays
15
+ * reachable for any verb the surface has not lifted.
16
+ */
17
+ export { O11yClient, createTransport, credentialInterceptor, type ClientOptions, } from "./client.js";
18
+ export { assertLooksLikeMachineCredential, CREDENTIAL_HEADER, CREDENTIAL_MAX_LEN, MACHINE_CREDENTIAL_PREFIX, ORG_ID_HEADER, TENANT_ID_HEADER, type MachineCredential, } from "./auth.js";
19
+ export { classify, MACHINE_SCOPES, type ClassifiedFailure, type FailureDisposition, type MachineScope, } from "./errors.js";
20
+ export { AgenticEvaluationClient, OneTimeCredential, scopesToCanonical, type CallerIdentity, type LeaseOutcome, } from "./evaluation.js";
21
+ export { ok, err, isOk, isErr, unwrap, type Result, type Ok, type Err, } from "./result.js";
22
+ export { ValidationError, requireNonEmpty, requireIdempotencyKey, requireExactlyOne, rejectUnspecified, requireWithinBytes, byteLength, } from "./validate.js";
23
+ export { findDetail, toLeaseRefusal, toCaseAck, toLaunchRejection, toCaptureRefusal, toDatasetVersionRejection, toAnnotationRejection, toCursorResync, type RecoveryAction, type LeaseRefusal, type LeaseRefusalReason, type CaseAck, type CaseAckKind, type SubmissionOutcome, type LaunchRejection, type LaunchRejectionReason, type CaptureRefusal, type CaptureRefusalReason, type DatasetVersionRejection, type DatasetVersionRejectionReason, type AnnotationRejection, type AnnotationRejectionReason, type CursorResync, type CursorResyncReason, } from "./refusals.js";
package/dist/index.js ADDED
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `@o11y-one/sdk` — hand-written client layer over the generated `@o11y-one/api-agentic`.
3
+ *
4
+ * The foundation:
5
+ * - transport construction (client.ts)
6
+ * - credential injection and its local validation (auth.ts)
7
+ * - the failure taxonomy an SDK exists to render (errors.ts)
8
+ *
9
+ * The agentic-evaluation journey surface (evaluation.ts) binds the code-first
10
+ * loop's verbs — identity, machine credentials, run submission, the
11
+ * externally-executed lease loop, dataset drafts, annotations — as thin, typed,
12
+ * validated wrappers. Its refusals are TYPED VALUES (refusals.ts) returned in a
13
+ * `Result` (result.ts), not thrown-away errors; its inputs are guarded
14
+ * synchronously (validate.ts) before a wire call. The generated client stays
15
+ * reachable for any verb the surface has not lifted.
16
+ */
17
+ export { O11yClient, createTransport, credentialInterceptor, } from "./client.js";
18
+ export { assertLooksLikeMachineCredential, CREDENTIAL_HEADER, CREDENTIAL_MAX_LEN, MACHINE_CREDENTIAL_PREFIX, ORG_ID_HEADER, TENANT_ID_HEADER, } from "./auth.js";
19
+ export { classify, MACHINE_SCOPES, } from "./errors.js";
20
+ // --- the journey surface ----------------------------------------------------
21
+ export { AgenticEvaluationClient, OneTimeCredential, scopesToCanonical, } from "./evaluation.js";
22
+ export { ok, err, isOk, isErr, unwrap, } from "./result.js";
23
+ export { ValidationError, requireNonEmpty, requireIdempotencyKey, requireExactlyOne, rejectUnspecified, requireWithinBytes, byteLength, } from "./validate.js";
24
+ export { findDetail, toLeaseRefusal, toCaseAck, toLaunchRejection, toCaptureRefusal, toDatasetVersionRejection, toAnnotationRejection, toCursorResync, } from "./refusals.js";
@@ -0,0 +1,116 @@
1
+ import type { DescMessage, MessageShape } from "@bufbuild/protobuf";
2
+ import { type ExternalLeaseRefusalV1, type EvaluationLaunchRejectionV1, type EvaluationCaptureRefusalV1, type EvaluationDatasetVersionRejectionV1, type PlatformAnnotationRejectionV1, type EvaluationCursorResyncV1, type ExternalCaseOutputAckV1 } from "@o11y-one/api-agentic/o11y_one/agentic/v1/evaluation_pb";
3
+ /** The one remedial action the server named, as a wire enum member name. */
4
+ export type RecoveryAction = "UNSPECIFIED" | "RETRY" | "RE_PREVIEW" | "EDIT_DEFINITION" | "WAIT" | "CONTACT_SUPPORT";
5
+ export type LeaseRefusalReason = "UNSPECIFIED" | "RUN_NOT_EXECUTABLE" | "CANDIDATE_NOT_EXTERNALLY_EXECUTED" | "LEASE_NOT_HELD" | "LEASE_EXPIRED" | "RENEWAL_BUDGET_EXHAUSTED" | "BOUNDS_EXCEEDED" | "SCOPE_MISSING";
6
+ export interface LeaseRefusal {
7
+ readonly type: "lease-refusal";
8
+ readonly reason: LeaseRefusalReason;
9
+ readonly message: string;
10
+ /** Set on `SCOPE_MISSING`: the canonical scope the credential lacks. */
11
+ readonly missingScope: string | undefined;
12
+ readonly recovery: RecoveryAction;
13
+ readonly raw: ExternalLeaseRefusalV1;
14
+ }
15
+ export declare function toLeaseRefusal(m: ExternalLeaseRefusalV1): LeaseRefusal;
16
+ export type CaseAckKind = "UNSPECIFIED" | "ACCEPTED" | "ALREADY_SUBMITTED" | "REJECTED";
17
+ export interface CaseAck {
18
+ readonly cohortKey: string;
19
+ readonly candidateKey: string;
20
+ readonly caseRevisionId: string;
21
+ readonly trial: number;
22
+ readonly kind: CaseAckKind;
23
+ /** The frozen execution state the server sees this coordinate in. */
24
+ readonly observedState: number;
25
+ readonly raw: ExternalCaseOutputAckV1;
26
+ }
27
+ export declare function toCaseAck(m: ExternalCaseOutputAckV1): CaseAck;
28
+ /**
29
+ * The partial-batch outcome of a `submitCaseOutputs`. This is not an error even
30
+ * when `rejected` is non-empty: the accepted cases DID land, and the caller
31
+ * resubmits only the rejected coordinates. `refusal`, when present, is a
32
+ * WHOLE-request refusal (the fence was lost) and no per-case answer is
33
+ * meaningful — the two are never both set.
34
+ */
35
+ export interface SubmissionOutcome {
36
+ readonly acceptedCount: number;
37
+ readonly alreadySubmittedCount: number;
38
+ readonly rejectedCount: number;
39
+ readonly acks: readonly CaseAck[];
40
+ readonly accepted: readonly CaseAck[];
41
+ readonly alreadySubmitted: readonly CaseAck[];
42
+ readonly rejected: readonly CaseAck[];
43
+ readonly remainingLeasedCaseCount: number;
44
+ /** Whole-request refusal; set only when the lease fence was lost. */
45
+ readonly refusal: LeaseRefusal | undefined;
46
+ }
47
+ export type LaunchRejectionReason = "UNSPECIFIED" | "PREVIEW_EXPIRED" | "PREVIEW_MALFORMED" | "DEFINITION_REVISION_MOVED" | "DEPENDENCY_VERSIONS_MOVED" | "CARDINALITY_MOVED" | "CAPABILITY_SET_MOVED" | "IDEMPOTENCY_KEY_REUSED";
48
+ export interface LaunchRejection {
49
+ readonly type: "launch-rejection";
50
+ readonly reason: LaunchRejectionReason;
51
+ readonly expectedDigest: string;
52
+ readonly observedDigest: string;
53
+ /** Set on `IDEMPOTENCY_KEY_REUSED`: the run the key already named. */
54
+ readonly existingEvaluationRunId: string | undefined;
55
+ readonly recovery: RecoveryAction;
56
+ readonly detail: string;
57
+ readonly raw: EvaluationLaunchRejectionV1;
58
+ }
59
+ export declare function toLaunchRejection(m: EvaluationLaunchRejectionV1): LaunchRejection;
60
+ export type CaptureRefusalReason = "UNSPECIFIED" | "SOURCE_NOT_FOUND" | "SOURCE_PLANE_NOT_SHIPPED" | "REQUIRED_FIELD_UNMAPPED" | "MAPPING_RESOLVED_NOTHING" | string;
61
+ export interface CaptureRefusal {
62
+ readonly type: "capture-refusal";
63
+ readonly reason: CaptureRefusalReason;
64
+ readonly detail: string;
65
+ readonly missingTargetPaths: readonly string[];
66
+ readonly recovery: RecoveryAction;
67
+ /** Set on `IDEMPOTENCY_KEY_REUSED`: the case the key already captured. */
68
+ readonly existingProposedCaseId: string | undefined;
69
+ readonly raw: EvaluationCaptureRefusalV1;
70
+ }
71
+ export declare function toCaptureRefusal(m: EvaluationCaptureRefusalV1): CaptureRefusal;
72
+ export type DatasetVersionRejectionReason = "UNSPECIFIED" | "COLLECTION_CAP_EXCEEDED" | "VERSION_CAP_ALL_PINNED" | "MALFORMED_JSONL_LINE" | "EMPTY_INGEST" | "INGEST_TOO_LARGE" | "DRAFT_NOT_FINALIZABLE" | "INGEST_BYTES_EXCEEDED" | "INGEST_CASE_COUNT_EXCEEDED" | "DRAFT_HAS_UNAPPROVED_CASES";
73
+ export interface DatasetVersionRejection {
74
+ readonly type: "dataset-version-rejection";
75
+ readonly reason: DatasetVersionRejectionReason;
76
+ /** The server's stable `reason_code` string, carried alongside the kind. */
77
+ readonly reasonCode: string;
78
+ /** 1-based, set only on `MALFORMED_JSONL_LINE`. */
79
+ readonly lineNumber: number | undefined;
80
+ readonly observedCount: number;
81
+ readonly maxCount: number;
82
+ readonly detail: string;
83
+ readonly recovery: RecoveryAction;
84
+ readonly raw: EvaluationDatasetVersionRejectionV1;
85
+ }
86
+ export declare function toDatasetVersionRejection(m: EvaluationDatasetVersionRejectionV1): DatasetVersionRejection;
87
+ export type AnnotationRejectionReason = "UNSPECIFIED" | "KIND_UNKNOWN" | "TITLE_TOO_LARGE" | "ATTRIBUTE_TOO_LARGE" | "TOO_MANY_ATTRIBUTES" | "TOO_MANY_LINKS" | "LINK_REF_TOO_LARGE" | "RANGE_INVERTED" | "IDEMPOTENCY_KEY_REUSED" | "WINDOW_INVALID";
88
+ export interface AnnotationRejection {
89
+ readonly type: "annotation-rejection";
90
+ readonly reason: AnnotationRejectionReason;
91
+ /** The field the server refused, when it named one. */
92
+ readonly field: string | undefined;
93
+ /** The bound that was exceeded, when the reason is a size/count cap. */
94
+ readonly limitValue: bigint | undefined;
95
+ /** On `KIND_UNKNOWN`: the kinds this server DOES support, as wire names. */
96
+ readonly supportedKinds: readonly string[];
97
+ readonly raw: PlatformAnnotationRejectionV1;
98
+ }
99
+ export declare function toAnnotationRejection(m: PlatformAnnotationRejectionV1): AnnotationRejection;
100
+ export type CursorResyncReason = "UNSPECIFIED" | "SNAPSHOT_SUPERSEDED" | "FILTER_CHANGED" | "CURSOR_MALFORMED" | "BACKLOG_EXCEEDED";
101
+ export interface CursorResync {
102
+ readonly type: "cursor-resync";
103
+ readonly reason: CursorResyncReason;
104
+ readonly raw: EvaluationCursorResyncV1;
105
+ }
106
+ export declare function toCursorResync(m: EvaluationCursorResyncV1): CursorResync;
107
+ /**
108
+ * Pull the first typed detail of a given schema off a thrown error.
109
+ *
110
+ * This is how a refusal that rides in `google.rpc.Status.details` (launch,
111
+ * capture, dataset-version, annotation) is recovered: connect-es already
112
+ * decodes `Status.details`, and `ConnectError.findDetails(schema)` hands back
113
+ * the typed messages. A non-Connect error, or a Connect error with no such
114
+ * detail, returns `undefined` and the caller rethrows.
115
+ */
116
+ export declare function findDetail<Desc extends DescMessage>(error: unknown, schema: Desc): MessageShape<Desc> | undefined;