opencandle 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (564) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +186 -117
  3. package/dist/analysts/contracts.d.ts +1 -3
  4. package/dist/analysts/contracts.js +1 -11
  5. package/dist/analysts/contracts.js.map +1 -1
  6. package/dist/analysts/orchestrator.d.ts +1 -3
  7. package/dist/analysts/orchestrator.js +1 -26
  8. package/dist/analysts/orchestrator.js.map +1 -1
  9. package/dist/cli.js +32 -8
  10. package/dist/cli.js.map +1 -1
  11. package/dist/config.d.ts +19 -3
  12. package/dist/config.js +69 -3
  13. package/dist/config.js.map +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/infra/browser.d.ts +1 -3
  18. package/dist/infra/browser.js +4 -2
  19. package/dist/infra/browser.js.map +1 -1
  20. package/dist/infra/cache.d.ts +8 -11
  21. package/dist/infra/cache.js +17 -15
  22. package/dist/infra/cache.js.map +1 -1
  23. package/dist/infra/http-client.d.ts +4 -1
  24. package/dist/infra/http-client.js +59 -6
  25. package/dist/infra/http-client.js.map +1 -1
  26. package/dist/infra/index.d.ts +3 -3
  27. package/dist/infra/index.js +3 -3
  28. package/dist/infra/index.js.map +1 -1
  29. package/dist/infra/native-dependencies.js +2 -2
  30. package/dist/infra/native-dependencies.js.map +1 -1
  31. package/dist/infra/node-version.js.map +1 -1
  32. package/dist/infra/opencandle-paths.d.ts +0 -3
  33. package/dist/infra/opencandle-paths.js +4 -11
  34. package/dist/infra/opencandle-paths.js.map +1 -1
  35. package/dist/infra/rate-limiter.d.ts +4 -0
  36. package/dist/infra/rate-limiter.js +17 -10
  37. package/dist/infra/rate-limiter.js.map +1 -1
  38. package/dist/market-state/alert-conditions.d.ts +34 -0
  39. package/dist/market-state/alert-conditions.js +23 -0
  40. package/dist/market-state/alert-conditions.js.map +1 -0
  41. package/dist/market-state/alert-runner.d.ts +55 -0
  42. package/dist/market-state/alert-runner.js +634 -0
  43. package/dist/market-state/alert-runner.js.map +1 -0
  44. package/dist/market-state/daily-report.d.ts +26 -0
  45. package/dist/market-state/daily-report.js +179 -0
  46. package/dist/market-state/daily-report.js.map +1 -0
  47. package/dist/market-state/local-automation-service.d.ts +25 -0
  48. package/dist/market-state/local-automation-service.js +119 -0
  49. package/dist/market-state/local-automation-service.js.map +1 -0
  50. package/dist/market-state/notification-delivery.d.ts +14 -0
  51. package/dist/market-state/notification-delivery.js +139 -0
  52. package/dist/market-state/notification-delivery.js.map +1 -0
  53. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  54. package/dist/market-state/resolve-for-mutation.js +15 -0
  55. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  56. package/dist/market-state/resolve.d.ts +14 -0
  57. package/dist/market-state/resolve.js +89 -0
  58. package/dist/market-state/resolve.js.map +1 -0
  59. package/dist/market-state/service.d.ts +527 -0
  60. package/dist/market-state/service.js +1099 -0
  61. package/dist/market-state/service.js.map +1 -0
  62. package/dist/memory/index.d.ts +7 -7
  63. package/dist/memory/index.js +6 -6
  64. package/dist/memory/index.js.map +1 -1
  65. package/dist/memory/manager.d.ts +9 -0
  66. package/dist/memory/manager.js +39 -22
  67. package/dist/memory/manager.js.map +1 -1
  68. package/dist/memory/retrieval.js +7 -4
  69. package/dist/memory/retrieval.js.map +1 -1
  70. package/dist/memory/sqlite.js +385 -3
  71. package/dist/memory/sqlite.js.map +1 -1
  72. package/dist/memory/storage.d.ts +3 -2
  73. package/dist/memory/storage.js +1 -2
  74. package/dist/memory/storage.js.map +1 -1
  75. package/dist/memory/tool-defaults.js +64 -28
  76. package/dist/memory/tool-defaults.js.map +1 -1
  77. package/dist/memory/types.js +4 -0
  78. package/dist/memory/types.js.map +1 -1
  79. package/dist/monitor.d.ts +2 -0
  80. package/dist/monitor.js +104 -0
  81. package/dist/monitor.js.map +1 -0
  82. package/dist/onboarding/connect.js +4 -6
  83. package/dist/onboarding/connect.js.map +1 -1
  84. package/dist/onboarding/credential-interceptor.js +1 -1
  85. package/dist/onboarding/credential-interceptor.js.map +1 -1
  86. package/dist/onboarding/degradation-accumulator.js +1 -3
  87. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  88. package/dist/onboarding/providers.js +3 -16
  89. package/dist/onboarding/providers.js.map +1 -1
  90. package/dist/onboarding/state.js.map +1 -1
  91. package/dist/onboarding/tool-helpers.js +1 -1
  92. package/dist/onboarding/tool-helpers.js.map +1 -1
  93. package/dist/onboarding/tool-tags.js +6 -4
  94. package/dist/onboarding/tool-tags.js.map +1 -1
  95. package/dist/onboarding/validation.js +1 -1
  96. package/dist/onboarding/validation.js.map +1 -1
  97. package/dist/pi/opencandle-extension.d.ts +8 -0
  98. package/dist/pi/opencandle-extension.js +637 -59
  99. package/dist/pi/opencandle-extension.js.map +1 -1
  100. package/dist/pi/session.d.ts +1 -1
  101. package/dist/pi/session.js +3 -1
  102. package/dist/pi/session.js.map +1 -1
  103. package/dist/pi/setup.js +17 -2
  104. package/dist/pi/setup.js.map +1 -1
  105. package/dist/pi/tool-adapter.js +5 -2
  106. package/dist/pi/tool-adapter.js.map +1 -1
  107. package/dist/prompts/context-builder.d.ts +18 -3
  108. package/dist/prompts/context-builder.js +117 -18
  109. package/dist/prompts/context-builder.js.map +1 -1
  110. package/dist/prompts/disclaimer.js +1 -1
  111. package/dist/prompts/disclaimer.js.map +1 -1
  112. package/dist/prompts/policy-cards.d.ts +13 -0
  113. package/dist/prompts/policy-cards.js +197 -0
  114. package/dist/prompts/policy-cards.js.map +1 -0
  115. package/dist/prompts/sections.d.ts +1 -1
  116. package/dist/prompts/sections.js +3 -3
  117. package/dist/prompts/sections.js.map +1 -1
  118. package/dist/prompts/symbol-preflight.d.ts +20 -0
  119. package/dist/prompts/symbol-preflight.js +49 -0
  120. package/dist/prompts/symbol-preflight.js.map +1 -0
  121. package/dist/prompts/workflow-prompts.d.ts +1 -1
  122. package/dist/prompts/workflow-prompts.js +209 -19
  123. package/dist/prompts/workflow-prompts.js.map +1 -1
  124. package/dist/providers/alpha-vantage.d.ts +1 -1
  125. package/dist/providers/alpha-vantage.js +49 -8
  126. package/dist/providers/alpha-vantage.js.map +1 -1
  127. package/dist/providers/coingecko.js +1 -1
  128. package/dist/providers/coingecko.js.map +1 -1
  129. package/dist/providers/errors.d.ts +5 -0
  130. package/dist/providers/errors.js +11 -0
  131. package/dist/providers/errors.js.map +1 -0
  132. package/dist/providers/exa-search.d.ts +2 -2
  133. package/dist/providers/exa-search.js +19 -11
  134. package/dist/providers/exa-search.js.map +1 -1
  135. package/dist/providers/fear-greed.js +1 -1
  136. package/dist/providers/fear-greed.js.map +1 -1
  137. package/dist/providers/finnhub.js +3 -5
  138. package/dist/providers/finnhub.js.map +1 -1
  139. package/dist/providers/fred.js +2 -2
  140. package/dist/providers/fred.js.map +1 -1
  141. package/dist/providers/index.d.ts +7 -6
  142. package/dist/providers/index.js +6 -5
  143. package/dist/providers/index.js.map +1 -1
  144. package/dist/providers/reddit.js +2 -2
  145. package/dist/providers/reddit.js.map +1 -1
  146. package/dist/providers/sec-edgar.d.ts +9 -1
  147. package/dist/providers/sec-edgar.js +181 -6
  148. package/dist/providers/sec-edgar.js.map +1 -1
  149. package/dist/providers/tradingview.d.ts +47 -0
  150. package/dist/providers/tradingview.js +275 -0
  151. package/dist/providers/tradingview.js.map +1 -0
  152. package/dist/providers/twitter.js +6 -8
  153. package/dist/providers/twitter.js.map +1 -1
  154. package/dist/providers/web-search.js +26 -12
  155. package/dist/providers/web-search.js.map +1 -1
  156. package/dist/providers/with-fallback.js +4 -2
  157. package/dist/providers/with-fallback.js.map +1 -1
  158. package/dist/providers/wrap-provider.d.ts +2 -3
  159. package/dist/providers/wrap-provider.js +14 -8
  160. package/dist/providers/wrap-provider.js.map +1 -1
  161. package/dist/providers/yahoo-finance.d.ts +3 -1
  162. package/dist/providers/yahoo-finance.js +226 -11
  163. package/dist/providers/yahoo-finance.js.map +1 -1
  164. package/dist/routing/classify-intent.d.ts +9 -0
  165. package/dist/routing/classify-intent.js +153 -3
  166. package/dist/routing/classify-intent.js.map +1 -1
  167. package/dist/routing/defaults.d.ts +1 -1
  168. package/dist/routing/defaults.js +3 -3
  169. package/dist/routing/defaults.js.map +1 -1
  170. package/dist/routing/entity-extractor.d.ts +2 -0
  171. package/dist/routing/entity-extractor.js +377 -26
  172. package/dist/routing/entity-extractor.js.map +1 -1
  173. package/dist/routing/fund-symbols.d.ts +2 -0
  174. package/dist/routing/fund-symbols.js +55 -0
  175. package/dist/routing/fund-symbols.js.map +1 -0
  176. package/dist/routing/horizon.d.ts +1 -0
  177. package/dist/routing/horizon.js +10 -0
  178. package/dist/routing/horizon.js.map +1 -0
  179. package/dist/routing/index.d.ts +12 -6
  180. package/dist/routing/index.js +8 -4
  181. package/dist/routing/index.js.map +1 -1
  182. package/dist/routing/legacy-rule-router.d.ts +9 -0
  183. package/dist/routing/legacy-rule-router.js +12 -0
  184. package/dist/routing/legacy-rule-router.js.map +1 -0
  185. package/dist/routing/planning.d.ts +54 -0
  186. package/dist/routing/planning.js +562 -0
  187. package/dist/routing/planning.js.map +1 -0
  188. package/dist/routing/route-manifest.d.ts +35 -0
  189. package/dist/routing/route-manifest.js +242 -0
  190. package/dist/routing/route-manifest.js.map +1 -0
  191. package/dist/routing/router-llm-client.js.map +1 -1
  192. package/dist/routing/router-prompt.js +46 -45
  193. package/dist/routing/router-prompt.js.map +1 -1
  194. package/dist/routing/router-types.d.ts +10 -0
  195. package/dist/routing/router.d.ts +1 -0
  196. package/dist/routing/router.js +572 -13
  197. package/dist/routing/router.js.map +1 -1
  198. package/dist/routing/slot-resolver.d.ts +1 -1
  199. package/dist/routing/slot-resolver.js +45 -7
  200. package/dist/routing/slot-resolver.js.map +1 -1
  201. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  202. package/dist/routing/symbol-disambiguator.js +52 -0
  203. package/dist/routing/symbol-disambiguator.js.map +1 -0
  204. package/dist/routing/turn-context.d.ts +44 -0
  205. package/dist/routing/turn-context.js +45 -0
  206. package/dist/routing/turn-context.js.map +1 -0
  207. package/dist/routing/types.d.ts +15 -1
  208. package/dist/runtime/answer-contracts.d.ts +82 -0
  209. package/dist/runtime/answer-contracts.js +442 -0
  210. package/dist/runtime/answer-contracts.js.map +1 -0
  211. package/dist/runtime/artifact-contracts.d.ts +14 -0
  212. package/dist/runtime/artifact-contracts.js +57 -0
  213. package/dist/runtime/artifact-contracts.js.map +1 -0
  214. package/dist/runtime/planning-evidence.d.ts +99 -0
  215. package/dist/runtime/planning-evidence.js +466 -0
  216. package/dist/runtime/planning-evidence.js.map +1 -0
  217. package/dist/runtime/prompt-step.d.ts +1 -9
  218. package/dist/runtime/prompt-step.js +0 -10
  219. package/dist/runtime/prompt-step.js.map +1 -1
  220. package/dist/runtime/run-context.d.ts +5 -2
  221. package/dist/runtime/run-context.js +8 -1
  222. package/dist/runtime/run-context.js.map +1 -1
  223. package/dist/runtime/session-coordinator.d.ts +29 -3
  224. package/dist/runtime/session-coordinator.js +204 -31
  225. package/dist/runtime/session-coordinator.js.map +1 -1
  226. package/dist/runtime/session-title.d.ts +14 -0
  227. package/dist/runtime/session-title.js +50 -0
  228. package/dist/runtime/session-title.js.map +1 -0
  229. package/dist/runtime/tool-defaults-wrapper.js +1 -3
  230. package/dist/runtime/tool-defaults-wrapper.js.map +1 -1
  231. package/dist/runtime/validation.js.map +1 -1
  232. package/dist/runtime/workflow-events.js.map +1 -1
  233. package/dist/runtime/workflow-runner.d.ts +3 -3
  234. package/dist/runtime/workflow-runner.js +1 -1
  235. package/dist/runtime/workflow-runner.js.map +1 -1
  236. package/dist/sentiment/adapters/finnhub.d.ts +1 -1
  237. package/dist/sentiment/adapters/finnhub.js +6 -1
  238. package/dist/sentiment/adapters/finnhub.js.map +1 -1
  239. package/dist/sentiment/adapters/reddit.d.ts +2 -2
  240. package/dist/sentiment/adapters/twitter.d.ts +1 -1
  241. package/dist/sentiment/adapters/web.d.ts +1 -1
  242. package/dist/sentiment/index.d.ts +9 -11
  243. package/dist/sentiment/index.js +9 -20
  244. package/dist/sentiment/index.js.map +1 -1
  245. package/dist/sentiment/keywords.js +26 -4
  246. package/dist/sentiment/keywords.js.map +1 -1
  247. package/dist/sentiment/pipeline.d.ts +2 -2
  248. package/dist/sentiment/pipeline.js +1 -1
  249. package/dist/sentiment/pipeline.js.map +1 -1
  250. package/dist/sentiment/scorer.js +1 -1
  251. package/dist/sentiment/store.d.ts +1 -1
  252. package/dist/sentiment/store.js +1 -1
  253. package/dist/sentiment/store.js.map +1 -1
  254. package/dist/sentiment/trends.d.ts +1 -1
  255. package/dist/sentiment/trends.js.map +1 -1
  256. package/dist/sentiment/types.js.map +1 -1
  257. package/dist/system-prompt.js +7 -3
  258. package/dist/system-prompt.js.map +1 -1
  259. package/dist/tool-kit.d.ts +7 -7
  260. package/dist/tool-kit.js +4 -4
  261. package/dist/tool-kit.js.map +1 -1
  262. package/dist/tools/fundamentals/company-overview.js +12 -7
  263. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  264. package/dist/tools/fundamentals/comps.js +19 -10
  265. package/dist/tools/fundamentals/comps.js.map +1 -1
  266. package/dist/tools/fundamentals/dcf.js +24 -12
  267. package/dist/tools/fundamentals/dcf.js.map +1 -1
  268. package/dist/tools/fundamentals/earnings.js +9 -4
  269. package/dist/tools/fundamentals/earnings.js.map +1 -1
  270. package/dist/tools/fundamentals/financials.js +9 -4
  271. package/dist/tools/fundamentals/financials.js.map +1 -1
  272. package/dist/tools/fundamentals/sec-filings.d.ts +1 -0
  273. package/dist/tools/fundamentals/sec-filings.js +36 -4
  274. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  275. package/dist/tools/index.d.ts +23 -18
  276. package/dist/tools/index.js +53 -38
  277. package/dist/tools/index.js.map +1 -1
  278. package/dist/tools/interaction/ask-user.js +15 -3
  279. package/dist/tools/interaction/ask-user.js.map +1 -1
  280. package/dist/tools/interaction/twitter-login.js +13 -3
  281. package/dist/tools/interaction/twitter-login.js.map +1 -1
  282. package/dist/tools/macro/fear-greed.js +1 -1
  283. package/dist/tools/macro/fear-greed.js.map +1 -1
  284. package/dist/tools/macro/fred-data.d.ts +1 -1
  285. package/dist/tools/macro/fred-data.js +44 -9
  286. package/dist/tools/macro/fred-data.js.map +1 -1
  287. package/dist/tools/market/crypto-history.js +21 -3
  288. package/dist/tools/market/crypto-history.js.map +1 -1
  289. package/dist/tools/market/crypto-price.js +4 -2
  290. package/dist/tools/market/crypto-price.js.map +1 -1
  291. package/dist/tools/market/screen-stocks.d.ts +18 -0
  292. package/dist/tools/market/screen-stocks.js +252 -0
  293. package/dist/tools/market/screen-stocks.js.map +1 -0
  294. package/dist/tools/market/search-ticker.js +161 -9
  295. package/dist/tools/market/search-ticker.js.map +1 -1
  296. package/dist/tools/market/stock-history.d.ts +2 -2
  297. package/dist/tools/market/stock-history.js +27 -8
  298. package/dist/tools/market/stock-history.js.map +1 -1
  299. package/dist/tools/market/stock-quote.js +6 -4
  300. package/dist/tools/market/stock-quote.js.map +1 -1
  301. package/dist/tools/options/greeks.js +1 -2
  302. package/dist/tools/options/greeks.js.map +1 -1
  303. package/dist/tools/options/option-chain.js +27 -9
  304. package/dist/tools/options/option-chain.js.map +1 -1
  305. package/dist/tools/portfolio/alerts.d.ts +15 -0
  306. package/dist/tools/portfolio/alerts.js +357 -0
  307. package/dist/tools/portfolio/alerts.js.map +1 -0
  308. package/dist/tools/portfolio/correlation.d.ts +1 -1
  309. package/dist/tools/portfolio/correlation.js +34 -14
  310. package/dist/tools/portfolio/correlation.js.map +1 -1
  311. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  312. package/dist/tools/portfolio/daily-report.js +83 -0
  313. package/dist/tools/portfolio/daily-report.js.map +1 -0
  314. package/dist/tools/portfolio/holdings-overlap.d.ts +8 -0
  315. package/dist/tools/portfolio/holdings-overlap.js +112 -0
  316. package/dist/tools/portfolio/holdings-overlap.js.map +1 -0
  317. package/dist/tools/portfolio/notifications.d.ts +7 -0
  318. package/dist/tools/portfolio/notifications.js +43 -0
  319. package/dist/tools/portfolio/notifications.js.map +1 -0
  320. package/dist/tools/portfolio/predictions.d.ts +12 -6
  321. package/dist/tools/portfolio/predictions.js +338 -88
  322. package/dist/tools/portfolio/predictions.js.map +1 -1
  323. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  324. package/dist/tools/portfolio/risk-analysis.js +46 -7
  325. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  326. package/dist/tools/portfolio/tracker.d.ts +4 -3
  327. package/dist/tools/portfolio/tracker.js +247 -102
  328. package/dist/tools/portfolio/tracker.js.map +1 -1
  329. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  330. package/dist/tools/portfolio/watchlist.js +209 -101
  331. package/dist/tools/portfolio/watchlist.js.map +1 -1
  332. package/dist/tools/sentiment/reddit-sentiment.js +24 -11
  333. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  334. package/dist/tools/sentiment/sentiment-summary.js +71 -14
  335. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  336. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  337. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  338. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  339. package/dist/tools/sentiment/twitter-sentiment.js +13 -6
  340. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  341. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  342. package/dist/tools/sentiment/untrusted-text.js +17 -0
  343. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  344. package/dist/tools/sentiment/web-search.js +37 -12
  345. package/dist/tools/sentiment/web-search.js.map +1 -1
  346. package/dist/tools/sentiment/web-sentiment.js +16 -4
  347. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  348. package/dist/tools/technical/backtest.d.ts +3 -3
  349. package/dist/tools/technical/backtest.js +65 -44
  350. package/dist/tools/technical/backtest.js.map +1 -1
  351. package/dist/tools/technical/indicators.js +24 -8
  352. package/dist/tools/technical/indicators.js.map +1 -1
  353. package/dist/types/index.d.ts +3 -3
  354. package/dist/types/index.js.map +1 -1
  355. package/dist/types/market.d.ts +1 -0
  356. package/dist/types/options.d.ts +10 -0
  357. package/dist/types/portfolio.d.ts +41 -4
  358. package/dist/workflows/compare-assets.d.ts +0 -3
  359. package/dist/workflows/compare-assets.js +55 -10
  360. package/dist/workflows/compare-assets.js.map +1 -1
  361. package/dist/workflows/index.d.ts +3 -4
  362. package/dist/workflows/index.js +3 -3
  363. package/dist/workflows/index.js.map +1 -1
  364. package/dist/workflows/options-screener.d.ts +0 -3
  365. package/dist/workflows/options-screener.js +88 -14
  366. package/dist/workflows/options-screener.js.map +1 -1
  367. package/dist/workflows/portfolio-builder.d.ts +0 -3
  368. package/dist/workflows/portfolio-builder.js +7 -11
  369. package/dist/workflows/portfolio-builder.js.map +1 -1
  370. package/gui/server/ask-user-bridge.ts +82 -0
  371. package/gui/server/automation-heartbeat.ts +97 -0
  372. package/gui/server/background-quotes.ts +97 -1
  373. package/gui/server/chat-event-adapter.ts +32 -10
  374. package/gui/server/chat-run-session.ts +16 -0
  375. package/gui/server/gui-session-manager.ts +5 -0
  376. package/gui/server/invoke-tool.ts +144 -1
  377. package/gui/server/live-chat-event-adapter.ts +21 -6
  378. package/gui/server/market-state-api.ts +315 -0
  379. package/gui/server/model-setup.ts +149 -2
  380. package/gui/server/private-api-access.ts +62 -0
  381. package/gui/server/projector.ts +58 -11
  382. package/gui/server/prompt-observation.ts +58 -0
  383. package/gui/server/quote-snapshot-store.ts +50 -0
  384. package/gui/server/server.ts +236 -376
  385. package/gui/server/session-actions.ts +186 -1
  386. package/gui/server/session-entry-wait.ts +81 -0
  387. package/gui/server/shutdown.ts +47 -0
  388. package/gui/server/tool-invoke-ack.ts +49 -0
  389. package/gui/server/tool-metadata.ts +23 -10
  390. package/gui/server/websocket.ts +13 -3
  391. package/gui/server/writer-lock.ts +6 -2
  392. package/gui/server/ws-hub.ts +292 -0
  393. package/gui/shared/chat-events.ts +16 -1
  394. package/gui/shared/event-reducer.ts +24 -6
  395. package/gui/web/dist/assets/CatalogOverlay-eJ2cBk33.js +1 -0
  396. package/gui/web/dist/assets/index-2KZtKBmu.css +1 -0
  397. package/gui/web/dist/assets/index-CveNgtDg.js +69 -0
  398. package/gui/web/dist/index.html +2 -2
  399. package/package.json +22 -12
  400. package/src/analysts/contracts.ts +10 -23
  401. package/src/analysts/orchestrator.ts +8 -43
  402. package/src/cli.ts +37 -13
  403. package/src/config.ts +99 -7
  404. package/src/index.ts +1 -1
  405. package/src/infra/browser.ts +4 -2
  406. package/src/infra/cache.ts +41 -30
  407. package/src/infra/http-client.ts +72 -6
  408. package/src/infra/index.ts +7 -10
  409. package/src/infra/native-dependencies.ts +8 -3
  410. package/src/infra/node-version.ts +3 -1
  411. package/src/infra/opencandle-paths.ts +3 -14
  412. package/src/infra/rate-limiter.ts +32 -20
  413. package/src/market-state/alert-conditions.ts +82 -0
  414. package/src/market-state/alert-runner.ts +863 -0
  415. package/src/market-state/daily-report.ts +247 -0
  416. package/src/market-state/local-automation-service.ts +162 -0
  417. package/src/market-state/notification-delivery.ts +158 -0
  418. package/src/market-state/resolve-for-mutation.ts +24 -0
  419. package/src/market-state/resolve.ts +112 -0
  420. package/src/market-state/service.ts +2344 -0
  421. package/src/memory/index.ts +7 -7
  422. package/src/memory/manager.ts +57 -26
  423. package/src/memory/retrieval.ts +8 -7
  424. package/src/memory/sqlite.ts +407 -6
  425. package/src/memory/storage.ts +8 -17
  426. package/src/memory/tool-defaults.ts +60 -39
  427. package/src/memory/types.ts +7 -3
  428. package/src/monitor.ts +121 -0
  429. package/src/onboarding/connect.ts +10 -33
  430. package/src/onboarding/credential-interceptor.ts +3 -15
  431. package/src/onboarding/degradation-accumulator.ts +1 -3
  432. package/src/onboarding/providers.ts +9 -40
  433. package/src/onboarding/state.ts +4 -15
  434. package/src/onboarding/tool-helpers.ts +2 -9
  435. package/src/onboarding/tool-tags.ts +6 -6
  436. package/src/onboarding/validation.ts +14 -20
  437. package/src/pi/opencandle-extension.ts +795 -120
  438. package/src/pi/session.ts +7 -5
  439. package/src/pi/setup.ts +61 -33
  440. package/src/pi/tool-adapter.ts +5 -2
  441. package/src/prompts/context-builder.ts +143 -21
  442. package/src/prompts/disclaimer.ts +1 -1
  443. package/src/prompts/policy-cards.ts +220 -0
  444. package/src/prompts/sections.ts +4 -4
  445. package/src/prompts/symbol-preflight.ts +80 -0
  446. package/src/prompts/workflow-prompts.ts +231 -28
  447. package/src/providers/alpha-vantage.ts +82 -40
  448. package/src/providers/coingecko.ts +2 -5
  449. package/src/providers/errors.ts +9 -0
  450. package/src/providers/exa-search.ts +24 -22
  451. package/src/providers/fear-greed.ts +1 -1
  452. package/src/providers/finnhub.ts +7 -6
  453. package/src/providers/fred.ts +3 -3
  454. package/src/providers/index.ts +14 -6
  455. package/src/providers/reddit.ts +17 -6
  456. package/src/providers/sec-edgar.ts +235 -5
  457. package/src/providers/tradingview.ts +399 -0
  458. package/src/providers/twitter.ts +6 -8
  459. package/src/providers/web-search.ts +30 -20
  460. package/src/providers/with-fallback.ts +8 -7
  461. package/src/providers/wrap-provider.ts +15 -10
  462. package/src/providers/yahoo-finance.ts +292 -20
  463. package/src/routing/classify-intent.ts +186 -4
  464. package/src/routing/defaults.ts +4 -4
  465. package/src/routing/entity-extractor.ts +428 -28
  466. package/src/routing/fund-symbols.ts +58 -0
  467. package/src/routing/horizon.ts +7 -0
  468. package/src/routing/index.ts +60 -16
  469. package/src/routing/legacy-rule-router.ts +13 -0
  470. package/src/routing/planning.ts +823 -0
  471. package/src/routing/route-manifest.ts +309 -0
  472. package/src/routing/router-llm-client.ts +4 -4
  473. package/src/routing/router-prompt.ts +52 -52
  474. package/src/routing/router-types.ts +18 -0
  475. package/src/routing/router.ts +717 -20
  476. package/src/routing/slot-resolver.ts +75 -14
  477. package/src/routing/symbol-disambiguator.ts +72 -0
  478. package/src/routing/turn-context.ts +108 -0
  479. package/src/routing/types.ts +15 -1
  480. package/src/runtime/answer-contracts.ts +672 -0
  481. package/src/runtime/artifact-contracts.ts +77 -0
  482. package/src/runtime/planning-evidence.ts +682 -0
  483. package/src/runtime/prompt-step.ts +1 -16
  484. package/src/runtime/run-context.ts +12 -2
  485. package/src/runtime/session-coordinator.ts +297 -56
  486. package/src/runtime/session-title.ts +60 -0
  487. package/src/runtime/tool-defaults-wrapper.ts +1 -3
  488. package/src/runtime/validation.ts +1 -4
  489. package/src/runtime/workflow-events.ts +7 -7
  490. package/src/runtime/workflow-runner.ts +5 -11
  491. package/src/sentiment/adapters/finnhub.ts +7 -2
  492. package/src/sentiment/adapters/reddit.ts +2 -2
  493. package/src/sentiment/adapters/twitter.ts +1 -1
  494. package/src/sentiment/adapters/web.ts +1 -1
  495. package/src/sentiment/index.ts +16 -26
  496. package/src/sentiment/keywords.ts +26 -4
  497. package/src/sentiment/pipeline.ts +15 -4
  498. package/src/sentiment/scorer.ts +1 -1
  499. package/src/sentiment/store.ts +2 -2
  500. package/src/sentiment/trends.ts +9 -3
  501. package/src/sentiment/types.ts +5 -4
  502. package/src/system-prompt.ts +7 -3
  503. package/src/tool-kit.ts +10 -9
  504. package/src/tools/fundamentals/company-overview.ts +20 -10
  505. package/src/tools/fundamentals/comps.ts +69 -56
  506. package/src/tools/fundamentals/dcf.ts +146 -96
  507. package/src/tools/fundamentals/earnings.ts +17 -7
  508. package/src/tools/fundamentals/financials.ts +17 -8
  509. package/src/tools/fundamentals/sec-filings.ts +52 -8
  510. package/src/tools/index.ts +53 -38
  511. package/src/tools/interaction/ask-user.ts +22 -10
  512. package/src/tools/interaction/twitter-login.ts +17 -5
  513. package/src/tools/macro/fear-greed.ts +2 -2
  514. package/src/tools/macro/fred-data.ts +80 -42
  515. package/src/tools/market/crypto-history.ts +25 -4
  516. package/src/tools/market/crypto-price.ts +7 -7
  517. package/src/tools/market/screen-stocks.ts +279 -0
  518. package/src/tools/market/search-ticker.ts +219 -18
  519. package/src/tools/market/stock-history.ts +38 -13
  520. package/src/tools/market/stock-quote.ts +11 -8
  521. package/src/tools/options/greeks.ts +5 -6
  522. package/src/tools/options/option-chain.ts +47 -18
  523. package/src/tools/portfolio/alerts.ts +457 -0
  524. package/src/tools/portfolio/correlation.ts +48 -21
  525. package/src/tools/portfolio/daily-report.ts +101 -0
  526. package/src/tools/portfolio/holdings-overlap.ts +139 -0
  527. package/src/tools/portfolio/notifications.ts +45 -0
  528. package/src/tools/portfolio/predictions.ts +407 -107
  529. package/src/tools/portfolio/risk-analysis.ts +47 -8
  530. package/src/tools/portfolio/tracker.ts +271 -110
  531. package/src/tools/portfolio/watchlist.ts +251 -116
  532. package/src/tools/sentiment/reddit-sentiment.ts +51 -25
  533. package/src/tools/sentiment/sentiment-summary.ts +116 -35
  534. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  535. package/src/tools/sentiment/twitter-sentiment.ts +23 -16
  536. package/src/tools/sentiment/untrusted-text.ts +21 -0
  537. package/src/tools/sentiment/web-search.ts +52 -16
  538. package/src/tools/sentiment/web-sentiment.ts +27 -11
  539. package/src/tools/technical/backtest.ts +78 -47
  540. package/src/tools/technical/indicators.ts +40 -17
  541. package/src/types/index.ts +8 -3
  542. package/src/types/market.ts +1 -0
  543. package/src/types/options.ts +17 -0
  544. package/src/types/portfolio.ts +46 -4
  545. package/src/types/sentiment.ts +2 -2
  546. package/src/workflows/compare-assets.ts +67 -19
  547. package/src/workflows/index.ts +3 -4
  548. package/src/workflows/options-screener.ts +98 -22
  549. package/src/workflows/portfolio-builder.ts +40 -29
  550. package/dist/runtime/index.d.ts +0 -16
  551. package/dist/runtime/index.js +0 -10
  552. package/dist/runtime/index.js.map +0 -1
  553. package/dist/runtime/provider-ids.d.ts +0 -14
  554. package/dist/runtime/provider-ids.js +0 -14
  555. package/dist/runtime/provider-ids.js.map +0 -1
  556. package/dist/workflows/types.d.ts +0 -4
  557. package/dist/workflows/types.js +0 -2
  558. package/dist/workflows/types.js.map +0 -1
  559. package/gui/web/dist/assets/CatalogOverlay-D1ImSJTe.js +0 -1
  560. package/gui/web/dist/assets/index-DBrWq43L.css +0 -1
  561. package/gui/web/dist/assets/index-RflHaj0y.js +0 -67
  562. package/src/runtime/index.ts +0 -55
  563. package/src/runtime/provider-ids.ts +0 -15
  564. package/src/workflows/types.ts +0 -4
