@badger_badger/badger_hq 1.5.1 → 1.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.
@@ -39,7 +39,7 @@ var TASK_STATUS_VALUES = [
39
39
  ];
40
40
  zod.z.enum(TASK_STATUS_VALUES);
41
41
  var REGION_MODE_VALUES = ["must", "preferred"];
42
- zod.z.enum(REGION_MODE_VALUES);
42
+ var RegionMode = zod.z.enum(REGION_MODE_VALUES);
43
43
  var ASSIGNEE_TYPE_VALUES = ["scout", "analyst"];
44
44
  zod.z.enum(ASSIGNEE_TYPE_VALUES);
45
45
  var ASSIGNMENT_STATUS_VALUES = ["active", "completed", "revoked"];
@@ -60,6 +60,21 @@ var SOURCE_ROUTE_VALUES = ["scraper", "monitor", "waiting_for_scraper"];
60
60
  zod.z.enum(SOURCE_ROUTE_VALUES);
61
61
  var SOURCE_STATUS_VALUES = ["active", "paused", "closed", "access_problem"];
62
62
  zod.z.enum(SOURCE_STATUS_VALUES);
63
+ var ACCESS_ISSUE_VALUES = [
64
+ "login_required",
65
+ "private",
66
+ "bot_detection",
67
+ "captcha",
68
+ "geo_blocked",
69
+ "rate_limited",
70
+ "not_found",
71
+ "no_collector",
72
+ "collector_full",
73
+ "other"
74
+ ];
75
+ var AccessIssue = zod.z.enum(ACCESS_ISSUE_VALUES);
76
+ var CAPTURE_WINDOW_MODE_VALUES = ["last_days", "range"];
77
+ var CaptureWindowMode = zod.z.enum(CAPTURE_WINDOW_MODE_VALUES);
63
78
  var DISCOVERED_BY_TYPE_VALUES = ["scout", "analyst", "agent", "client", "import"];
64
79
  zod.z.enum(DISCOVERED_BY_TYPE_VALUES);
65
80
  var SOURCE_IMPORT_KIND_VALUES = ["create", "update"];
@@ -107,6 +122,8 @@ var AGENT_SOURCE_OUTCOME_VALUES = [
107
122
  var AgentSourceOutcome = zod.z.enum(AGENT_SOURCE_OUTCOME_VALUES);
108
123
  var CAPTURE_STATUS_VALUES = ["uploaded", "analyzing", "analyzed", "failed", "expired"];
109
124
  var CaptureStatus = zod.z.enum(CAPTURE_STATUS_VALUES);
125
+ var HOURS_LOG_SOURCE_VALUES = ["manual", "inferred"];
126
+ zod.z.enum(HOURS_LOG_SOURCE_VALUES);
110
127
  var CAPTURE_DOM_STATUS_VALUES = ["ok", "unavailable", "truncated"];
111
128
  var CaptureDomStatus = zod.z.enum(CAPTURE_DOM_STATUS_VALUES);
112
129
  var LLM_CAPABILITY_VALUES = ["plan", "vision_extract", "judge", "chat"];
@@ -199,7 +216,11 @@ var INDEXES = {
199
216
  ],
200
217
  [COLLECTIONS.assigneeLoads]: [{ key: { userId: 1, scope: 1 }, options: { unique: true } }],
201
218
  [COLLECTIONS.missions]: [{ key: { orgId: 1, status: 1, priority: 1 } }],
202
- [COLLECTIONS.tasks]: [{ key: { orgId: 1, missionId: 1, status: 1 } }],
219
+ [COLLECTIONS.tasks]: [
220
+ { key: { orgId: 1, missionId: 1, status: 1 } },
221
+ // Tasks that target a source (cascade on source delete).
222
+ { key: { orgId: 1, "target.sourceId": 1 }, options: { partialFilterExpression: { "target.sourceId": { $exists: true } } } }
223
+ ],
203
224
  [COLLECTIONS.assignments]: [
204
225
  { key: { assigneeUserId: 1, status: 1, orgId: 1 } },
205
226
  { key: { orgId: 1, taskId: 1, status: 1 } },
@@ -212,7 +233,14 @@ var INDEXES = {
212
233
  }
213
234
  }
214
235
  ],
