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.cjs CHANGED
@@ -506,6 +506,8 @@ __export(index_exports, {
506
506
  FeedbackError: () => FeedbackError,
507
507
  HOME_ENV_VAR: () => HOME_ENV_VAR,
508
508
  ISSUE_STATUSES: () => ISSUE_STATUSES,
509
+ MAX_ATTACHMENTS: () => MAX_ATTACHMENTS,
510
+ MAX_ATTACHMENT_BYTES: () => MAX_ATTACHMENT_BYTES,
509
511
  MAX_FEEDBACK_LENGTH: () => MAX_FEEDBACK_LENGTH,
510
512
  MAX_GUIDANCE_LENGTH: () => MAX_GUIDANCE_LENGTH,
511
513
  MAX_SUBJECT_LENGTH: () => MAX_SUBJECT_LENGTH,
@@ -555,380 +557,10 @@ init_cjs_shims();
555
557
 
556
558
  // ../core/dist/index.js
557
559
  init_cjs_shims();
558
- var import_fs = require("fs");
560
+ var import_crypto = require("crypto");
561
+ var import_promises = require("fs/promises");
559
562
  var import_path2 = __toESM(require("path"), 1);
560
563
 
561
- // ../../node_modules/.pnpm/agent-cli-detector@0.1.5/node_modules/agent-cli-detector/dist/index.js
562
- init_cjs_shims();
563
- var import_child_process = require("child_process");
564
- var import_path = __toESM(require("path"), 1);
565
- var defaultAgents = [
566
- {
567
- id: "codex",
568
- name: "Codex",
569
- env: [
570
- { name: "CODEX_CI", value: "1" },
571
- { name: "CODEX_SHELL", value: "1" },
572
- { name: "CODEX_THREAD_ID" }
573
- ],
574
- process: [{ pattern: /^codex$/i }],
575
- sessionEnv: ["CODEX_THREAD_ID"]
576
- },
577
- {
578
- id: "cursor",
579
- name: "Cursor",
580
- env: [
581
- { name: "CURSOR_AGENT", value: "1" },
582
- { name: "CURSOR_CONVERSATION_ID" }
583
- ],
584
- process: [{ pattern: /^cursor$/i }],
585
- sessionEnv: ["CURSOR_CONVERSATION_ID"]
586
- },
587
- {
588
- id: "claude-code",
589
- name: "Claude Code",
590
- env: [
591
- { name: "CLAUDECODE", value: "1" },
592
- { name: "CLAUDE_CODE_SESSION_ID" }
593
- ],
594
- process: [{ pattern: /^claude$/i }],
595
- sessionEnv: ["CLAUDE_CODE_SESSION_ID"]
596
- },
597
- {
598
- id: "opencode",
599
- name: "OpenCode",
600
- env: [
601
- { name: "OPENCODE", value: "1" },
602
- { name: "OPENCODE_PID" }
603
- ],
604
- process: [{ pattern: /^opencode$/i }]
605
- },
606
- {
607
- id: "devin",
608
- name: "Devin",
609
- process: [{ pattern: /devin/i }]
610
- },
611
- {
612
- id: "gemini",
613
- name: "Gemini CLI",
614
- env: [{ name: "GEMINI_CLI", value: "1" }],
615
- process: [{ pattern: /^gemini$/i }]
616
- },
617
- {
618
- id: "antigravity",
619
- name: "Antigravity",
620
- env: [
621
- { name: "ANTIGRAVITY_AGENT", value: "1" },
622
- { name: "ANTIGRAVITY_TRAJECTORY_ID" }
623
- ],
624
- process: [{ pattern: /^antigravity$/i }],
625
- sessionEnv: ["ANTIGRAVITY_TRAJECTORY_ID"]
626
- },
627
- {
628
- id: "bolt",
629
- name: "Bolt",
630
- env: [
631
- { name: "BOLT_ENV" },
632
- { name: "BOLT_ORIGIN" },
633
- { name: "BOLT_SERVER_URL" }
634
- ]
635
- },
636
- {
637
- id: "pi",
638
- name: "Pi",
639
- env: [{ name: "PI_CODING_AGENT", value: "true" }]
640
- },
641
- {
642
- id: "replit",
643
- name: "Replit",
644
- env: [{ name: "REPLIT_SESSION" }],
645
- sessionEnv: ["REPLIT_SESSION"]
646
- },
647
- {
648
- id: "rork",
649
- name: "Rork",
650
- env: [{ name: "RORK_API_URL" }]
651
- },
652
- {
653
- id: "kiro",
654
- name: "Kiro",
655
- env: [{ name: "KIRO_SESSION_ID" }],
656
- process: [{ pattern: /kiro/i }],
657
- sessionEnv: ["KIRO_SESSION_ID"]
658
- },
659
- {
660
- id: "cline",
661
- name: "Cline",
662
- env: [{ name: "CLINE_WRAPPER_PATH" }],
663
- process: [{ pattern: /^cline$/i }]
664
- },
665
- {
666
- id: "kilocode",
667
- name: "Kilo Code",
668
- env: [
669
- { name: "KILO", value: "1" },
670
- { name: "KILOCODE_VERSION" },
671
- { name: "KILO_RUN_ID" }
672
- ],
673
- process: [{ pattern: /^(kilo|kilocode)$/i }],
674
- sessionEnv: ["KILO_RUN_ID"]
675
- },
676
- {
677
- id: "copilot",
678
- name: "GitHub Copilot CLI",
679
- env: [
680
- { name: "COPILOT_CLI", value: "1" },
681
- { name: "COPILOT_AGENT_SESSION_ID" },
682
- { name: "COPILOT_RUN_APP", value: "1" }
683
- ],
684
- process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],
685
- sessionEnv: ["COPILOT_AGENT_SESSION_ID"]
686
- }
687
- ];
688
- var EnvironmentDetectionStrategy = class {
689
- name = "environment";
690
- detect(context) {
691
- const matches = [];
692
- for (const agent of context.agents) {
693
- for (const signal of agent.env ?? []) {
694
- const value = context.env[signal.name];
695
- if (value === void 0 || !matchesEnvValue(value, signal.value)) {
696
- continue;
697
- }
698
- matches.push(toEvidence(agent, signal, value));
699
- }
700
- }
701
- return matches;
702
- }
703
- };
704
- function matchesEnvValue(value, matcher) {
705
- if (matcher === void 0) {
706
- return true;
707
- }
708
- if (typeof matcher === "string") {
709
- return value === matcher;
710
- }
711
- if (matcher instanceof RegExp) {
712
- return matcher.test(value);
713
- }
714
- return matcher(value);
715
- }
716
- function toEvidence(agent, signal, value) {
717
- return {
718
- agent: {
719
- id: agent.id,
720
- name: agent.name
721
- },
722
- strategy: "environment",
723
- signal: signal.name,
724
- value
725
- };
726
- }
727
- var PsProcessReader = class {
728
- read(pid) {
729
- const command = readPsField(pid, "comm");
730
- const ppid = Number.parseInt(readPsField(pid, "ppid"), 10);
731
- if (!command || !Number.isInteger(ppid)) {
732
- return void 0;
733
- }
734
- return {
735
- pid,
736
- ppid,
737
- command: normalizeCommand(command)
738
- };
739
- }
740
- };
741
- var ProcessTreeDetectionStrategy = class {
742
- constructor(reader = new PsProcessReader()) {
743
- this.reader = reader;
744
- }
745
- reader;
746
- name = "process-tree";
747
- detect(context) {
748
- const matches = [];
749
- let currentPid = context.pid;
750
- for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {
751
- const processInfo = this.reader.read(currentPid);
752
- if (processInfo === void 0) {
753
- break;
754
- }
755
- matches.push(...matchProcess(context.agents, processInfo));
756
- if (processInfo.ppid === void 0 || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {
757
- break;
758
- }
759
- currentPid = processInfo.ppid;
760
- }
761
- return matches;
762
- }
763
- };
764
- function matchProcess(agents, processInfo) {
765
- const matches = [];
766
- const command = normalizeCommand(processInfo.command);
767
- for (const agent of agents) {
768
- for (const signal of agent.process ?? []) {
769
- if (!signal.pattern.test(command)) {
770
- continue;
771
- }
772
- matches.push(toEvidence2(agent, signal, { ...processInfo, command }));
773
- }
774
- }
775
- return matches;
776
- }
777
- function toEvidence2(agent, signal, processInfo) {
778
- return {
779
- agent: {
780
- id: agent.id,
781
- name: agent.name
782
- },
783
- strategy: "process-tree",
784
- signal: signal.pattern.toString(),
785
- value: `${processInfo.pid}:${processInfo.command}`
786
- };
787
- }
788
- function readPsField(pid, field) {
789
- try {
790
- return (0, import_child_process.execFileSync)("ps", ["-o", `${field}=`, "-p", String(pid)], {
791
- encoding: "utf8",
792
- stdio: ["ignore", "pipe", "ignore"]
793
- }).trim();
794
- } catch {
795
- return "";
796
- }
797
- }
798
- function normalizeCommand(command) {
799
- const baseName = import_path.default.basename(command.trim());
800
- return baseName.replace(/^-+/, "");
801
- }
802
- function detectAgent(options = {}) {
803
- const agents = options.agents ?? defaultAgents;
804
- const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);
805
- const context = {
806
- env: options.env ?? process.env,
807
- pid: options.pid ?? process.pid,
808
- agents,
809
- maxProcessDepth: options.maxProcessDepth ?? 20
810
- };
811
- const matches = strategies.flatMap((strategy) => strategy.detect(context));
812
- const bestMatch = selectBestAgent(agents, matches, context.env);
813
- if (bestMatch === void 0) {
814
- return {
815
- detected: false
816
- };
817
- }
818
- return {
819
- detected: true,
820
- agent: bestMatch
821
- };
822
- }
823
- function defaultStrategies(experimentalProcessTree = false) {
824
- const strategies = [new EnvironmentDetectionStrategy()];
825
- if (experimentalProcessTree) {
826
- strategies.push(new ProcessTreeDetectionStrategy());
827
- }
828
- return strategies;
829
- }
830
- function selectBestAgent(agents, matches, env) {
831
- const matchedAgentIds = new Set(matches.map((match) => match.agent.id));
832
- for (const agent of agents) {
833
- if (!matchedAgentIds.has(agent.id)) {
834
- continue;
835
- }
836
- return {
837
- id: agent.id,
838
- name: agent.name,
839
- ...sessionIdProperties(agent, env)
840
- };
841
- }
842
- return void 0;
843
- }
844
- function sessionIdProperties(agent, env) {
845
- for (const envName of agent.sessionEnv ?? []) {
846
- const value = env[envName];
847
- if (value !== void 0 && value.length > 0) {
848
- return { sessionId: value };
849
- }
850
- }
851
- return {};
852
- }
853
-
854
- // ../core/dist/index.js
855
- var import_ci_info = __toESM(require_ci_info(), 1);
856
-
857
- // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/index.js
858
- init_cjs_shims();
859
-
860
- // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/chunk-HZH3SATY.js
861
- init_cjs_shims();
862
- var defaultSandboxes = [
863
- {
864
- id: "replit",
865
- name: "Replit",
866
- env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }]
867
- },
868
- {
869
- id: "bolt",
870
- name: "bolt.new",
871
- env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }]
872
- },
873
- {
874
- id: "e2b",
875
- name: "E2B",
876
- env: [{ name: "E2B_SANDBOX", value: "true" }]
877
- },
878
- {
879
- id: "vercel-sandbox",
880
- name: "Vercel Sandbox",
881
- env: [{ name: "HOME", value: "/home/vercel-sandbox" }]
882
- },
883
- {
884
- id: "daytona",
885
- name: "Daytona",
886
- env: [{ name: "DAYTONA_SANDBOX_ID" }]
887
- },
888
- {
889
- id: "modal",
890
- name: "Modal",
891
- env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }]
892
- },
893
- {
894
- id: "cloudflare-sandbox",
895
- name: "Cloudflare Sandbox",
896
- env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }]
897
- },
898
- {
899
- id: "codespaces",
900
- name: "GitHub Codespaces",
901
- env: [{ name: "CODESPACES", value: "true" }]
902
- },
903
- {
904
- id: "codesandbox",
905
- name: "CodeSandbox",
906
- env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }]
907
- }
908
- ];
909
- function detectSandbox(options = {}) {
910
- const env = options.env ?? process.env;
911
- const sandboxes = options.sandboxes ?? defaultSandboxes;
912
- const sandbox = sandboxes.find(
913
- (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env))
914
- );
915
- if (sandbox === void 0) return { detected: false };
916
- return {
917
- detected: true,
918
- sandbox: { id: sandbox.id, name: sandbox.name }
919
- };
920
- }
921
- function matchesEnvSignal(signal, env) {
922
- const value = env[signal.name];
923
- return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value);
924
- }
925
-
926
- // ../core/dist/index.js
927
- var import_os = require("os");
928
- var import_path3 = __toESM(require("path"), 1);
929
- var import_fs2 = require("fs");
930
- var import_path4 = __toESM(require("path"), 1);
931
-
932
564
  // ../spec/dist/index.js