@@ -8,8 +8,8 @@
8
8
  <link rel="preconnect" href="https://fonts.googleapis.com" />
9
9
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
10
10
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet" />
11
- <script type="module" crossorigin src="/assets/index-RflHaj0y.js"></script>
12
- <link rel="stylesheet" crossorigin href="/assets/index-DBrWq43L.css">
11
+ <script type="module" crossorigin src="/assets/index-CveNgtDg.js"></script>
12
+ <link rel="stylesheet" crossorigin href="/assets/index-2KZtKBmu.css">
13
13
  </head>
14
14
  <body>
15
15
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencandle",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Financial trading & investing agent",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/Kahtaf/OpenCandle#readme",
@@ -13,7 +13,8 @@
13
13
  },
14
14
  "type": "module",
15
15
  "workspaces": [
16
- "gui/*"
16
+ "gui/server",
17
+ "gui/web"
17
18
  ],
18
19
  "bin": {
19
20
  "opencandle": "dist/cli.js"
@@ -85,8 +86,12 @@
85
86
  "prestart": "npm run check:node",
86
87
  "start": "tsx src/cli.ts",
87
88
  "gui:web:build": "npm --workspace @opencandle/gui-web run build",
89
+ "docs:site:build": "node website/build.mjs",
90
+ "docs:site:serve": "node website/serve.mjs",
88
91
  "gui": "tsx gui/server/server.ts",
89
92
  "gui:dev": "tsx gui/server/server.ts",
93
+ "lint": "biome check .",
94
+ "format": "biome check --write .",
90
95
  "pretest": "npm run check:node",
91
96
  "test": "vitest run",
92
97
  "test:watch": "vitest",
@@ -101,6 +106,10 @@
101
106
  "test:evals": "vitest run --config vitest.config.evals.ts",
102
107
  "eval:router-live": "tsx tests/scripts/run-live-router-eval.ts",
103
108
  "test:evals:usually": "EVAL_TIER=usually vitest run --config vitest.config.evals.ts",
109
+ "test:evals:product": "tsx tests/scripts/run-product-evals.ts",
110
+ "test:evals:competitive": "tsx tests/scripts/run-competitive-finance-eval.ts",
111
+ "eval:competitive:analyze": "tsx tests/scripts/analyze-competitive-finance-report.ts",
112
+ "review:pr": ".agents/skills/autoreview/scripts/autoreview --mode branch --prompt-file .agents/skills/autoreview/references/opencandle-review.md --parallel-tests \"npx tsc --noEmit && npx vitest run\"",
104
113
  "version:patch": "npm version patch --no-git-tag-version",
105
114
  "version:minor": "npm version minor --no-git-tag-version",
106
115
  "version:major": "npm version major --no-git-tag-version",
@@ -115,29 +124,30 @@
115
124
  "node": "^20.19.0 || ^22.12.0 || >=24.0.0 <27"
116
125
  },
