hivenet 0.4.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -496,379 +496,9 @@ init_esm_shims();
496
496
 
497
497
  // ../core/dist/index.js
498
498
  init_esm_shims();
499
- import { readFileSync } from "fs";
500
- import path22 from "path";
501
-
502
- // ../../node_modules/.pnpm/agent-cli-detector@0.1.5/node_modules/agent-cli-detector/dist/index.js
503
- init_esm_shims();
504
- import { execFileSync } from "child_process";
505
- import path2 from "path";
506
- var defaultAgents = [
507
- {
508
- id: "codex",
509
- name: "Codex",
510
- env: [
511
- { name: "CODEX_CI", value: "1" },
512
- { name: "CODEX_SHELL", value: "1" },
513
- { name: "CODEX_THREAD_ID" }
514
- ],
515
- process: [{ pattern: /^codex$/i }],
516
- sessionEnv: ["CODEX_THREAD_ID"]
517
- },
518
- {
519
- id: "cursor",
520
- name: "Cursor",
521
- env: [
522
- { name: "CURSOR_AGENT", value: "1" },
523
- { name: "CURSOR_CONVERSATION_ID" }
524
- ],
525
- process: [{ pattern: /^cursor$/i }],
526
- sessionEnv: ["CURSOR_CONVERSATION_ID"]
527
- },
528
- {
529
- id: "claude-code",
530
- name: "Claude Code",
531
- env: [
532
- { name: "CLAUDECODE", value: "1" },
533
- { name: "CLAUDE_CODE_SESSION_ID" }
534
- ],
535
- process: [{ pattern: /^claude$/i }],
536
- sessionEnv: ["CLAUDE_CODE_SESSION_ID"]
537
- },
538
- {
539
- id: "opencode",
540
- name: "OpenCode",
541
- env: [
542
- { name: "OPENCODE", value: "1" },
543
- { name: "OPENCODE_PID" }
544
- ],
545
- process: [{ pattern: /^opencode$/i }]
546
- },
547
- {
548
- id: "devin",
549
- name: "Devin",
550
- process: [{ pattern: /devin/i }]
551
- },
552
- {
553
- id: "gemini",
554
- name: "Gemini CLI",
555
- env: [{ name: "GEMINI_CLI", value: "1" }],
556
- process: [{ pattern: /^gemini$/i }]
557
- },
558
- {
559
- id: "antigravity",
560
- name: "Antigravity",
561
- env: [
562
- { name: "ANTIGRAVITY_AGENT", value: "1" },
563
- { name: "ANTIGRAVITY_TRAJECTORY_ID" }
564
- ],
565
- process: [{ pattern: /^antigravity$/i }],
566
- sessionEnv: ["ANTIGRAVITY_TRAJECTORY_ID"]
567
- },
568
- {
569
- id: "bolt",
570
- name: "Bolt",
571
- env: [
572
- { name: "BOLT_ENV" },
573
- { name: "BOLT_ORIGIN" },
574
- { name: "BOLT_SERVER_URL" }
575
- ]
576
- },
577
- {
578
- id: "pi",
579
- name: "Pi",
580
- env: [{ name: "PI_CODING_AGENT", value: "true" }]
581
- },
582
- {
583
- id: "replit",
584
- name: "Replit",
585
- env: [{ name: "REPLIT_SESSION" }],
586
- sessionEnv: ["REPLIT_SESSION"]
587
- },
588
- {
589
- id: "rork",
590
- name: "Rork",
591
- env: [{ name: "RORK_API_URL" }]
592
- },
593
- {
594
- id: "kiro",
595
- name: "Kiro",
596
- env: [{ name: "KIRO_SESSION_ID" }],
597
- process: [{ pattern: /kiro/i }],
598
- sessionEnv: ["KIRO_SESSION_ID"]
599
- },
600
- {
601
- id: "cline",
602
- name: "Cline",
603
- env: [{ name: "CLINE_WRAPPER_PATH" }],
604
- process: [{ pattern: /^cline$/i }]
605
- },
606
- {
607
- id: "kilocode",
608
- name: "Kilo Code",
609
- env: [
610
- { name: "KILO", value: "1" },
611
- { name: "KILOCODE_VERSION" },
612
- { name: "KILO_RUN_ID" }
613
- ],
614
- process: [{ pattern: /^(kilo|kilocode)$/i }],
615
- sessionEnv: ["KILO_RUN_ID"]
616
- },
617
- {
618
- id: "copilot",
619
- name: "GitHub Copilot CLI",
620
- env: [
621
- { name: "COPILOT_CLI", value: "1" },
622
- { name: "COPILOT_AGENT_SESSION_ID" },
623
- { name: "COPILOT_RUN_APP", value: "1" }
624
- ],
625
- process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],
626
- sessionEnv: ["COPILOT_AGENT_SESSION_ID"]
627
- }
628
- ];
629
- var EnvironmentDetectionStrategy = class {
630
- name = "environment";
631
- detect(context) {
632
- const matches = [];
633
- for (const agent of context.agents) {
634
- for (const signal of agent.env ?? []) {
635
- const value = context.env[signal.name];
636
- if (value === void 0 || !matchesEnvValue(value, signal.value)) {
637
- continue;
638
- }
639
- matches.push(toEvidence(agent, signal, value));
640
- }
641
- }
642
- return matches;
643
- }
644
- };
645
- function matchesEnvValue(value, matcher) {
646
- if (matcher === void 0) {
647
- return true;
648
- }
649
- if (typeof matcher === "string") {
650
- return value === matcher;
651
- }
652
- if (matcher instanceof RegExp) {
653
- return matcher.test(value);
654
- }
655
- return matcher(value);
656
- }
657
- function toEvidence(agent, signal, value) {
658
- return {
659
- agent: {
660
- id: agent.id,
661
- name: agent.name
662
- },
663
- strategy: "environment",
664
- signal: signal.name,
665
- value
666
- };
667
- }
668
- var PsProcessReader = class {
669
- read(pid) {
670
- const command = readPsField(pid, "comm");
671
- const ppid = Number.parseInt(readPsField(pid, "ppid"), 10);
672
- if (!command || !Number.isInteger(ppid)) {
673
- return void 0;
674
- }
675
- return {
676
- pid,
677
- ppid,
678
- command: normalizeCommand(command)
679
- };
680
- }
681
- };
682
- var ProcessTreeDetectionStrategy = class {
683
- constructor(reader = new PsProcessReader()) {
684
- this.reader = reader;
685
- }
686
- reader;
687
- name = "process-tree";
688
- detect(context) {
689
- const matches = [];
690
- let currentPid = context.pid;
691
- for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {
692
- const processInfo = this.reader.read(currentPid);
693
- if (processInfo === void 0) {
694
- break;
695
- }
696
- matches.push(...matchProcess(context.agents, processInfo));
697
- if (processInfo.ppid === void 0 || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {
698
- break;
699
- }
700
- currentPid = processInfo.ppid;
701
- }
702
- return matches;
703
- }
704
- };
705
- function matchProcess(agents, processInfo) {
706
- const matches = [];
707
- const command = normalizeCommand(processInfo.command);
708
- for (const agent of agents) {
709
- for (const signal of agent.process ?? []) {
710
- if (!signal.pattern.test(command)) {
711
- continue;
712
- }
713
- matches.push(toEvidence2(agent, signal, { ...processInfo, command }));
714
- }
715
- }
716
- return matches;
717
- }
718
- function toEvidence2(agent, signal, processInfo) {
719
- return {
720
- agent: {
721
- id: agent.id,
722
- name: agent.name
723
- },
724
- strategy: "process-tree",
725
- signal: signal.pattern.toString(),
726
- value: `${processInfo.pid}:${processInfo.command}`
727
- };
728
- }
729
- function readPsField(pid, field) {
730
- try {
731
- return execFileSync("ps", ["-o", `${field}=`, "-p", String(pid)], {
732
- encoding: "utf8",
733
- stdio: ["ignore", "pipe", "ignore"]
734
- }).trim();
735
- } catch {
736
- return "";
737
- }
738
- }
739
- function normalizeCommand(command) {
740
- const baseName = path2.basename(command.trim());
741
- return baseName.replace(/^-+/, "");
742
- }
743
- function detectAgent(options = {}) {
744
- const agents = options.agents ?? defaultAgents;
745
- const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);
746
- const context = {
747
- env: options.env ?? process.env,
748
- pid: options.pid ?? process.pid,
749
- agents,
750
- maxProcessDepth: options.maxProcessDepth ?? 20
751
- };
752
- const matches = strategies.flatMap((strategy) => strategy.detect(context));
753
- const bestMatch = selectBestAgent(agents, matches, context.env);
754
- if (bestMatch === void 0) {
755
- return {
756
- detected: false
757
- };
758
- }
759
- return {
760
- detected: true,
761
- agent: bestMatch
762
- };
763
- }
764
- function defaultStrategies(experimentalProcessTree = false) {
765
- const strategies = [new EnvironmentDetectionStrategy()];
766
- if (experimentalProcessTree) {
767
- strategies.push(new ProcessTreeDetectionStrategy());
768
- }
769
- return strategies;
770
- }
771
- function selectBestAgent(agents, matches, env) {
772
- const matchedAgentIds = new Set(matches.map((match) => match.agent.id));
773
- for (const agent of agents) {
774
- if (!matchedAgentIds.has(agent.id)) {
775
- continue;
776
- }
777
- return {
778
- id: agent.id,
779
- name: agent.name,
780
- ...sessionIdProperties(agent, env)
781
- };
782
- }
783
- return void 0;
784
- }
785
- function sessionIdProperties(agent, env) {
786
- for (const envName of agent.sessionEnv ?? []) {
787
- const value = env[envName];
788
- if (value !== void 0 && value.length > 0) {
789
- return { sessionId: value };
790
- }
791
- }
792
- return {};
793
- }
794
-
795
- // ../core/dist/index.js
796
- var import_ci_info = __toESM(require_ci_info(), 1);
797
-
798
- // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/index.js
799
- init_esm_shims();
800
-
801
- // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/chunk-HZH3SATY.js
802
- init_esm_shims();
803
- var defaultSandboxes = [
804
- {
805
- id: "replit",
806
- name: "Replit",
807
- env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }]
808
- },
809
- {
810
- id: "bolt",
811
- name: "bolt.new",
812
- env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }]
813
- },
814
- {
815
- id: "e2b",
816
- name: "E2B",
817
- env: [{ name: "E2B_SANDBOX", value: "true" }]
818
- },
819
- {
820
- id: "vercel-sandbox",
821
- name: "Vercel Sandbox",
822
- env: [{ name: "HOME", value: "/home/vercel-sandbox" }]
823
- },
824
- {
825
- id: "daytona",
826
- name: "Daytona",
827
- env: [{ name: "DAYTONA_SANDBOX_ID" }]
828
- },
829
- {
830
- id: "modal",
831
- name: "Modal",
832
- env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }]
833
- },
834
- {
835
- id: "cloudflare-sandbox",
836
- name: "Cloudflare Sandbox",
837
- env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }]
838
- },
839
- {
840
- id: "codespaces",
841
- name: "GitHub Codespaces",
842
- env: [{ name: "CODESPACES", value: "true" }]
843
- },
844
- {
845
- id: "codesandbox",
846
- name: "CodeSandbox",
847
- env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }]
848
- }
849
- ];
850
- function detectSandbox(options = {}) {
851
- const env = options.env ?? process.env;
852
- const sandboxes = options.sandboxes ?? defaultSandboxes;
853
- const sandbox = sandboxes.find(
854
- (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env))
855
- );
856
- if (sandbox === void 0) return { detected: false };
857
- return {
858
- detected: true,
859
- sandbox: { id: sandbox.id, name: sandbox.name }
860
- };
861
- }
862
- function matchesEnvSignal(signal, env) {
863
- const value = env[signal.name];
864
- return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value);
865
- }
866
-
867
- // ../core/dist/index.js
868
- import { homedir } from "os";
499
+ import { createHash } from "crypto";
500
+ import { readFile, stat } from "fs/promises";
869
501
  import path3 from "path";