215
- [COLLECTIONS.hoursLogs]: [{ key: { orgId: 1, scoutUserId: 1, date: -1 } }],
236
+ [COLLECTIONS.hoursLogs]: [
237
+ { key: { orgId: 1, scoutUserId: 1, date: -1 } },
238
+ // One inferred row per person + assignment + day (agent-service upserts it).
239
+ {
240
+ key: { orgId: 1, assignmentId: 1, scoutUserId: 1, date: 1 },
241
+ options: { unique: true, partialFilterExpression: { source: "inferred" } }
242
+ }
243
+ ],
216
244
  [COLLECTIONS.sources]: [
217
245
  {
218
246
  key: { orgId: 1, platform: 1, url: 1 },
@@ -255,7 +283,11 @@ var INDEXES = {
255
283
  [COLLECTIONS.captures]: [
256
284
  { key: { orgId: 1, sessionId: 1, capturedAt: -1 } },
257
285
  // Retention sweep: captures whose blob must be deleted.
258
- { key: { status: 1, expiresAt: 1 } }
286
+ { key: { status: 1, expiresAt: 1 } },
287
+ // Capture worker: oldest waiting (or stale analyzing) captures first.
288
+ { key: { status: 1, createdAt: 1 } },
289
+ // Task-tagged captures: per-assignment dedupe and inferred hours.
290
+ { key: { orgId: 1, assignmentId: 1, userId: 1, capturedAt: 1 } }
259
291
  ],
260
292
  [COLLECTIONS.llmCalls]: [
261
293
  { key: { orgId: 1, createdAt: -1 } },
@@ -340,6 +372,24 @@ OrgSettingsSchema.pick({
340
372
  defaultAnalystTaskLimit: true,
341
373
  timezone: true
342
374
  }).partial();
375
+ var CoverageWindowSchema = zod.z.object({
376
+ start: zod.z.coerce.date(),
377
+ end: zod.z.coerce.date()
378
+ }).refine((w) => w.end.getTime() > w.start.getTime(), {
379
+ message: "coverageWindow.end must be after coverageWindow.start",
380
+ path: ["end"]
381
+ });
382
+ zod.z.object({
383
+ languages: zod.z.array(LanguageCode),
384
+ platforms: zod.z.array(Platform),
385
+ regions: zod.z.array(zod.z.string().min(1)),
386
+ regionMode: RegionMode,
387
+ profession: zod.z.string().min(1).optional(),
388
+ coverageWindow: CoverageWindowSchema.optional(),
389
+ emergency: zod.z.boolean().default(false)
390
+ });
391
+
392
+ // src/zod/agent-session.ts
343
393
  var count = () => zod.z.number().int().min(0).default(0);
344
394
  var AgentSessionStatsSchema = zod.z.object({
345
395
  capturesCount: count(),
@@ -376,6 +426,8 @@ var AgentSessionSchema = zod.z.object({
376
426
  lastSeq: count(),
377
427
  stats: AgentSessionStatsSchema.default({}),
378
428
  budget: AgentSessionBudgetSchema.default({}),
429
+ /** The task's capture window as dates, fixed when the session started (`last_days` counted back from then). */
430
+ captureWindow: CoverageWindowSchema.optional(),
379
431
  /** Mode-specific engine state (search plan cursor, monitor queue). Opaque to clients. */
380
432
  state: zod.z.unknown().optional()
381
433
  }).merge(TimestampsSchema);
@@ -481,7 +533,14 @@ var CaptureDomSummarySchema = zod.z.object({
481
533
  var CaptureSchema = zod.z.object({
482
534
  _id: ObjectIdString,
483
535
  orgId: ClerkOrgId,
484
- sessionId: ObjectIdString,
536
+ /** Legacy: captures uploaded into a live session. Task-tagged captures have none. */
537
+ sessionId: ObjectIdString.optional(),
538
+ /** The assignment (and so the task) the person sent this capture for. */
539
+ assignmentId: ObjectIdString.optional(),
540
+ taskId: ObjectIdString.optional(),
541
+ missionId: ObjectIdString.optional(),
542
+ /** Who sent it; used to work out their hours. */
543
+ userId: ClerkUserId.optional(),
485
544
  /** From `chrome.tabs`, never from page content. */
486
545
  url: zod.z.string().url(),
487
546
  title: zod.z.string().max(500),
@@ -497,6 +556,10 @@ var CaptureSchema = zod.z.object({
497
556
  /** Validated `vision_extract` output; shape depends on session mode. */
498
557
  analysis: zod.z.unknown().optional(),
499
558
  error: zod.z.string().max(1e3).optional(),
559
+ /** Analysis attempts by the capture worker; it gives up (`failed`) after a few. */
560
+ attempts: zod.z.number().int().min(0).default(0),
561
+ /** When the worker claimed it (`analyzing`); an old claim is taken over after a restart. */
562
+ claimedAt: zod.z.coerce.date().optional(),
500
563
  /** Blob deleted and status set to `expired` after this instant (CAPTURE_RETENTION_DAYS). */
501
564
  expiresAt: zod.z.coerce.date()
502
565
  }).merge(TimestampsSchema);
@@ -614,6 +677,51 @@ var SourceImportCreateSchema = SourceImportSchema.omit({
614
677
  committedAt: true
615
678
  });
616
679
  SourceImportCreateSchema.partial();
680
+ var MAX_CAPTURE_WINDOW_DAYS = 90;
681
+ var DAY_MS = 24 * 60 * 60 * 1e3;
682
+ function isHttpUrl(value) {
683
+ try {
684
+ const { protocol } = new URL(value);
685
+ return protocol === "http:" || protocol === "https:";
686
+ } catch {
687
+ return false;
688
+ }
689
+ }
690
+ var WindowDate = zod.z.union([zod.z.date(), zod.z.string().datetime({ offset: true })]).pipe(zod.z.coerce.date());
691
+ zod.z.object({
692
+ sourceId: ObjectIdString.optional(),
693
+ platform: Platform,
694
+ url: zod.z.string().url().refine(isHttpUrl, "Expected an http(s) URL"),
695
+ label: zod.z.string().min(1).max(200).optional(),
696
+ /** Why a person collects it instead of a scraper. */
697
+ accessIssue: AccessIssue.optional(),
698
+ note: zod.z.string().max(500).optional()
699
+ });
700
+ var LastDaysWindowSchema = zod.z.object({
701
+ mode: zod.z.literal(CaptureWindowMode.enum.last_days),
702
+ days: zod.z.number().int().min(1).max(MAX_CAPTURE_WINDOW_DAYS)
703
+ });
704
+ var RangeWindowSchema = zod.z.object({
705
+ mode: zod.z.literal(CaptureWindowMode.enum.range),
706
+ start: WindowDate,
707
+ end: WindowDate
708
+ });
709
+ zod.z.discriminatedUnion("mode", [LastDaysWindowSchema, RangeWindowSchema]).superRefine((w, ctx) => {
710
+ if (w.mode !== "range" || !(w.start instanceof Date) || !(w.end instanceof Date)) return;
711
+ if (w.end.getTime() <= w.start.getTime()) {
712
+ ctx.addIssue({
713
+ code: zod.z.ZodIssueCode.custom,
714
+ message: "captureWindow.end must be after captureWindow.start",
715
+ path: ["end"]
716
+ });
717
+ } else if (w.end.getTime() - w.start.getTime() > MAX_CAPTURE_WINDOW_DAYS * DAY_MS) {
718
+ ctx.addIssue({
719
+ code: zod.z.ZodIssueCode.custom,
720
+ message: `captureWindow range must be at most ${MAX_CAPTURE_WINDOW_DAYS} days`,
721
+ path: ["end"]
722
+ });
723
+ }
724
+ });
617
725
 
618
726
  // src/mongo/schemas.ts
619
727
  var toObject = { ...toJSONOptions, flattenMaps: true };
@@ -664,6 +772,50 @@ var taskCriteriaSchema = new mongoose.Schema(
664
772
  },
665
773
  EMBEDDED
666
774
  );
775
+ var taskTargetSchema = new mongoose.Schema(
776
+ {
777
+ sourceId: { type: ObjectId },
778
+ platform: { type: String, enum: PLATFORM_VALUES, required: true },
779
+ url: { type: String, required: true, minlength: 1 },
780
+ label: { type: String, maxlength: 200 },
781
+ accessIssue: { type: String, enum: ACCESS_ISSUE_VALUES },
782
+ note: { type: String, maxlength: 500 }
783
+ },
784
+ EMBEDDED
785
+ );
786
+ var captureWindowSchema = new mongoose.Schema(
787
+ {
788
+ mode: { type: String, enum: CAPTURE_WINDOW_MODE_VALUES, required: true },
789
+ days: {
790
+ type: Number,
791
+ min: 1,
792
+ max: MAX_CAPTURE_WINDOW_DAYS,
793
+ validate: Number.isInteger,
794
+ required() {
795
+ return this.mode === "last_days";
796
+ }
797
+ },
798
+ start: {
799
+ type: Date,
800
+ required() {
801
+ return this.mode === "range";
802
+ }
803
+ },
804
+ end: {
805
+ type: Date,
806
+ required() {
807
+ return this.mode === "range";
808
+ },
809
+ validate: {
810
+ validator(end) {
811
+ return !this.start || end.getTime() > this.start.getTime();
812
+ },
813
+ message: "captureWindow.end must be after captureWindow.start"
814
+ }
815
+ }
816
+ },
817
+ EMBEDDED
818
+ );
667
819
  var availabilityWindowSchema = new mongoose.Schema(
668
820
  {
669
821
  day: { type: Number, enum: [0, 1, 2, 3, 4, 5, 6], required: true },
@@ -837,6 +989,8 @@ var taskSchema = new mongoose.Schema(
837
989
  keywords: { type: [String], required: true, default: [] },
838
990
  searchQueries: { type: [String], required: true, default: [] },
839
991
  criteria: { type: taskCriteriaSchema, required: true },
992
+ target: { type: taskTargetSchema },
993
+ captureWindow: { type: captureWindowSchema },
840
994
  status: { type: String, enum: TASK_STATUS_VALUES, required: true, default: "draft" },
841
995
  approvedBy: clerkUserIdOptional,
842
996
  approvedAt: { type: Date },
@@ -887,6 +1041,8 @@ var hoursLogSchema = new mongoose.Schema(
887
1041
  minutes: { type: Number, required: true, min: 1, validate: Number.isInteger },
888
1042
  note: { type: String, required: true, default: "" },
889
1043
  date: { type: Date, required: true },
1044
+ assignmentId: { type: ObjectId },
1045
+ source: { type: String, enum: HOURS_LOG_SOURCE_VALUES },
890
1046
  deletedAt: { type: Date }
891
1047
  },
892
1048
  collectionOptions(COLLECTIONS.hoursLogs)
@@ -1121,6 +1277,7 @@ var agentSessionSchema = new mongoose.Schema(
1121
1277
  lastSeq: counter,
1122
1278
  stats: { type: agentSessionStatsSchema, required: true, default: () => ({}) },
1123
1279
  budget: { type: agentSessionBudgetSchema, required: true, default: () => ({}) },
1280
+ captureWindow: { type: coverageWindowSchema },
1124
1281
  state: { type: Mixed }
1125
1282
  },
1126
1283
  collectionOptions(COLLECTIONS.agentSessions)
@@ -1173,7 +1330,11 @@ var captureDomSummarySchema = new mongoose.Schema(
1173
1330
  var captureSchema = new mongoose.Schema(
1174
1331
  {
1175
1332
  orgId: clerkOrgId,
1176
- sessionId: { type: ObjectId, required: true },
1333
+ sessionId: { type: ObjectId },
1334
+ assignmentId: { type: ObjectId },
1335
+ taskId: { type: ObjectId },
1336
+ missionId: { type: ObjectId },
1337
+ userId: clerkUserIdOptional,
1177
1338
  url: { type: String, required: true },
1178
1339
  title: { type: String, required: true, default: "", maxlength: 500 },
1179
1340
  capturedAt: { type: Date, required: true },
@@ -1186,6 +1347,8 @@ var captureSchema = new mongoose.Schema(
1186
1347
  status: { type: String, enum: CAPTURE_STATUS_VALUES, required: true, default: "uploaded" },
1187
1348
  analysis: { type: Mixed },
1188
1349
  error: { type: String, maxlength: 1e3 },
1350
+ attempts: { type: Number, required: true, default: 0, min: 0, validate: Number.isInteger },
1351
+ claimedAt: { type: Date },
1189
1352
  expiresAt: { type: Date, required: true }
1190
1353
  },
1191
1354
  collectionOptions(COLLECTIONS.captures)
@@ -1317,6 +1480,7 @@ exports.blobRefSchema = blobRefSchema;
1317
1480
  exports.captureDomSummarySchema = captureDomSummarySchema;
1318
1481
  exports.captureRegionSchema = captureRegionSchema;
1319
1482
  exports.captureSchema = captureSchema;
1483
+ exports.captureWindowSchema = captureWindowSchema;
1320
1484
  exports.collectorSchema = collectorSchema;
1321
1485
  exports.coverageWindowSchema = coverageWindowSchema;
1322
1486
  exports.dataItemContentSchema = dataItemContentSchema;
@@ -1350,6 +1514,7 @@ exports.specialAccessSchema = specialAccessSchema;
1350
1514
  exports.systemRecommendationSchema = systemRecommendationSchema;
1351
1515
  exports.taskCriteriaSchema = taskCriteriaSchema;
1352
1516
  exports.taskSchema = taskSchema;
1517
+ exports.taskTargetSchema = taskTargetSchema;
1353
1518
  exports.toJSONOptions = toJSONOptions;
1354
1519
  //# sourceMappingURL=index.cjs.map
1355
1520
  //# sourceMappingURL=index.cjs.map