@mindrian_os/install 1.13.0-beta.11

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 (597) hide show
  1. package/.claude-plugin/plugin.json +21 -0
  2. package/.mcp.json +9 -0
  3. package/CHANGELOG.md +3333 -0
  4. package/LICENSE +123 -0
  5. package/README.md +673 -0
  6. package/agents/brain-query.md +80 -0
  7. package/agents/framework-runner.md +237 -0
  8. package/agents/grading.md +188 -0
  9. package/agents/investor.md +128 -0
  10. package/agents/larry-extended.md +135 -0
  11. package/agents/opportunity-scanner.md +91 -0
  12. package/agents/persona-analyst.md +132 -0
  13. package/agents/research.md +89 -0
  14. package/agents/reverse-salient-agent.md +27 -0
  15. package/bin/cli.js +142 -0
  16. package/bin/mindrian-mcp-server.cjs +182 -0
  17. package/bin/mindrian-tools.cjs +765 -0
  18. package/commands/act.md +439 -0
  19. package/commands/admin.md +404 -0
  20. package/commands/analyze-needs.md +42 -0
  21. package/commands/analyze-systems.md +39 -0
  22. package/commands/analyze-timing.md +42 -0
  23. package/commands/auto-explore.md +64 -0
  24. package/commands/beautiful-question.md +40 -0
  25. package/commands/brain-derive.md +78 -0
  26. package/commands/build-knowledge.md +42 -0
  27. package/commands/build-thesis.md +46 -0
  28. package/commands/causal.md +234 -0
  29. package/commands/challenge-assumptions.md +33 -0
  30. package/commands/compare-ventures.md +83 -0
  31. package/commands/dashboard.md +110 -0
  32. package/commands/deep-grade.md +82 -0
  33. package/commands/diagnose.md +58 -0
  34. package/commands/diagnostics.md +151 -0
  35. package/commands/doctor.md +151 -0
  36. package/commands/dominant-designs.md +40 -0
  37. package/commands/explain-decision.md +87 -0
  38. package/commands/explore-domains.md +42 -0
  39. package/commands/explore-futures.md +40 -0
  40. package/commands/explore-trends.md +42 -0
  41. package/commands/export.md +103 -0
  42. package/commands/file-meeting.md +724 -0
  43. package/commands/find-analogies.md +188 -0
  44. package/commands/find-bottlenecks.md +62 -0
  45. package/commands/find-connections.md +76 -0
  46. package/commands/funding.md +81 -0
  47. package/commands/grade.md +203 -0
  48. package/commands/graph.md +128 -0
  49. package/commands/hat-briefing.md +125 -0
  50. package/commands/heal.md +196 -0
  51. package/commands/help.md +399 -0
  52. package/commands/hmi-status.md +172 -0
  53. package/commands/jtbd.md +241 -0
  54. package/commands/leadership.md +73 -0
  55. package/commands/lean-canvas.md +40 -0
  56. package/commands/macro-trends.md +40 -0
  57. package/commands/map-unknowns.md +40 -0
  58. package/commands/memory.md +173 -0
  59. package/commands/models.md +175 -0
  60. package/commands/mos-reason.md +285 -0
  61. package/commands/mullins.md +120 -0
  62. package/commands/new-project.md +481 -0
  63. package/commands/onboard.md +434 -0
  64. package/commands/operator.md +149 -0
  65. package/commands/opportunities.md +144 -0
  66. package/commands/organize.md +497 -0
  67. package/commands/persona.md +198 -0
  68. package/commands/pipeline.md +112 -0
  69. package/commands/present.md +91 -0
  70. package/commands/publish.md +201 -0
  71. package/commands/query.md +124 -0
  72. package/commands/radar.md +72 -0
  73. package/commands/reanalyze.md +91 -0
  74. package/commands/research.md +196 -0
  75. package/commands/room.md +352 -0
  76. package/commands/rooms.md +598 -0
  77. package/commands/root-cause.md +40 -0
  78. package/commands/rs-experts.md +85 -0
  79. package/commands/rs-explain.md +100 -0
  80. package/commands/rs-fetch.md +94 -0
  81. package/commands/rs-thesis.md +85 -0
  82. package/commands/scenario-plan.md +40 -0
  83. package/commands/scheduled-tasks.md +285 -0
  84. package/commands/score-innovation.md +43 -0
  85. package/commands/scout.md +239 -0
  86. package/commands/setup.md +618 -0
  87. package/commands/snapshot.md +147 -0
  88. package/commands/speakers.md +84 -0
  89. package/commands/splash.md +28 -0
  90. package/commands/status.md +75 -0
  91. package/commands/structure-argument.md +42 -0
  92. package/commands/suggest-next.md +80 -0
  93. package/commands/systems-thinking.md +40 -0
  94. package/commands/think-hats.md +42 -0
  95. package/commands/update.md +181 -0
  96. package/commands/user-needs.md +40 -0
  97. package/commands/validate.md +40 -0
  98. package/commands/value-proposition.md +61 -0
  99. package/commands/vault.md +180 -0
  100. package/commands/visualize.md +52 -0
  101. package/commands/whitespace.md +507 -0
  102. package/commands/wiki.md +69 -0
  103. package/hooks/hooks.json +381 -0
  104. package/hooks/run-hook.cmd +64 -0
  105. package/lib/__init__.py +0 -0
  106. package/lib/__pycache__/__init__.cpython-312.pyc +0 -0
  107. package/lib/agents/auto-explore-agent.cjs +1043 -0
  108. package/lib/agents/reverse-salient-agent.cjs +679 -0
  109. package/lib/agents/tension-hook-agent.cjs +544 -0
  110. package/lib/brain/ROOM.md +44 -0
  111. package/lib/brain/chain-recommender.cjs +301 -0
  112. package/lib/chat/chat-context.js +185 -0
  113. package/lib/chat/chat-panel.js +721 -0
  114. package/lib/chat/fabric-chat.cjs +288 -0
  115. package/lib/chat/generative-tools.js +219 -0
  116. package/lib/conversation/ROOM.md +39 -0
  117. package/lib/conversation/classifier-rules.json +38 -0
  118. package/lib/conversation/classifier.cjs +264 -0
  119. package/lib/conversation/operator.cjs +287 -0
  120. package/lib/copy/115-spec-strings.cjs +55 -0
  121. package/lib/core/__init__.py +0 -0
  122. package/lib/core/__nav-stub.cjs +14 -0
  123. package/lib/core/__pycache__/__init__.cpython-312.pyc +0 -0
  124. package/lib/core/__pycache__/rs-math.cpython-312.pyc +0 -0
  125. package/lib/core/__pycache__/rs_cache.cpython-312.pyc +0 -0
  126. package/lib/core/__pycache__/rs_corpus.cpython-312.pyc +0 -0
  127. package/lib/core/__pycache__/rs_hybrid.cpython-312.pyc +0 -0
  128. package/lib/core/__pycache__/rs_math.cpython-312.pyc +0 -0
  129. package/lib/core/__pycache__/rs_rooms.cpython-312.pyc +0 -0
  130. package/lib/core/artifact-id.cjs +148 -0
  131. package/lib/core/asset-ops.cjs +151 -0
  132. package/lib/core/auto-commit-throttle.cjs +129 -0
  133. package/lib/core/bearer-token.cjs +199 -0
  134. package/lib/core/brain-client.cjs +865 -0
  135. package/lib/core/brain-derivation-prompts.cjs +326 -0
  136. package/lib/core/brain-derivation-queue.cjs +431 -0
  137. package/lib/core/brain-derivation.cjs +580 -0
  138. package/lib/core/brain-md-schema.cjs +528 -0
  139. package/lib/core/brain-md-staleness.cjs +357 -0
  140. package/lib/core/brain-response-sanitize.cjs +188 -0
  141. package/lib/core/bridge-writer.cjs +477 -0
  142. package/lib/core/chat-context-builder.cjs +253 -0
  143. package/lib/core/cross-room-aggregator.cjs +762 -0
  144. package/lib/core/daily-briefing.cjs +438 -0
  145. package/lib/core/decision-capture.cjs +618 -0
  146. package/lib/core/deep-links.cjs +82 -0
  147. package/lib/core/dispatch-optimizer.cjs +354 -0
  148. package/lib/core/dual-path-detector.cjs +84 -0
  149. package/lib/core/dual-path-detector.test.cjs +334 -0
  150. package/lib/core/exports-log.cjs +79 -0
  151. package/lib/core/feynman-minto-invariants.cjs +605 -0
  152. package/lib/core/folder-memory-async.cjs +338 -0
  153. package/lib/core/folder-memory-shared.cjs +890 -0
  154. package/lib/core/folder-memory.cjs +416 -0
  155. package/lib/core/framework-chain-composer.cjs +411 -0
  156. package/lib/core/frontmatter-schemas.cjs +330 -0
  157. package/lib/core/git-ops.cjs +141 -0
  158. package/lib/core/graph-ops.cjs +258 -0
  159. package/lib/core/hat-persistence.cjs +362 -0
  160. package/lib/core/index.cjs +60 -0
  161. package/lib/core/integration-registry.cjs +232 -0
  162. package/lib/core/intelligence-cascade.cjs +661 -0
  163. package/lib/core/lazygraph-ops.cjs +1057 -0
  164. package/lib/core/lru-cache.cjs +139 -0
  165. package/lib/core/mcp-profiles.cjs +182 -0
  166. package/lib/core/meeting-ops.cjs +54 -0
  167. package/lib/core/memory-ops.cjs +600 -0
  168. package/lib/core/migrations/ROOM.md +33 -0
  169. package/lib/core/migrations/phase-109-nodes-provenance.cjs +339 -0
  170. package/lib/core/migrations/phase-109-session-focus.cjs +99 -0
  171. package/lib/core/model-profiles.cjs +246 -0
  172. package/lib/core/mullins-scaffold.cjs +160 -0
  173. package/lib/core/nav-dial.cjs +316 -0
  174. package/lib/core/navigation/ROOM.md +15 -0
  175. package/lib/core/navigation/explanation.cjs +43 -0
  176. package/lib/core/navigation/focus.cjs +135 -0
  177. package/lib/core/navigation/ingestion.cjs +82 -0
  178. package/lib/core/navigation/insights.cjs +350 -0
  179. package/lib/core/navigation/memory-events.cjs +118 -0
  180. package/lib/core/navigation/neighborhood.cjs +78 -0
  181. package/lib/core/navigation/packet.cjs +182 -0
  182. package/lib/core/navigation/room-home.cjs +127 -0
  183. package/lib/core/navigation/transitions.cjs +82 -0
  184. package/lib/core/navigation-engine-shared.cjs +242 -0
  185. package/lib/core/navigation-engine.cjs +664 -0
  186. package/lib/core/navigation.cjs +60 -0
  187. package/lib/core/nl-graph-queries.cjs +164 -0
  188. package/lib/core/offer-presenter.cjs +406 -0
  189. package/lib/core/opportunity-extractor.cjs +183 -0
  190. package/lib/core/opportunity-ops.cjs +1371 -0
  191. package/lib/core/persona-ops.cjs +537 -0
  192. package/lib/core/persona-taxonomy.cjs +190 -0
  193. package/lib/core/platform-gates.cjs +120 -0
  194. package/lib/core/platform.cjs +257 -0
  195. package/lib/core/proactive-intelligence.cjs +528 -0
  196. package/lib/core/problem-type-router.cjs +315 -0
  197. package/lib/core/reasoning-ops.cjs +639 -0
  198. package/lib/core/reverse-salient-persona-suffix.cjs +115 -0
  199. package/lib/core/room-classifier-strict-mode.cjs +229 -0
  200. package/lib/core/room-db.cjs +127 -0
  201. package/lib/core/room-ops-async.cjs +92 -0
  202. package/lib/core/room-ops-shared.cjs +64 -0
  203. package/lib/core/room-ops-sync.cjs +70 -0
  204. package/lib/core/room-ops.cjs +32 -0
  205. package/lib/core/room-type-detector.cjs +386 -0
  206. package/lib/core/rs-brain-substrate-prompts.cjs +129 -0
  207. package/lib/core/rs-brain-substrate.cjs +570 -0
  208. package/lib/core/rs-breakthrough-scorer.cjs +255 -0
  209. package/lib/core/rs-canon-violations.cjs +82 -0
  210. package/lib/core/rs-chain-feeder.cjs +343 -0
  211. package/lib/core/rs-commercial-assessor.cjs +280 -0
  212. package/lib/core/rs-differential-scorer.cjs +376 -0
  213. package/lib/core/rs-domain-analyzer.cjs +385 -0
  214. package/lib/core/rs-egress-prompts.cjs +113 -0
  215. package/lib/core/rs-egress-telemetry.cjs +225 -0
  216. package/lib/core/rs-egress-violations.cjs +53 -0
  217. package/lib/core/rs-expert-mapper.cjs +467 -0
  218. package/lib/core/rs-fetcher-academic.cjs +697 -0
  219. package/lib/core/rs-fetcher-experts.cjs +314 -0
  220. package/lib/core/rs-fetcher-industry.cjs +731 -0
  221. package/lib/core/rs-fetcher-patents.cjs +564 -0
  222. package/lib/core/rs-innovation-classifier.cjs +194 -0
  223. package/lib/core/rs-mind-map.cjs +656 -0
  224. package/lib/core/rs-neo4j-writer.cjs +388 -0
  225. package/lib/core/rs-nl-to-query.cjs +425 -0
  226. package/lib/core/rs-pinecone-bridge.cjs +303 -0
  227. package/lib/core/rs-preprocessor.cjs +350 -0
  228. package/lib/core/rs-query-matrix.cjs +316 -0
  229. package/lib/core/rs-query-to-text.cjs +438 -0
  230. package/lib/core/rs-sqlite-mirror.cjs +443 -0
  231. package/lib/core/rs-thesis-generator.cjs +188 -0
  232. package/lib/core/rs_cache.py +479 -0
  233. package/lib/core/rs_corpus.py +468 -0
  234. package/lib/core/rs_hybrid.py +586 -0
  235. package/lib/core/rs_math.py +287 -0
  236. package/lib/core/rs_rooms.py +193 -0
  237. package/lib/core/scheduled-scanner.cjs +463 -0
  238. package/lib/core/scratchpad-ops.cjs +201 -0
  239. package/lib/core/section-8-trace-schema.cjs +138 -0
  240. package/lib/core/section-registry.cjs +111 -0
  241. package/lib/core/session-state.cjs +144 -0
  242. package/lib/core/shallow-doc-parser.cjs +174 -0
  243. package/lib/core/shallow-doc-parser.test.cjs +226 -0
  244. package/lib/core/skill-activation-router.cjs +284 -0
  245. package/lib/core/state-ops.cjs +46 -0
  246. package/lib/core/statusline-cache.cjs +266 -0
  247. package/lib/core/token-estimator.cjs +348 -0
  248. package/lib/core/user-archetype.cjs +239 -0
  249. package/lib/core/user-md-ops.cjs +524 -0
  250. package/lib/core/visual-ops.cjs +624 -0
  251. package/lib/core/write-lock.cjs +149 -0
  252. package/lib/graph/canvas-graph.js +467 -0
  253. package/lib/graph/constellation-config.cjs +299 -0
  254. package/lib/graph/graph-detail-panel.js +165 -0
  255. package/lib/hmi/ROOM.md +47 -0
  256. package/lib/hmi/across-session-memory.cjs +604 -0
  257. package/lib/hmi/cross-room-memory.cjs +575 -0
  258. package/lib/hmi/decoy-tier.cjs +395 -0
  259. package/lib/hmi/jtbd-classifier.cjs +219 -0
  260. package/lib/hmi/jtbd-state.cjs +199 -0
  261. package/lib/hmi/jtbd-taxonomy.json +392 -0
  262. package/lib/hmi/selector-dispatcher.cjs +546 -0
  263. package/lib/hmi/selector-telemetry.cjs +263 -0
  264. package/lib/hmi/shape-f0-renderer.cjs +139 -0
  265. package/lib/hmi/shape-f1-fallback.cjs +80 -0
  266. package/lib/hmi/shape-f1-renderer.cjs +138 -0
  267. package/lib/hmi/shape-f2-renderer.cjs +132 -0
  268. package/lib/hmi/shape-f3-renderer.cjs +66 -0
  269. package/lib/hmi/shape-f4-renderer.cjs +72 -0
  270. package/lib/hmi/shape-f5-renderer.cjs +155 -0
  271. package/lib/hmi/shape-f6-plan-review-renderer.cjs +312 -0
  272. package/lib/hmi/shape-f6-renderer.cjs +144 -0
  273. package/lib/hmi/shape-g-renderer.cjs +219 -0
  274. package/lib/hmi/shape-h-renderer.cjs +222 -0
  275. package/lib/hmi/tier-check.cjs +63 -0
  276. package/lib/import/PRECONDITIONS.md +41 -0
  277. package/lib/import/branding.cjs +210 -0
  278. package/lib/import/branding.test.cjs +235 -0
  279. package/lib/import/classifications-sync.cjs +104 -0
  280. package/lib/import/classifications-sync.test.cjs +129 -0
  281. package/lib/import/enricher.cjs +296 -0
  282. package/lib/import/enricher.test.cjs +273 -0
  283. package/lib/import/integration.test.cjs +376 -0
  284. package/lib/import/manifest.cjs +129 -0
  285. package/lib/import/manifest.schema.json +185 -0
  286. package/lib/import/manifest.test.cjs +123 -0
  287. package/lib/import/meeting-detector.cjs +92 -0
  288. package/lib/import/meeting-detector.test.cjs +100 -0
  289. package/lib/import/person-detector.cjs +229 -0
  290. package/lib/import/person-detector.test.cjs +149 -0
  291. package/lib/import/report.cjs +186 -0
  292. package/lib/import/report.test.cjs +186 -0
  293. package/lib/import/room-md-scaffolder.cjs +49 -0
  294. package/lib/import/router.cjs +224 -0
  295. package/lib/import/router.test.cjs +356 -0
  296. package/lib/import/run-all-tests.cjs +36 -0
  297. package/lib/import/smoke-test.cjs +213 -0
  298. package/lib/import/smoke-test.test.cjs +148 -0
  299. package/lib/import/test-fixtures/collision-vault/preexisting-room/STATE.md +8 -0
  300. package/lib/import/test-fixtures/collision-vault/preexisting-room/problem-definition/onboarding/onboarding.md +7 -0
  301. package/lib/import/test-fixtures/collision-vault/source/onboarding.md +5 -0
  302. package/lib/import/test-fixtures/obsidian-vault/.obsidian/workspace.json +1 -0
  303. package/lib/import/test-fixtures/obsidian-vault/notes/with-wikilinks.md +4 -0
  304. package/lib/import/test-fixtures/tiny-vault/notes/2026-01-15-team-sync.md +9 -0
  305. package/lib/import/test-fixtures/tiny-vault/notes/empty.md +3 -0
  306. package/lib/import/test-fixtures/tiny-vault/notes/onboarding.md +5 -0
  307. package/lib/import/test-fixtures/tiny-vault/notes/pricing.md +5 -0
  308. package/lib/import/test-fixtures/tiny-vault/notes/random.md +4 -0
  309. package/lib/import/undo.test.cjs +199 -0
  310. package/lib/import/vault-scanner.cjs +105 -0
  311. package/lib/import/vault-scanner.test.cjs +67 -0
  312. package/lib/mcp/app-html/dashboard.html +316 -0
  313. package/lib/mcp/app-html/graph.html +428 -0
  314. package/lib/mcp/app-html/mindrian-platform.html +1841 -0
  315. package/lib/mcp/app-html/wiki.html +383 -0
  316. package/lib/mcp/app-views.cjs +322 -0
  317. package/lib/mcp/brain-router.cjs +418 -0
  318. package/lib/mcp/capability-registry.cjs +62 -0
  319. package/lib/mcp/larry-context.cjs +46 -0
  320. package/lib/mcp/larry-server-instructions.md +114 -0
  321. package/lib/mcp/pipeline-state.cjs +275 -0
  322. package/lib/mcp/prompts.cjs +302 -0
  323. package/lib/mcp/resources.cjs +227 -0
  324. package/lib/mcp/session-catchup.cjs +327 -0
  325. package/lib/mcp/surface-detect.cjs +75 -0
  326. package/lib/mcp/tool-router.cjs +1034 -0
  327. package/lib/memory/aaak-compress.cjs +403 -0
  328. package/lib/memory/aaak-compress.test.cjs +288 -0
  329. package/lib/memory/async-artifact-auto-commit.test.cjs +223 -0
  330. package/lib/memory/bearer-token.test.cjs +315 -0
  331. package/lib/memory/brain-cache-lru.test.cjs +259 -0
  332. package/lib/memory/brain-client-query-shape.test.cjs +160 -0
  333. package/lib/memory/brain-derivation-graceful-degradation.test.cjs +1019 -0
  334. package/lib/memory/brain-derivation-queue.test.cjs +539 -0
  335. package/lib/memory/brain-derivation.test.cjs +634 -0
  336. package/lib/memory/brain-derive-command.test.cjs +534 -0
  337. package/lib/memory/brain-md-invariants-validator.test.cjs +704 -0
  338. package/lib/memory/brain-md-schema.test.cjs +467 -0
  339. package/lib/memory/brain-md-staleness.test.cjs +525 -0
  340. package/lib/memory/brain-server-resolution.test.cjs +314 -0
  341. package/lib/memory/chain-recommender.test.cjs +233 -0
  342. package/lib/memory/chat-context.test.cjs +128 -0
  343. package/lib/memory/command-registry.test.cjs +220 -0
  344. package/lib/memory/cross-room-aggregator.test.cjs +909 -0
  345. package/lib/memory/dashboard-server.test.cjs +256 -0
  346. package/lib/memory/debouncer-drain-at-prompt.test.cjs +389 -0
  347. package/lib/memory/decision-capture.test.cjs +632 -0
  348. package/lib/memory/decision-capture.worker.cjs +70 -0
  349. package/lib/memory/explain-decision-command.test.cjs +521 -0
  350. package/lib/memory/explain-decision-footer.test.cjs +316 -0
  351. package/lib/memory/explored-materials-store.cjs +392 -0
  352. package/lib/memory/feynman-minto-guardian.test.cjs +736 -0
  353. package/lib/memory/feynman-minto-invariants.test.cjs +511 -0
  354. package/lib/memory/feynman-prompts-drift.test.cjs +144 -0
  355. package/lib/memory/feynman-prompts.cjs +151 -0
  356. package/lib/memory/feynman-prompts.test.cjs +96 -0
  357. package/lib/memory/folder-memory-quadruple.test.cjs +548 -0
  358. package/lib/memory/folder-memory.test.cjs +503 -0
  359. package/lib/memory/framework-chain-composer.test.cjs +515 -0
  360. package/lib/memory/frontmatter-schema-validator.test.cjs +290 -0
  361. package/lib/memory/heal-command.test.cjs +604 -0
  362. package/lib/memory/index-artifact-transaction.test.cjs +333 -0
  363. package/lib/memory/lazygraph-rs-discoveries-view.test.cjs +122 -0
  364. package/lib/memory/mcp-input-validation.test.cjs +240 -0
  365. package/lib/memory/mcp-server-brain-deps.test.cjs +270 -0
  366. package/lib/memory/mcp-stack-fallback.test.cjs +433 -0
  367. package/lib/memory/minto-debouncer.test.cjs +407 -0
  368. package/lib/memory/minto-debouncer.worker.cjs +46 -0
  369. package/lib/memory/minto-migration-v88.test.cjs +265 -0
  370. package/lib/memory/minto-schema-v88.test.cjs +390 -0
  371. package/lib/memory/mos-status-renderer.test.cjs +631 -0
  372. package/lib/memory/narrative-schema.cjs +376 -0
  373. package/lib/memory/narrative-schema.test.cjs +209 -0
  374. package/lib/memory/nav-dial.test.cjs +414 -0
  375. package/lib/memory/navigation-engine-core.test.cjs +722 -0
  376. package/lib/memory/navigation-invariants.test.cjs +483 -0
  377. package/lib/memory/offer-presenter.test.cjs +554 -0
  378. package/lib/memory/on-stop-snapshot.test.cjs +404 -0
  379. package/lib/memory/pending-tension-store.cjs +373 -0
  380. package/lib/memory/post-compact-reinjection.test.cjs +854 -0
  381. package/lib/memory/post-write-triple.test.cjs +317 -0
  382. package/lib/memory/pre-compact-snapshot.test.cjs +495 -0
  383. package/lib/memory/problem-type-router.test.cjs +656 -0
  384. package/lib/memory/query-efficiency-telemetry.test.cjs +370 -0
  385. package/lib/memory/recompile-room-references.test.cjs +392 -0
  386. package/lib/memory/recompile-room-references.worker.cjs +42 -0
  387. package/lib/memory/record-decision-dual-write.test.cjs +454 -0
  388. package/lib/memory/room-classifier-strict-mode.test.cjs +417 -0
  389. package/lib/memory/room-minto-hook.test.cjs +398 -0
  390. package/lib/memory/rs-discovery-engine.test.cjs +323 -0
  391. package/lib/memory/run-feynman-tests.cjs +1247 -0
  392. package/lib/memory/security-trifecta.test.cjs +312 -0
  393. package/lib/memory/session-start-brain-staleness.test.cjs +363 -0
  394. package/lib/memory/session-start-triple-injection.test.cjs +514 -0
  395. package/lib/memory/sessionstart-banner-formatter.cjs +318 -0
  396. package/lib/memory/sessionstart-minto-banner.test.cjs +373 -0
  397. package/lib/memory/skill-activation-router.test.cjs +419 -0
  398. package/lib/memory/stamp-artifact-write.test.cjs +304 -0
  399. package/lib/memory/statusline-active-room.test.cjs +315 -0
  400. package/lib/memory/statusline-minto-segment.test.cjs +292 -0
  401. package/lib/memory/sync-async-entry-points.test.cjs +204 -0
  402. package/lib/memory/test-bridge-writer-enhanced.cjs +452 -0
  403. package/lib/memory/test-rs-brain-substrate-shape.cjs +529 -0
  404. package/lib/memory/test-rs-brain-substrate.cjs +636 -0
  405. package/lib/memory/test-rs-breakthrough-scorer.cjs +375 -0
  406. package/lib/memory/test-rs-canon-violations.cjs +218 -0
  407. package/lib/memory/test-rs-chain-feeder-core.cjs +344 -0
  408. package/lib/memory/test-rs-chain-feeder-skill-spawn.cjs +297 -0
  409. package/lib/memory/test-rs-commercial-assessor.cjs +385 -0
  410. package/lib/memory/test-rs-differential-scorer.cjs +480 -0
  411. package/lib/memory/test-rs-discovery-engine.cjs +603 -0
  412. package/lib/memory/test-rs-domain-analyzer.cjs +492 -0
  413. package/lib/memory/test-rs-egress-primitives.cjs +420 -0
  414. package/lib/memory/test-rs-expert-mapper.cjs +547 -0
  415. package/lib/memory/test-rs-explain-command.cjs +443 -0
  416. package/lib/memory/test-rs-fetcher-academic.cjs +848 -0
  417. package/lib/memory/test-rs-fetcher-experts.cjs +496 -0
  418. package/lib/memory/test-rs-fetcher-industry.cjs +702 -0
  419. package/lib/memory/test-rs-fetcher-patents.cjs +674 -0
  420. package/lib/memory/test-rs-innovation-classifier.cjs +301 -0
  421. package/lib/memory/test-rs-mind-map.cjs +646 -0
  422. package/lib/memory/test-rs-neo4j-writer.cjs +518 -0
  423. package/lib/memory/test-rs-nl-to-query.cjs +449 -0
  424. package/lib/memory/test-rs-pinecone-bridge.cjs +277 -0
  425. package/lib/memory/test-rs-preprocessor.cjs +433 -0
  426. package/lib/memory/test-rs-query-matrix.cjs +391 -0
  427. package/lib/memory/test-rs-query-to-text.cjs +551 -0
  428. package/lib/memory/test-rs-sqlite-mirror.cjs +649 -0
  429. package/lib/memory/test-rs-thesis-generator.cjs +360 -0
  430. package/lib/memory/triple-context-formatter.cjs +473 -0
  431. package/lib/memory/triple-context-formatter.test.cjs +442 -0
  432. package/lib/memory/user-md-persona.test.cjs +565 -0
  433. package/lib/memory/userpromptsubmit-integration.test.cjs +690 -0
  434. package/lib/memory/validators/README.md +157 -0
  435. package/lib/memory/validators/brain-md-invariants.cjs +475 -0
  436. package/lib/memory/validators/brain-substrate-invariants.cjs +285 -0
  437. package/lib/memory/validators/external-academic-invariants.cjs +249 -0
  438. package/lib/memory/validators/external-industry-invariants.cjs +271 -0
  439. package/lib/memory/validators/external-patents-invariants.cjs +266 -0
  440. package/lib/memory/validators/minto-invariants.cjs +62 -0
  441. package/lib/memory/validators/navigation-invariants.cjs +340 -0
  442. package/lib/memory/validators/queue-health.cjs +95 -0
  443. package/lib/memory/validators/snapshot-integrity.cjs +129 -0
  444. package/lib/memory/validators/stale-lifecycle.cjs +116 -0
  445. package/lib/memory/vault-section-minto-generator-atomic.test.cjs +556 -0
  446. package/lib/memory/vault-section-minto-generator-atomic.worker.cjs +73 -0
  447. package/lib/memory/write-lock-atomic.test.cjs +137 -0
  448. package/lib/memory/write-lock-atomic.worker.cjs +55 -0
  449. package/lib/parity/check-parity.cjs +83 -0
  450. package/lib/presentation/presentation-server.cjs +101 -0
  451. package/lib/presentation/presentation-watcher.cjs +123 -0
  452. package/lib/quickview/hub-server.cjs +719 -0
  453. package/lib/quickview/server.cjs +533 -0
  454. package/lib/render/JTBD-PALETTES.md +145 -0
  455. package/lib/render/ROOM.md +59 -0
  456. package/lib/render/render-v2.cjs +486 -0
  457. package/lib/render/render-v2.test.cjs +267 -0
  458. package/lib/render/render.cjs +65 -0
  459. package/lib/state/ROOM.md +46 -0
  460. package/lib/state/state-md-parser.cjs +215 -0
  461. package/lib/statusline/ROOM.md +38 -0
  462. package/lib/statusline/banner-suppression.cjs +50 -0
  463. package/lib/statusline/surface-detect.cjs +85 -0
  464. package/lib/update-bootstrap.sh.template +145 -0
  465. package/lib/vault/frontmatter-schema.cjs +297 -0
  466. package/lib/vault/room-scanner.cjs +352 -0
  467. package/lib/vault/wikilink-builder.cjs +231 -0
  468. package/lib/vault/wikilink-builder.test.cjs +182 -0
  469. package/lib/wiki/graph-links.cjs +281 -0
  470. package/lib/wiki/page-renderer.cjs +229 -0
  471. package/lib/wiki/wiki-chat.cjs +81 -0
  472. package/lib/wiki/wiki-layout.cjs +1459 -0
  473. package/lib/wiki/wiki-search.cjs +142 -0
  474. package/lib/wiki/wiki-server.cjs +678 -0
  475. package/lib/wiki/wiki-watcher.cjs +105 -0
  476. package/lib/workflow/ROOM.md +47 -0
  477. package/lib/workflow/command-resolver.cjs +155 -0
  478. package/lib/workflow/command-resolver.test.cjs +235 -0
  479. package/package.json +44 -0
  480. package/pipelines/analogy/01-decompose.md +80 -0
  481. package/pipelines/analogy/02-abstract.md +87 -0
  482. package/pipelines/analogy/03-search.md +135 -0
  483. package/pipelines/analogy/04-transfer.md +101 -0
  484. package/pipelines/analogy/05-validate.md +106 -0
  485. package/pipelines/analogy/CHAIN.md +56 -0
  486. package/pipelines/discovery/01-explore-domains.md +44 -0
  487. package/pipelines/discovery/02-think-hats.md +50 -0
  488. package/pipelines/discovery/03-analyze-needs.md +54 -0
  489. package/pipelines/discovery/CHAIN.md +37 -0
  490. package/pipelines/thesis/01-structure-argument.md +45 -0
  491. package/pipelines/thesis/02-challenge-assumptions.md +48 -0
  492. package/pipelines/thesis/03-build-thesis.md +54 -0
  493. package/pipelines/thesis/CHAIN.md +37 -0
  494. package/references/brain/causal-directives.md +91 -0
  495. package/references/brain/causal-enrichment.cypher +165 -0
  496. package/references/brain/command-triggers-schema.md +226 -0
  497. package/references/brain/graph-architecture.md +317 -0
  498. package/references/brain/query-patterns.md +460 -0
  499. package/references/brain/room-hierarchy-schema.md +218 -0
  500. package/references/brain/schema.md +76 -0
  501. package/references/capability-radar/capabilities-index.md +241 -0
  502. package/references/capability-radar/changelog-cache.md +81 -0
  503. package/references/causal/causal-schema.md +103 -0
  504. package/references/design/email-template-standard.md +155 -0
  505. package/references/design/graph-visualization-standard.md +178 -0
  506. package/references/document-generation.md +179 -0
  507. package/references/hsi/HSI-TOOLS-REFERENCE.md +222 -0
  508. package/references/import-config.md +141 -0
  509. package/references/integrations/detection-patterns.md +101 -0
  510. package/references/meeting/artifact-template.md +377 -0
  511. package/references/meeting/cross-meeting-intelligence.md +216 -0
  512. package/references/meeting/cross-relationship-patterns.md +202 -0
  513. package/references/meeting/live-join-interface.md +244 -0
  514. package/references/meeting/section-mapping.md +192 -0
  515. package/references/meeting/segment-classification.md +258 -0
  516. package/references/meeting/speaker-profile-template.md +219 -0
  517. package/references/meeting/summary-template.md +348 -0
  518. package/references/meeting/transcript-patterns.md +226 -0
  519. package/references/methodology/analyze-needs.md +135 -0
  520. package/references/methodology/analyze-systems.md +121 -0
  521. package/references/methodology/analyze-timing.md +149 -0
  522. package/references/methodology/beautiful-question.md +109 -0
  523. package/references/methodology/build-knowledge.md +161 -0
  524. package/references/methodology/build-thesis.md +237 -0
  525. package/references/methodology/challenge-assumptions.md +127 -0
  526. package/references/methodology/diagnose.md +169 -0
  527. package/references/methodology/dominant-designs.md +212 -0
  528. package/references/methodology/explore-domains.md +147 -0
  529. package/references/methodology/explore-futures.md +163 -0
  530. package/references/methodology/explore-trends.md +129 -0
  531. package/references/methodology/find-bottlenecks.md +131 -0
  532. package/references/methodology/grade.md +211 -0
  533. package/references/methodology/index.md +97 -0
  534. package/references/methodology/leadership.md +200 -0
  535. package/references/methodology/lean-canvas.md +116 -0
  536. package/references/methodology/macro-trends.md +192 -0
  537. package/references/methodology/map-unknowns.md +137 -0
  538. package/references/methodology/mullins-7-domains.md +104 -0
  539. package/references/methodology/problem-types.md +65 -0
  540. package/references/methodology/root-cause.md +178 -0
  541. package/references/methodology/sapphire-encoding.md +355 -0
  542. package/references/methodology/scenario-plan.md +178 -0
  543. package/references/methodology/score-innovation.md +154 -0
  544. package/references/methodology/structure-argument.md +158 -0
  545. package/references/methodology/systems-thinking.md +159 -0
  546. package/references/methodology/think-hats.md +147 -0
  547. package/references/methodology/triz-matrix.json +751 -0
  548. package/references/methodology/triz-principles.md +501 -0
  549. package/references/methodology/user-needs.md +199 -0
  550. package/references/methodology/validate.md +163 -0
  551. package/references/methodology/value-proposition.md +244 -0
  552. package/references/opportunities/funding-lifecycle.md +103 -0
  553. package/references/opportunities/grant-api-patterns.md +99 -0
  554. package/references/opportunities/opportunity-template.md +84 -0
  555. package/references/personality/assessment-philosophy.md +72 -0
  556. package/references/personality/lexicon.md +100 -0
  557. package/references/personality/persona-chains.md +56 -0
  558. package/references/personality/pws-lexicon-full.md +499 -0
  559. package/references/personality/voice-dna.md +156 -0
  560. package/references/personas/hat-perspectives.md +76 -0
  561. package/references/personas/persona-template.md +63 -0
  562. package/references/pipeline/act-output-contract.md +88 -0
  563. package/references/pipeline/chains-index.md +39 -0
  564. package/references/pws-profile-generation.md +79 -0
  565. package/references/reasoning/reasoning-schema.md +143 -0
  566. package/references/reasoning/reasoning-template.md +68 -0
  567. package/references/reasoning/run-template.md +38 -0
  568. package/references/research/RESEARCH_14_CLAUDE_CODE_SOURCE_ARCHITECTURE.md +209 -0
  569. package/references/research/RESEARCH_15_V1.8_OPTIMIZATION_JTBD.md +375 -0
  570. package/references/research/RESEARCH_16_NATIVE_FIRST_PLUGIN_ARCHITECTURE.md +575 -0
  571. package/references/research/RESEARCH_17_MCP_UI_FRAMEWORKS.md +272 -0
  572. package/references/taxonomy/TAXONOMY.md +192 -0
  573. package/references/templates/MINTO.md +36 -0
  574. package/references/user-research/2026-04-05-leah-lawrence-session.md +202 -0
  575. package/references/vault-kit/README.md +35 -0
  576. package/references/vault-kit/app.json +12 -0
  577. package/references/vault-kit/appearance.json +12 -0
  578. package/references/vault-kit/graph.json +35 -0
  579. package/references/vault-kit/snippets/mindrian-destijl.css +297 -0
  580. package/references/vault-kit/templates/new-artifact.md +37 -0
  581. package/references/vault-kit/templates/new-meeting-note.md +35 -0
  582. package/references/vault-kit/templates/new-team-profile.md +29 -0
  583. package/references/vault-kit/templates/new-xref.md +35 -0
  584. package/references/visual/symbol-system.md +151 -0
  585. package/skills/MOSDeckEngine/SKILL.md +325 -0
  586. package/skills/brain-connector/SKILL.md +114 -0
  587. package/skills/context-engine/SKILL.md +147 -0
  588. package/skills/conversation-mode/SKILL.md +102 -0
  589. package/skills/larry-personality/SKILL.md +219 -0
  590. package/skills/larry-personality/framework-chains.md +92 -0
  591. package/skills/larry-personality/mode-engine.md +185 -0
  592. package/skills/mullins-scaffold/SKILL.md +61 -0
  593. package/skills/mullins-scaffold/scaffold.json +146 -0
  594. package/skills/pws-methodology/SKILL.md +49 -0
  595. package/skills/room-passive/SKILL.md +165 -0
  596. package/skills/room-proactive/SKILL.md +250 -0
  597. package/skills/ui-system/SKILL.md +277 -0
