lynkr 7.2.5 → 8.0.1

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 (124) hide show
  1. package/README.md +3 -3
  2. package/config/model-tiers.json +89 -0
  3. package/install.sh +6 -1
  4. package/package.json +4 -2
  5. package/scripts/setup.js +0 -1
  6. package/src/agents/executor.js +14 -6
  7. package/src/api/middleware/session.js +15 -2
  8. package/src/api/openai-router.js +162 -37
  9. package/src/api/providers-handler.js +15 -1
  10. package/src/api/router.js +107 -2
  11. package/src/budget/index.js +4 -3
  12. package/src/clients/databricks.js +431 -234
  13. package/src/clients/gpt-utils.js +181 -0
  14. package/src/clients/ollama-utils.js +66 -140
  15. package/src/clients/routing.js +0 -1
  16. package/src/clients/standard-tools.js +99 -3
  17. package/src/config/index.js +133 -35
  18. package/src/context/toon.js +173 -0
  19. package/src/logger/index.js +23 -0
  20. package/src/orchestrator/index.js +688 -213
  21. package/src/routing/agentic-detector.js +320 -0
  22. package/src/routing/complexity-analyzer.js +202 -2
  23. package/src/routing/cost-optimizer.js +305 -0
  24. package/src/routing/index.js +168 -159
  25. package/src/routing/model-tiers.js +365 -0
  26. package/src/server.js +4 -14
  27. package/src/sessions/cleanup.js +3 -3
  28. package/src/sessions/record.js +10 -1
  29. package/src/sessions/store.js +7 -2
  30. package/src/tools/agent-task.js +48 -1
  31. package/src/tools/index.js +19 -2
  32. package/src/tools/lazy-loader.js +7 -0
  33. package/src/tools/tinyfish.js +358 -0
  34. package/src/tools/truncate.js +1 -0
  35. package/.github/FUNDING.yml +0 -15
  36. package/.github/workflows/README.md +0 -215
  37. package/.github/workflows/ci.yml +0 -69
  38. package/.github/workflows/index.yml +0 -62
  39. package/.github/workflows/web-tools-tests.yml +0 -56
  40. package/CITATIONS.bib +0 -6
  41. package/CLAWROUTER_ROUTING_PLAN.md +0 -910
  42. package/DEPLOYMENT.md +0 -1001
  43. package/LYNKR-TUI-PLAN.md +0 -984
  44. package/PERFORMANCE-REPORT.md +0 -866
  45. package/PLAN-per-client-model-routing.md +0 -252
  46. package/ROUTER_COMPARISON.md +0 -173
  47. package/TIER_ROUTING_PLAN.md +0 -771
  48. package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
  49. package/docs/BingSiteAuth.xml +0 -4
  50. package/docs/docs-style.css +0 -478
  51. package/docs/docs.html +0 -197
  52. package/docs/google5be250e608e6da39.html +0 -1
  53. package/docs/index.html +0 -577
  54. package/docs/index.md +0 -577
  55. package/docs/robots.txt +0 -4
  56. package/docs/sitemap.xml +0 -44
  57. package/docs/style.css +0 -1223
  58. package/documentation/README.md +0 -100
  59. package/documentation/api.md +0 -806
  60. package/documentation/claude-code-cli.md +0 -672
  61. package/documentation/codex-cli.md +0 -397
  62. package/documentation/contributing.md +0 -571
  63. package/documentation/cursor-integration.md +0 -731
  64. package/documentation/docker.md +0 -867
  65. package/documentation/embeddings.md +0 -760
  66. package/documentation/faq.md +0 -659
  67. package/documentation/features.md +0 -396
  68. package/documentation/headroom.md +0 -519
  69. package/documentation/installation.md +0 -706
  70. package/documentation/memory-system.md +0 -476
  71. package/documentation/production.md +0 -601
  72. package/documentation/providers.md +0 -906
  73. package/documentation/testing.md +0 -629
  74. package/documentation/token-optimization.md +0 -323
  75. package/documentation/tools.md +0 -697
  76. package/documentation/troubleshooting.md +0 -893
  77. package/final-test.js +0 -33
  78. package/headroom-sidecar/config.py +0 -93
  79. package/headroom-sidecar/requirements.txt +0 -14
  80. package/headroom-sidecar/server.py +0 -451
  81. package/monitor-agents.sh +0 -31
  82. package/scripts/audit-log-reader.js +0 -399
  83. package/scripts/compact-dictionary.js +0 -204
  84. package/scripts/test-deduplication.js +0 -448
  85. package/src/db/database.sqlite +0 -0
  86. package/test/README.md +0 -212
  87. package/test/azure-openai-config.test.js +0 -204
  88. package/test/azure-openai-error-resilience.test.js +0 -238
  89. package/test/azure-openai-format-conversion.test.js +0 -354
  90. package/test/azure-openai-integration.test.js +0 -281
  91. package/test/azure-openai-routing.test.js +0 -177
  92. package/test/azure-openai-streaming.test.js +0 -171
  93. package/test/bedrock-integration.test.js +0 -471
  94. package/test/comprehensive-test-suite.js +0 -928
  95. package/test/config-validation.test.js +0 -207
  96. package/test/cursor-integration.test.js +0 -484
  97. package/test/format-conversion.test.js +0 -578
  98. package/test/hybrid-routing-integration.test.js +0 -254
  99. package/test/hybrid-routing-performance.test.js +0 -418
  100. package/test/llamacpp-integration.test.js +0 -863
  101. package/test/lmstudio-integration.test.js +0 -335
  102. package/test/memory/extractor.test.js +0 -398
  103. package/test/memory/retriever.test.js +0 -613
  104. package/test/memory/retriever.test.js.bak +0 -585
  105. package/test/memory/search.test.js +0 -537
  106. package/test/memory/search.test.js.bak +0 -389
  107. package/test/memory/store.test.js +0 -344
  108. package/test/memory/store.test.js.bak +0 -312
  109. package/test/memory/surprise.test.js +0 -300
  110. package/test/memory-performance.test.js +0 -472
  111. package/test/openai-integration.test.js +0 -686
  112. package/test/openrouter-error-resilience.test.js +0 -418
  113. package/test/passthrough-mode.test.js +0 -385
  114. package/test/performance-benchmark.js +0 -351
  115. package/test/performance-tests.js +0 -528
  116. package/test/routing.test.js +0 -219
  117. package/test/web-tools.test.js +0 -329
  118. package/test-agents-simple.js +0 -43
  119. package/test-cli-connection.sh +0 -33
  120. package/test-learning-unit.js +0 -126
  121. package/test-learning.js +0 -112
  122. package/test-parallel-agents.sh +0 -124
  123. package/test-parallel-direct.js +0 -155
  124. package/test-subagents.sh +0 -117
