opencandle 0.5.0 → 0.7.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 (574) hide show
  1. package/README.md +170 -186
  2. package/dist/analysts/contracts.d.ts +1 -3
  3. package/dist/analysts/contracts.js +1 -11
  4. package/dist/analysts/contracts.js.map +1 -1
  5. package/dist/analysts/orchestrator.d.ts +1 -3
  6. package/dist/analysts/orchestrator.js +1 -26
  7. package/dist/analysts/orchestrator.js.map +1 -1
  8. package/dist/cli.js +66 -7
  9. package/dist/cli.js.map +1 -1
  10. package/dist/config.d.ts +13 -3
  11. package/dist/config.js +25 -5
  12. package/dist/config.js.map +1 -1
  13. package/dist/index.d.ts +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/infra/cache.d.ts +8 -11
  17. package/dist/infra/cache.js +17 -15
  18. package/dist/infra/cache.js.map +1 -1
  19. package/dist/infra/http-client.d.ts +4 -1
  20. package/dist/infra/http-client.js +59 -6
  21. package/dist/infra/http-client.js.map +1 -1
  22. package/dist/infra/index.d.ts +2 -3
  23. package/dist/infra/index.js +2 -3
  24. package/dist/infra/index.js.map +1 -1
  25. package/dist/infra/native-dependencies.js +2 -2
  26. package/dist/infra/native-dependencies.js.map +1 -1
  27. package/dist/infra/node-version.js.map +1 -1
  28. package/dist/infra/opencandle-paths.d.ts +0 -3
  29. package/dist/infra/opencandle-paths.js +4 -11
  30. package/dist/infra/opencandle-paths.js.map +1 -1
  31. package/dist/infra/rate-limiter.js +12 -9
  32. package/dist/infra/rate-limiter.js.map +1 -1
  33. package/dist/market-state/alert-conditions.d.ts +34 -0
  34. package/dist/market-state/alert-conditions.js +23 -0
  35. package/dist/market-state/alert-conditions.js.map +1 -0
  36. package/dist/market-state/alert-runner.d.ts +55 -0
  37. package/dist/market-state/alert-runner.js +634 -0
  38. package/dist/market-state/alert-runner.js.map +1 -0
  39. package/dist/market-state/daily-report.d.ts +26 -0
  40. package/dist/market-state/daily-report.js +179 -0
  41. package/dist/market-state/daily-report.js.map +1 -0
  42. package/dist/market-state/local-automation-service.d.ts +25 -0
  43. package/dist/market-state/local-automation-service.js +119 -0
  44. package/dist/market-state/local-automation-service.js.map +1 -0
  45. package/dist/market-state/notification-delivery.d.ts +14 -0
  46. package/dist/market-state/notification-delivery.js +139 -0
  47. package/dist/market-state/notification-delivery.js.map +1 -0
  48. package/dist/market-state/resolve-for-mutation.d.ts +10 -0
  49. package/dist/market-state/resolve-for-mutation.js +15 -0
  50. package/dist/market-state/resolve-for-mutation.js.map +1 -0
  51. package/dist/market-state/resolve.d.ts +14 -0
  52. package/dist/market-state/resolve.js +89 -0
  53. package/dist/market-state/resolve.js.map +1 -0
  54. package/dist/market-state/service.d.ts +527 -0
  55. package/dist/market-state/service.js +1099 -0
  56. package/dist/market-state/service.js.map +1 -0
  57. package/dist/memory/index.d.ts +7 -7
  58. package/dist/memory/index.js +6 -6
  59. package/dist/memory/index.js.map +1 -1
  60. package/dist/memory/manager.js +11 -11
  61. package/dist/memory/manager.js.map +1 -1
  62. package/dist/memory/retrieval.js +7 -4
  63. package/dist/memory/retrieval.js.map +1 -1
  64. package/dist/memory/sqlite.js +385 -3
  65. package/dist/memory/sqlite.js.map +1 -1
  66. package/dist/memory/storage.js +1 -2
  67. package/dist/memory/storage.js.map +1 -1
  68. package/dist/memory/tool-defaults.js +64 -28
  69. package/dist/memory/tool-defaults.js.map +1 -1
  70. package/dist/memory/types.js.map +1 -1
  71. package/dist/monitor.d.ts +2 -0
  72. package/dist/monitor.js +104 -0
  73. package/dist/monitor.js.map +1 -0
  74. package/dist/onboarding/connect.d.ts +2 -2
  75. package/dist/onboarding/connect.js +13 -8
  76. package/dist/onboarding/connect.js.map +1 -1
  77. package/dist/onboarding/credential-interceptor.js +1 -1
  78. package/dist/onboarding/credential-interceptor.js.map +1 -1
  79. package/dist/onboarding/degradation-accumulator.js +1 -3
  80. package/dist/onboarding/degradation-accumulator.js.map +1 -1
  81. package/dist/onboarding/provider-status.d.ts +48 -0
  82. package/dist/onboarding/provider-status.js +285 -0
  83. package/dist/onboarding/provider-status.js.map +1 -0
  84. package/dist/onboarding/providers.d.ts +85 -8
  85. package/dist/onboarding/providers.js +83 -18
  86. package/dist/onboarding/providers.js.map +1 -1
  87. package/dist/onboarding/state.d.ts +1 -0
  88. package/dist/onboarding/state.js +5 -0
  89. package/dist/onboarding/state.js.map +1 -1
  90. package/dist/onboarding/tool-helpers.js +1 -1
  91. package/dist/onboarding/tool-helpers.js.map +1 -1
  92. package/dist/onboarding/tool-tags.d.ts +12 -1
  93. package/dist/onboarding/tool-tags.js +37 -5
  94. package/dist/onboarding/tool-tags.js.map +1 -1
  95. package/dist/onboarding/validation.d.ts +2 -2
  96. package/dist/onboarding/validation.js +1 -1
  97. package/dist/onboarding/validation.js.map +1 -1
  98. package/dist/pi/opencandle-extension.d.ts +8 -0
  99. package/dist/pi/opencandle-extension.js +502 -42
  100. package/dist/pi/opencandle-extension.js.map +1 -1
  101. package/dist/pi/session.d.ts +1 -1
  102. package/dist/pi/session.js +3 -1
  103. package/dist/pi/session.js.map +1 -1
  104. package/dist/pi/setup.js +8 -3
  105. package/dist/pi/setup.js.map +1 -1
  106. package/dist/pi/tool-adapter.d.ts +4 -1
  107. package/dist/pi/tool-adapter.js +10 -6
  108. package/dist/pi/tool-adapter.js.map +1 -1
  109. package/dist/prompts/context-builder.d.ts +1 -1
  110. package/dist/prompts/context-builder.js +20 -7
  111. package/dist/prompts/context-builder.js.map +1 -1
  112. package/dist/prompts/policy-cards.d.ts +1 -1
  113. package/dist/prompts/policy-cards.js +2 -2
  114. package/dist/prompts/policy-cards.js.map +1 -1
  115. package/dist/prompts/sections.d.ts +1 -1
  116. package/dist/prompts/symbol-preflight.d.ts +20 -0
  117. package/dist/prompts/symbol-preflight.js +49 -0
  118. package/dist/prompts/symbol-preflight.js.map +1 -0
  119. package/dist/prompts/workflow-prompts.d.ts +1 -1
  120. package/dist/prompts/workflow-prompts.js +54 -16
  121. package/dist/prompts/workflow-prompts.js.map +1 -1
  122. package/dist/providers/alpha-vantage.d.ts +1 -1
  123. package/dist/providers/alpha-vantage.js +26 -7
  124. package/dist/providers/alpha-vantage.js.map +1 -1
  125. package/dist/providers/coingecko.js +1 -1
  126. package/dist/providers/coingecko.js.map +1 -1
  127. package/dist/providers/errors.d.ts +5 -0
  128. package/dist/providers/errors.js +11 -0
  129. package/dist/providers/errors.js.map +1 -0
  130. package/dist/providers/exa-search.d.ts +2 -2
  131. package/dist/providers/exa-search.js +19 -11
  132. package/dist/providers/exa-search.js.map +1 -1
  133. package/dist/providers/external-tool-error.d.ts +10 -0
  134. package/dist/providers/external-tool-error.js +21 -0
  135. package/dist/providers/external-tool-error.js.map +1 -0
  136. package/dist/providers/fear-greed.js +1 -1
  137. package/dist/providers/fear-greed.js.map +1 -1
  138. package/dist/providers/finnhub.js +3 -5
  139. package/dist/providers/finnhub.js.map +1 -1
  140. package/dist/providers/fred.js +2 -2
  141. package/dist/providers/fred.js.map +1 -1
  142. package/dist/providers/index.d.ts +7 -6
  143. package/dist/providers/index.js +6 -5
  144. package/dist/providers/index.js.map +1 -1
  145. package/dist/providers/reddit-cli.d.ts +36 -0
  146. package/dist/providers/reddit-cli.js +201 -0
  147. package/dist/providers/reddit-cli.js.map +1 -0
  148. package/dist/providers/reddit.d.ts +1 -1
  149. package/dist/providers/reddit.js +9 -37
  150. package/dist/providers/reddit.js.map +1 -1
  151. package/dist/providers/sec-edgar.d.ts +1 -0
  152. package/dist/providers/sec-edgar.js +12 -4
  153. package/dist/providers/sec-edgar.js.map +1 -1
  154. package/dist/providers/tradingview.d.ts +47 -0
  155. package/dist/providers/tradingview.js +275 -0
  156. package/dist/providers/tradingview.js.map +1 -0
  157. package/dist/providers/twitter-cli.d.ts +40 -0
  158. package/dist/providers/twitter-cli.js +153 -0
  159. package/dist/providers/twitter-cli.js.map +1 -0
  160. package/dist/providers/twitter.d.ts +0 -8
  161. package/dist/providers/twitter.js +8 -60
  162. package/dist/providers/twitter.js.map +1 -1
  163. package/dist/providers/web-search.js +26 -12
  164. package/dist/providers/web-search.js.map +1 -1
  165. package/dist/providers/with-fallback.js +4 -2
  166. package/dist/providers/with-fallback.js.map +1 -1
  167. package/dist/providers/wrap-provider.d.ts +2 -3
  168. package/dist/providers/wrap-provider.js +44 -8
  169. package/dist/providers/wrap-provider.js.map +1 -1
  170. package/dist/providers/yahoo-finance.d.ts +1 -1
  171. package/dist/providers/yahoo-finance.js +153 -48
  172. package/dist/providers/yahoo-finance.js.map +1 -1
  173. package/dist/routing/classify-intent.d.ts +6 -0
  174. package/dist/routing/classify-intent.js +78 -7
  175. package/dist/routing/classify-intent.js.map +1 -1
  176. package/dist/routing/defaults.d.ts +1 -1
  177. package/dist/routing/entity-extractor.d.ts +1 -0
  178. package/dist/routing/entity-extractor.js +234 -29
  179. package/dist/routing/entity-extractor.js.map +1 -1
  180. package/dist/routing/fund-symbols.d.ts +2 -0
  181. package/dist/routing/fund-symbols.js +55 -0
  182. package/dist/routing/fund-symbols.js.map +1 -0
  183. package/dist/routing/horizon.d.ts +1 -0
  184. package/dist/routing/horizon.js +10 -0
  185. package/dist/routing/horizon.js.map +1 -0
  186. package/dist/routing/index.d.ts +10 -10
  187. package/dist/routing/index.js +6 -6
  188. package/dist/routing/index.js.map +1 -1
  189. package/dist/routing/planning.d.ts +2 -2
  190. package/dist/routing/planning.js +65 -34
  191. package/dist/routing/planning.js.map +1 -1
  192. package/dist/routing/route-manifest.d.ts +2 -2
  193. package/dist/routing/route-manifest.js +25 -4
  194. package/dist/routing/route-manifest.js.map +1 -1
  195. package/dist/routing/router-llm-client.js.map +1 -1
  196. package/dist/routing/router-prompt.js +7 -9
  197. package/dist/routing/router-prompt.js.map +1 -1
  198. package/dist/routing/router-types.d.ts +1 -0
  199. package/dist/routing/router.js +137 -22
  200. package/dist/routing/router.js.map +1 -1
  201. package/dist/routing/slot-resolver.d.ts +1 -1
  202. package/dist/routing/slot-resolver.js +2 -4
  203. package/dist/routing/slot-resolver.js.map +1 -1
  204. package/dist/routing/symbol-disambiguator.d.ts +11 -0
  205. package/dist/routing/symbol-disambiguator.js +52 -0
  206. package/dist/routing/symbol-disambiguator.js.map +1 -0
  207. package/dist/routing/turn-context.d.ts +1 -1
  208. package/dist/routing/turn-context.js +1 -1
  209. package/dist/routing/turn-context.js.map +1 -1
  210. package/dist/routing/types.d.ts +2 -0
  211. package/dist/runtime/answer-contracts.d.ts +1 -1
  212. package/dist/runtime/answer-contracts.js +48 -9
  213. package/dist/runtime/answer-contracts.js.map +1 -1
  214. package/dist/runtime/artifact-contracts.js.map +1 -1
  215. package/dist/runtime/planning-evidence.js +47 -26
  216. package/dist/runtime/planning-evidence.js.map +1 -1
  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 +13 -5
  224. package/dist/runtime/session-coordinator.js +160 -20
  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 +7 -5
  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 +10 -11
  243. package/dist/sentiment/index.js +10 -20
  244. package/dist/sentiment/index.js.map +1 -1
  245. package/dist/sentiment/insights.d.ts +17 -0
  246. package/dist/sentiment/insights.js +206 -0
  247. package/dist/sentiment/insights.js.map +1 -0
  248. package/dist/sentiment/keywords.js +26 -4
  249. package/dist/sentiment/keywords.js.map +1 -1
  250. package/dist/sentiment/pipeline.d.ts +2 -2
  251. package/dist/sentiment/pipeline.js +14 -2
  252. package/dist/sentiment/pipeline.js.map +1 -1
  253. package/dist/sentiment/scorer.d.ts +2 -0
  254. package/dist/sentiment/scorer.js +11 -2
  255. package/dist/sentiment/scorer.js.map +1 -1
  256. package/dist/sentiment/store.d.ts +1 -1
  257. package/dist/sentiment/store.js +1 -1
  258. package/dist/sentiment/store.js.map +1 -1
  259. package/dist/sentiment/trends.d.ts +1 -1
  260. package/dist/sentiment/trends.js.map +1 -1
  261. package/dist/sentiment/types.d.ts +2 -0
  262. package/dist/sentiment/types.js.map +1 -1
  263. package/dist/system-prompt.js +6 -9
  264. package/dist/system-prompt.js.map +1 -1
  265. package/dist/tool-kit.d.ts +7 -7
  266. package/dist/tool-kit.js +4 -4
  267. package/dist/tool-kit.js.map +1 -1
  268. package/dist/tools/fundamentals/company-overview.js +11 -6
  269. package/dist/tools/fundamentals/company-overview.js.map +1 -1
  270. package/dist/tools/fundamentals/comps.js +18 -9
  271. package/dist/tools/fundamentals/comps.js.map +1 -1
  272. package/dist/tools/fundamentals/dcf.js +23 -11
  273. package/dist/tools/fundamentals/dcf.js.map +1 -1
  274. package/dist/tools/fundamentals/earnings.js +8 -3
  275. package/dist/tools/fundamentals/earnings.js.map +1 -1
  276. package/dist/tools/fundamentals/financials.js +8 -3
  277. package/dist/tools/fundamentals/financials.js.map +1 -1
  278. package/dist/tools/fundamentals/sec-filings.js +21 -6
  279. package/dist/tools/fundamentals/sec-filings.js.map +1 -1
  280. package/dist/tools/index.d.ts +27 -20
  281. package/dist/tools/index.js +55 -43
  282. package/dist/tools/index.js.map +1 -1
  283. package/dist/tools/interaction/ask-user.js +15 -3
  284. package/dist/tools/interaction/ask-user.js.map +1 -1
  285. package/dist/tools/macro/fear-greed.js.map +1 -1
  286. package/dist/tools/macro/fred-data.d.ts +1 -1
  287. package/dist/tools/macro/fred-data.js +17 -6
  288. package/dist/tools/macro/fred-data.js.map +1 -1
  289. package/dist/tools/market/crypto-history.js +3 -1
  290. package/dist/tools/market/crypto-history.js.map +1 -1
  291. package/dist/tools/market/crypto-price.js +3 -1
  292. package/dist/tools/market/crypto-price.js.map +1 -1
  293. package/dist/tools/market/screen-stocks.d.ts +18 -0
  294. package/dist/tools/market/screen-stocks.js +252 -0
  295. package/dist/tools/market/screen-stocks.js.map +1 -0
  296. package/dist/tools/market/search-ticker.js +160 -8
  297. package/dist/tools/market/search-ticker.js.map +1 -1
  298. package/dist/tools/market/stock-history.d.ts +2 -2
  299. package/dist/tools/market/stock-history.js +26 -7
  300. package/dist/tools/market/stock-history.js.map +1 -1
  301. package/dist/tools/market/stock-quote.js +5 -3
  302. package/dist/tools/market/stock-quote.js.map +1 -1
  303. package/dist/tools/options/greeks.js +1 -1
  304. package/dist/tools/options/greeks.js.map +1 -1
  305. package/dist/tools/options/option-chain.js +19 -6
  306. package/dist/tools/options/option-chain.js.map +1 -1
  307. package/dist/tools/portfolio/alerts.d.ts +15 -0
  308. package/dist/tools/portfolio/alerts.js +357 -0
  309. package/dist/tools/portfolio/alerts.js.map +1 -0
  310. package/dist/tools/portfolio/correlation.d.ts +1 -1
  311. package/dist/tools/portfolio/correlation.js +33 -13
  312. package/dist/tools/portfolio/correlation.js.map +1 -1
  313. package/dist/tools/portfolio/daily-report.d.ts +8 -0
  314. package/dist/tools/portfolio/daily-report.js +83 -0
  315. package/dist/tools/portfolio/daily-report.js.map +1 -0
  316. package/dist/tools/portfolio/holdings-overlap.js +10 -3
  317. package/dist/tools/portfolio/holdings-overlap.js.map +1 -1
  318. package/dist/tools/portfolio/notifications.d.ts +7 -0
  319. package/dist/tools/portfolio/notifications.js +43 -0
  320. package/dist/tools/portfolio/notifications.js.map +1 -0
  321. package/dist/tools/portfolio/predictions.d.ts +12 -6
  322. package/dist/tools/portfolio/predictions.js +337 -87
  323. package/dist/tools/portfolio/predictions.js.map +1 -1
  324. package/dist/tools/portfolio/risk-analysis.d.ts +1 -1
  325. package/dist/tools/portfolio/risk-analysis.js +45 -6
  326. package/dist/tools/portfolio/risk-analysis.js.map +1 -1
  327. package/dist/tools/portfolio/tracker.d.ts +4 -3
  328. package/dist/tools/portfolio/tracker.js +246 -101
  329. package/dist/tools/portfolio/tracker.js.map +1 -1
  330. package/dist/tools/portfolio/watchlist.d.ts +6 -4
  331. package/dist/tools/portfolio/watchlist.js +208 -108
  332. package/dist/tools/portfolio/watchlist.js.map +1 -1
  333. package/dist/tools/sentiment/insight-format.d.ts +2 -0
  334. package/dist/tools/sentiment/insight-format.js +36 -0
  335. package/dist/tools/sentiment/insight-format.js.map +1 -0
  336. package/dist/tools/sentiment/query-match.d.ts +3 -0
  337. package/dist/tools/sentiment/query-match.js +113 -0
  338. package/dist/tools/sentiment/query-match.js.map +1 -0
  339. package/dist/tools/sentiment/reddit-sentiment.d.ts +12 -1
  340. package/dist/tools/sentiment/reddit-sentiment.js +266 -107
  341. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -1
  342. package/dist/tools/sentiment/sentiment-summary.d.ts +9 -1
  343. package/dist/tools/sentiment/sentiment-summary.js +223 -205
  344. package/dist/tools/sentiment/sentiment-summary.js.map +1 -1
  345. package/dist/tools/sentiment/sentiment-trend.d.ts +1 -1
  346. package/dist/tools/sentiment/sentiment-trend.js +12 -2
  347. package/dist/tools/sentiment/sentiment-trend.js.map +1 -1
  348. package/dist/tools/sentiment/twitter-sentiment.d.ts +11 -1
  349. package/dist/tools/sentiment/twitter-sentiment.js +188 -58
  350. package/dist/tools/sentiment/twitter-sentiment.js.map +1 -1
  351. package/dist/tools/sentiment/untrusted-text.d.ts +2 -0
  352. package/dist/tools/sentiment/untrusted-text.js +17 -0
  353. package/dist/tools/sentiment/untrusted-text.js.map +1 -0
  354. package/dist/tools/sentiment/web-search.js +9 -13
  355. package/dist/tools/sentiment/web-search.js.map +1 -1
  356. package/dist/tools/sentiment/web-sentiment.js +19 -3
  357. package/dist/tools/sentiment/web-sentiment.js.map +1 -1
  358. package/dist/tools/technical/backtest.d.ts +1 -1
  359. package/dist/tools/technical/backtest.js +27 -20
  360. package/dist/tools/technical/backtest.js.map +1 -1
  361. package/dist/tools/technical/indicators.js +23 -5
  362. package/dist/tools/technical/indicators.js.map +1 -1
  363. package/dist/types/index.d.ts +3 -3
  364. package/dist/types/index.js.map +1 -1
  365. package/dist/types/market.d.ts +1 -0
  366. package/dist/types/portfolio.d.ts +14 -4
  367. package/dist/types/sentiment.d.ts +52 -0
  368. package/dist/workflows/compare-assets.d.ts +0 -3
  369. package/dist/workflows/compare-assets.js +20 -11
  370. package/dist/workflows/compare-assets.js.map +1 -1
  371. package/dist/workflows/index.d.ts +3 -4
  372. package/dist/workflows/index.js +3 -3
  373. package/dist/workflows/index.js.map +1 -1
  374. package/dist/workflows/options-screener.d.ts +0 -3
  375. package/dist/workflows/options-screener.js +4 -11
  376. package/dist/workflows/options-screener.js.map +1 -1
  377. package/dist/workflows/portfolio-builder.d.ts +0 -3
  378. package/dist/workflows/portfolio-builder.js +0 -8
  379. package/dist/workflows/portfolio-builder.js.map +1 -1
  380. package/gui/server/ask-user-bridge.ts +1 -1
  381. package/gui/server/automation-heartbeat.ts +97 -0
  382. package/gui/server/background-quotes.ts +97 -1
  383. package/gui/server/chat-event-adapter.ts +32 -10
  384. package/gui/server/chat-run-session.ts +16 -0
  385. package/gui/server/invoke-tool.ts +160 -3
  386. package/gui/server/live-chat-event-adapter.ts +21 -6
  387. package/gui/server/market-state-api.ts +315 -0
  388. package/gui/server/model-setup.ts +156 -2
  389. package/gui/server/private-api-access.ts +62 -0
  390. package/gui/server/projector.ts +18 -9
  391. package/gui/server/prompt-observation.ts +4 -7
  392. package/gui/server/quote-snapshot-store.ts +50 -0
  393. package/gui/server/server.ts +218 -451
  394. package/gui/server/session-actions.ts +186 -1
  395. package/gui/server/shutdown.ts +47 -0
  396. package/gui/server/tool-invoke-ack.ts +49 -0
  397. package/gui/server/tool-metadata.ts +101 -24
  398. package/gui/server/websocket.ts +13 -3
  399. package/gui/server/writer-lock.ts +6 -2
  400. package/gui/server/ws-hub.ts +311 -0
  401. package/gui/shared/chat-events.ts +16 -1
  402. package/gui/shared/event-reducer.ts +24 -6
  403. package/gui/web/dist/assets/CatalogOverlay-CgeY5Pkp.js +1 -0
  404. package/gui/web/dist/assets/index-C6W_2eAn.js +69 -0
  405. package/gui/web/dist/assets/index-hwbx24a5.css +1 -0
  406. package/gui/web/dist/index.html +2 -2
  407. package/package.json +9 -6
  408. package/src/analysts/contracts.ts +10 -23
  409. package/src/analysts/orchestrator.ts +8 -43
  410. package/src/cli.ts +76 -12
  411. package/src/config.ts +44 -9
  412. package/src/index.ts +1 -1
  413. package/src/infra/cache.ts +41 -30
  414. package/src/infra/http-client.ts +72 -6
  415. package/src/infra/index.ts +6 -10
  416. package/src/infra/native-dependencies.ts +8 -3
  417. package/src/infra/node-version.ts +3 -1
  418. package/src/infra/opencandle-paths.ts +3 -14
  419. package/src/infra/rate-limiter.ts +22 -19
  420. package/src/market-state/alert-conditions.ts +82 -0
  421. package/src/market-state/alert-runner.ts +863 -0
  422. package/src/market-state/daily-report.ts +247 -0
  423. package/src/market-state/local-automation-service.ts +162 -0
  424. package/src/market-state/notification-delivery.ts +158 -0
  425. package/src/market-state/resolve-for-mutation.ts +24 -0
  426. package/src/market-state/resolve.ts +112 -0
  427. package/src/market-state/service.ts +2344 -0
  428. package/src/memory/index.ts +7 -7
  429. package/src/memory/manager.ts +14 -16
  430. package/src/memory/retrieval.ts +8 -7
  431. package/src/memory/sqlite.ts +407 -6
  432. package/src/memory/storage.ts +5 -15
  433. package/src/memory/tool-defaults.ts +60 -39
  434. package/src/memory/types.ts +3 -3
  435. package/src/monitor.ts +121 -0
  436. package/src/onboarding/connect.ts +24 -31
  437. package/src/onboarding/credential-interceptor.ts +3 -15
  438. package/src/onboarding/degradation-accumulator.ts +1 -3
  439. package/src/onboarding/provider-status.ts +410 -0
  440. package/src/onboarding/providers.ts +144 -45
  441. package/src/onboarding/state.ts +13 -15
  442. package/src/onboarding/tool-helpers.ts +2 -9
  443. package/src/onboarding/tool-tags.ts +51 -8
  444. package/src/onboarding/validation.ts +16 -22
  445. package/src/pi/opencandle-extension.ts +643 -101
  446. package/src/pi/session.ts +7 -5
  447. package/src/pi/setup.ts +61 -43
  448. package/src/pi/tool-adapter.ts +19 -6
  449. package/src/prompts/context-builder.ts +24 -13
  450. package/src/prompts/policy-cards.ts +3 -3
  451. package/src/prompts/sections.ts +1 -1
  452. package/src/prompts/symbol-preflight.ts +80 -0
  453. package/src/prompts/workflow-prompts.ts +77 -28
  454. package/src/providers/alpha-vantage.ts +58 -39
  455. package/src/providers/coingecko.ts +2 -5
  456. package/src/providers/errors.ts +9 -0
  457. package/src/providers/exa-search.ts +24 -22
  458. package/src/providers/external-tool-error.ts +20 -0
  459. package/src/providers/fear-greed.ts +1 -1
  460. package/src/providers/finnhub.ts +7 -6
  461. package/src/providers/fred.ts +3 -3
  462. package/src/providers/index.ts +14 -6
  463. package/src/providers/reddit-cli.ts +317 -0
  464. package/src/providers/reddit.ts +14 -59
  465. package/src/providers/sec-edgar.ts +20 -6
  466. package/src/providers/tradingview.ts +399 -0
  467. package/src/providers/twitter-cli.ts +233 -0
  468. package/src/providers/twitter.ts +8 -79
  469. package/src/providers/web-search.ts +30 -20
  470. package/src/providers/with-fallback.ts +8 -7
  471. package/src/providers/wrap-provider.ts +49 -10
  472. package/src/providers/yahoo-finance.ts +204 -66
  473. package/src/routing/classify-intent.ts +101 -10
  474. package/src/routing/defaults.ts +1 -1
  475. package/src/routing/entity-extractor.ts +287 -38
  476. package/src/routing/fund-symbols.ts +58 -0
  477. package/src/routing/horizon.ts +7 -0
  478. package/src/routing/index.ts +48 -48
  479. package/src/routing/planning.ts +145 -53
  480. package/src/routing/route-manifest.ts +37 -15
  481. package/src/routing/router-llm-client.ts +4 -4
  482. package/src/routing/router-prompt.ts +15 -19
  483. package/src/routing/router-types.ts +2 -5
  484. package/src/routing/router.ts +251 -53
  485. package/src/routing/slot-resolver.ts +34 -11
  486. package/src/routing/symbol-disambiguator.ts +72 -0
  487. package/src/routing/turn-context.ts +6 -9
  488. package/src/routing/types.ts +2 -0
  489. package/src/runtime/answer-contracts.ts +105 -45
  490. package/src/runtime/artifact-contracts.ts +2 -1
  491. package/src/runtime/planning-evidence.ts +157 -66
  492. package/src/runtime/prompt-step.ts +1 -16
  493. package/src/runtime/run-context.ts +12 -2
  494. package/src/runtime/session-coordinator.ts +238 -63
  495. package/src/runtime/session-title.ts +60 -0
  496. package/src/runtime/tool-defaults-wrapper.ts +13 -5
  497. package/src/runtime/validation.ts +1 -4
  498. package/src/runtime/workflow-events.ts +7 -7
  499. package/src/runtime/workflow-runner.ts +5 -11
  500. package/src/sentiment/adapters/finnhub.ts +7 -2
  501. package/src/sentiment/adapters/reddit.ts +2 -2
  502. package/src/sentiment/adapters/twitter.ts +1 -1
  503. package/src/sentiment/adapters/web.ts +1 -1
  504. package/src/sentiment/index.ts +17 -26
  505. package/src/sentiment/insights.ts +269 -0
  506. package/src/sentiment/keywords.ts +26 -4
  507. package/src/sentiment/pipeline.ts +28 -5
  508. package/src/sentiment/scorer.ts +13 -2
  509. package/src/sentiment/store.ts +2 -2
  510. package/src/sentiment/trends.ts +9 -3
  511. package/src/sentiment/types.ts +8 -4
  512. package/src/system-prompt.ts +6 -9
  513. package/src/tool-kit.ts +10 -9
  514. package/src/tools/fundamentals/company-overview.ts +19 -9
  515. package/src/tools/fundamentals/comps.ts +68 -55
  516. package/src/tools/fundamentals/dcf.ts +145 -95
  517. package/src/tools/fundamentals/earnings.ts +16 -6
  518. package/src/tools/fundamentals/financials.ts +16 -7
  519. package/src/tools/fundamentals/sec-filings.ts +37 -16
  520. package/src/tools/index.ts +56 -43
  521. package/src/tools/interaction/ask-user.ts +22 -10
  522. package/src/tools/macro/fear-greed.ts +1 -1
  523. package/src/tools/macro/fred-data.ts +58 -46
  524. package/src/tools/market/crypto-history.ts +8 -3
  525. package/src/tools/market/crypto-price.ts +6 -6
  526. package/src/tools/market/screen-stocks.ts +279 -0
  527. package/src/tools/market/search-ticker.ts +218 -17
  528. package/src/tools/market/stock-history.ts +37 -12
  529. package/src/tools/market/stock-quote.ts +10 -7
  530. package/src/tools/options/greeks.ts +5 -5
  531. package/src/tools/options/option-chain.ts +41 -17
  532. package/src/tools/portfolio/alerts.ts +457 -0
  533. package/src/tools/portfolio/correlation.ts +47 -20
  534. package/src/tools/portfolio/daily-report.ts +101 -0
  535. package/src/tools/portfolio/holdings-overlap.ts +31 -15
  536. package/src/tools/portfolio/notifications.ts +45 -0
  537. package/src/tools/portfolio/predictions.ts +406 -106
  538. package/src/tools/portfolio/risk-analysis.ts +46 -7
  539. package/src/tools/portfolio/tracker.ts +270 -109
  540. package/src/tools/portfolio/watchlist.ts +250 -121
  541. package/src/tools/sentiment/insight-format.ts +50 -0
  542. package/src/tools/sentiment/query-match.ts +117 -0
  543. package/src/tools/sentiment/reddit-sentiment.ts +360 -121
  544. package/src/tools/sentiment/sentiment-summary.ts +302 -235
  545. package/src/tools/sentiment/sentiment-trend.ts +24 -7
  546. package/src/tools/sentiment/twitter-sentiment.ts +264 -73
  547. package/src/tools/sentiment/untrusted-text.ts +21 -0
  548. package/src/tools/sentiment/web-search.ts +21 -18
  549. package/src/tools/sentiment/web-sentiment.ts +30 -10
  550. package/src/tools/technical/backtest.ts +32 -22
  551. package/src/tools/technical/indicators.ts +39 -14
  552. package/src/types/index.ts +8 -3
  553. package/src/types/market.ts +1 -0
  554. package/src/types/portfolio.ts +14 -4
  555. package/src/types/sentiment.ts +61 -2
  556. package/src/workflows/compare-assets.ts +33 -21
  557. package/src/workflows/index.ts +3 -4
  558. package/src/workflows/options-screener.ts +27 -29
  559. package/src/workflows/portfolio-builder.ts +34 -27
  560. package/dist/infra/browser.d.ts +0 -35
  561. package/dist/infra/browser.js +0 -103
  562. package/dist/infra/browser.js.map +0 -1
  563. package/dist/tools/interaction/twitter-login.d.ts +0 -8
  564. package/dist/tools/interaction/twitter-login.js +0 -77
  565. package/dist/tools/interaction/twitter-login.js.map +0 -1
  566. package/dist/workflows/types.d.ts +0 -4
  567. package/dist/workflows/types.js +0 -2
  568. package/dist/workflows/types.js.map +0 -1
  569. package/gui/web/dist/assets/CatalogOverlay-Bmp6Knu7.js +0 -1
  570. package/gui/web/dist/assets/index-Bxt9QpLX.css +0 -1
  571. package/gui/web/dist/assets/index-CZ9DHZYy.js +0 -67
  572. package/src/infra/browser.ts +0 -111
  573. package/src/tools/interaction/twitter-login.ts +0 -93
  574. package/src/workflows/types.ts +0 -4
