@heretek-ai/epistemic-swarm 0.7.11 → 0.7.13

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 (63) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/package.json +1 -1
  3. package/plugins/research-cache/skills/research_cache/hasher.py +56 -14
  4. package/prompts/agent_brainstormer.md +14 -0
  5. package/prompts/agent_darkharvest.md +20 -1
  6. package/runner/__pycache__/__init__.cpython-311.pyc +0 -0
  7. package/runner/__pycache__/auctioneer.cpython-311.pyc +0 -0
  8. package/runner/__pycache__/auditor_engine.cpython-311.pyc +0 -0
  9. package/runner/__pycache__/claim_store.cpython-311.pyc +0 -0
  10. package/runner/__pycache__/claim_witness.cpython-311.pyc +0 -0
  11. package/runner/__pycache__/darkharvest_claims.cpython-311.pyc +0 -0
  12. package/runner/__pycache__/living_dossiers.cpython-311.pyc +0 -0
  13. package/runner/__pycache__/mcp_protocol.cpython-311.pyc +0 -0
  14. package/runner/__pycache__/mcp_server.cpython-311.pyc +0 -0
  15. package/runner/__pycache__/path_safety.cpython-311.pyc +0 -0
  16. package/runner/__pycache__/pcrb.cpython-311.pyc +0 -0
  17. package/runner/__pycache__/pcrb_verify.cpython-311.pyc +0 -0
  18. package/runner/__pycache__/refinement.cpython-311.pyc +0 -0
  19. package/runner/__pycache__/research_swarm.cpython-311.pyc +0 -0
  20. package/runner/__pycache__/retrieval_log.cpython-311.pyc +0 -0
  21. package/runner/__pycache__/state_machine.cpython-311.pyc +0 -0
  22. package/runner/auditor_engine.py +267 -80
  23. package/runner/darkharvest_claims.py +326 -0
  24. package/runner/refinement.py +74 -0
  25. package/runner/research_swarm.py +119 -24
  26. package/runner/retrieval_log.py +90 -0
  27. package/runner/state_machine.py +35 -0
  28. package/runner/tests/__pycache__/test_auction_order.cpython-311.pyc +0 -0
  29. package/runner/tests/__pycache__/test_backends.cpython-311.pyc +0 -0
  30. package/runner/tests/__pycache__/test_bet1_spike.cpython-311.pyc +0 -0
  31. package/runner/tests/__pycache__/test_claim_store.cpython-311.pyc +0 -0
  32. package/runner/tests/__pycache__/test_claim_witness.cpython-311.pyc +0 -0
  33. package/runner/tests/__pycache__/test_claude_plugin.cpython-311.pyc +0 -0
  34. package/runner/tests/__pycache__/test_darkharvest_audit.cpython-311.pyc +0 -0
  35. package/runner/tests/__pycache__/test_domain_packs.cpython-311.pyc +0 -0
  36. package/runner/tests/__pycache__/test_dossier_contract.cpython-311.pyc +0 -0
  37. package/runner/tests/__pycache__/test_factory.cpython-311.pyc +0 -0
  38. package/runner/tests/__pycache__/test_fleet_seam.cpython-311.pyc +0 -0
  39. package/runner/tests/__pycache__/test_grounding.cpython-311.pyc +0 -0
  40. package/runner/tests/__pycache__/test_living_dossiers.cpython-311.pyc +0 -0
  41. package/runner/tests/__pycache__/test_manifest_concurrency.cpython-311.pyc +0 -0
  42. package/runner/tests/__pycache__/test_mcp_server.cpython-311.pyc +0 -0
  43. package/runner/tests/__pycache__/test_opencode_ux.cpython-311.pyc +0 -0
  44. package/runner/tests/__pycache__/test_opencode_v2_registrars.cpython-311.pyc +0 -0
  45. package/runner/tests/__pycache__/test_pcrb.cpython-311.pyc +0 -0
  46. package/runner/tests/__pycache__/test_refinement.cpython-311.pyc +0 -0
  47. package/runner/tests/__pycache__/test_swarm.cpython-311.pyc +0 -0
  48. package/runner/tests/__pycache__/test_swarm_config.cpython-311.pyc +0 -0
  49. package/runner/tests/__pycache__/test_sweep_regressions.cpython-311.pyc +0 -0
  50. package/runner/tests/__pycache__/test_webcache.cpython-311.pyc +0 -0
  51. package/runner/tests/test_darkharvest_audit.py +290 -0
  52. package/runner/tests/test_grounding.py +183 -0
  53. package/scripts/__pycache__/bet1_advisory_spike.cpython-311.pyc +0 -0
  54. package/scripts/__pycache__/build_adapters.cpython-311.pyc +0 -0
  55. package/scripts/__pycache__/divergence_experiment.cpython-311.pyc +0 -0
  56. package/skills/epistemic_search/scripts/__pycache__/search.cpython-311.pyc +0 -0
  57. package/skills/epistemic_search/scripts/search.py +16 -0
  58. package/skills/epistemic_search/scripts/webcache.py +11 -0
  59. package/skills/research_cache/__pycache__/__init__.cpython-311.pyc +0 -0
  60. package/skills/research_cache/__pycache__/hasher.cpython-311.pyc +0 -0
  61. package/skills/research_cache/hasher.py +56 -14
  62. package/skills/swarm_config/__pycache__/__init__.cpython-311.pyc +0 -0
  63. package/skills/swarm_config/__pycache__/configure.cpython-311.pyc +0 -0
