unbrowse 2.12.2 → 2.12.7

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.
Files changed (64) hide show
  1. package/README.md +8 -44
  2. package/dist/cli.js +514 -20723
  3. package/package.json +4 -10
  4. package/runtime-src/api/routes.ts +15 -801
  5. package/runtime-src/auth/index.ts +32 -142
  6. package/runtime-src/capture/index.ts +101 -436
  7. package/runtime-src/cli.ts +371 -956
  8. package/runtime-src/client/index.ts +29 -622
  9. package/runtime-src/execution/index.ts +85 -345
  10. package/runtime-src/graph/index.ts +10 -128
  11. package/runtime-src/intent-match.ts +27 -27
  12. package/runtime-src/kuri/client.ts +82 -543
  13. package/runtime-src/orchestrator/index.ts +462 -2246
  14. package/runtime-src/reverse-engineer/index.ts +22 -220
  15. package/runtime-src/runtime/local-server.ts +16 -149
  16. package/runtime-src/runtime/paths.ts +5 -9
  17. package/runtime-src/runtime/setup.ts +1 -52
  18. package/runtime-src/server.ts +11 -6
  19. package/runtime-src/transform/schema-hints.ts +358 -0
  20. package/runtime-src/types/skill.ts +2 -49
  21. package/runtime-src/verification/index.ts +0 -15
  22. package/runtime-src/version.ts +13 -13
  23. package/vendor/kuri/darwin-arm64/kuri +0 -0
  24. package/vendor/kuri/darwin-x64/kuri +0 -0
  25. package/vendor/kuri/linux-arm64/kuri +0 -0
  26. package/vendor/kuri/linux-x64/kuri +0 -0
  27. package/bin/unbrowse-wrapper.mjs +0 -39
  28. package/bin/unbrowse.js +0 -38
  29. package/runtime-src/analytics-session.ts +0 -33
  30. package/runtime-src/api/browse-index.ts +0 -254
  31. package/runtime-src/api/browse-session.ts +0 -179
  32. package/runtime-src/api/browse-submit.ts +0 -455
  33. package/runtime-src/auth/runtime.ts +0 -116
  34. package/runtime-src/browser/index.ts +0 -635
  35. package/runtime-src/browser/types.ts +0 -41
  36. package/runtime-src/capture/prefetch.ts +0 -122
  37. package/runtime-src/capture/rsc.ts +0 -45
  38. package/runtime-src/cli/shortcuts.ts +0 -273
  39. package/runtime-src/client/graph-client.ts +0 -99
  40. package/runtime-src/execution/robots.ts +0 -167
  41. package/runtime-src/execution/search-forms.ts +0 -188
  42. package/runtime-src/graph/planner.ts +0 -411
  43. package/runtime-src/graph/session.ts +0 -294
  44. package/runtime-src/graph/trace-store.ts +0 -136
  45. package/runtime-src/indexer/index.ts +0 -480
  46. package/runtime-src/orchestrator/browser-agent.ts +0 -374
  47. package/runtime-src/orchestrator/dag-advisor.ts +0 -59
  48. package/runtime-src/orchestrator/dag-feedback.ts +0 -256
  49. package/runtime-src/orchestrator/first-pass-action.ts +0 -362
  50. package/runtime-src/orchestrator/passive-publish.ts +0 -152
  51. package/runtime-src/orchestrator/timing-economics.ts +0 -80
  52. package/runtime-src/payments/cascade.ts +0 -137
  53. package/runtime-src/payments/index.ts +0 -268
  54. package/runtime-src/payments/wallet.ts +0 -33
  55. package/runtime-src/reverse-engineer/description-prompt.ts +0 -132
  56. package/runtime-src/router.ts +0 -17
  57. package/runtime-src/runtime/browser-access.ts +0 -11
  58. package/runtime-src/runtime/browser-host.ts +0 -48
  59. package/runtime-src/runtime/lifecycle.ts +0 -17
  60. package/runtime-src/runtime/supervisor.ts +0 -69
  61. package/runtime-src/single-binary.ts +0 -141
  62. package/runtime-src/telemetry.ts +0 -253
  63. package/runtime-src/verification/matrix.ts +0 -30
  64. package/scripts/postinstall.mjs +0 -81