@@ -1,5 +1,10 @@
1
- import type { SessionEntry } from "@earendil-works/pi-coding-agent";
2
1
  import type { ToolResultMessage } from "@earendil-works/pi-ai";
2
+ import type { SessionEntry, SessionManager } from "@earendil-works/pi-coding-agent";
3
+ import { getAllTools } from "../../src/tools/index.js";
4
+ import { invokeToolFromUi } from "./invoke-tool.js";
5
+ import { projectDashboard } from "./projector.js";
6
+
7
+ export const BACKGROUND_QUOTE_POLL_INTERVAL_MS = 30_000;
3
8
 
4
9
  export interface BackgroundQuoteRefresh {
5
10
  symbol: string;
@@ -29,3 +34,94 @@ export class BackgroundQuoteRefreshes {
29
34
  return [...entries, ...this.entriesBySymbol.values()];
30
35
  }
31
36
  }
37
+
38
+ type IntervalHandle = ReturnType<typeof setInterval>;
39
+ type SetIntervalFn = (callback: () => void, ms: number) => IntervalHandle;
40
+ type ClearIntervalFn = (handle: IntervalHandle) => void;
41
+
42
+ export interface BackgroundQuotePoller {
43
+ updatePoller(): void;
44
+ pollVisibleQuotes(): Promise<void>;
45
+ stop(): void;
46
+ }
47
+
48
+ export interface BackgroundQuotePollerOptions {
49
+ getClientCount: () => number;
50
+ getSessionManager: () => SessionManager;
51
+ refreshes: BackgroundQuoteRefreshes;
52
+ broadcastState: () => void;
53
+ getTools?: typeof getAllTools;
54
+ invokeTool?: typeof invokeToolFromUi;
55
+ setIntervalFn?: SetIntervalFn;
56
+ clearIntervalFn?: ClearIntervalFn;
57
+ warn?: (message: string) => void;
58
+ }
59
+
60
+ export function createBackgroundQuotePoller({
61
+ getClientCount,
62
+ getSessionManager,
63
+ refreshes,
64
+ broadcastState,
65
+ getTools = getAllTools,
66
+ invokeTool = invokeToolFromUi,
67
+ setIntervalFn = setInterval,
68
+ clearIntervalFn = clearInterval,
69
+ warn = (message) => console.warn(message),
70
+ }: BackgroundQuotePollerOptions): BackgroundQuotePoller {
71
+ let poller: IntervalHandle | null = null;
72
+ let quotePollInFlight = false;
73
+
74
+ async function pollVisibleQuotes(): Promise<void> {
75
+ if (quotePollInFlight) return;
76
+ quotePollInFlight = true;
77
+ try {
78
+ const sessionManager = getSessionManager();
79
+ const state = projectDashboard(sessionManager.getEntries(), sessionManager.getSessionId());
80
+ const tool = getTools().find((candidate) => candidate.name === "get_stock_quote");
81
+ if (!tool) return;
82
+ for (const row of state.watchlist.filter((item) => item.pinned || item.quote)) {
83
+ const result = await invokeTool(
84
+ sessionManager,
85
+ tool,
86
+ { symbol: row.symbol },
87
+ "background",
88
+ { recordTranscript: false },
89
+ );
90
+ refreshes.upsert({
91
+ symbol: row.symbol,
92
+ toolName: tool.name,
93
+ args: { symbol: row.symbol },
94
+ value: result.result.details,
95
+ content: result.result.content,
96
+ isError: result.isError,
97
+ });
98
+ }
99
+ broadcastState();
100
+ } catch (error) {
101
+ warn(
102
+ `Background quote refresh failed: ${error instanceof Error ? error.message : String(error)}`,
103
+ );
104
+ } finally {
105
+ quotePollInFlight = false;
106
+ }
107
+ }
108
+
109
+ function updatePoller(): void {
110
+ if (getClientCount() > 0 && !poller) {
111
+ poller = setIntervalFn(() => void pollVisibleQuotes(), BACKGROUND_QUOTE_POLL_INTERVAL_MS);
112
+ }
113
+ if (getClientCount() === 0 && poller) {
114
+ clearIntervalFn(poller);
115
+ poller = null;
116
+ }
117
+ }
118
+
119
+ function stop(): void {
120
+ if (poller) {
121
+ clearIntervalFn(poller);
122
+ poller = null;
123
+ }
124
+ }
125
+
126
+ return { updatePoller, pollVisibleQuotes, stop };
127
+ }
@@ -1,5 +1,5 @@
1
- import type { SessionEntry } from "@earendil-works/pi-coding-agent";
2
1
  import type { Message, ToolResultMessage } from "@earendil-works/pi-ai";
