lynkr 9.0.2 → 9.1.3

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 (65) hide show
  1. package/README.md +21 -10
  2. package/bin/cli.js +18 -1
  3. package/bin/lynkr-trajectory.js +136 -0
  4. package/bin/lynkr-usage.js +219 -0
  5. package/funding.json +110 -0
  6. package/package.json +4 -2
  7. package/public/dashboard.html +665 -0
  8. package/scripts/build-knn-index.js +130 -0
  9. package/scripts/calibrate-thresholds.js +197 -0
  10. package/scripts/compare-policies.js +67 -0
  11. package/scripts/learn-output-ratios.js +162 -0
  12. package/scripts/refresh-pricing.js +122 -0
  13. package/scripts/run-routerarena.js +26 -0
  14. package/scripts/sample-regret.js +84 -0
  15. package/scripts/train-risk-classifier.js +191 -0
  16. package/src/api/files-router.js +6 -6
  17. package/src/api/middleware/budget-enforcer.js +60 -0
  18. package/src/api/middleware/budget.js +19 -1
  19. package/src/api/middleware/load-shedding.js +17 -0
  20. package/src/api/middleware/tenant.js +21 -0
  21. package/src/api/openai-router.js +1 -1
  22. package/src/api/router.js +204 -87
  23. package/src/budget/hierarchical-budget.js +159 -0
  24. package/src/cache/semantic.js +28 -2
  25. package/src/clients/databricks.js +68 -10
  26. package/src/clients/openai-format.js +31 -5
  27. package/src/config/index.js +246 -43
  28. package/src/context/toon.js +5 -4
  29. package/src/dashboard/api.js +170 -0
  30. package/src/dashboard/router.js +13 -0
  31. package/src/headroom/client.js +3 -109
  32. package/src/headroom/index.js +0 -14
  33. package/src/memory/search.js +0 -50
  34. package/src/orchestrator/index.js +106 -11
  35. package/src/orchestrator/preflight.js +188 -0
  36. package/src/prompts/system.js +34 -6
  37. package/src/routing/bandit.js +246 -0
  38. package/src/routing/cascade.js +106 -0
  39. package/src/routing/complexity-analyzer.js +7 -15
  40. package/src/routing/confidence-scorer.js +121 -0
  41. package/src/routing/context-validator.js +71 -0
  42. package/src/routing/cost-optimizer.js +5 -2
  43. package/src/routing/deadline.js +52 -0
  44. package/src/routing/drift-monitor.js +113 -0
  45. package/src/routing/embedding-cache.js +77 -0
  46. package/src/routing/index.js +374 -4
  47. package/src/routing/interaction.js +183 -0
  48. package/src/routing/knn-router.js +206 -0
  49. package/src/routing/latency-tracker.js +113 -71
  50. package/src/routing/model-tiers.js +156 -6
  51. package/src/routing/output-ratios.js +57 -0
  52. package/src/routing/regret-estimator.js +91 -0
  53. package/src/routing/reward-pipeline.js +62 -0
  54. package/src/routing/risk-analyzer.js +194 -0
  55. package/src/routing/risk-classifier.js +130 -0
  56. package/src/routing/shadow-mode.js +77 -0
  57. package/src/routing/telemetry.js +7 -0
  58. package/src/routing/tenant-policy.js +96 -0
  59. package/src/routing/tokenizer.js +162 -0
  60. package/src/server.js +12 -0
  61. package/src/stores/file-store.js +42 -7
  62. package/src/tools/smart-selection.js +11 -2
  63. package/src/training/trajectory-compressor.js +266 -0
  64. package/src/usage/aggregator.js +206 -0
  65. package/src/utils/markdown-ansi.js +146 -0
package/README.md CHANGED
@@ -225,14 +225,15 @@ Routes requests to the right model based on 5-phase complexity analysis. Simple
225
225
  - **Graphify integration** — AST-based knowledge graph detects god nodes, community cohesion, blast radius across 19 languages
226
226
  - **Routing telemetry** — every decision recorded with quality scoring (0-100) and latency tracking (P50/P95/P99)
227
227
 