@@ -1,204 +0,0 @@
1
- const assert = require("assert");
2
- const { describe, it, beforeEach, afterEach } = require("node:test");
3
-
4
- describe("Azure OpenAI Configuration Tests", () => {
5
- let originalConfig;
6
-
7
- beforeEach(() => {
8
- // Clear module cache
9
- delete require.cache[require.resolve("../src/config")];
10
-
11
- // Store original config
12
- originalConfig = { ...process.env };
13
-
14
- // Set Azure OpenAI environment variables to empty strings to override .env values
15
- // (deleting them would cause dotenv to reload from .env file)
16
- process.env.AZURE_OPENAI_ENDPOINT = "";
17
- process.env.AZURE_OPENAI_API_KEY = "";
18
- process.env.AZURE_OPENAI_DEPLOYMENT = "";
19
- process.env.AZURE_OPENAI_API_VERSION = "";
20
- });
21
-
22
- afterEach(() => {
23
- // Restore original environment
24
- process.env = originalConfig;
25
- });
26
-
27
- describe("Configuration Loading", () => {
28
- it("should load Azure OpenAI configuration with all values set", () => {
29
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
30
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
31
- process.env.AZURE_OPENAI_DEPLOYMENT = "gpt-4o";
32
- process.env.AZURE_OPENAI_API_VERSION = "2024-08-01-preview";
33
- process.env.MODEL_PROVIDER = "databricks";
34
- process.env.DATABRICKS_API_KEY = "test-key";
35
- process.env.DATABRICKS_API_BASE = "http://test.com";
36
-
37
- const config = require("../src/config");
38
-
39
- assert.strictEqual(config.azureOpenAI.endpoint, "https://test-resource.openai.azure.com");
40
- assert.strictEqual(config.azureOpenAI.apiKey, "test-api-key");
41
- assert.strictEqual(config.azureOpenAI.deployment, "gpt-4o");
42
- assert.strictEqual(config.azureOpenAI.apiVersion, "2024-08-01-preview");
43
- });
44
-
45
- it("should use default values when optional fields not set", () => {
46
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
47
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
48
- // Keep as empty strings (don't delete) to prevent dotenv from reloading
49
- process.env.AZURE_OPENAI_DEPLOYMENT = "";
50
- process.env.AZURE_OPENAI_API_VERSION = "";
51
- process.env.MODEL_PROVIDER = "databricks";
52
- process.env.DATABRICKS_API_KEY = "test-key";
53
- process.env.DATABRICKS_API_BASE = "http://test.com";
54
-
55
- const config = require("../src/config");
56
-
57
- assert.strictEqual(config.azureOpenAI.deployment, "gpt-4o");
58
- assert.strictEqual(config.azureOpenAI.apiVersion, "2024-08-01-preview");
59
- });
60
-
61
- it("should load null values when Azure OpenAI not configured", () => {
62
- // Keep as empty strings (don't delete) to prevent dotenv from reloading
63
- process.env.AZURE_OPENAI_ENDPOINT = "";
64
- process.env.AZURE_OPENAI_API_KEY = "";
65
- process.env.MODEL_PROVIDER = "databricks";
66
- process.env.DATABRICKS_API_KEY = "test-key";
67
- process.env.DATABRICKS_API_BASE = "http://test.com";
68
-
69
- const config = require("../src/config");
70
-
71
- assert.strictEqual(config.azureOpenAI.endpoint, null);
72
- assert.strictEqual(config.azureOpenAI.apiKey, null);
73
- assert.strictEqual(config.azureOpenAI.deployment, "gpt-4o"); // default
74
- assert.strictEqual(config.azureOpenAI.apiVersion, "2024-08-01-preview"); // default
75
- });
76
- });
77
-
78
- describe("Primary Provider Validation", () => {
79
- it("should accept azure-openai as MODEL_PROVIDER", () => {
80
- process.env.MODEL_PROVIDER = "azure-openai";
81
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
82
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
83
-
84
- const config = require("../src/config");
85
-
86
- assert.strictEqual(config.modelProvider.type, "azure-openai");
87
- });
88
-
89
- it("should throw error when azure-openai is primary provider without endpoint", () => {
90
- process.env.MODEL_PROVIDER = "azure-openai";
91
- process.env.AZURE_OPENAI_ENDPOINT = ""; // Empty string, not deleted
92
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
93
-
94
- assert.throws(() => {
95
- require("../src/config");
96
- }, /AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY/);
97
- });
98
-
99
- it("should throw error when azure-openai is primary provider without API key", () => {
100
- process.env.MODEL_PROVIDER = "azure-openai";
101
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
102
- process.env.AZURE_OPENAI_API_KEY = ""; // Empty string, not deleted
103
-
104
- assert.throws(() => {
105
- require("../src/config");
106
- }, /AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY/);
107
- });
108
-
109
- it("should throw error when azure-openai is primary provider without both", () => {
110
- process.env.MODEL_PROVIDER = "azure-openai";
111
- process.env.AZURE_OPENAI_ENDPOINT = ""; // Empty string, not deleted
112
- process.env.AZURE_OPENAI_API_KEY = ""; // Empty string, not deleted
113
-
114
- assert.throws(() => {
115
- require("../src/config");
116
- }, /AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY/);
117
- });
118
- });
119
-
120
- describe("Fallback Provider Validation", () => {
121
- it("should accept azure-openai as fallback provider with credentials", () => {
122
- process.env.PREFER_OLLAMA = "true";
123
- process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
124
- process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
125
- process.env.FALLBACK_ENABLED = "true";
126
- process.env.FALLBACK_PROVIDER = "azure-openai";
127
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
128
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
129
- process.env.DATABRICKS_API_KEY = "test-key";
130
- process.env.DATABRICKS_API_BASE = "http://test.com";
131
-
132
- const config = require("../src/config");
133
-
134
- assert.strictEqual(config.modelProvider.fallbackProvider, "azure-openai");
135
- });
136
-
137
- it("should reject when azure-openai is fallback but credentials missing", () => {
138
- process.env.MODEL_PROVIDER = "ollama"; // Set to ollama for hybrid routing scenario
139
- process.env.PREFER_OLLAMA = "true";
140
- process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
141
- process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
142
- process.env.FALLBACK_ENABLED = "true";
143
- process.env.FALLBACK_PROVIDER = "azure-openai";
144
- // Set to empty strings instead of deleting (dotenv.config() in config module would reload from .env)
145
- process.env.AZURE_OPENAI_ENDPOINT = "";
146
- process.env.AZURE_OPENAI_API_KEY = "";
147
- process.env.DATABRICKS_API_KEY = "test-key";
148
- process.env.DATABRICKS_API_BASE = "http://test.com";
149
-
150
- // Should throw error about missing Azure OpenAI credentials (fail-fast validation)
151
- assert.throws(() => {
152
- require("../src/config");
153
- }, /AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY/);
154
- });
155
- });
156
-
157
- describe("Deployment and API Version Defaults", () => {
158
- it("should use gpt-4o as default deployment", () => {
159
- // Keep as empty string (don't delete) to prevent dotenv from reloading from .env
160
- process.env.AZURE_OPENAI_DEPLOYMENT = "";
161
- process.env.MODEL_PROVIDER = "azure-openai";
162
- process.env.AZURE_OPENAI_ENDPOINT = "https://test-resource.openai.azure.com";
163
- process.env.AZURE_OPENAI_API_KEY = "test-api-key";
164
-
165
- const config = require("../src/config");
166
-
167
- assert.strictEqual(config.azureOpenAI.deployment, "gpt-4o");
168
- });
169
-
170
- it("should use custom deployment when specified", () => {
171
- process.env.AZURE_OPENAI_DEPLOYMENT = "gpt-5";
172
- process.env.MODEL_PROVIDER = "databricks";
173
- process.env.DATABRICKS_API_KEY = "test-key";
174
- process.env.DATABRICKS_API_BASE = "http://test.com";
175
-
176
- const config = require("../src/config");
177
-
178
- assert.strictEqual(config.azureOpenAI.deployment, "gpt-5");
179
- });
180
-
181
- it("should use 2024-08-01-preview as default API version", () => {
182
- // Keep as empty string (don't delete) to prevent dotenv from reloading from .env
183
- process.env.AZURE_OPENAI_API_VERSION = "";
184
- process.env.MODEL_PROVIDER = "databricks";
185
- process.env.DATABRICKS_API_KEY = "test-key";
186
- process.env.DATABRICKS_API_BASE = "http://test.com";
187
-
188
- const config = require("../src/config");
189
-
190
- assert.strictEqual(config.azureOpenAI.apiVersion, "2024-08-01-preview");
191
- });
192
-
193
- it("should use custom API version when specified", () => {
194
- process.env.AZURE_OPENAI_API_VERSION = "2025-01-01-preview";
195
- process.env.MODEL_PROVIDER = "databricks";
196
- process.env.DATABRICKS_API_KEY = "test-key";
197
- process.env.DATABRICKS_API_BASE = "http://test.com";
198
-
199
- const config = require("../src/config");
200
-
201
- assert.strictEqual(config.azureOpenAI.apiVersion, "2025-01-01-preview");
202
- });
203
- });
204
- });
@@ -1,238 +0,0 @@
1
- const assert = require("assert");
2
- const { describe, it } = require("node:test");
3
-
4
- describe("Azure OpenAI Error Resilience Tests", () => {
5
- describe("Error Response Structure", () => {
6
- it("should recognize 401 authentication error", () => {
7
- const errorResponse = {
8
- status: 401,
9
- json: {
10
- error: {
11
- message: "Incorrect API key provided",
12
- type: "invalid_request_error",
13
- code: "invalid_api_key"
14
- }
15
- }
16
- };
17
-
18
- assert.strictEqual(errorResponse.status, 401);
19
- assert.strictEqual(errorResponse.json.error.code, "invalid_api_key");
20
- });
21
-
22
- it("should recognize 403 permission denied error", () => {
23
- const errorResponse = {
24
- status: 403,
25
- json: {
26
- error: {
27
- message: "The API deployment for this resource does not exist",
28
- type: "invalid_request_error",
29
- code: "DeploymentNotFound"
30
- }
31
- }
32
- };
33
-
34
- assert.strictEqual(errorResponse.status, 403);
35
- });
36
-
37
- it("should recognize 404 deployment not found error", () => {
38
- const errorResponse = {
39
- status: 404,
40
- json: {
41
- error: {
42
- message: "The API deployment for this resource does not exist",
43
- type: "invalid_request_error",
44
- code: "DeploymentNotFound"
45
- }
46
- }
47
- };
48
-
49
- assert.strictEqual(errorResponse.status, 404);
50
- assert.strictEqual(errorResponse.json.error.code, "DeploymentNotFound");
51
- });
52
-
53
- it("should recognize 429 rate limit error with Retry-After header", () => {
54
- const errorResponse = {
55
- status: 429,
56
- headers: {
57
- "retry-after": "2",
58
- "x-ratelimit-remaining-tokens": "0",
59
- "x-ratelimit-remaining-requests": "0"
60
- },
61
- json: {
62
- error: {
63
- message: "Rate limit reached",
64
- type: "rate_limit_error",
65
- code: "rate_limit_exceeded"
66
- }
67
- }
68
- };
69
-
70
- assert.strictEqual(errorResponse.status, 429);
71
- assert.strictEqual(errorResponse.headers["retry-after"], "2");
72
- assert.strictEqual(errorResponse.json.error.code, "rate_limit_exceeded");
73
- });
74
-
75
- it("should recognize 400 content filter error", () => {
76
- const errorResponse = {
77
- status: 400,
78
- json: {
79
- error: {
80
- message: "The response was filtered due to the prompt triggering Azure OpenAI's content management policy",
81
- type: "invalid_request_error",
82
- code: "content_filter"
83
- }
84
- }
85
- };
86
-
87
- assert.strictEqual(errorResponse.status, 400);
88
- assert.strictEqual(errorResponse.json.error.code, "content_filter");
89
- });
90
-
91
- it("should recognize 500 internal server error", () => {
92
- const errorResponse = {
93
- status: 500,
94
- json: {
95
- error: {
96
- message: "The server had an error while processing your request",
97
- type: "server_error",
98
- code: "internal_error"
99
- }
100
- }
101
- };
102
-
103
- assert.strictEqual(errorResponse.status, 500);
104
- assert.strictEqual(errorResponse.json.error.type, "server_error");
105
- });
106
-
107
- it("should recognize 503 service unavailable error", () => {
108
- const errorResponse = {
109
- status: 503,
110
- json: {
111
- error: {
112
- message: "The service is temporarily unavailable",
113
- type: "server_error",
114
- code: "service_unavailable"
115
- }
116
- }
117
- };
118
-
119
- assert.strictEqual(errorResponse.status, 503);
120
- });
121
- });
122
-
123
- describe("Missing Choices Array Validation", () => {
124
- it("should detect missing choices array", () => {
125
- const invalidResponse = {
126
- id: "chatcmpl-123",
127
- object: "chat.completion",
128
- model: "gpt-4o"
129
- // choices array missing
130
- };
131
-
132
- assert.strictEqual(invalidResponse.choices, undefined);
133
- });
134
-
135
- it("should detect empty choices array", () => {
136
- const invalidResponse = {
137
- id: "chatcmpl-123",
138
- object: "chat.completion",
139
- model: "gpt-4o",
140
- choices: []
141
- };
142
-
143
- assert.strictEqual(invalidResponse.choices.length, 0);
144
- });
145
-
146
- it("should validate valid choices array", () => {
147
- const validResponse = {
148
- choices: [
149
- {
150
- message: {
151
- role: "assistant",
152
- content: "Hello"
153
- },
154
- finish_reason: "stop"
155
- }
156
- ]
157
- };
158
-
159
- assert.ok(validResponse.choices?.length > 0);
160
- assert.ok(validResponse.choices[0].message);
161
- });
162
- });
163
-
164
- describe("Network Error Categorization", () => {
165
- it("should categorize ETIMEDOUT as timeout error", () => {
166
- const error = new Error("Request timeout");
167
- error.code = "ETIMEDOUT";
168
-
169
- assert.strictEqual(error.code, "ETIMEDOUT");
170
- });
171
-
172
- it("should categorize ECONNREFUSED as connection error", () => {
173
- const error = new Error("Connection refused");
174
- error.code = "ECONNREFUSED";
175
-
176
- assert.strictEqual(error.code, "ECONNREFUSED");
177
- });
178
-
179
- it("should categorize ENOTFOUND as DNS error", () => {
180
- const error = new Error("DNS lookup failed");
181
- error.code = "ENOTFOUND";
182
-
183
- assert.strictEqual(error.code, "ENOTFOUND");
184
- });
185
- });
186
-
187
- describe("Retry Strategy", () => {
188
- it("should identify retryable 5xx errors", () => {
189
- const retryableStatuses = [500, 502, 503, 504];
190
-
191
- for (const status of retryableStatuses) {
192
- assert.ok(status >= 500 && status < 600);
193
- }
194
- });
195
-
196
- it("should identify non-retryable 4xx errors", () => {
197
- const nonRetryableStatuses = [400, 401, 403, 404];
198
-
199
- for (const status of nonRetryableStatuses) {
200
- assert.ok(status >= 400 && status < 500);
201
- assert.notStrictEqual(status, 429); // 429 is retryable
202
- }
203
- });
204
-
205
- it("should treat 429 as retryable with backoff", () => {
206
- const status = 429;
207
-
208
- assert.strictEqual(status, 429);
209
- assert.ok(status === 429); // Special handling for rate limits
210
- });
211
- });
212
-
213
- describe("Malformed JSON Response", () => {
214
- it("should handle truncated JSON response", () => {
215
- const truncatedJSON = '{"choices":[{"message":{"role":"assistant","content":"Hello';
216
-
217
- assert.throws(() => {
218
- JSON.parse(truncatedJSON);
219
- }, SyntaxError);
220
- });
221
-
222
- it("should handle empty response body", () => {
223
- const emptyBody = "";
224
-
225
- assert.throws(() => {
226
- JSON.parse(emptyBody);
227
- }, SyntaxError);
228
- });
229
-
230
- it("should handle non-JSON response", () => {
231
- const htmlError = "<html><body>Error 503</body></html>";
232
-
233
- assert.throws(() => {
234
- JSON.parse(htmlError);
235
- }, SyntaxError);
236
- });
237
- });
238
- });