@tangle-network/browser-agent-driver 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 (208) hide show
  1. package/LICENSE +196 -0
  2. package/README.md +149 -0
  3. package/dist/artifacts/composite-sink.d.ts +15 -0
  4. package/dist/artifacts/composite-sink.d.ts.map +1 -0
  5. package/dist/artifacts/composite-sink.js +27 -0
  6. package/dist/artifacts/composite-sink.js.map +1 -0
  7. package/dist/artifacts/filesystem-sink.d.ts +26 -0
  8. package/dist/artifacts/filesystem-sink.d.ts.map +1 -0
  9. package/dist/artifacts/filesystem-sink.js +81 -0
  10. package/dist/artifacts/filesystem-sink.js.map +1 -0
  11. package/dist/artifacts/types.d.ts +97 -0
  12. package/dist/artifacts/types.d.ts.map +1 -0
  13. package/dist/artifacts/types.js +9 -0
  14. package/dist/artifacts/types.js.map +1 -0
  15. package/dist/artifacts/webhook-sink.d.ts +71 -0
  16. package/dist/artifacts/webhook-sink.d.ts.map +1 -0
  17. package/dist/artifacts/webhook-sink.js +117 -0
  18. package/dist/artifacts/webhook-sink.js.map +1 -0
  19. package/dist/brain/index.d.ts +149 -0
  20. package/dist/brain/index.d.ts.map +1 -0
  21. package/dist/brain/index.js +1030 -0
  22. package/dist/brain/index.js.map +1 -0
  23. package/dist/browser-launch.d.ts +27 -0
  24. package/dist/browser-launch.d.ts.map +1 -0
  25. package/dist/browser-launch.js +92 -0
  26. package/dist/browser-launch.js.map +1 -0
  27. package/dist/cli.d.ts +14 -0
  28. package/dist/cli.d.ts.map +1 -0
  29. package/dist/cli.js +1035 -0
  30. package/dist/cli.js.map +1 -0
  31. package/dist/config.d.ts +143 -0
  32. package/dist/config.d.ts.map +1 -0
  33. package/dist/config.js +205 -0
  34. package/dist/config.js.map +1 -0
  35. package/dist/context-budget.d.ts +14 -0
  36. package/dist/context-budget.d.ts.map +1 -0
  37. package/dist/context-budget.js +39 -0
  38. package/dist/context-budget.js.map +1 -0
  39. package/dist/design-audit.d.ts +48 -0
  40. package/dist/design-audit.d.ts.map +1 -0
  41. package/dist/design-audit.js +172 -0
  42. package/dist/design-audit.js.map +1 -0
  43. package/dist/domain-policy.d.ts +4 -0
  44. package/dist/domain-policy.d.ts.map +1 -0
  45. package/dist/domain-policy.js +69 -0
  46. package/dist/domain-policy.js.map +1 -0
  47. package/dist/drivers/block-patterns.d.ts +11 -0
  48. package/dist/drivers/block-patterns.d.ts.map +1 -0
  49. package/dist/drivers/block-patterns.js +123 -0
  50. package/dist/drivers/block-patterns.js.map +1 -0
  51. package/dist/drivers/cdp-page-state.d.ts +22 -0
  52. package/dist/drivers/cdp-page-state.d.ts.map +1 -0
  53. package/dist/drivers/cdp-page-state.js +38 -0
  54. package/dist/drivers/cdp-page-state.js.map +1 -0
  55. package/dist/drivers/cdp-snapshot.d.ts +32 -0
  56. package/dist/drivers/cdp-snapshot.d.ts.map +1 -0
  57. package/dist/drivers/cdp-snapshot.js +120 -0
  58. package/dist/drivers/cdp-snapshot.js.map +1 -0
  59. package/dist/drivers/playwright.d.ts +84 -0
  60. package/dist/drivers/playwright.d.ts.map +1 -0
  61. package/dist/drivers/playwright.js +385 -0
  62. package/dist/drivers/playwright.js.map +1 -0
  63. package/dist/drivers/snapshot.d.ts +134 -0
  64. package/dist/drivers/snapshot.d.ts.map +1 -0
  65. package/dist/drivers/snapshot.js +354 -0
  66. package/dist/drivers/snapshot.js.map +1 -0
  67. package/dist/drivers/types.d.ts +37 -0
  68. package/dist/drivers/types.d.ts.map +1 -0
  69. package/dist/drivers/types.js +5 -0
  70. package/dist/drivers/types.js.map +1 -0
  71. package/dist/env-loader.d.ts +2 -0
  72. package/dist/env-loader.d.ts.map +1 -0
  73. package/dist/env-loader.js +35 -0
  74. package/dist/env-loader.js.map +1 -0
  75. package/dist/formatters/slack.d.ts +37 -0
  76. package/dist/formatters/slack.d.ts.map +1 -0
  77. package/dist/formatters/slack.js +97 -0
  78. package/dist/formatters/slack.js.map +1 -0
  79. package/dist/index.d.ts +60 -0
  80. package/dist/index.d.ts.map +1 -0
  81. package/dist/index.js +53 -0
  82. package/dist/index.js.map +1 -0
  83. package/dist/memory/analyzer.d.ts +67 -0
  84. package/dist/memory/analyzer.d.ts.map +1 -0
  85. package/dist/memory/analyzer.js +265 -0
  86. package/dist/memory/analyzer.js.map +1 -0
  87. package/dist/memory/knowledge.d.ts +55 -0
  88. package/dist/memory/knowledge.d.ts.map +1 -0
  89. package/dist/memory/knowledge.js +115 -0
  90. package/dist/memory/knowledge.js.map +1 -0
  91. package/dist/memory/project-store.d.ts +43 -0
  92. package/dist/memory/project-store.d.ts.map +1 -0
  93. package/dist/memory/project-store.js +119 -0
  94. package/dist/memory/project-store.js.map +1 -0
  95. package/dist/memory/selectors.d.ts +46 -0
  96. package/dist/memory/selectors.d.ts.map +1 -0
  97. package/dist/memory/selectors.js +89 -0
  98. package/dist/memory/selectors.js.map +1 -0
  99. package/dist/memory/store.d.ts +39 -0
  100. package/dist/memory/store.d.ts.map +1 -0
  101. package/dist/memory/store.js +193 -0
  102. package/dist/memory/store.js.map +1 -0
  103. package/dist/model-pricing.d.ts +41 -0
  104. package/dist/model-pricing.d.ts.map +1 -0
  105. package/dist/model-pricing.js +197 -0
  106. package/dist/model-pricing.js.map +1 -0
  107. package/dist/multi-actor.d.ts +108 -0
  108. package/dist/multi-actor.d.ts.map +1 -0
  109. package/dist/multi-actor.js +192 -0
  110. package/dist/multi-actor.js.map +1 -0
  111. package/dist/override-pipeline.d.ts +57 -0
  112. package/dist/override-pipeline.d.ts.map +1 -0
  113. package/dist/override-pipeline.js +26 -0
  114. package/dist/override-pipeline.js.map +1 -0
  115. package/dist/personas.d.ts +19 -0
  116. package/dist/personas.d.ts.map +1 -0
  117. package/dist/personas.js +86 -0
  118. package/dist/personas.js.map +1 -0
  119. package/dist/preview.d.ts +32 -0
  120. package/dist/preview.d.ts.map +1 -0
  121. package/dist/preview.js +138 -0
  122. package/dist/preview.js.map +1 -0
  123. package/dist/provider-defaults.d.ts +6 -0
  124. package/dist/provider-defaults.d.ts.map +1 -0
  125. package/dist/provider-defaults.js +40 -0
  126. package/dist/provider-defaults.js.map +1 -0
  127. package/dist/providers/sandbox-backend.d.ts +22 -0
  128. package/dist/providers/sandbox-backend.d.ts.map +1 -0
  129. package/dist/providers/sandbox-backend.js +259 -0
  130. package/dist/providers/sandbox-backend.js.map +1 -0
  131. package/dist/recovery.d.ts +91 -0
  132. package/dist/recovery.d.ts.map +1 -0
  133. package/dist/recovery.js +500 -0
  134. package/dist/recovery.js.map +1 -0
  135. package/dist/reporters/junit.d.ts +8 -0
  136. package/dist/reporters/junit.d.ts.map +1 -0
  137. package/dist/reporters/junit.js +99 -0
  138. package/dist/reporters/junit.js.map +1 -0
  139. package/dist/run-metrics.d.ts +4 -0
  140. package/dist/run-metrics.d.ts.map +1 -0
  141. package/dist/run-metrics.js +48 -0
  142. package/dist/run-metrics.js.map +1 -0
  143. package/dist/run-state.d.ts +22 -0
  144. package/dist/run-state.d.ts.map +1 -0
  145. package/dist/run-state.js +33 -0
  146. package/dist/run-state.js.map +1 -0
  147. package/dist/runner/goal-verification.d.ts +13 -0
  148. package/dist/runner/goal-verification.d.ts.map +1 -0
  149. package/dist/runner/goal-verification.js +152 -0
  150. package/dist/runner/goal-verification.js.map +1 -0
  151. package/dist/runner/index.d.ts +12 -0
  152. package/dist/runner/index.d.ts.map +1 -0
  153. package/dist/runner/index.js +18 -0
  154. package/dist/runner/index.js.map +1 -0
  155. package/dist/runner/overrides.d.ts +75 -0
  156. package/dist/runner/overrides.d.ts.map +1 -0
  157. package/dist/runner/overrides.js +385 -0
  158. package/dist/runner/overrides.js.map +1 -0
  159. package/dist/runner/page-analysis.d.ts +23 -0
  160. package/dist/runner/page-analysis.d.ts.map +1 -0
  161. package/dist/runner/page-analysis.js +91 -0
  162. package/dist/runner/page-analysis.js.map +1 -0
  163. package/dist/runner/runner.d.ts +66 -0
  164. package/dist/runner/runner.d.ts.map +1 -0
  165. package/dist/runner/runner.js +1314 -0
  166. package/dist/runner/runner.js.map +1 -0
  167. package/dist/runner/scout.d.ts +29 -0
  168. package/dist/runner/scout.d.ts.map +1 -0
  169. package/dist/runner/scout.js +81 -0
  170. package/dist/runner/scout.js.map +1 -0
  171. package/dist/runner/search-guidance.d.ts +31 -0
  172. package/dist/runner/search-guidance.d.ts.map +1 -0
  173. package/dist/runner/search-guidance.js +210 -0
  174. package/dist/runner/search-guidance.js.map +1 -0
  175. package/dist/runner/utils.d.ts +28 -0
  176. package/dist/runner/utils.d.ts.map +1 -0
  177. package/dist/runner/utils.js +119 -0
  178. package/dist/runner/utils.js.map +1 -0
  179. package/dist/runner.d.ts +8 -0
  180. package/dist/runner.d.ts.map +1 -0
  181. package/dist/runner.js +21 -0
  182. package/dist/runner.js.map +1 -0
  183. package/dist/supervisor/critic.d.ts +29 -0
  184. package/dist/supervisor/critic.d.ts.map +1 -0
  185. package/dist/supervisor/critic.js +320 -0
  186. package/dist/supervisor/critic.js.map +1 -0
  187. package/dist/supervisor/policy.d.ts +11 -0
  188. package/dist/supervisor/policy.d.ts.map +1 -0
  189. package/dist/supervisor/policy.js +118 -0
  190. package/dist/supervisor/policy.js.map +1 -0
  191. package/dist/test-report.d.ts +15 -0
  192. package/dist/test-report.d.ts.map +1 -0
  193. package/dist/test-report.js +241 -0
  194. package/dist/test-report.js.map +1 -0
  195. package/dist/test-runner.d.ts +99 -0
  196. package/dist/test-runner.d.ts.map +1 -0
  197. package/dist/test-runner.js +1075 -0
  198. package/dist/test-runner.js.map +1 -0
  199. package/dist/types.d.ts +493 -0
  200. package/dist/types.d.ts.map +1 -0
  201. package/dist/types.js +5 -0
  202. package/dist/types.js.map +1 -0
  203. package/dist/wallet/automation.d.ts +52 -0
  204. package/dist/wallet/automation.d.ts.map +1 -0
  205. package/dist/wallet/automation.js +507 -0
  206. package/dist/wallet/automation.js.map +1 -0
  207. package/package.json +101 -0
  208. package/scripts/postinstall-provider-patches.mjs +52 -0
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Trajectory Store — saves and loads successful trajectories for case-based reasoning.
3
+ *
4
+ * Stores trajectories as JSON files in a configurable directory (.trajectories/ by default).
5
+ * No fine-tuning needed — successful trajectories are injected into the system prompt
6
+ * as reference guides for similar future tasks.
7
+ */
8
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync } from 'fs';
9
+ import { join } from 'path';
10
+ import { snapshotHash } from '../recovery.js';
11
+ export class TrajectoryStore {
12
+ storePath;
13
+ cache = null;
14
+ similarityThreshold;
15
+ enableScoring;
16
+ ttlDays;
17
+ constructor(storePath, thresholdOrOptions = 0.5) {
18
+ const opts = typeof thresholdOrOptions === 'number'
19
+ ? { similarityThreshold: thresholdOrOptions }
20
+ : thresholdOrOptions;
21
+ this.storePath = storePath || join(process.cwd(), '.trajectories');
22
+ this.similarityThreshold = opts.similarityThreshold ?? 0.5;
23
+ this.enableScoring = opts.enableScoring === true;
24
+ this.ttlDays = opts.ttlDays ?? 30;
25
+ if (!existsSync(this.storePath)) {
26
+ mkdirSync(this.storePath, { recursive: true });
27
+ }
28
+ }
29
+ /** Save a trajectory from a completed agent run */
30
+ save(goal, turns, success, model, options = {}) {
31
+ const normalizedOrigin = normalizeOrigin(options.origin);
32
+ const trajectory = {
33
+ id: `traj_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
34
+ goal,
35
+ origin: normalizedOrigin,
36
+ steps: turns
37
+ .filter((t) => t.action.action !== 'complete' && t.action.action !== 'abort')
38
+ .map((t) => ({
39
+ url: t.state.url,
40
+ action: t.action,
41
+ snapshotHash: snapshotHash(t.state.snapshot).toString(),
42
+ verified: t.verified,
43
+ })),
44
+ success,
45
+ durationMs: turns.reduce((sum, t) => sum + t.durationMs, 0),
46
+ model,
47
+ timestamp: new Date().toISOString(),
48
+ };
49
+ const filePath = join(this.storePath, `${trajectory.id}.json`);
50
+ writeFileSync(filePath, JSON.stringify(trajectory, null, 2));
51
+ this.cache = null; // Invalidate cache on write
52
+ return trajectory;
53
+ }
54
+ /** Load all trajectories (cached — invalidated on save) */
55
+ loadAll() {
56
+ if (this.cache)
57
+ return this.cache;
58
+ if (!existsSync(this.storePath))
59
+ return [];
60
+ this.cache = readdirSync(this.storePath)
61
+ .filter((f) => f.endsWith('.json'))
62
+ .map((f) => {
63
+ try {
64
+ return JSON.parse(readFileSync(join(this.storePath, f), 'utf-8'));
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ })
70
+ .filter((t) => t !== null);
71
+ return this.cache;
72
+ }
73
+ /** Find the best matching trajectory for a goal */
74
+ findBestMatch(goal, options = {}) {
75
+ const now = Date.now();
76
+ const maxAgeMs = this.ttlDays * 24 * 60 * 60 * 1000;
77
+ let trajectories = this.loadAll().filter((t) => {
78
+ if (!t.success)
79
+ return false;
80
+ const ts = Date.parse(t.timestamp);
81
+ if (Number.isNaN(ts))
82
+ return true;
83
+ return now - ts <= maxAgeMs;
84
+ });
85
+ const requestedOrigin = normalizeOrigin(options.origin);
86
+ if (requestedOrigin) {
87
+ trajectories = trajectories.filter((trajectory) => normalizeOrigin(trajectory.origin) === requestedOrigin);
88
+ }
89
+ if (trajectories.length === 0)
90
+ return null;
91
+ const scored = trajectories.map((trajectory) => {
92
+ const similarity = goalSimilarity(goal, trajectory.goal);
93
+ const score = this.enableScoring
94
+ ? this.computeScore(similarity, trajectory, now)
95
+ : similarity;
96
+ return { trajectory, score, similarity };
97
+ });
98
+ scored.sort((a, b) => b.score - a.score);
99
+ if (scored[0].similarity > this.similarityThreshold) {
100
+ return scored[0].trajectory;
101
+ }
102
+ return null;
103
+ }
104
+ computeScore(similarity, trajectory, nowMs) {
105
+ const recency = this.computeRecencyScore(trajectory.timestamp, nowMs);
106
+ const durationScore = 1 / (1 + trajectory.durationMs / 60_000);
107
+ const verificationScore = this.computeVerificationScore(trajectory);
108
+ // Weighted blend tuned for stable reuse:
109
+ // similarity dominates, then recency, then execution quality proxies.
110
+ return (similarity * 0.6) + (recency * 0.2) + (durationScore * 0.1) + (verificationScore * 0.1);
111
+ }
112
+ computeRecencyScore(timestamp, nowMs) {
113
+ const ts = Date.parse(timestamp);
114
+ if (Number.isNaN(ts))
115
+ return 0.5;
116
+ const ageDays = Math.max(0, (nowMs - ts) / (24 * 60 * 60 * 1000));
117
+ return Math.exp(-ageDays / 14);
118
+ }
119
+ computeVerificationScore(trajectory) {
120
+ if (trajectory.steps.length === 0)
121
+ return 0.5;
122
+ const verified = trajectory.steps.filter((step) => step.verified === true).length;
123
+ return verified / trajectory.steps.length;
124
+ }
125
+ /** Format a trajectory as a human-readable reference for the brain */
126
+ formatAsReference(trajectory) {
127
+ const lines = [];
128
+ lines.push(`Goal: ${trajectory.goal}`);
129
+ lines.push(`Steps (${trajectory.steps.length} total):`);
130
+ for (let i = 0; i < trajectory.steps.length; i++) {
131
+ const step = trajectory.steps[i];
132
+ const actionStr = formatAction(step.action);
133
+ const verified = step.verified ? ' [verified]' : '';
134
+ lines.push(` ${i + 1}. ${actionStr} (on ${step.url})${verified}`);
135
+ }
136
+ return lines.join('\n');
137
+ }
138
+ }
139
+ function normalizeOrigin(origin) {
140
+ if (!origin)
141
+ return undefined;
142
+ try {
143
+ return new URL(origin).origin;
144
+ }
145
+ catch {
146
+ return undefined;
147
+ }
148
+ }
149
+ /**
150
+ * Compute similarity between two goal strings.
151
+ * Uses word overlap (Jaccard coefficient) — simple and effective.
152
+ */
153
+ function goalSimilarity(a, b) {
154
+ const wordsA = new Set(tokenize(a));
155
+ const wordsB = new Set(tokenize(b));
156
+ if (wordsA.size === 0 && wordsB.size === 0)
157
+ return 1;
158
+ if (wordsA.size === 0 || wordsB.size === 0)
159
+ return 0;
160
+ let intersection = 0;
161
+ for (const w of wordsA) {
162
+ if (wordsB.has(w))
163
+ intersection++;
164
+ }
165
+ const union = new Set([...wordsA, ...wordsB]).size;
166
+ return intersection / union;
167
+ }
168
+ function tokenize(text) {
169
+ return text
170
+ .toLowerCase()
171
+ .replace(/[^a-z0-9\s]/g, '')
172
+ .split(/\s+/)
173
+ .filter((w) => w.length > 1); // Skip single-char words
174
+ }
175
+ function formatAction(action) {
176
+ switch (action.action) {
177
+ case 'click':
178
+ return `click ${action.selector}`;
179
+ case 'type':
180
+ return `type "${action.text}" into ${action.selector}`;
181
+ case 'press':
182
+ return `press ${action.key} on ${action.selector}`;
183
+ case 'scroll':
184
+ return `scroll ${action.direction}`;
185
+ case 'navigate':
186
+ return `navigate to ${action.url}`;
187
+ case 'wait':
188
+ return `wait ${action.ms}ms`;
189
+ default:
190
+ return JSON.stringify(action);
191
+ }
192
+ }
193
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/memory/store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Model pricing — fetched from LiteLLM's community-maintained pricing database.
3
+ *
4
+ * Source: https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json
5
+ *
6
+ * Pricing is fetched once on first use, cached in memory and on disk (24h TTL).
7
+ * Falls back to a minimal hardcoded table if the fetch fails.
8
+ */
9
+ export interface ModelPricing {
10
+ /** Cost per token (USD) */
11
+ inputCostPerToken: number;
12
+ outputCostPerToken: number;
13
+ cacheReadCostPerToken?: number;
14
+ }
15
+ /**
16
+ * Load the pricing database. Tries (in order):
17
+ * 1. In-memory cache
18
+ * 2. Disk cache (24h TTL)
19
+ * 3. Fetch from GitHub
20
+ * 4. Hardcoded fallback
21
+ */
22
+ export declare function loadPricing(): Promise<Map<string, ModelPricing>>;
23
+ /** Synchronous version — returns fallback if not yet loaded */
24
+ export declare function getPricingSync(): Map<string, ModelPricing>;
25
+ /**
26
+ * Look up pricing for a model. Tries exact match, then prefix match.
27
+ */
28
+ export declare function getModelPricing(model: string): ModelPricing | null;
29
+ /**
30
+ * Calculate estimated cost in USD for a set of tokens.
31
+ *
32
+ * @param model - Model name
33
+ * @param inputTokens - Total input tokens (summed across all calls)
34
+ * @param outputTokens - Total output tokens
35
+ * @param cacheReadTokens - Tokens served from cache (discounted rate)
36
+ * @param maxInputPerCall - Largest single-call input context size.
37
+ * Used to determine tiered pricing (e.g., gpt-5.4 charges 2x above 272K context).
38
+ * If not provided, uses base tier pricing.
39
+ */
40
+ export declare function calculateCost(model: string, inputTokens: number, outputTokens: number, cacheReadTokens?: number, maxInputPerCall?: number): number;
41
+ //# sourceMappingURL=model-pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-pricing.d.ts","sourceRoot":"","sources":["../src/model-pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAYH,MAAM,WAAW,YAAY;IAC3B,2BAA2B;IAC3B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC;AAsFD;;;;;;GAMG;AACH,wBAAsB,WAAW,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC,CA+BtE;AAED,+DAA+D;AAC/D,wBAAgB,cAAc,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAU1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAclE;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,eAAe,SAAI,EACnB,eAAe,SAAI,GAClB,MAAM,CAuBR"}
@@ -0,0 +1,197 @@
1
+ /**
2
+ * Model pricing — fetched from LiteLLM's community-maintained pricing database.
3
+ *
4
+ * Source: https://github.com/BerriAI/litellm/blob/main/model_prices_and_context_window.json
5
+ *
6
+ * Pricing is fetched once on first use, cached in memory and on disk (24h TTL).
7
+ * Falls back to a minimal hardcoded table if the fetch fails.
8
+ */
9
+ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
10
+ import { tmpdir } from 'node:os';
11
+ import { join } from 'node:path';
12
+ const LITELLM_PRICING_URL = 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json';
13
+ const CACHE_PATH = join(tmpdir(), 'browser-agent-driver-model-pricing.json');
14
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
15
+ /**
16
+ * Context-length-based tiered pricing overrides.
17
+ * Some models (e.g., gpt-5.4) charge more above a context threshold.
18
+ * LiteLLM's database only has the base tier, so we handle overrides here.
19
+ * Pricing is per 1M tokens.
20
+ */
21
+ const TIERED_PRICING = {
22
+ 'gpt-5.4': { threshold: 272_000, input: 5.00, output: 22.50 },
23
+ 'gpt-5.4-2026-03-05': { threshold: 272_000, input: 5.00, output: 22.50 },
24
+ 'gpt-5.2': { threshold: 200_000, input: 3.50, output: 21.00 },
25
+ };
26
+ /** Parsed pricing map: model name → pricing */
27
+ let pricingCache = null;
28
+ /**
29
+ * Minimal fallback pricing (per 1M tokens) for when the fetch fails.
30
+ * These are last-known-good values — always prefer the fetched database.
31
+ */
32
+ const FALLBACK_PRICING = {
33
+ 'gpt-5.4': { input: 2.50, output: 15.00, cacheRead: 0.25 },
34
+ 'gpt-5.2': { input: 1.75, output: 14.00, cacheRead: 0.175 },
35
+ 'gpt-5.1': { input: 1.25, output: 10.00, cacheRead: 0.125 },
36
+ 'gpt-5.3-codex': { input: 1.75, output: 14.00, cacheRead: 0.175 },
37
+ 'gpt-4.1': { input: 2.00, output: 8.00, cacheRead: 0.20 },
38
+ 'gpt-4.1-mini': { input: 0.40, output: 1.60, cacheRead: 0.04 },
39
+ 'gpt-4.1-nano': { input: 0.10, output: 0.40, cacheRead: 0.01 },
40
+ 'gpt-4o': { input: 2.50, output: 10.00, cacheRead: 1.25 },
41
+ 'gpt-4o-mini': { input: 0.15, output: 0.60, cacheRead: 0.075 },
42
+ 'claude-opus-4-6': { input: 5.00, output: 25.00, cacheRead: 0.50 },
43
+ 'claude-sonnet-4-6': { input: 3.00, output: 15.00, cacheRead: 0.30 },
44
+ 'claude-haiku-4-5': { input: 1.00, output: 5.00, cacheRead: 0.10 },
45
+ };
46
+ function parseLiteLLMPricing(raw) {
47
+ const map = new Map();
48
+ for (const [key, value] of Object.entries(raw)) {
49
+ if (key === 'sample_spec')
50
+ continue;
51
+ const entry = value;
52
+ const inputCost = entry.input_cost_per_token;
53
+ const outputCost = entry.output_cost_per_token;
54
+ if (typeof inputCost !== 'number' || typeof outputCost !== 'number')
55
+ continue;
56
+ map.set(key, {
57
+ inputCostPerToken: inputCost,
58
+ outputCostPerToken: outputCost,
59
+ cacheReadCostPerToken: typeof entry.cache_read_input_token_cost === 'number'
60
+ ? entry.cache_read_input_token_cost
61
+ : undefined,
62
+ });
63
+ }
64
+ return map;
65
+ }
66
+ function loadDiskCache() {
67
+ try {
68
+ if (!existsSync(CACHE_PATH))
69
+ return null;
70
+ const raw = JSON.parse(readFileSync(CACHE_PATH, 'utf-8'));
71
+ if (Date.now() - raw.fetchedAt > CACHE_TTL_MS)
72
+ return null;
73
+ return parseLiteLLMPricing(raw.data);
74
+ }
75
+ catch {
76
+ return null;
77
+ }
78
+ }
79
+ function saveDiskCache(data) {
80
+ try {
81
+ writeFileSync(CACHE_PATH, JSON.stringify({ fetchedAt: Date.now(), data }));
82
+ }
83
+ catch {
84
+ // Best-effort
85
+ }
86
+ }
87
+ function buildFallbackMap() {
88
+ const map = new Map();
89
+ for (const [key, { input, output, cacheRead }] of Object.entries(FALLBACK_PRICING)) {
90
+ map.set(key, {
91
+ inputCostPerToken: input / 1_000_000,
92
+ outputCostPerToken: output / 1_000_000,
93
+ cacheReadCostPerToken: cacheRead != null ? cacheRead / 1_000_000 : undefined,
94
+ });
95
+ }
96
+ return map;
97
+ }
98
+ /**
99
+ * Load the pricing database. Tries (in order):
100
+ * 1. In-memory cache
101
+ * 2. Disk cache (24h TTL)
102
+ * 3. Fetch from GitHub
103
+ * 4. Hardcoded fallback
104
+ */
105
+ export async function loadPricing() {
106
+ if (pricingCache)
107
+ return pricingCache;
108
+ // Try disk cache
109
+ const diskCached = loadDiskCache();
110
+ if (diskCached && diskCached.size > 50) {
111
+ pricingCache = diskCached;
112
+ return pricingCache;
113
+ }
114
+ // Fetch from GitHub
115
+ try {
116
+ const response = await fetch(LITELLM_PRICING_URL, {
117
+ signal: AbortSignal.timeout(10_000),
118
+ });
119
+ if (response.ok) {
120
+ const data = await response.json();
121
+ const parsed = parseLiteLLMPricing(data);
122
+ if (parsed.size > 50) {
123
+ pricingCache = parsed;
124
+ saveDiskCache(data);
125
+ return pricingCache;
126
+ }
127
+ }
128
+ }
129
+ catch {
130
+ // Network failure — fall through to fallback
131
+ }
132
+ // Fallback
133
+ pricingCache = buildFallbackMap();
134
+ return pricingCache;
135
+ }
136
+ /** Synchronous version — returns fallback if not yet loaded */
137
+ export function getPricingSync() {
138
+ if (pricingCache)
139
+ return pricingCache;
140
+ // Try disk cache synchronously
141
+ const diskCached = loadDiskCache();
142
+ if (diskCached && diskCached.size > 50) {
143
+ pricingCache = diskCached;
144
+ return pricingCache;
145
+ }
146
+ pricingCache = buildFallbackMap();
147
+ return pricingCache;
148
+ }
149
+ /**
150
+ * Look up pricing for a model. Tries exact match, then prefix match.
151
+ */
152
+ export function getModelPricing(model) {
153
+ const pricing = getPricingSync();
154
+ // Exact match
155
+ if (pricing.has(model))
156
+ return pricing.get(model);
157
+ // Prefix match (e.g., "gpt-5.4-turbo" matches "gpt-5.4")
158
+ for (const [key, value] of pricing) {
159
+ if (model.startsWith(key) || key.startsWith(model)) {
160
+ return value;
161
+ }
162
+ }
163
+ return null;
164
+ }
165
+ /**
166
+ * Calculate estimated cost in USD for a set of tokens.
167
+ *
168
+ * @param model - Model name
169
+ * @param inputTokens - Total input tokens (summed across all calls)
170
+ * @param outputTokens - Total output tokens
171
+ * @param cacheReadTokens - Tokens served from cache (discounted rate)
172
+ * @param maxInputPerCall - Largest single-call input context size.
173
+ * Used to determine tiered pricing (e.g., gpt-5.4 charges 2x above 272K context).
174
+ * If not provided, uses base tier pricing.
175
+ */
176
+ export function calculateCost(model, inputTokens, outputTokens, cacheReadTokens = 0, maxInputPerCall = 0) {
177
+ let pricing = getModelPricing(model);
178
+ if (!pricing)
179
+ return 0;
180
+ if (inputTokens === 0 && outputTokens === 0)
181
+ return 0;
182
+ // Check for context-length tiered pricing override
183
+ const tiered = TIERED_PRICING[model];
184
+ if (tiered && maxInputPerCall > tiered.threshold) {
185
+ pricing = {
186
+ ...pricing,
187
+ inputCostPerToken: tiered.input / 1_000_000,
188
+ outputCostPerToken: tiered.output / 1_000_000,
189
+ };
190
+ }
191
+ const nonCachedInput = Math.max(0, inputTokens - cacheReadTokens);
192
+ const cacheReadCost = pricing.cacheReadCostPerToken ?? pricing.inputCostPerToken;
193
+ return (nonCachedInput * pricing.inputCostPerToken +
194
+ cacheReadTokens * cacheReadCost +
195
+ outputTokens * pricing.outputCostPerToken);
196
+ }
197
+ //# sourceMappingURL=model-pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-pricing.js","sourceRoot":"","sources":["../src/model-pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * Multi-Actor Session — coordinated multi-user browser testing
3
+ *
4
+ * Each actor gets an isolated BrowserContext (separate cookies, localStorage,
5
+ * auth state) while sharing the same Browser instance. Consumers orchestrate
6
+ * with standard async/await for sequential flows and Promise.all() for parallel.
7
+ *
8
+ * ```typescript
9
+ * const session = await MultiActorSession.create(browser, {
10
+ * actors: {
11
+ * admin: { storageState: '.auth/admin.json' },
12
+ * partner: { storageState: '.auth/partner.json' },
13
+ * user1: {},
14
+ * },
15
+ * agentConfig: { model: 'gpt-4o', vision: true },
16
+ * });
17
+ *
18
+ * await session.actor('admin').run({ goal: 'Create quest', startUrl: '/admin' });
19
+ * await session.actor('partner').run({ goal: 'Approve quest' });
20
+ *
21
+ * await session.parallel(
22
+ * ['user1', { goal: 'Start quest' }],
23
+ * ['admin', { goal: 'Monitor dashboard' }],
24
+ * );
25
+ *
26
+ * await session.close();
27
+ * ```
28
+ */
29
+ import type { Browser, BrowserContext, BrowserContextOptions, Page } from 'playwright';
30
+ import { PlaywrightDriver } from './drivers/playwright.js';
31
+ import type { PlaywrightDriverOptions } from './drivers/playwright.js';
32
+ import { AgentRunner } from './runner.js';
33
+ import type { Scenario, AgentConfig, AgentResult, Turn } from './types.js';
34
+ import type { ProjectStore } from './memory/project-store.js';
35
+ export interface ActorConfig {
36
+ /** Playwright storage state for pre-authenticated sessions */
37
+ storageState?: string | BrowserContextOptions['storageState'];
38
+ /** Setup hook called after context+page creation (e.g., manual login) */
39
+ setup?: (page: Page) => Promise<void>;
40
+ /** Agent config overrides for this actor (merged on top of shared config) */
41
+ agentConfig?: Partial<AgentConfig>;
42
+ /** Playwright context options (viewport, locale, etc.) */
43
+ contextOptions?: BrowserContextOptions;
44
+ /** Playwright driver options (timeout, screenshots) */
45
+ driverOptions?: PlaywrightDriverOptions;
46
+ }
47
+ export interface MultiActorSessionConfig {
48
+ /** Named actors keyed by role/identity */
49
+ actors: Record<string, ActorConfig>;
50
+ /** Shared agent config applied to all actors (per-actor overrides win) */
51
+ agentConfig?: AgentConfig;
52
+ /** Turn callback receives actor name + turn for cross-actor logging */
53
+ onTurn?: (actorName: string, turn: Turn) => void;
54
+ /** Project memory store shared across actors */
55
+ projectStore?: ProjectStore;
56
+ }
57
+ export declare class Actor {
58
+ readonly name: string;
59
+ private _context;
60
+ private _page;
61
+ private _driver;
62
+ private _runner;
63
+ private _results;
64
+ constructor(name: string, _context: BrowserContext, _page: Page, _driver: PlaywrightDriver, _runner: AgentRunner);
65
+ /** Run a scenario with this actor's agent. Results accumulate across calls. */
66
+ run(scenario: Scenario): Promise<AgentResult>;
67
+ /** Raw Playwright page for direct operations / assertions */
68
+ get page(): Page;
69
+ /** BrowserContext for cookie/storage inspection */
70
+ get context(): BrowserContext;
71
+ /** PlaywrightDriver for low-level driver access */
72
+ get driver(): PlaywrightDriver;
73
+ /** All results from this actor's runs */
74
+ get results(): readonly AgentResult[];
75
+ /** Most recent result, or undefined if no runs yet */
76
+ get lastResult(): AgentResult | undefined;
77
+ }
78
+ export declare class MultiActorSession {
79
+ private _actors;
80
+ private _closed;
81
+ private constructor();
82
+ /**
83
+ * Create a session with isolated browser contexts per actor.
84
+ *
85
+ * 1. Creates BrowserContext + Page + PlaywrightDriver + AgentRunner per actor
86
+ * 2. Calls setup() hooks if provided
87
+ * 3. Returns session ready for orchestration
88
+ */
89
+ static create(browser: Browser, config: MultiActorSessionConfig): Promise<MultiActorSession>;
90
+ /** Get an actor by name. Throws with available names on miss. */
91
+ actor(name: string): Actor;
92
+ /**
93
+ * Run multiple actors in parallel. Returns a Map of actor name → result.
94
+ *
95
+ * If any actor fails (throws), all results collected so far are still
96
+ * available via each actor's `.results` array.
97
+ */
98
+ parallel(...tasks: [actorName: string, scenario: Scenario][]): Promise<Map<string, AgentResult>>;
99
+ /** Close all browser contexts. Pages close with their contexts. */
100
+ close(): Promise<void>;
101
+ /** All results across all actors, keyed by actor name */
102
+ get results(): Map<string, readonly AgentResult[]>;
103
+ /** All actors for iteration */
104
+ get allActors(): Actor[];
105
+ /** Actor names */
106
+ get actorNames(): string[];
107
+ }
108
+ //# sourceMappingURL=multi-actor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"multi-actor.d.ts","sourceRoot":"","sources":["../src/multi-actor.ts"],"names":[],"mappings":"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"}