117
126
  "dependencies": {
118
- "@earendil-works/pi-agent-core": "^0.74.0",
119
- "@earendil-works/pi-ai": "^0.74.0",
120
- "@earendil-works/pi-coding-agent": "^0.74.0",
127
+ "@earendil-works/pi-agent-core": "^0.75.5",
128
+ "@earendil-works/pi-ai": "^0.75.5",
129
+ "@earendil-works/pi-coding-agent": "^0.75.5",
121
130
  "@the-convocation/twitter-scraper": "^0.22.3",
122
131
  "better-sqlite3": "^12.10.0",
123
132
  "camoufox-js": "^0.10.2",
124
133
  "duck-duck-scrape": "^2.2.7",
125
134
  "playwright-core": "^1.60.0",
126
- "tsx": "^4.22.0"
135
+ "tsx": "^4.22.3"
127
136
  },
128
137
  "peerDependencies": {
129
138
  "@sinclair/typebox": "*"
130
139
  },
131
140
  "devDependencies": {
132
- "@mariozechner/pi-agent-core": "^0.73.1",
133
- "@mariozechner/pi-ai": "^0.73.1",
134
- "@mariozechner/pi-coding-agent": "^0.73.1",
141
+ "@agentclientprotocol/claude-agent-acp": "^0.37.0",
142
+ "@biomejs/biome": "2.5.0",
135
143
  "@sinclair/typebox": "^0.34.0",
136
144
  "@types/better-sqlite3": "^7.6.13",
137
145
  "@types/node": "^22.19.19",
146
+ "@zed-industries/codex-acp": "^0.15.0",
147
+ "acpx": "^0.10.0",
138
148
  "typescript": "^6.0.3",
139
- "vite": "^8.0.11",
140
- "vitest": "^4.1.6",
141
- "vitest-evals": "^0.8.0"
149
+ "vite": "^8.0.14",
150
+ "vitest": "^4.1.7",
151
+ "vitest-evals": "^0.11.0"
142
152
  }
