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
@@ -1,6 +1,3 @@
1
1
  import type { OptionsScreenerSlots, SlotResolution } from "../routing/types.js";
2
- import type { WorkflowPlan } from "./types.js";
3
2
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
4
3
  export declare function buildOptionsScreenerWorkflowDefinition(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowDefinition;
5
- /** @deprecated Use buildOptionsScreenerWorkflowDefinition instead */
6
- export declare function buildOptionsScreenerWorkflow(resolution: SlotResolution<OptionsScreenerSlots>): WorkflowPlan;
@@ -2,7 +2,68 @@ import { buildOptionsScreenerPrompt } from "../prompts/workflow-prompts.js";
2
2
  import { promptStep } from "../runtime/prompt-step.js";
3
3
  export function buildOptionsScreenerWorkflowDefinition(resolution) {
4
4
  const s = resolution.resolved;
5
- const contractType = s.direction === "bullish" ? "calls" : "puts";
5
+ const isProtectivePutContext = s.optionStrategy === "protective_put";
6
+ const contractType = s.direction === "bullish" && !isProtectivePutContext ? "calls" : "puts";
7
+ const horizonPhrase = describeDteTarget(s.dteTarget);
8
+ const isCoveredCallContext = !isProtectivePutContext &&
9
+ (s.optionStrategy === "covered_call" ||
10
+ s.costBasis !== undefined ||
11
+ (s.catalystSymbols?.length ?? 0) > 0);
12
+ const rankingInstruction = isProtectivePutContext
13
+ ? "Rank by protection per dollar of premium, expiration fit, moneyness, hedge floor, live liquidity, and premium as a percent of the stock position."
14
+ : isCoveredCallContext
15
+ ? "Rank by premium collected, strike above cost basis, assignment risk, event risk, live liquidity, and probability of expiring out of the money."
16
+ : `Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit. Only include contracts with |delta| >= 0.20.`;
17
+ const maxPremiumInstruction = s.maxPremium !== undefined
18
+ ? ` Do not rank contracts above the user's max premium of $${s.maxPremium.toLocaleString("en-US")} unless no contracts under that cap are liquid; if so, say the cap could not be met.`
19
+ : "";
20
+ const riskInstruction = isProtectivePutContext
21
+ ? "Include protective-put hedge risks: premium decay/cost, imperfect hedge before the strike, liquidity, and opportunity cost. Long protective puts do not have short-option assignment risk."
22
+ : isCoveredCallContext
23
+ ? "Include covered-call sale risks: assignment risk, upside is capped at the strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and exit liquidity. Do not say max loss = premium or describe max loss as the option premium paid."
24
+ : "Include risk caveats: max loss = premium, IV crush risk, time decay (theta).";
25
+ const coveredCallFallback = isCoveredCallContext
26
+ ? `
27
+ Covered-call fallback:
28
+ - Treat this as selling covered calls against an existing ${s.symbol} share position, not buying calls.
29
+ - Briefly state that you are treating ${s.symbol} as the held ticker because the user phrased it as an existing holding. If they meant memory exposure or a different ticker, tell them to clarify and do not silently switch to another underlying.
30
+ - If retrieved contracts have zero bid/ask, zero open interest, or otherwise unusable live quotes, do not stop at "cannot rank."
31
+ - This fallback overrides the normal ranking/table requirements and the delta >= 0.20 filter.
32
+ - Do NOT conclude with "I cannot provide a recommendation" in this fallback case.
33
+ - The final answer MUST include:
34
+ - "Best action:" no trade unless the user's broker shows a real bid.
35
+ - "Conditional candidate:" a conditional limit-order candidate using the user's cost basis and catalyst context.
36
+ - If you cannot compute an exact premium, say "premium: use live broker bid/ask" rather than omitting the candidate.
37
+ - Choose a conditional candidate strike above cost basis that the user would accept for assignment, prefer near-term expirations around the catalyst, and label it as conditional on live bid/ask and premium collected.
38
+ - For the top pick, include the effective assignment sale price (strike + premium collected) and compare it with the ${s.costBasis !== undefined ? `$${s.costBasis}` : "user's"} cost basis.
39
+ - Include return-if-assigned when cost basis is available: (strike - cost basis + premium received) / cost basis.
40
+ - Verify bid/ask and open interest in the user's broker before trading, even when OC shows live values.
41
+ - Mention the catalyst only as event/sympathy risk; do not switch the underlying away from ${s.symbol}.
42
+ - Do not describe max loss as the option premium paid; covered-call sale risk is assignment risk, upside is capped at strike plus premium, share-price downside in the owned stock less premium received, IV/event risk, and poor exit liquidity.
43
+ - If the tool reports closed_market_or_stale_quotes, do not treat zero bid/ask as confirmed live illiquidity; say the chain was checked outside regular options trading and recheck after regular options trading opens.
44
+ - Required fallback format:
45
+ Interpretation: Treating ${s.symbol} as the held ticker because you phrased it as an existing position. If you meant ${s.symbol} as memory exposure or another ticker, clarify before trading.
46
+ Best action: ...
47
+ Conditional candidate: ...
48
+ Why: ...
49
+ Risk: ...
50
+ `
51
+ : "";
52
+ const coveredCallNoDataGuidance = isCoveredCallContext
53
+ ? "- For covered-call requests in that no-data fallback, explain how to evaluate covered calls: compare 1-week vs 2-week theta/gamma tradeoffs, use delta as an assignment-risk proxy, avoid strikes where assignment would violate the user's cost basis unless premium offsets it, calculate static premium yield and return-if-assigned, and flag catalyst/IV-crush risk."
54
+ : "";
55
+ const protectivePutFallback = isProtectivePutContext
56
+ ? `
57
+ Protective-put requirements:
58
+ - Treat this as buying puts to hedge an existing long ${s.symbol} share position, not buying calls.
59
+ - The final answer MUST discuss hedge floor, premium as a percent of position value, expiration fit, moneyness, and liquidity.
60
+ - If share quantity is available, translate shares into approximate contract count using 1 put contract per 100 shares.
61
+ - If share quantity is available, state total premium for the required number of contracts.
62
+ - For cost-sensitive requests, explain the tradeoff between cheaper lower-strike puts and weaker protection.
63
+ - Mention lower-cost alternatives such as collars or put spreads when outright put premium is high.
64
+ - Do not frame assignment risk like a short option sale.
65
+ `
66
+ : "";
6
67
  return {
7
68
  workflowType: "options_screener",
8
69
  steps: [
@@ -13,19 +74,23 @@ export function buildOptionsScreenerWorkflowDefinition(resolution) {
13
74
  promptStep("rank_and_present", "Rank and present top contracts", `Now rank and present the top ${contractType} for ${s.symbol}. You MUST produce a final text response — never end this turn with only tool calls.
14
75
 
15
76
  1. From the option chain data already fetched, select the top 3-5 contracts matching: ${s.moneynessPreference} strikes, DTE near ${s.dteTarget}, with ${s.liquidityMinimum}.
16
- 2. Rank by ${s.objective}: balance premium cost, delta exposure, and probability of profit. Only include contracts with |delta| >= 0.20.
17
- 3. Present a table: strike, expiry, premium, delta, IV, open interest, bid-ask spread.
77
+ 2. ${rankingInstruction}${maxPremiumInstruction}
78
+ 3. Present a table: strike, expiry, premium, delta, gamma, theta, vega, rho, IV, open interest, bid-ask spread.
18
79
  4. Explain why the #1 pick is ranked highest.
19
- 5. State all assumptions used (which were defaults vs user-specified vs saved preferences).
20
- 6. Include risk caveats: max loss = premium, IV crush risk, time decay (theta).
80
+ 5. Reproduce the exact Assumptions block from the initial prompt; keep the provenance label text intact and do not shorten it to "Assumptions:".
81
+ 6. State the requested option horizon in plain language (${horizonPhrase}) in the bottom line or table intro; use the user's horizon wording or approximate DTE window without forcing a fixed sentence.
82
+ 7. ${riskInstruction}
21
83
 
22
84
  If some or all of the option chain fetches returned "⚠ Options chain unavailable" or similar gaps, do NOT abort. Instead:
23
85
  - Rank and present whatever contracts you did retrieve from the successful fetches, even if fewer than 3.
24
- - If no chain data is usable at all, still produce a text response: reproduce the Assumptions block, state which expirations failed, and commit to a next step (e.g. "retry nearest monthly expiration", "use price/technicals instead"). Never end the turn with only tool calls.
86
+ - If no chain data is usable at all, still produce a text response: reproduce the Assumptions block, state which expirations failed, and give actionable fallback guidance for the requested DTE instead of ranking nonexistent contracts. Do not promise to retry later. Never end the turn with only tool calls.
87
+ ${coveredCallNoDataGuidance}
88
+ ${coveredCallFallback}
89
+ ${protectivePutFallback}
25
90
 
26
91
  Length constraints:
27
92
  - Max 1 sentence explaining the #1 pick.
28
- - Risk caveats: max 3 bullet points.
93
+ - Risk section: max 3 bullets.
29
94
  - Keep total response under 30 lines.`, {
30
95
  requiredInputs: ["option_chain"],
31
96
  expectedOutputs: ["ranked_contracts"],
@@ -33,12 +98,21 @@ Length constraints:
33
98
  ],
34
99
  };
35
100
  }
36
- /** @deprecated Use buildOptionsScreenerWorkflowDefinition instead */
37
- export function buildOptionsScreenerWorkflow(resolution) {
38
- const def = buildOptionsScreenerWorkflowDefinition(resolution);
39
- return {
40
- initialPrompt: def.steps[0].prompt,
41
- followUps: def.steps.slice(1).map((s) => s.prompt),
42
- };
101
+ function describeDteTarget(dteTarget) {
102
+ if (dteTarget === "25_to_45_days")
103
+ return "25-45 day target, roughly one month";
104
+ if (dteTarget === "7_to_14_days")
105
+ return "1-2 week horizon";
106
+ if (dteTarget === "0_to_7_days")
107
+ return "event-week or 0-7 day horizon";
108
+ if (dteTarget === "180_plus_days")
109
+ return "LEAPS or long-dated horizon";
110
+ const range = dteTarget.match(/^(\d+)_to_(\d+)_days$/);
111
+ if (range)
112
+ return `${range[1]}-${range[2]} day horizon`;
113
+ const plus = dteTarget.match(/^(\d+)_plus_days$/);
114
+ if (plus)
115
+ return `${plus[1]}+ day horizon`;
116
+ return `${dteTarget} horizon`;
43
117
  }
44
118
  //# sourceMappingURL=options-screener.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"options-screener.js","sourceRoot":"","sources":["../../src/workflows/options-screener.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAG5E,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,UAAU,sCAAsC,CAAC,UAAgD;IACrG,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC;IAC9B,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAElE,OAAO;QACL,YAAY,EAAE,kBAAkB;QAChC,KAAK,EAAE;YACL,UAAU,CAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,CAAC,UAAU,CAAC,EAAE;gBAC3F,cAAc,EAAE,CAAC,QAAQ,CAAC;gBAC1B,eAAe,EAAE,CAAC,cAAc,CAAC;aAClC,CAAC;YACF,UAAU,CAAC,kBAAkB,EAAE,gCAAgC,EAAE,gCAAgC,YAAY,QAAQ,CAAC,CAAC,MAAM;;wFAE3C,CAAC,CAAC,mBAAmB,sBAAsB,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,gBAAgB;aAC7J,CAAC,CAAC,SAAS;;;;;;;;;;;;;sCAac,EAAE;gBAChC,cAAc,EAAE,CAAC,cAAc,CAAC;gBAChC,eAAe,EAAE,CAAC,kBAAkB,CAAC;aACtC,CAAC;SACH;KACF,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,MAAM,UAAU,4BAA4B,CAAC,UAAgD;IAC3F,MAAM,GAAG,GAAG,sCAAsC,CAAC,UAAU,CAAC,CAAC;IAC/D,OAAO;QACL,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;QAClC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;KACnD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"options-screener.js","sourceRoot":"","sources":["../../src/workflows/options-screener.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAG5E,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,UAAU,sCAAsC,CACpD,UAAgD;IAEhD,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC;IAC9B,MAAM,sBAAsB,GAAG,CAAC,CAAC,cAAc,KAAK,gBAAgB,CAAC;IACrE,MAAM,YAAY,GAAG,CAAC,CAAC,SAAS,KAAK,SAAS,IAAI,CAAC,sBAAsB,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;IAC7F,MAAM,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IACrD,MAAM,oBAAoB,GACxB,CAAC,sBAAsB;QACvB,CAAC,CAAC,CAAC,cAAc,KAAK,cAAc;YAClC,CAAC,CAAC,SAAS,KAAK,SAAS;YACzB,CAAC,CAAC,CAAC,eAAe,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1C,MAAM,kBAAkB,GAAG,sBAAsB;QAC/C,CAAC,CAAC,mJAAmJ;QACrJ,CAAC,CAAC,oBAAoB;YACpB,CAAC,CAAC,gJAAgJ;YAClJ,CAAC,CAAC,WAAW,CAAC,CAAC,SAAS,iHAAiH,CAAC;IAC9I,MAAM,qBAAqB,GACzB,CAAC,CAAC,UAAU,KAAK,SAAS;QACxB,CAAC,CAAC,2DAA2D,CAAC,CAAC,UAAU,CAAC,cAAc,CAAC,OAAO,CAAC,sFAAsF;QACvL,CAAC,CAAC,EAAE,CAAC;IACT,MAAM,eAAe,GAAG,sBAAsB;QAC5C,CAAC,CAAC,4LAA4L;QAC9L,CAAC,CAAC,oBAAoB;YACpB,CAAC,CAAC,iRAAiR;YACnR,CAAC,CAAC,8EAA8E,CAAC;IACrF,MAAM,mBAAmB,GAAG,oBAAoB;QAC9C,CAAC,CAAC;;4DAEsD,CAAC,CAAC,MAAM;wCAC5B,CAAC,CAAC,MAAM;;;;;;;;;uHASuE,CAAC,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,QAAQ;;;6FAGlF,CAAC,CAAC,MAAM;;;;6BAIxE,CAAC,CAAC,MAAM,oFAAoF,CAAC,CAAC,MAAM;;;;;CAKhI;QACG,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,yBAAyB,GAAG,oBAAoB;QACpD,CAAC,CAAC,2WAA2W;QAC7W,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,qBAAqB,GAAG,sBAAsB;QAClD,CAAC,CAAC;;wDAEkD,CAAC,CAAC,MAAM;;;;;;;CAO/D;QACG,CAAC,CAAC,EAAE,CAAC;IAEP,OAAO;QACL,YAAY,EAAE,kBAAkB;QAChC,KAAK,EAAE;YACL,UAAU,CAAC,aAAa,EAAE,yBAAyB,EAAE,0BAA0B,CAAC,UAAU,CAAC,EAAE;gBAC3F,cAAc,EAAE,CAAC,QAAQ,CAAC;gBAC1B,eAAe,EAAE,CAAC,cAAc,CAAC;aAClC,CAAC;YACF,UAAU,CACR,kBAAkB,EAClB,gCAAgC,EAChC,gCAAgC,YAAY,QAAQ,CAAC,CAAC,MAAM;;wFAEoB,CAAC,CAAC,mBAAmB,sBAAsB,CAAC,CAAC,SAAS,UAAU,CAAC,CAAC,gBAAgB;KACrK,kBAAkB,GAAG,qBAAqB;;;;2DAIY,aAAa;KACnE,eAAe;;;;;EAKlB,yBAAyB;EACzB,mBAAmB;EACnB,qBAAqB;;;;;sCAKe,EAC9B;gBACE,cAAc,EAAE,CAAC,cAAc,CAAC;gBAChC,eAAe,EAAE,CAAC,kBAAkB,CAAC;aACtC,CACF;SACF;KACF,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,SAAS,KAAK,eAAe;QAAE,OAAO,qCAAqC,CAAC;IAChF,IAAI,SAAS,KAAK,cAAc;QAAE,OAAO,kBAAkB,CAAC;IAC5D,IAAI,SAAS,KAAK,aAAa;QAAE,OAAO,+BAA+B,CAAC;IACxE,IAAI,SAAS,KAAK,eAAe;QAAE,OAAO,6BAA6B,CAAC;IACxE,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;IACvD,IAAI,KAAK;QAAE,OAAO,GAAG,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC;IACxD,MAAM,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;IAClD,IAAI,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC;IAC3C,OAAO,GAAG,SAAS,UAAU,CAAC;AAChC,CAAC"}
@@ -1,6 +1,3 @@
1
1
  import type { PortfolioSlots, SlotResolution } from "../routing/types.js";
2
- import type { WorkflowPlan } from "./types.js";
3
2
  import type { WorkflowDefinition } from "../runtime/prompt-step.js";
4
3
  export declare function buildPortfolioWorkflowDefinition(resolution: SlotResolution<PortfolioSlots>): WorkflowDefinition;
5
- /** @deprecated Use buildPortfolioWorkflowDefinition instead */
6
- export declare function buildPortfolioWorkflow(resolution: SlotResolution<PortfolioSlots>): WorkflowPlan;
@@ -13,7 +13,8 @@ export function buildPortfolioWorkflowDefinition(resolution) {
13
13
  1. Use analyze_correlation across all ${s.positionCount} candidates to check for concentration risk.
14
14
  2. Use analyze_risk on each position for volatility and max drawdown.
15
15
  3. If correlation is too high (>0.7 between any pair), suggest a replacement to improve diversification.
16
- 4. Confirm the portfolio fits a ${s.riskProfile} risk profile with ${s.timeHorizon} horizon.`, {
16
+ 4. If any position's risk metrics undermine its intended role, lower its allocation, replace it with a role-equivalent candidate, or explicitly justify why it remains.
17
+ 5. Confirm the portfolio fits a ${s.riskProfile} risk profile with ${s.timeHorizon} horizon.`, {
17
18
  skippable: true,
18
19
  requiredInputs: ["candidate_positions"],
19
20
  expectedOutputs: ["risk_assessment"],
@@ -21,13 +22,16 @@ export function buildPortfolioWorkflowDefinition(resolution) {
21
22
  promptStep("synthesize", "Present final portfolio draft", `Present the final portfolio draft as a structured summary:
22
23
  - State all assumptions at the top (which parameters were defaults vs user-specified vs saved preferences).
23
24
  - Commit to the allocation: concrete percentages per position, not ranges.
24
- - Present an allocation table: symbol, allocation %, dollar amount ($${s.budget.toLocaleString("en-US")} total), and one-line analyst rationale per position.
25
+ - Present an allocation table: symbol, allocation %, dollar amount ($${s.budget.toLocaleString("en-US")} total), current price used, estimated shares, role, and one-line analyst rationale per position.
26
+ - Add a brief "Why this fits the horizon" summary explaining the growth/stability tradeoff for the ${s.timeHorizon} horizon.
25
27
  - Include overall portfolio risk summary: estimated volatility, diversification quality, largest single risk. State an invalidation condition for the draft.
28
+ - Include implementation notes: rebalance cadence, low-cost/liquid implementation, and tax/account caveats where relevant.
26
29
  - Suggest what to change for more growth or more safety.
27
30
 
28
31
  Length constraints:
29
- - Max 1 sentence of rationale per position in the allocation table.
32
+ - Max 1 sentence of rationale per position in the allocation table; do not paste company descriptions or issuer background.
30
33
  - Risk summary: max 3 bullet points.
34
+ - Implementation notes: max 3 bullets.
31
35
  - Growth/safety suggestions: max 2 bullet points each.
32
36
  - Keep total response under 40 lines.`, {
33
37
  requiredInputs: ["risk_assessment"],
@@ -36,12 +40,4 @@ Length constraints:
36
40
  ],
37
41
  };
38
42
  }
39
- /** @deprecated Use buildPortfolioWorkflowDefinition instead */
40
- export function buildPortfolioWorkflow(resolution) {
41
- const def = buildPortfolioWorkflowDefinition(resolution);
42
- return {
43
- initialPrompt: def.steps[0].prompt,
44
- followUps: def.steps.slice(1).map((s) => s.prompt),
45
- };
46
- }
47
43
  //# sourceMappingURL=portfolio-builder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"portfolio-builder.js","sourceRoot":"","sources":["../../src/workflows/portfolio-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,UAAU,gCAAgC,CAAC,UAA0C;IACzF,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC;IAE9B,OAAO;QACL,YAAY,EAAE,mBAAmB;QACjC,KAAK,EAAE;YACL,UAAU,CAAC,kBAAkB,EAAE,yCAAyC,EAAE,oBAAoB,CAAC,UAAU,CAAC,EAAE;gBAC1G,cAAc,EAAE,CAAC,SAAS,CAAC;gBAC3B,eAAe,EAAE,CAAC,qBAAqB,CAAC;aACzC,CAAC;YACF,UAAU,CAAC,aAAa,EAAE,iCAAiC,EAAE;wCAC3B,CAAC,CAAC,aAAa;;;kCAGrB,CAAC,CAAC,WAAW,sBAAsB,CAAC,CAAC,WAAW,WAAW,EAAE;gBACvF,SAAS,EAAE,IAAI;gBACf,cAAc,EAAE,CAAC,qBAAqB,CAAC;gBACvC,eAAe,EAAE,CAAC,iBAAiB,CAAC;aACrC,CAAC;YACF,UAAU,CAAC,YAAY,EAAE,+BAA+B,EAAE;;;uEAGO,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC;;;;;;;;sCAQjE,EAAE;gBAChC,cAAc,EAAE,CAAC,iBAAiB,CAAC;gBACnC,eAAe,EAAE,CAAC,mBAAmB,CAAC;aACvC,CAAC;SACH;KACF,CAAC;AACJ,CAAC;AAED,+DAA+D;AAC/D,MAAM,UAAU,sBAAsB,CAAC,UAA0C;IAC/E,MAAM,GAAG,GAAG,gCAAgC,CAAC,UAAU,CAAC,CAAC;IACzD,OAAO;QACL,aAAa,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM;QAClC,SAAS,EAAE,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;KACnD,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"portfolio-builder.js","sourceRoot":"","sources":["../../src/workflows/portfolio-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,MAAM,UAAU,gCAAgC,CAC9C,UAA0C;IAE1C,MAAM,CAAC,GAAG,UAAU,CAAC,QAAQ,CAAC;IAE9B,OAAO;QACL,YAAY,EAAE,mBAAmB;QACjC,KAAK,EAAE;YACL,UAAU,CACR,kBAAkB,EAClB,yCAAyC,EACzC,oBAAoB,CAAC,UAAU,CAAC,EAChC;gBACE,cAAc,EAAE,CAAC,SAAS,CAAC;gBAC3B,eAAe,EAAE,CAAC,qBAAqB,CAAC;aACzC,CACF;YACD,UAAU,CACR,aAAa,EACb,iCAAiC,EACjC;wCACgC,CAAC,CAAC,aAAa;;;;kCAIrB,CAAC,CAAC,WAAW,sBAAsB,CAAC,CAAC,WAAW,WAAW,EACrF;gBACE,SAAS,EAAE,IAAI;gBACf,cAAc,EAAE,CAAC,qBAAqB,CAAC;gBACvC,eAAe,EAAE,CAAC,iBAAiB,CAAC;aACrC,CACF;YACD,UAAU,CACR,YAAY,EACZ,+BAA+B,EAC/B;;;uEAG+D,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,OAAO,CAAC;qGACF,CAAC,CAAC,WAAW;;;;;;;;;;sCAU5E,EAC9B;gBACE,cAAc,EAAE,CAAC,iBAAiB,CAAC;gBACnC,eAAe,EAAE,CAAC,mBAAmB,CAAC;aACvC,CACF;SACF;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,82 @@
1
+ import type { AskUserHandler } from "../../src/types/index.js";
2
+
3
+ type AskUserParams = Parameters<AskUserHandler>[0];
4
+ type AskUserResult = Awaited<ReturnType<AskUserHandler>>;
5
+
6
+ export interface GuiAskUserPrompt extends AskUserParams {
7
+ id: string;
8
+ sessionId: string;
9
+ status: "pending" | "answered" | "cancelled";
10
+ answer: string | null;
11
+ }
12
+
13
+ interface PendingPrompt {
14
+ prompt: GuiAskUserPrompt;
15
+ resolve: (result: AskUserResult) => void;
16
+ }
17
+
18
+ export function createAskUserBridge({
19
+ broadcast,
20
+ getSessionId,
21
+ }: {
22
+ broadcast: (message: unknown) => void;
23
+ getSessionId: () => string;
24
+ }): {
25
+ ask: AskUserHandler;
26
+ answer: (id: string, answer: string) => boolean;
27
+ cancel: (id: string) => boolean;
28
+ getPrompts: () => GuiAskUserPrompt[];
29
+ } {
30
+ let nextId = 1;
31
+ const prompts = new Map<string, GuiAskUserPrompt>();
32
+ const pending = new Map<string, PendingPrompt>();
33
+
34
+ const ask: AskUserHandler = async (params) => {
35
+ const id = `ask-user-${Date.now()}-${nextId++}`;
36
+ const prompt: GuiAskUserPrompt = {
37
+ id,
38
+ sessionId: getSessionId(),
39
+ question: params.question,
40
+ questionType: params.questionType,
41
+ options: params.options,
42
+ placeholder: params.placeholder,
43
+ reason: params.reason,
44
+ status: "pending",
45
+ answer: null,
46
+ };
47
+ prompts.set(id, prompt);
48
+ broadcast({ type: "ask_user.prompt", prompt });
49
+
50
+ return new Promise<AskUserResult>((resolve) => {
51
+ pending.set(id, { prompt, resolve });
52
+ });
53
+ };
54
+
55
+ const resolvePrompt = (id: string, result: AskUserResult): boolean => {
56
+ const item = pending.get(id);
57
+ if (!item) return false;
58
+ pending.delete(id);
59
+ const prompt: GuiAskUserPrompt = {
60
+ ...item.prompt,
61
+ status: result.cancelled ? "cancelled" : "answered",
62
+ answer: result.cancelled ? null : (result.answer ?? null),
63
+ };
64
+ prompts.set(id, prompt);
65
+ broadcast({ type: "ask_user.resolved", prompt });
66
+ item.resolve(result);
67
+ return true;
68
+ };
69
+
70
+ return {
71
+ ask,
72
+ answer(id, answer) {
73
+ return resolvePrompt(id, { answer, cancelled: false });
74
+ },
75
+ cancel(id) {
76
+ return resolvePrompt(id, { answer: null, cancelled: true });
77
+ },
78
+ getPrompts() {
79
+ return [...prompts.values()];
80
+ },
81
+ };
82
+ }
@@ -0,0 +1,97 @@
1
+ import { runLocalAutomationHeartbeat } from "../../src/market-state/local-automation-service.js";
2
+ import { initDefaultDatabase } from "../../src/memory/sqlite.js";
3
+
4
+ export const DEFAULT_AUTOMATION_HEARTBEAT_MS = 60_000;
5
+ export const MIN_AUTOMATION_HEARTBEAT_MS = 5_000;
6
+
7
+ export function normalizeAutomationHeartbeatMs(raw: string | undefined): number {
8
+ if (raw == null || raw.trim() === "") return DEFAULT_AUTOMATION_HEARTBEAT_MS;
9
+ const parsed = Number(raw);
10
+ if (!Number.isFinite(parsed) || parsed < MIN_AUTOMATION_HEARTBEAT_MS)
11
+ return DEFAULT_AUTOMATION_HEARTBEAT_MS;
12
+ return Math.floor(parsed);
13
+ }
14
+
15
+ export function createAutomationHeartbeatRunner(
16
+ run: (checkAlerts: boolean) => Promise<void>,
17
+ ): (checkAlerts: boolean) => Promise<boolean> {
18
+ let inFlight = false;
19
+ return async (checkAlerts: boolean) => {
20
+ if (inFlight) return false;
21
+ inFlight = true;
22
+ try {
23
+ await run(checkAlerts);
24
+ return true;
25
+ } finally {
26
+ inFlight = false;
27
+ }
28
+ };
29
+ }
30
+
31
+ type IntervalHandle = ReturnType<typeof setInterval>;
32
+ type SetIntervalFn = (callback: () => void, ms: number) => IntervalHandle;
33
+ type ClearIntervalFn = (handle: IntervalHandle) => void;
34
+
35
+ export interface LocalAutomationHeartbeat {
36
+ start(): void;
37
+ stop(): void;
38
+ }
39
+
40
+ export interface LocalAutomationHeartbeatOptions {
41
+ role: string;
42
+ getSessionId: () => string;
43
+ intervalMs: number;
44
+ initDatabase?: typeof initDefaultDatabase;
45
+ runHeartbeat?: typeof runLocalAutomationHeartbeat;
46
+ setIntervalFn?: SetIntervalFn;
47
+ clearIntervalFn?: ClearIntervalFn;
48
+ warn?: (message: string) => void;
49
+ }
50
+
51
+ export function createLocalAutomationHeartbeat({
52
+ role,
53
+ getSessionId,
54
+ intervalMs,
55
+ initDatabase = initDefaultDatabase,
56
+ runHeartbeat = runLocalAutomationHeartbeat,
57
+ setIntervalFn = setInterval,
58
+ clearIntervalFn = clearInterval,
59
+ warn = (message) => console.warn(message),
60
+ }: LocalAutomationHeartbeatOptions): LocalAutomationHeartbeat {
61
+ let automationHeartbeat: IntervalHandle | null = null;
62
+
63
+ async function executeGuiAutomationHeartbeat(checkAlerts: boolean): Promise<void> {
64
+ const db = initDatabase();
65
+ try {
66
+ await runHeartbeat(db, {
67
+ ownerId: `gui:${getSessionId()}`,
68
+ ownerKind: "writer",
69
+ ttlSeconds: Math.max(90, Math.ceil((intervalMs * 2) / 1000)),
70
+ checkAlerts,
71
+ });
72
+ } catch (error) {
73
+ warn(
74
+ `Local automation heartbeat failed: ${error instanceof Error ? error.message : String(error)}`,
75
+ );
76
+ } finally {
77
+ db.close();
78
+ }
79
+ }
80
+
81
+ const runGuiAutomationHeartbeat = createAutomationHeartbeatRunner(executeGuiAutomationHeartbeat);
82
+
83
+ function start(): void {
84
+ if (role !== "writer") return;
85
+ void runGuiAutomationHeartbeat(true);
86
+ automationHeartbeat = setIntervalFn(() => void runGuiAutomationHeartbeat(true), intervalMs);
87
+ }
88
+
89
+ function stop(): void {
90
+ if (automationHeartbeat) {
91
+ clearIntervalFn(automationHeartbeat);
92
+ automationHeartbeat = null;
93
+ }
94
+ }
95
+
96
+ return { start, stop };
97
+ }
@@ -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
+ }
@@ -0,0 +1,5 @@
1
+ import { SessionManager } from "@earendil-works/pi-coding-agent";
2
+
3
+ export function createInitialGuiSessionManager(cwd: string, sessionDir?: string): SessionManager {
4
+ return SessionManager.create(cwd, sessionDir);
5
+ }