@@ -0,0 +1,564 @@
1
+ /*
2
+ * Copyright (c) 2026 Mindrian. BSL 1.1.
3
+ * Phase 89.2 Plan 03 -- Patents external-egress fetcher.
4
+ * Phase 94 Plan 05 amendment (2026-04-28): envelope wrap only.
5
+ * Free-tier paths (google_patents / uspto) preserved byte-identical;
6
+ * the wrap layer adds {tier, source, results} on top of the existing
7
+ * {patents, telemetry} return. tier='paid' annotates that a real
8
+ * keyless API tier produced data; backward-compat patents[] key is
9
+ * preserved for downstream consumers. Per Canon Part 4 the tier
10
+ * annotation feeds the section-8 trace web_research_tier field; per
11
+ * Canon Part 7 envelope wrap reuses the existing Phase 89.2 fetcher.
12
+ *
13
+ * Two sources covered with one Canon Part 8 chokepoint:
14
+ * google_patents no API key patents.google.com search-as-html;
15
+ * parser scrapes JSON-LD <script> tags
16
+ * (no cheerio; pure regex extraction)
17
+ * uspto no API key USPTO Open Data Portal search API;
18
+ * returns JSON
19
+ *
20
+ * Single chokepoint: buildPatentsQuery(query, source, opts) is the ONLY
21
+ * function that constructs an outbound URL. Every call invokes
22
+ * auditQueryString(query, 'patents') from rs-egress-prompts.cjs BEFORE
23
+ * the URL is returned. ExternalEgressViolation throws pre-egress on any
24
+ * FORBIDDEN_PATTERNS hit, so adversarial input never reaches the wire.
25
+ *
26
+ * Per-source rate-limit graceful degradation per Phase 88.6-03 pattern
27
+ * (mirrors lib/core/rs-fetcher-academic.cjs byte-for-byte):
28
+ * 429 / 503 -> recordTelemetry(status='rate_limited') + return empty
29
+ * for that source + continue with remaining sources
30
+ * timeout -> recordTelemetry(status='timeout') + continue
31
+ * parse err -> recordTelemetry(status='api_error') + continue
32
+ * budget==0 -> skip source (synthetic 'budget_exhausted' on returned
33
+ * telemetry; not in v1 ALLOWED_STATUSES so not persisted)
34
+ *
35
+ * NEVER throws on rate-limit. ONLY throws on Canon Part 8 violation.
36
+ *
37
+ * Network: native Node 18+ global fetch. AbortController gives a per-request
38
+ * 10s default timeout. NO node-fetch, NO axios, NO cheerio, NO additional
39
+ * npm dep.
40
+ *
41
+ * Output shape per patent (after dedupe):
42
+ * { patent_id, title, abstract, inventors[], assignee, filing_date,
43
+ * source, fetched_at }
44
+ *
45
+ * Pure CJS, zero npm deps, node built-ins only beyond the three rs-egress-*
46
+ * primitives shipped in Wave 1 (Plan 89.2-01).
47
+ */
48
+ 'use strict';
49
+
50
+ const { ExternalEgressViolation } = require('./rs-egress-violations.cjs');
51
+ const { auditQueryString } = require('./rs-egress-prompts.cjs');
52
+ const {
53
+ recordTelemetry,
54
+ computeRemainingBudget,
55
+ DEFAULT_BUDGETS,
56
+ } = require('./rs-egress-telemetry.cjs');
57
+
58
+ // ---------- Frozen invariants ----------
59
+
60
+ // Iteration order is deliberate: google_patents first because Google's
61
+ // canonicalized patent_id is the strongest dedupe signal. uspto second
62
+ // so its records back-fill any patents Google misses on the first sweep.
63
+ const SOURCES = Object.freeze(['google_patents', 'uspto']);
64
+
65
+ // Both sources are no-key. Object kept for parity with the academic
66
+ // fetcher pattern; consumers MUST NOT depend on its emptiness because
67
+ // future paid patents APIs (Patsnap, Derwent) may add env-gated rows.
68
+ const SOURCE_ENV_VARS = Object.freeze({});
69
+
70
+ const DEFAULT_TIMEOUT_MS = 10000;
71
+
72
+ const USER_AGENT = 'MindrianOS-Plugin/1.11.0 (https://github.com/jsagir/mindrian-os-plugin)';
73
+
74
+ // Per-source endpoint base URLs.
75
+ const ENDPOINTS = Object.freeze({
76
+ google_patents: 'https://patents.google.com/',
77
+ uspto: 'https://api.uspto.gov/ds-api/oa_actions/v1/records',
78
+ });
79
+
80
+ // ---------- buildPatentsQuery (THE chokepoint) ----------
81
+ //
82
+ // This is the ONLY function in the module that constructs an outbound URL.
83
+ // Every per-source dispatcher invokes it. auditQueryString runs before the
84
+ // URL is returned, so adversarial input throws ExternalEgressViolation
85
+ // pre-egress and the URL is never built (and never reaches fetch()).
86
+ //
87
+ // Returns one of:
88
+ // { skip: true, reason: 'api_key_missing' } when env var absent for gated source
89
+ // { skip: false, url, headers, method, source } when ready to fetch
90
+ //
91
+ // Throws:
92
+ // TypeError if query is not a non-empty string or source unknown
93
+ // ExternalEgressViolation if query matches FORBIDDEN_PATTERNS
94
+
95
+ function buildPatentsQuery(query, source, opts) {
96
+ if (typeof query !== 'string' || query.length === 0) {
97
+ throw new TypeError('buildPatentsQuery: query must be a non-empty string');
98
+ }
99
+ if (!SOURCES.includes(source)) {
100
+ throw new TypeError('buildPatentsQuery: unknown source: ' + String(source));
101
+ }
102
+
103
+ // Canon Part 8 chokepoint: throws on adversarial query BEFORE URL build.
104
+ auditQueryString(query, 'patents');
105
+
106
+ // Env-var gate for paid sources (none today; preserved for shape parity).
107
+ const envVar = SOURCE_ENV_VARS[source];
108
+ if (envVar && !process.env[envVar]) {
109
+ return { skip: true, reason: 'api_key_missing' };
110
+ }
111
+
112
+ const headers = {
113
+ 'User-Agent': USER_AGENT,
114
+ 'Accept': source === 'google_patents' ? 'text/html' : 'application/json',
115
+ };
116
+
117
+ const encoded = encodeURIComponent(query);
118
+ let url;
119
+ switch (source) {
120
+ case 'google_patents': {
121
+ // No-key path: HTML response carrying JSON-LD <script> tags. The
122
+ // ?q + ?oq pair mirrors what patents.google.com search uses; the
123
+ // parser is regex-based so the HTML/JSON-LD shape is the contract.
124
+ url = ENDPOINTS.google_patents
125
+ + '?q=' + encoded
126
+ + '&oq=' + encoded;
127
+ break;
128
+ }
129
+ case 'uspto': {
130
+ // USPTO Open Data Portal. JSON response. ~50 req/day budget per
131
+ // 89.2-CONTEXT.md. No API key for basic search.
132
+ url = ENDPOINTS.uspto
133
+ + '?searchText=' + encoded;
134
+ break;
135
+ }
136
+ default:
137
+ // SOURCES list is frozen; this path is unreachable but the linter
138
+ // appreciates the explicit fallthrough.
139
+ throw new TypeError('buildPatentsQuery: unhandled source: ' + source);
140
+ }
141
+
142
+ return { skip: false, url: url, headers: headers, method: 'GET', source: source };
143
+ }
144
+
145
+ // ---------- fetchWithTimeout (the ONE native fetch call site) ----------
146
+ //
147
+ // Per the chokepoint exclusivity rule: every per-source dispatcher routes
148
+ // through this helper. This is the only place that touches global.fetch.
149
+ // Returns the raw Response on success; throws on network error or timeout.
150
+
151
+ async function fetchWithTimeout(built, opts) {
152
+ const timeoutMs = (opts && typeof opts.timeoutMs === 'number') ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
153
+ const controller = new AbortController();
154
+ const t = setTimeout(function () { controller.abort(); }, timeoutMs);
155
+ try {
156
+ const res = await fetch(built.url, {
157
+ method: built.method,
158
+ headers: built.headers,
159
+ signal: controller.signal,
160
+ });
161
+ return res;
162
+ } finally {
163
+ clearTimeout(t);
164
+ }
165
+ }
166
+
167
+ // ---------- parseRateLimit ----------
168
+ //
169
+ // Pulls a remaining-budget signal out of common HTTP headers. Returns
170
+ // undefined if absent. The shape varies per source so we probe a few names.
171
+
172
+ function parseRateLimit(headers) {
173
+ if (!headers) return undefined;
174
+ const probes = ['x-ratelimit-remaining', 'X-RateLimit-Remaining', 'ratelimit-remaining'];
175
+ for (const k of probes) {
176
+ let v;
177
+ if (typeof headers.get === 'function') {
178
+ v = headers.get(k);
179
+ } else if (Object.prototype.hasOwnProperty.call(headers, k)) {
180
+ v = headers[k];
181
+ }
182
+ if (v !== undefined && v !== null) {
183
+ const n = parseInt(String(v), 10);
184
+ if (!Number.isNaN(n)) return n;
185
+ }
186
+ }
187
+ return undefined;
188
+ }
189
+
190
+ // ---------- Dedup key + dedupe ----------
191
+ //
192
+ // patent_id is canonical (USPTO + Google Patents both emit the same
193
+ // patent_id for the same patent). First-seen wins so SOURCES iteration
194
+ // order determines tie-breaking; google_patents iterates first.
195
+
196
+ function dedupKey(p) {
197
+ if (p && typeof p.patent_id === 'string' && p.patent_id.trim().length > 0) {
198
+ return 'patent:' + p.patent_id.trim().toUpperCase();
199
+ }
200
+ // Defensive fallback: title + first-inventor lastname. Should never
201
+ // be needed for real responses but keeps dedupe deterministic on
202
+ // malformed inputs.
203
+ const titleNorm = (p && typeof p.title === 'string')
204
+ ? p.title.toLowerCase().replace(/[^a-z0-9\s]+/g, ' ').replace(/\s+/g, ' ').trim()
205
+ : '';
206
+ let firstInventor = '';
207
+ if (p && Array.isArray(p.inventors) && p.inventors.length > 0) {
208
+ const a = String(p.inventors[0] || '');
209
+ const parts = a.split(/[\s,]+/);
210
+ firstInventor = parts.length > 0 ? parts[0].toLowerCase() : '';
211
+ }
212
+ if (titleNorm) {
213
+ return 'title:' + titleNorm + '|' + firstInventor;
214
+ }
215
+ return 'unknown:' + JSON.stringify(p || {});
216
+ }
217
+
218
+ function dedupe(patents) {
219
+ const seen = new Set();
220
+ const out = [];
221
+ for (const p of patents) {
222
+ const key = dedupKey(p);
223
+ if (seen.has(key)) continue;
224
+ seen.add(key);
225
+ out.push(p);
226
+ }
227
+ return out;
228
+ }
229
+
230
+ // ---------- Per-source response parsers ----------
231
+
232
+ // Google Patents: HTML with embedded JSON-LD <script type="application/ld+json">
233
+ // blocks carrying patent metadata. Pure regex extraction; no cheerio.
234
+ //
235
+ // Throws on a completely empty body (treated as api_error upstream); returns
236
+ // [] when no JSON-LD blocks are found (graceful: the page rendered but had
237
+ // no @type=Patent records).
238
+
239
+ function parseGooglePatentsResponse(htmlText) {
240
+ const out = [];
241
+ if (typeof htmlText !== 'string' || htmlText.length === 0) {
242
+ throw new Error('google_patents: empty body');
243
+ }
244
+ const re = /<script type="application\/ld\+json">([\s\S]*?)<\/script>/gi;
245
+ let m;
246
+ while ((m = re.exec(htmlText)) !== null) {
247
+ let obj = null;
248
+ try {
249
+ obj = JSON.parse(m[1]);
250
+ } catch (_e) {
251
+ continue;
252
+ }
253
+ if (!obj || typeof obj !== 'object') continue;
254
+ if (obj['@type'] !== 'Patent' && obj['@type'] !== 'Product') continue;
255
+ const inventors = [];
256
+ if (Array.isArray(obj.inventor)) {
257
+ for (const inv of obj.inventor.slice(0, 10)) {
258
+ if (inv && typeof inv === 'object' && typeof inv.name === 'string') {
259
+ inventors.push(inv.name);
260
+ } else if (typeof inv === 'string') {
261
+ inventors.push(inv);
262
+ }
263
+ }
264
+ }
265
+ let assignee = '';
266
+ if (obj.assignee && typeof obj.assignee === 'object' && typeof obj.assignee.name === 'string') {
267
+ assignee = obj.assignee.name;
268
+ } else if (typeof obj.assignee === 'string') {
269
+ assignee = obj.assignee;
270
+ }
271
+ out.push({
272
+ patent_id: typeof obj.patentNumber === 'string' ? obj.patentNumber : '',
273
+ title: typeof obj.name === 'string' ? obj.name : '',
274
+ abstract: typeof obj.description === 'string' ? obj.description : '',
275
+ inventors: inventors,
276
+ assignee: assignee,
277
+ filing_date: typeof obj.filingDate === 'string' ? obj.filingDate : '',
278
+ source: 'google_patents',
279
+ fetched_at: new Date().toISOString(),
280
+ });
281
+ }
282
+ return out;
283
+ }
284
+
285
+ // USPTO: JSON response with results[] (or records[] in legacy variants).
286
+ // Each record carries patentNumber + patentTitle + patentAbstract +
287
+ // inventorName[] + assigneeEntityName + filingDate.
288
+
289
+ function parseUsptoResponse(json) {
290
+ const out = [];
291
+ if (!json || typeof json !== 'object') return out;
292
+ // Accept either results[] (modern) or records[] (legacy) array shape.
293
+ const records = Array.isArray(json.results) ? json.results
294
+ : (Array.isArray(json.records) ? json.records : []);
295
+ for (const r of records) {
296
+ if (!r || typeof r !== 'object') continue;
297
+ let inventors = [];
298
+ if (Array.isArray(r.inventorName)) {
299
+ inventors = r.inventorName.slice(0, 10).map(String);
300
+ } else if (typeof r.inventorName === 'string') {
301
+ inventors = [r.inventorName];
302
+ }
303
+ out.push({
304
+ patent_id: typeof r.patentNumber === 'string' ? r.patentNumber : '',
305
+ title: typeof r.patentTitle === 'string' ? r.patentTitle : '',
306
+ abstract: typeof r.patentAbstract === 'string' ? r.patentAbstract : '',
307
+ inventors: inventors,
308
+ assignee: typeof r.assigneeEntityName === 'string' ? r.assigneeEntityName : '',
309
+ filing_date: typeof r.filingDate === 'string' ? r.filingDate : '',
310
+ source: 'uspto',
311
+ fetched_at: new Date().toISOString(),
312
+ });
313
+ }
314
+ return out;
315
+ }
316
+
317
+ function parseSourceResponse(payload, source) {
318
+ switch (source) {
319
+ case 'google_patents': return parseGooglePatentsResponse(payload);
320
+ case 'uspto': return parseUsptoResponse(payload);
321
+ default: return [];
322
+ }
323
+ }
324
+
325
+ // ---------- normalizePatent ----------
326
+ //
327
+ // Final shape guarantee. Per-source parsers already produce this shape
328
+ // but normalizePatent is exposed so future call sites can canonicalize
329
+ // hand-constructed records.
330
+
331
+ function normalizePatent(raw) {
332
+ return {
333
+ patent_id: raw && raw.patent_id ? String(raw.patent_id) : '',
334
+ title: raw && raw.title ? String(raw.title) : '',
335
+ abstract: raw && raw.abstract ? String(raw.abstract) : '',
336
+ inventors: raw && Array.isArray(raw.inventors) ? raw.inventors.slice(0, 10).map(String) : [],
337
+ assignee: raw && raw.assignee ? String(raw.assignee) : '',
338
+ filing_date: raw && raw.filing_date ? String(raw.filing_date) : '',
339
+ source: raw && raw.source ? String(raw.source) : '',
340
+ fetched_at: raw && raw.fetched_at ? String(raw.fetched_at) : new Date().toISOString(),
341
+ };
342
+ }
343
+
344
+ // ---------- fetchPatents ----------
345
+ //
346
+ // Top-level orchestrator. Iterates SOURCES in frozen order; per source
347
+ // iterates the input queries. Per (source, query) tuple:
348
+ // 1. Check budget. budget==0 -> skip source for this run.
349
+ // 2. Build query via chokepoint (throws ExternalEgressViolation on adversarial).
350
+ // 3. If skip (api_key_missing) -> recordTelemetry + continue.
351
+ // 4. fetchWithTimeout -> on timeout: recordTelemetry + continue.
352
+ // 5. status 429/503 -> recordTelemetry(rate_limited) + continue.
353
+ // 6. parse response. parse error -> recordTelemetry(api_error) + continue.
354
+ // 7. recordTelemetry(ok) + accumulate patents.
355
+ // After all sources, dedupe and return.
356
+
357
+ async function fetchPatents(queries, opts) {
358
+ if (!Array.isArray(queries)) {
359
+ throw new TypeError('fetchPatents: queries must be an array of non-empty strings');
360
+ }
361
+ for (const q of queries) {
362
+ if (typeof q !== 'string' || q.length === 0) {
363
+ throw new TypeError('fetchPatents: each query must be a non-empty string');
364
+ }
365
+ }
366
+ opts = opts || {};
367
+ const budgetOverrides = opts.budget || {};
368
+
369
+ // Pre-flight Canon Part 8 audit: scan ALL queries before iterating sources.
370
+ // Without this, an adversarial query in position N would run sources for
371
+ // queries 0..N-1 first (issuing real fetch() calls), then throw on N. The
372
+ // tests assert ZERO captured URLs on adversarial input, so the audit must
373
+ // happen before any source loop runs. SOURCES[0] is used as the surface
374
+ // anchor; the throw still carries meta.surface='patents' from the
375
+ // chokepoint helper.
376
+ for (const q of queries) {
377
+ auditQueryString(q, 'patents');
378
+ }
379
+
380
+ const out = { patents: [], telemetry: [] };
381
+
382
+ for (const source of SOURCES) {
383
+ const budgetCap = (typeof budgetOverrides[source] === 'number')
384
+ ? budgetOverrides[source]
385
+ : DEFAULT_BUDGETS[source];
386
+ const remaining = computeRemainingBudget(source, budgetCap);
387
+ if (remaining <= 0) {
388
+ // Skip source for this run; budget itself is the trace. We do NOT
389
+ // call recordTelemetry here (status 'budget_exhausted' is not in
390
+ // ALLOWED_STATUSES for the v1 telemetry primitive).
391
+ out.telemetry.push({ source: source, status: 'budget_exhausted' });
392
+ continue;
393
+ }
394
+
395
+ for (const query of queries) {
396
+ // Build (chokepoint). Throws ExternalEgressViolation if adversarial,
397
+ // but the pre-flight audit above has already cleared every query.
398
+ // Re-running here is defense-in-depth: if a future code path mutates
399
+ // queries between the pre-flight and the loop, this still throws.
400
+ const built = buildPatentsQuery(query, source, opts);
401
+
402
+ if (built.skip) {
403
+ // env var missing for gated source.
404
+ recordTelemetry({
405
+ source: source,
406
+ query_text: query,
407
+ status: built.reason,
408
+ });
409
+ out.telemetry.push({ source: source, status: built.reason });
410
+ // No need to keep iterating queries for a source with no key:
411
+ // every query will hit the same gate. Break early to save work.
412
+ break;
413
+ }
414
+
415
+ let res = null;
416
+ try {
417
+ res = await fetchWithTimeout(built, opts);
418
+ } catch (err) {
419
+ if (err && err.name === 'AbortError') {
420
+ recordTelemetry({
421
+ source: source,
422
+ query_text: query,
423
+ status: 'timeout',
424
+ });
425
+ out.telemetry.push({ source: source, status: 'timeout' });
426
+ continue;
427
+ }
428
+ recordTelemetry({
429
+ source: source,
430
+ query_text: query,
431
+ status: 'network_error',
432
+ });
433
+ out.telemetry.push({ source: source, status: 'network_error' });
434
+ continue;
435
+ }
436
+
437
+ if (!res.ok) {
438
+ const httpStatus = res.status || 0;
439
+ if (httpStatus === 429 || httpStatus === 503) {
440
+ recordTelemetry({
441
+ source: source,
442
+ query_text: query,
443
+ status: 'rate_limited',
444
+ http_status: httpStatus,
445
+ });
446
+ out.telemetry.push({ source: source, status: 'rate_limited', http_status: httpStatus });
447
+ continue;
448
+ }
449
+ recordTelemetry({
450
+ source: source,
451
+ query_text: query,
452
+ status: 'api_error',
453
+ http_status: httpStatus,
454
+ });
455
+ out.telemetry.push({ source: source, status: 'api_error', http_status: httpStatus });
456
+ continue;
457
+ }
458
+
459
+ let parsed = null;
460
+ try {
461
+ // google_patents returns HTML; uspto returns JSON.
462
+ const payload = (source === 'google_patents') ? await res.text() : await res.json();
463
+ parsed = parseSourceResponse(payload, source);
464
+ } catch (_err) {
465
+ recordTelemetry({
466
+ source: source,
467
+ query_text: query,
468
+ status: 'api_error',
469
+ http_status: res.status || 0,
470
+ });
471
+ out.telemetry.push({ source: source, status: 'api_error', http_status: res.status || 0 });
472
+ continue;
473
+ }
474
+
475
+ const rateRemaining = parseRateLimit(res.headers);
476
+ const recOpts = {
477
+ source: source,
478
+ query_text: query,
479
+ status: 'ok',
480
+ http_status: res.status || 200,
481
+ };
482
+ if (typeof rateRemaining === 'number') {
483
+ recOpts.rate_limit_remaining = rateRemaining;
484
+ }
485
+ recordTelemetry(recOpts);
486
+ out.telemetry.push({ source: source, status: 'ok', http_status: res.status || 200 });
487
+
488
+ for (const p of parsed) {
489
+ out.patents.push(p);
490
+ }
491
+ }
492
+ }
493
+
494
+ // First-seen wins on dedupe; google_patents ran first so it wins
495
+ // patent_id ties.
496
+ out.patents = dedupe(out.patents);
497
+
498
+ // ---- Phase 94 Plan 05 amendment: envelope wrap ----
499
+ // Determine source tag from first telemetry row with status:'ok';
500
+ // fall back to SOURCES[0] when no source produced data so the
501
+ // envelope still has a valid source tag.
502
+ let source = SOURCES[0];
503
+ for (const t of out.telemetry) {
504
+ if (t && t.status === 'ok' && typeof t.source === 'string') {
505
+ source = t.source;
506
+ break;
507
+ }
508
+ }
509
+ return {
510
+ tier: 'paid',
511
+ source: source,
512
+ results: out.patents.slice(),
513
+ patents: out.patents,
514
+ telemetry: out.telemetry,
515
+ };
516
+ }
517
+
518
+ // ---------- Per-source dispatchers ----------
519
+ //
520
+ // Convenience entry points for callers that want one source. Both
521
+ // route through fetchPatents internally (limiting SOURCES to the one
522
+ // requested) so the chokepoint exclusivity rule is preserved.
523
+
524
+ async function fetchOneSource(source, queries, opts) {
525
+ const orig = SOURCES.slice();
526
+ // Build a one-source orchestrator by delegating to fetchPatents with
527
+ // a budget map that zeroes out every other source. This keeps the
528
+ // chokepoint exclusivity invariant intact (no new fetch sites).
529
+ opts = opts || {};
530
+ const budget = Object.assign({}, opts.budget || {});
531
+ for (const s of orig) {
532
+ if (s !== source) budget[s] = 0;
533
+ }
534
+ const merged = Object.assign({}, opts, { budget: budget });
535
+ return fetchPatents(queries, merged);
536
+ }
537
+
538
+ async function fetchGooglePatents(queries, opts) { return fetchOneSource('google_patents', queries, opts); }
539
+ async function fetchUspto(queries, opts) { return fetchOneSource('uspto', queries, opts); }
540
+
541
+ // ---------- Exports ----------
542
+
543
+ module.exports = {
544
+ fetchPatents,
545
+ buildPatentsQuery,
546
+ fetchGooglePatents,
547
+ fetchUspto,
548
+ // Test surface (private; do NOT consume in production).
549
+ _test: {
550
+ dedupe,
551
+ dedupKey,
552
+ normalizePatent,
553
+ fetchWithTimeout,
554
+ parseSourceResponse,
555
+ parseGooglePatentsResponse,
556
+ parseUsptoResponse,
557
+ parseRateLimit,
558
+ SOURCES,
559
+ SOURCE_ENV_VARS,
560
+ ENDPOINTS,
561
+ DEFAULT_TIMEOUT_MS,
562
+ USER_AGENT,
563
+ },
564
+ };