143
153
  }
@@ -1,31 +1,19 @@
1
- import type { AnalystOutput, AnalystSignal, DebateSide, DebateOutput } from "../runtime/workflow-types.js";
2
1
  import type { EvidenceRecord } from "../runtime/evidence.js";
2
+ import type {
3
+ AnalystOutput,
4
+ AnalystSignal,
5
+ DebateOutput,
6
+ DebateSide,
7
+ } from "../runtime/workflow-types.js";
3
8
 
4
9
  /** All analyst roles. */
5
- export type AnalystRole =
6
- | "valuation"
7
- | "momentum"
8
- | "options"
9
- | "contrarian"
10
- | "risk";
11
-
12
- /** Evidence fields expected per analyst role. */
13
- export const ROLE_EXPECTED_EVIDENCE: Record<AnalystRole, string[]> = {
14
- valuation: ["P/E Ratio", "Forward P/E", "EPS", "Intrinsic Value", "Revenue Growth"],
15
- momentum: ["RSI", "MACD", "SMA 50", "SMA 200", "Volume Trend"],
16
- options: ["Put/Call Ratio", "IV Level", "Unusual Volume", "Max Pain"],
17
- contrarian: ["Fear & Greed Index", "Reddit Sentiment", "Sentiment Score"],
18
- risk: ["Annualized Volatility", "Sharpe Ratio", "Max Drawdown", "VaR 95%", "Position Size"],
19
- };
10
+ export type AnalystRole = "valuation" | "momentum" | "options" | "contrarian" | "risk";
20
11
 