228
- ### Token Optimization (7 Phases)
229
- - **Smart tool selection** — only sends tools relevant to the current task
230
- - **Code Mode** — replaces 100+ MCP tools with 4 meta-tools (~96% token reduction)
231
- - **Distill compression** — structural similarity, delta rendering, smart dedup of repetitive tool outputs
232
- - **Prompt caching** — SHA-256 keyed LRU cache
233
- - **Memory deduplication** — eliminates repeated information across turns
234
- - **History compression** — sliding window with Distill-powered structural dedup
235
- - **Headroom sidecar** — optional 47-92% ML-based compression (Smart Crusher, CCR, LLMLingua)
228
+ ### Token Optimization (8 Phases)
229
+ - **MCP Code Mode** — replaces 100+ MCP tool schemas with 4 meta-tools (~96% reduction, lazy tool discovery)
230
+ - **Smart tool selection** — only sends tools relevant to the current task (50-70% reduction)
231
+ - **Prompt caching** — SHA-256 keyed LRU cache (30-45% reduction on repeated prompts)
232
+ - **Memory deduplication** — eliminates repeated information across turns (20-30% reduction)
233
+ - **Tool response truncation** — intelligent truncation of long outputs (15-25% reduction)
234
+ - **Dynamic system prompts** — adapt complexity to request type (10-20% reduction)
235
+ - **Distill compression** — structural similarity, delta rendering, smart dedup of repetitive tool outputs (20-40% reduction)
236
+ - **Headroom sidecar** — optional ML-based compression: Smart Crusher, CCR, LLMLingua (47-92% reduction)
236
237
 
237
238
  ### Enterprise Resilience
238
239
  - **Circuit breakers** — automatic failover with half-open probe recovery
@@ -254,12 +255,22 @@ SEMANTIC_CACHE_THRESHOLD=0.95
254
255
  ```
255
256
 
256
257
  ### MCP Integration + Code Mode
257
- Automatic Model Context Protocol server discovery and orchestration. Your MCP tools work through Lynkr without configuration. Enable Code Mode to replace 100+ MCP tool definitions with 4 lightweight meta-tools:
258
+ Automatic Model Context Protocol server discovery and orchestration. Your MCP tools work through Lynkr without configuration.
259
+
260
+ **MCP Code Mode** — Token optimization for heavy MCP setups:
261
+ - Replaces 100+ individual MCP tool schemas with 4 meta-tools
262
+ - Reduces tool catalog from ~17,500 tokens to ~700 tokens (**96% reduction**)
263
+ - Enables lazy tool discovery: model queries `mcp_list_tools`, then `mcp_tool_info`, then `mcp_execute`
264
+ - Best for: 50+ MCP tools, long conversations, context-constrained setups
265
+ - Trade-off: 3 sequential calls instead of 1 (adds ~2-3s latency)
258
266
 
259
267
  ```bash
