hivenet 0.6.0 → 0.7.1

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.
@@ -10,7 +10,7 @@ import {
10
10
  require_arg,
11
11
  validateDescriptor,
12
12
  validateFeedbackEvent
13
- } from "./chunk-DNR6HNJD.js";
13
+ } from "./chunk-A3UY57EQ.js";
14
14
 
15
15
  // src/doctor.ts
16
16
  var import_arg = __toESM(require_arg(), 1);
@@ -21,6 +21,7 @@ async function runDoctor(argv, cliVersion) {
21
21
  "--help": Boolean,
22
22
  "--to": String,
23
23
  "--dsn": String,
24
+ "--registry": String,
24
25
  "--smoke": Boolean,
25
26
  "--json": Boolean,
26
27
  "-h": "--help",
@@ -49,7 +50,10 @@ async function runDoctor(argv, cliVersion) {
49
50
  }
50
51
  } else {
51
52
  const to = args["--to"].trim();
52
- const descriptorUrl = to.includes(".") ? `https://${to}${WELL_KNOWN_PATH}` : `${registryUrl()}/v1/registry/${to}`;
53
+ if (args["--registry"] !== void 0 && (!args["--registry"].trim() || to.includes("."))) {
54
+ throw new FeedbackError("Use --registry with --to <project-slug>, not a domain.");
55
+ }
56
+ const descriptorUrl = to.includes(".") ? `https://${to}${WELL_KNOWN_PATH}` : `${registryUrl(args["--registry"] !== void 0 ? { HIVENET_REGISTRY: args["--registry"] } : process.env)}/v1/registry/${encodeURIComponent(to)}`;
53
57
  const result = await checkDescriptor(descriptorUrl);
54
58
  checks.push(result.check);
55
59
  descriptor = result.descriptor;
@@ -200,6 +204,7 @@ function printDoctorHelp() {
200
204
  Options
201
205
  --to, -t <slug|domain> Registered slug, or a domain serving ${WELL_KNOWN_PATH}
202
206
  --dsn <url> Explicit DSN: https://hv_pub_KEY@host/slug
207
+ --registry <origin> Check this server's registry with --to <slug>
203
208
  --smoke Also send one disposable, clearly-labeled event
204
209
  --json Machine-readable result
205
210
  --help, -h This help
package/dist/index.cjs CHANGED
@@ -33,10 +33,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
33
33
  ));
34
34
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
35
35
 
36
- // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.26_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js
36
+ // ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.30_typescript@5.9.3_yaml@2.9.1/node_modules/tsup/assets/cjs_shims.js
37
37
  var getImportMetaUrl, importMetaUrl;