21
12
  /**
22
13
  * Parse an LLM response into a structured AnalystOutput.
23
14
  * Falls back to raw text if parsing fails.
24
15
  */
25
- export function parseAnalystOutput(
26
- role: string,
27
- responseText: string,
28
- ): AnalystOutput {
16
+ export function parseAnalystOutput(role: string, responseText: string): AnalystOutput {
29
17
  const signal = extractSignal(responseText);
30
18
  const conviction = extractConviction(responseText);
31
19
  const thesis = extractThesis(responseText);
@@ -91,9 +79,8 @@ export function tallyVotes(outputs: AnalystOutput[]): {
91
79
  weightedSum += signalValue * output.conviction;
92
80
  }
93
81
 
94
- const weightedConviction = totalWeight > 0
95
- ? Math.round((totalWeight / outputs.length) * 10) / 10
96
- : 0;
82
+ const weightedConviction =
83
+ totalWeight > 0 ? Math.round((totalWeight / outputs.length) * 10) / 10 : 0;
97
84
 
98
85
  let verdict: AnalystSignal;
99
86
  if (weightedSum > 0) verdict = "BUY";
@@ -1,9 +1,7 @@
1
- export type AnalystRole =
2
- | "valuation"
3
- | "momentum"
4
- | "options"
5
- | "contrarian"
6
- | "risk";
1
+ import type { WorkflowDefinition } from "../runtime/prompt-step.js";
2
+ import { promptStep } from "../runtime/prompt-step.js";
3
+
4
+ export type AnalystRole = "valuation" | "momentum" | "options" | "contrarian" | "risk";
7
5
 
8
6
  const SYMBOL_CAPTURE = "(\\$?[A-Za-z]{1,5}(?:[./-][A-Za-z]{1,2})?)";
9
7
  const NORMALIZED_SYMBOL_PATTERN = /^[A-Z]{1,5}(?:[./-][A-Z]{1,2})?$/;
@@ -173,33 +171,10 @@ export interface ComprehensiveAnalysisOptions {
173
171
  debate?: boolean;
174
172
  }
175
173
 
176
- export function getComprehensiveAnalysisPrompts(symbol: string, options?: ComprehensiveAnalysisOptions): string[] {
177
- const debate = options?.debate ?? true;
178
- const roles: AnalystRole[] = ["valuation", "momentum", "options", "contrarian", "risk"];
179
- const prompts = [getInitialAnalysisPrompt(symbol)];
180
-
181
- for (const role of roles) {
182
- prompts.push(ANALYST_PROMPTS[role](symbol));
183
- }
184
-
185
- if (debate) {
186
- prompts.push(buildBullPrompt(symbol));
187
- prompts.push(buildBearPrompt(symbol));
188
- prompts.push(buildRebuttalPrompt(symbol));
189
- prompts.push(buildSynthesisPrompt(symbol));
190
- prompts.push(VALIDATION_PROMPT_DEBATE(symbol));
191
- } else {
192
- prompts.push(SYNTHESIS_PROMPT_NO_DEBATE(symbol));
193
- prompts.push(VALIDATION_PROMPT_NO_DEBATE(symbol));
194
- }
195
-
196
- return prompts;
197
- }
198
-
199
- import type { WorkflowDefinition } from "../runtime/prompt-step.js";
200
- import { promptStep } from "../runtime/prompt-step.js";
201
-
202
- export function buildComprehensiveAnalysisDefinition(symbol: string, options?: ComprehensiveAnalysisOptions): WorkflowDefinition {
174
+ export function buildComprehensiveAnalysisDefinition(
175
+ symbol: string,
176
+ options?: ComprehensiveAnalysisOptions,
177
+ ): WorkflowDefinition {
203
178
  const debate = options?.debate ?? true;
204
179
  const roles: AnalystRole[] = ["valuation", "momentum", "options", "contrarian", "risk"];
205
180
 
@@ -265,16 +240,6 @@ export function buildComprehensiveAnalysisDefinition(symbol: string, options?: C
265
240
  };
266
241
  }
267
242
 
268
- export function runComprehensiveAnalysis(
269
- enqueueFollowUp: (prompt: string) => void,
270
- symbol: string,
271
- options?: ComprehensiveAnalysisOptions,
272
- ): void {
273
- for (const prompt of getComprehensiveAnalysisPrompts(symbol, options).slice(1)) {
274
- enqueueFollowUp(prompt);
275
- }
276
- }
277
-
278
243
  export function isAnalysisRequest(input: string): { match: boolean; symbol?: string } {
279
244
  const patterns = [
280
245
  new RegExp(`^analyze\\s+${SYMBOL_CAPTURE}\\s*$`, "i"),
package/src/cli.ts CHANGED
@@ -3,22 +3,21 @@ import "./infra/node-version.js";
3
3
  import { spawn } from "node:child_process";
4
4
  import { createRequire } from "node:module";
5
5
  import { dirname, resolve } from "node:path";
6
- import { parseArgs } from "node:util";
7
6
  import { fileURLToPath } from "node:url";
8
7
  import {
9
8
  AuthStorage,
10
- DefaultPackageManager,
11
- InteractiveMode,
12
- ModelRegistry,
13
- SettingsManager,
14
9
  createAgentSessionRuntime,
15
10
  createAgentSessionServices,
11
+ DefaultPackageManager,
16
12
  getAgentDir,
13
+ InteractiveMode,
17
14
  initTheme,
15
+ ModelRegistry,
16
+ SettingsManager,
18
17
  } from "@earendil-works/pi-coding-agent";
18
+ import { loadEnv } from "./config.js";
19
19
  import { createOpenCandleSession } from "./pi/session.js";
20
20
  import { continueOpenCandleSession } from "./pi/session-storage.js";
21
- import { loadEnv } from "./config.js";
22
21
 
23
22
  const require = createRequire(import.meta.url);
24
23
  const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
@@ -29,10 +28,7 @@ async function handlePackageCommand(
29
28
  agentDir: string,
30
29
  ): Promise<boolean> {
31
30
  const [command, ...rest] = args;
32
- if (
33
- !command ||
34
- !["install", "remove", "uninstall", "list", "update"].includes(command)
35
- ) {
31
+ if (!command || !["install", "remove", "uninstall", "list", "update"].includes(command)) {
36
32
  return false;
37
33
  }
38
34
 
@@ -139,16 +135,44 @@ async function handleGuiCommand(args: string[], cwd: string): Promise<boolean> {
139
135
  return true;
140
136
  }
141
137
 
138
+ async function handleMonitorCommand(args: string[], cwd: string): Promise<boolean> {
139
+ if (args[0] !== "monitor") return false;
140
+
141
+ const tsxCli = require.resolve("tsx/cli");
142
+ const monitorPath = resolve(packageRoot, "src/monitor.ts");
143
+ const child = spawn(process.execPath, [tsxCli, monitorPath, ...args.slice(1)], {
144
+ cwd,
145
+ env: process.env,
146
+ stdio: "inherit",
147
+ });
148
+
149
+ const exitCode = await new Promise<number>((resolveExit) => {
150
+ child.on("close", (code, signal) => {
151
+ if (signal) {
152
+ resolveExit(1);
153
+ } else {
154
+ resolveExit(code ?? 0);
155
+ }
156
+ });
157
+ });
158
+ process.exitCode = exitCode;
159
+ return true;
160
+ }
161
+
142
162
  async function main(): Promise<void> {
143
- const { positionals } = parseArgs({ allowPositionals: true, strict: false });
163
+ const rawArgs = process.argv.slice(2);
144
164
  const cwd = process.cwd();
145
165
  const agentDir = getAgentDir();
146
166
 
147
- if (await handleGuiCommand(positionals, cwd)) {
167
+ if (await handleGuiCommand(rawArgs, cwd)) {
168
+ return;
169
+ }
170
+
171
+ if (await handleMonitorCommand(rawArgs, cwd)) {
148
172
  return;
149
173
  }
150
174
 
151
- if (await handlePackageCommand(positionals, cwd, agentDir)) {
175
+ if (await handlePackageCommand(rawArgs, cwd, agentDir)) {
152
176
  return;
153
177
  }
154
178
 
package/src/config.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { chmodSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { ensureParentDir, getConfigPath } from "./infra/opencandle-paths.js";
3
+ import type { PlanningBehaviorMode, TaskFamily } from "./routing/planning.js";
3
4
 
4
5
  export interface SentimentConfig {
5
6
  retentionDays: number;
@@ -9,6 +10,8 @@ export interface SentimentConfig {
9
10
  }
10
11
 
11
12
  export type RouterMode = "rules" | "llm";
13
+ export type ToolScopeMode = "observe" | "enforce";
14
+ export type PlanningMigrationStatuses = Partial<Record<TaskFamily, PlanningBehaviorMode>>;
12
15
 
13
16
  export interface Config {
14
17
  alphaVantageApiKey?: string;
@@ -19,11 +22,24 @@ export interface Config {
19
22
  /** Enable adversarial bull/bear debate in comprehensive analysis. Default: true. */
20
23
  debate?: boolean;
21
24
  /**
22
- * Intent-router rollout flag. `"rules"` (default) runs the legacy regex
23
- * `classifyIntent` + `extractPreferences` path. `"llm"` runs the LLM router
24
- * ahead of prompt assembly. Controlled by `OPENCANDLE_ROUTER_MODE`.
25
+ * Intent-router mode. `"rules"` (default) uses the deterministic rule
26
+ * router (`classifyIntent` + `extractPreferences`). `"llm"` opts into the
27
+ * LLM router ahead of prompt assembly. Controlled by
28
+ * `OPENCANDLE_ROUTER_MODE`.
25
29
  */
26
30
  routerMode: RouterMode;
31
+ /**
32
+ * Route-selected tool scope mode. `"observe"` (default) records selected
33
+ * bundles and active-tool candidates. `"enforce"` applies Pi active tools
34
+ * for the turn via `pi.setActiveTools`.
35
+ */
36
+ toolScopeMode: ToolScopeMode;
37
+ /**
38
+ * Per-task planning behavior rollback/activation overrides. Controlled by
39
+ * `OPENCANDLE_PLANNING_MIGRATION_STATUSES`, e.g.
40
+ * `asset_compare=dual_run,single_asset_decision=observe_only`.
41
+ */
42
+ planningMigrationStatuses?: PlanningMigrationStatuses;
27
43
  sentiment?: SentimentConfig;
28
44
  }
29
45
 
@@ -84,6 +100,30 @@ const SENTIMENT_DEFAULTS: SentimentConfig = {
84
100
  divergenceThreshold: 0.4,
85
101
  };
86
102
 
103
+ const PLANNING_TASK_FAMILIES = [
104
+ "single_asset_decision",
105
+ "asset_compare",
106
+ "portfolio_build",
107
+ "portfolio_review",
108
+ "macro_allocation_review",
109
+ "options_strategy",
110
+ "current_event_explanation",
111
+ "ticker_disambiguation",
112
+ "filing_thesis_review",
113
+ "sentiment_snapshot",
114
+ "concept_explainer",
115
+ "retail_finance_tradeoff",
116
+ "stateful_tracking_update",
117
+ "backtest_review",
118
+ "general_fallback",
119
+ ] as const satisfies readonly TaskFamily[];
120
+
121
+ const PLANNING_BEHAVIOR_MODES = [
122
+ "observe_only",
123
+ "dual_run",
124
+ "replacement_active",
125
+ ] as const satisfies readonly PlanningBehaviorMode[];
126
+
87
127
  function resolveRouterMode(): RouterMode {
88
128
  const raw = process.env.OPENCANDLE_ROUTER_MODE;
89
129
  if (raw === undefined || raw === "") return "rules";
@@ -93,6 +133,48 @@ function resolveRouterMode(): RouterMode {
93
133
  );
94
134
  }
95
135
 
136
+ function resolveToolScopeMode(): ToolScopeMode {
137
+ const raw = process.env.OPENCANDLE_TOOL_SCOPE_MODE;
138
+ if (raw === undefined || raw === "") return "observe";
139
+ if (raw === "observe" || raw === "enforce") return raw;
140
+ throw new Error(
141
+ `Invalid OPENCANDLE_TOOL_SCOPE_MODE="${raw}". Allowed values: "observe" (default) or "enforce".`,
142
+ );
143
+ }
144
+
145
+ function resolvePlanningMigrationStatuses(): PlanningMigrationStatuses | undefined {
146
+ const raw = process.env.OPENCANDLE_PLANNING_MIGRATION_STATUSES;
147
+ if (raw === undefined || raw.trim() === "") return undefined;
148
+
149
+ const statuses: PlanningMigrationStatuses = {};
150
+ for (const entry of raw.split(",")) {
151
+ const trimmed = entry.trim();
152
+ if (!trimmed) continue;
153
+
154
+ const parts = trimmed.split("=");
155
+ const taskFamily = parts[0]?.trim();
156
+ const behaviorMode = parts[1]?.trim();
157
+ if (
158
+ parts.length !== 2 ||
159
+ !isPlanningTaskFamily(taskFamily) ||
160
+ !isPlanningBehaviorMode(behaviorMode)
161
+ ) {
162
+ throw new Error(`Invalid OPENCANDLE_PLANNING_MIGRATION_STATUSES entry "${trimmed}".`);
163
+ }
164
+ statuses[taskFamily] = behaviorMode;
165
+ }
166
+
167
+ return Object.keys(statuses).length > 0 ? statuses : undefined;
168
+ }
169
+
170
+ function isPlanningTaskFamily(value: string | undefined): value is TaskFamily {
171
+ return PLANNING_TASK_FAMILIES.includes(value as TaskFamily);
172
+ }
173
+
174
+ function isPlanningBehaviorMode(value: string | undefined): value is PlanningBehaviorMode {
175
+ return PLANNING_BEHAVIOR_MODES.includes(value as PlanningBehaviorMode);
176
+ }
177
+
96
178
  function resolveConfig(fileConfig: OpenCandleFileConfig): Config {
97
179
  const debateEnv = process.env.OPENCANDLE_DEBATE;
98
180
  const fileSentiment = fileConfig.sentiment;
@@ -103,13 +185,19 @@ function resolveConfig(fileConfig: OpenCandleFileConfig): Config {
103
185
  braveApiKey: process.env.BRAVE_API_KEY ?? fileConfig.providers?.brave?.apiKey,
104
186
  exaApiKey: process.env.EXA_API_KEY ?? fileConfig.providers?.exa?.apiKey,
105
187
  finnhubApiKey: process.env.FINNHUB_API_KEY ?? fileConfig.providers?.finnhub?.apiKey,
106
- debate: debateEnv !== undefined ? debateEnv !== "false" && debateEnv !== "0" : fileConfig.debate ?? true,
188
+ debate:
189
+ debateEnv !== undefined
190
+ ? debateEnv !== "false" && debateEnv !== "0"
191
+ : (fileConfig.debate ?? true),
107
192
  routerMode: resolveRouterMode(),
193
+ toolScopeMode: resolveToolScopeMode(),
194
+ planningMigrationStatuses: resolvePlanningMigrationStatuses(),
108
195
  sentiment: {
109
196
  retentionDays: fileSentiment?.retentionDays ?? SENTIMENT_DEFAULTS.retentionDays,
110
197
  defaultSubreddits: fileSentiment?.defaultSubreddits ?? SENTIMENT_DEFAULTS.defaultSubreddits,
111
198
  commentsPerPost: fileSentiment?.commentsPerPost ?? SENTIMENT_DEFAULTS.commentsPerPost,
112
- divergenceThreshold: fileSentiment?.divergenceThreshold ?? SENTIMENT_DEFAULTS.divergenceThreshold,
199
+ divergenceThreshold:
200
+ fileSentiment?.divergenceThreshold ?? SENTIMENT_DEFAULTS.divergenceThreshold,
113
201
  },
114
202
  };
115
203
  }
@@ -138,7 +226,11 @@ export function loadFileConfig(path = getConfigPath()): OpenCandleFileConfig {
138
226
 
139
227
  export function saveFileConfig(config: OpenCandleFileConfig, path = getConfigPath()): void {
140
228
  ensureParentDir(path);
141
- writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, "utf-8");
229
+ writeFileSync(path, `${JSON.stringify(config, null, 2)}\n`, {
230
+ encoding: "utf-8",
231
+ mode: 0o600,
232
+ });
233
+ if (process.platform !== "win32") chmodSync(path, 0o600);
142
234
  }
143
235
 
144
236
  export function loadConfig(): Config {
package/src/index.ts CHANGED
@@ -1,5 +1,5 @@
1
- export { createOpenCandleSession, type CreateOpenCandleSessionOptions } from "./pi/session.js";
2
1
  export { default as openCandleExtension } from "./pi/opencandle-extension.js";
2
+ export { type CreateOpenCandleSessionOptions, createOpenCandleSession } from "./pi/session.js";
3
3
  export { agentToolToPiTool, getOpenCandleToolDefinitions } from "./pi/tool-adapter.js";
4
4
  export { registerTools } from "./tool-kit.js";
5
5
  export { getAllTools } from "./tools/index.js";
@@ -38,7 +38,9 @@ async function ensureBrowser(): Promise<Page> {
38
38
 
39
39
  async function withPage<T>(fn: (p: Page) => Promise<T>): Promise<T> {
40
40
  let resolve!: () => void;
41
- const next = new Promise<void>((r) => { resolve = r; });
41
+ const next = new Promise<void>((r) => {
42
+ resolve = r;
43
+ });
42
44
  const prev = pageQueue;
43
45
  pageQueue = next;
44
46
  await prev;
@@ -65,7 +67,7 @@ export const StealthBrowser = {
65
67
  * Fetch JSON from a URL using the browser's session (cookies, TLS fingerprint).
66
68
  * Useful for APIs that block Node.js fetch but allow real browsers.
67
69
  */
68
- async fetchJson<T>(url: string, options?: { cookies?: string }): Promise<T> {
70
+ async fetchJson<T>(url: string): Promise<T> {
69
71
  return withPage(async (p) => {
70
72
  const result = await p.evaluate(async (fetchUrl: string) => {
71
73
  const res = await fetch(fetchUrl, { credentials: "include" });
@@ -1,3 +1,5 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
1
3
  interface CacheEntry<T> {
2
4
  value: T;
3
5
  expiresAt: number;
@@ -10,10 +12,26 @@ export interface StaleResult<T> {
10
12
  cachedAt: number;
11
13
  }
12
14
 
15
+ interface StaleMetadata {
16
+ stale: boolean;
17
+ cachedAt: number;
18
+ }
19
+
20
+ const staleMetadataStorage = new AsyncLocalStorage<StaleMetadata>();
21
+
22
+ export async function runWithStaleMetadata<T>(
23
+ fn: () => Promise<T>,
24
+ ): Promise<{ value: T; stale?: { cachedAt: number } }> {
25
+ const metadata: StaleMetadata = { stale: false, cachedAt: 0 };
26
+ const value = await staleMetadataStorage.run(metadata, fn);
27
+ return {
28
+ value,
29
+ stale: metadata.stale ? { cachedAt: metadata.cachedAt } : undefined,
30
+ };
31
+ }
32
+
13
33
  export class Cache {
14
34
  private store = new Map<string, CacheEntry<unknown>>();
15
- private lastStaleHit = false;
16
- private lastStaleCachedAt = 0;
17
35
 
18
36
  get<T>(key: string): T | undefined {
19
37
  const entry = this.store.get(key);
@@ -36,23 +54,14 @@ export class Cache {
36
54
  return undefined;
37
55
  }
38
56
 
39
- this.lastStaleHit = true;
40
- this.lastStaleCachedAt = entry.cachedAt;
57
+ const metadata = staleMetadataStorage.getStore();
58
+ if (metadata) {
59
+ metadata.stale = true;
60
+ metadata.cachedAt = entry.cachedAt;
61
+ }
41
62
  return { value: entry.value as T, stale: true, cachedAt: entry.cachedAt };
42
63
  }
43
64
 
44
- /**
45
- * Consume the stale flag set by the last getStale() hit.
46
- * Returns { stale: true, cachedAt } if the last getStale() found data,
47
- * then resets the flag. Used by wrapProvider to propagate stale metadata.
48
- */
49
- consumeStaleFlag(): { stale: boolean; cachedAt: number } {
50
- const result = { stale: this.lastStaleHit, cachedAt: this.lastStaleCachedAt };
51
- this.lastStaleHit = false;
52
- this.lastStaleCachedAt = 0;
53
- return result;
54
- }
55
-
56
65
  set<T>(key: string, value: T, ttlMs: number): void {
57
66
  this.store.set(key, { value, expiresAt: Date.now() + ttlMs, cachedAt: Date.now() });
58
67
  }
@@ -79,25 +88,27 @@ export const cache = new Cache();
79
88
 
80
89
  // Default TTLs
81
90
  export const TTL = {
82
- QUOTE: 60_000, // 1 minute
83
- HISTORY: 3_600_000, // 1 hour
91
+ QUOTE: 60_000, // 1 minute
92
+ HISTORY: 3_600_000, // 1 hour
84
93
  FUNDAMENTALS: 86_400_000, // 24 hours
85
- MACRO: 3_600_000, // 1 hour
86
- SENTIMENT: 300_000, // 5 minutes
94
+ MACRO: 3_600_000, // 1 hour
95
+ SENTIMENT: 300_000, // 5 minutes
87
96
  OPTIONS_CHAIN: 120_000, // 2 minutes
88
- CRUMB: 900_000, // 15 minutes
89
- WEB_SEARCH: 300_000, // 5 minutes
97
+ SCREENER: 60_000, // 1 minute
98
+ CRUMB: 900_000, // 15 minutes
99
+ WEB_SEARCH: 300_000, // 5 minutes
90
100
  FINNHUB_NEWS: 300_000, // 5 minutes
91
101
  } as const;
92
102
 
93
103
  // Stale limits — how long past TTL expiry a cached value is still useful as fallback
94
104
  export const STALE_LIMIT = {
95
- QUOTE: 15 * 60_000, // 15 minutes
96
- HISTORY: 24 * 3_600_000, // 24 hours
97
- FUNDAMENTALS: 7 * 86_400_000, // 7 days
98
- MACRO: 24 * 3_600_000, // 24 hours
99
- SENTIMENT: 3_600_000, // 1 hour
100
- OPTIONS_CHAIN: 30 * 60_000, // 30 minutes
101
- WEB_SEARCH: 3_600_000, // 1 hour
102
- FINNHUB_NEWS: 3_600_000, // 1 hour
105
+ QUOTE: 15 * 60_000, // 15 minutes
106
+ HISTORY: 24 * 3_600_000, // 24 hours
107
+ FUNDAMENTALS: 7 * 86_400_000, // 7 days
108
+ MACRO: 24 * 3_600_000, // 24 hours
109
+ SENTIMENT: 3_600_000, // 1 hour
110
+ OPTIONS_CHAIN: 30 * 60_000, // 30 minutes
111
+ SCREENER: 15 * 60_000, // 15 minutes
112
+ WEB_SEARCH: 3_600_000, // 1 hour
113
+ FINNHUB_NEWS: 3_600_000, // 1 hour
103
114
  } as const;