@@ -16,6 +16,21 @@ from xml.sax.saxutils import escape as xml_escape
16
16
  USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
17
17
 
18
18
 
19
+ def _log_retrieval(kind: str, **fields) -> None:
20
+ """Best-effort retrieval telemetry (see runner/retrieval_log.py)."""
21
+ try:
22
+ from pathlib import Path as _Path
23
+
24
+ root = _Path(__file__).resolve().parents[3]
25
+ if str(root) not in sys.path:
26
+ sys.path.insert(0, str(root))
27
+ from runner.retrieval_log import default_base_dir, log_event
28
+
29
+ log_event(default_base_dir(), kind, **fields)
30
+ except Exception:
31
+ pass
32
+
33
+
19
34
  class DDGLiteParser(HTMLParser):
20
35
  """Linear, non-backtracking HTML parser for DuckDuckGo Lite search results.
21
36
 
@@ -204,6 +219,7 @@ def search_duckduckgo(
204
219
  if len(results) >= max_results:
205
220
  break
206
221
 
222
+ _log_retrieval("query", query=effective_query, results=len(results))
207
223
  return results
208
224
 
209
225
 
@@ -249,6 +249,17 @@ def cmd_archive(stdin_data):
249
249
  encoding="utf-8",
250
250
  )
251
251
  log(f"archived {domain_of(url)} -> {digest[:16]} ({len(text)} chars)")
252
+ try:
253
+ root = Path(__file__).resolve().parents[3]
254
+ if str(root) not in sys.path:
255
+ sys.path.insert(0, str(root))
256
+ from runner.retrieval_log import log_event
257
+
258
+ log_event(
259
+ research_dir(stdin_data), "cache", url=url, hash=digest, tool="webcache"
260
+ )
261
+ except Exception:
262
+ pass
252
263
  except Exception as e:
253
264
  log(f"archive failed for {domain_of(url)}: {e}")
254
265
  return None
@@ -17,6 +17,20 @@ from typing import Dict, Any, Optional, Tuple
17
17
  DEFAULT_RESEARCH_DIR = ".research"
18
18
  BASE_DIR_HELP = "Base .research directory"
19
19
 
20
+
21
+ def _log_retrieval(base_dir, kind: str, **fields) -> None:
22
+ """Best-effort retrieval telemetry (see runner/retrieval_log.py)."""
23
+ try:
24
+ root = Path(__file__).resolve().parents[2]
25
+ if str(root) not in sys.path:
26
+ sys.path.insert(0, str(root))
27
+ from runner.retrieval_log import log_event
28
+
29
+ log_event(base_dir, kind, **fields)
30
+ except Exception:
31
+ pass
32
+
33
+
20
34
  class SourceHasher:
21
35
  def __init__(self, base_dir: Optional[Path] = None):
22
36
  target = base_dir or Path(DEFAULT_RESEARCH_DIR)
@@ -30,8 +44,14 @@ class SourceHasher:
30
44
  normalized = content.strip().encode("utf-8")
31
45
  return hashlib.sha256(normalized).hexdigest()
32
46
 
33
- def store_source(self, url: str, content: str, title: Optional[str] = None,
34
- tier: str = "WEB_DOCUMENT", metadata: Optional[Dict[str, Any]] = None) -> str:
47
+ def store_source(
48
+ self,
49
+ url: str,
50
+ content: str,
51
+ title: Optional[str] = None,
52
+ tier: str = "WEB_DOCUMENT",
53
+ metadata: Optional[Dict[str, Any]] = None,
54
+ ) -> str:
35
55
  """Store content and metadata content-addressed by SHA-256."""
36
56
  content_hash = self.compute_sha256(content)
37
57
  md_path = self.sources_dir / f"{content_hash}.md"
@@ -50,7 +70,7 @@ class SourceHasher:
50
70
  "cached_at": datetime.now(timezone.utc).isoformat(),
51
71
  "byte_size": len(content.encode("utf-8")),
52
72
  "char_count": len(content),
53
- "custom_metadata": metadata or {}
73
+ "custom_metadata": metadata or {},
54
74
  }
55
75
  with open(json_path, "w", encoding="utf-8") as f:
56
76
  json.dump(meta, f, indent=2)
@@ -84,7 +104,9 @@ class SourceHasher:
84
104
  """
85
105
  if not re.match(r"^[a-fA-F0-9]+$", hash_prefix):