870
- import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
871
- import path32 from "path";
872
502
 
873
503
  // ../spec/dist/index.js
874
504
  init_esm_shims();
@@ -1652,10 +1282,10 @@ function mergeDefs(...defs) {
1652
1282
  function cloneDef(schema) {
1653
1283
  return mergeDefs(schema._zod.def);
1654
1284
  }
1655
- function getElementAtPath(obj, path5) {
1656
- if (!path5)
1285
+ function getElementAtPath(obj, path6) {
1286
+ if (!path6)
1657
1287
  return obj;
1658
- return path5.reduce((acc, key) => acc?.[key], obj);
1288
+ return path6.reduce((acc, key) => acc?.[key], obj);
1659
1289
  }
1660
1290
  function promiseAllObject(promisesObj) {
1661
1291
  const keys = Object.keys(promisesObj);
@@ -2064,11 +1694,11 @@ function explicitlyAborted(x, startIndex = 0) {
2064
1694
  }
2065
1695
  return false;
2066
1696
  }
2067
- function prefixIssues(path5, issues) {
1697
+ function prefixIssues(path6, issues) {
2068
1698
  return issues.map((iss) => {
2069
1699
  var _a3;
2070
1700
  (_a3 = iss).path ?? (_a3.path = []);
2071
- iss.path.unshift(path5);
1701
+ iss.path.unshift(path6);
2072
1702
  return iss;
2073
1703
  });
2074
1704
  }
@@ -2215,16 +1845,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2215
1845
  }
2216
1846
  function formatError(error51, mapper = (issue2) => issue2.message) {
2217
1847
  const fieldErrors = { _errors: [] };
2218
- const processError = (error52, path5 = []) => {
1848
+ const processError = (error52, path6 = []) => {
2219
1849
  for (const issue2 of error52.issues) {
2220
1850
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2221
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1851
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
2222
1852
  } else if (issue2.code === "invalid_key") {
2223
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1853
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2224
1854
  } else if (issue2.code === "invalid_element") {
2225
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1855
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2226
1856
  } else {
2227
- const fullpath = [...path5, ...issue2.path];
1857
+ const fullpath = [...path6, ...issue2.path];
2228
1858
  if (fullpath.length === 0) {
2229
1859
  fieldErrors._errors.push(mapper(issue2));
2230
1860
  } else {
@@ -2251,17 +1881,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2251
1881
  }
2252
1882
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2253
1883
  const result = { errors: [] };
2254
- const processError = (error52, path5 = []) => {
1884
+ const processError = (error52, path6 = []) => {
2255
1885
  var _a3, _b;
2256
1886
  for (const issue2 of error52.issues) {
2257
1887
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2258
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1888
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
2259
1889
  } else if (issue2.code === "invalid_key") {
2260
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1890
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2261
1891
  } else if (issue2.code === "invalid_element") {
2262
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1892
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2263
1893
  } else {
2264
- const fullpath = [...path5, ...issue2.path];
1894
+ const fullpath = [...path6, ...issue2.path];
2265
1895
  if (fullpath.length === 0) {
2266
1896
  result.errors.push(mapper(issue2));
2267
1897
  continue;
@@ -2293,8 +1923,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2293
1923
  }
2294
1924
  function toDotPath(_path) {
2295
1925
  const segs = [];
2296
- const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2297
- for (const seg of path5) {
1926
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1927
+ for (const seg of path6) {
2298
1928
  if (typeof seg === "number")
2299
1929
  segs.push(`[${seg}]`);
2300
1930
  else if (typeof seg === "symbol")
@@ -15067,13 +14697,13 @@ function resolveRef(ref, ctx) {
15067
14697
  if (!ref.startsWith("#")) {
15068
14698
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15069
14699
  }
15070
- const path5 = ref.slice(1).split("/").filter(Boolean);
15071
- if (path5.length === 0) {
14700
+ const path6 = ref.slice(1).split("/").filter(Boolean);
14701
+ if (path6.length === 0) {
15072
14702
  return ctx.rootSchema;
15073
14703
  }
15074
14704
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15075
- if (path5[0] === defsKey) {
15076
- const key = path5[1];
14705
+ if (path6[0] === defsKey) {
14706
+ const key = path6[1];
15077
14707
  if (!key || !ctx.defs[key]) {
15078
14708
  throw new Error(`Reference not found: ${ref}`);
15079
14709
  }
@@ -15478,11 +15108,107 @@ function bigint3(params) {
15478
15108
  function date4(params) {
15479
15109
  return _coercedDate(ZodDate, params);
15480
15110
  }
15481
-
15482
- // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15483
- config(en_default());
15484
-
15485
- // ../spec/dist/index.js
15111
+
15112
+ // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15113
+ config(en_default());
15114
+
15115
+ // ../spec/dist/index.js
15116
+ var MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
15117
+ var MAX_ATTACHMENTS = 10;
15118
+ var MAX_ATTACHMENT_DESCRIPTION_LENGTH = 1e3;
15119
+ var ATTACHMENT_UPLOAD_PATH = "/v1/attachments";
15120
+ var attachmentDescriptionSchema = external_exports.string().trim().min(1).max(MAX_ATTACHMENT_DESCRIPTION_LENGTH).describe("Optional plain text: what this file shows and why it matters.");
15121
+ var attachmentIdSchema = external_exports.string().regex(/^att_[a-f0-9]{32}$/);
15122
+ var ATTACHMENT_TARGET_TYPES = [
15123
+ "event",
15124
+ "issue",
15125
+ "question",
15126
+ "eval",
15127
+ "investigation",
15128
+ "investigation_question",
15129
+ "investigation_reply"
15130
+ ];
15131
+ var attachmentTargetSchema = external_exports.object({
15132
+ type: external_exports.enum(ATTACHMENT_TARGET_TYPES),
15133
+ id: external_exports.string().min(1).max(64)
15134
+ });
15135
+ var attachmentManifestSchema = external_exports.object({
15136
+ filename: external_exports.string().min(1).max(180).refine(
15137
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Reject control characters in filenames and HTTP headers.
15138
+ (name) => !/[\\/\u0000-\u001f\u007f]/.test(name) && name !== "." && name !== "..",
15139
+ "Use a filename without directories or control characters."
15140
+ ),
15141
+ mediaType: external_exports.string().max(127).regex(/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/),
15142
+ size: external_exports.number().int().min(1).max(MAX_ATTACHMENT_BYTES),
15143
+ sha256: external_exports.string().regex(/^[a-f0-9]{64}$/)
15144
+ });
15145
+ var prepareAttachmentSchema = attachmentManifestSchema.extend({
15146
+ to: external_exports.string().min(1).max(253).optional(),
15147
+ scope: external_exports.enum(["reporter", "owner"]).default("reporter"),
15148
+ /** Self-reported agent name for reporter uploads. */
15149
+ uploader: external_exports.string().min(1).max(128).optional()
15150
+ });
15151
+ var preparedAttachmentSchema = external_exports.object({
15152
+ reference: external_exports.object({
15153
+ id: attachmentIdSchema,
15154
+ token: external_exports.string().regex(/^hva_[a-f0-9]{64}$/)
15155
+ }),
15156
+ uploadUrl: external_exports.url(),
15157
+ expiresAt: external_exports.string()
15158
+ });
15159
+ var attachmentReferenceSchema = preparedAttachmentSchema.shape.reference.extend({
15160
+ description: attachmentDescriptionSchema.optional()
15161
+ });
15162
+ var attachmentReferencesSchema = external_exports.array(attachmentReferenceSchema).max(MAX_ATTACHMENTS).refine(
15163
+ (refs) => new Set(refs.map((ref) => ref.id)).size === refs.length,
15164
+ "Each file can be attached once."
15165
+ );
15166
+ var attachFilesSchema = external_exports.object({
15167
+ target: attachmentTargetSchema,
15168
+ attachments: attachmentReferencesSchema.min(1)
15169
+ });
15170
+ var attachmentSchema = attachmentManifestSchema.extend({
15171
+ id: attachmentIdSchema,
15172
+ createdAt: external_exports.string(),
15173
+ source: external_exports.enum(["reporter", "owner"]),
15174
+ /** Reporter names are self-reported; owner uploads are authenticated. */
15175
+ uploader: external_exports.string().nullable(),
15176
+ /** The record the file belongs to — a report when shown on its issue or eval. */
15177
+ target: attachmentTargetSchema,
15178
+ description: attachmentDescriptionSchema.optional()
15179
+ });
15180
+ var deliveredAttachmentSchema = attachmentSchema.extend({
15181
+ url: external_exports.url(),
15182
+ expiresAt: external_exports.string()
15183
+ });
15184
+ var MEDIA_TYPES = {
15185
+ png: "image/png",
15186
+ jpg: "image/jpeg",
15187
+ jpeg: "image/jpeg",
15188
+ gif: "image/gif",
15189
+ webp: "image/webp",
15190
+ svg: "image/svg+xml",
15191
+ mp4: "video/mp4",
15192
+ webm: "video/webm",
15193
+ mp3: "audio/mpeg",
15194
+ m4a: "audio/mp4",
15195
+ wav: "audio/wav",
15196
+ ogg: "audio/ogg",
15197
+ pdf: "application/pdf",
15198
+ json: "application/json",
15199
+ jsonl: "application/x-ndjson",
15200
+ ndjson: "application/x-ndjson",
15201
+ txt: "text/plain",
15202
+ log: "text/plain",
15203
+ md: "text/markdown",
15204
+ csv: "text/csv",
15205
+ zip: "application/zip",
15206
+ html: "text/html"
15207
+ };
15208
+ function mediaTypeForFilename(filename) {
15209
+ const extension = /\.([^./\\]+)$/.exec(filename)?.[1]?.toLowerCase();
15210
+ return extension && MEDIA_TYPES[extension] || "application/octet-stream";
15211
+ }
15486
15212
  var PROTOCOL_VERSION = 1;
15487
15213
  var MAX_FEEDBACK_LENGTH = 5e3;
15488
15214
  var MAX_SUBJECT_LENGTH = 500;
@@ -15502,10 +15228,18 @@ var FEEDBACK_CATEGORIES = [
15502
15228
  "cli",
15503
15229
  "api",
15504
15230
  "model",
15505
- "eval",
15506
15231
  "ux",
15507
15232
  "other"
15508
15233
  ];
15234
+ var createQuestionInputSchema = external_exports.object({
15235
+ prompt: external_exports.string().trim().min(8).max(500).describe('The question, e.g. "If you used <feature>: what broke or was missing?"'),
15236
+ category: external_exports.enum(FEEDBACK_CATEGORIES).optional().describe("Only ask agents whose submission carries this category."),
15237
+ attachments: attachmentReferencesSchema.optional().describe("Uploaded files, each {id, token, description?}; every agent asked can open them."),
15238
+ idempotencyKey: external_exports.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional().describe(
15239
+ "Same key + same project = the same question, so a retried publish never duplicates."
15240
+ )
15241
+ });
15242
+ var LEGACY_CATEGORIES = ["eval"];
15509
15243
  var EVENT_STATUSES = [
15510
15244
  "new",
15511
15245
  "seen",
@@ -15514,10 +15248,19 @@ var EVENT_STATUSES = [
15514
15248
  "wont_fix",
15515
15249
  "spam"
15516
15250
  ];
15517
- var ISSUE_STATUSES = ["open", "in_progress", "resolved", "wont_fix", "spam"];
15251
+ var ISSUE_STATUSES = [
15252
+ "open",
15253
+ "in_progress",
15254
+ "needs_review",
15255
+ "resolved",
15256
+ "wont_fix",
15257
+ "spam"
15258
+ ];
15259
+ var issueStatusSchema = external_exports.enum(ISSUE_STATUSES);
15518
15260
  var VENDOR_CATEGORY_PATTERN = /^x-[a-z0-9][a-z0-9-]{0,38}$/;
15519
15261
  var categorySchema = external_exports.union([
15520
15262
  external_exports.enum(FEEDBACK_CATEGORIES),
15263
+ external_exports.enum(LEGACY_CATEGORIES),
15521
15264
  external_exports.string().regex(VENDOR_CATEGORY_PATTERN, "vendor categories must match x-<kebab-case>")
15522
15265
  ]);
15523
15266
  var sentimentSchema = external_exports.enum(["positive", "negative", "neutral"]);
@@ -15579,6 +15322,10 @@ var feedbackEventSchema = external_exports.object({
15579
15322
  author: authorSchema.optional(),
15580
15323
  thread: external_exports.object({ id: external_exports.string().regex(THREAD_ID_PATTERN) }),
15581
15324
  eval: evalReportSchema.optional(),
15325
+ /** Id of an owner/system question this submission answers (from a prior response's `ask`). */
15326
+ question: external_exports.string().max(64).optional(),
15327
+ /** Uploaded files, committed with the event or not at all. Bytes never travel in feedback JSON. */
15328
+ attachments: attachmentReferencesSchema.optional(),
15582
15329
  context: feedbackContextSchema.optional(),
15583
15330
  metadata: metadataSchema.optional(),
15584
15331
  /** Client-minted retry key: same key + same project = same stored event. */
@@ -15589,6 +15336,31 @@ var feedbackEventSchema = external_exports.object({
15589
15336
  }),
15590
15337
  consent: external_exports.object({ telemetry: external_exports.boolean() })
15591
15338
  });
15339
+ var knownIssueSchema = external_exports.object({
15340
+ title: external_exports.string().max(120),
15341
+ /** wont_fix is disclosed only when the owner attached a closing note. */
15342
+ status: external_exports.enum(["open", "wont_fix"]),
15343
+ /** This very report flipped a resolved issue back open — already recorded. */
15344
+ reopened: external_exports.boolean().optional(),
15345
+ /** Total reports on this issue, including this one. */
15346
+ reports: external_exports.number().int().positive(),
15347
+ /** The owner's closing note, verbatim — their explanation of the decision. */
15348
+ note: external_exports.string().max(MAX_GUIDANCE_LENGTH).optional(),
15349
+ /** Server-composed next step for the submitting agent. */
15350
+ hint: external_exports.string().max(300)
15351
+ });
15352
+ var ownerIssueSchema = external_exports.object({
15353
+ id: external_exports.string(),
15354
+ title: external_exports.string(),
15355
+ summary: external_exports.string().nullable(),
15356
+ status: issueStatusSchema,
15357
+ fingerprint: external_exports.string().nullable(),
15358
+ event_count: external_exports.number(),
15359
+ reopened_count: external_exports.number(),
15360
+ first_seen: external_exports.string(),
15361
+ last_seen: external_exports.string(),
15362
+ links_json: external_exports.string()
15363
+ });
15592
15364
  var ingestResponseSchema = external_exports.object({
15593
15365
  id: external_exports.string().max(64),
15594
15366
  thread: external_exports.string().regex(THREAD_ID_PATTERN),
@@ -15596,6 +15368,19 @@ var ingestResponseSchema = external_exports.object({
15596
15368
  resume: external_exports.string().max(512).optional(),
15597
15369
  /** Vendor-configured answer back to the submitting agent (known issues, workarounds). */
15598
15370
  guidance: external_exports.string().max(MAX_GUIDANCE_LENGTH).optional(),
15371
+ /** The report matched an already-tracked issue; recorded — don't file variants. */
15372
+ known_issue: knownIssueSchema.optional(),
15373
+ /**
15374
+ * A question from the project's team, delivered at most one per response.
15375
+ * Answering is optional and must be grounded in what the agent actually did;
15376
+ * `command` is ready to run and files the answer on this thread.
15377
+ */
15378
+ ask: external_exports.object({
15379
+ id: external_exports.string().max(64),
15380
+ prompt: external_exports.string().max(500),
15381
+ command: external_exports.string().max(512),
15382
+ attachments: external_exports.array(deliveredAttachmentSchema).optional()
15383
+ }).optional(),
15599
15384
  /** True when idempotencyKey matched an already-stored event; id/thread are the original's. */
15600
15385
  duplicate: external_exports.boolean().optional()
15601
15386
  });
@@ -15624,50 +15409,546 @@ var repoEntrySchema = external_exports.object({
15624
15409
  var repoListSchema = external_exports.array(repoEntrySchema).max(20);
15625
15410
  function formatIssues(error51) {
15626
15411
  return error51.issues.map((issue2) => {
15627
- const path5 = issue2.path.join(".");
15628
- return path5 ? `${path5}: ${issue2.message}` : issue2.message;
15412
+ const path6 = issue2.path.join(".");
15413
+ return path6 ? `${path6}: ${issue2.message}` : issue2.message;
15629
15414
  });
15630
15415
  }
15631
- function validateFeedbackEvent(input) {
15632
- const result = feedbackEventSchema.safeParse(input);
15633
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15416
+ function validateFeedbackEvent(input) {
15417
+ const result = feedbackEventSchema.safeParse(input);
15418
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15419
+ }
15420
+ function validateDescriptor(input) {
15421
+ const result = descriptorSchema.safeParse(input);
15422
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15423
+ }
15424
+ function validateIngestResponse(input) {
15425
+ const result = ingestResponseSchema.safeParse(input);
15426
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15427
+ }
15428
+ function feedbackEventJsonSchema() {
15429
+ return external_exports.toJSONSchema(feedbackEventSchema);
15430
+ }
15431
+ function descriptorJsonSchema() {
15432
+ return external_exports.toJSONSchema(descriptorSchema);
15433
+ }
15434
+
15435
+ // ../core/dist/index.js
15436
+ import { readFileSync } from "fs";
15437
+ import path32 from "path";
15438
+
15439
+ // ../../node_modules/.pnpm/agent-cli-detector@0.1.5/node_modules/agent-cli-detector/dist/index.js
15440
+ init_esm_shims();
15441
+ import { execFileSync } from "child_process";
15442
+ import path2 from "path";
15443
+ var defaultAgents = [
15444
+ {
15445
+ id: "codex",
15446
+ name: "Codex",
15447
+ env: [
15448
+ { name: "CODEX_CI", value: "1" },
15449
+ { name: "CODEX_SHELL", value: "1" },
15450
+ { name: "CODEX_THREAD_ID" }
15451
+ ],
15452
+ process: [{ pattern: /^codex$/i }],
15453
+ sessionEnv: ["CODEX_THREAD_ID"]
15454
+ },
15455
+ {
15456
+ id: "cursor",
15457
+ name: "Cursor",
15458
+ env: [
15459
+ { name: "CURSOR_AGENT", value: "1" },
15460
+ { name: "CURSOR_CONVERSATION_ID" }
15461
+ ],
15462
+ process: [{ pattern: /^cursor$/i }],
15463
+ sessionEnv: ["CURSOR_CONVERSATION_ID"]
15464
+ },
15465
+ {
15466
+ id: "claude-code",
15467
+ name: "Claude Code",
15468
+ env: [
15469
+ { name: "CLAUDECODE", value: "1" },
15470
+ { name: "CLAUDE_CODE_SESSION_ID" }
15471
+ ],
15472
+ process: [{ pattern: /^claude$/i }],
15473
+ sessionEnv: ["CLAUDE_CODE_SESSION_ID"]
15474
+ },
15475
+ {
15476
+ id: "opencode",
15477
+ name: "OpenCode",
15478
+ env: [
15479
+ { name: "OPENCODE", value: "1" },
15480
+ { name: "OPENCODE_PID" }
15481
+ ],
15482
+ process: [{ pattern: /^opencode$/i }]
15483
+ },
15484
+ {
15485
+ id: "devin",
15486
+ name: "Devin",
15487
+ process: [{ pattern: /devin/i }]
15488
+ },
15489
+ {
15490
+ id: "gemini",
15491
+ name: "Gemini CLI",
15492
+ env: [{ name: "GEMINI_CLI", value: "1" }],
15493
+ process: [{ pattern: /^gemini$/i }]
15494
+ },
15495
+ {
15496
+ id: "antigravity",
15497
+ name: "Antigravity",
15498
+ env: [
15499
+ { name: "ANTIGRAVITY_AGENT", value: "1" },
15500
+ { name: "ANTIGRAVITY_TRAJECTORY_ID" }
15501
+ ],
15502
+ process: [{ pattern: /^antigravity$/i }],
15503
+ sessionEnv: ["ANTIGRAVITY_TRAJECTORY_ID"]
15504
+ },
15505
+ {
15506
+ id: "bolt",
15507
+ name: "Bolt",
15508
+ env: [
15509
+ { name: "BOLT_ENV" },
15510
+ { name: "BOLT_ORIGIN" },
15511
+ { name: "BOLT_SERVER_URL" }
15512
+ ]
15513
+ },
15514
+ {
15515
+ id: "pi",
15516
+ name: "Pi",
15517
+ env: [{ name: "PI_CODING_AGENT", value: "true" }]
15518
+ },
15519
+ {
15520
+ id: "replit",
15521
+ name: "Replit",
15522
+ env: [{ name: "REPLIT_SESSION" }],
15523
+ sessionEnv: ["REPLIT_SESSION"]
15524
+ },
15525
+ {
15526
+ id: "rork",
15527
+ name: "Rork",
15528
+ env: [{ name: "RORK_API_URL" }]
15529
+ },
15530
+ {
15531
+ id: "kiro",
15532
+ name: "Kiro",
15533
+ env: [{ name: "KIRO_SESSION_ID" }],
15534
+ process: [{ pattern: /kiro/i }],
15535
+ sessionEnv: ["KIRO_SESSION_ID"]
15536
+ },
15537
+ {
15538
+ id: "cline",
15539
+ name: "Cline",
15540
+ env: [{ name: "CLINE_WRAPPER_PATH" }],
15541
+ process: [{ pattern: /^cline$/i }]
15542
+ },
15543
+ {
15544
+ id: "kilocode",
15545
+ name: "Kilo Code",
15546
+ env: [
15547
+ { name: "KILO", value: "1" },
15548
+ { name: "KILOCODE_VERSION" },
15549
+ { name: "KILO_RUN_ID" }
15550
+ ],
15551
+ process: [{ pattern: /^(kilo|kilocode)$/i }],
15552
+ sessionEnv: ["KILO_RUN_ID"]
15553
+ },
15554
+ {
15555
+ id: "copilot",
15556
+ name: "GitHub Copilot CLI",
15557
+ env: [
15558
+ { name: "COPILOT_CLI", value: "1" },
15559
+ { name: "COPILOT_AGENT_SESSION_ID" },
15560
+ { name: "COPILOT_RUN_APP", value: "1" }
15561
+ ],
15562
+ process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],
15563
+ sessionEnv: ["COPILOT_AGENT_SESSION_ID"]
15564
+ }
15565
+ ];
15566
+ var EnvironmentDetectionStrategy = class {
15567
+ name = "environment";
15568
+ detect(context) {
15569
+ const matches = [];
15570
+ for (const agent of context.agents) {
15571
+ for (const signal of agent.env ?? []) {
15572
+ const value = context.env[signal.name];
15573
+ if (value === void 0 || !matchesEnvValue(value, signal.value)) {
15574
+ continue;
15575
+ }
15576
+ matches.push(toEvidence(agent, signal, value));
15577
+ }
15578
+ }
15579
+ return matches;
15580
+ }
15581
+ };
15582
+ function matchesEnvValue(value, matcher) {
15583
+ if (matcher === void 0) {
15584
+ return true;
15585
+ }
15586
+ if (typeof matcher === "string") {
15587
+ return value === matcher;
15588
+ }
15589
+ if (matcher instanceof RegExp) {
15590
+ return matcher.test(value);
15591
+ }
15592
+ return matcher(value);
15593
+ }
15594
+ function toEvidence(agent, signal, value) {
15595
+ return {
15596
+ agent: {
15597
+ id: agent.id,
15598
+ name: agent.name
15599
+ },
15600
+ strategy: "environment",
15601
+ signal: signal.name,
15602
+ value
15603
+ };
15604
+ }
15605
+ var PsProcessReader = class {
15606
+ read(pid) {
15607
+ const command = readPsField(pid, "comm");
15608
+ const ppid = Number.parseInt(readPsField(pid, "ppid"), 10);
15609
+ if (!command || !Number.isInteger(ppid)) {
15610
+ return void 0;
15611
+ }
15612
+ return {
15613
+ pid,
15614
+ ppid,
15615
+ command: normalizeCommand(command)
15616
+ };
15617
+ }
15618
+ };
15619
+ var ProcessTreeDetectionStrategy = class {
15620
+ constructor(reader = new PsProcessReader()) {
15621
+ this.reader = reader;
15622
+ }
15623
+ reader;
15624
+ name = "process-tree";
15625
+ detect(context) {
15626
+ const matches = [];
15627
+ let currentPid = context.pid;
15628
+ for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {
15629
+ const processInfo = this.reader.read(currentPid);
15630
+ if (processInfo === void 0) {
15631
+ break;
15632
+ }
15633
+ matches.push(...matchProcess(context.agents, processInfo));
15634
+ if (processInfo.ppid === void 0 || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {
15635
+ break;
15636
+ }
15637
+ currentPid = processInfo.ppid;
15638
+ }
15639
+ return matches;
15640
+ }
15641
+ };
15642
+ function matchProcess(agents, processInfo) {
15643
+ const matches = [];
15644
+ const command = normalizeCommand(processInfo.command);
15645
+ for (const agent of agents) {
15646
+ for (const signal of agent.process ?? []) {
15647
+ if (!signal.pattern.test(command)) {
15648
+ continue;
15649
+ }
15650
+ matches.push(toEvidence2(agent, signal, { ...processInfo, command }));
15651
+ }
15652
+ }
15653
+ return matches;
15654
+ }
15655
+ function toEvidence2(agent, signal, processInfo) {
15656
+ return {
15657
+ agent: {
15658
+ id: agent.id,
15659
+ name: agent.name
15660
+ },
15661
+ strategy: "process-tree",
15662
+ signal: signal.pattern.toString(),
15663
+ value: `${processInfo.pid}:${processInfo.command}`
15664
+ };
15634
15665
  }
15635
- function validateDescriptor(input) {
15636
- const result = descriptorSchema.safeParse(input);
15637
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15666
+ function readPsField(pid, field) {
15667
+ try {
15668
+ return execFileSync("ps", ["-o", `${field}=`, "-p", String(pid)], {
15669
+ encoding: "utf8",
15670
+ stdio: ["ignore", "pipe", "ignore"]
15671
+ }).trim();
15672
+ } catch {
15673
+ return "";
15674
+ }
15638
15675
  }
15639
- function validateIngestResponse(input) {
15640
- const result = ingestResponseSchema.safeParse(input);
15641
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15676
+ function normalizeCommand(command) {
15677
+ const baseName = path2.basename(command.trim());
15678
+ return baseName.replace(/^-+/, "");
15642
15679
  }
15643
- function feedbackEventJsonSchema() {
15644
- return external_exports.toJSONSchema(feedbackEventSchema);
15680
+ function detectAgent(options = {}) {
15681
+ const agents = options.agents ?? defaultAgents;
15682
+ const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);
15683
+ const context = {
15684
+ env: options.env ?? process.env,
15685
+ pid: options.pid ?? process.pid,
15686
+ agents,
15687
+ maxProcessDepth: options.maxProcessDepth ?? 20
15688
+ };
15689
+ const matches = strategies.flatMap((strategy) => strategy.detect(context));
15690
+ const bestMatch = selectBestAgent(agents, matches, context.env);
15691
+ if (bestMatch === void 0) {
15692
+ return {
15693
+ detected: false
15694
+ };
15695
+ }
15696
+ return {
15697
+ detected: true,
15698
+ agent: bestMatch
15699
+ };
15645
15700
  }
15646
- function descriptorJsonSchema() {
15647
- return external_exports.toJSONSchema(descriptorSchema);
15701
+ function defaultStrategies(experimentalProcessTree = false) {
15702
+ const strategies = [new EnvironmentDetectionStrategy()];
15703
+ if (experimentalProcessTree) {
15704
+ strategies.push(new ProcessTreeDetectionStrategy());
15705
+ }
15706
+ return strategies;
15707
+ }
15708
+ function selectBestAgent(agents, matches, env) {
15709
+ const matchedAgentIds = new Set(matches.map((match) => match.agent.id));
15710
+ for (const agent of agents) {
15711
+ if (!matchedAgentIds.has(agent.id)) {
15712
+ continue;
15713
+ }
15714
+ return {
15715
+ id: agent.id,
15716
+ name: agent.name,
15717
+ ...sessionIdProperties(agent, env)
15718
+ };
15719
+ }
15720
+ return void 0;
15721
+ }
15722
+ function sessionIdProperties(agent, env) {
15723
+ for (const envName of agent.sessionEnv ?? []) {
15724
+ const value = env[envName];
15725
+ if (value !== void 0 && value.length > 0) {
15726
+ return { sessionId: value };
15727
+ }
15728
+ }
15729
+ return {};
15730
+ }
15731
+
15732
+ // ../core/dist/index.js
15733
+ var import_ci_info = __toESM(require_ci_info(), 1);
15734
+
15735
+ // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/index.js
15736
+ init_esm_shims();
15737
+
15738
+ // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/chunk-HZH3SATY.js
15739
+ init_esm_shims();
15740
+ var defaultSandboxes = [
15741
+ {
15742
+ id: "replit",
15743
+ name: "Replit",
15744
+ env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }]
15745
+ },
15746
+ {
15747
+ id: "bolt",
15748
+ name: "bolt.new",
15749
+ env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }]
15750
+ },
15751
+ {
15752
+ id: "e2b",
15753
+ name: "E2B",
15754
+ env: [{ name: "E2B_SANDBOX", value: "true" }]
15755
+ },
15756
+ {
15757
+ id: "vercel-sandbox",
15758
+ name: "Vercel Sandbox",
15759
+ env: [{ name: "HOME", value: "/home/vercel-sandbox" }]
15760
+ },
15761
+ {
15762
+ id: "daytona",
15763
+ name: "Daytona",
15764
+ env: [{ name: "DAYTONA_SANDBOX_ID" }]
15765
+ },
15766
+ {
15767
+ id: "modal",
15768
+ name: "Modal",
15769
+ env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }]
15770
+ },
15771
+ {
15772
+ id: "cloudflare-sandbox",
15773
+ name: "Cloudflare Sandbox",
15774
+ env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }]
15775
+ },
15776
+ {
15777
+ id: "codespaces",
15778
+ name: "GitHub Codespaces",
15779
+ env: [{ name: "CODESPACES", value: "true" }]
15780
+ },
15781
+ {
15782
+ id: "codesandbox",
15783
+ name: "CodeSandbox",
15784
+ env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }]
15785
+ }
15786
+ ];
15787
+ function detectSandbox(options = {}) {
15788
+ const env = options.env ?? process.env;
15789
+ const sandboxes = options.sandboxes ?? defaultSandboxes;
15790
+ const sandbox = sandboxes.find(
15791
+ (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env))
15792
+ );
15793
+ if (sandbox === void 0) return { detected: false };
15794
+ return {
15795
+ detected: true,
15796
+ sandbox: { id: sandbox.id, name: sandbox.name }
15797
+ };
15798
+ }
15799
+ function matchesEnvSignal(signal, env) {
15800
+ const value = env[signal.name];
15801
+ return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value);
15648
15802
  }
15649
15803
 
15650
15804
  // ../core/dist/index.js
15651
- import { appendFileSync, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
15805
+ import { homedir } from "os";
15806
+ import path22 from "path";
15807
+ import { mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
15652
15808
  import path4 from "path";
15809
+ import { randomUUID } from "crypto";
15810
+ import { appendFileSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync3, renameSync, rmSync } from "fs";
15811
+ import path5 from "path";
15653
15812
  import { randomBytes } from "crypto";
15654
15813
  import { createRequire } from "module";
15814
+ var FeedbackError = class extends Error {
15815
+ constructor(message) {
15816
+ super(message);
15817
+ this.name = "FeedbackError";
15818
+ }
15819
+ };
15820
+ async function readLocalAttachments(inputs, cwd = process.cwd()) {
15821
+ if (inputs.length > MAX_ATTACHMENTS)
15822
+ throw new FeedbackError(`At most ${MAX_ATTACHMENTS} files can be attached.`);
15823
+ const files = [];
15824
+ for (const input of inputs) {
15825
+ const selected = typeof input === "string" ? input : input.path;
15826
+ const filename = path3.basename(selected);
15827
+ const description = attachmentDescriptionSchema.optional().safeParse(typeof input === "string" ? void 0 : input.description);
15828
+ if (!description.success)
15829
+ throw new FeedbackError(
15830
+ `Invalid description for "${filename}": ${description.error.issues.map((issue2) => issue2.message).join("; ")}`
15831
+ );
15832
+ const file2 = path3.resolve(cwd, selected);
15833
+ const info = await stat(file2).catch(() => null);
15834
+ if (!info?.isFile())
15835
+ throw new FeedbackError(`Cannot attach "${selected}": choose an existing, readable file.`);
15836
+ if (info.size < 1 || info.size > MAX_ATTACHMENT_BYTES)
15837
+ throw new FeedbackError(`"${filename}" must be between 1 byte and 20 MiB.`);
15838
+ const bytes = new Uint8Array(
15839
+ await readFile(file2).catch(() => {
15840
+ throw new FeedbackError(`Cannot read "${selected}".`);
15841
+ })
15842
+ );
15843
+ const manifest = attachmentManifestSchema.safeParse({
15844
+ filename,
15845
+ mediaType: mediaTypeForFilename(filename),
15846
+ size: bytes.length,
15847
+ sha256: createHash("sha256").update(bytes).digest("hex")
15848
+ });
15849
+ if (!manifest.success)
15850
+ throw new FeedbackError(
15851
+ `Cannot attach "${filename}": ${manifest.error.issues.map((issue2) => issue2.message).join("; ")}`
15852
+ );
15853
+ files.push({
15854
+ manifest: manifest.data,
15855
+ bytes,
15856
+ ...description.data ? { description: description.data } : {}
15857
+ });
15858
+ }
15859
+ return files;
15860
+ }
15861
+ var AttachmentUploadError = class extends FeedbackError {
15862
+ constructor(message, rejected) {
15863
+ super(message);
15864
+ this.rejected = rejected;
15865
+ this.name = "AttachmentUploadError";
15866
+ }
15867
+ rejected;
15868
+ };
15869
+ async function send(fetchImpl, url2, init, timeoutMs) {
15870
+ let response;
15871
+ try {
15872
+ response = await fetchImpl(url2, {
15873
+ ...init,
15874
+ redirect: "error",
15875
+ signal: AbortSignal.timeout(timeoutMs)
15876
+ });
15877
+ } catch {
15878
+ throw new AttachmentUploadError("Could not reach the server to upload files.", false);
15879
+ }
15880
+ if (response.ok) return response;
15881
+ const body = await response.json().catch(() => null);
15882
+ const errors = Array.isArray(body?.errors) ? body.errors.map(String).join("; ") : void 0;
15883
+ if (response.status === 404 && !errors)
15884
+ throw new AttachmentUploadError("This server does not accept files.", false);
15885
+ throw new AttachmentUploadError(
15886
+ errors ?? `File upload failed (${response.status}).`,
15887
+ !(response.status >= 500 || response.status === 429 || response.status === 408)
15888
+ );
15889
+ }
15890
+ async function uploadFiles(options) {
15891
+ const fetchImpl = options.fetchImpl ?? fetch;
15892
+ const timeoutMs = options.timeoutMs ?? 6e4;
15893
+ const origin = new URL(options.endpoint).origin;
15894
+ const references = [];
15895
+ for (const file2 of options.files) {
15896
+ const response = await send(
15897
+ fetchImpl,
15898
+ `${origin}${ATTACHMENT_UPLOAD_PATH}`,
15899
+ {
15900
+ method: "POST",
15901
+ headers: {
15902
+ "content-type": "application/json",
15903
+ ...options.secretKey ? { authorization: `Bearer ${options.secretKey}` } : {},
15904
+ ...options.writeKey ? { [WRITE_KEY_HEADER]: options.writeKey } : {}
15905
+ },
15906
+ body: JSON.stringify({
15907
+ ...file2.manifest,
15908
+ to: options.to,
15909
+ scope: options.secretKey ? "owner" : "reporter",
15910
+ uploader: options.uploader
15911
+ })
15912
+ },
15913
+ timeoutMs
15914
+ );
15915
+ const prepared = preparedAttachmentSchema.safeParse(await response.json().catch(() => null));
15916
+ if (!prepared.success || new URL(prepared.data.uploadUrl).origin !== origin)
15917
+ throw new AttachmentUploadError("The server returned an invalid upload response.", true);
15918
+ await send(
15919
+ fetchImpl,
15920
+ prepared.data.uploadUrl,
15921
+ {
15922
+ method: "PUT",
15923
+ headers: { authorization: `Bearer ${prepared.data.reference.token}` },
15924
+ body: file2.bytes
15925
+ },
15926
+ // At least 100 KB/s: a 20 MiB file on a slow link still gets about 3.5 minutes.
15927
+ Math.max(timeoutMs, file2.bytes.length / 100)
15928
+ );
15929
+ references.push({
15930
+ ...prepared.data.reference,
15931
+ ...file2.description ? { description: file2.description } : {}
15932
+ });
15933
+ }
15934
+ return references;
15935
+ }
15655
15936
  var DSN_ENV_VAR = "HIVENET_DSN";
15656
15937
  var REGISTRY_ENV_VAR = "HIVENET_REGISTRY";
15657
15938
  var HOME_ENV_VAR = "HIVENET_HOME";
15658
15939
  var NO_TELEMETRY_ENV_VAR = "HIVENET_NO_TELEMETRY";
15659
15940
  function feedbackHomeDir(env = process.env) {
15660
15941
  const override = env[HOME_ENV_VAR];
15661
- return override?.trim() ? override : path3.join(homedir(), ".hivenet");
15942
+ return override?.trim() ? override : path22.join(homedir(), ".hivenet");
15662
15943
  }
15663
15944
  function isTelemetryDisabled(extraOptOutEnvVars = [], env = process.env) {
15664
15945
  const vars = ["DO_NOT_TRACK", NO_TELEMETRY_ENV_VAR, ...extraOptOutEnvVars];
15665
15946
  return vars.some((name) => env[name] === "1" || env[name] === "true");
15666
15947
  }
15667
15948
  var MAX_SESSION_ID_LENGTH = 256;
15668
- function detectAgentSafe() {
15949
+ function detectAgentSafe(env) {
15669
15950
  try {
15670
- const result = detectAgent();
15951
+ const result = detectAgent({ env });
15671
15952
  if (result.detected && result.agent) {
15672
15953
  return {
15673
15954
  id: result.agent.id,
@@ -15679,9 +15960,9 @@ function detectAgentSafe() {
15679
15960
  }
15680
15961
  return void 0;
15681
15962
  }
15682
- function detectSandboxSafe() {
15963
+ function detectSandboxSafe(env) {
15683
15964
  try {
15684
- const result = detectSandbox();
15965
+ const result = detectSandbox({ env });
15685
15966
  if (result.detected && result.sandbox) {
15686
15967
  return {
15687
15968
  id: result.sandbox.id,
@@ -15746,7 +16027,7 @@ function resolveSessions(sources, projectRoot, env) {
15746
16027
  }
15747
16028
  const value = "envVar" in source ? normalizeSessionId(env[source.envVar]) : normalizeSessionId(
15748
16029
  readDotenvKey(
15749
- path22.resolve(projectRoot, source.dotenvFile),
16030
+ path32.resolve(projectRoot, source.dotenvFile),
15750
16031
  source.dotenvKey ?? source.key
15751
16032
  )
15752
16033
  );
@@ -15762,8 +16043,8 @@ async function collectContext(options = {}) {
15762
16043
  return void 0;
15763
16044
  }
15764
16045
  const projectRoot = options.projectRoot ?? process.cwd();
15765
- const agent = detectAgentSafe();
15766
- const sandbox = detectSandboxSafe();
16046
+ const agent = detectAgentSafe(env);
16047
+ const sandbox = detectSandboxSafe(env);
15767
16048
  const ci = detectCi();
15768
16049
  const session = resolveSessions(options.sessions ?? [], projectRoot, env);
15769
16050
  let project;
@@ -15786,12 +16067,6 @@ async function collectContext(options = {}) {
15786
16067
  function inferAuthor(context) {
15787
16068
  return context?.agent ? "agent" : void 0;
15788
16069
  }
15789
- var FeedbackError = class extends Error {
15790
- constructor(message) {
15791
- super(message);
15792
- this.name = "FeedbackError";
15793
- }
15794
- };
15795
16070
  var CONFIG_FILE_NAME = "feedback.config.json";
15796
16071
  var DESCRIPTOR_CACHE_TTL_MS = 60 * 60 * 1e3;
15797
16072
  var RESOLVE_TIMEOUT_MS = 5e3;
@@ -15816,9 +16091,9 @@ function parseDsn(dsn) {
15816
16091
  };
15817
16092
  }
15818
16093
  function findConfigFile(startDir) {
15819
- let current = path32.resolve(startDir);
16094
+ let current = path4.resolve(startDir);
15820
16095
  while (true) {
15821
- const candidate = path32.join(current, CONFIG_FILE_NAME);
16096
+ const candidate = path4.join(current, CONFIG_FILE_NAME);
15822
16097
  try {
15823
16098
  const raw = readFileSync2(candidate, "utf8");
15824
16099
  const parsed = JSON.parse(raw);
@@ -15827,7 +16102,7 @@ function findConfigFile(startDir) {
15827
16102
  }
15828
16103
  } catch {
15829
16104
  }
15830
- const parent = path32.dirname(current);
16105
+ const parent = path4.dirname(current);
15831
16106
  if (parent === current) {
15832
16107
  return void 0;
15833
16108
  }
@@ -15835,7 +16110,7 @@ function findConfigFile(startDir) {
15835
16110
  }
15836
16111
  }
15837
16112
  function cacheFilePath(cacheDir) {
15838
- return path32.join(cacheDir, "descriptors.json");
16113
+ return path4.join(cacheDir, "descriptors.json");
15839
16114
  }
15840
16115
  function readCache(cacheDir) {
15841
16116
  try {
@@ -15960,7 +16235,7 @@ async function postEvent(options) {
15960
16235
  error: error51 instanceof Error ? error51.message : String(error51)
15961
16236
  };
15962
16237
  }
15963
- if (response.status >= 500) {
16238
+ if (response.status >= 500 || response.status === 429 || response.status === 408) {
15964
16239
  return { kind: "unreachable", error: `server error (${response.status})` };
15965
16240
  }
15966
16241
  if (!response.ok) {
@@ -15988,13 +16263,14 @@ async function postEvent(options) {
15988
16263
  }
15989
16264
  var OUTBOX_FILE = "outbox.jsonl";
15990
16265
  var MAX_OUTBOX_ENTRIES = 100;
16266
+ var STALE_CLAIM_MS = 30 * 60 * 1e3;
15991
16267
  function outboxPath(env = process.env) {
15992
- return path4.join(feedbackHomeDir(env), OUTBOX_FILE);
16268
+ return path5.join(feedbackHomeDir(env), OUTBOX_FILE);
15993
16269
  }
15994
16270
  function appendToOutbox(entry, env = process.env) {
15995
16271
  try {
15996
16272
  const file2 = outboxPath(env);
15997
- mkdirSync2(path4.dirname(file2), { recursive: true });
16273
+ mkdirSync2(path5.dirname(file2), { recursive: true });
15998
16274
  appendFileSync(file2, `${JSON.stringify(entry)}
15999
16275
  `);
16000
16276
  return true;
@@ -16002,19 +16278,43 @@ function appendToOutbox(entry, env = process.env) {
16002
16278
  return false;
16003
16279
  }
16004
16280
  }
16005
- function readOutbox(env) {
16281
+ function readEntries(file2) {
16282
+ try {
16283
+ return readFileSync3(file2, "utf8").split("\n").filter((line) => line.trim()).flatMap((line) => {
16284
+ try {
16285
+ return [JSON.parse(line)];
16286
+ } catch {
16287
+ return [];
16288
+ }
16289
+ });
16290
+ } catch {
16291
+ return [];
16292
+ }
16293
+ }
16294
+ function claimQueue(env) {
16295
+ const file2 = outboxPath(env);
16296
+ let names;
16006
16297
  try {
16007
- return readFileSync3(outboxPath(env), "utf8").split("\n").filter((line) => line.trim()).slice(-MAX_OUTBOX_ENTRIES).map((line) => JSON.parse(line));
16298
+ names = readdirSync(path5.dirname(file2));
16008
16299
  } catch {
16009
16300
  return [];
16010
16301
  }
16302
+ const claims = names.filter((name) => {
16303
+ const claimedAt = Number(/\.flushing-(\d+)-/.exec(name)?.[1]);
16304
+ return name.startsWith(OUTBOX_FILE) && Date.now() - claimedAt > STALE_CLAIM_MS;
16305
+ }).map((name) => path5.join(path5.dirname(file2), name));
16306
+ const fresh = `${file2}.flushing-${Date.now()}-${randomUUID()}`;
16307
+ try {
16308
+ renameSync(file2, fresh);
16309
+ claims.push(fresh);
16310
+ } catch {
16311
+ }
16312
+ return claims;
16011
16313
  }
16012
16314
  async function flushOutbox(options = {}) {
16013
16315
  const env = options.env ?? process.env;
16014
- const entries = readOutbox(env);
16015
- if (entries.length === 0) {
16016
- return { delivered: 0, remaining: 0 };
16017
- }
16316
+ const claims = claimQueue(env);
16317
+ const entries = claims.flatMap(readEntries).slice(-MAX_OUTBOX_ENTRIES);
16018
16318
  const failures = [];
16019
16319
  let delivered = 0;
16020
16320
  for (const entry of entries) {
@@ -16031,12 +16331,13 @@ async function flushOutbox(options = {}) {
16031
16331
  failures.push(entry);
16032
16332
  }
16033
16333
  }
16034
- try {
16035
- writeFileSync2(
16036
- outboxPath(env),
16037
- failures.map((entry) => JSON.stringify(entry)).join("\n") + (failures.length ? "\n" : "")
16038
- );
16039
- } catch {
16334
+ if (failures.every((entry) => appendToOutbox(entry, env))) {
16335
+ for (const claim of claims) {
16336
+ try {
16337
+ rmSync(claim, { force: true });
16338
+ } catch {
16339
+ }
16340
+ }
16040
16341
  }
16041
16342
  return { delivered, remaining: failures.length };
16042
16343
  }
@@ -16056,10 +16357,10 @@ function ownerApi(options) {
16056
16357
  const origin = (options.apiOrigin ?? new URL(DEFAULT_REGISTRY_URL).origin).replace(/\/$/, "");
16057
16358
  const fetchImpl = options.fetchImpl ?? fetch;
16058
16359
  const timeoutMs = options.timeoutMs ?? 15e3;
16059
- async function request(path5, init) {
16360
+ async function request(path6, init) {
16060
16361
  let response;
16061
16362
  try {
16062
- response = await fetchImpl(`${origin}${path5}`, {
16363
+ response = await fetchImpl(`${origin}${path6}`, {
16063
16364
  ...init,
16064
16365
  signal: AbortSignal.timeout(timeoutMs),
16065
16366
  headers: {
@@ -16087,7 +16388,34 @@ function ownerApi(options) {
16087
16388
  }
16088
16389
  return body;
16089
16390
  }
16391
+ async function upload(paths, cwd) {
16392
+ return uploadFiles({
16393
+ endpoint: origin,
16394
+ files: await readLocalAttachments(paths, cwd),
16395
+ secretKey: options.secretKey,
16396
+ fetchImpl,
16397
+ timeoutMs: Math.max(timeoutMs, 6e4)
16398
+ });
16399
+ }
16090
16400
  return {
16401
+ /** Upload local files and attach them to a record (at most 10 files per record). */
16402
+ async attachFiles(target, paths, cwd) {
16403
+ if (!paths.length) throw new FeedbackError("Select at least one file.");
16404
+ const attachments = await upload(paths, cwd);
16405
+ return request("/v1/attachments/attach", {
16406
+ method: "POST",
16407
+ body: JSON.stringify({ target, attachments })
16408
+ });
16409
+ },
16410
+ /** A record's files; issues and evals include their reports' files. */
16411
+ async listAttachments(target, offset = 0) {
16412
+ return request(
16413
+ `/v1/attachments?${new URLSearchParams({ type: target.type, id: target.id, offset: String(offset) })}`
16414
+ );
16415
+ },
16416
+ async deleteAttachment(id) {
16417
+ return request(`/v1/attachments/${encodeURIComponent(id)}`, { method: "DELETE" });
16418
+ },
16091
16419
  async listFeedback(filters = {}) {
16092
16420
  const params = new URLSearchParams();
16093
16421
  if (filters.status) params.set("status", filters.status);
@@ -16124,10 +16452,53 @@ function ownerApi(options) {
16124
16452
  body: JSON.stringify({ status, ...note !== void 0 ? { note } : {} })
16125
16453
  });
16126
16454
  },
16455
+ async listQuestions(filters = {}) {
16456
+ const query = filters.status ? `?status=${filters.status}` : "";
16457
+ return request(`/v1/questions${query}`);
16458
+ },
16459
+ /** Files are shared with every agent that receives the question. */
16460
+ async createQuestion(prompt, category, options2 = {}) {
16461
+ const attachments = options2.files?.length ? await upload(options2.files, options2.cwd) : [];
16462
+ return request("/v1/questions", {
16463
+ method: "POST",
16464
+ body: JSON.stringify({
16465
+ prompt,
16466
+ ...category ? { category } : {},
16467
+ ...attachments.length ? { attachments } : {},
16468
+ ...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
16469
+ })
16470
+ });
16471
+ },
16472
+ async closeQuestion(id) {
16473
+ return request(`/v1/questions/${encodeURIComponent(id)}`, {
16474
+ method: "PATCH",
16475
+ body: JSON.stringify({ status: "closed" })
16476
+ });
16477
+ },
16478
+ async listEvals(filters = {}) {
16479
+ const query = filters.status ? `?status=${filters.status}` : "";
16480
+ return request(`/v1/evals${query}`);
16481
+ },
16482
+ async getEval(id) {
16483
+ return request(`/v1/evals/${encodeURIComponent(id)}`);
16484
+ },
16485
+ /** Promote a failed-task report (category=eval event) into the eval corpus. */
16486
+ async promoteEval(eventId) {
16487
+ return request("/v1/evals", {
16488
+ method: "POST",
16489
+ body: JSON.stringify({ event_id: eventId })
16490
+ });
16491
+ },
16492
+ async setEvalStatus(id, status, note) {
16493
+ return request(`/v1/evals/${encodeURIComponent(id)}`, {
16494
+ method: "PATCH",
16495
+ body: JSON.stringify({ status, ...note !== void 0 ? { note } : {} })
16496
+ });
16497
+ },
16127
16498
  /** Attach an external link (Slack message, Linear/GitHub issue, PR) to evt_… or iss_…. */
16128
16499
  async addLink(id, url2, label) {
16129
- const path5 = id.startsWith("iss_") ? `/v1/issues/${id}` : `/v1/events/${id}`;
16130
- return request(path5, {
16500
+ const path6 = id.startsWith("iss_") ? `/v1/issues/${id}` : `/v1/events/${id}`;
16501
+ return request(path6, {
16131
16502
  method: "PATCH",
16132
16503
  body: JSON.stringify({ addLink: { url: url2, ...label !== void 0 ? { label } : {} } })
16133
16504
  });
@@ -16155,9 +16526,10 @@ function resolveThreadId(resume) {
16155
16526
  }
16156
16527
  return { id: generateThreadId(), resumed: false };
16157
16528
  }
16158
- async function sendFeedback(options) {
16529
+ async function prepareFeedback(options) {
16159
16530
  const env = options.env ?? process.env;
16160
16531
  const cwd = options.cwd ?? process.cwd();
16532
+ const files = await readLocalAttachments(options.attachments ?? [], cwd);
16161
16533
  const config2 = findConfigFile(cwd)?.config;
16162
16534
  const resolveOptions = {
16163
16535
  ...options.to !== void 0 ? { to: options.to } : {},
@@ -16193,6 +16565,7 @@ async function sendFeedback(options) {
16193
16565
  ...author ? { author } : {},
16194
16566
  thread: { id: thread.id },
16195
16567
  ...options.eval ? { eval: options.eval } : {},
16568
+ ...options.question ? { question: options.question } : {},
16196
16569
  ...context ? { context } : {},
16197
16570
  ...options.metadata && Object.keys(options.metadata).length > 0 ? { metadata: options.metadata } : {},
16198
16571
  // Minted before the first attempt so outbox retries replay the SAME event
@@ -16208,7 +16581,32 @@ async function sendFeedback(options) {
16208
16581
  if (!validated.ok) {
16209
16582
  throw new FeedbackError(`Invalid feedback event: ${validated.errors.join("; ")}`);
16210
16583
  }
16211
- const event = validated.data;
16584
+ return { event: validated.data, destination, resumed: thread.resumed, files };
16585
+ }
16586
+ async function sendFeedback(options) {
16587
+ const env = options.env ?? process.env;
16588
+ const { event, destination, resumed, files } = await prepareFeedback(options);
16589
+ const names = files.map((file2) => file2.manifest.filename);
16590
+ let filesNotAttached;
16591
+ if (files.length) {
16592
+ try {
16593
+ event.attachments = await uploadFiles({
16594
+ endpoint: destination.endpoint,
16595
+ files,
16596
+ ...destination.to ? { to: destination.to } : {},
16597
+ ...destination.writeKey ? { writeKey: destination.writeKey } : {},
16598
+ ...event.context?.agent?.id ? { uploader: event.context.agent.id } : {},
16599
+ ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
16600
+ });
16601
+ } catch (error51) {
16602
+ if (!(error51 instanceof AttachmentUploadError) || error51.rejected) {
16603
+ throw new FeedbackError(
16604
+ `${error51 instanceof Error ? error51.message : String(error51)} Nothing was sent.`
16605
+ );
16606
+ }
16607
+ filesNotAttached = { files: names, reason: error51.message };
16608
+ }
16609
+ }
16212
16610
  const result = await postEvent({
16213
16611
  endpoint: destination.endpoint,
16214
16612
  ...destination.writeKey ? { writeKey: destination.writeKey } : {},
@@ -16222,12 +16620,17 @@ async function sendFeedback(options) {
16222
16620
  response: result.response,
16223
16621
  event,
16224
16622
  destination,
16225
- resumed: thread.resumed
16623
+ resumed,
16624
+ ...filesNotAttached ? { filesNotAttached } : {}
16226
16625
  };
16227
16626
  }
16228
16627
  if (result.kind === "rejected") {
16229
16628
  throw new FeedbackError(`Feedback rejected: ${result.errors.join("; ")}`);
16230
16629
  }
16630
+ if (event.attachments) {
16631
+ delete event.attachments;
16632
+ filesNotAttached = { files: names, reason: result.error };
16633
+ }
16231
16634
  const queued = options.outbox !== false && appendToOutbox(
16232
16635
  {
16233
16636
  endpoint: destination.endpoint,
@@ -16243,7 +16646,8 @@ async function sendFeedback(options) {
16243
16646
  error: result.error,
16244
16647
  event,
16245
16648
  destination,
16246
- resumed: thread.resumed
16649
+ resumed,
16650
+ ...filesNotAttached ? { filesNotAttached } : {}
16247
16651
  };
16248
16652
  }
16249
16653
  export {
@@ -16256,6 +16660,8 @@ export {
16256
16660
  FeedbackError,
16257
16661
  HOME_ENV_VAR,
16258
16662
  ISSUE_STATUSES,
16663
+ MAX_ATTACHMENTS,
16664
+ MAX_ATTACHMENT_BYTES,
16259
16665
  MAX_FEEDBACK_LENGTH,
16260
16666
  MAX_GUIDANCE_LENGTH,
16261
16667
  MAX_SUBJECT_LENGTH,