flowseeker 0.1.7

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 (82) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/COMPATIBILITY.md +281 -0
  3. package/LICENSE +21 -0
  4. package/README.md +375 -0
  5. package/dist/adapters/frameworkEdges.js +586 -0
  6. package/dist/agent/approvalPolicy.js +81 -0
  7. package/dist/agent/commandRunner.js +166 -0
  8. package/dist/agent/flowCommandRunner.js +124 -0
  9. package/dist/agent/mcpToolRunner.js +167 -0
  10. package/dist/auth/githubAuth.js +71 -0
  11. package/dist/auth/modelList.js +127 -0
  12. package/dist/auth/oauthHandler.js +377 -0
  13. package/dist/chat/nativeChatParticipant.js +616 -0
  14. package/dist/cli/mcpServer.js +383 -0
  15. package/dist/cli/runEvaluation.js +789 -0
  16. package/dist/cli/runReplay.js +481 -0
  17. package/dist/commands/checkHostCompatibility.js +149 -0
  18. package/dist/commands/copyAgentPrompt.js +52 -0
  19. package/dist/commands/copyContext.js +54 -0
  20. package/dist/commands/explainSelectionRelevance.js +57 -0
  21. package/dist/commands/findRelevantContext.js +127 -0
  22. package/dist/commands/openEvidence.js +49 -0
  23. package/dist/commands/openMcpConfig.js +81 -0
  24. package/dist/commands/openRelatedTests.js +54 -0
  25. package/dist/commands/rebuildIndex.js +45 -0
  26. package/dist/commands/runEvaluationSuite.js +323 -0
  27. package/dist/commands/runReplaySuite.js +228 -0
  28. package/dist/config/defaultConfig.js +72 -0
  29. package/dist/config/loadConfig.js +84 -0
  30. package/dist/config/loadConfigFromPath.js +60 -0
  31. package/dist/extension.js +513 -0
  32. package/dist/gateway/agentPrompts.js +176 -0
  33. package/dist/gateway/aiGateway.js +1255 -0
  34. package/dist/gateway/aiProviders.js +901 -0
  35. package/dist/gateway/contextExpansion.js +331 -0
  36. package/dist/gateway/editProposalStore.js +238 -0
  37. package/dist/gateway/planProposalStore.js +28 -0
  38. package/dist/index/cacheStore.js +51 -0
  39. package/dist/index/chunker.js +45 -0
  40. package/dist/index/dependencyExtractor.js +107 -0
  41. package/dist/index/fileDiscovery.js +177 -0
  42. package/dist/index/structuredExtractor.js +256 -0
  43. package/dist/index/workspaceIndex.js +518 -0
  44. package/dist/mcp/mcpConfig.js +154 -0
  45. package/dist/mcp/mcpProvider.js +109 -0
  46. package/dist/mcp/mcpTools.js +215 -0
  47. package/dist/pipeline/agentPrompt.js +79 -0
  48. package/dist/pipeline/agentPromptHeadless.js +85 -0
  49. package/dist/pipeline/contextBlueprint.js +346 -0
  50. package/dist/pipeline/contextPack.js +80 -0
  51. package/dist/pipeline/diffPreview.js +79 -0
  52. package/dist/pipeline/evaluationMetrics.js +154 -0
  53. package/dist/pipeline/evidenceGraph.js +389 -0
  54. package/dist/pipeline/feedback.js +215 -0
  55. package/dist/pipeline/fileGroups.js +84 -0
  56. package/dist/pipeline/fileScanner.js +866 -0
  57. package/dist/pipeline/nodeScan.js +219 -0
  58. package/dist/pipeline/ranker.js +563 -0
  59. package/dist/pipeline/responseLanguage.js +39 -0
  60. package/dist/pipeline/retrievalPlan.js +163 -0
  61. package/dist/pipeline/runHeadless.js +54 -0
  62. package/dist/pipeline/runPipeline.js +114 -0
  63. package/dist/pipeline/solvePacket.js +382 -0
  64. package/dist/pipeline/subsystem.js +257 -0
  65. package/dist/pipeline/taskUnderstanding.js +453 -0
  66. package/dist/pipeline/tokenSavings.js +146 -0
  67. package/dist/pipeline/universalScan.js +216 -0
  68. package/dist/pipeline/verifyAfterApply.js +233 -0
  69. package/dist/runtime/capabilities.js +71 -0
  70. package/dist/runtime/hostDetect.js +98 -0
  71. package/dist/runtime/hostTier.js +68 -0
  72. package/dist/runtime/statusBar.js +80 -0
  73. package/dist/skills/skillRegistry.js +208 -0
  74. package/dist/types.js +3 -0
  75. package/dist/ui/chatViewProvider.js +3899 -0
  76. package/dist/ui/resultTreeProvider.js +174 -0
  77. package/dist/usage/quotaTracker.js +358 -0
  78. package/dist/utils/async.js +30 -0
  79. package/dist/utils/logger.js +64 -0
  80. package/dist/utils/text.js +364 -0
  81. package/dist/utils/updateChecker.js +140 -0
  82. package/package.json +561 -0
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ResultTreeProvider = exports.ResultNode = void 0;
37
+ const vscode = __importStar(require("vscode"));
38
+ class ResultNode {
39
+ constructor(type, label, role, unit) {
40
+ this.type = type;
41
+ this.label = label;
42
+ this.role = role;
43
+ this.unit = unit;
44
+ }
45
+ }
46
+ exports.ResultNode = ResultNode;
47
+ const roleLabels = [
48
+ { role: "edit_candidate", label: "Most Likely Edit Areas" },
49
+ { role: "read_context", label: "Read Before Editing" },
50
+ { role: "impact", label: "Impact And Risk" },
51
+ { role: "test", label: "Tests To Run Or Update" },
52
+ { role: "verify", label: "Verify" },
53
+ { role: "unknown", label: "Uncertain Candidates" }
54
+ ];
55
+ class ResultTreeProvider {
56
+ constructor() {
57
+ this.changeEmitter = new vscode.EventEmitter();
58
+ this.onDidChangeTreeData = this.changeEmitter.event;
59
+ this.checkedIds = new Set();
60
+ }
61
+ setResult(result) {
62
+ this.result = result;
63
+ this.checkedIds = new Set(result.units.filter((unit) => unit.role !== "unknown").map((unit) => unit.id));
64
+ this.changeEmitter.fire();
65
+ }
66
+ getResult() {
67
+ return this.result;
68
+ }
69
+ setChecked(unitId, checked) {
70
+ if (checked) {
71
+ this.checkedIds.add(unitId);
72
+ }
73
+ else {
74
+ this.checkedIds.delete(unitId);
75
+ }
76
+ }
77
+ getSelectedUnits() {
78
+ if (!this.result) {
79
+ return [];
80
+ }
81
+ const selected = this.result.units.filter((unit) => this.checkedIds.has(unit.id));
82
+ return selected.length > 0 ? selected : this.result.units;
83
+ }
84
+ findUnitsForFile(filePath) {
85
+ return this.result?.units.filter((unit) => unit.file === filePath) ?? [];
86
+ }
87
+ getUnitsByRole(role) {
88
+ return this.result?.units.filter((unit) => unit.role === role) ?? [];
89
+ }
90
+ getTreeItem(element) {
91
+ if (element.type === "empty") {
92
+ const item = new vscode.TreeItem(element.label, vscode.TreeItemCollapsibleState.None);
93
+ item.iconPath = new vscode.ThemeIcon("search");
94
+ return item;
95
+ }
96
+ if (element.type === "group") {
97
+ const count = this.result?.units.filter((unit) => unit.role === element.role).length ?? 0;
98
+ const item = new vscode.TreeItem(`${element.label} (${count})`, vscode.TreeItemCollapsibleState.Expanded);
99
+ item.contextValue = "flowseekerGroup";
100
+ item.iconPath = iconForRole(element.role);
101
+ return item;
102
+ }
103
+ const unit = element.unit;
104
+ const range = unit?.range ? `:${unit.range.startLine}-${unit.range.endLine}` : "";
105
+ const item = new vscode.TreeItem(`${unit?.relativePath ?? element.label}${range}`, vscode.TreeItemCollapsibleState.None);
106
+ item.description = unit ? `${unit.kind} ${unit.tier} ${unit.confidence.toFixed(2)}` : undefined;
107
+ item.tooltip = unit ? tooltipForUnit(unit) : undefined;
108
+ item.contextValue = "flowseekerEvidence";
109
+ item.iconPath = unit ? iconForRole(unit.role) : undefined;
110
+ item.command = unit
111
+ ? {
112
+ command: "flowseeker.openEvidence",
113
+ title: "Open Evidence",
114
+ arguments: [unit]
115
+ }
116
+ : undefined;
117
+ if (unit) {
118
+ item.checkboxState = this.checkedIds.has(unit.id) ? vscode.TreeItemCheckboxState.Checked : vscode.TreeItemCheckboxState.Unchecked;
119
+ }
120
+ return item;
121
+ }
122
+ getChildren(element) {
123
+ if (!this.result) {
124
+ return [new ResultNode("empty", "Run FlowSeeker: Find Relevant Context")];
125
+ }
126
+ if (!element) {
127
+ return roleLabels
128
+ .filter(({ role }) => this.result?.units.some((unit) => unit.role === role))
129
+ .map(({ role, label }) => new ResultNode("group", label, role));
130
+ }
131
+ if (element.type === "group" && element.role) {
132
+ return this.result.units
133
+ .filter((unit) => unit.role === element.role)
134
+ .map((unit) => new ResultNode("evidence", unit.relativePath, element.role, unit));
135
+ }
136
+ return [];
137
+ }
138
+ }
139
+ exports.ResultTreeProvider = ResultTreeProvider;
140
+ function tooltipForUnit(unit) {
141
+ const lines = [
142
+ `**${unit.relativePath}**`,
143
+ "",
144
+ `Kind: ${unit.kind}`,
145
+ `Role: ${unit.role}`,
146
+ `Score: ${unit.score.toFixed(1)}`,
147
+ `Confidence: ${unit.confidence.toFixed(2)}`,
148
+ `Retrieval passes: ${unit.retrievalPasses.length > 0 ? unit.retrievalPasses.join(", ") : "seed"}`,
149
+ `Imports: ${unit.imports.length > 0 ? unit.imports.slice(0, 5).join(", ") : "none"}`,
150
+ "",
151
+ "Reasons:",
152
+ ...unit.reasons.map((reason) => `- ${reason}`)
153
+ ];
154
+ return new vscode.MarkdownString(lines.join("\n"));
155
+ }
156
+ function iconForRole(role) {
157
+ switch (role) {
158
+ case "edit_candidate":
159
+ return new vscode.ThemeIcon("edit");
160
+ case "read_context":
161
+ return new vscode.ThemeIcon("book");
162
+ case "impact":
163
+ return new vscode.ThemeIcon("references");
164
+ case "test":
165
+ return new vscode.ThemeIcon("beaker");
166
+ case "verify":
167
+ return new vscode.ThemeIcon("checklist");
168
+ case "unknown":
169
+ return new vscode.ThemeIcon("question");
170
+ default:
171
+ return new vscode.ThemeIcon("folder");
172
+ }
173
+ }
174
+ //# sourceMappingURL=resultTreeProvider.js.map
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.QuotaTracker = void 0;
37
+ exports.formatUsageSummary = formatUsageSummary;
38
+ const vscode = __importStar(require("vscode"));
39
+ const PRICING = {
40
+ "gpt-5.2": { inputPer1M: 2.50, outputPer1M: 10.00 },
41
+ "gpt-5.5": { inputPer1M: 2.50, outputPer1M: 10.00 },
42
+ "gpt-5": { inputPer1M: 2.50, outputPer1M: 10.00 },
43
+ "gpt-4o": { inputPer1M: 2.50, outputPer1M: 10.00 },
44
+ "gpt-4o-mini": { inputPer1M: 0.15, outputPer1M: 0.60 },
45
+ "gpt-4.1": { inputPer1M: 2.00, outputPer1M: 8.00 },
46
+ "gpt-4.1-mini": { inputPer1M: 0.40, outputPer1M: 1.60 },
47
+ "gpt-4.1-nano": { inputPer1M: 0.10, outputPer1M: 0.40 },
48
+ "o4-mini": { inputPer1M: 1.10, outputPer1M: 4.40 },
49
+ "claude-sonnet-4-5": { inputPer1M: 3.00, outputPer1M: 15.00 },
50
+ "claude-sonnet-4": { inputPer1M: 3.00, outputPer1M: 15.00 },
51
+ "claude-opus-4": { inputPer1M: 15.00, outputPer1M: 75.00 },
52
+ "claude-haiku-4-5": { inputPer1M: 0.80, outputPer1M: 4.00 },
53
+ "gemini-2.5-flash": { inputPer1M: 0.15, outputPer1M: 0.60 },
54
+ "gemini-2.5-pro": { inputPer1M: 1.25, outputPer1M: 10.00 },
55
+ "grok-4": { inputPer1M: 2.00, outputPer1M: 8.00 },
56
+ "deepseek-chat": { inputPer1M: 0.27, outputPer1M: 1.10 },
57
+ "deepseek-reasoner": { inputPer1M: 0.55, outputPer1M: 2.19 },
58
+ "mistral-large-latest": { inputPer1M: 2.00, outputPer1M: 6.00 },
59
+ "llama-3.3-70b": { inputPer1M: 0.59, outputPer1M: 0.79 },
60
+ };
61
+ const DEFAULT_MONTHLY_COST = 10;
62
+ const QUOTA_LIMITS_KEY = "flowseeker.usage.limits";
63
+ const BUDGET_KEY = "flowseeker.usage.budget";
64
+ const LIVE_QUOTA_KEY = "flowseeker.usage.liveQuota.v1";
65
+ function getPricing(model) {
66
+ const lower = model.toLowerCase();
67
+ for (const [key, tier] of Object.entries(PRICING)) {
68
+ const lowerKey = key.toLowerCase();
69
+ if (lower === lowerKey || lower.includes(lowerKey) || lowerKey.includes(lower)) {
70
+ return tier;
71
+ }
72
+ }
73
+ return { inputPer1M: 1.00, outputPer1M: 4.00 };
74
+ }
75
+ function calculateCost(model, inputTokens, outputTokens) {
76
+ const pricing = getPricing(model);
77
+ return (inputTokens / 1000000) * pricing.inputPer1M + (outputTokens / 1000000) * pricing.outputPer1M;
78
+ }
79
+ function usageStateKey() {
80
+ return "flowseeker.usage.records.v1";
81
+ }
82
+ function dailyResetMs() {
83
+ const now = new Date();
84
+ const tomorrow = new Date(now);
85
+ tomorrow.setDate(tomorrow.getDate() + 1);
86
+ tomorrow.setHours(0, 0, 0, 0);
87
+ return tomorrow.getTime();
88
+ }
89
+ function monthlyResetMs() {
90
+ const now = new Date();
91
+ const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1);
92
+ nextMonth.setHours(0, 0, 0, 0);
93
+ return nextMonth.getTime();
94
+ }
95
+ class QuotaTracker {
96
+ constructor(context) {
97
+ this.context = context;
98
+ this.records = [];
99
+ this.limits = {};
100
+ this.liveQuota = new Map();
101
+ this.onChangeCallbacks = [];
102
+ this.load();
103
+ }
104
+ onDidChange(callback) {
105
+ this.onChangeCallbacks.push(callback);
106
+ return { dispose: () => { this.onChangeCallbacks = this.onChangeCallbacks.filter((cb) => cb !== callback); } };
107
+ }
108
+ notifyChange() {
109
+ for (const cb of this.onChangeCallbacks) {
110
+ try {
111
+ cb();
112
+ }
113
+ catch { /* ignore */ }
114
+ }
115
+ }
116
+ record(provider, model, inputTokens, outputTokens, providerQuota) {
117
+ const cost = calculateCost(model, inputTokens, outputTokens);
118
+ this.records.push({
119
+ provider,
120
+ model,
121
+ timestamp: Date.now(),
122
+ inputTokens,
123
+ outputTokens,
124
+ costUSD: cost
125
+ });
126
+ if (this.records.length > 2000) {
127
+ this.records = this.records.slice(-2000);
128
+ }
129
+ // Store latest live quota from provider
130
+ if (providerQuota && hasQuotaSignal(providerQuota)) {
131
+ this.liveQuota.set(`${provider}|${model}`, providerQuota);
132
+ this.saveLiveQuota();
133
+ }
134
+ this.save();
135
+ this.notifyChange();
136
+ void this.checkBudget();
137
+ }
138
+ getSummary() {
139
+ const todayStart = new Date();
140
+ todayStart.setHours(0, 0, 0, 0);
141
+ const monthStart = new Date(new Date().getFullYear(), new Date().getMonth(), 1);
142
+ const now = Date.now();
143
+ const modelMap = new Map();
144
+ let totalInput = 0;
145
+ let totalOutput = 0;
146
+ let totalCost = 0;
147
+ let todayCost = 0;
148
+ for (const r of this.records) {
149
+ totalInput += r.inputTokens;
150
+ totalOutput += r.outputTokens;
151
+ totalCost += r.costUSD;
152
+ const key = `${r.provider}|${r.model}`;
153
+ let entry = modelMap.get(key);
154
+ if (!entry) {
155
+ entry = {
156
+ provider: r.provider,
157
+ model: r.model,
158
+ dailyTokens: 0,
159
+ monthlyCost: 0,
160
+ requestsToday: 0
161
+ };
162
+ modelMap.set(key, entry);
163
+ }
164
+ if (r.timestamp >= todayStart.getTime()) {
165
+ entry.dailyTokens += r.inputTokens + r.outputTokens;
166
+ entry.requestsToday++;
167
+ todayCost += r.costUSD;
168
+ }
169
+ if (r.timestamp >= monthStart.getTime()) {
170
+ entry.monthlyCost += r.costUSD;
171
+ }
172
+ }
173
+ const quotaKeys = new Set([...modelMap.keys(), ...this.liveQuota.keys()]);
174
+ const models = [];
175
+ for (const key of quotaKeys) {
176
+ const entry = modelMap.get(key) ?? createEmptyEntryFromKey(key);
177
+ if (!entry) {
178
+ continue;
179
+ }
180
+ const userLimit = this.getLimit(entry.provider, entry.model);
181
+ const liveQuota = this.liveQuota.get(key);
182
+ const hasLiveQuota = liveQuota && hasQuotaSignal(liveQuota);
183
+ const hasLiveTokenLimit = typeof liveQuota?.tokensLimit === "number";
184
+ // Prefer provider quota when available. If a provider only exposes request
185
+ // limits, keep token quota unknown instead of showing a fake token cap.
186
+ const dailyLimit = hasLiveTokenLimit
187
+ ? liveQuota.tokensLimit
188
+ : userLimit.dailyTokens ?? -1;
189
+ const monthlyLimit = userLimit.monthlyCost ?? DEFAULT_MONTHLY_COST;
190
+ const quotaSource = hasLiveQuota
191
+ ? "live"
192
+ : userLimit.dailyTokens
193
+ ? "manual"
194
+ : "unknown";
195
+ // Calculate tokens used from live quota (remaining) if available
196
+ const tokensUsed = hasLiveTokenLimit && liveQuota?.tokensRemaining !== undefined
197
+ ? dailyLimit - liveQuota.tokensRemaining
198
+ : entry.dailyTokens;
199
+ const dailyPct = dailyLimit > 0 ? Math.min(100, Math.round((tokensUsed / dailyLimit) * 100)) : -1;
200
+ models.push({
201
+ provider: entry.provider,
202
+ model: entry.model,
203
+ dailyTokensUsed: tokensUsed,
204
+ dailyTokenLimit: dailyLimit,
205
+ dailyPct,
206
+ quotaSource,
207
+ requestsLimit: liveQuota?.requestsLimit,
208
+ requestsRemaining: liveQuota?.requestsRemaining,
209
+ monthlyCostUsed: Math.round(entry.monthlyCost * 10000) / 10000,
210
+ monthlyCostLimit: monthlyLimit,
211
+ monthlyPct: Math.min(100, Math.round((entry.monthlyCost / monthlyLimit) * 100)),
212
+ requestsToday: entry.requestsToday,
213
+ dailyResetMs: dailyResetMs(),
214
+ monthlyResetMs: monthlyResetMs()
215
+ });
216
+ }
217
+ models.sort((a, b) => b.monthlyCostUsed - a.monthlyCostUsed);
218
+ const weekAgo = now - 7 * 24 * 60 * 60 * 1000;
219
+ let weekCost = 0;
220
+ for (const r of this.records) {
221
+ if (r.timestamp >= weekAgo) {
222
+ weekCost += r.costUSD;
223
+ }
224
+ }
225
+ return {
226
+ totalRequests: this.records.length,
227
+ totalInputTokens: totalInput,
228
+ totalOutputTokens: totalOutput,
229
+ totalCostUSD: Math.round(totalCost * 10000) / 10000,
230
+ todayCostUSD: Math.round(todayCost * 10000) / 10000,
231
+ thisWeekCostUSD: Math.round(weekCost * 10000) / 10000,
232
+ models
233
+ };
234
+ }
235
+ async setLimit(provider, model, limit) {
236
+ const key = `${provider}|${model}`;
237
+ this.limits[key] = limit;
238
+ await this.saveLimits();
239
+ }
240
+ getLimit(provider, model) {
241
+ const key = `${provider}|${model}`;
242
+ return this.limits[key] ?? {};
243
+ }
244
+ async setBudget(limitUSD) {
245
+ await this.context.globalState.update(BUDGET_KEY, limitUSD);
246
+ }
247
+ getBudget() {
248
+ return this.context.globalState.get(BUDGET_KEY, 0);
249
+ }
250
+ async clearHistory() {
251
+ this.records = [];
252
+ await this.context.globalState.update(usageStateKey(), undefined);
253
+ }
254
+ load() {
255
+ const stored = this.context.globalState.get(usageStateKey(), []);
256
+ this.records = Array.isArray(stored) ? stored : [];
257
+ this.limits = this.context.globalState.get(QUOTA_LIMITS_KEY, {});
258
+ const storedLiveQuota = this.context.globalState.get(LIVE_QUOTA_KEY, {});
259
+ this.liveQuota = new Map(Object.entries(storedLiveQuota).filter((entry) => hasQuotaSignal(entry[1])));
260
+ }
261
+ save() {
262
+ void this.context.globalState.update(usageStateKey(), this.records);
263
+ }
264
+ async saveLimits() {
265
+ await this.context.globalState.update(QUOTA_LIMITS_KEY, this.limits);
266
+ }
267
+ saveLiveQuota() {
268
+ void this.context.globalState.update(LIVE_QUOTA_KEY, Object.fromEntries(this.liveQuota));
269
+ }
270
+ async checkBudget() {
271
+ const budget = this.getBudget();
272
+ if (budget <= 0) {
273
+ return;
274
+ }
275
+ const summary = this.getSummary();
276
+ const pctUsed = summary.todayCostUSD / budget;
277
+ if (pctUsed >= 0.75 && pctUsed < 1.0) {
278
+ void vscode.window.showWarningMessage(`FlowSeeker: ${Math.round(pctUsed * 100)}% of daily budget used ($${summary.todayCostUSD.toFixed(2)} / $${budget.toFixed(2)})`, "Dismiss");
279
+ }
280
+ else if (pctUsed >= 1.0) {
281
+ void vscode.window.showErrorMessage(`FlowSeeker: Daily budget exceeded ($${summary.todayCostUSD.toFixed(2)} / $${budget.toFixed(2)}).`, "Set new budget", "Dismiss").then((choice) => {
282
+ if (choice === "Set new budget") {
283
+ void vscode.commands.executeCommand("flowseeker.setBudget");
284
+ }
285
+ });
286
+ }
287
+ }
288
+ }
289
+ exports.QuotaTracker = QuotaTracker;
290
+ function hasQuotaSignal(value) {
291
+ return Boolean(value &&
292
+ (value.tokensLimit !== undefined ||
293
+ value.tokensRemaining !== undefined ||
294
+ value.requestsLimit !== undefined ||
295
+ value.requestsRemaining !== undefined));
296
+ }
297
+ function createEmptyEntryFromKey(key) {
298
+ const separator = key.indexOf("|");
299
+ if (separator <= 0) {
300
+ return undefined;
301
+ }
302
+ return {
303
+ provider: key.slice(0, separator),
304
+ model: key.slice(separator + 1),
305
+ dailyTokens: 0,
306
+ monthlyCost: 0,
307
+ requestsToday: 0
308
+ };
309
+ }
310
+ function formatUsageSummary(summary) {
311
+ const lines = [
312
+ `**Today:** $${summary.todayCostUSD.toFixed(3)} | **Week:** $${summary.thisWeekCostUSD.toFixed(3)} | **Total:** $${summary.totalCostUSD.toFixed(3)}`,
313
+ `**Requests:** ${summary.totalRequests} | **Tokens:** ${formatTokenCount(summary.totalInputTokens + summary.totalOutputTokens)}`,
314
+ "",
315
+ ];
316
+ if (summary.models.length > 0) {
317
+ lines.push("| Provider/Model | Today Tokens | Daily % | Month Cost | Month % |");
318
+ lines.push("|---|---|---|---|---|");
319
+ for (const m of summary.models.slice(0, 10)) {
320
+ const dailyLabel = m.dailyTokenLimit > 0
321
+ ? `${formatTokenCount(m.dailyTokensUsed)} / ${formatTokenCount(m.dailyTokenLimit)}`
322
+ : `${formatTokenCount(m.dailyTokensUsed)} / token limit unknown`;
323
+ const dailyPctLabel = m.dailyPct >= 0
324
+ ? `${progressBar(m.dailyPct)} ${m.dailyPct}%`
325
+ : formatRequestQuota(m);
326
+ lines.push(`| ${m.provider} / ${m.model} | ${dailyLabel} | ${dailyPctLabel} | $${m.monthlyCostUsed.toFixed(2)} / $${m.monthlyCostLimit} | ${progressBar(m.monthlyPct)} ${m.monthlyPct}% |`);
327
+ }
328
+ }
329
+ return lines.join("\n");
330
+ }
331
+ function formatRequestQuota(model) {
332
+ if (model.requestsLimit !== undefined && model.requestsRemaining !== undefined) {
333
+ const used = Math.max(0, model.requestsLimit - model.requestsRemaining);
334
+ return `${formatTokenCount(used)} / ${formatTokenCount(model.requestsLimit)} requests`;
335
+ }
336
+ if (model.requestsRemaining !== undefined) {
337
+ return `${formatTokenCount(model.requestsRemaining)} requests remaining`;
338
+ }
339
+ if (model.requestsLimit !== undefined) {
340
+ return `${formatTokenCount(model.requestsLimit)} request limit`;
341
+ }
342
+ return model.quotaSource === "live" ? "live quota" : "unknown";
343
+ }
344
+ function progressBar(pct) {
345
+ const filled = Math.round(pct / 10);
346
+ const empty = 10 - filled;
347
+ return `[${"#".repeat(filled)}${".".repeat(empty)}]`;
348
+ }
349
+ function formatTokenCount(n) {
350
+ if (n >= 1000000) {
351
+ return `${(n / 1000000).toFixed(1)}M`;
352
+ }
353
+ if (n >= 1000) {
354
+ return `${(n / 1000).toFixed(0)}K`;
355
+ }
356
+ return String(n);
357
+ }
358
+ //# sourceMappingURL=quotaTracker.js.map
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.withTimeout = withTimeout;
4
+ exports.checkCancelled = checkCancelled;
5
+ async function withTimeout(promise, ms, label) {
6
+ if (ms <= 0) {
7
+ return promise;
8
+ }
9
+ let timeoutId;
10
+ const timeout = new Promise((_resolve, reject) => {
11
+ timeoutId = setTimeout(() => {
12
+ reject(new Error(`Timed out after ${ms}ms: ${label}`));
13
+ }, ms);
14
+ });
15
+ try {
16
+ const result = await Promise.race([promise, timeout]);
17
+ return result;
18
+ }
19
+ finally {
20
+ if (timeoutId) {
21
+ clearTimeout(timeoutId);
22
+ }
23
+ }
24
+ }
25
+ function checkCancelled(token) {
26
+ if (token?.isCancellationRequested) {
27
+ throw new Error("Cancelled");
28
+ }
29
+ }
30
+ //# sourceMappingURL=async.js.map
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.initializeLogger = initializeLogger;
37
+ exports.showLog = showLog;
38
+ exports.logInfo = logInfo;
39
+ exports.logWarn = logWarn;
40
+ exports.logError = logError;
41
+ const vscode = __importStar(require("vscode"));
42
+ let channel;
43
+ function initializeLogger(context) {
44
+ channel = vscode.window.createOutputChannel("FlowSeeker");
45
+ context.subscriptions.push(channel);
46
+ }
47
+ function showLog() {
48
+ channel?.show(true);
49
+ }
50
+ function logInfo(message) {
51
+ write("INFO", message);
52
+ }
53
+ function logWarn(message) {
54
+ write("WARN", message);
55
+ }
56
+ function logError(message, error) {
57
+ const suffix = error instanceof Error ? `\n${error.stack ?? error.message}` : error ? `\n${String(error)}` : "";
58
+ write("ERROR", `${message}${suffix}`);
59
+ }
60
+ function write(level, message) {
61
+ const timestamp = new Date().toISOString();
62
+ channel?.appendLine(`[${level}] ${timestamp} - ${message}`);
63
+ }
64
+ //# sourceMappingURL=logger.js.map