@@ -291,7 +291,7 @@ function inferRequires(endpoint: EndpointDescriptor): OperationBinding[] {
291
291
  semantic_type: key.endsWith("_id") || key === "id" ? "identifier" : "input",
292
292
  });
293
293
  };
294
- for (const key of Object.keys(endpoint.path_params ?? {})) add(key, "path_params", false);
294
+ for (const key of Object.keys(endpoint.path_params ?? {})) add(key, "path_params");
295
295
  for (const key of Object.keys(endpoint.query ?? {})) add(normalizeQueryBindingKey(key), "query", false);
296
296
  for (const match of endpoint.url_template.matchAll(/\{([^}]+)\}/g)) add(match[1], "url_template");
297
297
  return requires;
@@ -411,20 +411,6 @@ function isGenericBindingKey(key: string | undefined): boolean {
411
411
  return /^(id|ids|url|urls|page|cursor|offset|limit|slug(?:_\d+)?|pathname|domain|query|q|type|name)$/.test(key);
412
412
  }
413
413
 
414
- const PAGINATION_KEYS = new Set(["cursor", "page", "offset", "page_token", "next_cursor", "after", "before", "start", "next_page", "continuation_token", "skip", "from", "scroll_id"]);
415
- function isPaginationBindingKey(key: string): boolean {
416
- return PAGINATION_KEYS.has(key) || /^(next_|prev_|previous_)/.test(key) || /_cursor$/.test(key);
417
- }
418
-
419
- function classifyEdgeKind(source: SkillOperationNode, target: SkillOperationNode, bindingKey: string): SkillOperationEdge["kind"] {
420
- if (source.operation_id === target.operation_id && isPaginationBindingKey(bindingKey)) return "pagination";
421
- const LIST_ACTIONS = new Set(["list", "search", "timeline", "trending", "feed"]);
422
- const DETAIL_ACTIONS = new Set(["detail", "fetch"]);
423
- if (LIST_ACTIONS.has(source.action_kind) && DETAIL_ACTIONS.has(target.action_kind) && source.resource_kind === target.resource_kind && source.resource_kind !== "resource") return "parent_child";
424
- if (target.auth_required && /^(auth|login|token|session|oauth)$/.test(source.action_kind)) return "auth";
425
- return "dependency";
426
- }
427
-
428
414
  function isGenericSemanticType(type: string | undefined): boolean {
429
415
  if (!type) return true;
430
416
  return /^(identifier|input|resource|value|string|number|flag)$/.test(type);
@@ -576,27 +562,6 @@ export function resolveEndpointSemantic(
576
562
 
577
563
  function buildOperationNode(endpoint: EndpointDescriptor): SkillOperationNode {
578
564
  const semantic = resolveEndpointSemantic(endpoint);
579
- const normalizedRequires = (() => {
580
- const byBinding = new Map<string, OperationBinding>();
581
- for (const binding of semantic.requires ?? []) {
582
- const pathParamValue = (endpoint.path_params as Record<string, string> | undefined)?.[binding.key];
583
- const defaultedPathParam =
584
- binding.source === "path_params" &&
585
- typeof pathParamValue === "string" && pathParamValue !== "";
586
- const normalized = defaultedPathParam ? { ...binding, required: false } : binding;
587
- const id = [normalized.key, normalized.source ?? "", normalized.semantic_type ?? ""].join("|");
588
- const existing = byBinding.get(id);
589
- if (!existing) {
590
- byBinding.set(id, normalized);
591
- continue;
592
- }
593
- byBinding.set(id, {
594
- ...existing,
595
- required: existing.required && normalized.required,
596
- });
597
- }
598
- return [...byBinding.values()];
599
- })();
600
565
  return {
601
566
  operation_id: endpoint.endpoint_id,
602
567
  endpoint_id: endpoint.endpoint_id,
@@ -608,7 +573,7 @@ function buildOperationNode(endpoint: EndpointDescriptor): SkillOperationNode {
608
573
  description_in: semantic.description_in,
609
574
  description_out: semantic.description_out,
610
575
  response_summary: semantic.response_summary,
611
- requires: normalizedRequires,
576
+ requires: semantic.requires ?? [],
612
577
  provides: semantic.provides ?? [],
613
578
  negative_tags: semantic.negative_tags ?? [],
614
579
  example_request: semantic.example_request,
@@ -694,7 +659,7 @@ export function buildSkillOperationGraph(endpoints: EndpointDescriptor[]): Skill
694
659
  for (const target of operations) {
695
660
  for (const required of target.requires) {
696
661
  for (const source of operations) {
697
- if (source.operation_id === target.operation_id && !isPaginationBindingKey(required.key)) continue;
662
+ if (source.operation_id === target.operation_id) continue;
698
663
  const match = source.provides.find((provided) => {
699
664
  const exactKeyMatch = provided.key === required.key && !isGenericBindingKey(required.key);
700
665
  const semanticMatch =
@@ -702,14 +667,10 @@ export function buildSkillOperationGraph(endpoints: EndpointDescriptor[]): Skill
702
667
  !!required.semantic_type &&
703
668
  provided.semantic_type === required.semantic_type &&
704
669
  !isGenericSemanticType(required.semantic_type);
705
- const paginationSelfMatch =
706
- source.operation_id === target.operation_id &&
707
- provided.key === required.key &&
708
- isPaginationBindingKey(required.key);
709
- return exactKeyMatch || semanticMatch || paginationSelfMatch;
670
+ return exactKeyMatch || semanticMatch;
710
671
  });
711
672
  if (!match) continue;
712
- if (source.operation_id !== target.operation_id && !isBefore(source.observed_at, target.observed_at)) continue;
673
+ if (!isBefore(source.observed_at, target.observed_at)) continue;
713
674
  const edgeId = `${source.operation_id}:${target.operation_id}:${required.key}`;
714
675
  if (seenEdges.has(edgeId)) continue;
715
676
  seenEdges.add(edgeId);
@@ -718,26 +679,14 @@ export function buildSkillOperationGraph(endpoints: EndpointDescriptor[]): Skill
718
679
  from_operation_id: source.operation_id,
719
680
  to_operation_id: target.operation_id,
720
681
  binding_key: required.key,
721
- kind: match.key === required.key ? classifyEdgeKind(source, target, required.key) : "hint",
682
+ kind: match.key === required.key ? "dependency" : "hint",
722
683
  confidence: match.key === required.key ? 0.9 : 0.6,
723
684
  });
724
685
  }
725
686
  }
726
687
  }
727
-
728
- // Operations that are targets of dependency edges are not entry points
729
- const dependencyTargets = new Set(
730
- edges.filter((e) => e.kind === "dependency").map((e) => e.to_operation_id),
731
- );
732
688
  const entryOperationIds = operations
733
- .filter(
734
- (operation) =>
735
- !dependencyTargets.has(operation.operation_id) &&
736
- (operation.requires.length === 0 ||
737
- operation.requires.every(
738
- (binding) => binding.source === "query" || binding.source === "path_params",
739
- )),
740
- )
689
+ .filter((operation) => operation.requires.length === 0 || operation.requires.every((binding) => binding.source === "query"))
741
690
  .map((operation) => operation.operation_id);
742
691
 
743
692
  return {
@@ -749,12 +698,8 @@ export function buildSkillOperationGraph(endpoints: EndpointDescriptor[]): Skill
749
698
  }
750
699
 
751
700
  export function ensureSkillOperationGraph(skill: SkillManifest): SkillOperationGraph {
752
- if (skill.operation_graph?.operations?.length) {
753
- // Rebuild if stored graph is stale (doesn't cover all endpoints)
754
- const graphOpIds = new Set(skill.operation_graph.operations.map((op) => op.operation_id));
755
- const allCovered = skill.endpoints.every((ep) => graphOpIds.has(ep.endpoint_id));
756
- if (allCovered) return skill.operation_graph;
757
- }
701
+ if (skill.endpoints.length > 0) return buildSkillOperationGraph(skill.endpoints);
702
+ if (skill.operation_graph?.operations?.length) return skill.operation_graph;
758
703
  return buildSkillOperationGraph(skill.endpoints);
759
704
  }
760
705
 
@@ -782,7 +727,7 @@ export function knownBindingsFromInputs(
782
727
  return known;
783
728
  }
784
729
 
785
- export function isRunnable(operation: SkillOperationNode, bindings: Record<string, unknown>): boolean {
730
+ function isRunnable(operation: SkillOperationNode, bindings: Record<string, unknown>): boolean {
786
731
  return operation.requires.every((binding) => {
787
732
  if (!binding.required) return true;
788
733
  const value = bindings[binding.key];
@@ -790,69 +735,6 @@ export function isRunnable(operation: SkillOperationNode, bindings: Record<strin
790
735
  });
791
736
  }
792
737
 
793
- /**
794
- * Compute the set of reachable operation IDs given current known bindings.
795
- *
796
- * An operation is reachable (A_reachable) if:
797
- * 1. It has no required bindings (entry point), OR
798
- * 2. All its required bindings are present in knownBindings, OR
799
- * 3. All its required bindings can be transitively satisfied — either
800
- * directly from knownBindings or from the provides of another
801
- * reachable operation.
802
- *
803
- * Uses a fixpoint computation: starts with directly-runnable operations,
804
- * then iteratively adds operations whose requirements are met by the
805
- * accumulated provides of the reachable set, until no new operations
806
- * are added.
807
- */
808
- export function computeReachableEndpoints(
809
- graph: SkillOperationGraph,
810
- knownBindings: Record<string, unknown>,
811
- ): Set<string> {
812
- const reachable = new Set<string>();
813
- if (!graph.operations || graph.operations.length === 0) return reachable;
814
-
815
- // Seed: operations that are directly runnable with current bindings
816
- for (const op of graph.operations) {
817
- if (isRunnable(op, knownBindings)) {
818
- reachable.add(op.operation_id);
819
- }
820
- }
821
-
822
- // Fixpoint expansion: iterate until stable
823
- let changed = true;
824
- while (changed) {
825
- changed = false;
826
- // Collect all binding keys provided by currently reachable operations
827
- const availableKeys = new Set<string>(Object.keys(knownBindings));
828
- for (const op of graph.operations) {
829
- if (!reachable.has(op.operation_id)) continue;
830
- for (const provided of op.provides) {
831
- availableKeys.add(provided.key);
832
- }
833
- }
834
-
835
- // Check each non-reachable operation
836
- for (const op of graph.operations) {
837
- if (reachable.has(op.operation_id)) continue;
838
- const allSatisfied = op.requires.every((binding) => {
839
- if (!binding.required) return true;
840
- // Check direct bindings first
841
- const directValue = knownBindings[binding.key];
842
- if (directValue != null && directValue !== "") return true;
843
- // Check if any reachable operation provides this key
844
- return availableKeys.has(binding.key);
845
- });
846
- if (allSatisfied) {
847
- reachable.add(op.operation_id);
848
- changed = true;
849
- }
850
- }
851
- }
852
-
853
- return reachable;
854
- }
855
-
856
738
  export function getSkillChunk(
857
739
  skill: SkillManifest,
858
740
  opts?: {
@@ -1266,7 +1266,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1266
1266
  hasAnyPath(row, ["full_name", "name", "repository.name", "path_with_namespace"]) &&
1267
1267
  hasAnyPath(row, ["url", "web_url", "description", "stargazers_count", "stars", "star_count", "forks_count", "owner.login", "owner"])
1268
1268
  );
1269
- return matching.length >= 1 ? { verdict: "pass", reason: "repository_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1269
+ return matching.length >= 1 ? { verdict: "pass", reason: "repository_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1270
1270
  }
1271
1271
 
1272
1272
  if (/\b(package|packages)\b/.test(lower)) {
@@ -1274,7 +1274,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1274
1274
  hasAnyPath(row, ["name", "package.name"]) &&
1275
1275
  hasAnyPath(row, ["version", "description", "summary", "url", "keywords", "requires_dist", "dependencies"])
1276
1276
  );
1277
- return matching.length >= 1 ? { verdict: "pass", reason: "package_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1277
+ return matching.length >= 1 ? { verdict: "pass", reason: "package_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1278
1278
  }
1279
1279
 
1280
1280
  if (/\b(model|models)\b/.test(lower)) {
@@ -1282,7 +1282,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1282
1282
  hasAnyPath(row, ["id", "modelId", "name"]) &&
1283
1283
  hasAnyPath(row, ["downloads", "likes", "pipeline_tag", "url"])
1284
1284
  );
1285
- return matching.length >= 1 ? { verdict: "pass", reason: "model_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1285
+ return matching.length >= 1 ? { verdict: "pass", reason: "model_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1286
1286
  }
1287
1287
 
1288
1288
  if (/\b(news|story|stories|article|articles|hacker news)\b/.test(lower)) {
@@ -1290,7 +1290,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1290
1290
  hasAnyPath(row, ["objectID", "id", "url", "link"]) &&
1291
1291
  hasAnyPath(row, ["title", "story_title", "author", "points", "num_comments", "meta"])
1292
1292
  );
1293
- return matching.length >= 1 ? { verdict: "pass", reason: "story_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1293
+ return matching.length >= 1 ? { verdict: "pass", reason: "story_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1294
1294
  }
1295
1295
 
1296
1296
  if (/\bsearch images\b/.test(lower)) {
@@ -1298,7 +1298,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1298
1298
  hasAnyPath(row, ["repo_name", "name", "full_name"]) &&
1299
1299
  hasAnyPath(row, ["short_description", "description", "star_count", "pull_count", "url"])
1300
1300
  );
1301
- return matching.length >= 1 ? { verdict: "pass", reason: "image_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1301
+ return matching.length >= 1 ? { verdict: "pass", reason: "image_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1302
1302
  }
1303
1303
 
1304
1304
  if (/\b(get )?image tags\b/.test(lower)) {
@@ -1306,7 +1306,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1306
1306
  hasAnyPath(row, ["name", "tag"]) &&
1307
1307
  hasAnyPath(row, ["last_updated", "updated_at", "full_size", "digest"])
1308
1308
  );
1309
- return matching.length >= 1 ? { verdict: "pass", reason: "tag_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1309
+ return matching.length >= 1 ? { verdict: "pass", reason: "tag_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1310
1310
  }
1311
1311
 
1312
1312
  if (/\b(company|companies|organization|organisations|business)\b/.test(lower)) {
@@ -1314,7 +1314,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1314
1314
  hasAnyPath(row, ["name", "title", "public_identifier"]) &&
1315
1315
  hasAnyPath(row, ["description", "industry", "url", "website", "employee_count", "follower_count"])
1316
1316
  );
1317
- return matching.length >= 1 ? { verdict: "pass", reason: "company_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1317
+ return matching.length >= 1 ? { verdict: "pass", reason: "company_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1318
1318
  }
1319
1319
 
1320
1320
  if (/\b(person|people|profile|profiles|member|members|user|users)\b/.test(lower)) {
@@ -1323,7 +1323,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1323
1323
  hasAnyPath(row, ["headline", "url", "public_identifier", "username"])
1324
1324
  );
1325
1325
  const minRows = /\b(profile|profiles|user|users|person)\b/.test(lower) && !/\bsearch\b/.test(lower) ? 1 : 2;
1326
- return matching.length >= minRows ? { verdict: "pass", reason: "people_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1326
+ return matching.length >= minRows ? { verdict: "pass", reason: "people_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1327
1327
  }
1328
1328
 
1329
1329
  if (/\b(comment|comments)\b/.test(lower)) {
@@ -1331,7 +1331,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1331
1331
  hasAnyPath(row, ["id", "url", "permalink"]) &&
1332
1332
  hasAnyPath(row, ["author", "body", "text"])
1333
1333
  );
1334
- return matching.length >= 1 ? { verdict: "pass", reason: "comment_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1334
+ return matching.length >= 1 ? { verdict: "pass", reason: "comment_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1335
1335
  }
1336
1336
 
1337
1337
  if (/\b(email|emails|mail|inbox)\b/.test(lower)) {
@@ -1340,20 +1340,20 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1340
1340
  hasAnyPath(row, ["subject", "thread.subject", "from", "sender", "thread.latest_sender_name"]) &&
1341
1341
  hasAnyPath(row, ["date", "thread.formatted_date", "preview", "thread.preview", "matchedEmail.content_markdown"])
1342
1342
  );
1343
- return matching.length >= 1 ? { verdict: "pass", reason: "email_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1343
+ return matching.length >= 1 ? { verdict: "pass", reason: "email_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1344
1344
  }
1345
1345
 
1346
1346
  if (/\b(post|posts|tweet|tweets|status|statuses)\b/.test(lower)) {
1347
1347
  const matching = objects.filter((row) =>
1348
- hasAnyPath(row, ["id", "url", "uri", "permalink", "_id", "entityUrn", "rest_id"]) &&
1348
+ hasAnyPath(row, ["id", "url", "uri", "permalink"]) &&
1349
1349
  countMatchingGroups(row, [
1350
- ["content", "text", "title", "body", "message", "description", "summary", "commentary.text.text"],
1351
- ["account.username", "account.acct", "username", "author", "user.name", "from.name", "actor.name", "actor.name.text"],
1352
- ["score", "points", "likes", "favorite_count", "num_comments", "reply_count", "reactions", "repost_count"],
1353
- ["date", "created_at", "published_at", "timestamp", "meta", "created_time", "createdAt", "updated_at", "time"],
1350
+ ["content", "text", "title", "body"],
1351
+ ["account.username", "account.acct", "username", "author", "user.name"],
1352
+ ["score", "points", "likes", "favorite_count", "num_comments", "reply_count"],
1353
+ ["date", "created_at", "published_at", "timestamp", "meta"],
1354
1354
  ]) >= 2
1355
1355
  );
1356
- return matching.length >= 1 ? { verdict: "pass", reason: "post_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1356
+ return matching.length >= 1 ? { verdict: "pass", reason: "post_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1357
1357
  }
1358
1358
 
1359
1359
  if (/\b(reddit|subreddit)\b/.test(lower)) {
@@ -1362,7 +1362,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1362
1362
  hasAnyPath(row, ["title", "subreddit", "author"]) &&
1363
1363
  hasAnyPath(row, ["num_comments", "score", "permalink"])
1364
1364
  );
1365
- return matching.length >= 1 ? { verdict: "pass", reason: "reddit_post_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1365
+ return matching.length >= 1 ? { verdict: "pass", reason: "reddit_post_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1366
1366
  }
1367
1367
 
1368
1368
  if (/\b(topic|topics|trend|trending|hashtag|hashtags)\b/.test(lower)) {
@@ -1370,7 +1370,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1370
1370
  hasAnyPath(row, ["name", "title", "query"]) &&
1371
1371
  hasAnyPath(row, ["url", "name", "query"])
1372
1372
  );
1373
- return matching.length >= 2 ? { verdict: "pass", reason: "topic_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1373
+ return matching.length >= 2 ? { verdict: "pass", reason: "topic_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1374
1374
  }
1375
1375
 
1376
1376
  if (/\b(doc|docs|documentation)\b/.test(lower)) {
@@ -1379,7 +1379,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1379
1379
  hasAnyPath(row, ["url", "link", "href", "mdn_url"]) &&
1380
1380
  hasAnyPath(row, ["summary", "description", "slug", "popularity", "score"])
1381
1381
  );
1382
- return matching.length >= 1 ? { verdict: "pass", reason: "document_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1382
+ return matching.length >= 1 ? { verdict: "pass", reason: "document_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1383
1383
  }
1384
1384
 
1385
1385
  if (/\b(paper|papers)\b/.test(lower)) {
@@ -1388,7 +1388,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1388
1388
  hasAnyPath(row, ["url", "link", "href"]) &&
1389
1389
  hasAnyPath(row, ["summary", "description", "author", "authors", "date", "published", "meta"])
1390
1390
  );
1391
- return matching.length >= 1 ? { verdict: "pass", reason: "paper_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1391
+ return matching.length >= 1 ? { verdict: "pass", reason: "paper_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1392
1392
  }
1393
1393
 
1394
1394
  if (/\b(question|questions)\b/.test(lower)) {
@@ -1401,7 +1401,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1401
1401
  ["author", "date", "created_at", "meta"],
1402
1402
  ]) >= 2
1403
1403
  );
1404
- return matching.length >= 2 ? { verdict: "pass", reason: "question_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1404
+ return matching.length >= 2 ? { verdict: "pass", reason: "question_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1405
1405
  }
1406
1406
 
1407
1407
  if (/\b(recipe|recipes)\b/.test(lower)) {
@@ -1410,7 +1410,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1410
1410
  hasAnyPath(row, ["url", "link", "href"]) &&
1411
1411
  hasAnyPath(row, ["rating", "review_count", "author", "description", "summary", "cook_time", "total_time"])
1412
1412
  );
1413
- return matching.length >= 1 ? { verdict: "pass", reason: "recipe_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1413
+ return matching.length >= 1 ? { verdict: "pass", reason: "recipe_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1414
1414
  }
1415
1415
 
1416
1416
  if (/\b(course|courses)\b/.test(lower)) {
@@ -1419,7 +1419,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1419
1419
  hasAnyPath(row, ["url", "link", "href"]) &&
1420
1420
  hasAnyPath(row, ["rating", "partner", "provider", "instructor", "description", "summary", "duration", "level"])
1421
1421
  );
1422
- return matching.length >= 1 ? { verdict: "pass", reason: "course_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1422
+ return matching.length >= 1 ? { verdict: "pass", reason: "course_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1423
1423
  }
1424
1424
 
1425
1425
  if (/\b(definition|dictionary|meaning)\b/.test(lower)) {
@@ -1427,7 +1427,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1427
1427
  hasAnyPath(row, ["term", "title", "name", "word"]) &&
1428
1428
  hasAnyPath(row, ["definition", "summary", "body", "description"])
1429
1429
  );
1430
- return matching.length >= 1 ? { verdict: "pass", reason: "definition_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1430
+ return matching.length >= 1 ? { verdict: "pass", reason: "definition_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1431
1431
  }
1432
1432
 
1433
1433
  if (/\b(stock|stocks|ticker|tickers|quote|quotes)\b/.test(lower)) {
@@ -1436,7 +1436,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1436
1436
  hasAnyPath(row, ["price", "regularMarketPrice", "current_price"]) &&
1437
1437
  hasAnyPath(row, ["name", "currency", "change_percent", "market_cap", "market_state"])
1438
1438
  );
1439
- return matching.length >= 1 ? { verdict: "pass", reason: "quote_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1439
+ return matching.length >= 1 ? { verdict: "pass", reason: "quote_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1440
1440
  }
1441
1441
 
1442
1442
  if (/\b(product|products|item|items)\b/.test(lower)) {
@@ -1453,7 +1453,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1453
1453
  String(getPath(row, "url") ?? ""),
1454
1454
  )
1455
1455
  );
1456
- return matching.length >= 1 ? { verdict: "pass", reason: "product_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1456
+ return matching.length >= 1 ? { verdict: "pass", reason: "product_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1457
1457
  }
1458
1458
 
1459
1459
  if (/\b(channel|channels|server|servers|guild|guilds|workspace|workspaces)\b/.test(lower)) {
@@ -1462,7 +1462,7 @@ function classifyRows(rows: unknown[], intent: string): { verdict: "pass" | "fai
1462
1462
  hasAnyPath(row, ["id", "url", "channel_id", "guild_id", "workspace_id"]) &&
1463
1463
  hasAnyPath(row, ["description", "type", "member_count", "url"])
1464
1464
  );
1465
- return matching.length >= 1 ? { verdict: "pass", reason: "channel_rows" } : { verdict: "skip", reason: "wrong_entity_type" };
1465
+ return matching.length >= 1 ? { verdict: "pass", reason: "channel_rows" } : { verdict: "fail", reason: "wrong_entity_type" };
1466
1466
  }
1467
1467
 
1468
1468
  return { verdict: "skip", reason: "unclassified_array" };