2
+ import type { SessionEntry } from "@earendil-works/pi-coding-agent";
3
3
  import type { ChatEvent, MessageContent, ToolOutput } from "../shared/chat-events.js";
4
4
 
5
5
  export interface SessionEventOptions {
@@ -16,6 +16,9 @@ export function sessionEntriesToChatEvents(
16
16
  let seq = options.startSeq ?? 1;
17
17
  const events: ChatEvent[] = [];
18
18
  const seenToolCalls = new Set<string>();
19
+ // Set by an opencandle-user-input marker: the user's words before a workflow
20
+ // transform expanded the turn. The next user message renders this instead.
21
+ let pendingOriginalInput: string | null = null;
19
22
  const updatedAt = options.updatedAt ?? entries.at(-1)?.timestamp ?? new Date().toISOString();
20
23
 
21
24
  events.push({
@@ -27,12 +30,17 @@ export function sessionEntriesToChatEvents(
27
30
  });
28
31
 
29
32
  for (const entry of entries) {
33
+ if (isOriginalInputEntry(entry)) {
34
+ pendingOriginalInput = originalInputText(entry);
35
+ continue;
36
+ }
37
+
30
38
  if (entry.type === "custom_message") {
31
39
  const messageId = entry.id;
32
- events.push({ type: "message.created", messageId, role: "assistant", seq: seq++ });
33
40
  events.push({
34
- type: "message.completed",
41
+ type: "custom.message",
35
42
  messageId,
43
+ customType: String((entry as { customType?: unknown }).customType || "custom"),
36
44
  content: [{ type: "text", text: customMessageText(entry.content) }],
37
45
  seq: seq++,
38
46
  });
@@ -48,9 +56,10 @@ export function sessionEntriesToChatEvents(
48
56
  events.push({
49
57
  type: "message.completed",
50
58
  messageId,
51
- content: [{ type: "text", text: messageText(message.content) }],
59
+ content: [{ type: "text", text: pendingOriginalInput ?? messageText(message.content) }],
52
60
  seq: seq++,
53
61
  });
62
+ pendingOriginalInput = null;
54
63
  continue;
55
64
  }
56
65
 
@@ -109,6 +118,20 @@ export function sessionEntriesToChatEvents(
109
118
  return events;
110
119
  }
111
120
 
121
+ export function isOriginalInputEntry(entry: SessionEntry): boolean {
122
+ return (
123
+ entry.type === "custom" &&
124
+ (entry as { customType?: unknown }).customType === "opencandle-user-input"
125
+ );
126
+ }
127
+
128
+ export function originalInputText(entry: SessionEntry): string | null {
129
+ const data = (entry as { data?: { original?: unknown } }).data;
130
+ return typeof data?.original === "string" && data.original.trim().length > 0
131
+ ? data.original
132
+ : null;
133
+ }
134
+
112
135
  function customMessageText(content: unknown): string {
113
136
  if (typeof content === "string") return content;
114
137
  return messageText(content);
@@ -117,16 +140,15 @@ function customMessageText(content: unknown): string {
117
140
  function messageText(content: unknown): string {
118
141
  if (typeof content === "string") return content;
119
142
  if (!Array.isArray(content)) return "";
120
- return content
121
- .map((part) => typeof part.text === "string" ? part.text : "")
122
- .join("");
143
+ return content.map((part) => (typeof part.text === "string" ? part.text : "")).join("");
123
144
  }
124
145
 
125
146
  function toolOutput(message: ToolResultMessage): ToolOutput {
126
147
  const details = message.details;
127
- const source = typeof details === "object" && details !== null && "source" in details
128
- ? String((details as { source?: unknown }).source ?? "")
129
- : undefined;
148
+ const source =
149
+ typeof details === "object" && details !== null && "source" in details
150
+ ? String((details as { source?: unknown }).source ?? "")
151
+ : undefined;
130
152
  return {
131
153
  content: message.content,
132
154
  details,
@@ -0,0 +1,16 @@
1
+ export interface ChatRunSessionConflict {
2
+ error: string;
3
+ code: "session_changed";
4
+ }
5
+
6
+ export function chatRunSessionConflict(
7
+ expectedSessionId: unknown,
8
+ activeSessionId: string,
9
+ ): ChatRunSessionConflict | null {
10
+ const expected = typeof expectedSessionId === "string" ? expectedSessionId.trim() : "";
11
+ if (!expected || expected === activeSessionId) return null;
12
+ return {
13
+ error: "The active session changed before this message was sent.",
14
+ code: "session_changed",
15
+ };
16
+ }
@@ -5,6 +5,9 @@ import type { TSchema } from "@sinclair/typebox";
5
5
  import { Value } from "@sinclair/typebox/value";
6
6
  import { getDefaults } from "../../src/memory/tool-defaults.js";
7
7
  import { wrapWithDefaults } from "../../src/runtime/tool-defaults-wrapper.js";
8
+ import { getAllTools } from "../../src/tools/index.js";
9
+ import type { AskUserHandler } from "../../src/types/index.js";
10
+ import { buildToolInvokeAckMessage } from "./tool-invoke-ack.js";
8
11
 
9
12
  export interface InvokeToolResult {
10
13
  toolCallId: string;
@@ -12,12 +15,89 @@ export interface InvokeToolResult {
12
15
  isError: boolean;
13
16
  }
14
17
 
18
+ interface ToolInvokeClient {
19
+ send(message: unknown): void;
20
+ }
21
+
22
+ export interface ToolInvokeController {
23
+ handleToolInvoke(toolName: string, args: Record<string, unknown>): Promise<InvokeToolResult>;
24
+ handleToolInvokeMessage(client: ToolInvokeClient, data: Record<string, unknown>): Promise<void>;
25
+ }
26
+
27
+ export interface ToolInvokeControllerOptions {
28
+ role: string;
29
+ getSessionManager: () => SessionManager;
30
+ broadcastState: () => void;
31
+ onMarketStateChanged?: () => void;
32
+ getTools?: typeof getAllTools;
33
+ invokeTool?: typeof invokeToolFromUi;
34
+ askUserHandler?: AskUserHandler;
35
+ }
36
+
37
+ export function createToolInvokeController({
38
+ role,
39
+ getSessionManager,
40
+ broadcastState,
41
+ onMarketStateChanged,
42
+ getTools = getAllTools,
43
+ invokeTool = invokeToolFromUi,
44
+ askUserHandler,
45
+ }: ToolInvokeControllerOptions): ToolInvokeController {
46
+ async function handleToolInvoke(
47
+ toolName: string,
48
+ args: Record<string, unknown>,
49
+ ): Promise<InvokeToolResult> {
50
+ if (role !== "writer") throw new Error("Read-only follower mode");
51
+ const tool = getTools().find((candidate) => candidate.name === toolName);
52
+ if (!tool) throw new Error(`Unknown tool: ${toolName}`);
53
+ const result = await invokeTool(getSessionManager(), tool, args, "ui", { askUserHandler });
54
+ if (!result.isError && marketStateToolMapping(toolName) != null) {
55
+ onMarketStateChanged?.();
56
+ }
57
+ broadcastState();
58
+ return result;
59
+ }
60
+
61
+ async function handleToolInvokeMessage(
62
+ client: ToolInvokeClient,
63
+ data: Record<string, unknown>,
64
+ ): Promise<void> {
65
+ const requestId = typeof data.requestId === "string" ? data.requestId : "";
66
+ const toolName = String(data.toolName ?? "");
67
+ try {
68
+ const result = await handleToolInvoke(toolName, requestArgs(data.args));
69
+ if (requestId) {
70
+ client.send(buildToolInvokeAckMessage(requestId, toolName, result));
71
+ }
72
+ } catch (error) {
73
+ const message = error instanceof Error ? error.message : String(error);
74
+ if (requestId) {
75
+ client.send({
76
+ type: "tool.invoke.result",
77
+ requestId,
78
+ ok: false,
79
+ toolName,
80
+ error: { message },
81
+ });
82
+ return;
83
+ }
84
+ throw error;
85
+ }
86
+ }
87
+
88
+ return { handleToolInvoke, handleToolInvokeMessage };
89
+ }
90
+
91
+ function requestArgs(value: unknown): Record<string, unknown> {
92
+ return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : {};
93
+ }
94
+
15
95
  export async function invokeToolFromUi(
16
96
  sessionManager: SessionManager,
17
97
  tool: AgentTool<TSchema, unknown>,
18
98
  args: Record<string, unknown>,
19
99
  source: "ui" | "background" = "ui",
20
- options: { recordTranscript?: boolean } = {},
100
+ options: { askUserHandler?: AskUserHandler; recordTranscript?: boolean } = {},
21
101
  ): Promise<InvokeToolResult> {
22
102
  if (!Value.Check(tool.parameters, args)) {
23
103
  const errors = [...Value.Errors(tool.parameters, args)]
@@ -53,7 +133,18 @@ export async function invokeToolFromUi(
53
133
  let result: AgentToolResult<unknown>;
54
134
  let isError = false;
55
135
  try {
56
- result = await wrapped.execute(toolCallId, args as never);
136
+ result = await (
137
+ wrapped.execute as (
138
+ id: string,
139
+ params: never,
140
+ signal: AbortSignal | undefined,
141
+ onUpdate: undefined,
142
+ ctx: { askUserHandler?: AskUserHandler; hasUI: false },
143
+ ) => ReturnType<typeof wrapped.execute>
144
+ )(toolCallId, args as never, undefined, undefined, {
145
+ askUserHandler: options.askUserHandler,
146
+ hasUI: false,
147
+ });
57
148
  } catch (error) {
58
149
  isError = true;
59
150
  result = {
@@ -68,7 +159,12 @@ export async function invokeToolFromUi(
68
159
  toolCallId,
69
160
  toolName: tool.name,
70
161
  content: result.content,
71
- details: { source, args, value: result.details },
162
+ details: {
163
+ source,
164
+ args,
165
+ value: result.details,
166
+ ...stateChangeDetails(tool.name, args, result.details, source),
167
+ },
72
168
  isError,
73
169
  timestamp: Date.now(),
74
170
  });
@@ -77,6 +173,67 @@ export async function invokeToolFromUi(
77
173
  return { toolCallId, result, isError };
78
174
  }
79
175
 
176
+ function stateChangeDetails(
177
+ toolName: string,
178
+ args: Record<string, unknown>,
179
+ value: unknown,
180
+ source: "ui" | "background",
181
+ ): { stateChange: Record<string, unknown> } | Record<string, never> {
182
+ const mapping = marketStateToolMapping(toolName);
183
+ if (mapping == null) return {};
184
+
185
+ const valueRecord = asRecord(value);
186
+ return {
187
+ stateChange: {
188
+ source,
189
+ domain: mapping.domain,
190
+ action: typeof args.action === "string" ? args.action : "invoke",
191
+ targetType: mapping.targetType,
192
+ targetId: numericField(valueRecord, "id"),
193
+ targetIds: numericArrayField(valueRecord, "removedLotIds"),
194
+ instrumentId: numericField(valueRecord, "instrumentId"),
195
+ instrumentIds: numericArrayField(valueRecord, "instrumentIds"),
196
+ toolName,
197
+ },
198
+ };
199
+ }
200
+
201
+ function marketStateToolMapping(toolName: string): { domain: string; targetType: string } | null {
202
+ switch (toolName) {
203
+ case "manage_watchlist":
204
+ return { domain: "watchlist", targetType: "watchlist_item" };
205
+ case "track_portfolio":
206
+ return { domain: "portfolio", targetType: "portfolio_lot" };
207
+ case "track_prediction":
208
+ return { domain: "predictions", targetType: "prediction" };
209
+ case "manage_alerts":
210
+ return { domain: "alerts", targetType: "alert_rule" };
211
+ case "daily_watchlist_report":
212
+ return { domain: "reports", targetType: "report_run" };
213
+ default:
214
+ return null;
215
+ }
216
+ }
217
+
218
+ function asRecord(value: unknown): Record<string, unknown> | null {
219
+ return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
220
+ }
221
+
222
+ function numericField(record: Record<string, unknown> | null, key: string): number | undefined {
223
+ const value = record?.[key];
224
+ return typeof value === "number" ? value : undefined;
225
+ }
226
+
227
+ function numericArrayField(
228
+ record: Record<string, unknown> | null,
229
+ key: string,
230
+ ): number[] | undefined {
231
+ const value = record?.[key];
232
+ if (!Array.isArray(value)) return undefined;
233
+ const numbers = value.filter((item): item is number => typeof item === "number");
234
+ return numbers.length > 0 ? numbers : undefined;
235
+ }
236
+
80
237
  function emptyUsage(): Usage {
81
238
  return {
82
239
  input: 0,
@@ -1,5 +1,5 @@
1
- import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
2
1
  import type { AssistantMessage, Message } from "@earendil-works/pi-ai";
2
+ import type { AgentSessionEvent } from "@earendil-works/pi-coding-agent";
3
3
  import type { ChatEvent, MessageContent, ToolOutput } from "../shared/chat-events.js";
4
4
 
5
5
  export interface LiveChatEventAdapterOptions {
@@ -7,6 +7,11 @@ export interface LiveChatEventAdapterOptions {
7
7
  sessionId: string;
8
8
  startSeq: number;
9
9
  emit: (event: ChatEvent) => void;
10
+ /**
11
+ * The user's words as typed. Workflow transforms can replace the first user
12
+ * message with an expanded prompt; the live view renders this instead.
13
+ */
14
+ originalPrompt?: string;
10
15
  }
11
16
 
12
17
  export interface LiveChatEventAdapter {
@@ -14,7 +19,9 @@ export interface LiveChatEventAdapter {
14
19
  nextSeq(): number;
15
20
  }
16
21
 
17
- export function createLiveChatEventAdapter(options: LiveChatEventAdapterOptions): LiveChatEventAdapter {
22
+ export function createLiveChatEventAdapter(
23
+ options: LiveChatEventAdapterOptions,
24
+ ): LiveChatEventAdapter {
18
25
  let seq = options.startSeq;
19
26
  let userCount = 0;
20
27
  let assistantCount = 0;
@@ -55,11 +62,15 @@ export function createLiveChatEventAdapter(options: LiveChatEventAdapterOptions)
55
62
  const message = event.message as Message;
56
63
  if (message.role === "user") {
57
64
  const messageId = `${options.runId}-user-${++userCount}`;
65
+ const text =
66
+ userCount === 1 && options.originalPrompt
67
+ ? options.originalPrompt
68
+ : messageText(message.content);
58
69
  emit({ type: "message.created", messageId, role: "user" });
59
70
  emit({
60
71
  type: "message.completed",
61
72
  messageId,
62
- content: [{ type: "text", text: messageText(message.content) }],
73
+ content: [{ type: "text", text }],
63
74
  });
64
75
  return;
65
76
  }
@@ -161,14 +172,18 @@ function messageText(content: unknown): string {
161
172
  if (typeof content === "string") return content;
162
173
  if (!Array.isArray(content)) return "";
163
174
  return content
164
- .map((part) => typeof part === "object" && part !== null && "text" in part && typeof part.text === "string" ? part.text : "")
175
+ .map((part) =>
176
+ typeof part === "object" && part !== null && "text" in part && typeof part.text === "string"
177
+ ? part.text
178
+ : "",
179
+ )
165
180
  .join("");
166
181
  }
167
182
 
168
183
  function toolOutput(result: unknown, isError: boolean): ToolOutput {
169
184
  const record = asRecord(result);
170
185
  return {
171
- content: Array.isArray(record.content) ? record.content as ToolOutput["content"] : [],
186
+ content: Array.isArray(record.content) ? (record.content as ToolOutput["content"]) : [],
172
187
  details: record.details,
173
188
  isError,
174
189
  };
@@ -176,6 +191,6 @@ function toolOutput(result: unknown, isError: boolean): ToolOutput {
176
191
 
177
192
  function asRecord(value: unknown): Record<string, unknown> {
178
193
  return typeof value === "object" && value !== null && !Array.isArray(value)
179
- ? value as Record<string, unknown>
194
+ ? (value as Record<string, unknown>)
180
195
  : {};
181
196
  }