@inflexa-ai/harness 0.42.0 → 0.42.2

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,492 @@
1
+ /**
2
+ * Pure async client functions for the Amass Data Platform API: RegulatoryCore
3
+ * (the FDA and EMA authorizations, with the parsed sections of their labels,
4
+ * reviews, SmPCs and EPARs) and PatentCore (patents).
5
+ *
6
+ * The schemas mirror the published OpenAPI document
7
+ * (`https://api.amass.tech/api/doc/openapi.json`). A live key verified them on
8
+ * 2026-09-30, and the golden fixtures under `__fixtures__/amass/` hold those
9
+ * answers.
10
+ *
11
+ * Absence policy: a base field is always present, and an absent value is an
12
+ * explicit `null` (`therapeuticIndication`, `grantDate`). A field that only an
13
+ * `include` parameter adds (`fdaDetails`, `emaDetails`, `claims`,
14
+ * `description`) is an omitted key when the request does not ask for it. Thus
15
+ * a base field carries `.nullable()`, and an include field carries
16
+ * `.nullable().optional()`. A lookup item omits `amassIds` when it carries
17
+ * `error`, and omits `error` when it carries `amassIds`.
18
+ *
19
+ * A response enum is a plain string here. Amass grows those vocabularies, and
20
+ * one unknown value would otherwise reject a whole search. The input filters
21
+ * of the tools carry the enums of the OpenAPI document.
22
+ *
23
+ * Status policy: only a 404 and a lookup item with `NOT_FOUND` are an absence.
24
+ * `isUnexpectedApiError` reads every 4xx as an absence, which would turn a
25
+ * rejected key (401) or a refused organization (403) into "no such record".
26
+ * Thus this client classifies each status itself.
27
+ *
28
+ * Every call spends credits: a search costs 5 credits for each 20 records of
29
+ * `limit`, and a get, a lookup (whatever its item count) or a 404 costs 1.
30
+ */
31
+ import { err, ok } from "neverthrow";
32
+ import { z } from "zod";
33
+ import { apiFetchValidated, describeApiError } from "./api-utils.js";
34
+ import { AMASS_BASE } from "./amass-config.js";
35
+ /** Render an `AmassError` as a one-line message for the model or for a throw. */
36
+ export function describeAmassError(e) {
37
+ switch (e.type) {
38
+ case "invalid_input":
39
+ return `Amass rejected the request (HTTP ${e.status}): ${e.message}`;
40
+ case "unauthorized":
41
+ return `Amass rejected the API key (HTTP 401): ${e.message}. Make sure that AMASS_API_KEY is correct.`;
42
+ case "forbidden":
43
+ return (`Amass refused the request (HTTP 403): ${e.message}. ` +
44
+ "Make sure that AMASS_API_KEY is correct and that the organization of the key has Amass credits.");
45
+ case "rejected":
46
+ return `Amass answered ${e.code}${e.status === null ? "" : ` (HTTP ${e.status})`}: ${e.message}`;
47
+ case "api":
48
+ return describeApiError(e.error);
49
+ }
50
+ }
51
+ // Every error of Amass has this one shape. A 400 adds `fields`, the reason for
52
+ // each rejected parameter.
53
+ export const AmassErrorEnvelopeSchema = z.object({
54
+ error: z.object({
55
+ status: z.number(),
56
+ code: z.string(),
57
+ message: z.string(),
58
+ fields: z.record(z.string(), z.string()).optional(),
59
+ }),
60
+ });
61
+ /** Read the message of an Amass error body, with the per-field reasons of a 400. */
62
+ function envelopeOf(body) {
63
+ let parsed;
64
+ try {
65
+ parsed = JSON.parse(body);
66
+ }
67
+ catch {
68
+ return { code: null, message: body || "no message" };
69
+ }
70
+ const envelope = AmassErrorEnvelopeSchema.safeParse(parsed);
71
+ if (!envelope.success)
72
+ return { code: null, message: body || "no message" };
73
+ const { code, message, fields } = envelope.data.error;
74
+ const reasons = fields ? Object.entries(fields).map(([field, reason]) => `${field}: ${reason}`) : [];
75
+ return { code, message: reasons.length > 0 ? `${message} (${reasons.join("; ")})` : message };
76
+ }
77
+ /**
78
+ * Classify a failed call. A 404 is the one absence, thus it resolves to `ok(null)`.
79
+ * A 429 that outlived the retries arrives as `exhausted`, never as `http_status`.
80
+ */
81
+ function classify(e) {
82
+ if (e.type !== "http_status")
83
+ return err({ type: "api", error: e });
84
+ if (e.status === 404)
85
+ return ok(null);
86
+ const { code, message } = envelopeOf(e.body);
87
+ if (e.status === 400 || e.status === 422)
88
+ return err({ type: "invalid_input", status: e.status, message });
89
+ if (e.status === 401)
90
+ return err({ type: "unauthorized", message });
91
+ if (e.status === 403)
92
+ return err({ type: "forbidden", message });
93
+ if (e.status >= 400 && e.status < 500)
94
+ return err({ type: "rejected", status: e.status, code: code ?? "UNKNOWN", message });
95
+ return err({ type: "api", error: e });
96
+ }
97
+ function amassFetch(url, schema, headers, body) {
98
+ const options = body === undefined ? { headers } : { method: "POST", headers: { ...headers, "Content-Type": "application/json" }, body: JSON.stringify(body) };
99
+ return apiFetchValidated(url.toString(), schema, options).orElse(classify);
100
+ }
101
+ /** Append each defined value; an array repeats the parameter, which Amass reads as "match any". */
102
+ function urlOf(path, params) {
103
+ const url = new URL(`${AMASS_BASE}${path}`);
104
+ for (const [key, value] of Object.entries(params)) {
105
+ if (value === undefined)
106
+ continue;
107
+ if (Array.isArray(value)) {
108
+ for (const item of value)
109
+ url.searchParams.append(key, item);
110
+ }
111
+ else {
112
+ url.searchParams.append(key, String(value));
113
+ }
114
+ }
115
+ return url;
116
+ }
117
+ // ---------------------------------------------------------------------------
118
+ // Lookup
119
+ // ---------------------------------------------------------------------------
120
+ export const AmassLookupResponseSchema = z.object({
121
+ data: z.array(z.object({
122
+ input: z.record(z.string(), z.string()),
123
+ amassIds: z.array(z.string()).optional(),
124
+ error: z.object({ code: z.string(), message: z.string() }).optional(),
125
+ })),
126
+ });
127
+ /**
128
+ * Resolve external identifiers to Amass IDs in one lookup request. The IDs of every
129
+ * item are merged, in order and without duplicates. An item with `NOT_FOUND`
130
+ * contributes no ID; an item with any other error code rejects the whole lookup.
131
+ */
132
+ function lookup(core, items, headers) {
133
+ return amassFetch(urlOf(`/cores/${core}/records/lookup`, {}), AmassLookupResponseSchema, headers, { items }).andThen((response) => {
134
+ const ids = [];
135
+ for (const item of response?.data ?? []) {
136
+ if (item.error && item.error.code !== "NOT_FOUND") {
137
+ return err({ type: "rejected", status: null, code: item.error.code, message: item.error.message });
138
+ }
139
+ for (const id of item.amassIds ?? []) {
140
+ if (!ids.includes(id))
141
+ ids.push(id);
142
+ }
143
+ }
144
+ return ok(ids);
145
+ });
146
+ }
147
+ // ---------------------------------------------------------------------------
148
+ // Identifier forms
149
+ // ---------------------------------------------------------------------------
150
+ const FDA_APPLICATION_PREFIXES = ["NDA", "BLA", "ANDA"];
151
+ /**
152
+ * The lookup forms of an FDA application number. Amass stores the number with its
153
+ * prefix (`NDA021588`) and answers `NOT_FOUND` for the bare `021588`. A bare number
154
+ * thus goes as all three prefixes, in one request at the price of one.
155
+ */
156
+ export function fdaApplicationForms(input) {
157
+ const compact = input.replace(/[\s-]/g, "").toUpperCase();
158
+ if (/^\d+$/.test(compact))
159
+ return FDA_APPLICATION_PREFIXES.map((prefix) => `${prefix}${compact}`);
160
+ return [compact];
161
+ }
162
+ /**
163
+ * The lookup form of an NDC. Amass stores the two-segment product NDC (`0078-0401`)
164
+ * and answers `NOT_FOUND` for the package NDC (`0078-0401-05`).
165
+ */
166
+ export function productNdcOf(input) {
167
+ const trimmed = input.trim();
168
+ const segments = trimmed.split("-");
169
+ return segments.length === 3 && segments.every((segment) => /^\d+$/.test(segment)) ? `${segments[0]}-${segments[1]}` : trimmed;
170
+ }
171
+ // ---------------------------------------------------------------------------
172
+ // RegulatoryCore
173
+ // ---------------------------------------------------------------------------
174
+ export const REGULATORY_AGENCIES = ["FDA", "EMA"];
175
+ export const REGULATORY_MOLECULE_TYPES = [
176
+ "SMALL_MOLECULE",
177
+ "ANTIBODY",
178
+ "PROTEIN",
179
+ "ENZYME",
180
+ "OLIGONUCLEOTIDE",
181
+ "GENE",
182
+ "CELL",
183
+ "ANTIBODY_DRUG_CONJUGATE",
184
+ "VACCINE_COMPONENT",
185
+ "VACCINE",
186
+ "OLIGOSACCHARIDE",
187
+ "UNKNOWN",
188
+ ];
189
+ export const REGULATORY_AUTHORIZATION_STATUSES = [
190
+ "ACTIVE",
191
+ "APPROVED_NOT_MARKETED",
192
+ "CONDITIONAL",
193
+ "SUSPENDED",
194
+ "WITHDRAWN_VOLUNTARY",
195
+ "WITHDRAWN_FORCED",
196
+ "REVOKED",
197
+ "LAPSED_SUNSET",
198
+ "REFUSED",
199
+ "WITHDRAWN_DURING_REVIEW",
200
+ "EXPIRED",
201
+ "UNKNOWN",
202
+ ];
203
+ export const REGULATORY_DESIGNATIONS = [
204
+ "PRIORITY_REVIEW",
205
+ "BREAKTHROUGH_THERAPY",
206
+ "FAST_TRACK",
207
+ "RMAT",
208
+ "ACCELERATED_APPROVAL",
209
+ "ACCELERATED_ASSESSMENT",
210
+ "PRIME",
211
+ "CONDITIONAL_MA",
212
+ "EXCEPTIONAL_CIRCUMSTANCES",
213
+ ];
214
+ const DocumentSectionSchema = z.object({
215
+ documentSectionId: z.string(),
216
+ amassId: z.string(),
217
+ docType: z.string(),
218
+ path: z.string().nullable(),
219
+ title: z.string().nullable(),
220
+ textType: z.string().nullable(),
221
+ sourceUrl: z.string().nullable(),
222
+ sourceDate: z.string().nullable(),
223
+ });
224
+ const MatchedSectionSchema = DocumentSectionSchema.extend({ matchedText: z.string().nullable() });
225
+ const DesignationSchema = z.object({
226
+ axis: z.string(),
227
+ type: z.string(),
228
+ agency: z.string(),
229
+ nativeName: z.string().nullable(),
230
+ basis: z.string().nullable(),
231
+ indication: z.string().nullable(),
232
+ postMarketingObligation: z.boolean().nullable(),
233
+ });
234
+ const OtherAuthorizationSchema = z.object({
235
+ amassId: z.string(),
236
+ agency: z.string(),
237
+ name: z.string().nullable(),
238
+ authorizationStatus: z.string().nullable(),
239
+ });
240
+ const FdaDetailsSchema = z.object({
241
+ applicationNumber: z.string().nullable(),
242
+ prescriptionClass: z.string().nullable(),
243
+ submissionClassCode: z.string().nullable(),
244
+ labelDate: z.string().nullable(),
245
+ labelUrl: z.string().nullable(),
246
+ ndc: z.array(z.string()),
247
+ splSetId: z.array(z.string()),
248
+ withdrawalDate: z.string().nullable(),
249
+ withdrawalReason: z.string().nullable(),
250
+ withdrawalSourceUrl: z.string().nullable(),
251
+ });
252
+ const EmaDetailsSchema = z.object({
253
+ productNumber: z.string().nullable(),
254
+ category: z.string().nullable(),
255
+ opinionStatus: z.string().nullable(),
256
+ isBiosimilar: z.boolean().nullable(),
257
+ isAdvancedTherapy: z.boolean().nullable(),
258
+ isGenericOrHybrid: z.boolean().nullable(),
259
+ additionalMonitoring: z.boolean().nullable(),
260
+ pharmacotherapeuticGroup: z.string().nullable(),
261
+ patientSafety: z.string().nullable(),
262
+ latestProcedure: z.string().nullable(),
263
+ revisionNumber: z.number().nullable(),
264
+ smpcUrl: z.string().nullable(),
265
+ smpcDate: z.string().nullable(),
266
+ opinionAdoptedDate: z.string().nullable(),
267
+ europeanCommissionDecisionDate: z.string().nullable(),
268
+ withdrawalOfApplicationDate: z.string().nullable(),
269
+ refusalOfMarketingAuthorisationDate: z.string().nullable(),
270
+ withdrawalExpiryRevocationLapseDate: z.string().nullable(),
271
+ firstPublishedDate: z.string().nullable(),
272
+ });
273
+ const RegulatoryBaseSchema = z.object({
274
+ amassId: z.string(),
275
+ agency: z.string(),
276
+ name: z.string().nullable(),
277
+ activeSubstance: z.string().nullable(),
278
+ moleculeType: z.string().nullable(),
279
+ authorizationStatus: z.string().nullable(),
280
+ procedureType: z.string().nullable(),
281
+ therapeuticIndication: z.string().nullable(),
282
+ marketingAuthorisationHolder: z.string().nullable(),
283
+ authorizationDate: z.string().nullable(),
284
+ firstAuthorizationDate: z.string().nullable(),
285
+ sourceUrl: z.string().nullable(),
286
+ isOrphan: z.boolean().nullable(),
287
+ designations: z.array(DesignationSchema),
288
+ authorizationsByAgency: z.array(OtherAuthorizationSchema),
289
+ fdaDetails: FdaDetailsSchema.nullable().optional(),
290
+ emaDetails: EmaDetailsSchema.nullable().optional(),
291
+ });
292
+ export const RegulatorySearchResponseSchema = z.object({
293
+ data: z.array(RegulatoryBaseSchema.extend({ documentSections: z.array(MatchedSectionSchema) })),
294
+ });
295
+ export const RegulatoryRecordResponseSchema = z.object({
296
+ data: RegulatoryBaseSchema.extend({ documentSections: z.array(DocumentSectionSchema) }),
297
+ });
298
+ export const RegulatorySectionResponseSchema = z.object({
299
+ data: DocumentSectionSchema.extend({ content: z.string().nullable() }),
300
+ });
301
+ /**
302
+ * Group sections by their source document, in the order of first appearance. An
303
+ * FDA label has a hundred sections or more, and each raw entry repeats the record
304
+ * ID, the PDF URL and the revision date; the group states the last two once.
305
+ */
306
+ function groupSections(sections, entryOf) {
307
+ const groups = new Map();
308
+ for (const section of sections) {
309
+ const key = `${section.docType}\u0000${section.sourceUrl ?? ""}\u0000${section.sourceDate ?? ""}`;
310
+ let group = groups.get(key);
311
+ if (!group) {
312
+ group = { docType: section.docType, sourceUrl: section.sourceUrl, sourceDate: section.sourceDate, sections: [] };
313
+ groups.set(key, group);
314
+ }
315
+ group.sections.push(entryOf(section));
316
+ }
317
+ return [...groups.values()];
318
+ }
319
+ function summaryOf(record) {
320
+ return {
321
+ amassId: record.amassId,
322
+ agency: record.agency,
323
+ name: record.name,
324
+ activeSubstance: record.activeSubstance,
325
+ moleculeType: record.moleculeType,
326
+ authorizationStatus: record.authorizationStatus,
327
+ procedureType: record.procedureType,
328
+ therapeuticIndication: record.therapeuticIndication,
329
+ marketingAuthorisationHolder: record.marketingAuthorisationHolder,
330
+ authorizationDate: record.authorizationDate,
331
+ firstAuthorizationDate: record.firstAuthorizationDate,
332
+ isOrphan: record.isOrphan,
333
+ designations: record.designations,
334
+ otherAuthorizations: record.authorizationsByAgency,
335
+ sourceUrl: record.sourceUrl,
336
+ };
337
+ }
338
+ /** The matched sections that a search row keeps. The count still reports all of them. */
339
+ export const MAX_MATCHED_SECTIONS = 5;
340
+ export function mapRegulatorySearchRow(record) {
341
+ return {
342
+ ...summaryOf(record),
343
+ matchedSectionCount: record.documentSections.length,
344
+ matchedDocuments: groupSections(record.documentSections.slice(0, MAX_MATCHED_SECTIONS), (section) => ({
345
+ documentSectionId: section.documentSectionId,
346
+ path: section.path,
347
+ title: section.title,
348
+ matchedText: section.matchedText,
349
+ })),
350
+ };
351
+ }
352
+ export function mapRegulatoryRecord(record) {
353
+ return {
354
+ ...summaryOf(record),
355
+ fdaDetails: record.fdaDetails ?? null,
356
+ emaDetails: record.emaDetails ?? null,
357
+ sectionCount: record.documentSections.length,
358
+ documents: groupSections(record.documentSections, (section) => ({
359
+ documentSectionId: section.documentSectionId,
360
+ path: section.path,
361
+ title: section.title,
362
+ })),
363
+ };
364
+ }
365
+ export function searchRegulatoryRecords(params, headers) {
366
+ const url = urlOf("/cores/regulatorycore/records", { ...params });
367
+ return amassFetch(url, RegulatorySearchResponseSchema, headers).map((response) => (response?.data ?? []).map(mapRegulatorySearchRow));
368
+ }
369
+ /** One authorization with its table of contents, or `null` when Amass holds no such ID. */
370
+ export function getRegulatoryRecord(amassId, headers) {
371
+ const url = urlOf(`/cores/regulatorycore/records/${encodeURIComponent(amassId)}`, { include: ["fdaDetails", "emaDetails"] });
372
+ return amassFetch(url, RegulatoryRecordResponseSchema, headers).map((response) => (response ? mapRegulatoryRecord(response.data) : null));
373
+ }
374
+ /** The full text of one document section, or `null` when Amass holds no such section. */
375
+ export function getRegulatorySection(amassId, documentSectionId, headers) {
376
+ const url = urlOf(`/cores/regulatorycore/records/${encodeURIComponent(amassId)}/document-sections/${encodeURIComponent(documentSectionId)}`, {});
377
+ return amassFetch(url, RegulatorySectionResponseSchema, headers).map((response) => {
378
+ if (!response)
379
+ return null;
380
+ const { textType: _textType, ...section } = response.data;
381
+ return section;
382
+ });
383
+ }
384
+ export function regulatoryLookupItems(id) {
385
+ if (id.fdaApplicationNumber !== undefined)
386
+ return fdaApplicationForms(id.fdaApplicationNumber).map((form) => ({ fdaApplicationNumber: form }));
387
+ if (id.ndc !== undefined)
388
+ return [{ ndc: productNdcOf(id.ndc) }];
389
+ if (id.emaProductNumber !== undefined)
390
+ return [{ emaProductNumber: id.emaProductNumber.trim() }];
391
+ if (id.splSetId !== undefined)
392
+ return [{ splSetId: id.splSetId.trim() }];
393
+ return [];
394
+ }
395
+ export function lookupRegulatoryIds(id, headers) {
396
+ return lookup("regulatorycore", regulatoryLookupItems(id), headers);
397
+ }
398
+ // ---------------------------------------------------------------------------
399
+ // PatentCore
400
+ // ---------------------------------------------------------------------------
401
+ const PatentBaseSchema = z.object({
402
+ amassId: z.string(),
403
+ publicationNumber: z.string().nullable(),
404
+ url: z.string().nullable(),
405
+ applicationNumber: z.string().nullable(),
406
+ countryCode: z.string().nullable(),
407
+ kindCode: z.string().nullable(),
408
+ familyId: z.string().nullable(),
409
+ familyMembers: z.array(z.string()),
410
+ title: z.string().nullable(),
411
+ abstract: z.string().nullable(),
412
+ language: z.string().nullable(),
413
+ nonEnglishFallback: z.boolean().nullable(),
414
+ cpcCodes: z.array(z.string()),
415
+ inventors: z.array(z.string()),
416
+ assignees: z.array(z.string()),
417
+ publicationDate: z.string().nullable(),
418
+ filingDate: z.string().nullable(),
419
+ grantDate: z.string().nullable(),
420
+ priorityDate: z.string().nullable(),
421
+ citedPatents: z.array(z.string()),
422
+ hasClaims: z.boolean().nullable(),
423
+ hasDescription: z.boolean().nullable(),
424
+ nplCount: z.number().nullable(),
425
+ citedByCount: z.number().nullable(),
426
+ claims: z.string().nullable().optional(),
427
+ description: z.string().nullable().optional(),
428
+ });
429
+ export const PatentSearchResponseSchema = z.object({ data: z.array(PatentBaseSchema) });
430
+ export const PatentRecordResponseSchema = z.object({ data: PatentBaseSchema });
431
+ export function mapPatentSummary(record) {
432
+ return {
433
+ amassId: record.amassId,
434
+ publicationNumber: record.publicationNumber,
435
+ applicationNumber: record.applicationNumber,
436
+ countryCode: record.countryCode,
437
+ kindCode: record.kindCode,
438
+ familyId: record.familyId,
439
+ familyMemberCount: record.familyMembers.length,
440
+ title: record.title,
441
+ abstract: record.abstract,
442
+ assignees: record.assignees,
443
+ inventors: record.inventors,
444
+ cpcCodes: record.cpcCodes,
445
+ publicationDate: record.publicationDate,
446
+ filingDate: record.filingDate,
447
+ grantDate: record.grantDate,
448
+ priorityDate: record.priorityDate,
449
+ citedPatentCount: record.citedPatents.length,
450
+ citedByCount: record.citedByCount,
451
+ nplCount: record.nplCount,
452
+ hasClaims: record.hasClaims,
453
+ hasDescription: record.hasDescription,
454
+ url: record.url,
455
+ };
456
+ }
457
+ export function mapPatentRecord(record) {
458
+ return {
459
+ ...mapPatentSummary(record),
460
+ familyMembers: record.familyMembers,
461
+ language: record.language,
462
+ nonEnglishFallback: record.nonEnglishFallback,
463
+ ...(record.claims === undefined ? {} : { claims: record.claims }),
464
+ ...(record.description === undefined ? {} : { description: record.description }),
465
+ };
466
+ }
467
+ /** PatentCore reads a list filter as one comma-separated value, not as a repeated parameter. */
468
+ function commaList(values) {
469
+ return values && values.length > 0 ? values.join(",") : undefined;
470
+ }
471
+ export function searchPatentRecords(params, headers) {
472
+ const url = urlOf("/cores/patentcore/records", {
473
+ ...params,
474
+ assignee: commaList(params.assignee),
475
+ cpcCodes: commaList(params.cpcCodes),
476
+ countryCode: commaList(params.countryCode),
477
+ });
478
+ return amassFetch(url, PatentSearchResponseSchema, headers).map((response) => (response?.data ?? []).map(mapPatentSummary));
479
+ }
480
+ export function getPatentRecord(amassId, include, headers) {
481
+ const includes = [...(include.claims ? ["claims"] : []), ...(include.description ? ["description"] : [])];
482
+ const url = urlOf(`/cores/patentcore/records/${encodeURIComponent(amassId)}`, { include: includes });
483
+ return amassFetch(url, PatentRecordResponseSchema, headers).map((response) => (response ? mapPatentRecord(response.data) : null));
484
+ }
485
+ export function lookupPatentIds(id, headers) {
486
+ const items = id.publicationNumber !== undefined
487
+ ? [{ publicationNumber: id.publicationNumber.trim() }]
488
+ : id.applicationNumber !== undefined
489
+ ? [{ applicationNumber: id.applicationNumber.trim() }]
490
+ : [];
491
+ return lookup("patentcore", items, headers);
492
+ }
@@ -0,0 +1,2 @@
1
+ export declare const AMASS_BASE = "https://api.amass.tech/api/v1";
2
+ export declare function getAmassHeaders(apiKey: string): Record<string, string>;
@@ -0,0 +1,10 @@
1
+ export const AMASS_BASE = "https://api.amass.tech/api/v1";
2
+ export function getAmassHeaders(apiKey) {
3
+ if (!apiKey) {
4
+ throw new Error("AMASS_API_KEY environment variable is not set. " + "Obtain a key from https://platform.amass.tech/api-keys");
5
+ }
6
+ return {
7
+ Authorization: `Bearer ${apiKey}`,
8
+ Accept: "application/json",
9
+ };
10
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inflexa-ai/harness",
3
- "version": "0.42.0",
3
+ "version": "0.42.2",
4
4
  "description": "Host-agnostic bioinformatics agent harness — hand-rolled agent loop, DBOS-durable workflows, sandboxed compute.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",