opencandle 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (408) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +106 -14
  3. package/assets/logo.svg +187 -0
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.js +40 -3
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts +25 -0
  8. package/dist/config.js +72 -0
  9. package/dist/config.js.map +1 -1
  10. package/dist/infra/browser.d.ts +11 -3
  11. package/dist/infra/browser.js +2 -1
  12. package/dist/infra/browser.js.map +1 -1
  13. package/dist/infra/native-dependencies.d.ts +1 -0
  14. package/dist/infra/native-dependencies.js +10 -0
  15. package/dist/infra/native-dependencies.js.map +1 -0
  16. package/dist/infra/node-version.d.ts +2 -0
  17. package/dist/infra/node-version.js +23 -0
  18. package/dist/infra/node-version.js.map +1 -0
  19. package/dist/infra/rate-limiter.d.ts +4 -0
  20. package/dist/infra/rate-limiter.js +5 -1
  21. package/dist/infra/rate-limiter.js.map +1 -1
  22. package/dist/memory/index.d.ts +2 -0
  23. package/dist/memory/index.js +1 -0
  24. package/dist/memory/index.js.map +1 -1
  25. package/dist/memory/manager.d.ts +9 -0
  26. package/dist/memory/manager.js +28 -11
  27. package/dist/memory/manager.js.map +1 -1
  28. package/dist/memory/sqlite.js +42 -4
  29. package/dist/memory/sqlite.js.map +1 -1
  30. package/dist/memory/storage.d.ts +7 -0
  31. package/dist/memory/storage.js +3 -3
  32. package/dist/memory/storage.js.map +1 -1
  33. package/dist/memory/tool-defaults.d.ts +8 -0
  34. package/dist/memory/tool-defaults.js +59 -0
  35. package/dist/memory/tool-defaults.js.map +1 -0
  36. package/dist/memory/types.js +4 -0
  37. package/dist/memory/types.js.map +1 -1
  38. package/dist/onboarding/connect.d.ts +13 -1
  39. package/dist/onboarding/connect.js +21 -10
  40. package/dist/onboarding/connect.js.map +1 -1
  41. package/dist/onboarding/prompt-user.d.ts +1 -1
  42. package/dist/onboarding/providers.d.ts +7 -0
  43. package/dist/onboarding/providers.js +6 -3
  44. package/dist/onboarding/providers.js.map +1 -1
  45. package/dist/onboarding/tool-helpers.d.ts +1 -1
  46. package/dist/pi/opencandle-extension.d.ts +7 -1
  47. package/dist/pi/opencandle-extension.js +391 -21
  48. package/dist/pi/opencandle-extension.js.map +1 -1
  49. package/dist/pi/session-storage.d.ts +2 -0
  50. package/dist/pi/session-storage.js +5 -0
  51. package/dist/pi/session-storage.js.map +1 -0
  52. package/dist/pi/session.d.ts +4 -1
  53. package/dist/pi/session.js +25 -3
  54. package/dist/pi/session.js.map +1 -1
  55. package/dist/pi/setup.d.ts +1 -1
  56. package/dist/pi/setup.js +11 -1
  57. package/dist/pi/setup.js.map +1 -1
  58. package/dist/pi/tool-adapter.d.ts +2 -2
  59. package/dist/pi/tool-adapter.js +14 -1
  60. package/dist/pi/tool-adapter.js.map +1 -1
  61. package/dist/prompts/context-builder.d.ts +40 -3
  62. package/dist/prompts/context-builder.js +140 -19
  63. package/dist/prompts/context-builder.js.map +1 -1
  64. package/dist/prompts/disclaimer.d.ts +6 -0
  65. package/dist/prompts/disclaimer.js +9 -0
  66. package/dist/prompts/disclaimer.js.map +1 -0
  67. package/dist/prompts/policy-cards.d.ts +13 -0
  68. package/dist/prompts/policy-cards.js +197 -0
  69. package/dist/prompts/policy-cards.js.map +1 -0
  70. package/dist/prompts/sections.js +3 -3
  71. package/dist/prompts/sections.js.map +1 -1
  72. package/dist/prompts/workflow-prompts.d.ts +8 -0
  73. package/dist/prompts/workflow-prompts.js +208 -22
  74. package/dist/prompts/workflow-prompts.js.map +1 -1
  75. package/dist/providers/alpha-vantage.js +23 -1
  76. package/dist/providers/alpha-vantage.js.map +1 -1
  77. package/dist/providers/sec-edgar.d.ts +8 -1
  78. package/dist/providers/sec-edgar.js +172 -5
  79. package/dist/providers/sec-edgar.js.map +1 -1
  80. package/dist/providers/yahoo-finance.d.ts +2 -0
  81. package/dist/providers/yahoo-finance.js +203 -35
  82. package/dist/providers/yahoo-finance.js.map +1 -1
  83. package/dist/routing/classify-intent.d.ts +3 -0
  84. package/dist/routing/classify-intent.js +82 -3
  85. package/dist/routing/classify-intent.js.map +1 -1
  86. package/dist/routing/defaults.js +4 -4
  87. package/dist/routing/defaults.js.map +1 -1
  88. package/dist/routing/entity-extractor.d.ts +1 -0
  89. package/dist/routing/entity-extractor.js +158 -12
  90. package/dist/routing/entity-extractor.js.map +1 -1
  91. package/dist/routing/index.d.ts +10 -0
  92. package/dist/routing/index.js +7 -0
  93. package/dist/routing/index.js.map +1 -1
  94. package/dist/routing/legacy-rule-router.d.ts +9 -0
  95. package/dist/routing/legacy-rule-router.js +12 -0
  96. package/dist/routing/legacy-rule-router.js.map +1 -0
  97. package/dist/routing/planning.d.ts +54 -0
  98. package/dist/routing/planning.js +531 -0
  99. package/dist/routing/planning.js.map +1 -0
  100. package/dist/routing/route-manifest.d.ts +35 -0
  101. package/dist/routing/route-manifest.js +221 -0
  102. package/dist/routing/route-manifest.js.map +1 -0
  103. package/dist/routing/router-llm-client.d.ts +11 -0
  104. package/dist/routing/router-llm-client.js +42 -0
  105. package/dist/routing/router-llm-client.js.map +1 -0
  106. package/dist/routing/router-prompt.d.ts +2 -0
  107. package/dist/routing/router-prompt.js +141 -0
  108. package/dist/routing/router-prompt.js.map +1 -0
  109. package/dist/routing/router-types.d.ts +71 -0
  110. package/dist/routing/router-types.js +2 -0
  111. package/dist/routing/router-types.js.map +1 -0
  112. package/dist/routing/router.d.ts +11 -0
  113. package/dist/routing/router.js +638 -0
  114. package/dist/routing/router.js.map +1 -0
  115. package/dist/routing/slot-resolver.js +46 -6
  116. package/dist/routing/slot-resolver.js.map +1 -1
  117. package/dist/routing/turn-context.d.ts +44 -0
  118. package/dist/routing/turn-context.js +45 -0
  119. package/dist/routing/turn-context.js.map +1 -0
  120. package/dist/routing/types.d.ts +13 -1
  121. package/dist/runtime/answer-contracts.d.ts +82 -0
  122. package/dist/runtime/answer-contracts.js +414 -0
  123. package/dist/runtime/answer-contracts.js.map +1 -0
  124. package/dist/runtime/artifact-contracts.d.ts +14 -0
  125. package/dist/runtime/artifact-contracts.js +57 -0
  126. package/dist/runtime/artifact-contracts.js.map +1 -0
  127. package/dist/runtime/planning-evidence.d.ts +99 -0
  128. package/dist/runtime/planning-evidence.js +445 -0
  129. package/dist/runtime/planning-evidence.js.map +1 -0
  130. package/dist/runtime/session-coordinator.d.ts +81 -3
  131. package/dist/runtime/session-coordinator.js +201 -17
  132. package/dist/runtime/session-coordinator.js.map +1 -1
  133. package/dist/runtime/tool-defaults-wrapper.d.ts +3 -0
  134. package/dist/runtime/tool-defaults-wrapper.js +25 -0
  135. package/dist/runtime/tool-defaults-wrapper.js.map +1 -0
  136. package/dist/sentiment/store.js +5 -0
  137. package/dist/sentiment/store.js.map +1 -1
  138. package/dist/system-prompt.js +23 -12
  139. package/dist/system-prompt.js.map +1 -1
  140. package/dist/tool-kit.d.ts +4 -4
  141. package/dist/tools/fundamentals/company-overview.d.ts +1 -1
  142. package/dist/tools/fundamentals/company-overview.js +1 -1
  143. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  144. package/dist/tools/fundamentals/comps.d.ts +1 -1
  145. package/dist/tools/fundamentals/comps.js +1 -1
  146. package/dist/tools/fundamentals/comps.js.map +1 -1
  147. package/dist/tools/fundamentals/dcf.d.ts +1 -1
  148. package/dist/tools/fundamentals/dcf.js +1 -1
  149. package/dist/tools/fundamentals/dcf.js.map +1 -1
  150. package/dist/tools/fundamentals/earnings.d.ts +1 -1
  151. package/dist/tools/fundamentals/earnings.js +1 -1
  152. package/dist/tools/fundamentals/earnings.js.map +1 -1
  153. package/dist/tools/fundamentals/financials.d.ts +1 -1
  154. package/dist/tools/fundamentals/financials.js +1 -1
  155. package/dist/tools/fundamentals/financials.js.map +1 -1
  156. package/dist/tools/fundamentals/sec-filings.d.ts +2 -1
  157. package/dist/tools/fundamentals/sec-filings.js +19 -2
  158. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  159. package/dist/tools/index.d.ts +29 -1
  160. package/dist/tools/index.js +30 -0
  161. package/dist/tools/index.js.map +1 -1
  162. package/dist/tools/interaction/ask-user.d.ts +1 -1
  163. package/dist/tools/interaction/twitter-login.d.ts +1 -1
  164. package/dist/tools/macro/fear-greed.d.ts +1 -1
  165. package/dist/tools/macro/fear-greed.js +1 -1
  166. package/dist/tools/macro/fear-greed.js.map +1 -1
  167. package/dist/tools/macro/fred-data.d.ts +1 -1
  168. package/dist/tools/macro/fred-data.js +29 -5
  169. package/dist/tools/macro/fred-data.js.map +1 -1
  170. package/dist/tools/market/crypto-history.d.ts +1 -1
  171. package/dist/tools/market/crypto-history.js +18 -2
  172. package/dist/tools/market/crypto-history.js.map +1 -1
  173. package/dist/tools/market/crypto-price.d.ts +1 -1
  174. package/dist/tools/market/crypto-price.js +1 -1
  175. package/dist/tools/market/crypto-price.js.map +1 -1
  176. package/dist/tools/market/search-ticker.d.ts +1 -1
  177. package/dist/tools/market/search-ticker.js +1 -1
  178. package/dist/tools/market/search-ticker.js.map +1 -1
  179. package/dist/tools/market/stock-history.d.ts +1 -1
  180. package/dist/tools/market/stock-history.js +1 -1
  181. package/dist/tools/market/stock-history.js.map +1 -1
  182. package/dist/tools/market/stock-quote.d.ts +1 -1
  183. package/dist/tools/market/stock-quote.js +1 -1
  184. package/dist/tools/market/stock-quote.js.map +1 -1
  185. package/dist/tools/options/greeks.js +0 -1
  186. package/dist/tools/options/greeks.js.map +1 -1
  187. package/dist/tools/options/option-chain.d.ts +1 -1
  188. package/dist/tools/options/option-chain.js +13 -5
  189. package/dist/tools/options/option-chain.js.map +1 -1
  190. package/dist/tools/portfolio/correlation.d.ts +1 -1
  191. package/dist/tools/portfolio/correlation.js +1 -1
  192. package/dist/tools/portfolio/correlation.js.map +1 -1
  193. package/dist/tools/portfolio/holdings-overlap.d.ts +8 -0
  194. package/dist/tools/portfolio/holdings-overlap.js +105 -0
  195. package/dist/tools/portfolio/holdings-overlap.js.map +1 -0
  196. package/dist/tools/portfolio/predictions.d.ts +1 -1
  197. package/dist/tools/portfolio/predictions.js +1 -1
  198. package/dist/tools/portfolio/predictions.js.map +1 -1
  199. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  200. package/dist/tools/portfolio/risk-analysis.js +1 -1
  201. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  202. package/dist/tools/portfolio/tracker.d.ts +1 -1
  203. package/dist/tools/portfolio/tracker.js +1 -1
  204. package/dist/tools/portfolio/tracker.js.map +1 -1
  205. package/dist/tools/portfolio/watchlist.d.ts +1 -1
  206. package/dist/tools/portfolio/watchlist.js +12 -4
  207. package/dist/tools/portfolio/watchlist.js.map +1 -1
  208. package/dist/tools/sentiment/reddit-sentiment.d.ts +1 -1
  209. package/dist/tools/sentiment/reddit-sentiment.js +1 -1
  210. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  211. package/dist/tools/sentiment/sentiment-summary.d.ts +1 -1
  212. package/dist/tools/sentiment/sentiment-summary.js +57 -2
  213. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  214. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  215. package/dist/tools/sentiment/twitter-sentiment.d.ts +1 -1
  216. package/dist/tools/sentiment/twitter-sentiment.js +1 -1
  217. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  218. package/dist/tools/sentiment/web-search.d.ts +1 -1
  219. package/dist/tools/sentiment/web-search.js +32 -3
  220. package/dist/tools/sentiment/web-search.js.map +1 -1
  221. package/dist/tools/sentiment/web-sentiment.d.ts +1 -1
  222. package/dist/tools/sentiment/web-sentiment.js +1 -1
  223. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  224. package/dist/tools/technical/backtest.d.ts +3 -3
  225. package/dist/tools/technical/backtest.js +41 -27
  226. package/dist/tools/technical/backtest.js.map +1 -1
  227. package/dist/tools/technical/indicators.d.ts +1 -1
  228. package/dist/tools/technical/indicators.js +8 -4
  229. package/dist/tools/technical/indicators.js.map +1 -1
  230. package/dist/types/options.d.ts +10 -0
  231. package/dist/types/portfolio.d.ts +27 -0
  232. package/dist/workflows/compare-assets.js +38 -2
  233. package/dist/workflows/compare-assets.js.map +1 -1
  234. package/dist/workflows/options-screener.js +94 -8
  235. package/dist/workflows/options-screener.js.map +1 -1
  236. package/dist/workflows/portfolio-builder.js +9 -5
  237. package/dist/workflows/portfolio-builder.js.map +1 -1
  238. package/gui/server/ask-user-bridge.ts +82 -0
  239. package/gui/server/background-quotes.ts +31 -0
  240. package/gui/server/chat-event-adapter.ts +142 -0
  241. package/gui/server/gui-session-manager.ts +5 -0
  242. package/gui/server/invoke-tool.ts +89 -0
  243. package/gui/server/live-chat-event-adapter.ts +181 -0
  244. package/gui/server/model-setup.ts +100 -0
  245. package/gui/server/package.json +5 -0
  246. package/gui/server/projector.ts +254 -0
  247. package/gui/server/prompt-observation.ts +61 -0
  248. package/gui/server/server.ts +703 -0
  249. package/gui/server/session-actions.ts +31 -0
  250. package/gui/server/session-entry-wait.ts +81 -0
  251. package/gui/server/tool-metadata.ts +88 -0
  252. package/gui/server/websocket.ts +128 -0
  253. package/gui/server/writer-lock.ts +118 -0
  254. package/gui/shared/chat-events.ts +118 -0
  255. package/gui/shared/event-reducer.ts +186 -0
  256. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +1 -0
  257. package/gui/web/dist/assets/index-Bxt9QpLX.css +1 -0
  258. package/gui/web/dist/assets/index-CZ9DHZYy.js +67 -0
  259. package/gui/web/dist/assets/logo-CWpt6Y2a.svg +187 -0
  260. package/gui/web/dist/index.html +17 -0
  261. package/package.json +50 -18
  262. package/src/analysts/contracts.ts +189 -0
  263. package/src/analysts/orchestrator.ts +300 -0
  264. package/src/cli.ts +206 -0
  265. package/src/config.ts +245 -0
  266. package/src/index.ts +5 -0
  267. package/src/infra/browser.ts +111 -0
  268. package/src/infra/cache.ts +103 -0
  269. package/src/infra/http-client.ts +68 -0
  270. package/src/infra/index.ts +18 -0
  271. package/src/infra/native-dependencies.ts +12 -0
  272. package/src/infra/node-version.ts +24 -0
  273. package/src/infra/open-url.ts +28 -0
  274. package/src/infra/opencandle-paths.ts +64 -0
  275. package/src/infra/rate-limiter.ts +73 -0
  276. package/src/memory/index.ts +10 -0
  277. package/src/memory/manager.ts +192 -0
  278. package/src/memory/preference-extractor.ts +106 -0
  279. package/src/memory/retrieval.ts +70 -0
  280. package/src/memory/sqlite.ts +172 -0
  281. package/src/memory/storage.ts +205 -0
  282. package/src/memory/tool-defaults.ts +87 -0
  283. package/src/memory/types.ts +71 -0
  284. package/src/onboarding/connect.ts +184 -0
  285. package/src/onboarding/credential-interceptor.ts +134 -0
  286. package/src/onboarding/degradation-accumulator.ts +79 -0
  287. package/src/onboarding/prompt-user.ts +85 -0
  288. package/src/onboarding/providers.ts +315 -0
  289. package/src/onboarding/state.ts +218 -0
  290. package/src/onboarding/tool-helpers.ts +111 -0
  291. package/src/onboarding/tool-tags.ts +201 -0
  292. package/src/onboarding/validation.ts +158 -0
  293. package/src/pi/opencandle-extension.ts +955 -0
  294. package/src/pi/session-storage.ts +5 -0
  295. package/src/pi/session.ts +81 -0
  296. package/src/pi/setup.ts +381 -0
  297. package/src/pi/tool-adapter.ts +36 -0
  298. package/src/prompts/context-builder.ts +315 -0
  299. package/src/prompts/disclaimer.ts +9 -0
  300. package/src/prompts/policy-cards.ts +220 -0
  301. package/src/prompts/sections.ts +46 -0
  302. package/src/prompts/workflow-prompts.ts +433 -0
  303. package/src/providers/alpha-vantage.ts +315 -0
  304. package/src/providers/coingecko.ts +96 -0
  305. package/src/providers/exa-search.ts +373 -0
  306. package/src/providers/fear-greed.ts +45 -0
  307. package/src/providers/finnhub.ts +124 -0
  308. package/src/providers/fred.ts +83 -0
  309. package/src/providers/index.ts +9 -0
  310. package/src/providers/provider-credential-error.ts +23 -0
  311. package/src/providers/reddit.ts +151 -0
  312. package/src/providers/sec-edgar.ts +312 -0
  313. package/src/providers/twitter.ts +173 -0
  314. package/src/providers/web-search.ts +293 -0
  315. package/src/providers/with-fallback.ts +41 -0
  316. package/src/providers/wrap-provider.ts +64 -0
  317. package/src/providers/yahoo-finance.ts +534 -0
  318. package/src/routing/classify-intent.ts +285 -0
  319. package/src/routing/defaults.ts +29 -0
  320. package/src/routing/entity-extractor.ts +291 -0
  321. package/src/routing/index.ts +70 -0
  322. package/src/routing/legacy-rule-router.ts +13 -0
  323. package/src/routing/planning.ts +732 -0
  324. package/src/routing/route-manifest.ts +287 -0
  325. package/src/routing/router-llm-client.ts +51 -0
  326. package/src/routing/router-prompt.ts +163 -0
  327. package/src/routing/router-types.ts +87 -0
  328. package/src/routing/router.ts +712 -0
  329. package/src/routing/slot-resolver.ts +190 -0
  330. package/src/routing/turn-context.ts +111 -0
  331. package/src/routing/types.ts +75 -0
  332. package/src/runtime/answer-contracts.ts +633 -0
  333. package/src/runtime/artifact-contracts.ts +76 -0
  334. package/src/runtime/evidence.ts +77 -0
  335. package/src/runtime/planning-evidence.ts +591 -0
  336. package/src/runtime/prompt-step.ts +75 -0
  337. package/src/runtime/provider-tracker.ts +40 -0
  338. package/src/runtime/run-context.ts +22 -0
  339. package/src/runtime/session-coordinator.ts +472 -0
  340. package/src/runtime/tool-defaults-wrapper.ts +35 -0
  341. package/src/runtime/validation.ts +214 -0
  342. package/src/runtime/workflow-events.ts +75 -0
  343. package/src/runtime/workflow-runner.ts +188 -0
  344. package/src/runtime/workflow-types.ts +102 -0
  345. package/src/sentiment/adapters/finnhub.ts +44 -0
  346. package/src/sentiment/adapters/reddit.ts +65 -0
  347. package/src/sentiment/adapters/twitter.ts +36 -0
  348. package/src/sentiment/adapters/web.ts +44 -0
  349. package/src/sentiment/index.ts +58 -0
  350. package/src/sentiment/keywords.ts +9 -0
  351. package/src/sentiment/pipeline.ts +68 -0
  352. package/src/sentiment/scorer.ts +78 -0
  353. package/src/sentiment/store.ts +260 -0
  354. package/src/sentiment/trends.ts +90 -0
  355. package/src/sentiment/types.ts +108 -0
  356. package/src/system-prompt.ts +118 -0
  357. package/src/tool-kit.ts +68 -0
  358. package/src/tools/AGENTS.md +36 -0
  359. package/src/tools/fundamentals/company-overview.ts +54 -0
  360. package/src/tools/fundamentals/comps.ts +156 -0
  361. package/src/tools/fundamentals/dcf.ts +267 -0
  362. package/src/tools/fundamentals/earnings.ts +47 -0
  363. package/src/tools/fundamentals/financials.ts +54 -0
  364. package/src/tools/fundamentals/sec-filings.ts +84 -0
  365. package/src/tools/index.ts +91 -0
  366. package/src/tools/interaction/ask-user.ts +81 -0
  367. package/src/tools/interaction/twitter-login.ts +93 -0
  368. package/src/tools/macro/fear-greed.ts +41 -0
  369. package/src/tools/macro/fred-data.ts +80 -0
  370. package/src/tools/market/crypto-history.ts +67 -0
  371. package/src/tools/market/crypto-price.ts +53 -0
  372. package/src/tools/market/search-ticker.ts +53 -0
  373. package/src/tools/market/stock-history.ts +79 -0
  374. package/src/tools/market/stock-quote.ts +64 -0
  375. package/src/tools/options/greeks.ts +81 -0
  376. package/src/tools/options/option-chain.ts +96 -0
  377. package/src/tools/portfolio/correlation.ts +162 -0
  378. package/src/tools/portfolio/holdings-overlap.ts +123 -0
  379. package/src/tools/portfolio/predictions.ts +253 -0
  380. package/src/tools/portfolio/risk-analysis.ts +134 -0
  381. package/src/tools/portfolio/tracker.ts +147 -0
  382. package/src/tools/portfolio/watchlist.ts +159 -0
  383. package/src/tools/sentiment/reddit-sentiment.ts +164 -0
  384. package/src/tools/sentiment/sentiment-summary.ts +316 -0
  385. package/src/tools/sentiment/sentiment-trend.ts +58 -0
  386. package/src/tools/sentiment/twitter-sentiment.ts +96 -0
  387. package/src/tools/sentiment/web-search.ts +183 -0
  388. package/src/tools/sentiment/web-sentiment.ts +76 -0
  389. package/src/tools/technical/backtest.ts +267 -0
  390. package/src/tools/technical/indicators.ts +256 -0
  391. package/src/types/fundamentals.ts +46 -0
  392. package/src/types/index.ts +20 -0
  393. package/src/types/macro.ts +27 -0
  394. package/src/types/market.ts +43 -0
  395. package/src/types/options.ts +52 -0
  396. package/src/types/portfolio.ts +73 -0
  397. package/src/types/sentiment.ts +70 -0
  398. package/src/workflows/compare-assets.ts +75 -0
  399. package/src/workflows/index.ts +4 -0
  400. package/src/workflows/options-screener.ts +127 -0
  401. package/src/workflows/portfolio-builder.ts +56 -0
  402. package/src/workflows/types.ts +4 -0
  403. package/dist/runtime/index.d.ts +0 -16
  404. package/dist/runtime/index.js +0 -10
  405. package/dist/runtime/index.js.map +0 -1
  406. package/dist/runtime/provider-ids.d.ts +0 -14
  407. package/dist/runtime/provider-ids.js +0 -14
  408. package/dist/runtime/provider-ids.js.map +0 -1
