opencandle 0.1.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 (228) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +150 -0
  3. package/dist/analysts/orchestrator.d.ts +9 -0
  4. package/dist/analysts/orchestrator.js +100 -0
  5. package/dist/analysts/orchestrator.js.map +1 -0
  6. package/dist/cli.d.ts +2 -0
  7. package/dist/cli.js +122 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/config.d.ts +24 -0
  10. package/dist/config.js +76 -0
  11. package/dist/config.js.map +1 -0
  12. package/dist/index.d.ts +5 -0
  13. package/dist/index.js +6 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/infra/browser.d.ts +27 -0
  16. package/dist/infra/browser.js +102 -0
  17. package/dist/infra/browser.js.map +1 -0
  18. package/dist/infra/cache.d.ts +18 -0
  19. package/dist/infra/cache.js +42 -0
  20. package/dist/infra/cache.js.map +1 -0
  21. package/dist/infra/http-client.d.ts +13 -0
  22. package/dist/infra/http-client.js +54 -0
  23. package/dist/infra/http-client.js.map +1 -0
  24. package/dist/infra/index.d.ts +5 -0
  25. package/dist/infra/index.js +6 -0
  26. package/dist/infra/index.js.map +1 -0
  27. package/dist/infra/open-url.d.ts +1 -0
  28. package/dist/infra/open-url.js +29 -0
  29. package/dist/infra/open-url.js.map +1 -0
  30. package/dist/infra/opencandle-paths.d.ts +11 -0
  31. package/dist/infra/opencandle-paths.js +48 -0
  32. package/dist/infra/opencandle-paths.js.map +1 -0
  33. package/dist/infra/rate-limiter.d.ts +7 -0
  34. package/dist/infra/rate-limiter.js +38 -0
  35. package/dist/infra/rate-limiter.js.map +1 -0
  36. package/dist/memory/index.d.ts +5 -0
  37. package/dist/memory/index.js +5 -0
  38. package/dist/memory/index.js.map +1 -0
  39. package/dist/memory/preference-extractor.d.ts +6 -0
  40. package/dist/memory/preference-extractor.js +88 -0
  41. package/dist/memory/preference-extractor.js.map +1 -0
  42. package/dist/memory/retrieval.d.ts +8 -0
  43. package/dist/memory/retrieval.js +61 -0
  44. package/dist/memory/retrieval.js.map +1 -0
  45. package/dist/memory/sqlite.d.ts +5 -0
  46. package/dist/memory/sqlite.js +95 -0
  47. package/dist/memory/sqlite.js.map +1 -0
  48. package/dist/memory/storage.d.ts +47 -0
  49. package/dist/memory/storage.js +124 -0
  50. package/dist/memory/storage.js.map +1 -0
  51. package/dist/onboarding/state.d.ts +8 -0
  52. package/dist/onboarding/state.js +31 -0
  53. package/dist/onboarding/state.js.map +1 -0
  54. package/dist/pi/opencandle-extension.d.ts +2 -0
  55. package/dist/pi/opencandle-extension.js +205 -0
  56. package/dist/pi/opencandle-extension.js.map +1 -0
  57. package/dist/pi/session.d.ts +10 -0
  58. package/dist/pi/session.js +28 -0
  59. package/dist/pi/session.js.map +1 -0
  60. package/dist/pi/setup.d.ts +10 -0
  61. package/dist/pi/setup.js +357 -0
  62. package/dist/pi/setup.js.map +1 -0
  63. package/dist/pi/tool-adapter.d.ts +5 -0
  64. package/dist/pi/tool-adapter.js +17 -0
  65. package/dist/pi/tool-adapter.js.map +1 -0
  66. package/dist/prompts/workflow-prompts.d.ts +9 -0
  67. package/dist/prompts/workflow-prompts.js +202 -0
  68. package/dist/prompts/workflow-prompts.js.map +1 -0
  69. package/dist/providers/alpha-vantage.d.ts +4 -0
  70. package/dist/providers/alpha-vantage.js +122 -0
  71. package/dist/providers/alpha-vantage.js.map +1 -0
  72. package/dist/providers/coingecko.d.ts +3 -0
  73. package/dist/providers/coingecko.js +53 -0
  74. package/dist/providers/coingecko.js.map +1 -0
  75. package/dist/providers/fear-greed.d.ts +2 -0
  76. package/dist/providers/fear-greed.js +26 -0
  77. package/dist/providers/fear-greed.js.map +1 -0
  78. package/dist/providers/fred.d.ts +2 -0
  79. package/dist/providers/fred.js +37 -0
  80. package/dist/providers/fred.js.map +1 -0
  81. package/dist/providers/index.d.ts +7 -0
  82. package/dist/providers/index.js +8 -0
  83. package/dist/providers/index.js.map +1 -0
  84. package/dist/providers/reddit.d.ts +9 -0
  85. package/dist/providers/reddit.js +69 -0
  86. package/dist/providers/reddit.js.map +1 -0
  87. package/dist/providers/sec-edgar.d.ts +9 -0
  88. package/dist/providers/sec-edgar.js +59 -0
  89. package/dist/providers/sec-edgar.js.map +1 -0
  90. package/dist/providers/yahoo-finance.d.ts +17 -0
  91. package/dist/providers/yahoo-finance.js +231 -0
  92. package/dist/providers/yahoo-finance.js.map +1 -0
  93. package/dist/routing/classify-intent.d.ts +2 -0
  94. package/dist/routing/classify-intent.js +147 -0
  95. package/dist/routing/classify-intent.js.map +1 -0
  96. package/dist/routing/defaults.d.ts +7 -0
  97. package/dist/routing/defaults.js +26 -0
  98. package/dist/routing/defaults.js.map +1 -0
  99. package/dist/routing/entity-extractor.d.ts +3 -0
  100. package/dist/routing/entity-extractor.js +130 -0
  101. package/dist/routing/entity-extractor.js.map +1 -0
  102. package/dist/routing/index.d.ts +5 -0
  103. package/dist/routing/index.js +5 -0
  104. package/dist/routing/index.js.map +1 -0
  105. package/dist/routing/slot-resolver.d.ts +15 -0
  106. package/dist/routing/slot-resolver.js +120 -0
  107. package/dist/routing/slot-resolver.js.map +1 -0
  108. package/dist/routing/types.d.ts +51 -0
  109. package/dist/routing/types.js +2 -0
  110. package/dist/routing/types.js.map +1 -0
  111. package/dist/system-prompt.d.ts +1 -0
  112. package/dist/system-prompt.js +49 -0
  113. package/dist/system-prompt.js.map +1 -0
  114. package/dist/tool-kit.d.ts +19 -0
  115. package/dist/tool-kit.js +20 -0
  116. package/dist/tool-kit.js.map +1 -0
  117. package/dist/tools/fundamentals/company-overview.d.ts +7 -0
  118. package/dist/tools/fundamentals/company-overview.js +40 -0
  119. package/dist/tools/fundamentals/company-overview.js.map +1 -0
  120. package/dist/tools/fundamentals/comps.d.ts +22 -0
  121. package/dist/tools/fundamentals/comps.js +110 -0
  122. package/dist/tools/fundamentals/comps.js.map +1 -0
  123. package/dist/tools/fundamentals/dcf.d.ts +46 -0
  124. package/dist/tools/fundamentals/dcf.js +184 -0
  125. package/dist/tools/fundamentals/dcf.js.map +1 -0
  126. package/dist/tools/fundamentals/earnings.d.ts +7 -0
  127. package/dist/tools/fundamentals/earnings.js +33 -0
  128. package/dist/tools/fundamentals/earnings.js.map +1 -0
  129. package/dist/tools/fundamentals/financials.d.ts +7 -0
  130. package/dist/tools/fundamentals/financials.js +37 -0
  131. package/dist/tools/fundamentals/financials.js.map +1 -0
  132. package/dist/tools/fundamentals/sec-filings.d.ts +8 -0
  133. package/dist/tools/fundamentals/sec-filings.js +40 -0
  134. package/dist/tools/fundamentals/sec-filings.js.map +1 -0
  135. package/dist/tools/index.d.ts +2 -0
  136. package/dist/tools/index.js +51 -0
  137. package/dist/tools/index.js.map +1 -0
  138. package/dist/tools/macro/fear-greed.d.ts +5 -0
  139. package/dist/tools/macro/fear-greed.js +26 -0
  140. package/dist/tools/macro/fear-greed.js.map +1 -0
  141. package/dist/tools/macro/fred-data.d.ts +8 -0
  142. package/dist/tools/macro/fred-data.js +34 -0
  143. package/dist/tools/macro/fred-data.js.map +1 -0
  144. package/dist/tools/market/crypto-history.d.ts +8 -0
  145. package/dist/tools/market/crypto-history.js +32 -0
  146. package/dist/tools/market/crypto-history.js.map +1 -0
  147. package/dist/tools/market/crypto-price.d.ts +7 -0
  148. package/dist/tools/market/crypto-price.js +42 -0
  149. package/dist/tools/market/crypto-price.js.map +1 -0
  150. package/dist/tools/market/search-ticker.d.ts +6 -0
  151. package/dist/tools/market/search-ticker.js +33 -0
  152. package/dist/tools/market/search-ticker.js.map +1 -0
  153. package/dist/tools/market/stock-history.d.ts +9 -0
  154. package/dist/tools/market/stock-history.js +35 -0
  155. package/dist/tools/market/stock-history.js.map +1 -0
  156. package/dist/tools/market/stock-quote.d.ts +7 -0
  157. package/dist/tools/market/stock-quote.js +32 -0
  158. package/dist/tools/market/stock-quote.js.map +1 -0
  159. package/dist/tools/options/greeks.d.ts +14 -0
  160. package/dist/tools/options/greeks.js +65 -0
  161. package/dist/tools/options/greeks.js.map +1 -0
  162. package/dist/tools/options/option-chain.d.ts +9 -0
  163. package/dist/tools/options/option-chain.js +61 -0
  164. package/dist/tools/options/option-chain.js.map +1 -0
  165. package/dist/tools/portfolio/correlation.d.ts +10 -0
  166. package/dist/tools/portfolio/correlation.js +122 -0
  167. package/dist/tools/portfolio/correlation.js.map +1 -0
  168. package/dist/tools/portfolio/predictions.d.ts +49 -0
  169. package/dist/tools/portfolio/predictions.js +171 -0
  170. package/dist/tools/portfolio/predictions.js.map +1 -0
  171. package/dist/tools/portfolio/risk-analysis.d.ts +12 -0
  172. package/dist/tools/portfolio/risk-analysis.js +113 -0
  173. package/dist/tools/portfolio/risk-analysis.js.map +1 -0
  174. package/dist/tools/portfolio/tracker.d.ts +10 -0
  175. package/dist/tools/portfolio/tracker.js +125 -0
  176. package/dist/tools/portfolio/tracker.js.map +1 -0
  177. package/dist/tools/portfolio/watchlist.d.ts +10 -0
  178. package/dist/tools/portfolio/watchlist.js +120 -0
  179. package/dist/tools/portfolio/watchlist.js.map +1 -0
  180. package/dist/tools/sentiment/news-sentiment.d.ts +7 -0
  181. package/dist/tools/sentiment/news-sentiment.js +57 -0
  182. package/dist/tools/sentiment/news-sentiment.js.map +1 -0
  183. package/dist/tools/sentiment/reddit-sentiment.d.ts +8 -0
  184. package/dist/tools/sentiment/reddit-sentiment.js +37 -0
  185. package/dist/tools/sentiment/reddit-sentiment.js.map +1 -0
  186. package/dist/tools/technical/backtest.d.ts +26 -0
  187. package/dist/tools/technical/backtest.js +190 -0
  188. package/dist/tools/technical/backtest.js.map +1 -0
  189. package/dist/tools/technical/indicators.d.ts +23 -0
  190. package/dist/tools/technical/indicators.js +212 -0
  191. package/dist/tools/technical/indicators.js.map +1 -0
  192. package/dist/types/fundamentals.d.ts +44 -0
  193. package/dist/types/fundamentals.js +2 -0
  194. package/dist/types/fundamentals.js.map +1 -0
  195. package/dist/types/index.d.ts +7 -0
  196. package/dist/types/index.js +2 -0
  197. package/dist/types/index.js.map +1 -0
  198. package/dist/types/macro.d.ts +24 -0
  199. package/dist/types/macro.js +14 -0
  200. package/dist/types/macro.js.map +1 -0
  201. package/dist/types/market.d.ts +41 -0
  202. package/dist/types/market.js +2 -0
  203. package/dist/types/market.js.map +1 -0
  204. package/dist/types/options.d.ts +33 -0
  205. package/dist/types/options.js +2 -0
  206. package/dist/types/options.js.map +1 -0
  207. package/dist/types/portfolio.d.ts +44 -0
  208. package/dist/types/portfolio.js +2 -0
  209. package/dist/types/portfolio.js.map +1 -0
  210. package/dist/types/sentiment.d.ts +24 -0
  211. package/dist/types/sentiment.js +2 -0
  212. package/dist/types/sentiment.js.map +1 -0
  213. package/dist/workflows/compare-assets.d.ts +3 -0
  214. package/dist/workflows/compare-assets.js +14 -0
  215. package/dist/workflows/compare-assets.js.map +1 -0
  216. package/dist/workflows/index.d.ts +4 -0
  217. package/dist/workflows/index.js +4 -0
  218. package/dist/workflows/index.js.map +1 -0
  219. package/dist/workflows/options-screener.d.ts +3 -0
  220. package/dist/workflows/options-screener.js +22 -0
  221. package/dist/workflows/options-screener.js.map +1 -0
  222. package/dist/workflows/portfolio-builder.d.ts +3 -0
  223. package/dist/workflows/portfolio-builder.js +26 -0
  224. package/dist/workflows/portfolio-builder.js.map +1 -0
  225. package/dist/workflows/types.d.ts +4 -0
  226. package/dist/workflows/types.js +2 -0
  227. package/dist/workflows/types.js.map +1 -0
  228. package/package.json +97 -0
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Shared stealth browser infrastructure using Camoufox (anti-detection Firefox).
3
+ * Provides a singleton browser instance that any tool can use for scraping.
4
+ *
5
+ * Usage:
6
+ * import { StealthBrowser } from "../infra/browser.js";
7
+ * const data = await StealthBrowser.fetchJson<MyType>(url);
8
+ * const result = await StealthBrowser.evaluate(url, () => document.title);
9
+ */
10
+ import { Camoufox } from "camoufox-js";
11
+ let browser = null;
12
+ let page = null;
13
+ let launching = null;
14
+ let pageQueue = Promise.resolve();
15
+ async function ensureBrowser() {
16
+ if (page && browser?.isConnected())
17
+ return page;
18
+ // Prevent concurrent launches
19
+ if (launching) {
20
+ await launching;
21
+ if (page && browser?.isConnected())
22
+ return page;
23
+ }
24
+ launching = (async () => {
25
+ const b = await Camoufox({ headless: true });
26
+ browser = b;
27
+ page = await b.newPage();
28
+ })();
29
+ await launching;
30
+ launching = null;
31
+ return page;
32
+ }
33
+ async function withPage(fn) {
34
+ let resolve;
35
+ const next = new Promise((r) => { resolve = r; });
36
+ const prev = pageQueue;
37
+ pageQueue = next;
38
+ await prev;
39
+ try {
40
+ const p = await ensureBrowser();
41
+ return await fn(p);
42
+ }
43
+ finally {
44
+ resolve();
45
+ }
46
+ }
47
+ export const StealthBrowser = {
48
+ /**
49
+ * Navigate to a URL, run a JS function in the page context, and return the result.
50
+ */
51
+ async evaluate(url, fn) {
52
+ return withPage(async (p) => {
53
+ await p.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
54
+ return p.evaluate(fn);
55
+ });
56
+ },
57
+ /**
58
+ * Fetch JSON from a URL using the browser's session (cookies, TLS fingerprint).
59
+ * Useful for APIs that block Node.js fetch but allow real browsers.
60
+ */
61
+ async fetchJson(url, options) {
62
+ return withPage(async (p) => {
63
+ const result = await p.evaluate(async (fetchUrl) => {
64
+ const res = await fetch(fetchUrl, { credentials: "include" });
65
+ if (!res.ok)
66
+ throw new Error(`HTTP ${res.status}`);
67
+ return res.json();
68
+ }, url);
69
+ return result;
70
+ });
71
+ },
72
+ /**
73
+ * Run a custom async function in the browser page context.
74
+ * The page must already be on a relevant domain for cookies to work.
75
+ */
76
+ async run(fn) {
77
+ return withPage(async (p) => fn(p));
78
+ },
79
+ /**
80
+ * Navigate to a URL and establish session cookies for that domain.
81
+ */
82
+ async initSession(url) {
83
+ return withPage(async (p) => {
84
+ await p.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
85
+ });
86
+ },
87
+ /**
88
+ * Close the browser. It will be re-launched on next use.
89
+ */
90
+ async close() {
91
+ if (browser) {
92
+ await browser.close().catch(() => { });
93
+ browser = null;
94
+ page = null;
95
+ }
96
+ },
97
+ };
98
+ // Clean up on process exit
99
+ process.on("exit", () => {
100
+ browser?.close().catch(() => { });
101
+ });
102
+ //# sourceMappingURL=browser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.js","sourceRoot":"","sources":["../../src/infra/browser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ export declare class Cache {
2
+ private store;
3
+ get<T>(key: string): T | undefined;
4
+ set<T>(key: string, value: T, ttlMs: number): void;
5
+ invalidate(pattern: string): void;
6
+ clear(): void;
7
+ get size(): number;
8
+ }
9
+ export declare const cache: Cache;
10
+ export declare const TTL: {
11
+ readonly QUOTE: 60000;
12
+ readonly HISTORY: 3600000;
13
+ readonly FUNDAMENTALS: 86400000;
14
+ readonly MACRO: 3600000;
15
+ readonly SENTIMENT: 300000;
16
+ readonly OPTIONS_CHAIN: 120000;
17
+ readonly CRUMB: 900000;
18
+ };
@@ -0,0 +1,42 @@
1
+ export class Cache {
2
+ store = new Map();
3
+ get(key) {
4
+ const entry = this.store.get(key);
5
+ if (!entry)
6
+ return undefined;
7
+ if (Date.now() > entry.expiresAt) {
8
+ this.store.delete(key);
9
+ return undefined;
10
+ }
11
+ return entry.value;
12
+ }
13
+ set(key, value, ttlMs) {
14
+ this.store.set(key, { value, expiresAt: Date.now() + ttlMs });
15
+ }
16
+ invalidate(pattern) {
17
+ for (const key of this.store.keys()) {
18
+ if (key.includes(pattern)) {
19
+ this.store.delete(key);
20
+ }
21
+ }
22
+ }
23
+ clear() {
24
+ this.store.clear();
25
+ }
26
+ get size() {
27
+ return this.store.size;
28
+ }
29
+ }
30
+ // Shared cache instance
31
+ export const cache = new Cache();
32
+ // Default TTLs
33
+ export const TTL = {
34
+ QUOTE: 60_000, // 1 minute
35
+ HISTORY: 3_600_000, // 1 hour
36
+ FUNDAMENTALS: 86_400_000, // 24 hours
37
+ MACRO: 3_600_000, // 1 hour
38
+ SENTIMENT: 300_000, // 5 minutes
39
+ OPTIONS_CHAIN: 120_000, // 2 minutes
40
+ CRUMB: 900_000, // 15 minutes
41
+ };
42
+ //# sourceMappingURL=cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.js","sourceRoot":"","sources":["../../src/infra/cache.ts"],"names":[],"mappings":"AAKA,MAAM,OAAO,KAAK;IACR,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;IAEvD,GAAG,CAAI,GAAW;QAChB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK;YAAE,OAAO,SAAS,CAAC;QAC7B,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACvB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,OAAO,KAAK,CAAC,KAAU,CAAC;IAC1B,CAAC;IAED,GAAG,CAAI,GAAW,EAAE,KAAQ,EAAE,KAAa;QACzC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,UAAU,CAAC,OAAe;QACxB,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACpC,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YACzB,CAAC;QACH,CAAC;IACH,CAAC;IAED,KAAK;QACH,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC;IACrB,CAAC;IAED,IAAI,IAAI;QACN,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC;IACzB,CAAC;CACF;AAED,wBAAwB;AACxB,MAAM,CAAC,MAAM,KAAK,GAAG,IAAI,KAAK,EAAE,CAAC;AAEjC,eAAe;AACf,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,KAAK,EAAE,MAAM,EAAU,WAAW;IAClC,OAAO,EAAE,SAAS,EAAK,SAAS;IAChC,YAAY,EAAE,UAAU,EAAE,WAAW;IACrC,KAAK,EAAE,SAAS,EAAO,SAAS;IAChC,SAAS,EAAE,OAAO,EAAK,YAAY;IACnC,aAAa,EAAE,OAAO,EAAE,YAAY;IACpC,KAAK,EAAE,OAAO,EAAS,aAAa;CAC5B,CAAC"}
@@ -0,0 +1,13 @@
1
+ export interface HttpClientOptions {
2
+ timeoutMs?: number;
3
+ maxRetries?: number;
4
+ retryDelayMs?: number;
5
+ headers?: Record<string, string>;
6
+ }
7
+ export declare class HttpError extends Error {
8
+ readonly status: number;
9
+ readonly statusText: string;
10
+ readonly body: string;
11
+ constructor(status: number, statusText: string, body: string);
12
+ }
13
+ export declare function httpGet<T>(url: string, options?: HttpClientOptions): Promise<T>;
@@ -0,0 +1,54 @@
1
+ const DEFAULT_OPTIONS = {
2
+ timeoutMs: 10_000,
3
+ maxRetries: 2,
4
+ retryDelayMs: 1_000,
5
+ headers: {},
6
+ };
7
+ export class HttpError extends Error {
8
+ status;
9
+ statusText;
10
+ body;
11
+ constructor(status, statusText, body) {
12
+ super(`HTTP ${status} ${statusText}`);
13
+ this.status = status;
14
+ this.statusText = statusText;
15
+ this.body = body;
16
+ this.name = "HttpError";
17
+ }
18
+ }
19
+ export async function httpGet(url, options = {}) {
20
+ const opts = { ...DEFAULT_OPTIONS, ...options };
21
+ let lastError;
22
+ for (let attempt = 0; attempt <= opts.maxRetries; attempt++) {
23
+ if (attempt > 0) {
24
+ await sleep(opts.retryDelayMs * attempt);
25
+ }
26
+ const controller = new AbortController();
27
+ const timeout = setTimeout(() => controller.abort(), opts.timeoutMs);
28
+ try {
29
+ const response = await fetch(url, {
30
+ signal: controller.signal,
31
+ headers: opts.headers,
32
+ });
33
+ if (!response.ok) {
34
+ const body = await response.text().catch(() => "");
35
+ throw new HttpError(response.status, response.statusText, body);
36
+ }
37
+ return (await response.json());
38
+ }
39
+ catch (error) {
40
+ lastError = error;
41
+ if (error instanceof HttpError && error.status >= 400 && error.status < 500) {
42
+ throw error; // Don't retry client errors
43
+ }
44
+ }
45
+ finally {
46
+ clearTimeout(timeout);
47
+ }
48
+ }
49
+ throw lastError;
50
+ }
51
+ function sleep(ms) {
52
+ return new Promise((resolve) => setTimeout(resolve, ms));
53
+ }
54
+ //# sourceMappingURL=http-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http-client.js","sourceRoot":"","sources":["../../src/infra/http-client.ts"],"names":[],"mappings":"AAOA,MAAM,eAAe,GAAgC;IACnD,SAAS,EAAE,MAAM;IACjB,UAAU,EAAE,CAAC;IACb,YAAY,EAAE,KAAK;IACnB,OAAO,EAAE,EAAE;CACZ,CAAC;AAEF,MAAM,OAAO,SAAU,SAAQ,KAAK;IAEhB;IACA;IACA;IAHlB,YACkB,MAAc,EACd,UAAkB,EAClB,IAAY;QAE5B,KAAK,CAAC,QAAQ,MAAM,IAAI,UAAU,EAAE,CAAC,CAAC;QAJtB,WAAM,GAAN,MAAM,CAAQ;QACd,eAAU,GAAV,UAAU,CAAQ;QAClB,SAAI,GAAJ,IAAI,CAAQ;QAG5B,IAAI,CAAC,IAAI,GAAG,WAAW,CAAC;IAC1B,CAAC;CACF;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAC3B,GAAW,EACX,UAA6B,EAAE;IAE/B,MAAM,IAAI,GAAG,EAAE,GAAG,eAAe,EAAE,GAAG,OAAO,EAAE,CAAC;IAChD,IAAI,SAA4B,CAAC;IAEjC,KAAK,IAAI,OAAO,GAAG,CAAC,EAAE,OAAO,IAAI,IAAI,CAAC,UAAU,EAAE,OAAO,EAAE,EAAE,CAAC;QAC5D,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;YAChB,MAAM,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,OAAO,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,MAAM,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAErE,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;gBAChC,MAAM,EAAE,UAAU,CAAC,MAAM;gBACzB,OAAO,EAAE,IAAI,CAAC,OAAO;aACtB,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;gBACnD,MAAM,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;YAClE,CAAC;YAED,OAAO,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAM,CAAC;QACtC,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,SAAS,GAAG,KAAc,CAAC;YAC3B,IAAI,KAAK,YAAY,SAAS,IAAI,KAAK,CAAC,MAAM,IAAI,GAAG,IAAI,KAAK,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;gBAC5E,MAAM,KAAK,CAAC,CAAC,4BAA4B;YAC3C,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,YAAY,CAAC,OAAO,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,MAAM,SAAU,CAAC;AACnB,CAAC;AAED,SAAS,KAAK,CAAC,EAAU;IACvB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { Cache, cache, TTL } from "./cache.js";
2
+ export { RateLimiter, rateLimiter } from "./rate-limiter.js";
3
+ export { httpGet, HttpError, type HttpClientOptions } from "./http-client.js";
4
+ export { StealthBrowser } from "./browser.js";
5
+ export { getOpenCandleHomeDir, ensureOpenCandleHomeDir, resolveOpenCandlePath, ensureParentDir, getWatchlistPath, getPortfolioPath, getPredictionsPath, getConfigPath, getOnboardingPath, getStateDbPath, getLogsDir, } from "./opencandle-paths.js";
@@ -0,0 +1,6 @@
1
+ export { Cache, cache, TTL } from "./cache.js";
2
+ export { RateLimiter, rateLimiter } from "./rate-limiter.js";
3
+ export { httpGet, HttpError } from "./http-client.js";
4
+ export { StealthBrowser } from "./browser.js";
5
+ export { getOpenCandleHomeDir, ensureOpenCandleHomeDir, resolveOpenCandlePath, ensureParentDir, getWatchlistPath, getPortfolioPath, getPredictionsPath, getConfigPath, getOnboardingPath, getStateDbPath, getLogsDir, } from "./opencandle-paths.js";
6
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/infra/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EACL,oBAAoB,EACpB,uBAAuB,EACvB,qBAAqB,EACrB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,kBAAkB,EAClB,aAAa,EACb,iBAAiB,EACjB,cAAc,EACd,UAAU,GACX,MAAM,uBAAuB,CAAC"}
@@ -0,0 +1 @@
1
+ export declare function openInBrowser(url: string): Promise<void>;
@@ -0,0 +1,29 @@
1
+ import { execFile } from "node:child_process";
2
+ export function openInBrowser(url) {
3
+ return new Promise((resolve, reject) => {
4
+ let command;
5
+ let args;
6
+ if (process.platform === "darwin") {
7
+ command = "open";
8
+ args = [url];
9
+ }
10
+ else if (process.platform === "win32") {
11
+ command = "cmd";
12
+ args = ["/c", "start", "", url];
13
+ }
14
+ else {
15
+ command = "xdg-open";
16
+ args = [url];
17
+ }
18
+ const child = execFile(command, args, { timeout: 5000 }, (error) => {
19
+ if (error) {
20
+ reject(error);
21
+ }
22
+ else {
23
+ resolve();
24
+ }
25
+ });
26
+ child.unref();
27
+ });
28
+ }
29
+ //# sourceMappingURL=open-url.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"open-url.js","sourceRoot":"","sources":["../../src/infra/open-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,MAAM,UAAU,aAAa,CAAC,GAAW;IACvC,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAC3C,IAAI,OAAe,CAAC;QACpB,IAAI,IAAc,CAAC;QAEnB,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;YAClC,OAAO,GAAG,MAAM,CAAC;YACjB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YACxC,OAAO,GAAG,KAAK,CAAC;YAChB,IAAI,GAAG,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;QAClC,CAAC;aAAM,CAAC;YACN,OAAO,GAAG,UAAU,CAAC;YACrB,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QACf,CAAC;QAED,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE;YACjE,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,CAAC,KAAK,CAAC,CAAC;YAChB,CAAC;iBAAM,CAAC;gBACN,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QACH,KAAK,CAAC,KAAK,EAAE,CAAC;IAChB,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,11 @@
1
+ export declare function getOpenCandleHomeDir(): string;
2
+ export declare function ensureOpenCandleHomeDir(): string;
3
+ export declare function resolveOpenCandlePath(...segments: string[]): string;
4
+ export declare function ensureParentDir(path: string): string;
5
+ export declare function getWatchlistPath(): string;
6
+ export declare function getPortfolioPath(): string;
7
+ export declare function getPredictionsPath(): string;
8
+ export declare function getConfigPath(): string;
9
+ export declare function getOnboardingPath(): string;
10
+ export declare function getStateDbPath(): string;
11
+ export declare function getLogsDir(): string;
@@ -0,0 +1,48 @@
1
+ import { existsSync, mkdirSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ const OPENCANDLE_HOME_ENV = "OPENCANDLE_HOME";
5
+ function ensureDir(path) {
6
+ if (!existsSync(path)) {
7
+ mkdirSync(path, { recursive: true });
8
+ }
9
+ }
10
+ export function getOpenCandleHomeDir() {
11
+ const override = process.env[OPENCANDLE_HOME_ENV];
12
+ return override ? resolve(override) : join(homedir(), ".opencandle");
13
+ }
14
+ export function ensureOpenCandleHomeDir() {
15
+ const home = getOpenCandleHomeDir();
16
+ ensureDir(home);
17
+ return home;
18
+ }
19
+ export function resolveOpenCandlePath(...segments) {
20
+ return join(getOpenCandleHomeDir(), ...segments);
21
+ }
22
+ export function ensureParentDir(path) {
23
+ const parent = dirname(path);
24
+ ensureDir(parent);
25
+ return parent;
26
+ }
27
+ export function getWatchlistPath() {
28
+ return resolveOpenCandlePath("watchlist.json");
29
+ }
30
+ export function getPortfolioPath() {
31
+ return resolveOpenCandlePath("portfolio.json");
32
+ }
33
+ export function getPredictionsPath() {
34
+ return resolveOpenCandlePath("predictions.json");
35
+ }
36
+ export function getConfigPath() {
37
+ return resolveOpenCandlePath("config.json");
38
+ }
39
+ export function getOnboardingPath() {
40
+ return resolveOpenCandlePath("onboarding.json");
41
+ }
42
+ export function getStateDbPath() {
43
+ return resolveOpenCandlePath("state.db");
44
+ }
45
+ export function getLogsDir() {
46
+ return resolveOpenCandlePath("logs");
47
+ }
48
+ //# sourceMappingURL=opencandle-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opencandle-paths.js","sourceRoot":"","sources":["../../src/infra/opencandle-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEnD,MAAM,mBAAmB,GAAG,iBAAiB,CAAC;AAE9C,SAAS,SAAS,CAAC,IAAY;IAC7B,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACtB,SAAS,CAAC,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACvC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC,CAAC;IAClD,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,aAAa,CAAC,CAAC;AACvE,CAAC;AAED,MAAM,UAAU,uBAAuB;IACrC,MAAM,IAAI,GAAG,oBAAoB,EAAE,CAAC;IACpC,SAAS,CAAC,IAAI,CAAC,CAAC;IAChB,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,GAAG,QAAkB;IACzD,OAAO,IAAI,CAAC,oBAAoB,EAAE,EAAE,GAAG,QAAQ,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,CAAC,MAAM,CAAC,CAAC;IAClB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,OAAO,qBAAqB,CAAC,gBAAgB,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,UAAU,kBAAkB;IAChC,OAAO,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;AACnD,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,qBAAqB,CAAC,aAAa,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,iBAAiB;IAC/B,OAAO,qBAAqB,CAAC,iBAAiB,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,cAAc;IAC5B,OAAO,qBAAqB,CAAC,UAAU,CAAC,CAAC;AAC3C,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,OAAO,qBAAqB,CAAC,MAAM,CAAC,CAAC;AACvC,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare class RateLimiter {
2
+ private buckets;
3
+ configure(provider: string, maxTokens: number, refillRate: number): void;
4
+ acquire(provider: string): Promise<void>;
5
+ private refill;
6
+ }
7
+ export declare const rateLimiter: RateLimiter;
@@ -0,0 +1,38 @@
1
+ export class RateLimiter {
2
+ buckets = new Map();
3
+ configure(provider, maxTokens, refillRate) {
4
+ this.buckets.set(provider, {
5
+ tokens: maxTokens,
6
+ lastRefill: Date.now(),
7
+ config: { maxTokens, refillRate },
8
+ });
9
+ }
10
+ async acquire(provider) {
11
+ const bucket = this.buckets.get(provider);
12
+ if (!bucket)
13
+ return; // No limit configured
14
+ this.refill(bucket);
15
+ if (bucket.tokens >= 1) {
16
+ bucket.tokens -= 1;
17
+ return;
18
+ }
19
+ // Wait until a token is available
20
+ const waitMs = ((1 - bucket.tokens) / bucket.config.refillRate) * 1000;
21
+ await new Promise((resolve) => setTimeout(resolve, Math.ceil(waitMs)));
22
+ this.refill(bucket);
23
+ bucket.tokens -= 1;
24
+ }
25
+ refill(bucket) {
26
+ const now = Date.now();
27
+ const elapsed = (now - bucket.lastRefill) / 1000;
28
+ bucket.tokens = Math.min(bucket.config.maxTokens, bucket.tokens + elapsed * bucket.config.refillRate);
29
+ bucket.lastRefill = now;
30
+ }
31
+ }
32
+ // Shared instance with default provider limits
33
+ export const rateLimiter = new RateLimiter();
34
+ rateLimiter.configure("yahoo", 5, 5); // 5 req/s
35
+ rateLimiter.configure("coingecko", 10, 0.167); // 10 req/min
36
+ rateLimiter.configure("alphavantage", 5, 0.083); // 5 req/min (free tier)
37
+ rateLimiter.configure("fred", 120, 2); // 120 req/min
38
+ //# sourceMappingURL=rate-limiter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limiter.js","sourceRoot":"","sources":["../../src/infra/rate-limiter.ts"],"names":[],"mappings":"AAWA,MAAM,OAAO,WAAW;IACd,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE5C,SAAS,CAAC,QAAgB,EAAE,SAAiB,EAAE,UAAkB;QAC/D,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,EAAE;YACzB,MAAM,EAAE,SAAS;YACjB,UAAU,EAAE,IAAI,CAAC,GAAG,EAAE;YACtB,MAAM,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE;SAClC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,QAAgB;QAC5B,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,CAAC,MAAM;YAAE,OAAO,CAAC,sBAAsB;QAE3C,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAEpB,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QAED,kCAAkC;QAClC,MAAM,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;QACvE,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpB,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;IACrB,CAAC;IAEO,MAAM,CAAC,MAAc;QAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,CAAC,GAAG,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,IAAI,CAAC;QACjD,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CACtB,MAAM,CAAC,MAAM,CAAC,SAAS,EACvB,MAAM,CAAC,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,UAAU,CACnD,CAAC;QACF,MAAM,CAAC,UAAU,GAAG,GAAG,CAAC;IAC1B,CAAC;CACF;AAED,+CAA+C;AAC/C,MAAM,CAAC,MAAM,WAAW,GAAG,IAAI,WAAW,EAAE,CAAC;AAC7C,WAAW,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAW,UAAU;AAC1D,WAAW,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC,CAAE,aAAa;AAC7D,WAAW,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,wBAAwB;AACzE,WAAW,CAAC,SAAS,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAW,cAAc"}
@@ -0,0 +1,5 @@
1
+ export { initDatabase, initDefaultDatabase, getTableNames, getSchemaVersion } from "./sqlite.js";
2
+ export { MemoryStorage } from "./storage.js";
3
+ export type { WorkflowPreferences } from "./storage.js";
4
+ export { buildMemoryContext } from "./retrieval.js";
5
+ export { extractPreferences } from "./preference-extractor.js";
@@ -0,0 +1,5 @@
1
+ export { initDatabase, initDefaultDatabase, getTableNames, getSchemaVersion } from "./sqlite.js";
2
+ export { MemoryStorage } from "./storage.js";
3
+ export { buildMemoryContext } from "./retrieval.js";
4
+ export { extractPreferences } from "./preference-extractor.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/memory/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACjG,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AACpD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC"}
@@ -0,0 +1,6 @@
1
+ export interface ExtractedPreference {
2
+ key: string;
3
+ value: string;
4
+ confidence: "high" | "medium" | "low";
5
+ }
6
+ export declare function extractPreferences(input: string): ExtractedPreference[];
@@ -0,0 +1,88 @@
1
+ const PREFERENCE_PATTERNS = [
2
+ // Asset scope
3
+ {
4
+ pattern: /\bprefer\s+etfs?\b/i,
5
+ key: "asset_scope",
6
+ value: "etf_focused",
7
+ confidence: "high",
8
+ },
9
+ {
10
+ pattern: /\betf[\s-]*(?:heavy|focused|only)\b/i,
11
+ key: "asset_scope",
12
+ value: "etf_focused",
13
+ confidence: "high",
14
+ },
15
+ // Risk profile
16
+ {
17
+ pattern: /\b(?:i'?m|i\s+am)\s+conservative\b/i,
18
+ key: "risk_profile",
19
+ value: "conservative",
20
+ confidence: "high",
21
+ },
22
+ {
23
+ pattern: /\brisk\s*averse\b/i,
24
+ key: "risk_profile",
25
+ value: "conservative",
26
+ confidence: "high",
27
+ },
28
+ {
29
+ pattern: /\b(?:i'?m|i\s+am)\s+aggressive\b/i,
30
+ key: "risk_profile",
31
+ value: "aggressive",
32
+ confidence: "high",
33
+ },
34
+ {
35
+ pattern: /\baggressive\s+growth\b/i,
36
+ key: "risk_profile",
37
+ value: "aggressive",
38
+ confidence: "high",
39
+ },
40
+ {
41
+ pattern: /\b(?:i'?m|i\s+am)\s+(?:moderate|balanced)\b/i,
42
+ key: "risk_profile",
43
+ value: "balanced",
44
+ confidence: "high",
45
+ },
46
+ // Time horizon
47
+ {
48
+ pattern: /\b(?:12\s*month|one\s*year|1\s*year)\s*horizon/i,
49
+ key: "time_horizon",
50
+ value: "1y_plus",
51
+ confidence: "high",
52
+ },
53
+ {
54
+ pattern: /\blong[\s-]*term\s+(?:invest|hold|horizon)/i,
55
+ key: "time_horizon",
56
+ value: "long",
57
+ confidence: "medium",
58
+ },
59
+ {
60
+ pattern: /\bshort[\s-]*term\s+(?:trad|invest|horizon)/i,
61
+ key: "time_horizon",
62
+ value: "short",
63
+ confidence: "medium",
64
+ },
65
+ // Options liquidity
66
+ {
67
+ pattern: /\b(?:only|prefer)\s+(?:trade\s+)?liquid\s+options?\b/i,
68
+ key: "options_liquidity",
69
+ value: "high",
70
+ confidence: "high",
71
+ },
72
+ ];
73
+ export function extractPreferences(input) {
74
+ const preferences = [];
75
+ const seenKeys = new Set();
76
+ for (const rule of PREFERENCE_PATTERNS) {
77
+ if (rule.pattern.test(input) && !seenKeys.has(rule.key)) {
78
+ preferences.push({
79
+ key: rule.key,
80
+ value: rule.value,
81
+ confidence: rule.confidence,
82
+ });
83
+ seenKeys.add(rule.key);
84
+ }
85
+ }
86
+ return preferences;
87
+ }
88
+ //# sourceMappingURL=preference-extractor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preference-extractor.js","sourceRoot":"","sources":["../../src/memory/preference-extractor.ts"],"names":[],"mappings":"AAaA,MAAM,mBAAmB,GAAkB;IACzC,cAAc;IACd;QACE,OAAO,EAAE,qBAAqB;QAC9B,GAAG,EAAE,aAAa;QAClB,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,sCAAsC;QAC/C,GAAG,EAAE,aAAa;QAClB,KAAK,EAAE,aAAa;QACpB,UAAU,EAAE,MAAM;KACnB;IAED,eAAe;IACf;QACE,OAAO,EAAE,qCAAqC;QAC9C,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,oBAAoB;QAC7B,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,cAAc;QACrB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,mCAAmC;QAC5C,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,YAAY;QACnB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,0BAA0B;QACnC,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,YAAY;QACnB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,8CAA8C;QACvD,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,UAAU;QACjB,UAAU,EAAE,MAAM;KACnB;IAED,eAAe;IACf;QACE,OAAO,EAAE,iDAAiD;QAC1D,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,SAAS;QAChB,UAAU,EAAE,MAAM;KACnB;IACD;QACE,OAAO,EAAE,6CAA6C;QACtD,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,QAAQ;KACrB;IACD;QACE,OAAO,EAAE,8CAA8C;QACvD,GAAG,EAAE,cAAc;QACnB,KAAK,EAAE,OAAO;QACd,UAAU,EAAE,QAAQ;KACrB;IAED,oBAAoB;IACpB;QACE,OAAO,EAAE,uDAAuD;QAChE,GAAG,EAAE,mBAAmB;QACxB,KAAK,EAAE,MAAM;QACb,UAAU,EAAE,MAAM;KACnB;CACF,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,MAAM,WAAW,GAA0B,EAAE,CAAC;IAC9C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,mBAAmB,EAAE,CAAC;QACvC,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;YACxD,WAAW,CAAC,IAAI,CAAC;gBACf,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,UAAU,EAAE,IAAI,CAAC,UAAU;aAC5B,CAAC,CAAC;YACH,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAED,OAAO,WAAW,CAAC;AACrB,CAAC"}
@@ -0,0 +1,8 @@
1
+ import type { MemoryStorage } from "./storage.js";
2
+ /**
3
+ * Build compact memory context for agent injection.
4
+ * @param overriddenSlots Slot names whose values were overridden by current-turn user input.
5
+ * The corresponding preference keys will be excluded from memory context to avoid
6
+ * conflicting provenance signals.
7
+ */
8
+ export declare function buildMemoryContext(storage: MemoryStorage, overriddenSlots?: string[]): string;
@@ -0,0 +1,61 @@
1
+ const MAX_PREFERENCE_LINES = 15;
2
+ const MAX_WORKFLOW_SUMMARY_LINES = 12;
3
+ /** Slot name → preference key(s) mapping for suppression. */
4
+ const SLOT_TO_PREF_KEYS = {
5
+ riskProfile: ["risk_profile"],
6
+ assetScope: ["asset_scope"],
7
+ timeHorizon: ["time_horizon"],
8
+ dteTarget: ["dte_target"],
9
+ moneynessPreference: ["moneyness_preference"],
10
+ liquidityMinimum: ["liquidity_minimum", "options_liquidity"],
11
+ };
12
+ /**
13
+ * Build compact memory context for agent injection.
14
+ * @param overriddenSlots Slot names whose values were overridden by current-turn user input.
15
+ * The corresponding preference keys will be excluded from memory context to avoid
16
+ * conflicting provenance signals.
17
+ */
18
+ export function buildMemoryContext(storage, overriddenSlots) {
19
+ const sections = [];
20
+ // Build set of preference keys to suppress
21
+ const suppressedKeys = new Set();
22
+ if (overriddenSlots) {
23
+ for (const slot of overriddenSlots) {
24
+ const keys = SLOT_TO_PREF_KEYS[slot];
25
+ if (keys)
26
+ keys.forEach((k) => suppressedKeys.add(k));
27
+ }
28
+ }
29
+ // Preferences
30
+ const prefs = storage.getPreferencesByNamespace("global");
31
+ if (prefs.length > 0) {
32
+ const filtered = prefs.filter((p) => !suppressedKeys.has(String(p.key)));
33
+ if (filtered.length > 0) {
34
+ const lines = filtered.slice(0, MAX_PREFERENCE_LINES).map((p) => {
35
+ const value = tryParseJson(p.value_json);
36
+ return `- ${p.key}: ${value}`;
37
+ });
38
+ sections.push("User Preferences:\n" + lines.join("\n"));
39
+ }
40
+ }
41
+ // Recent workflow runs
42
+ const runs = storage.getRecentWorkflowRuns(3);
43
+ if (runs.length > 0) {
44
+ const lines = runs.slice(0, MAX_WORKFLOW_SUMMARY_LINES).map((r) => {
45
+ const summary = r.output_summary ? ` — ${r.output_summary}` : "";
46
+ return `- ${r.workflow_type} (${r.created_at})${summary}`;
47
+ });
48
+ sections.push("Recent Workflows:\n" + lines.join("\n"));
49
+ }
50
+ return sections.join("\n\n");
51
+ }
52
+ function tryParseJson(json) {
53
+ try {
54
+ const parsed = JSON.parse(json);
55
+ return typeof parsed === "string" ? parsed : JSON.stringify(parsed);
56
+ }
57
+ catch {
58
+ return json;
59
+ }
60
+ }
61
+ //# sourceMappingURL=retrieval.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retrieval.js","sourceRoot":"","sources":["../../src/memory/retrieval.ts"],"names":[],"mappings":"AAEA,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAChC,MAAM,0BAA0B,GAAG,EAAE,CAAC;AAEtC,6DAA6D;AAC7D,MAAM,iBAAiB,GAA6B;IAClD,WAAW,EAAE,CAAC,cAAc,CAAC;IAC7B,UAAU,EAAE,CAAC,aAAa,CAAC;IAC3B,WAAW,EAAE,CAAC,cAAc,CAAC;IAC7B,SAAS,EAAE,CAAC,YAAY,CAAC;IACzB,mBAAmB,EAAE,CAAC,sBAAsB,CAAC;IAC7C,gBAAgB,EAAE,CAAC,mBAAmB,EAAE,mBAAmB,CAAC;CAC7D,CAAC;AAEF;;;;;GAKG;AACH,MAAM,UAAU,kBAAkB,CAChC,OAAsB,EACtB,eAA0B;IAE1B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,2CAA2C;IAC3C,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;IACzC,IAAI,eAAe,EAAE,CAAC;QACpB,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YACrC,IAAI,IAAI;gBAAE,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IAED,cAAc;IACd,MAAM,KAAK,GAAG,OAAO,CAAC,yBAAyB,CAAC,QAAQ,CAAC,CAAC;IAC1D,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,QAAQ,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACzE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;gBAC9D,MAAM,KAAK,GAAG,YAAY,CAAC,CAAC,CAAC,UAAoB,CAAC,CAAC;gBACnD,OAAO,KAAK,CAAC,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YAChC,CAAC,CAAC,CAAC;YACH,QAAQ,CAAC,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,uBAAuB;IACvB,MAAM,IAAI,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,0BAA0B,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAChE,MAAM,OAAO,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACjE,OAAO,KAAK,CAAC,CAAC,aAAa,KAAK,CAAC,CAAC,UAAU,IAAI,OAAO,EAAE,CAAC;QAC5D,CAAC,CAAC,CAAC;QACH,QAAQ,CAAC,IAAI,CAAC,qBAAqB,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,OAAO,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,YAAY,CAAC,IAAY;IAChC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChC,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IACtE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import Database from "better-sqlite3";
2
+ export declare function initDatabase(path: string): Database.Database;
3
+ export declare function initDefaultDatabase(): Database.Database;
4
+ export declare function getTableNames(db: Database.Database): string[];
5
+ export declare function getSchemaVersion(db: Database.Database): number;