933
565
  init_cjs_shims();
934
566
 
@@ -1711,10 +1343,10 @@ function mergeDefs(...defs) {
1711
1343
  function cloneDef(schema) {
1712
1344
  return mergeDefs(schema._zod.def);
1713
1345
  }
1714
- function getElementAtPath(obj, path5) {
1715
- if (!path5)
1346
+ function getElementAtPath(obj, path6) {
1347
+ if (!path6)
1716
1348
  return obj;
1717
- return path5.reduce((acc, key) => acc?.[key], obj);
1349
+ return path6.reduce((acc, key) => acc?.[key], obj);
1718
1350
  }
1719
1351
  function promiseAllObject(promisesObj) {
1720
1352
  const keys = Object.keys(promisesObj);
@@ -2123,11 +1755,11 @@ function explicitlyAborted(x, startIndex = 0) {
2123
1755
  }
2124
1756
  return false;
2125
1757
  }
2126
- function prefixIssues(path5, issues) {
1758
+ function prefixIssues(path6, issues) {
2127
1759
  return issues.map((iss) => {
2128
1760
  var _a3;
2129
1761
  (_a3 = iss).path ?? (_a3.path = []);
2130
- iss.path.unshift(path5);
1762
+ iss.path.unshift(path6);
2131
1763
  return iss;
2132
1764
  });
2133
1765
  }
@@ -2274,16 +1906,16 @@ function flattenError(error51, mapper = (issue2) => issue2.message) {
2274
1906
  }
2275
1907
  function formatError(error51, mapper = (issue2) => issue2.message) {
2276
1908
  const fieldErrors = { _errors: [] };
2277
- const processError = (error52, path5 = []) => {
1909
+ const processError = (error52, path6 = []) => {
2278
1910
  for (const issue2 of error52.issues) {
2279
1911
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2280
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1912
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
2281
1913
  } else if (issue2.code === "invalid_key") {
2282
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1914
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2283
1915
  } else if (issue2.code === "invalid_element") {
2284
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1916
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2285
1917
  } else {
2286
- const fullpath = [...path5, ...issue2.path];
1918
+ const fullpath = [...path6, ...issue2.path];
2287
1919
  if (fullpath.length === 0) {
2288
1920
  fieldErrors._errors.push(mapper(issue2));
2289
1921
  } else {
@@ -2310,17 +1942,17 @@ function formatError(error51, mapper = (issue2) => issue2.message) {
2310
1942
  }
2311
1943
  function treeifyError(error51, mapper = (issue2) => issue2.message) {
2312
1944
  const result = { errors: [] };
2313
- const processError = (error52, path5 = []) => {
1945
+ const processError = (error52, path6 = []) => {
2314
1946
  var _a3, _b;
2315
1947
  for (const issue2 of error52.issues) {
2316
1948
  if (issue2.code === "invalid_union" && issue2.errors.length) {
2317
- issue2.errors.map((issues) => processError({ issues }, [...path5, ...issue2.path]));
1949
+ issue2.errors.map((issues) => processError({ issues }, [...path6, ...issue2.path]));
2318
1950
  } else if (issue2.code === "invalid_key") {
2319
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1951
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2320
1952
  } else if (issue2.code === "invalid_element") {
2321
- processError({ issues: issue2.issues }, [...path5, ...issue2.path]);
1953
+ processError({ issues: issue2.issues }, [...path6, ...issue2.path]);
2322
1954
  } else {
2323
- const fullpath = [...path5, ...issue2.path];
1955
+ const fullpath = [...path6, ...issue2.path];
2324
1956
  if (fullpath.length === 0) {
2325
1957
  result.errors.push(mapper(issue2));
2326
1958
  continue;
@@ -2352,8 +1984,8 @@ function treeifyError(error51, mapper = (issue2) => issue2.message) {
2352
1984
  }
2353
1985
  function toDotPath(_path) {
2354
1986
  const segs = [];
2355
- const path5 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
2356
- for (const seg of path5) {
1987
+ const path6 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
1988
+ for (const seg of path6) {
2357
1989
  if (typeof seg === "number")
2358
1990
  segs.push(`[${seg}]`);
2359
1991
  else if (typeof seg === "symbol")
@@ -15126,13 +14758,13 @@ function resolveRef(ref, ctx) {
15126
14758
  if (!ref.startsWith("#")) {
15127
14759
  throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
15128
14760
  }
15129
- const path5 = ref.slice(1).split("/").filter(Boolean);
15130
- if (path5.length === 0) {
14761
+ const path6 = ref.slice(1).split("/").filter(Boolean);
14762
+ if (path6.length === 0) {
15131
14763
  return ctx.rootSchema;
15132
14764
  }
15133
14765
  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";
15134
- if (path5[0] === defsKey) {
15135
- const key = path5[1];
14766
+ if (path6[0] === defsKey) {
14767
+ const key = path6[1];
15136
14768
  if (!key || !ctx.defs[key]) {
15137
14769
  throw new Error(`Reference not found: ${ref}`);
15138
14770
  }
@@ -15537,11 +15169,107 @@ function bigint3(params) {
15537
15169
  function date4(params) {
15538
15170
  return _coercedDate(ZodDate, params);
15539
15171
  }
15540
-
15541
- // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15542
- config(en_default());
15543
-
15544
- // ../spec/dist/index.js
15172
+
15173
+ // ../../node_modules/.pnpm/zod@4.4.3/node_modules/zod/v4/classic/external.js
15174
+ config(en_default());
15175
+
15176
+ // ../spec/dist/index.js
15177
+ var MAX_ATTACHMENT_BYTES = 20 * 1024 * 1024;
15178
+ var MAX_ATTACHMENTS = 10;
15179
+ var MAX_ATTACHMENT_DESCRIPTION_LENGTH = 1e3;
15180
+ var ATTACHMENT_UPLOAD_PATH = "/v1/attachments";
15181
+ 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.");
15182
+ var attachmentIdSchema = external_exports.string().regex(/^att_[a-f0-9]{32}$/);
15183
+ var ATTACHMENT_TARGET_TYPES = [
15184
+ "event",
15185
+ "issue",
15186
+ "question",
15187
+ "eval",
15188
+ "investigation",
15189
+ "investigation_question",
15190
+ "investigation_reply"
15191
+ ];
15192
+ var attachmentTargetSchema = external_exports.object({
15193
+ type: external_exports.enum(ATTACHMENT_TARGET_TYPES),
15194
+ id: external_exports.string().min(1).max(64)
15195
+ });
15196
+ var attachmentManifestSchema = external_exports.object({
15197
+ filename: external_exports.string().min(1).max(180).refine(
15198
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Reject control characters in filenames and HTTP headers.
15199
+ (name) => !/[\\/\u0000-\u001f\u007f]/.test(name) && name !== "." && name !== "..",
15200
+ "Use a filename without directories or control characters."
15201
+ ),
15202
+ mediaType: external_exports.string().max(127).regex(/^[a-z0-9!#$&^_.+-]+\/[a-z0-9!#$&^_.+-]+$/),
15203
+ size: external_exports.number().int().min(1).max(MAX_ATTACHMENT_BYTES),
15204
+ sha256: external_exports.string().regex(/^[a-f0-9]{64}$/)
15205
+ });
15206
+ var prepareAttachmentSchema = attachmentManifestSchema.extend({
15207
+ to: external_exports.string().min(1).max(253).optional(),
15208
+ scope: external_exports.enum(["reporter", "owner"]).default("reporter"),
15209
+ /** Self-reported agent name for reporter uploads. */
15210
+ uploader: external_exports.string().min(1).max(128).optional()
15211
+ });
15212
+ var preparedAttachmentSchema = external_exports.object({
15213
+ reference: external_exports.object({
15214
+ id: attachmentIdSchema,
15215
+ token: external_exports.string().regex(/^hva_[a-f0-9]{64}$/)
15216
+ }),
15217
+ uploadUrl: external_exports.url(),
15218
+ expiresAt: external_exports.string()
15219
+ });
15220
+ var attachmentReferenceSchema = preparedAttachmentSchema.shape.reference.extend({
15221
+ description: attachmentDescriptionSchema.optional()
15222
+ });
15223
+ var attachmentReferencesSchema = external_exports.array(attachmentReferenceSchema).max(MAX_ATTACHMENTS).refine(
15224
+ (refs) => new Set(refs.map((ref) => ref.id)).size === refs.length,
15225
+ "Each file can be attached once."
15226
+ );
15227
+ var attachFilesSchema = external_exports.object({
15228
+ target: attachmentTargetSchema,
15229
+ attachments: attachmentReferencesSchema.min(1)
15230
+ });
15231
+ var attachmentSchema = attachmentManifestSchema.extend({
15232
+ id: attachmentIdSchema,
15233
+ createdAt: external_exports.string(),
15234
+ source: external_exports.enum(["reporter", "owner"]),
15235
+ /** Reporter names are self-reported; owner uploads are authenticated. */
15236
+ uploader: external_exports.string().nullable(),
15237
+ /** The record the file belongs to — a report when shown on its issue or eval. */
15238
+ target: attachmentTargetSchema,
15239
+ description: attachmentDescriptionSchema.optional()
15240
+ });
15241
+ var deliveredAttachmentSchema = attachmentSchema.extend({
15242
+ url: external_exports.url(),
15243
+ expiresAt: external_exports.string()
15244
+ });
15245
+ var MEDIA_TYPES = {
15246
+ png: "image/png",
15247
+ jpg: "image/jpeg",
15248
+ jpeg: "image/jpeg",
15249
+ gif: "image/gif",
15250
+ webp: "image/webp",
15251
+ svg: "image/svg+xml",
15252
+ mp4: "video/mp4",
15253
+ webm: "video/webm",
15254
+ mp3: "audio/mpeg",
15255
+ m4a: "audio/mp4",
15256
+ wav: "audio/wav",
15257
+ ogg: "audio/ogg",
15258
+ pdf: "application/pdf",
15259
+ json: "application/json",
15260
+ jsonl: "application/x-ndjson",
15261
+ ndjson: "application/x-ndjson",
15262
+ txt: "text/plain",
15263
+ log: "text/plain",
15264
+ md: "text/markdown",
15265
+ csv: "text/csv",
15266
+ zip: "application/zip",
15267
+ html: "text/html"
15268
+ };
15269
+ function mediaTypeForFilename(filename) {
15270
+ const extension = /\.([^./\\]+)$/.exec(filename)?.[1]?.toLowerCase();
15271
+ return extension && MEDIA_TYPES[extension] || "application/octet-stream";
15272
+ }
15545
15273
  var PROTOCOL_VERSION = 1;
15546
15274
  var MAX_FEEDBACK_LENGTH = 5e3;
15547
15275
  var MAX_SUBJECT_LENGTH = 500;
@@ -15561,10 +15289,18 @@ var FEEDBACK_CATEGORIES = [
15561
15289
  "cli",
15562
15290
  "api",
15563
15291
  "model",
15564
- "eval",
15565
15292
  "ux",
15566
15293
  "other"
15567
15294
  ];
15295
+ var createQuestionInputSchema = external_exports.object({
15296
+ prompt: external_exports.string().trim().min(8).max(500).describe('The question, e.g. "If you used <feature>: what broke or was missing?"'),
15297
+ category: external_exports.enum(FEEDBACK_CATEGORIES).optional().describe("Only ask agents whose submission carries this category."),
15298
+ attachments: attachmentReferencesSchema.optional().describe("Uploaded files, each {id, token, description?}; every agent asked can open them."),
15299
+ idempotencyKey: external_exports.string().regex(/^[A-Za-z0-9_-]{8,64}$/).optional().describe(
15300
+ "Same key + same project = the same question, so a retried publish never duplicates."
15301
+ )
15302
+ });
15303
+ var LEGACY_CATEGORIES = ["eval"];
15568
15304
  var EVENT_STATUSES = [
15569
15305
  "new",
15570
15306
  "seen",
@@ -15573,10 +15309,19 @@ var EVENT_STATUSES = [
15573
15309
  "wont_fix",
15574
15310
  "spam"
15575
15311
  ];
15576
- var ISSUE_STATUSES = ["open", "in_progress", "resolved", "wont_fix", "spam"];
15312
+ var ISSUE_STATUSES = [
15313
+ "open",
15314
+ "in_progress",
15315
+ "needs_review",
15316
+ "resolved",
15317
+ "wont_fix",
15318
+ "spam"
15319
+ ];
15320
+ var issueStatusSchema = external_exports.enum(ISSUE_STATUSES);
15577
15321
  var VENDOR_CATEGORY_PATTERN = /^x-[a-z0-9][a-z0-9-]{0,38}$/;
15578
15322
  var categorySchema = external_exports.union([
15579
15323
  external_exports.enum(FEEDBACK_CATEGORIES),
15324
+ external_exports.enum(LEGACY_CATEGORIES),
15580
15325
  external_exports.string().regex(VENDOR_CATEGORY_PATTERN, "vendor categories must match x-<kebab-case>")
15581
15326
  ]);
15582
15327
  var sentimentSchema = external_exports.enum(["positive", "negative", "neutral"]);
@@ -15638,6 +15383,10 @@ var feedbackEventSchema = external_exports.object({
15638
15383
  author: authorSchema.optional(),
15639
15384
  thread: external_exports.object({ id: external_exports.string().regex(THREAD_ID_PATTERN) }),
15640
15385
  eval: evalReportSchema.optional(),
15386
+ /** Id of an owner/system question this submission answers (from a prior response's `ask`). */
15387
+ question: external_exports.string().max(64).optional(),
15388
+ /** Uploaded files, committed with the event or not at all. Bytes never travel in feedback JSON. */
15389
+ attachments: attachmentReferencesSchema.optional(),
15641
15390
  context: feedbackContextSchema.optional(),
15642
15391
  metadata: metadataSchema.optional(),
15643
15392
  /** Client-minted retry key: same key + same project = same stored event. */
@@ -15648,6 +15397,31 @@ var feedbackEventSchema = external_exports.object({
15648
15397
  }),
15649
15398
  consent: external_exports.object({ telemetry: external_exports.boolean() })
15650
15399
  });
15400
+ var knownIssueSchema = external_exports.object({
15401
+ title: external_exports.string().max(120),
15402
+ /** wont_fix is disclosed only when the owner attached a closing note. */
15403
+ status: external_exports.enum(["open", "wont_fix"]),
15404
+ /** This very report flipped a resolved issue back open — already recorded. */
15405
+ reopened: external_exports.boolean().optional(),
15406
+ /** Total reports on this issue, including this one. */
15407
+ reports: external_exports.number().int().positive(),
15408
+ /** The owner's closing note, verbatim — their explanation of the decision. */
15409
+ note: external_exports.string().max(MAX_GUIDANCE_LENGTH).optional(),
15410
+ /** Server-composed next step for the submitting agent. */
15411
+ hint: external_exports.string().max(300)
15412
+ });
15413
+ var ownerIssueSchema = external_exports.object({
15414
+ id: external_exports.string(),
15415
+ title: external_exports.string(),
15416
+ summary: external_exports.string().nullable(),
15417
+ status: issueStatusSchema,
15418
+ fingerprint: external_exports.string().nullable(),
15419
+ event_count: external_exports.number(),
15420
+ reopened_count: external_exports.number(),
15421
+ first_seen: external_exports.string(),
15422
+ last_seen: external_exports.string(),
15423
+ links_json: external_exports.string()
15424
+ });
15651
15425
  var ingestResponseSchema = external_exports.object({
15652
15426
  id: external_exports.string().max(64),
15653
15427
  thread: external_exports.string().regex(THREAD_ID_PATTERN),
@@ -15655,6 +15429,19 @@ var ingestResponseSchema = external_exports.object({
15655
15429
  resume: external_exports.string().max(512).optional(),
15656
15430
  /** Vendor-configured answer back to the submitting agent (known issues, workarounds). */
15657
15431
  guidance: external_exports.string().max(MAX_GUIDANCE_LENGTH).optional(),
15432
+ /** The report matched an already-tracked issue; recorded — don't file variants. */
15433
+ known_issue: knownIssueSchema.optional(),
15434
+ /**
15435
+ * A question from the project's team, delivered at most one per response.
15436
+ * Answering is optional and must be grounded in what the agent actually did;
15437
+ * `command` is ready to run and files the answer on this thread.
15438
+ */
15439
+ ask: external_exports.object({
15440
+ id: external_exports.string().max(64),
15441
+ prompt: external_exports.string().max(500),
15442
+ command: external_exports.string().max(512),
15443
+ attachments: external_exports.array(deliveredAttachmentSchema).optional()
15444
+ }).optional(),
15658
15445
  /** True when idempotencyKey matched an already-stored event; id/thread are the original's. */
15659
15446
  duplicate: external_exports.boolean().optional()
15660
15447
  });
@@ -15683,50 +15470,546 @@ var repoEntrySchema = external_exports.object({
15683
15470
  var repoListSchema = external_exports.array(repoEntrySchema).max(20);
15684
15471
  function formatIssues(error51) {
15685
15472
  return error51.issues.map((issue2) => {
15686
- const path5 = issue2.path.join(".");
15687
- return path5 ? `${path5}: ${issue2.message}` : issue2.message;
15473
+ const path6 = issue2.path.join(".");
15474
+ return path6 ? `${path6}: ${issue2.message}` : issue2.message;
15688
15475
  });
15689
15476
  }
15690
- function validateFeedbackEvent(input) {
15691
- const result = feedbackEventSchema.safeParse(input);
15692
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15477
+ function validateFeedbackEvent(input) {
15478
+ const result = feedbackEventSchema.safeParse(input);
15479
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15480
+ }
15481
+ function validateDescriptor(input) {
15482
+ const result = descriptorSchema.safeParse(input);
15483
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15484
+ }
15485
+ function validateIngestResponse(input) {
15486
+ const result = ingestResponseSchema.safeParse(input);
15487
+ return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15488
+ }
15489
+ function feedbackEventJsonSchema() {
15490
+ return external_exports.toJSONSchema(feedbackEventSchema);
15491
+ }
15492
+ function descriptorJsonSchema() {
15493
+ return external_exports.toJSONSchema(descriptorSchema);
15494
+ }
15495
+
15496
+ // ../core/dist/index.js
15497
+ var import_fs = require("fs");
15498
+ var import_path3 = __toESM(require("path"), 1);
15499
+
15500
+ // ../../node_modules/.pnpm/agent-cli-detector@0.1.5/node_modules/agent-cli-detector/dist/index.js
15501
+ init_cjs_shims();
15502
+ var import_child_process = require("child_process");
15503
+ var import_path = __toESM(require("path"), 1);
15504
+ var defaultAgents = [
15505
+ {
15506
+ id: "codex",
15507
+ name: "Codex",
15508
+ env: [
15509
+ { name: "CODEX_CI", value: "1" },
15510
+ { name: "CODEX_SHELL", value: "1" },
15511
+ { name: "CODEX_THREAD_ID" }
15512
+ ],
15513
+ process: [{ pattern: /^codex$/i }],
15514
+ sessionEnv: ["CODEX_THREAD_ID"]
15515
+ },
15516
+ {
15517
+ id: "cursor",
15518
+ name: "Cursor",
15519
+ env: [
15520
+ { name: "CURSOR_AGENT", value: "1" },
15521
+ { name: "CURSOR_CONVERSATION_ID" }
15522
+ ],
15523
+ process: [{ pattern: /^cursor$/i }],
15524
+ sessionEnv: ["CURSOR_CONVERSATION_ID"]
15525
+ },
15526
+ {
15527
+ id: "claude-code",
15528
+ name: "Claude Code",
15529
+ env: [
15530
+ { name: "CLAUDECODE", value: "1" },
15531
+ { name: "CLAUDE_CODE_SESSION_ID" }
15532
+ ],
15533
+ process: [{ pattern: /^claude$/i }],
15534
+ sessionEnv: ["CLAUDE_CODE_SESSION_ID"]
15535
+ },
15536
+ {
15537
+ id: "opencode",
15538
+ name: "OpenCode",
15539
+ env: [
15540
+ { name: "OPENCODE", value: "1" },
15541
+ { name: "OPENCODE_PID" }
15542
+ ],
15543
+ process: [{ pattern: /^opencode$/i }]
15544
+ },
15545
+ {
15546
+ id: "devin",
15547
+ name: "Devin",
15548
+ process: [{ pattern: /devin/i }]
15549
+ },
15550
+ {
15551
+ id: "gemini",
15552
+ name: "Gemini CLI",
15553
+ env: [{ name: "GEMINI_CLI", value: "1" }],
15554
+ process: [{ pattern: /^gemini$/i }]
15555
+ },
15556
+ {
15557
+ id: "antigravity",
15558
+ name: "Antigravity",
15559
+ env: [
15560
+ { name: "ANTIGRAVITY_AGENT", value: "1" },
15561
+ { name: "ANTIGRAVITY_TRAJECTORY_ID" }
15562
+ ],
15563
+ process: [{ pattern: /^antigravity$/i }],
15564
+ sessionEnv: ["ANTIGRAVITY_TRAJECTORY_ID"]
15565
+ },
15566
+ {
15567
+ id: "bolt",
15568
+ name: "Bolt",
15569
+ env: [
15570
+ { name: "BOLT_ENV" },
15571
+ { name: "BOLT_ORIGIN" },
15572
+ { name: "BOLT_SERVER_URL" }
15573
+ ]
15574
+ },
15575
+ {
15576
+ id: "pi",
15577
+ name: "Pi",
15578
+ env: [{ name: "PI_CODING_AGENT", value: "true" }]
15579
+ },
15580
+ {
15581
+ id: "replit",
15582
+ name: "Replit",
15583
+ env: [{ name: "REPLIT_SESSION" }],
15584
+ sessionEnv: ["REPLIT_SESSION"]
15585
+ },
15586
+ {
15587
+ id: "rork",
15588
+ name: "Rork",
15589
+ env: [{ name: "RORK_API_URL" }]
15590
+ },
15591
+ {
15592
+ id: "kiro",
15593
+ name: "Kiro",
15594
+ env: [{ name: "KIRO_SESSION_ID" }],
15595
+ process: [{ pattern: /kiro/i }],
15596
+ sessionEnv: ["KIRO_SESSION_ID"]
15597
+ },
15598
+ {
15599
+ id: "cline",
15600
+ name: "Cline",
15601
+ env: [{ name: "CLINE_WRAPPER_PATH" }],
15602
+ process: [{ pattern: /^cline$/i }]
15603
+ },
15604
+ {
15605
+ id: "kilocode",
15606
+ name: "Kilo Code",
15607
+ env: [
15608
+ { name: "KILO", value: "1" },
15609
+ { name: "KILOCODE_VERSION" },
15610
+ { name: "KILO_RUN_ID" }
15611
+ ],
15612
+ process: [{ pattern: /^(kilo|kilocode)$/i }],
15613
+ sessionEnv: ["KILO_RUN_ID"]
15614
+ },
15615
+ {
15616
+ id: "copilot",
15617
+ name: "GitHub Copilot CLI",
15618
+ env: [
15619
+ { name: "COPILOT_CLI", value: "1" },
15620
+ { name: "COPILOT_AGENT_SESSION_ID" },
15621
+ { name: "COPILOT_RUN_APP", value: "1" }
15622
+ ],
15623
+ process: [{ pattern: /^(github-copilot-cli|copilot)$/i }],
15624
+ sessionEnv: ["COPILOT_AGENT_SESSION_ID"]
15625
+ }
15626
+ ];
15627
+ var EnvironmentDetectionStrategy = class {
15628
+ name = "environment";
15629
+ detect(context) {
15630
+ const matches = [];
15631
+ for (const agent of context.agents) {
15632
+ for (const signal of agent.env ?? []) {
15633
+ const value = context.env[signal.name];
15634
+ if (value === void 0 || !matchesEnvValue(value, signal.value)) {
15635
+ continue;
15636
+ }
15637
+ matches.push(toEvidence(agent, signal, value));
15638
+ }
15639
+ }
15640
+ return matches;
15641
+ }
15642
+ };
15643
+ function matchesEnvValue(value, matcher) {
15644
+ if (matcher === void 0) {
15645
+ return true;
15646
+ }
15647
+ if (typeof matcher === "string") {
15648
+ return value === matcher;
15649
+ }
15650
+ if (matcher instanceof RegExp) {
15651
+ return matcher.test(value);
15652
+ }
15653
+ return matcher(value);
15654
+ }
15655
+ function toEvidence(agent, signal, value) {
15656
+ return {
15657
+ agent: {
15658
+ id: agent.id,
15659
+ name: agent.name
15660
+ },
15661
+ strategy: "environment",
15662
+ signal: signal.name,
15663
+ value
15664
+ };
15665
+ }
15666
+ var PsProcessReader = class {
15667
+ read(pid) {
15668
+ const command = readPsField(pid, "comm");
15669
+ const ppid = Number.parseInt(readPsField(pid, "ppid"), 10);
15670
+ if (!command || !Number.isInteger(ppid)) {
15671
+ return void 0;
15672
+ }
15673
+ return {
15674
+ pid,
15675
+ ppid,
15676
+ command: normalizeCommand(command)
15677
+ };
15678
+ }
15679
+ };
15680
+ var ProcessTreeDetectionStrategy = class {
15681
+ constructor(reader = new PsProcessReader()) {
15682
+ this.reader = reader;
15683
+ }
15684
+ reader;
15685
+ name = "process-tree";
15686
+ detect(context) {
15687
+ const matches = [];
15688
+ let currentPid = context.pid;
15689
+ for (let depth = 0; depth < context.maxProcessDepth; depth += 1) {
15690
+ const processInfo = this.reader.read(currentPid);
15691
+ if (processInfo === void 0) {
15692
+ break;
15693
+ }
15694
+ matches.push(...matchProcess(context.agents, processInfo));
15695
+ if (processInfo.ppid === void 0 || processInfo.ppid <= 1 || processInfo.ppid === currentPid) {
15696
+ break;
15697
+ }
15698
+ currentPid = processInfo.ppid;
15699
+ }
15700
+ return matches;
15701
+ }
15702
+ };
15703
+ function matchProcess(agents, processInfo) {
15704
+ const matches = [];
15705
+ const command = normalizeCommand(processInfo.command);
15706
+ for (const agent of agents) {
15707
+ for (const signal of agent.process ?? []) {
15708
+ if (!signal.pattern.test(command)) {
15709
+ continue;
15710
+ }
15711
+ matches.push(toEvidence2(agent, signal, { ...processInfo, command }));
15712
+ }
15713
+ }
15714
+ return matches;
15715
+ }
15716
+ function toEvidence2(agent, signal, processInfo) {
15717
+ return {
15718
+ agent: {
15719
+ id: agent.id,
15720
+ name: agent.name
15721
+ },
15722
+ strategy: "process-tree",
15723
+ signal: signal.pattern.toString(),
15724
+ value: `${processInfo.pid}:${processInfo.command}`
15725
+ };
15693
15726
  }
15694
- function validateDescriptor(input) {
15695
- const result = descriptorSchema.safeParse(input);
15696
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15727
+ function readPsField(pid, field) {
15728
+ try {
15729
+ return (0, import_child_process.execFileSync)("ps", ["-o", `${field}=`, "-p", String(pid)], {
15730
+ encoding: "utf8",
15731
+ stdio: ["ignore", "pipe", "ignore"]
15732
+ }).trim();
15733
+ } catch {
15734
+ return "";
15735
+ }
15697
15736
  }
15698
- function validateIngestResponse(input) {
15699
- const result = ingestResponseSchema.safeParse(input);
15700
- return result.success ? { ok: true, data: result.data } : { ok: false, errors: formatIssues(result.error) };
15737
+ function normalizeCommand(command) {
15738
+ const baseName = import_path.default.basename(command.trim());
15739
+ return baseName.replace(/^-+/, "");
15701
15740
  }
15702
- function feedbackEventJsonSchema() {
15703
- return external_exports.toJSONSchema(feedbackEventSchema);
15741
+ function detectAgent(options = {}) {
15742
+ const agents = options.agents ?? defaultAgents;
15743
+ const strategies = options.strategies ?? defaultStrategies(options.experimentalProcessTree);
15744
+ const context = {
15745
+ env: options.env ?? process.env,
15746
+ pid: options.pid ?? process.pid,
15747
+ agents,
15748
+ maxProcessDepth: options.maxProcessDepth ?? 20
15749
+ };
15750
+ const matches = strategies.flatMap((strategy) => strategy.detect(context));
15751
+ const bestMatch = selectBestAgent(agents, matches, context.env);
15752
+ if (bestMatch === void 0) {
15753
+ return {
15754
+ detected: false
15755
+ };
15756
+ }
15757
+ return {
15758
+ detected: true,
15759
+ agent: bestMatch
15760
+ };
15704
15761
  }
15705
- function descriptorJsonSchema() {
15706
- return external_exports.toJSONSchema(descriptorSchema);
15762
+ function defaultStrategies(experimentalProcessTree = false) {
15763
+ const strategies = [new EnvironmentDetectionStrategy()];
15764
+ if (experimentalProcessTree) {
15765
+ strategies.push(new ProcessTreeDetectionStrategy());
15766
+ }
15767
+ return strategies;
15768
+ }
15769
+ function selectBestAgent(agents, matches, env) {
15770
+ const matchedAgentIds = new Set(matches.map((match) => match.agent.id));
15771
+ for (const agent of agents) {
15772
+ if (!matchedAgentIds.has(agent.id)) {
15773
+ continue;
15774
+ }
15775
+ return {
15776
+ id: agent.id,
15777
+ name: agent.name,
15778
+ ...sessionIdProperties(agent, env)
15779
+ };
15780
+ }
15781
+ return void 0;
15782
+ }
15783
+ function sessionIdProperties(agent, env) {
15784
+ for (const envName of agent.sessionEnv ?? []) {
15785
+ const value = env[envName];
15786
+ if (value !== void 0 && value.length > 0) {
15787
+ return { sessionId: value };
15788
+ }
15789
+ }
15790
+ return {};
15707
15791
  }
15708
15792
 
15709
15793
  // ../core/dist/index.js
15710
- var import_fs3 = require("fs");
15794
+ var import_ci_info = __toESM(require_ci_info(), 1);
15795
+
15796
+ // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/index.js
15797
+ init_cjs_shims();
15798
+
15799
+ // ../../node_modules/.pnpm/sandbox-cli-detector@0.2.0/node_modules/sandbox-cli-detector/dist/chunk-HZH3SATY.js
15800
+ init_cjs_shims();
15801
+ var defaultSandboxes = [
15802
+ {
15803
+ id: "replit",
15804
+ name: "Replit",
15805
+ env: [{ name: "REPLIT_SESSION" }, { name: "REPLIT_CONTAINER" }, { name: "REPLIT_USER" }]
15806
+ },
15807
+ {
15808
+ id: "bolt",
15809
+ name: "bolt.new",
15810
+ env: [{ name: "BOLT_ENV" }, { name: "BOLT_ORIGIN" }, { name: "BOLT_SERVER_URL" }]
15811
+ },
15812
+ {
15813
+ id: "e2b",
15814
+ name: "E2B",
15815
+ env: [{ name: "E2B_SANDBOX", value: "true" }]
15816
+ },
15817
+ {
15818
+ id: "vercel-sandbox",
15819
+ name: "Vercel Sandbox",
15820
+ env: [{ name: "HOME", value: "/home/vercel-sandbox" }]
15821
+ },
15822
+ {
15823
+ id: "daytona",
15824
+ name: "Daytona",
15825
+ env: [{ name: "DAYTONA_SANDBOX_ID" }]
15826
+ },
15827
+ {
15828
+ id: "modal",
15829
+ name: "Modal",
15830
+ env: [{ name: "MODAL_SANDBOX_ID" }, { name: "MODAL_TASK_ID" }]
15831
+ },
15832
+ {
15833
+ id: "cloudflare-sandbox",
15834
+ name: "Cloudflare Sandbox",
15835
+ env: [{ name: "CLOUDFLARE_DURABLE_OBJECT_ID" }]
15836
+ },
15837
+ {
15838
+ id: "codespaces",
15839
+ name: "GitHub Codespaces",
15840
+ env: [{ name: "CODESPACES", value: "true" }]
15841
+ },
15842
+ {
15843
+ id: "codesandbox",
15844
+ name: "CodeSandbox",
15845
+ env: [{ name: "CSB", value: "true" }, { name: "CSB_SANDBOX_ID" }]
15846
+ }
15847
+ ];
15848
+ function detectSandbox(options = {}) {
15849
+ const env = options.env ?? process.env;
15850
+ const sandboxes = options.sandboxes ?? defaultSandboxes;
15851
+ const sandbox = sandboxes.find(
15852
+ (candidate) => candidate.env.some((signal) => matchesEnvSignal(signal, env))
15853
+ );
15854
+ if (sandbox === void 0) return { detected: false };
15855
+ return {
15856
+ detected: true,
15857
+ sandbox: { id: sandbox.id, name: sandbox.name }
15858
+ };
15859
+ }
15860
+ function matchesEnvSignal(signal, env) {
15861
+ const value = env[signal.name];
15862
+ return value !== void 0 && value !== "" && (signal.value === void 0 || value === signal.value);
15863
+ }
15864
+
15865
+ // ../core/dist/index.js
15866
+ var import_os = require("os");
15867
+ var import_path4 = __toESM(require("path"), 1);
15868
+ var import_fs2 = require("fs");
15711
15869
  var import_path5 = __toESM(require("path"), 1);
15712
- var import_crypto = require("crypto");
15870
+ var import_crypto2 = require("crypto");
15871
+ var import_fs3 = require("fs");
15872
+ var import_path6 = __toESM(require("path"), 1);
15873
+ var import_crypto3 = require("crypto");
15713
15874
  var import_module = require("module");
15875
+ var FeedbackError = class extends Error {
15876
+ constructor(message) {
15877
+ super(message);
15878
+ this.name = "FeedbackError";
15879
+ }
15880
+ };
15881
+ async function readLocalAttachments(inputs, cwd = process.cwd()) {
15882
+ if (inputs.length > MAX_ATTACHMENTS)
15883
+ throw new FeedbackError(`At most ${MAX_ATTACHMENTS} files can be attached.`);
15884
+ const files = [];
15885
+ for (const input of inputs) {
15886
+ const selected = typeof input === "string" ? input : input.path;
15887
+ const filename = import_path2.default.basename(selected);
15888
+ const description = attachmentDescriptionSchema.optional().safeParse(typeof input === "string" ? void 0 : input.description);
15889
+ if (!description.success)
15890
+ throw new FeedbackError(
15891
+ `Invalid description for "${filename}": ${description.error.issues.map((issue2) => issue2.message).join("; ")}`
15892
+ );
15893
+ const file2 = import_path2.default.resolve(cwd, selected);
15894
+ const info = await (0, import_promises.stat)(file2).catch(() => null);
15895
+ if (!info?.isFile())
15896
+ throw new FeedbackError(`Cannot attach "${selected}": choose an existing, readable file.`);
15897
+ if (info.size < 1 || info.size > MAX_ATTACHMENT_BYTES)
15898
+ throw new FeedbackError(`"${filename}" must be between 1 byte and 20 MiB.`);
15899
+ const bytes = new Uint8Array(
15900
+ await (0, import_promises.readFile)(file2).catch(() => {
15901
+ throw new FeedbackError(`Cannot read "${selected}".`);
15902
+ })
15903
+ );
15904
+ const manifest = attachmentManifestSchema.safeParse({
15905
+ filename,
15906
+ mediaType: mediaTypeForFilename(filename),
15907
+ size: bytes.length,
15908
+ sha256: (0, import_crypto.createHash)("sha256").update(bytes).digest("hex")
15909
+ });
15910
+ if (!manifest.success)
15911
+ throw new FeedbackError(
15912
+ `Cannot attach "${filename}": ${manifest.error.issues.map((issue2) => issue2.message).join("; ")}`
15913
+ );
15914
+ files.push({
15915
+ manifest: manifest.data,
15916
+ bytes,
15917
+ ...description.data ? { description: description.data } : {}
15918
+ });
15919
+ }
15920
+ return files;
15921
+ }
15922
+ var AttachmentUploadError = class extends FeedbackError {
15923
+ constructor(message, rejected) {
15924
+ super(message);
15925
+ this.rejected = rejected;
15926
+ this.name = "AttachmentUploadError";
15927
+ }
15928
+ rejected;
15929
+ };
15930
+ async function send(fetchImpl, url2, init, timeoutMs) {
15931
+ let response;
15932
+ try {
15933
+ response = await fetchImpl(url2, {
15934
+ ...init,
15935
+ redirect: "error",
15936
+ signal: AbortSignal.timeout(timeoutMs)
15937
+ });
15938
+ } catch {
15939
+ throw new AttachmentUploadError("Could not reach the server to upload files.", false);
15940
+ }
15941
+ if (response.ok) return response;
15942
+ const body = await response.json().catch(() => null);
15943
+ const errors = Array.isArray(body?.errors) ? body.errors.map(String).join("; ") : void 0;
15944
+ if (response.status === 404 && !errors)
15945
+ throw new AttachmentUploadError("This server does not accept files.", false);
15946
+ throw new AttachmentUploadError(
15947
+ errors ?? `File upload failed (${response.status}).`,
15948
+ !(response.status >= 500 || response.status === 429 || response.status === 408)
15949
+ );
15950
+ }
15951
+ async function uploadFiles(options) {
15952
+ const fetchImpl = options.fetchImpl ?? fetch;
15953
+ const timeoutMs = options.timeoutMs ?? 6e4;
15954
+ const origin = new URL(options.endpoint).origin;
15955
+ const references = [];
15956
+ for (const file2 of options.files) {
15957
+ const response = await send(
15958
+ fetchImpl,
15959
+ `${origin}${ATTACHMENT_UPLOAD_PATH}`,
15960
+ {
15961
+ method: "POST",
15962
+ headers: {
15963
+ "content-type": "application/json",
15964
+ ...options.secretKey ? { authorization: `Bearer ${options.secretKey}` } : {},
15965
+ ...options.writeKey ? { [WRITE_KEY_HEADER]: options.writeKey } : {}
15966
+ },
15967
+ body: JSON.stringify({
15968
+ ...file2.manifest,
15969
+ to: options.to,
15970
+ scope: options.secretKey ? "owner" : "reporter",
15971
+ uploader: options.uploader
15972
+ })
15973
+ },
15974
+ timeoutMs
15975
+ );
15976
+ const prepared = preparedAttachmentSchema.safeParse(await response.json().catch(() => null));
15977
+ if (!prepared.success || new URL(prepared.data.uploadUrl).origin !== origin)
15978
+ throw new AttachmentUploadError("The server returned an invalid upload response.", true);
15979
+ await send(
15980
+ fetchImpl,
15981
+ prepared.data.uploadUrl,
15982
+ {
15983
+ method: "PUT",
15984
+ headers: { authorization: `Bearer ${prepared.data.reference.token}` },
15985
+ body: file2.bytes
15986
+ },
15987
+ // At least 100 KB/s: a 20 MiB file on a slow link still gets about 3.5 minutes.
15988
+ Math.max(timeoutMs, file2.bytes.length / 100)
15989
+ );
15990
+ references.push({
15991
+ ...prepared.data.reference,
15992
+ ...file2.description ? { description: file2.description } : {}
15993
+ });
15994
+ }
15995
+ return references;
15996
+ }
15714
15997
  var DSN_ENV_VAR = "HIVENET_DSN";
15715
15998
  var REGISTRY_ENV_VAR = "HIVENET_REGISTRY";
15716
15999
  var HOME_ENV_VAR = "HIVENET_HOME";
15717
16000
  var NO_TELEMETRY_ENV_VAR = "HIVENET_NO_TELEMETRY";
15718
16001
  function feedbackHomeDir(env = process.env) {
15719
16002
  const override = env[HOME_ENV_VAR];
15720
- return override?.trim() ? override : import_path3.default.join((0, import_os.homedir)(), ".hivenet");
16003
+ return override?.trim() ? override : import_path4.default.join((0, import_os.homedir)(), ".hivenet");
15721
16004
  }
15722
16005
  function isTelemetryDisabled(extraOptOutEnvVars = [], env = process.env) {
15723
16006
  const vars = ["DO_NOT_TRACK", NO_TELEMETRY_ENV_VAR, ...extraOptOutEnvVars];
15724
16007
  return vars.some((name) => env[name] === "1" || env[name] === "true");
15725
16008
  }
15726
16009
  var MAX_SESSION_ID_LENGTH = 256;
15727
- function detectAgentSafe() {
16010
+ function detectAgentSafe(env) {
15728
16011
  try {
15729
- const result = detectAgent();
16012
+ const result = detectAgent({ env });
15730
16013
  if (result.detected && result.agent) {
15731
16014
  return {
15732
16015
  id: result.agent.id,
@@ -15738,9 +16021,9 @@ function detectAgentSafe() {
15738
16021
  }
15739
16022
  return void 0;
15740
16023
  }
15741
- function detectSandboxSafe() {
16024
+ function detectSandboxSafe(env) {
15742
16025
  try {
15743
- const result = detectSandbox();
16026
+ const result = detectSandbox({ env });
15744
16027
  if (result.detected && result.sandbox) {
15745
16028
  return {
15746
16029
  id: result.sandbox.id,
@@ -15805,7 +16088,7 @@ function resolveSessions(sources, projectRoot, env) {
15805
16088
  }
15806
16089
  const value = "envVar" in source ? normalizeSessionId(env[source.envVar]) : normalizeSessionId(
15807
16090
  readDotenvKey(
15808
- import_path2.default.resolve(projectRoot, source.dotenvFile),
16091
+ import_path3.default.resolve(projectRoot, source.dotenvFile),
15809
16092
  source.dotenvKey ?? source.key
15810
16093
  )
15811
16094
  );
@@ -15821,8 +16104,8 @@ async function collectContext(options = {}) {
15821
16104
  return void 0;
15822
16105
  }
15823
16106
  const projectRoot = options.projectRoot ?? process.cwd();
15824
- const agent = detectAgentSafe();
15825
- const sandbox = detectSandboxSafe();
16107
+ const agent = detectAgentSafe(env);
16108
+ const sandbox = detectSandboxSafe(env);
15826
16109
  const ci = detectCi();
15827
16110
  const session = resolveSessions(options.sessions ?? [], projectRoot, env);
15828
16111
  let project;
@@ -15845,12 +16128,6 @@ async function collectContext(options = {}) {
15845
16128
  function inferAuthor(context) {
15846
16129
  return context?.agent ? "agent" : void 0;
15847
16130
  }
15848
- var FeedbackError = class extends Error {
15849
- constructor(message) {
15850
- super(message);
15851
- this.name = "FeedbackError";
15852
- }
15853
- };
15854
16131
  var CONFIG_FILE_NAME = "feedback.config.json";
15855
16132
  var DESCRIPTOR_CACHE_TTL_MS = 60 * 60 * 1e3;
15856
16133
  var RESOLVE_TIMEOUT_MS = 5e3;
@@ -15875,9 +16152,9 @@ function parseDsn(dsn) {
15875
16152
  };
15876
16153
  }
15877
16154
  function findConfigFile(startDir) {
15878
- let current = import_path4.default.resolve(startDir);
16155
+ let current = import_path5.default.resolve(startDir);
15879
16156
  while (true) {
15880
- const candidate = import_path4.default.join(current, CONFIG_FILE_NAME);
16157
+ const candidate = import_path5.default.join(current, CONFIG_FILE_NAME);
15881
16158
  try {
15882
16159
  const raw = (0, import_fs2.readFileSync)(candidate, "utf8");
15883
16160
  const parsed = JSON.parse(raw);
@@ -15886,7 +16163,7 @@ function findConfigFile(startDir) {
15886
16163
  }
15887
16164
  } catch {
15888
16165
  }
15889
- const parent = import_path4.default.dirname(current);
16166
+ const parent = import_path5.default.dirname(current);
15890
16167
  if (parent === current) {
15891
16168
  return void 0;
15892
16169
  }
@@ -15894,7 +16171,7 @@ function findConfigFile(startDir) {
15894
16171
  }
15895
16172
  }
15896
16173
  function cacheFilePath(cacheDir) {
15897
- return import_path4.default.join(cacheDir, "descriptors.json");
16174
+ return import_path5.default.join(cacheDir, "descriptors.json");
15898
16175
  }
15899
16176
  function readCache(cacheDir) {
15900
16177
  try {
@@ -16019,7 +16296,7 @@ async function postEvent(options) {
16019
16296
  error: error51 instanceof Error ? error51.message : String(error51)
16020
16297
  };
16021
16298
  }
16022
- if (response.status >= 500) {
16299
+ if (response.status >= 500 || response.status === 429 || response.status === 408) {
16023
16300
  return { kind: "unreachable", error: `server error (${response.status})` };
16024
16301
  }
16025
16302
  if (!response.ok) {
@@ -16047,13 +16324,14 @@ async function postEvent(options) {
16047
16324
  }
16048
16325
  var OUTBOX_FILE = "outbox.jsonl";
16049
16326
  var MAX_OUTBOX_ENTRIES = 100;
16327
+ var STALE_CLAIM_MS = 30 * 60 * 1e3;
16050
16328
  function outboxPath(env = process.env) {
16051
- return import_path5.default.join(feedbackHomeDir(env), OUTBOX_FILE);
16329
+ return import_path6.default.join(feedbackHomeDir(env), OUTBOX_FILE);
16052
16330
  }
16053
16331
  function appendToOutbox(entry, env = process.env) {
16054
16332
  try {
16055
16333
  const file2 = outboxPath(env);
16056
- (0, import_fs3.mkdirSync)(import_path5.default.dirname(file2), { recursive: true });
16334
+ (0, import_fs3.mkdirSync)(import_path6.default.dirname(file2), { recursive: true });
16057
16335
  (0, import_fs3.appendFileSync)(file2, `${JSON.stringify(entry)}
16058
16336
  `);
16059
16337
  return true;
@@ -16061,19 +16339,43 @@ function appendToOutbox(entry, env = process.env) {
16061
16339
  return false;
16062
16340
  }
16063
16341
  }
16064
- function readOutbox(env) {
16342
+ function readEntries(file2) {
16343
+ try {
16344
+ return (0, import_fs3.readFileSync)(file2, "utf8").split("\n").filter((line) => line.trim()).flatMap((line) => {
16345
+ try {
16346
+ return [JSON.parse(line)];
16347
+ } catch {
16348
+ return [];
16349
+ }
16350
+ });
16351
+ } catch {
16352
+ return [];
16353
+ }
16354
+ }
16355
+ function claimQueue(env) {
16356
+ const file2 = outboxPath(env);
16357
+ let names;
16065
16358
  try {
16066
- return (0, import_fs3.readFileSync)(outboxPath(env), "utf8").split("\n").filter((line) => line.trim()).slice(-MAX_OUTBOX_ENTRIES).map((line) => JSON.parse(line));
16359
+ names = (0, import_fs3.readdirSync)(import_path6.default.dirname(file2));
16067
16360
  } catch {
16068
16361
  return [];
16069
16362
  }
16363
+ const claims = names.filter((name) => {
16364
+ const claimedAt = Number(/\.flushing-(\d+)-/.exec(name)?.[1]);
16365
+ return name.startsWith(OUTBOX_FILE) && Date.now() - claimedAt > STALE_CLAIM_MS;
16366
+ }).map((name) => import_path6.default.join(import_path6.default.dirname(file2), name));
16367
+ const fresh = `${file2}.flushing-${Date.now()}-${(0, import_crypto2.randomUUID)()}`;
16368
+ try {
16369
+ (0, import_fs3.renameSync)(file2, fresh);
16370
+ claims.push(fresh);
16371
+ } catch {
16372
+ }
16373
+ return claims;
16070
16374
  }
16071
16375
  async function flushOutbox(options = {}) {
16072
16376
  const env = options.env ?? process.env;
16073
- const entries = readOutbox(env);
16074
- if (entries.length === 0) {
16075
- return { delivered: 0, remaining: 0 };
16076
- }
16377
+ const claims = claimQueue(env);
16378
+ const entries = claims.flatMap(readEntries).slice(-MAX_OUTBOX_ENTRIES);
16077
16379
  const failures = [];
16078
16380
  let delivered = 0;
16079
16381
  for (const entry of entries) {
@@ -16090,12 +16392,13 @@ async function flushOutbox(options = {}) {
16090
16392
  failures.push(entry);
16091
16393
  }
16092
16394
  }
16093
- try {
16094
- (0, import_fs3.writeFileSync)(
16095
- outboxPath(env),
16096
- failures.map((entry) => JSON.stringify(entry)).join("\n") + (failures.length ? "\n" : "")
16097
- );
16098
- } catch {
16395
+ if (failures.every((entry) => appendToOutbox(entry, env))) {
16396
+ for (const claim of claims) {
16397
+ try {
16398
+ (0, import_fs3.rmSync)(claim, { force: true });
16399
+ } catch {
16400
+ }
16401
+ }
16099
16402
  }
16100
16403
  return { delivered, remaining: failures.length };
16101
16404
  }
@@ -16115,10 +16418,10 @@ function ownerApi(options) {
16115
16418
  const origin = (options.apiOrigin ?? new URL(DEFAULT_REGISTRY_URL).origin).replace(/\/$/, "");
16116
16419
  const fetchImpl = options.fetchImpl ?? fetch;
16117
16420
  const timeoutMs = options.timeoutMs ?? 15e3;
16118
- async function request(path5, init) {
16421
+ async function request(path6, init) {
16119
16422
  let response;
16120
16423
  try {
16121
- response = await fetchImpl(`${origin}${path5}`, {
16424
+ response = await fetchImpl(`${origin}${path6}`, {
16122
16425
  ...init,
16123
16426
  signal: AbortSignal.timeout(timeoutMs),
16124
16427
  headers: {
@@ -16146,7 +16449,34 @@ function ownerApi(options) {
16146
16449
  }
16147
16450
  return body;
16148
16451
  }
16452
+ async function upload(paths, cwd) {
16453
+ return uploadFiles({
16454
+ endpoint: origin,
16455
+ files: await readLocalAttachments(paths, cwd),
16456
+ secretKey: options.secretKey,
16457
+ fetchImpl,
16458
+ timeoutMs: Math.max(timeoutMs, 6e4)
16459
+ });
16460
+ }
16149
16461
  return {
16462
+ /** Upload local files and attach them to a record (at most 10 files per record). */
16463
+ async attachFiles(target, paths, cwd) {
16464
+ if (!paths.length) throw new FeedbackError("Select at least one file.");
16465
+ const attachments = await upload(paths, cwd);
16466
+ return request("/v1/attachments/attach", {
16467
+ method: "POST",
16468
+ body: JSON.stringify({ target, attachments })
16469
+ });
16470
+ },
16471
+ /** A record's files; issues and evals include their reports' files. */
16472
+ async listAttachments(target, offset = 0) {
16473
+ return request(
16474
+ `/v1/attachments?${new URLSearchParams({ type: target.type, id: target.id, offset: String(offset) })}`
16475
+ );
16476
+ },
16477
+ async deleteAttachment(id) {
16478
+ return request(`/v1/attachments/${encodeURIComponent(id)}`, { method: "DELETE" });
16479
+ },
16150
16480
  async listFeedback(filters = {}) {
16151
16481
  const params = new URLSearchParams();
16152
16482
  if (filters.status) params.set("status", filters.status);
@@ -16183,10 +16513,53 @@ function ownerApi(options) {
16183
16513
  body: JSON.stringify({ status, ...note !== void 0 ? { note } : {} })
16184
16514
  });
16185
16515
  },
16516
+ async listQuestions(filters = {}) {
16517
+ const query = filters.status ? `?status=${filters.status}` : "";
16518
+ return request(`/v1/questions${query}`);
16519
+ },
16520
+ /** Files are shared with every agent that receives the question. */
16521
+ async createQuestion(prompt, category, options2 = {}) {
16522
+ const attachments = options2.files?.length ? await upload(options2.files, options2.cwd) : [];
16523
+ return request("/v1/questions", {
16524
+ method: "POST",
16525
+ body: JSON.stringify({
16526
+ prompt,
16527
+ ...category ? { category } : {},
16528
+ ...attachments.length ? { attachments } : {},
16529
+ ...options2.idempotencyKey ? { idempotencyKey: options2.idempotencyKey } : {}
16530
+ })
16531
+ });
16532
+ },
16533
+ async closeQuestion(id) {
16534
+ return request(`/v1/questions/${encodeURIComponent(id)}`, {
16535
+ method: "PATCH",
16536
+ body: JSON.stringify({ status: "closed" })
16537
+ });
16538
+ },
16539
+ async listEvals(filters = {}) {
16540
+ const query = filters.status ? `?status=${filters.status}` : "";
16541
+ return request(`/v1/evals${query}`);
16542
+ },
16543
+ async getEval(id) {
16544
+ return request(`/v1/evals/${encodeURIComponent(id)}`);
16545
+ },
16546
+ /** Promote a failed-task report (category=eval event) into the eval corpus. */
16547
+ async promoteEval(eventId) {
16548
+ return request("/v1/evals", {
16549
+ method: "POST",
16550
+ body: JSON.stringify({ event_id: eventId })
16551
+ });
16552
+ },
16553
+ async setEvalStatus(id, status, note) {
16554
+ return request(`/v1/evals/${encodeURIComponent(id)}`, {
16555
+ method: "PATCH",
16556
+ body: JSON.stringify({ status, ...note !== void 0 ? { note } : {} })
16557
+ });
16558
+ },
16186
16559
  /** Attach an external link (Slack message, Linear/GitHub issue, PR) to evt_… or iss_…. */
16187
16560
  async addLink(id, url2, label) {
16188
- const path5 = id.startsWith("iss_") ? `/v1/issues/${id}` : `/v1/events/${id}`;
16189
- return request(path5, {
16561
+ const path6 = id.startsWith("iss_") ? `/v1/issues/${id}` : `/v1/events/${id}`;
16562
+ return request(path6, {
16190
16563
  method: "PATCH",
16191
16564
  body: JSON.stringify({ addLink: { url: url2, ...label !== void 0 ? { label } : {} } })
16192
16565
  });
@@ -16194,7 +16567,7 @@ function ownerApi(options) {
16194
16567
  };
16195
16568
  }
16196
16569
  function mintIdempotencyKey() {
16197
- return (0, import_crypto.randomBytes)(16).toString("hex");
16570
+ return (0, import_crypto3.randomBytes)(16).toString("hex");
16198
16571
  }
16199
16572
  var DEFAULT_TIMEOUT_MS = 15e3;
16200
16573
  var DEFAULT_CLIENT_NAME = "hivenet";
@@ -16206,7 +16579,7 @@ var DEFAULT_CLIENT_VERSION = (() => {
16206
16579
  }
16207
16580
  })();
16208
16581
  function generateThreadId() {
16209
- return (0, import_crypto.randomBytes)(16).toString("hex");
16582
+ return (0, import_crypto3.randomBytes)(16).toString("hex");
16210
16583
  }
16211
16584
  function resolveThreadId(resume) {
16212
16585
  if (resume !== void 0 && THREAD_ID_PATTERN.test(resume)) {
@@ -16214,9 +16587,10 @@ function resolveThreadId(resume) {
16214
16587
  }
16215
16588
  return { id: generateThreadId(), resumed: false };
16216
16589
  }
16217
- async function sendFeedback(options) {
16590
+ async function prepareFeedback(options) {
16218
16591
  const env = options.env ?? process.env;
16219
16592
  const cwd = options.cwd ?? process.cwd();
16593
+ const files = await readLocalAttachments(options.attachments ?? [], cwd);
16220
16594
  const config2 = findConfigFile(cwd)?.config;
16221
16595
  const resolveOptions = {
16222
16596
  ...options.to !== void 0 ? { to: options.to } : {},
@@ -16252,6 +16626,7 @@ async function sendFeedback(options) {
16252
16626
  ...author ? { author } : {},
16253
16627
  thread: { id: thread.id },
16254
16628
  ...options.eval ? { eval: options.eval } : {},
16629
+ ...options.question ? { question: options.question } : {},
16255
16630
  ...context ? { context } : {},
16256
16631
  ...options.metadata && Object.keys(options.metadata).length > 0 ? { metadata: options.metadata } : {},
16257
16632
  // Minted before the first attempt so outbox retries replay the SAME event
@@ -16267,7 +16642,32 @@ async function sendFeedback(options) {
16267
16642
  if (!validated.ok) {
16268
16643
  throw new FeedbackError(`Invalid feedback event: ${validated.errors.join("; ")}`);
16269
16644
  }
16270
- const event = validated.data;
16645
+ return { event: validated.data, destination, resumed: thread.resumed, files };
16646
+ }
16647
+ async function sendFeedback(options) {
16648
+ const env = options.env ?? process.env;
16649
+ const { event, destination, resumed, files } = await prepareFeedback(options);
16650
+ const names = files.map((file2) => file2.manifest.filename);
16651
+ let filesNotAttached;
16652
+ if (files.length) {
16653
+ try {
16654
+ event.attachments = await uploadFiles({
16655
+ endpoint: destination.endpoint,
16656
+ files,
16657
+ ...destination.to ? { to: destination.to } : {},
16658
+ ...destination.writeKey ? { writeKey: destination.writeKey } : {},
16659
+ ...event.context?.agent?.id ? { uploader: event.context.agent.id } : {},
16660
+ ...options.fetchImpl ? { fetchImpl: options.fetchImpl } : {}
16661
+ });
16662
+ } catch (error51) {
16663
+ if (!(error51 instanceof AttachmentUploadError) || error51.rejected) {
16664
+ throw new FeedbackError(
16665
+ `${error51 instanceof Error ? error51.message : String(error51)} Nothing was sent.`
16666
+ );
16667
+ }
16668
+ filesNotAttached = { files: names, reason: error51.message };
16669
+ }
16670
+ }
16271
16671
  const result = await postEvent({
16272
16672
  endpoint: destination.endpoint,
16273
16673
  ...destination.writeKey ? { writeKey: destination.writeKey } : {},
@@ -16281,12 +16681,17 @@ async function sendFeedback(options) {
16281
16681
  response: result.response,
16282
16682
  event,
16283
16683
  destination,
16284
- resumed: thread.resumed
16684
+ resumed,
16685
+ ...filesNotAttached ? { filesNotAttached } : {}
16285
16686
  };
16286
16687
  }
16287
16688
  if (result.kind === "rejected") {
16288
16689
  throw new FeedbackError(`Feedback rejected: ${result.errors.join("; ")}`);
16289
16690
  }
16691
+ if (event.attachments) {
16692
+ delete event.attachments;
16693
+ filesNotAttached = { files: names, reason: result.error };
16694
+ }
16290
16695
  const queued = options.outbox !== false && appendToOutbox(
16291
16696
  {
16292
16697
  endpoint: destination.endpoint,
@@ -16302,7 +16707,8 @@ async function sendFeedback(options) {
16302
16707
  error: result.error,
16303
16708
  event,
16304
16709
  destination,
16305
- resumed: thread.resumed
16710
+ resumed,
16711
+ ...filesNotAttached ? { filesNotAttached } : {}
16306
16712
  };
16307
16713
  }
16308
16714
  // Annotate the CommonJS export names for ESM import in node:
@@ -16316,6 +16722,8 @@ async function sendFeedback(options) {
16316
16722
  FeedbackError,
16317
16723
  HOME_ENV_VAR,
16318
16724
  ISSUE_STATUSES,
16725
+ MAX_ATTACHMENTS,
16726
+ MAX_ATTACHMENT_BYTES,
16319
16727
  MAX_FEEDBACK_LENGTH,
16320
16728
  MAX_GUIDANCE_LENGTH,
16321
16729
  MAX_SUBJECT_LENGTH,