86
106
  return None
87
- exact_path = Path(os.path.realpath(str(self.sources_dir / f"{hash_prefix}{extension}")))
107
+ exact_path = Path(
108
+ os.path.realpath(str(self.sources_dir / f"{hash_prefix}{extension}"))
109
+ )
88
110
  if exact_path.parent != self.sources_dir:
89
111
  return None
90
112
  if exact_path.exists():
@@ -102,12 +124,16 @@ class SourceHasher:
102
124
  def normalize_text_for_search(text: str) -> str:
103
125
  """Collapse whitespace and normalize typography for substring matching."""
104
126
  # Replace smart quotes and dashes
105
- text = text.replace("“", '"').replace("”", '"').replace("‘", "'").replace("’", "'")
127
+ text = (
128
+ text.replace("“", '"').replace("”", '"').replace("‘", "'").replace("’", "'")
129
+ )
106
130
  text = text.replace("—", "-").replace("–", "-")
107
131
  # Collapse all whitespace to single spaces
108
132
  return re.sub(r"\s+", " ", text).strip().lower()
109
133
 
110
- def verify_quote(self, content_hash: str, quote: str) -> Tuple[bool, float, Optional[str]]:
134
+ def verify_quote(
135
+ self, content_hash: str, quote: str
136
+ ) -> Tuple[bool, float, Optional[str]]:
111
137
  """
112
138
  Verifies whether quote exists in cached document.
113
139
  Returns: (is_verified, confidence_score, context_match)
@@ -136,7 +162,7 @@ class SourceHasher:
136
162
  best_score = 0.0
137
163
 
138
164
  for i in range(max(1, len(source_words) - window_size + 1)):
139
- window = source_words[i:i + window_size]
165
+ window = source_words[i : i + window_size]
140
166
  matches = sum(1 for w1, w2 in zip(quote_words, window) if w1 == w2)
141
167
  score = matches / window_size
142
168
  if score > best_score:
@@ -147,11 +173,17 @@ class SourceHasher:
147
173
  if best_score >= 0.88:
148
174
  return True, best_score, f"High-confidence fuzzy match ({best_score:.2f})."
149
175
 
150
- return False, best_score, f"Verification failed. Highest word overlap: {best_score:.2f}."
176
+ return (
177
+ False,
178
+ best_score,
179
+ f"Verification failed. Highest word overlap: {best_score:.2f}.",
180
+ )
151
181
 
152
182
 
153
183
  def main():
154
- parser = argparse.ArgumentParser(description="Epistemic Swarm Content Hasher & Quote Verifier")
184
+ parser = argparse.ArgumentParser(
185
+ description="Epistemic Swarm Content Hasher & Quote Verifier"
186
+ )
155
187
  subparsers = parser.add_subparsers(dest="command")
156
188
 
157
189
  # Cache command
@@ -166,7 +198,9 @@ def main():
166
198
  verify_parser = subparsers.add_parser("verify", help="Verify a verbatim quote")
167
199
  verify_parser.add_argument("--hash", required=True, help="Document SHA-256 hash")
168
200
  verify_parser.add_argument("--quote", required=True, help="Verbatim quote to check")
169
- verify_parser.add_argument("--dir", default=DEFAULT_RESEARCH_DIR, help=BASE_DIR_HELP)
201
+ verify_parser.add_argument(
202
+ "--dir", default=DEFAULT_RESEARCH_DIR, help=BASE_DIR_HELP
203
+ )
170
204
 
171
205
  # List command
172
206
  list_parser = subparsers.add_parser("list", help="List cached sources")
@@ -182,18 +216,26 @@ def main():
182
216
  if not sys.stdin.isatty():
183
217
  content = sys.stdin.read()
184
218
  else:
185
- print("Error: No content provided via --content or stdin.", file=sys.stderr)
219
+ print(
220
+ "Error: No content provided via --content or stdin.",
221
+ file=sys.stderr,
222
+ )
186
223
  sys.exit(1)
187
- h = hasher.store_source(url=args.url, content=content, title=args.title, tier=args.tier)
224
+ h = hasher.store_source(
225
+ url=args.url, content=content, title=args.title, tier=args.tier
226
+ )
188
227
  print(f"[CACHED] {h} -> {args.title} ({args.url})")
228
+ _log_retrieval(hasher.base_dir, "cache", url=args.url, hash=h, tool="hasher")
189
229
 
190
230
  elif args.command == "verify":
191
- verified, conf, msg = hasher.verify_quote(content_hash=args.hash, quote=args.quote)
231
+ verified, conf, msg = hasher.verify_quote(
232
+ content_hash=args.hash, quote=args.quote
233
+ )
192
234
  result = {
193
235
  "hash": args.hash,
194
236
  "verified": verified,
195
237
  "confidence": conf,
196
- "message": msg
238
+ "message": msg,
197
239
  }
198
240
  print(json.dumps(result, indent=2))
199
241
  sys.exit(0 if verified else 1)