260
- CODE_MODE_ENABLED=true # ~96% reduction in tool-catalog tokens
268
+ CODE_MODE_ENABLED=true # Enable Code Mode
269
+ CODE_MODE_CACHE_TTL=60000 # Tool list cache TTL (ms)
261
270
  ```
262
271
 
272
+ See [Token Optimization Guide](documentation/token-optimization.md#phase-0-mcp-code-mode-96-reduction-for-mcp-tools) and [Tools Documentation](documentation/tools.md#mcp-code-mode-token-optimization) for details.
273
+
263
274
  ---
264
275
 
265
276
  ## Deployment Options
package/bin/cli.js CHANGED
@@ -1,7 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ const path = require("path");
3
4
  const pkg = require('../package.json');
4
5
 
6
+ // Subcommands. Dispatched before server boot so `lynkr usage` / `lynkr trajectory`
7
+ // don't start the proxy. Add new subcommands here, not in scattered binaries.
8
+ const SUBCOMMANDS = {
9
+ usage: path.join(__dirname, "lynkr-usage.js"),
10
+ trajectory: path.join(__dirname, "lynkr-trajectory.js"),
11
+ };
12
+
13
+ const sub = process.argv[2];
14
+ if (sub && Object.prototype.hasOwnProperty.call(SUBCOMMANDS, sub)) {
15
+ process.argv.splice(2, 1); // drop the subcommand token so the script's own arg parser is happy
16
+ require(SUBCOMMANDS[sub]);
17
+ return;
18
+ }
19
+
5
20
  if (process.argv.includes('--version') || process.argv.includes('-v')) {
6
21
  console.log(pkg.version);
7
22
  process.exit(0);
@@ -14,7 +29,9 @@ ${pkg.name} v${pkg.version}
14
29
  ${pkg.description}
15
30
 
16
31
  Usage:
17
- lynkr [options]
32
+ lynkr [options] Start the proxy server (default)
33
+ lynkr usage [options] Show AI spend report and tier-routing savings
34
+ lynkr trajectory [options] Export agent trajectories as JSONL training data
18
35
 
19
36
  Options:
20
37
  -h, --help Show this help message
@@ -0,0 +1,136 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+ /**
4
+ * lynkr trajectory — export agent trajectories from the session DB
5
+ * as JSONL training data.
6
+ *
7
+ * Usage:
8
+ * lynkr trajectory # stdout, last 30 days
9
+ * lynkr trajectory --since 7d # last 7 days
10
+ * lynkr trajectory --output trajectories.jsonl # write to file
11
+ * lynkr trajectory --tier COMPLEX # only complex sessions
12
+ * lynkr trajectory --anonymize # strip PII / paths / secrets
13
+ * lynkr trajectory --count # just print the row count
14
+ */
15
+
16
+ const path = require("path");
17
+
18
+ process.env.WORKSPACE_ROOT = process.env.WORKSPACE_ROOT || path.resolve(__dirname, "..");
19
+
20
+ const compressor = require("../src/training/trajectory-compressor");
21
+
22
+ function parseArgs(argv) {
23
+ const opts = { since: "30d", anonymize: false, output: "-", count: false };
24
+ for (let i = 2; i < argv.length; i++) {
25
+ const a = argv[i];
26
+ const next = argv[i + 1];
27
+ if (a === "--since" && next) {
28
+ opts.since = next;
29
+ i++;
30
+ } else if (a === "--days" && next) {
31
+ opts.since = `${parseInt(next, 10)}d`;
32
+ i++;
33
+ } else if (a === "--tier" && next) {
34
+ opts.tier = next.toUpperCase();
35
+ i++;
36
+ } else if (a === "--output" && next) {
37
+ opts.output = next;
38
+ i++;
39
+ } else if (a === "-o" && next) {
40
+ opts.output = next;
41
+ i++;
42
+ } else if (a === "--anonymize" || a === "--anonymise") {
43
+ opts.anonymize = true;
44
+ } else if (a === "--count") {
45
+ opts.count = true;
46
+ } else if (a === "--help" || a === "-h") {
47
+ printHelp();
48
+ process.exit(0);
49
+ } else if (a === "--format" && next) {
50
+ // Reserved for future formats. Only "jsonl" is supported today.
51
+ if (next !== "jsonl") {
52
+ console.error(`Unsupported --format: ${next}. Only 'jsonl' is supported.`);
53
+ process.exit(2);
54
+ }
55
+ i++;
56
+ }
57
+ }
58
+ return opts;
59
+ }
60
+
61
+ function printHelp() {
62
+ console.log(`Lynkr trajectory exporter — emit JSONL training samples from session history.
63
+
64
+ Usage:
65
+ lynkr trajectory [options]
66
+
67
+ Options:
68
+ --since <window> "7d", "30d", ISO date, or epoch ms (default: 30d)
69
+ --days N Shorthand for --since Nd
70
+ --tier <tier> Filter to one tier: SIMPLE, MEDIUM, COMPLEX, REASONING
71
+ --output, -o <path> Output file (default: stdout, "-")
72
+ --anonymize Strip PII, file paths, API keys, hostnames
73
+ --count Print only the row count, no output
74
+ --format jsonl Output format (only jsonl supported)
75
+ -h, --help Show this help
76
+
77
+ Examples:
78
+ lynkr trajectory --days 7 --output last-week.jsonl
79
+ lynkr trajectory --tier COMPLEX --anonymize -o complex-anon.jsonl
80
+ lynkr trajectory --count
81
+
82
+ Output format (one JSON object per line):
83
+ {
84
+ "session_id": "...",
85
+ "messages": [{"role": "user", "content": "..."}, ...],
86
+ "tool_calls": [...],
87
+ "outcome": "success" | "error",
88
+ "tier": "MEDIUM",
89
+ "complexity_score": 38,
90
+ "model_used": "gpt-4o",
91
+ "provider_used": "azure-openai",
92
+ "tokens_in": 1234,
93
+ "tokens_out": 456,
94
+ "latency_ms": 2400,
95
+ "started_at": "...",
96
+ "ended_at": "..."
97
+ }
98
+ `);
99
+ }
100
+
101
+ function fmtInt(n) {
102
+ return new Intl.NumberFormat("en-US").format(n || 0);
103
+ }
104
+
105
+ function main() {
106
+ const opts = parseArgs(process.argv);
107
+
108
+ if (opts.count) {
109
+ // Quick path — stream-walk the sessions and just count valid trajectories.
110
+ let count = 0;
111
+ compressor.exportJsonl({
112
+ ...opts,
113
+ output: { write: () => count++, end: () => {} },
114
+ });
115
+ console.log(`${fmtInt(count)} trajectories`);
116
+ return;
117
+ }
118
+
119
+ const isStdout = opts.output === "-";
120
+ const start = Date.now();
121
+ const result = compressor.exportJsonl({
122
+ since: opts.since,
123
+ tier: opts.tier,
124
+ anonymize: opts.anonymize,
125
+ output: opts.output,
126
+ });
127
+
128
+ if (!isStdout) {
129
+ const elapsed = ((Date.now() - start) / 1000).toFixed(1);
130
+ process.stderr.write(
131
+ `Exported ${fmtInt(result.count)} trajectories to ${result.output} in ${elapsed}s\n`
132
+ );
133
+ }
134
+ }
135
+
136
+ main();
@@ -0,0 +1,219 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+ /**
4
+ * lynkr usage — print AI spend report from routing telemetry.
5
+ *
6
+ * Usage:
7
+ * lynkr-usage # last 30 days
8
+ * lynkr-usage --days 7
9
+ * lynkr-usage --window 1d
10
+ * lynkr-usage --window all
11
+ * lynkr-usage --json # machine-readable
12
+ * lynkr-usage --flagship gpt-5 # alternative comparison model
13
+ * lynkr-usage --provider moonshot # filter to one provider
14
+ */
15
+
16
+ const path = require("path");
17
+
18
+ // Make sure config/logger pick up the workspace root
19
+ process.env.WORKSPACE_ROOT = process.env.WORKSPACE_ROOT || path.resolve(__dirname, "..");
20
+
21
+ const aggregator = require("../src/usage/aggregator");
22
+
23
+ function parseArgs(argv) {
24
+ const opts = { window: "30d", json: false };
25
+ for (let i = 2; i < argv.length; i++) {
26
+ const a = argv[i];
27
+ const next = argv[i + 1];
28
+ if (a === "--json") opts.json = true;
29
+ else if (a === "--days" && next) {
30
+ opts.window = `${parseInt(next, 10)}d`;
31
+ i++;
32
+ } else if (a === "--window" && next) {
33
+ opts.window = next;
34
+ i++;
35
+ } else if (a === "--since" && next) {
36
+ opts.window = next;
37
+ i++;
38
+ } else if (a === "--flagship" && next) {
39
+ opts.flagship = next;
40
+ i++;
41
+ } else if (a === "--provider" && next) {
42
+ opts.provider = next;
43
+ i++;
44
+ } else if (a === "--model" && next) {
45
+ opts.model = next;
46
+ i++;
47
+ } else if (a === "--help" || a === "-h") {
48
+ printHelp();
49
+ process.exit(0);
50
+ }
51
+ }
52
+ return opts;
53
+ }
54
+
55
+ function printHelp() {
56
+ console.log(`Lynkr usage report — show AI spend and tier-routing savings.
57
+
58
+ Usage:
59
+ lynkr usage [options]
60
+
61
+ Options:
62
+ --days N Window in days (e.g. --days 7)
63
+ --window <preset> Window preset: 1d, 7d, 30d, all (default: 30d)
64
+ --since <iso> Custom start time (ISO 8601 or epoch ms)
65
+ --flagship <model> Comparison model for "savings" math (default: claude-sonnet-4-5-20250929)
66
+ --provider <name> Filter to a single provider
67
+ --model <id> Filter to a single model
68
+ --json Print as JSON instead of a formatted table
69
+ -h, --help Show this help
70
+
71
+ Examples:
72
+ lynkr usage
73
+ lynkr usage --days 7
74
+ lynkr usage --window all --json
75
+ `);
76
+ }
77
+
78
+ const C = {
79
+ reset: "\x1b[0m",
80
+ dim: "\x1b[2m",
81
+ bold: "\x1b[1m",
82
+ green: "\x1b[32m",
83
+ yellow: "\x1b[33m",
84
+ cyan: "\x1b[36m",
85
+ red: "\x1b[31m",
86
+ gray: "\x1b[90m",
87
+ };
88
+
89
+ function colour(text, code) {
90
+ if (!process.stdout.isTTY) return text;
91
+ return `${code}${text}${C.reset}`;
92
+ }
93
+
94
+ function fmtUSD(n) {
95
+ if (!n) return "$0.00";
96
+ if (n < 0.01) return `$${n.toFixed(4)}`;
97
+ return `$${n.toFixed(2)}`;
98
+ }
99
+
100
+ function fmtTokens(n) {
101
+ if (!n) return "0";
102
+ if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(2)}M`;
103
+ if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
104
+ return String(n);
105
+ }
106
+
107
+ function fmtInt(n) {
108
+ return new Intl.NumberFormat("en-US").format(n || 0);
109
+ }
110
+
111
+ function pad(s, width, align = "left") {
112
+ s = String(s);
113
+ if (s.length >= width) return s;
114
+ const filler = " ".repeat(width - visibleLength(s));
115
+ return align === "right" ? filler + s : s + filler;
116
+ }
117
+
118
+ function visibleLength(s) {
119
+ // strip ANSI for column-width math
120
+ return String(s).replace(/\x1b\[[0-9;]*m/g, "").length;
121
+ }
122
+
123
+ function tableRow(cells, widths, aligns) {
124
+ return cells
125
+ .map((c, i) => pad(c, widths[i], aligns[i] || "left"))
126
+ .join(" ");
127
+ }
128
+
129
+ function printTable(rows, header, widths, aligns) {
130
+ console.log(colour(tableRow(header, widths, aligns), C.bold));
131
+ console.log(colour(widths.map((w) => "─".repeat(w)).join(" "), C.dim));
132
+ for (const row of rows) {
133
+ console.log(tableRow(row, widths, aligns));
134
+ }
135
+ }
136
+
137
+ function bucketRows(bucket, widths) {
138
+ return Object.entries(bucket)
139
+ .sort((a, b) => b[1].actualCost - a[1].actualCost)
140
+ .map(([key, b]) => [
141
+ key,
142
+ fmtInt(b.requests),
143
+ fmtTokens(b.totalTokens),
144
+ colour(fmtUSD(b.actualCost), C.cyan),
145
+ colour(fmtUSD(b.flagshipCost), C.gray),
146
+ colour(fmtUSD(b.saved), C.green),
147
+ colour(`${b.savedPercent.toFixed(1)}%`, C.green),
148
+ ]);
149
+ }
150
+
151
+ function printReport(usage) {
152
+ const { window, since, flagship, totals, byTier, byProvider, byModel } = usage;
153
+
154
+ const banner = `Lynkr — Usage Report`;
155
+ console.log("");
156
+ console.log(colour(banner, C.bold));
157
+ console.log(
158
+ colour(
159
+ `window: ${window}${since ? ` since: ${since}` : ""} flagship-comparison: ${flagship}`,
160
+ C.dim
161
+ )
162
+ );
163
+ console.log("");
164
+
165
+ // Summary line
166
+ const headline =
167
+ `${fmtInt(totals.requests)} requests ` +
168
+ `${fmtTokens(totals.totalTokens)} tokens ` +
169
+ `actual ${colour(fmtUSD(totals.actualCost), C.cyan)} ` +
170
+ `flagship-only ${colour(fmtUSD(totals.flagshipCost), C.gray)} ` +
171
+ `saved ${colour(fmtUSD(totals.saved), C.green)} ` +
172
+ colour(`(${totals.savedPercent.toFixed(1)}%)`, C.green);
173
+ console.log(headline);
174
+ if (totals.fallbacks || totals.errors) {
175
+ console.log(
176
+ colour(
177
+ ` ${totals.fallbacks} fallback${totals.fallbacks !== 1 ? "s" : ""}, ` +
178
+ `${totals.errors} error${totals.errors !== 1 ? "s" : ""}`,
179
+ C.yellow
180
+ )
181
+ );
182
+ }
183
+ console.log("");
184
+
185
+ if (totals.requests === 0) {
186
+ console.log(colour("No telemetry yet for this window. Send some requests through Lynkr first.", C.yellow));
187
+ return;
188
+ }
189
+
190
+ const headers = ["", "REQUESTS", "TOKENS", "ACTUAL", "FLAGSHIP", "SAVED", "PCT"];
191
+ const widths = [22, 9, 9, 10, 10, 10, 7];
192
+ const aligns = ["left", "right", "right", "right", "right", "right", "right"];
193
+
194
+ console.log(colour("BY TIER", C.bold));
195
+ printTable(bucketRows(byTier, widths), ["TIER", ...headers.slice(1)], widths, aligns);
196
+ console.log("");
197
+
198
+ console.log(colour("BY PROVIDER", C.bold));
199
+ printTable(bucketRows(byProvider, widths), ["PROVIDER", ...headers.slice(1)], widths, aligns);
200
+ console.log("");
201
+
202
+ console.log(colour("BY MODEL", C.bold));
203
+ printTable(bucketRows(byModel, widths), ["MODEL", ...headers.slice(1)], widths, aligns);
204
+ console.log("");
205
+ }
206
+
207
+ function main() {
208
+ const opts = parseArgs(process.argv);
209
+ const usage = aggregator.getUsage(opts);
210
+
211
+ if (opts.json) {
212
+ process.stdout.write(JSON.stringify(usage, null, 2) + "\n");
213
+ return;
214
+ }
215
+
216
+ printReport(usage);
217
+ }
218
+
219
+ main();
package/funding.json ADDED
@@ -0,0 +1,110 @@
1
+ {
2
+ "$schema": "https://fundingjson.org/schema/v1.1.0.json",
3
+ "version": "v1.1.0",
4
+
5
+ "entity": {
6
+ "type": "individual",
7
+ "role": "maintainer",
8
+ "name": "Vishal Veera Reddy",
9
+ "email": "veerareddyvishal56@gmail.com",
10
+ "description": "Indian software engineer building open-source AI infrastructure. Sole maintainer of Lynkr, a self-hosted AI gateway that lets developers run any AI coding tool on any LLM provider.",
11
+ "webpageUrl": {
12
+ "url": "https://github.com/vishalveerareddy123"
13
+ }
14
+ },
15
+
16
+ "projects": [
17
+ {
18
+ "guid": "lynkr",
19
+ "name": "Lynkr",
20
+ "description": "A self-hosted AI gateway that decouples AI coding tools (Claude Code, Cursor, Codex, Cline, jcode, Pi) from their default LLM providers. Lynkr auto-detects the connecting tool, translates between Anthropic and OpenAI request formats, and routes to any of 12+ backends (Ollama, AWS Bedrock, Azure OpenAI, OpenRouter, Databricks, Moonshot, Google Vertex, llama.cpp, LM Studio, and more). A request-complexity classifier sends simple turns to free local models and complex ones to flagship cloud models, cutting per-developer AI bills 60-80% while removing vendor lock-in. Includes tool-result compression, MCP Code Mode (96% token reduction on tool definitions), persistent memory, and tier-based routing — all configured through a single .env file.",
21
+ "webpageUrl": {
22
+ "url": "https://fast-editor.github.io/Lynkr/"
23
+ },
24
+ "repositoryUrl": {
25
+ "url": "https://github.com/Fast-Editor/Lynkr"
26
+ },
27
+ "licenses": ["spdx:Apache-2.0"],
28
+ "tags": [
29
+ "ai",
30
+ "ai-gateway",
31
+ "llm",
32
+ "llm-router",
33
+ "developer-tools",
34
+ "proxy",
35
+ "claude-code",
36
+ "ollama",
37
+ "anthropic",
38
+ "openai"
39
+ ]
40
+ }
41
+ ],
42
+
43
+ "funding": {
44
+ "channels": [
45
+ {
46
+ "guid": "github-sponsors",
47
+ "type": "payment-provider",
48
+ "address": "https://github.com/sponsors/vishalveerareddy123",
49
+ "description": "Support Lynkr development via GitHub Sponsors."
50
+ },
51
+ {
52
+ "guid": "fossunited-grant",
53
+ "type": "other",
54
+ "address": "grants@fossunited.org",
55
+ "description": "FOSS United Foundation grant channel for institutional FOSS funding."
56
+ },
57
+ {
58
+ "guid": "bank-transfer",
59
+ "type": "bank",
60
+ "address": "Available on request via the project email.",
61
+ "description": "Direct bank transfer for organisations or grant disbursements."
62
+ }
63
+ ],
64
+
65
+ "plans": [
66
+ {
67
+ "guid": "core-maintenance-2026",
68
+ "status": "active",
69
+ "name": "Core maintenance + roadmap (12 months)",
70
+ "description": "Funds full-time work on Lynkr's core gateway: provider-format conversions, tier routing, tool-call translation across 10+ model formats (Minimax, Qwen, GLM, Llama, DeepSeek, Mistral), tool-result compression, persistent memory, MCP Code Mode, observability, tests, and docs. Estimated cost reflects one Indian maintainer working full-time for a year.",
71
+ "amount": 500000,
72
+ "currency": "INR",
73
+ "frequency": "yearly",
74
+ "channels": ["fossunited-grant", "bank-transfer"]
75
+ },
76
+ {
77
+ "guid": "infra-2026",
78
+ "status": "active",
79
+ "name": "Infrastructure + benchmarks",
80
+ "description": "Funds CI runners, benchmark harness for cost/quality/latency comparisons across providers, public dashboard at lynkr.dev, and self-hosted SearXNG + telemetry mirrors used by Lynkr's web search and routing layers.",
81
+ "amount": 150000,
82
+ "currency": "INR",
83
+ "frequency": "yearly",
84
+ "channels": ["fossunited-grant", "bank-transfer"]
85
+ },
86
+ {
87
+ "guid": "community-sponsor",
88
+ "status": "active",
89
+ "name": "Community sponsorship",
90
+ "description": "Recurring small-amount sponsorship from individual developers and small teams who use Lynkr.",
91
+ "amount": 0,
92
+ "currency": "USD",
93
+ "frequency": "monthly",
94
+ "channels": ["github-sponsors"]
95
+ },
96
+ {
97
+ "guid": "one-time",
98
+ "status": "active",
99
+ "name": "One-time contribution",
100
+ "description": "Any-amount one-time contribution from users or supporters.",
101
+ "amount": 0,
102
+ "currency": "USD",
103
+ "frequency": "one-time",
104
+ "channels": ["github-sponsors", "bank-transfer"]
105
+ }
106
+ ],
107
+
108
+ "history": []
109
+ }
110
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lynkr",
3
- "version": "9.0.2",
3
+ "version": "9.1.3",
4
4
  "description": "Self-hosted Claude Code & Cursor proxy with Databricks,AWS BedRock,Azure adapters, openrouter, Ollama,llamacpp,LM Studio, workspace tooling, and MCP integration.",
5
5
  "main": "index.js",
6
6
  "bin": {
@@ -14,7 +14,7 @@
14
14
  "dev": "nodemon index.js",
15
15
  "lint": "eslint src index.js",
16
16
  "test": "npm run test:unit && npm run test:performance",
17
- "test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js test/hybrid-routing-integration.test.js test/web-tools.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/toon-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/distill.test.js test/large-payload.test.js test/code-mode.test.js test/prompt-cache-injection.test.js",
17
+ "test:unit": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js test/hybrid-routing-integration.test.js test/web-tools.test.js test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js test/azure-openai-config.test.js test/azure-openai-format-conversion.test.js test/azure-openai-routing.test.js test/azure-openai-streaming.test.js test/azure-openai-error-resilience.test.js test/azure-openai-integration.test.js test/openai-integration.test.js test/toon-compression.test.js test/llamacpp-integration.test.js test/resilience.test.js test/telemetry-routing.test.js test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js test/distill.test.js test/large-payload.test.js test/code-mode.test.js test/prompt-cache-injection.test.js test/risk-analyzer.test.js test/interaction-block.test.js test/preflight.test.js",
18
18
  "test:memory": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/memory/store.test.js test/memory/surprise.test.js test/memory/extractor.test.js test/memory/search.test.js test/memory/retriever.test.js",
19
19
  "test:new-features": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/passthrough-mode.test.js test/openrouter-error-resilience.test.js test/format-conversion.test.js",
20
20
  "test:performance": "DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/hybrid-routing-performance.test.js && DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node test/performance-tests.js",
@@ -55,6 +55,8 @@
55
55
  "express": "^5.1.0",
56
56
  "express-rate-limit": "^8.2.1",
57
57
  "fast-glob": "^3.3.2",
58
+ "hnswlib-node": "^3.0.0",
59
+ "js-tiktoken": "^1.0.20",
58
60
  "js-yaml": "^4.1.1",
59
61
  "openai": "^6.14.0",
60
62
  "pino": "^8.17.2",