38
38
  var init_cjs_shims = __esm({
39
- "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.26_typescript@5.9.3/node_modules/tsup/assets/cjs_shims.js"() {
39
+ "../../node_modules/.pnpm/tsup@8.5.1_jiti@2.7.0_postcss@8.5.30_typescript@5.9.3_yaml@2.9.1/node_modules/tsup/assets/cjs_shims.js"() {
40
40
  "use strict";
41
41
  getImportMetaUrl = () => typeof document === "undefined" ? new URL(`file:${__filename}`).href : document.currentScript && document.currentScript.tagName.toUpperCase() === "SCRIPT" ? document.currentScript.src : new URL("main.js", document.baseURI).href;
42
42
  importMetaUrl = /* @__PURE__ */ getImportMetaUrl();
@@ -548,6 +548,7 @@ __export(index_exports, {
548
548
  resolveThreadId: () => resolveThreadId,
549
549
  sendFeedback: () => sendFeedback,
550
550
  sentimentSchema: () => sentimentSchema,
551
+ threadSchema: () => threadSchema,
551
552
  validateDescriptor: () => validateDescriptor,
552
553
  validateFeedbackEvent: () => validateFeedbackEvent,
553
554
  validateIngestResponse: () => validateIngestResponse
@@ -15270,6 +15271,157 @@ function mediaTypeForFilename(filename) {
15270
15271
  const extension = /\.([^./\\]+)$/.exec(filename)?.[1]?.toLowerCase();
15271
15272
  return extension && MEDIA_TYPES[extension] || "application/octet-stream";
15272
15273
  }
15274
+ var evalCriterionIdSchema = external_exports.string().regex(
15275
+ /^[a-z][a-z0-9_]{0,47}$/,
15276
+ "Criterion ids are snake_case: a lowercase letter, then lowercase letters, digits or _ (at most 48)."
15277
+ );
15278
+ var weightSchema = external_exports.number().positive().max(100).optional();
15279
+ var evalJudgeCriterionSchema = external_exports.object({
15280
+ id: evalCriterionIdSchema,
15281
+ kind: external_exports.literal("judge"),
15282
+ description: external_exports.string().trim().min(1).max(2e3).describe("The yes/no question the judge answers about the result."),
15283
+ weight: weightSchema
15284
+ });
15285
+ var evalCommandCriterionSchema = external_exports.object({
15286
+ id: evalCriterionIdSchema,
15287
+ kind: external_exports.literal("command"),
15288
+ description: external_exports.string().trim().min(1).max(500).describe("What the check proves, for people reading results."),
15289
+ command: external_exports.string().trim().min(1).max(4e3),
15290
+ contains: external_exports.string().min(1).max(1e3).optional().describe("Stdout must contain this text."),
15291
+ regex: external_exports.string().min(1).max(500).optional().describe("Stdout must match this regex."),
15292
+ timeout_sec: external_exports.number().int().min(1).max(600).optional(),
15293
+ weight: weightSchema
15294
+ }).refine((criterion) => !(criterion.contains && criterion.regex), {
15295
+ message: "A command criterion takes contains or regex, not both."
15296
+ });
15297
+ var evalCriterionSchema = external_exports.discriminatedUnion("kind", [
15298
+ evalJudgeCriterionSchema,
15299
+ evalCommandCriterionSchema
15300
+ ]);
15301
+ var evalJudgeSchema = external_exports.object({
15302
+ model: external_exports.string().trim().min(1).max(128).default("anthropic/claude-sonnet-5"),
15303
+ /** Give the judge the agent's ATIF trajectory, e.g. to grade which tool or skill it used. */
15304
+ reads_trajectory: external_exports.boolean().default(false),
15305
+ /** Workspace files the judge reads, relative to the workdir. */
15306
+ files: external_exports.array(external_exports.string().trim().min(1).max(256)).max(20).default([])
15307
+ });
15308
+ var ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/;
15309
+ var SINGLE_LINE = /^[^\r\n]+$/;
15310
+ var evalMcpServerSchema = external_exports.object({
15311
+ name: external_exports.string().regex(/^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/),
15312
+ transport: external_exports.enum(["stdio", "sse", "streamable-http"]),
15313
+ url: external_exports.url().max(2e3).optional(),
15314
+ command: external_exports.string().trim().min(1).max(500).optional(),
15315
+ args: external_exports.array(external_exports.string().max(500)).max(30).default([])
15316
+ });
15317
+ var evalEnvironmentSchema = external_exports.object({
15318
+ /** The base image, e.g. `node:22-slim`. */
15319
+ image: external_exports.string().trim().regex(/^[A-Za-z0-9][A-Za-z0-9._\-/:@]{0,255}$/, "image must be a Docker image reference."),
15320
+ /** Dockerfile RUN lines, cached in the image: toolchains, not the product under test. */
15321
+ build: external_exports.array(external_exports.string().trim().regex(SINGLE_LINE).max(2e3)).max(30).default([]),
15322
+ /**
15323
+ * Runs before every trial, so each one tests the current product. Harbor runs it as the
15324
+ * healthcheck and retries it on failure, so it must be safe to run twice.
15325
+ */
15326
+ setup: external_exports.object({
15327
+ command: external_exports.string().trim().min(1).max(8e3),
15328
+ timeout_sec: external_exports.number().int().min(10).max(3600).default(600)
15329
+ }).nullable().default(null),
15330
+ workdir: external_exports.string().regex(/^\/[A-Za-z0-9._\-/]{0,255}$/).default("/app"),
15331
+ network: external_exports.object({
15332
+ mode: external_exports.enum(["public", "allowlist", "no-network"]).default("public"),
15333
+ allowed_hosts: external_exports.array(external_exports.string().trim().min(1).max(253)).max(50).default([])
15334
+ }).prefault({}),
15335
+ /** Values reach setup and the agent; secret-like names take only `${VAR}` templates. */
15336
+ env: external_exports.record(external_exports.string().regex(ENV_NAME), external_exports.string().max(2e3)).default({}),
15337
+ mcp_servers: external_exports.array(evalMcpServerSchema).max(10).default([])
15338
+ });
15339
+ var evalAgentSchema = external_exports.object({
15340
+ timeout_sec: external_exports.number().int().min(30).max(7200).default(900)
15341
+ });
15342
+ var evalVersionFieldsSchema = external_exports.object({
15343
+ /** The task in the owner's words. Never the reporter's `actual` or `mistake`. */
15344
+ instruction: external_exports.string().trim().min(1).max(2e4),
15345
+ /** What "done" means, in one or two sentences. */
15346
+ end_goal: external_exports.string().trim().min(1).max(2e3).optional(),
15347
+ criteria: external_exports.array(evalCriterionSchema).min(1).max(30),
15348
+ judge: evalJudgeSchema.optional(),
15349
+ environment: evalEnvironmentSchema,
15350
+ agent: evalAgentSchema.prefault({}),
15351
+ /** A reference solution for Harbor's `oracle` agent, as a bash script. */
15352
+ solution: external_exports.string().trim().min(1).max(2e4).optional(),
15353
+ change_note: external_exports.string().trim().min(1).max(500).optional()
15354
+ });
15355
+ var evalVersionInputSchema = evalVersionFieldsSchema.refine(
15356
+ (input) => new Set(input.criteria.map((c) => c.id)).size === input.criteria.length,
15357
+ { message: "Criterion ids must be unique.", path: ["criteria"] }
15358
+ );
15359
+ var HARBOR_ID = external_exports.string().regex(/^[0-9a-f-]{36}$/i, "Harbor ids are UUIDs.");
15360
+ var boundedRecord = (max) => external_exports.record(external_exports.string().min(1).max(200), external_exports.number()).refine((record2) => Object.keys(record2).length <= max, `At most ${max} keys.`);
15361
+ var evalTrialScenarioSchema = external_exports.object({
15362
+ /** Folders the job mounted (`harbor run --mounts`), by name: the path stays on the machine. */
15363
+ mounts: external_exports.array(external_exports.string().min(1).max(200)).max(5).optional(),
15364
+ /** `--skill` */
15365
+ skills: external_exports.array(external_exports.string().min(1).max(200)).max(20).optional(),
15366
+ /** `--mcp-config` */
15367
+ mcp_servers: external_exports.array(external_exports.string().min(1).max(100)).max(20).optional(),
15368
+ /** `--extra-instruction` text, or the file `--extra-instruction-path` named. */
15369
+ instructions: external_exports.array(external_exports.string().min(1).max(500)).max(5).optional(),
15370
+ /** `--ak` and `--ae`, without secret-looking names. */
15371
+ agent_options: external_exports.record(external_exports.string().min(1).max(64), external_exports.string().max(200)).refine((record2) => Object.keys(record2).length <= 20, "At most 20 agent options.").optional()
15372
+ });
15373
+ var evalTrialCiSchema = external_exports.object({
15374
+ provider: external_exports.literal("github"),
15375
+ /** `owner/name` */
15376
+ repository: external_exports.string().min(1).max(200),
15377
+ /** The commit under test: a pull request's head, else the commit that triggered the run. */
15378
+ commit: external_exports.string().regex(/^[0-9a-f]{7,64}$/i).optional(),
15379
+ branch: external_exports.string().min(1).max(250).optional(),
15380
+ pull_request: external_exports.number().int().positive().optional(),
15381
+ /** What started the workflow: pull_request, push, schedule, workflow_dispatch… */
15382
+ event: external_exports.string().min(1).max(50).optional(),
15383
+ run_url: external_exports.url({ protocol: /^https?$/ }).max(500).optional()
15384
+ });
15385
+ var evalTrialUploadSchema = external_exports.object({
15386
+ harbor_trial_id: HARBOR_ID,
15387
+ harbor_job_id: HARBOR_ID,
15388
+ harbor_job_name: external_exports.string().max(200).optional(),
15389
+ /** Set on a regrade: the trial it derives from. */
15390
+ source_harbor_trial_id: HARBOR_ID.optional(),
15391
+ /** `[task].name` from the trial's result: `<project slug>/<eval id>` for Hivenet tasks. */
15392
+ task_name: external_exports.string().min(1).max(300),
15393
+ /** `task.digest` from the trial's lock.json. */
15394
+ task_digest: external_exports.string().regex(/^sha256:[0-9a-f]{64}$/),
15395
+ agent: external_exports.string().min(1).max(100),
15396
+ agent_version: external_exports.string().max(100).optional(),
15397
+ model: external_exports.string().max(200).optional(),
15398
+ /** The reasoning effort it ran at (`harbor run --ak reasoning_effort=…`): part of the lane. */
15399
+ effort: external_exports.string().min(1).max(40).optional(),
15400
+ harbor_version: external_exports.string().max(50).optional(),
15401
+ exception: external_exports.object({ type: external_exports.string().min(1).max(200), message: external_exports.string().max(2e3) }).optional(),
15402
+ /** reward.json. Absent when the verifier wrote nothing: the trial wasn't graded. */
15403
+ rewards: boundedRecord(32).optional(),
15404
+ /** reward-details.json leaves, keyed by criterion id. */
15405
+ criteria: boundedRecord(64).optional(),
15406
+ metrics: external_exports.object({
15407
+ input_tokens: external_exports.number().int().nonnegative(),
15408
+ output_tokens: external_exports.number().int().nonnegative(),
15409
+ cache_tokens: external_exports.number().int().nonnegative(),
15410
+ cost_usd: external_exports.number().nonnegative(),
15411
+ setup_sec: external_exports.number().nonnegative(),
15412
+ agent_sec: external_exports.number().nonnegative(),
15413
+ verifier_sec: external_exports.number().nonnegative()
15414
+ }).partial().optional(),
15415
+ started_at: external_exports.iso.datetime({ offset: true }).optional(),
15416
+ finished_at: external_exports.iso.datetime({ offset: true }).optional(),
15417
+ scenario: evalTrialScenarioSchema.optional(),
15418
+ ci: evalTrialCiSchema.optional()
15419
+ });
15420
+ var MAX_TRIALS_PER_PUSH = 200;
15421
+ var MAX_TRAJECTORY_BYTES = 8 * 1024 * 1024;
15422
+ var evalResultsUploadSchema = external_exports.object({
15423
+ trials: external_exports.array(evalTrialUploadSchema).min(1).max(MAX_TRIALS_PER_PUSH)
15424
+ });
15273
15425
  var PROTOCOL_VERSION = 1;
15274
15426
  var MAX_FEEDBACK_LENGTH = 5e3;
15275
15427
  var MAX_SUBJECT_LENGTH = 500;
@@ -15372,8 +15524,12 @@ var metadataSchema = external_exports.record(
15372
15524
  external_exports.string().min(1).max(40),
15373
15525
  external_exports.union([external_exports.string().max(200), external_exports.number().finite(), external_exports.boolean()])
15374
15526
  ).refine((value) => Object.keys(value).length <= 20, { message: "at most 20 metadata keys" });
15527
+ var threadSchema = external_exports.object({ id: external_exports.string().regex(THREAD_ID_PATTERN) });
15528
+ function newThreadId() {
15529
+ return crypto.randomUUID().replaceAll("-", "");
15530
+ }
15375
15531
  var feedbackEventSchema = external_exports.object({
15376
- v: external_exports.literal(PROTOCOL_VERSION),
15532
+ v: external_exports.literal(PROTOCOL_VERSION).default(PROTOCOL_VERSION),
15377
15533
  /** Tenant slug or domain this feedback is addressed to. Optional when the write key implies it. */
15378
15534
  to: external_exports.string().max(253).optional(),
15379
15535
  feedback: external_exports.string().min(1).max(MAX_FEEDBACK_LENGTH),
@@ -15381,7 +15537,7 @@ var feedbackEventSchema = external_exports.object({
15381
15537
  subject: external_exports.string().min(1).max(MAX_SUBJECT_LENGTH).optional(),
15382
15538
  sentiment: sentimentSchema.optional(),
15383
15539
  author: authorSchema.optional(),
15384
- thread: external_exports.object({ id: external_exports.string().regex(THREAD_ID_PATTERN) }),
15540
+ thread: threadSchema.optional().transform((thread) => thread ?? { id: newThreadId() }),
15385
15541
  eval: evalReportSchema.optional(),
15386
15542
  /** Id of an owner/system question this submission answers (from a prior response's `ask`). */
15387
15543
  question: external_exports.string().max(64).optional(),
@@ -15394,8 +15550,8 @@ var feedbackEventSchema = external_exports.object({
15394
15550
  client: external_exports.object({
15395
15551
  name: external_exports.string().max(64),
15396
15552
  version: external_exports.string().max(32)
15397
- }),
15398
- consent: external_exports.object({ telemetry: external_exports.boolean() })
15553
+ }).default({ name: "http", version: "0" }),
15554
+ consent: external_exports.object({ telemetry: external_exports.boolean() }).default({ telemetry: false })
15399
15555
  });
15400
15556
  var knownIssueSchema = external_exports.object({
15401
15557
  title: external_exports.string().max(120),
@@ -15487,7 +15643,7 @@ function validateIngestResponse(input) {
15487
15643
  return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15488
15644
  }
15489
15645
  function feedbackEventJsonSchema() {
15490
- return external_exports.toJSONSchema(feedbackEventSchema);
15646
+ return external_exports.toJSONSchema(feedbackEventSchema, { io: "input" });
15491
15647
  }
15492
15648
  function descriptorJsonSchema() {
15493
15649
  return external_exports.toJSONSchema(descriptorSchema);
@@ -16130,7 +16286,7 @@ function inferAuthor(context) {
16130
16286
  }
16131
16287
  var CONFIG_FILE_NAME = "feedback.config.json";
16132
16288
  var DESCRIPTOR_CACHE_TTL_MS = 60 * 60 * 1e3;
16133
- var RESOLVE_TIMEOUT_MS = 5e3;
16289
+ var RESOLVE_TIMEOUT_MS = 1e4;
16134
16290
  function parseDsn(dsn) {
16135
16291
  let url2;
16136
16292
  try {
@@ -16246,7 +16402,17 @@ function destinationFromDescriptor(descriptor, to) {
16246
16402
  };
16247
16403
  }
16248
16404
  function registryUrl(env = process.env) {
16249
- return env[REGISTRY_ENV_VAR]?.trim() || DEFAULT_REGISTRY_URL;
16405
+ const value = env[REGISTRY_ENV_VAR]?.trim() || DEFAULT_REGISTRY_URL;
16406
+ try {
16407
+ const url2 = new URL(value);
16408
+ if (!["http:", "https:"].includes(url2.protocol) || url2.username || url2.password || url2.search || url2.hash || url2.pathname !== "/" || !/^[a-z0-9._:[\]-]+$/i.test(url2.hostname))
16409
+ throw new Error("Not an origin");
16410
+ return url2.origin;
16411
+ } catch {
16412
+ throw new FeedbackError(
16413
+ "Registry must be an HTTP(S) origin, such as https://feedback.example.com, without credentials, a path, query or fragment."
16414
+ );
16415
+ }
16250
16416
  }
16251
16417
  async function resolveDestination(options = {}) {
16252
16418
  const env = options.env ?? process.env;
@@ -16255,6 +16421,19 @@ async function resolveDestination(options = {}) {
16255
16421
  if (options.dsn) {
16256
16422
  return parseDsn(options.dsn);
16257
16423
  }
16424
+ if (options.registry !== void 0) {
16425
+ const to2 = options.to?.trim();
16426
+ if (!options.registry.trim() || !to2 || to2.includes(".")) {
16427
+ throw new FeedbackError("Use --registry with --to <project-slug>, not a domain.");
16428
+ }
16429
+ const origin = registryUrl({ [REGISTRY_ENV_VAR]: options.registry });
16430
+ return resolveViaDescriptor(
16431
+ `${origin}/v1/registry/${encodeURIComponent(to2)}`,
16432
+ to2,
16433
+ fetchImpl,
16434
+ cacheDir
16435
+ );
16436
+ }
16258
16437
  const envDsn = env[DSN_ENV_VAR]?.trim();
16259
16438
  if (envDsn) {
16260
16439
  return parseDsn(envDsn);
@@ -16317,23 +16496,24 @@ async function postEvent(options) {
16317
16496
  body = void 0;
16318
16497
  }
16319
16498
  const parsed = validateIngestResponse(body);
16320
- return {
16321
- kind: "accepted",
16322
- response: parsed.ok ? parsed.data : { id: "unknown", thread: options.event.thread.id }
16323
- };
16499
+ return parsed.ok ? { kind: "accepted", response: parsed.data } : { kind: "unreachable", error: "Server returned an invalid delivery receipt." };
16324
16500
  }
16325
16501
  var OUTBOX_FILE = "outbox.jsonl";
16326
- var MAX_OUTBOX_ENTRIES = 100;
16502
+ var MAX_FLUSH_ENTRIES = 100;
16327
16503
  var STALE_CLAIM_MS = 30 * 60 * 1e3;
16328
16504
  function outboxPath(env = process.env) {
16329
16505
  return import_path6.default.join(feedbackHomeDir(env), OUTBOX_FILE);
16330
16506
  }
16331
16507
  function appendToOutbox(entry, env = process.env) {
16508
+ return appendEntriesToOutbox([entry], env);
16509
+ }
16510
+ function appendEntriesToOutbox(entries, env) {
16511
+ if (entries.length === 0) return true;
16332
16512
  try {
16333
16513
  const file2 = outboxPath(env);
16334
16514
  (0, import_fs3.mkdirSync)(import_path6.default.dirname(file2), { recursive: true });
16335
- (0, import_fs3.appendFileSync)(file2, `${JSON.stringify(entry)}
16336
- `);
16515
+ (0, import_fs3.appendFileSync)(file2, entries.map((entry) => `${JSON.stringify(entry)}
16516
+ `).join(""));
16337
16517
  return true;
16338
16518
  } catch {
16339
16519
  return false;
@@ -16375,24 +16555,30 @@ function claimQueue(env) {
16375
16555
  async function flushOutbox(options = {}) {
16376
16556
  const env = options.env ?? process.env;
16377
16557
  const claims = claimQueue(env);
16378
- const entries = claims.flatMap(readEntries).slice(-MAX_OUTBOX_ENTRIES);
16558
+ const entries = claims.flatMap(readEntries);
16559
+ const deadline = Date.now() + (options.maxDurationMs ?? Number.POSITIVE_INFINITY);
16379
16560
  const failures = [];
16561
+ let attempted = 0;
16380
16562
  let delivered = 0;
16381
- for (const entry of entries) {
16563
+ for (const entry of entries.slice(0, MAX_FLUSH_ENTRIES)) {
16564
+ const timeLeft = deadline - Date.now();
16565
+ if (timeLeft <= 0) break;
16566
+ attempted++;
16382
16567
  const result = await postEvent({
16383
16568
  endpoint: entry.endpoint,
16384
16569
  ...entry.writeKey ? { writeKey: entry.writeKey } : {},
16385
16570
  event: entry.event,
16386
- timeoutMs: options.timeoutMs ?? 5e3,
16571
+ timeoutMs: Math.max(1, Math.floor(Math.min(options.timeoutMs ?? 5e3, timeLeft))),
16387
16572
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
16388
16573
  });
16389
- if (result.kind === "accepted" || result.kind === "rejected") {
16390
- delivered += result.kind === "accepted" ? 1 : 0;
16574
+ if (result.kind === "accepted") {
16575
+ delivered++;
16391
16576
  } else {
16392
16577
  failures.push(entry);
16393
16578
  }
16394
16579
  }
16395
- if (failures.every((entry) => appendToOutbox(entry, env))) {
16580
+ const remaining = entries.slice(attempted).concat(failures);
16581
+ if (appendEntriesToOutbox(remaining, env)) {
16396
16582
  for (const claim of claims) {
16397
16583
  try {
16398
16584
  (0, import_fs3.rmSync)(claim, { force: true });
@@ -16400,16 +16586,22 @@ async function flushOutbox(options = {}) {
16400
16586
  }
16401
16587
  }
16402
16588
  }
16403
- return { delivered, remaining: failures.length };
16589
+ return { delivered, remaining: remaining.length };
16404
16590
  }
16405
16591
  async function resolveApiOrigin(options) {
16406
- if (!options.to && !options.dsn) {
16407
- return new URL(DEFAULT_REGISTRY_URL).origin;
16592
+ const env = options.env ?? process.env;
16593
+ if (options.registry !== void 0 && !options.to && !options.dsn) {
16594
+ if (!options.registry.trim()) throw new FeedbackError("Registry must not be empty.");
16595
+ return registryUrl({ [REGISTRY_ENV_VAR]: options.registry });
16596
+ }
16597
+ if (!options.to && !options.dsn && !env[DSN_ENV_VAR]?.trim()) {
16598
+ return registryUrl(env);
16408
16599
  }
16409
16600
  const destination = await resolveDestination({
16410
16601
  ...options.to !== void 0 ? { to: options.to } : {},
16411
16602
  ...options.dsn !== void 0 ? { dsn: options.dsn } : {},
16412
- ...options.env ? { env: options.env } : {},
16603
+ ...options.registry !== void 0 ? { registry: options.registry } : {},
16604
+ env,
16413
16605
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
16414
16606
  });
16415
16607
  return new URL(destination.endpoint).origin;
@@ -16418,12 +16610,12 @@ function ownerApi(options) {
16418
16610
  const origin = (options.apiOrigin ?? new URL(DEFAULT_REGISTRY_URL).origin).replace(/\/$/, "");
16419
16611
  const fetchImpl = options.fetchImpl ?? fetch;
16420
16612
  const timeoutMs = options.timeoutMs ?? 15e3;
16421
- async function request(path6, init) {
16613
+ async function request(path6, init, timeout = timeoutMs) {
16422
16614
  let response;
16423
16615
  try {
16424
16616
  response = await fetchImpl(`${origin}${path6}`, {
16425
16617
  ...init,
16426
- signal: AbortSignal.timeout(timeoutMs),
16618
+ signal: AbortSignal.timeout(timeout),
16427
16619
  headers: {
16428
16620
  authorization: `Bearer ${options.secretKey}`,
16429
16621
  ...init?.body ? { "content-type": "application/json" } : {},
@@ -16556,6 +16748,33 @@ function ownerApi(options) {
16556
16748
  body: JSON.stringify({ status, ...note !== void 0 ? { note } : {} })
16557
16749
  });
16558
16750
  },
16751
+ /** Draft a runnable version of an eval (a person approves it in the dashboard). */
16752
+ async draftEvalVersion(id, fields) {
16753
+ return request(`/v1/evals/${encodeURIComponent(id)}/versions`, {
16754
+ method: "POST",
16755
+ body: JSON.stringify(fields)
16756
+ });
16757
+ },
16758
+ /** Compiled Harbor tasks: every accepted eval's current version, or the given refs. */
16759
+ async pullEvalTasks(refs = []) {
16760
+ const query = refs.map((ref) => `ref=${encodeURIComponent(ref)}`).join("&");
16761
+ return request(`/v1/evals/tasks${query ? `?${query}` : ""}`);
16762
+ },
16763
+ /** Harbor trials read from a job directory, with their grades. */
16764
+ async pushEvalResults(trials) {
16765
+ return request("/v1/evals/results", {
16766
+ method: "POST",
16767
+ body: JSON.stringify({ trials })
16768
+ });
16769
+ },
16770
+ /** A stored trial's ATIF trajectory (`agent/trajectory.json`), as read from disk. */
16771
+ async pushTrialTrajectory(harborTrialId, trajectory) {
16772
+ return request(
16773
+ `/v1/evals/trials/${encodeURIComponent(harborTrialId)}/trajectory`,
16774
+ { method: "PUT", body: trajectory },
16775
+ Math.max(timeoutMs, 6e4)
16776
+ );
16777
+ },
16559
16778
  /** Attach an external link (Slack message, Linear/GitHub issue, PR) to evt_… or iss_…. */
16560
16779
  async addLink(id, url2, label) {
16561
16780
  const path6 = id.startsWith("iss_") ? `/v1/issues/${id}` : `/v1/events/${id}`;
@@ -16595,6 +16814,7 @@ async function prepareFeedback(options) {
16595
16814
  const resolveOptions = {
16596
16815
  ...options.to !== void 0 ? { to: options.to } : {},
16597
16816
  ...options.dsn !== void 0 ? { dsn: options.dsn } : {},
16817
+ ...options.registry !== void 0 ? { registry: options.registry } : {},
16598
16818
  cwd,
16599
16819
  env,
16600
16820
  ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
@@ -16764,6 +16984,7 @@ async function sendFeedback(options) {
16764
16984
  resolveThreadId,
16765
16985
  sendFeedback,
16766
16986
  sentimentSchema,
16987
+ threadSchema,
16767
16988
  validateDescriptor,
16768
16989
  validateFeedbackEvent,
16769
16990
  validateIngestResponse