@@ -0,0 +1,214 @@
1
+ import type { EvidenceRecord } from "./evidence.js";
2
+
3
+ /** A single validation check result. */
4
+ export interface ValidationEntry {
5
+ message: string;
6
+ evidenceLabel?: string;
7
+ detail?: string;
8
+ }
9
+
10
+ /** Result of running all deterministic validation checks. */
11
+ export interface ValidationResult {
12
+ passes: ValidationEntry[];
13
+ failures: ValidationEntry[];
14
+ warnings: ValidationEntry[];
15
+ }
16
+
17
+ /** Create an empty validation result. */
18
+ export function emptyValidationResult(): ValidationResult {
19
+ return { passes: [], failures: [], warnings: [] };
20
+ }
21
+
22
+ /** Check that market-sensitive evidence records have timestamps. */
23
+ export function checkTimestamps(
24
+ evidence: EvidenceRecord[],
25
+ marketSensitiveLabels: Set<string>,
26
+ ): ValidationEntry[] {
27
+ const warnings: ValidationEntry[] = [];
28
+ for (const record of evidence) {
29
+ if (
30
+ marketSensitiveLabels.has(record.label) &&
31
+ record.provenance.source === "fetched" &&
32
+ !record.provenance.timestamp
33
+ ) {
34
+ warnings.push({
35
+ message: `Market-sensitive value '${record.label}' has no timestamp`,
36
+ evidenceLabel: record.label,
37
+ });
38
+ }
39
+ }
40
+ return warnings;
41
+ }
42
+
43
+ /** Check that options expiry dates are in the future. */
44
+ export function checkOptionsExpiries(
45
+ evidence: EvidenceRecord[],
46
+ today: string,
47
+ ): ValidationEntry[] {
48
+ const failures: ValidationEntry[] = [];
49
+ for (const record of evidence) {
50
+ if (
51
+ record.label.toLowerCase().includes("expir") &&
52
+ typeof record.value === "string" &&
53
+ record.value < today
54
+ ) {
55
+ failures.push({
56
+ message: `Options expiry ${record.value} is in the past`,
57
+ evidenceLabel: record.label,
58
+ detail: `Today is ${today}`,
59
+ });
60
+ }
61
+ }
62
+ return failures;
63
+ }
64
+
65
+ /** Check that all required fields have evidence records. */
66
+ export function checkRequiredFields(
67
+ evidence: EvidenceRecord[],
68
+ requiredLabels: string[],
69
+ ): ValidationEntry[] {
70
+ const present = new Set(evidence.map((e) => e.label));
71
+ const failures: ValidationEntry[] = [];
72
+ for (const label of requiredLabels) {
73
+ if (!present.has(label)) {
74
+ failures.push({
75
+ message: `Required field '${label}' has no evidence record`,
76
+ evidenceLabel: label,
77
+ });
78
+ }
79
+ }
80
+ return failures;
81
+ }
82
+
83
+ /** Default market-sensitive labels. */
84
+ export const DEFAULT_MARKET_SENSITIVE_LABELS = new Set([
85
+ "Stock Price",
86
+ "Volume",
87
+ "Market Cap",
88
+ "52-Week High",
89
+ "52-Week Low",
90
+ "Bid",
91
+ "Ask",
92
+ "Day High",
93
+ "Day Low",
94
+ "Open",
95
+ "Previous Close",
96
+ "Crypto Price",
97
+ "Crypto Volume",
98
+ ]);
99
+
100
+ /** Configuration for the runtime validator. */
101
+ export interface ValidatorConfig {
102
+ marketSensitiveLabels?: Set<string>;
103
+ requiredFields?: string[];
104
+ toolResults?: Map<string, number>;
105
+ today?: string;
106
+ }
107
+
108
+ /**
109
+ * Orchestrates all deterministic validation checks on evidence records.
110
+ * Runs before LLM-based validation.
111
+ */
112
+ export class RuntimeValidator {
113
+ private readonly config: ValidatorConfig;
114
+
115
+ constructor(config: ValidatorConfig = {}) {
116
+ this.config = config;
117
+ }
118
+
119
+ /** Run all validation checks and return a combined result. */
120
+ validate(evidence: EvidenceRecord[]): ValidationResult {
121
+ const result = emptyValidationResult();
122
+
123
+ // Timestamp checks
124
+ const timestampWarnings = checkTimestamps(
125
+ evidence,
126
+ this.config.marketSensitiveLabels ?? DEFAULT_MARKET_SENSITIVE_LABELS,
127
+ );
128
+ result.warnings.push(...timestampWarnings);
129
+
130
+ // Options expiry checks
131
+ const today = this.config.today ?? new Date().toISOString().slice(0, 10);
132
+ const expiryFailures = checkOptionsExpiries(evidence, today);
133
+ result.failures.push(...expiryFailures);
134
+
135
+ // Required field checks
136
+ if (this.config.requiredFields) {
137
+ const fieldFailures = checkRequiredFields(evidence, this.config.requiredFields);
138
+ result.failures.push(...fieldFailures);
139
+ }
140
+
141
+ // Number match checks
142
+ if (this.config.toolResults) {
143
+ const numberEntries = checkNumberMatch(evidence, this.config.toolResults);
144
+ for (const entry of numberEntries) {
145
+ if ((entry as any).type === "pass") {
146
+ result.passes.push(entry);
147
+ } else {
148
+ result.failures.push(entry);
149
+ }
150
+ }
151
+ }
152
+
153
+ return result;
154
+ }
155
+
156
+ /** Format validation results as a summary string for the LLM validation prompt. */
157
+ formatForLLM(result: ValidationResult): string {
158
+ const lines: string[] = ["## Deterministic Validation Results"];
159
+
160
+ if (result.failures.length > 0) {
161
+ lines.push(`\n### Failures (${result.failures.length})`);
162
+ for (const f of result.failures) {
163
+ lines.push(`- ${f.message}`);
164
+ }
165
+ }
166
+
167
+ if (result.warnings.length > 0) {
168
+ lines.push(`\n### Warnings (${result.warnings.length})`);
169
+ for (const w of result.warnings) {
170
+ lines.push(`- ${w.message}`);
171
+ }
172
+ }
173
+
174
+ if (result.passes.length > 0) {
175
+ lines.push(`\n### Verified (${result.passes.length})`);
176
+ for (const p of result.passes) {
177
+ lines.push(`- ${p.message}`);
178
+ }
179
+ }
180
+
181
+ if (result.failures.length === 0 && result.warnings.length === 0) {
182
+ lines.push("\nAll deterministic checks passed.");
183
+ }
184
+
185
+ return lines.join("\n");
186
+ }
187
+ }
188
+
189
+ /** Check that evidence values match expected tool result values. */
190
+ export function checkNumberMatch(
191
+ evidence: EvidenceRecord[],
192
+ toolResults: Map<string, number>,
193
+ ): ValidationEntry[] {
194
+ const results: (ValidationEntry & { type: "pass" | "failure" })[] = [];
195
+ for (const record of evidence) {
196
+ if (typeof record.value !== "number") continue;
197
+ const expected = toolResults.get(record.label);
198
+ if (expected === undefined) continue;
199
+ if (record.value === expected) {
200
+ results.push({
201
+ type: "pass",
202
+ message: `${record.label}: ${record.value} matches tool result`,
203
+ evidenceLabel: record.label,
204
+ });
205
+ } else {
206
+ results.push({
207
+ type: "failure",
208
+ message: `${record.label} mismatch: evidence says ${record.value}, tool returned ${expected}`,
209
+ evidenceLabel: record.label,
210
+ });
211
+ }
212
+ }
213
+ return results;
214
+ }
@@ -0,0 +1,75 @@
1
+ import type Database from "better-sqlite3";
2
+
3
+ /** All workflow event types. */
4
+ export type WorkflowEventType =
5
+ | "workflow_started"
6
+ | "slot_resolved"
7
+ | "clarification_asked"
8
+ | "clarification_answered"
9
+ | "step_started"
10
+ | "step_completed"
11
+ | "step_failed"
12
+ | "step_skipped"
13
+ | "tool_called"
14
+ | "tool_failed"
15
+ | "validation_passed"
16
+ | "validation_failed"
17
+ | "workflow_completed"
18
+ | "workflow_cancelled";
19
+
20
+ /** A persisted workflow event row. */
21
+ export interface WorkflowEvent {
22
+ id: number;
23
+ runId: string;
24
+ stepIndex: number;
25
+ eventType: WorkflowEventType;
26
+ payloadJson: string | null;
27
+ timestamp: string;
28
+ }
29
+
30
+ /** Append-only workflow event logger backed by SQLite. */
31
+ export class WorkflowEventLogger {
32
+ constructor(private readonly db: Database.Database) {}
33
+
34
+ /** Append a workflow event. */
35
+ log(
36
+ runId: string,
37
+ stepIndex: number,
38
+ eventType: WorkflowEventType,
39
+ payload?: Record<string, unknown>,
40
+ ): void {
41
+ const now = new Date().toISOString();
42
+ const payloadJson = payload ? JSON.stringify(payload) : null;
43
+ this.db
44
+ .prepare(
45
+ `INSERT INTO workflow_events (run_id, step_index, event_type, payload_json, timestamp)
46
+ VALUES (?, ?, ?, ?, ?)`,
47
+ )
48
+ .run(runId, stepIndex, eventType, payloadJson, now);
49
+ }
50
+
51
+ /** Query all events for a given run ID, ordered by timestamp. */
52
+ getEventsByRunId(runId: string): WorkflowEvent[] {
53
+ const rows = this.db
54
+ .prepare(
55
+ "SELECT id, run_id, step_index, event_type, payload_json, timestamp FROM workflow_events WHERE run_id = ? ORDER BY id",
56
+ )
57
+ .all(runId) as Array<{
58
+ id: number;
59
+ run_id: string;
60
+ step_index: number;
61
+ event_type: string;
62
+ payload_json: string | null;
63
+ timestamp: string;
64
+ }>;
65
+
66
+ return rows.map((r) => ({
67
+ id: r.id,
68
+ runId: r.run_id,
69
+ stepIndex: r.step_index,
70
+ eventType: r.event_type as WorkflowEventType,
71
+ payloadJson: r.payload_json,
72
+ timestamp: r.timestamp,
73
+ }));
74
+ }
75
+ }
@@ -0,0 +1,188 @@
1
+ import type { WorkflowRun, StepOutput, WorkflowStep } from "./workflow-types.js";
2
+ import {
3
+ createWorkflowRun,
4
+ transitionStepStatus,
5
+ } from "./workflow-types.js";
6
+ import type { WorkflowEventLogger } from "./workflow-events.js";
7
+ import type { ProviderTracker } from "./provider-tracker.js";
8
+ import type { EvidenceRecord } from "./evidence.js";
9
+
10
+ /** Function that executes a single workflow step. */
11
+ export type StepExecutor = (
12
+ step: WorkflowStep,
13
+ stepIndex: number,
14
+ priorEvidence: EvidenceRecord[],
15
+ context: StepExecutionContext,
16
+ ) => Promise<StepOutput>;
17
+
18
+ /** Context passed to step executors. */
19
+ export interface StepExecutionContext {
20
+ runId: string;
21
+ providerTracker: ProviderTracker;
22
+ }
23
+
24
+ /** Options for creating a WorkflowRunner. */
25
+ export interface WorkflowRunnerOptions {
26
+ eventLogger?: WorkflowEventLogger;
27
+ providerTracker?: ProviderTracker;
28
+ }
29
+
30
+ let runCounter = 0;
31
+
32
+ function generateRunId(): string {
33
+ runCounter += 1;
34
+ return `run_${Date.now()}_${runCounter}`;
35
+ }
36
+
37
+ /**
38
+ * Typed workflow execution engine with run IDs, step definitions,
39
+ * state transitions, cancellation, and event logging.
40
+ */
41
+ export class WorkflowRunner {
42
+ private readonly eventLogger?: WorkflowEventLogger;
43
+ private readonly providerTracker?: ProviderTracker;
44
+ private activeRun: WorkflowRun | null = null;
45
+
46
+ constructor(options: WorkflowRunnerOptions = {}) {
47
+ this.eventLogger = options.eventLogger;
48
+ this.providerTracker = options.providerTracker;
49
+ }
50
+
51
+ /** Get the currently active run, if any. */
52
+ getActiveRun(): WorkflowRun | null {
53
+ return this.activeRun;
54
+ }
55
+
56
+ /**
57
+ * Start a new workflow run. If a run is already active, it is cancelled first.
58
+ */
59
+ async start(
60
+ workflowType: string,
61
+ stepDefinitions: Omit<WorkflowStep, "status">[],
62
+ executor: StepExecutor,
63
+ ): Promise<WorkflowRun> {
64
+ // Cancel any active run
65
+ if (this.activeRun && this.activeRun.status === "running") {
66
+ this.cancel();
67
+ }
68
+
69
+ const runId = generateRunId();
70
+ const run = createWorkflowRun(runId, workflowType, stepDefinitions);
71
+ this.activeRun = run;
72
+ run.status = "running";
73
+
74
+ this.providerTracker?.resetAll();
75
+
76
+ this.logEvent(runId, 0, "workflow_started", {
77
+ workflowType,
78
+ stepCount: stepDefinitions.length,
79
+ });
80
+
81
+ // Execute steps
82
+ await this.executeSteps(run, executor);
83
+
84
+ return run;
85
+ }
86
+
87
+ /** Cancel the active run. */
88
+ cancel(): void {
89
+ const run = this.activeRun;
90
+ if (!run || run.status !== "running") return;
91
+
92
+ for (let i = run.currentStepIndex; i < run.steps.length; i++) {
93
+ const step = run.steps[i];
94
+ if (step.status === "pending" || step.status === "running") {
95
+ step.status = transitionStepStatus(step.status, "skipped");
96
+ this.logEvent(run.runId, i, "step_skipped", {
97
+ stepType: step.stepType,
98
+ reason: "cancelled",
99
+ });
100
+ }
101
+ }
102
+
103
+ run.status = "cancelled";
104
+ this.logEvent(run.runId, run.currentStepIndex, "workflow_cancelled", {
105
+ cancelledAtStep: run.currentStepIndex,
106
+ });
107
+ }
108
+
109
+ private async executeSteps(
110
+ run: WorkflowRun,
111
+ executor: StepExecutor,
112
+ ): Promise<void> {
113
+ for (let i = 0; i < run.steps.length; i++) {
114
+ // Check if run was cancelled externally
115
+ if (run.status !== "running") return;
116
+
117
+ const step = run.steps[i];
118
+ run.currentStepIndex = i;
119
+
120
+ // Collect all prior evidence
121
+ const priorEvidence: EvidenceRecord[] = [];
122
+ for (const [, output] of run.stepOutputs) {
123
+ priorEvidence.push(...output.evidence);
124
+ }
125
+
126
+ // Transition to running
127
+ step.status = transitionStepStatus(step.status, "running");
128
+ this.logEvent(run.runId, i, "step_started", { stepType: step.stepType });
129
+
130
+ try {
131
+ const context: StepExecutionContext = {
132
+ runId: run.runId,
133
+ providerTracker: this.providerTracker!,
134
+ };
135
+
136
+ const output = await executor(step, i, priorEvidence, context);
137
+
138
+ // If run was cancelled during execution, stop without further transitions
139
+ if (run.status !== "running") return;
140
+
141
+ step.status = transitionStepStatus(step.status, "completed");
142
+ run.stepOutputs.set(i, output);
143
+
144
+ this.logEvent(run.runId, i, "step_completed", {
145
+ stepType: step.stepType,
146
+ evidenceCount: output.evidence.length,
147
+ });
148
+ } catch (error) {
149
+ // If run was cancelled during execution, stop without further transitions
150
+ if (run.status !== "running") return;
151
+
152
+ const message = error instanceof Error ? error.message : "unknown_error";
153
+
154
+ if (step.skippable) {
155
+ step.status = transitionStepStatus(step.status, "skipped");
156
+ this.logEvent(run.runId, i, "step_skipped", {
157
+ stepType: step.stepType,
158
+ reason: message,
159
+ });
160
+ } else {
161
+ step.status = transitionStepStatus(step.status, "failed");
162
+ this.logEvent(run.runId, i, "step_failed", {
163
+ stepType: step.stepType,
164
+ error: message,
165
+ });
166
+ run.status = "failed";
167
+ return;
168
+ }
169
+ }
170
+ }
171
+
172
+ if (run.status === "running") {
173
+ run.status = "completed";
174
+ this.logEvent(run.runId, run.steps.length - 1, "workflow_completed", {
175
+ workflowType: run.workflowType,
176
+ });
177
+ }
178
+ }
179
+
180
+ private logEvent(
181
+ runId: string,
182
+ stepIndex: number,
183
+ eventType: string,
184
+ payload: Record<string, unknown>,
185
+ ): void {
186
+ this.eventLogger?.log(runId, stepIndex, eventType as any, payload);
187
+ }
188
+ }
@@ -0,0 +1,102 @@
1
+ import type { EvidenceRecord } from "./evidence.js";
2
+
3
+ /** Status of a single workflow step. */
4
+ export type StepStatus = "pending" | "running" | "completed" | "failed" | "skipped";
5
+
6
+ /** Overall status of a workflow run. */
7
+ export type RunStatus = "pending" | "running" | "completed" | "failed" | "cancelled";
8
+
9
+ /** Valid step status transitions. */
10
+ const VALID_STEP_TRANSITIONS: Record<StepStatus, StepStatus[]> = {
11
+ pending: ["running", "skipped"],
12
+ running: ["completed", "failed", "skipped"],
13
+ completed: [],
14
+ failed: [],
15
+ skipped: [],
16
+ };
17
+
18
+ /** Check whether a step status transition is valid. */
19
+ export function isValidStepTransition(from: StepStatus, to: StepStatus): boolean {
20
+ return VALID_STEP_TRANSITIONS[from].includes(to);
21
+ }
22
+
23
+ /** Transition a step status, throwing on invalid transitions. */
24
+ export function transitionStepStatus(from: StepStatus, to: StepStatus): StepStatus {
25
+ if (!isValidStepTransition(from, to)) {
26
+ throw new Error(`Invalid step transition: ${from} → ${to}`);
27
+ }
28
+ return to;
29
+ }
30
+
31
+ /** Definition of a single workflow step. */
32
+ export interface WorkflowStep {
33
+ stepType: string;
34
+ description: string;
35
+ requiredInputs: string[];
36
+ expectedOutputs: string[];
37
+ skippable: boolean;
38
+ status: StepStatus;
39
+ }
40
+
41
+ /** Output produced by a completed workflow step. */
42
+ export interface StepOutput {
43
+ stepIndex: number;
44
+ stepType: string;
45
+ evidence: EvidenceRecord[];
46
+ rawText?: string;
47
+ }
48
+
49
+ /** Analyst signal direction. */
50
+ export type AnalystSignal = "BUY" | "HOLD" | "SELL";
51
+
52
+ /** Structured output from a single analyst role. */
53
+ export interface AnalystOutput {
54
+ role: string;
55
+ signal: AnalystSignal;
56
+ conviction: number;
57
+ thesis: string;
58
+ evidence: EvidenceRecord[];
59
+ rawText?: string;
60
+ }
61
+
62
+ /** Debate side in bull/bear adversarial debate. */
63
+ export type DebateSide = "bull" | "bear";
64
+
65
+ /** Structured output from a debate step (eval/test only — not used in live path). */
66
+ export interface DebateOutput {
67
+ side: DebateSide;
68
+ thesis: string;
69
+ keyRisk: string;
70
+ concessions: string[];
71
+ remainingConviction: number;
72
+ evidence: EvidenceRecord[];
73
+ rawText: string;
74
+ }
75
+
76
+ /** A complete workflow run definition and state. */
77
+ export interface WorkflowRun {
78
+ runId: string;
79
+ workflowType: string;
80
+ steps: WorkflowStep[];
81
+ currentStepIndex: number;
82
+ status: RunStatus;
83
+ stepOutputs: Map<number, StepOutput>;
84
+ createdAt: string;
85
+ }
86
+
87
+ /** Create a new workflow run with all steps in pending state. */
88
+ export function createWorkflowRun(
89
+ runId: string,
90
+ workflowType: string,
91
+ stepDefinitions: Omit<WorkflowStep, "status">[],
92
+ ): WorkflowRun {
93
+ return {
94
+ runId,
95
+ workflowType,
96
+ steps: stepDefinitions.map((def) => ({ ...def, status: "pending" as const })),
97
+ currentStepIndex: 0,
98
+ status: "pending",
99
+ stepOutputs: new Map(),
100
+ createdAt: new Date().toISOString(),
101
+ };
102
+ }
@@ -0,0 +1,44 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { SentinelRecord } from "../types.js";
3
+ import type { FinnhubArticle } from "../../providers/finnhub.js";
4
+ import { extractEntities } from "../../routing/entity-extractor.js";
5
+
6
+ const MAX_TICKERS = 3;
7
+
8
+ export class FinnhubAdapter {
9
+ readonly source = "finnhub" as const;
10
+
11
+ mapToRecords(articles: FinnhubArticle[], query: string): SentinelRecord[] {
12
+ const fetchedAt = new Date().toISOString();
13
+ return articles.map((article) => ({
14
+ id: randomUUID(),
15
+ source: this.source,
16
+ sourceId: String(article.id),
17
+ query,
18
+ title: article.headline,
19
+ text: article.summary,
20
+ author: article.source,
21
+ url: article.url,
22
+ publishedAt: new Date(article.datetime * 1000).toISOString(),
23
+ fetchedAt,
24
+ engagement: {
25
+ score: 0,
26
+ replies: null,
27
+ shares: null,
28
+ views: null,
29
+ },
30
+ sentiment: {
31
+ score: 0,
32
+ confidence: 0,
33
+ method: "keyword" as const,
34
+ tickers: article.related ? article.related.split(",").map((t) => t.trim()).filter(Boolean) : [],
35
+ },
36
+ metadata: { category: article.category },
37
+ }));
38
+ }
39
+ }
40
+
41
+ export function extractTickersFromQuery(query: string): string[] {
42
+ const entities = extractEntities(query);
43
+ return entities.symbols.slice(0, MAX_TICKERS);
44
+ }
@@ -0,0 +1,65 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import type { SentinelRecord, SentimentAdapter } from "../types.js";
3
+ import type { RedditSentimentResult } from "../../types/sentiment.js";
4
+ import type { RedditComment } from "../../providers/reddit.js";
5
+
6
+ export class RedditAdapter implements SentimentAdapter {
7
+ readonly source = "reddit" as const;
8
+
9
+ mapPostsToRecords(result: RedditSentimentResult, query: string): SentinelRecord[] {
10
+ const fetchedAt = result.fetchedAt;
11
+ return result.posts.map((post) => ({
12
+ id: randomUUID(),
13
+ source: this.source,
14
+ sourceId: post.id,
15
+ query,
16
+ title: post.title,
17
+ text: post.selftext ? `${post.title}\n${post.selftext}` : post.title,
18
+ author: post.author,
19
+ url: post.url,
20
+ publishedAt: post.created,
21
+ fetchedAt,
22
+ engagement: {
23
+ score: post.score,
24
+ replies: post.comments,
25
+ shares: null,
26
+ views: null,
27
+ },
28
+ sentiment: { score: 0, confidence: 0, method: "keyword" as const, tickers: [] },
29
+ metadata: { subreddit: result.subreddit },
30
+ }));
31
+ }
32
+
33
+ mapCommentsToRecords(
34
+ comments: RedditComment[],
35
+ parentId: string,
36
+ subreddit: string,
37
+ query: string,
38
+ ): SentinelRecord[] {
39
+ const fetchedAt = new Date().toISOString();
40
+ return comments.map((comment) => ({
41
+ id: randomUUID(),
42
+ source: this.source,
43
+ sourceId: comment.id,
44
+ query,
45
+ title: null,
46
+ text: comment.body,
47
+ author: comment.author,
48
+ url: comment.permalink,
49
+ publishedAt: null,
50
+ fetchedAt,
51
+ engagement: {
52
+ score: comment.score,
53
+ replies: null,
54
+ shares: null,
55
+ views: null,
56
+ },
57
+ sentiment: { score: 0, confidence: 0, method: "keyword" as const, tickers: [] },
58
+ metadata: { isComment: true, parentId, subreddit },
59
+ }));
60
+ }
61
+
62
+ async fetch(_query: string, _options?: { hours?: number }): Promise<SentinelRecord[]> {
63
+ throw new Error("Use pipeline.run() instead of adapter.fetch() directly");
64
+ }
65
+ }