lynkr 8.0.0 → 9.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.
- package/.lynkr/telemetry.db +0 -0
- package/.lynkr/telemetry.db-shm +0 -0
- package/.lynkr/telemetry.db-wal +0 -0
- package/README.md +196 -322
- package/lynkr-skill.tar.gz +0 -0
- package/package.json +4 -3
- package/src/api/openai-router.js +64 -13
- package/src/api/providers-handler.js +171 -3
- package/src/api/router.js +9 -2
- package/src/clients/circuit-breaker.js +10 -247
- package/src/clients/codex-process.js +342 -0
- package/src/clients/codex-utils.js +143 -0
- package/src/clients/databricks.js +210 -63
- package/src/clients/resilience.js +540 -0
- package/src/clients/retry.js +22 -167
- package/src/clients/standard-tools.js +23 -0
- package/src/config/index.js +77 -0
- package/src/context/compression.js +42 -9
- package/src/context/distill.js +492 -0
- package/src/orchestrator/index.js +48 -8
- package/src/routing/complexity-analyzer.js +258 -5
- package/src/routing/index.js +12 -2
- package/src/routing/latency-tracker.js +148 -0
- package/src/routing/model-tiers.js +2 -0
- package/src/routing/quality-scorer.js +113 -0
- package/src/routing/telemetry.js +464 -0
- package/src/server.js +13 -12
- package/src/tools/code-graph.js +538 -0
- package/src/tools/code-mode.js +304 -0
- package/src/tools/index.js +4 -0
- package/src/tools/lazy-loader.js +18 -0
- package/src/tools/mcp-remote.js +7 -0
- package/src/tools/smart-selection.js +11 -0
- package/src/tools/tinyfish.js +358 -0
- package/src/tools/truncate.js +1 -0
- package/src/utils/payload.js +206 -0
- package/src/utils/perf-timer.js +80 -0
- package/.github/FUNDING.yml +0 -15
- package/.github/workflows/README.md +0 -215
- package/.github/workflows/ci.yml +0 -69
- package/.github/workflows/index.yml +0 -62
- package/.github/workflows/web-tools-tests.yml +0 -56
- package/CITATIONS.bib +0 -6
- package/DEPLOYMENT.md +0 -1001
- package/LYNKR-TUI-PLAN.md +0 -984
- package/PERFORMANCE-REPORT.md +0 -866
- package/PLAN-per-client-model-routing.md +0 -252
- package/docs/42642f749da6234f41b6b425c3bb07c9.txt +0 -1
- package/docs/BingSiteAuth.xml +0 -4
- package/docs/docs-style.css +0 -478
- package/docs/docs.html +0 -198
- package/docs/google5be250e608e6da39.html +0 -1
- package/docs/index.html +0 -577
- package/docs/index.md +0 -584
- package/docs/robots.txt +0 -4
- package/docs/sitemap.xml +0 -44
- package/docs/style.css +0 -1223
- package/docs/toon-integration-spec.md +0 -130
- package/documentation/README.md +0 -101
- package/documentation/api.md +0 -806
- package/documentation/claude-code-cli.md +0 -679
- package/documentation/codex-cli.md +0 -397
- package/documentation/contributing.md +0 -571
- package/documentation/cursor-integration.md +0 -734
- package/documentation/docker.md +0 -874
- package/documentation/embeddings.md +0 -762
- package/documentation/faq.md +0 -713
- package/documentation/features.md +0 -403
- package/documentation/headroom.md +0 -519
- package/documentation/installation.md +0 -758
- package/documentation/memory-system.md +0 -476
- package/documentation/production.md +0 -636
- package/documentation/providers.md +0 -1009
- package/documentation/routing.md +0 -476
- package/documentation/testing.md +0 -629
- package/documentation/token-optimization.md +0 -325
- package/documentation/tools.md +0 -697
- package/documentation/troubleshooting.md +0 -969
- package/final-test.js +0 -33
- package/headroom-sidecar/config.py +0 -93
- package/headroom-sidecar/requirements.txt +0 -14
- package/headroom-sidecar/server.py +0 -451
- package/monitor-agents.sh +0 -31
- package/scripts/audit-log-reader.js +0 -399
- package/scripts/compact-dictionary.js +0 -204
- package/scripts/test-deduplication.js +0 -448
- package/src/db/database.sqlite +0 -0
- package/te +0 -11622
- package/test/README.md +0 -212
- package/test/azure-openai-config.test.js +0 -213
- package/test/azure-openai-error-resilience.test.js +0 -238
- package/test/azure-openai-format-conversion.test.js +0 -354
- package/test/azure-openai-integration.test.js +0 -287
- package/test/azure-openai-routing.test.js +0 -175
- package/test/azure-openai-streaming.test.js +0 -171
- package/test/bedrock-integration.test.js +0 -457
- package/test/comprehensive-test-suite.js +0 -928
- package/test/config-validation.test.js +0 -207
- package/test/cursor-integration.test.js +0 -484
- package/test/format-conversion.test.js +0 -578
- package/test/hybrid-routing-integration.test.js +0 -269
- package/test/hybrid-routing-performance.test.js +0 -428
- package/test/llamacpp-integration.test.js +0 -882
- package/test/lmstudio-integration.test.js +0 -347
- package/test/memory/extractor.test.js +0 -398
- package/test/memory/retriever.test.js +0 -613
- package/test/memory/retriever.test.js.bak +0 -585
- package/test/memory/search.test.js +0 -537
- package/test/memory/search.test.js.bak +0 -389
- package/test/memory/store.test.js +0 -344
- package/test/memory/store.test.js.bak +0 -312
- package/test/memory/surprise.test.js +0 -300
- package/test/memory-performance.test.js +0 -472
- package/test/openai-integration.test.js +0 -683
- package/test/openrouter-error-resilience.test.js +0 -418
- package/test/passthrough-mode.test.js +0 -385
- package/test/performance-benchmark.js +0 -351
- package/test/performance-tests.js +0 -528
- package/test/routing.test.js +0 -225
- package/test/toon-compression.test.js +0 -131
- package/test/web-tools.test.js +0 -329
- package/test-agents-simple.js +0 -43
- package/test-cli-connection.sh +0 -33
- package/test-learning-unit.js +0 -126
- package/test-learning.js +0 -112
- package/test-parallel-agents.sh +0 -124
- package/test-parallel-direct.js +0 -155
- package/test-subagents.sh +0 -117
package/src/clients/retry.js
CHANGED
|
@@ -1,200 +1,55 @@
|
|
|
1
|
-
const logger = require("../logger");
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
|
-
* Retry
|
|
2
|
+
* Retry logic for API calls — backed by Cockatiel
|
|
3
|
+
*
|
|
4
|
+
* This module re-exports the Cockatiel-backed retry adapter from resilience.js
|
|
5
|
+
* while preserving all original exports for consumers.
|
|
5
6
|
*/
|
|
7
|
+
const { withCockatielRetry, DEFAULT_RETRY_CONFIG } = require("./resilience");
|
|
8
|
+
|
|
6
9
|
const DEFAULT_CONFIG = {
|
|
7
|
-
maxRetries:
|
|
8
|
-
initialDelay:
|
|
9
|
-
maxDelay:
|
|
10
|
-
backoffMultiplier:
|
|
11
|
-
jitterFactor:
|
|
12
|
-
retryableStatuses:
|
|
13
|
-
retryableErrors:
|
|
10
|
+
maxRetries: DEFAULT_RETRY_CONFIG.maxRetries,
|
|
11
|
+
initialDelay: DEFAULT_RETRY_CONFIG.initialDelay,
|
|
12
|
+
maxDelay: DEFAULT_RETRY_CONFIG.maxDelay,
|
|
13
|
+
backoffMultiplier: DEFAULT_RETRY_CONFIG.backoffMultiplier,
|
|
14
|
+
jitterFactor: DEFAULT_RETRY_CONFIG.jitterFactor,
|
|
15
|
+
retryableStatuses: DEFAULT_RETRY_CONFIG.retryableStatuses,
|
|
16
|
+
retryableErrors: DEFAULT_RETRY_CONFIG.retryableErrors,
|
|
14
17
|
};
|
|
15
18
|
|
|
16
19
|
/**
|
|
17
|
-
*
|
|
18
|
-
*/
|
|
19
|
-
function addJitter(delay, jitterFactor) {
|
|
20
|
-
const jitter = delay * jitterFactor * (Math.random() * 2 - 1);
|
|
21
|
-
return Math.max(0, delay + jitter);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Calculate delay with exponential backoff
|
|
20
|
+
* Calculate delay with exponential backoff (preserved for any direct callers)
|
|
26
21
|
*/
|
|
27
22
|
function calculateDelay(attempt, config) {
|
|
28
23
|
const baseDelay = config.initialDelay * Math.pow(config.backoffMultiplier, attempt);
|
|
29
24
|
const cappedDelay = Math.min(baseDelay, config.maxDelay);
|
|
30
|
-
|
|
25
|
+
const jitter = cappedDelay * config.jitterFactor * (Math.random() * 2 - 1);
|
|
26
|
+
return Math.max(0, cappedDelay + jitter);
|
|
31
27
|
}
|
|
32
28
|
|
|
33
29
|
/**
|
|
34
|
-
* Check if error/response is retryable
|
|
30
|
+
* Check if error/response is retryable (preserved for any direct callers)
|
|
35
31
|
*/
|
|
36
32
|
function isRetryable(error, response, config) {
|
|
37
|
-
// Check response status codes
|
|
38
33
|
if (response && config.retryableStatuses.includes(response.status)) {
|
|
39
34
|
return true;
|
|
40
35
|
}
|
|
41
|
-
|
|
42
|
-
// Check error codes
|
|
43
36
|
if (error && error.code && config.retryableErrors.includes(error.code)) {
|
|
44
37
|
return true;
|
|
45
38
|
}
|
|
46
|
-
|
|
47
|
-
// Check nested cause (Node undici wraps connection errors as TypeError)
|
|
48
39
|
if (error && error.cause?.code && config.retryableErrors.includes(error.cause.code)) {
|
|
49
40
|
return true;
|
|
50
41
|
}
|
|
51
|
-
|
|
52
|
-
// Check for network errors
|
|
53
|
-
if (error && (error.name === 'FetchError' || error.name === 'AbortError')) {
|
|
42
|
+
if (error && (error.name === "FetchError" || error.name === "AbortError")) {
|
|
54
43
|
return true;
|
|
55
44
|
}
|
|
56
|
-
|
|
57
45
|
return false;
|
|
58
46
|
}
|
|
59
47
|
|
|
60
48
|
/**
|
|
61
|
-
* Detect if this is a cold start
|
|
49
|
+
* Detect if this is a cold start
|
|
62
50
|
*/
|
|
63
51
|
function detectColdStart(startTime, endTime, threshold = 5000) {
|
|
64
|
-
|
|
65
|
-
return duration > threshold;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Execute function with retry logic
|
|
70
|
-
*/
|
|
71
|
-
async function withRetry(fn, options = {}) {
|
|
72
|
-
const config = { ...DEFAULT_CONFIG, ...options };
|
|
73
|
-
let lastError;
|
|
74
|
-
let lastResponse;
|
|
75
|
-
|
|
76
|
-
for (let attempt = 0; attempt <= config.maxRetries; attempt++) {
|
|
77
|
-
const startTime = Date.now();
|
|
78
|
-
|
|
79
|
-
try {
|
|
80
|
-
const result = await fn(attempt);
|
|
81
|
-
const endTime = Date.now();
|
|
82
|
-
|
|
83
|
-
// Detect cold starts for monitoring
|
|
84
|
-
if (detectColdStart(startTime, endTime)) {
|
|
85
|
-
logger.warn({
|
|
86
|
-
attempt,
|
|
87
|
-
duration: endTime - startTime,
|
|
88
|
-
}, 'Potential cold start detected');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Check if response indicates we should retry
|
|
92
|
-
if (result && isRetryable(null, result, config) && attempt < config.maxRetries) {
|
|
93
|
-
lastResponse = result;
|
|
94
|
-
|
|
95
|
-
// Special handling for 429 (rate limiting)
|
|
96
|
-
if (result.status === 429) {
|
|
97
|
-
// Check for Retry-After header
|
|
98
|
-
const retryAfter = result.headers?.get?.('retry-after');
|
|
99
|
-
let delay;
|
|
100
|
-
|
|
101
|
-
if (retryAfter) {
|
|
102
|
-
// Retry-After can be in seconds or a date
|
|
103
|
-
const retryAfterNum = parseInt(retryAfter, 10);
|
|
104
|
-
if (!isNaN(retryAfterNum)) {
|
|
105
|
-
delay = retryAfterNum * 1000; // Convert to ms
|
|
106
|
-
} else {
|
|
107
|
-
const retryAfterDate = new Date(retryAfter);
|
|
108
|
-
delay = retryAfterDate.getTime() - Date.now();
|
|
109
|
-
}
|
|
110
|
-
} else {
|
|
111
|
-
// Use exponential backoff with longer delays for rate limiting
|
|
112
|
-
delay = calculateDelay(attempt, {
|
|
113
|
-
...config,
|
|
114
|
-
initialDelay: 2000, // Start at 2s for rate limits
|
|
115
|
-
maxDelay: 60000, // Up to 1 minute
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
logger.warn({
|
|
120
|
-
attempt,
|
|
121
|
-
delay,
|
|
122
|
-
retryAfter: retryAfter || 'not specified',
|
|
123
|
-
}, 'Rate limited (429), retrying after delay');
|
|
124
|
-
|
|
125
|
-
await sleep(delay);
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Regular retry with exponential backoff
|
|
130
|
-
const delay = calculateDelay(attempt, config);
|
|
131
|
-
logger.warn({
|
|
132
|
-
attempt,
|
|
133
|
-
status: result.status,
|
|
134
|
-
delay,
|
|
135
|
-
}, 'Request failed, retrying with backoff');
|
|
136
|
-
|
|
137
|
-
await sleep(delay);
|
|
138
|
-
continue;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Success or non-retryable error
|
|
142
|
-
return result;
|
|
143
|
-
|
|
144
|
-
} catch (error) {
|
|
145
|
-
lastError = error;
|
|
146
|
-
const endTime = Date.now();
|
|
147
|
-
|
|
148
|
-
// Check if cold start
|
|
149
|
-
if (detectColdStart(startTime, endTime)) {
|
|
150
|
-
logger.warn({
|
|
151
|
-
attempt,
|
|
152
|
-
duration: endTime - startTime,
|
|
153
|
-
error: error.message,
|
|
154
|
-
}, 'Potential cold start with error detected');
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Check if we should retry
|
|
158
|
-
if (isRetryable(error, null, config) && attempt < config.maxRetries) {
|
|
159
|
-
const delay = calculateDelay(attempt, config);
|
|
160
|
-
logger.warn({
|
|
161
|
-
attempt,
|
|
162
|
-
error: error.message,
|
|
163
|
-
code: error.code,
|
|
164
|
-
delay,
|
|
165
|
-
}, 'Request error, retrying with backoff');
|
|
166
|
-
|
|
167
|
-
await sleep(delay);
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
// Not retryable or out of retries
|
|
172
|
-
throw error;
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
// Max retries exceeded
|
|
177
|
-
if (lastError) {
|
|
178
|
-
lastError.message = `Max retries (${config.maxRetries}) exceeded: ${lastError.message}`;
|
|
179
|
-
throw lastError;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
if (lastResponse) {
|
|
183
|
-
logger.error({
|
|
184
|
-
status: lastResponse.status,
|
|
185
|
-
maxRetries: config.maxRetries,
|
|
186
|
-
}, 'Max retries exceeded');
|
|
187
|
-
return lastResponse;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
throw new Error('Retry logic failed unexpectedly');
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* Sleep helper
|
|
195
|
-
*/
|
|
196
|
-
function sleep(ms) {
|
|
197
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
52
|
+
return (endTime - startTime) > threshold;
|
|
198
53
|
}
|
|
199
54
|
|
|
200
55
|
/**
|
|
@@ -202,12 +57,12 @@ function sleep(ms) {
|
|
|
202
57
|
*/
|
|
203
58
|
function createRetryWrapper(fn, defaultOptions = {}) {
|
|
204
59
|
return async function (...args) {
|
|
205
|
-
return
|
|
60
|
+
return withCockatielRetry(() => fn(...args), defaultOptions);
|
|
206
61
|
};
|
|
207
62
|
}
|
|
208
63
|
|
|
209
64
|
module.exports = {
|
|
210
|
-
withRetry,
|
|
65
|
+
withRetry: withCockatielRetry,
|
|
211
66
|
createRetryWrapper,
|
|
212
67
|
calculateDelay,
|
|
213
68
|
isRetryable,
|
|
@@ -380,6 +380,29 @@ EXAMPLE: User says "explore this project" → Call Task with subagent_type="Expl
|
|
|
380
380
|
required: ["url", "prompt"]
|
|
381
381
|
}
|
|
382
382
|
},
|
|
383
|
+
{
|
|
384
|
+
name: "WebAgent",
|
|
385
|
+
description: "Launches a browser agent to navigate a website and accomplish a goal. Use when you need to interact with dynamic web content (click buttons, fill forms, extract data from JS-rendered pages) beyond what a simple HTTP fetch can do. Returns structured JSON. Takes 10-60 seconds.",
|
|
386
|
+
input_schema: {
|
|
387
|
+
type: "object",
|
|
388
|
+
properties: {
|
|
389
|
+
url: {
|
|
390
|
+
type: "string",
|
|
391
|
+
description: "Target URL to navigate to"
|
|
392
|
+
},
|
|
393
|
+
goal: {
|
|
394
|
+
type: "string",
|
|
395
|
+
description: "What to accomplish on the page. Be specific about what data to extract or actions to take."
|
|
396
|
+
},
|
|
397
|
+
browser_profile: {
|
|
398
|
+
type: "string",
|
|
399
|
+
enum: ["lite", "stealth"],
|
|
400
|
+
description: "lite (default, faster) or stealth (for bot-protected sites)"
|
|
401
|
+
}
|
|
402
|
+
},
|
|
403
|
+
required: ["url", "goal"]
|
|
404
|
+
}
|
|
405
|
+
},
|
|
383
406
|
{
|
|
384
407
|
name: "NotebookEdit",
|
|
385
408
|
description: "Completely replaces the contents of a specific cell in a Jupyter notebook (.ipynb file). Use for editing interactive documents that combine code, text, and visualizations.",
|
package/src/config/index.js
CHANGED
|
@@ -397,6 +397,14 @@ const webFetchBodyPreviewMax = Number.parseInt(process.env.WEB_FETCH_BODY_PREVIE
|
|
|
397
397
|
const webSearchRetryEnabled = process.env.WEB_SEARCH_RETRY_ENABLED !== "false"; // default true
|
|
398
398
|
const webSearchMaxRetries = Number.parseInt(process.env.WEB_SEARCH_MAX_RETRIES ?? "2", 10);
|
|
399
399
|
|
|
400
|
+
// TinyFish AI Browser Automation configuration
|
|
401
|
+
const tinyfishApiKey = process.env.TINYFISH_API_KEY?.trim() || null;
|
|
402
|
+
const tinyfishEndpoint = process.env.TINYFISH_ENDPOINT?.trim() || "https://agent.tinyfish.ai/v1/automation/run-sse";
|
|
403
|
+
const tinyfishBrowserProfile = process.env.TINYFISH_BROWSER_PROFILE?.trim() || "lite";
|
|
404
|
+
const tinyfishTimeoutMs = parseInt(process.env.TINYFISH_TIMEOUT_MS ?? "120000", 10);
|
|
405
|
+
const tinyfishProxyEnabled = process.env.TINYFISH_PROXY_ENABLED === "true";
|
|
406
|
+
const tinyfishProxyCountry = process.env.TINYFISH_PROXY_COUNTRY?.trim() || "US";
|
|
407
|
+
|
|
400
408
|
const policyMaxSteps = Number.parseInt(process.env.POLICY_MAX_STEPS ?? "8", 10);
|
|
401
409
|
const policyMaxToolCalls = Number.parseInt(process.env.POLICY_MAX_TOOL_CALLS ?? "12", 10);
|
|
402
410
|
const policyToolLoopThreshold = Number.parseInt(process.env.POLICY_TOOL_LOOP_THRESHOLD ?? "10", 10);
|
|
@@ -610,6 +618,12 @@ var config = {
|
|
|
610
618
|
endpoint: moonshotEndpoint,
|
|
611
619
|
model: moonshotModel,
|
|
612
620
|
},
|
|
621
|
+
codex: {
|
|
622
|
+
enabled: process.env.CODEX_ENABLED !== "false",
|
|
623
|
+
binaryPath: process.env.CODEX_BINARY_PATH?.trim() || null,
|
|
624
|
+
model: process.env.CODEX_MODEL?.trim() || "gpt-5.3-codex",
|
|
625
|
+
timeout: Number.parseInt(process.env.CODEX_TIMEOUT || "120000", 10) || 120000,
|
|
626
|
+
},
|
|
613
627
|
hotReload: {
|
|
614
628
|
enabled: hotReloadEnabled,
|
|
615
629
|
debounceMs: Number.isNaN(hotReloadDebounceMs) ? 1000 : hotReloadDebounceMs,
|
|
@@ -660,6 +674,14 @@ var config = {
|
|
|
660
674
|
retryEnabled: webSearchRetryEnabled,
|
|
661
675
|
maxRetries: Number.isNaN(webSearchMaxRetries) ? 2 : webSearchMaxRetries,
|
|
662
676
|
},
|
|
677
|
+
tinyfish: {
|
|
678
|
+
apiKey: tinyfishApiKey,
|
|
679
|
+
endpoint: tinyfishEndpoint,
|
|
680
|
+
browserProfile: tinyfishBrowserProfile,
|
|
681
|
+
timeoutMs: Number.isNaN(tinyfishTimeoutMs) ? 120000 : tinyfishTimeoutMs,
|
|
682
|
+
proxyEnabled: tinyfishProxyEnabled,
|
|
683
|
+
proxyCountry: tinyfishProxyCountry,
|
|
684
|
+
},
|
|
663
685
|
policy: {
|
|
664
686
|
maxStepsPerTurn: Number.isNaN(policyMaxSteps) ? 8 : policyMaxSteps,
|
|
665
687
|
maxToolCallsPerTurn: Number.isNaN(policyMaxToolCalls) ? 12 : policyMaxToolCalls,
|
|
@@ -717,6 +739,10 @@ var config = {
|
|
|
717
739
|
manifestPath: sandboxManifestPath,
|
|
718
740
|
manifestDirs: sandboxManifestDirs,
|
|
719
741
|
},
|
|
742
|
+
codeMode: {
|
|
743
|
+
enabled: process.env.CODE_MODE_ENABLED === 'true',
|
|
744
|
+
toolListCacheTtl: parseInt(process.env.CODE_MODE_CACHE_TTL, 10) || 60_000,
|
|
745
|
+
},
|
|
720
746
|
},
|
|
721
747
|
promptCache: {
|
|
722
748
|
enabled: promptCacheEnabled,
|
|
@@ -902,6 +928,25 @@ var config = {
|
|
|
902
928
|
COMPLEX: process.env.TIER_COMPLEX?.trim() || null,
|
|
903
929
|
REASONING: process.env.TIER_REASONING?.trim() || null,
|
|
904
930
|
},
|
|
931
|
+
|
|
932
|
+
// Graphify knowledge graph integration (structural analysis)
|
|
933
|
+
codeGraph: {
|
|
934
|
+
enabled: process.env.CODE_GRAPH_ENABLED === 'true',
|
|
935
|
+
command: process.env.CODE_GRAPH_COMMAND || 'graphify',
|
|
936
|
+
workspace: process.env.CODE_GRAPH_WORKSPACE || process.cwd(),
|
|
937
|
+
timeout: parseInt(process.env.CODE_GRAPH_TIMEOUT, 10) || 10000,
|
|
938
|
+
},
|
|
939
|
+
|
|
940
|
+
// Large payload optimization (skip cloning media blocks that get discarded)
|
|
941
|
+
largePayload: {
|
|
942
|
+
enabled: process.env.LARGE_PAYLOAD_OPTIMIZATION !== 'false',
|
|
943
|
+
threshold: parseInt(process.env.LARGE_PAYLOAD_THRESHOLD, 10) || 1_048_576,
|
|
944
|
+
},
|
|
945
|
+
|
|
946
|
+
// OpenClaw integration
|
|
947
|
+
openclaw: {
|
|
948
|
+
enabled: process.env.OPENCLAW_MODE === "true",
|
|
949
|
+
},
|
|
905
950
|
};
|
|
906
951
|
|
|
907
952
|
/**
|
|
@@ -938,15 +983,47 @@ function reloadConfig() {
|
|
|
938
983
|
config.modelProvider.fallbackProvider = (process.env.FALLBACK_PROVIDER ?? "databricks").toLowerCase();
|
|
939
984
|
config.modelProvider.suggestionModeModel = (process.env.SUGGESTION_MODE_MODEL ?? "default").trim();
|
|
940
985
|
|
|
986
|
+
// TinyFish config reload
|
|
987
|
+
config.tinyfish.apiKey = process.env.TINYFISH_API_KEY?.trim() || null;
|
|
988
|
+
config.tinyfish.browserProfile = process.env.TINYFISH_BROWSER_PROFILE?.trim() || "lite";
|
|
989
|
+
|
|
941
990
|
config.toon.enabled = process.env.TOON_ENABLED === "true";
|
|
942
991
|
const newToonMinBytes = Number.parseInt(process.env.TOON_MIN_BYTES ?? "4096", 10);
|
|
943
992
|
config.toon.minBytes = Number.isNaN(newToonMinBytes) ? 4096 : newToonMinBytes;
|
|
944
993
|
config.toon.failOpen = process.env.TOON_FAIL_OPEN !== "false";
|
|
945
994
|
config.toon.logStats = process.env.TOON_LOG_STATS !== "false";
|
|
946
995
|
|
|
996
|
+
// Tier routing (critical for fixing model name issues without restart)
|
|
997
|
+
config.modelTiers.SIMPLE = process.env.TIER_SIMPLE?.trim() || null;
|
|
998
|
+
config.modelTiers.MEDIUM = process.env.TIER_MEDIUM?.trim() || null;
|
|
999
|
+
config.modelTiers.COMPLEX = process.env.TIER_COMPLEX?.trim() || null;
|
|
1000
|
+
config.modelTiers.REASONING = process.env.TIER_REASONING?.trim() || null;
|
|
1001
|
+
config.modelTiers.enabled = !!(config.modelTiers.SIMPLE && config.modelTiers.MEDIUM && config.modelTiers.COMPLEX && config.modelTiers.REASONING);
|
|
1002
|
+
|
|
1003
|
+
// Ollama model
|
|
1004
|
+
config.ollama.endpoint = process.env.OLLAMA_ENDPOINT ?? config.ollama.endpoint;
|
|
1005
|
+
|
|
1006
|
+
// OpenClaw
|
|
1007
|
+
config.openclaw.enabled = process.env.OPENCLAW_MODE === "true";
|
|
1008
|
+
|
|
1009
|
+
// Graphify
|
|
1010
|
+
config.codeGraph.enabled = process.env.CODE_GRAPH_ENABLED === 'true';
|
|
1011
|
+
|
|
1012
|
+
// Code Mode
|
|
1013
|
+
config.mcp.codeMode.enabled = process.env.CODE_MODE_ENABLED === 'true';
|
|
1014
|
+
|
|
947
1015
|
// Log level
|
|
948
1016
|
config.logger.level = process.env.LOG_LEVEL ?? "info";
|
|
949
1017
|
|
|
1018
|
+
// Reset circuit breakers so stale OPEN states don't persist
|
|
1019
|
+
try {
|
|
1020
|
+
const { getCircuitBreakerRegistry } = require('../clients/circuit-breaker');
|
|
1021
|
+
getCircuitBreakerRegistry().resetAll();
|
|
1022
|
+
console.log("[CONFIG] Circuit breakers reset");
|
|
1023
|
+
} catch (e) {
|
|
1024
|
+
// Ignore if not yet initialized
|
|
1025
|
+
}
|
|
1026
|
+
|
|
950
1027
|
console.log("[CONFIG] Configuration reloaded from environment");
|
|
951
1028
|
return config;
|
|
952
1029
|
}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
const logger = require('../logger');
|
|
13
13
|
const config = require('../config');
|
|
14
|
+
const distill = require('./distill');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Compress conversation history to fit within token budget
|
|
@@ -62,6 +63,18 @@ function compressHistory(messages, options = {}) {
|
|
|
62
63
|
compressed = oldMessages.map(msg => compressMessage(msg));
|
|
63
64
|
}
|
|
64
65
|
|
|
66
|
+
// Apply Distill dedup across all old messages to collapse repetitive tool results
|
|
67
|
+
if (compressed.length > 0) {
|
|
68
|
+
const dedupResult = distill.deduplicateHistory(compressed);
|
|
69
|
+
if (dedupResult.stats.deduplicated > 0) {
|
|
70
|
+
compressed = dedupResult.messages;
|
|
71
|
+
logger.debug({
|
|
72
|
+
checked: dedupResult.stats.checked,
|
|
73
|
+
deduplicated: dedupResult.stats.deduplicated,
|
|
74
|
+
}, '[Distill] History dedup applied to old messages');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
65
78
|
// Add recent messages (may compress tool results but keep content)
|
|
66
79
|
const recentCompressed = recentMessages.map(msg => compressToolResults(msg));
|
|
67
80
|
|
|
@@ -248,12 +261,15 @@ function compressContentBlock(block) {
|
|
|
248
261
|
* Compress tool result block
|
|
249
262
|
*
|
|
250
263
|
* Tool results can be very large (file contents, bash output).
|
|
251
|
-
*
|
|
264
|
+
* Uses Distill algorithms (normalization, delta, dedup) before
|
|
265
|
+
* falling back to head/tail truncation.
|
|
252
266
|
*
|
|
253
267
|
* @param {Object} block - tool_result block
|
|
268
|
+
* @param {Object} options - Options for compression
|
|
269
|
+
* @param {string} options.previousResult - Previous tool result for delta rendering
|
|
254
270
|
* @returns {Object} Compressed tool_result
|
|
255
271
|
*/
|
|
256
|
-
function compressToolResultBlock(block) {
|
|
272
|
+
function compressToolResultBlock(block, options = {}) {
|
|
257
273
|
if (!block || block.type !== 'tool_result') return block;
|
|
258
274
|
|
|
259
275
|
const compressed = {
|
|
@@ -261,18 +277,32 @@ function compressToolResultBlock(block) {
|
|
|
261
277
|
tool_use_id: block.tool_use_id,
|
|
262
278
|
};
|
|
263
279
|
|
|
264
|
-
// Compress content
|
|
280
|
+
// Compress content using Distill when content is large enough to benefit
|
|
265
281
|
if (typeof block.content === 'string') {
|
|
266
|
-
|
|
282
|
+
if (block.content.length > 500) {
|
|
283
|
+
const result = distill.compressToolResult(block.content, {
|
|
284
|
+
previousResult: options.previousResult,
|
|
285
|
+
maxLength: 500,
|
|
286
|
+
});
|
|
287
|
+
compressed.content = result.text;
|
|
288
|
+
} else {
|
|
289
|
+
compressed.content = block.content;
|
|
290
|
+
}
|
|
267
291
|
} else if (Array.isArray(block.content)) {
|
|
268
292
|
compressed.content = block.content.map(item => {
|
|
269
293
|
if (typeof item === 'string') {
|
|
270
|
-
|
|
294
|
+
if (item.length > 500) {
|
|
295
|
+
return distill.compressToolResult(item, { maxLength: 500 }).text;
|
|
296
|
+
}
|
|
297
|
+
return item;
|
|
271
298
|
} else if (item.type === 'text') {
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
299
|
+
if (item.text && item.text.length > 500) {
|
|
300
|
+
return {
|
|
301
|
+
type: 'text',
|
|
302
|
+
text: distill.compressToolResult(item.text, { maxLength: 500 }).text,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
return item;
|
|
276
306
|
}
|
|
277
307
|
return item;
|
|
278
308
|
});
|
|
@@ -453,7 +483,10 @@ module.exports = {
|
|
|
453
483
|
compressHistory,
|
|
454
484
|
compressMessage,
|
|
455
485
|
compressToolResults,
|
|
486
|
+
compressToolResultBlock,
|
|
456
487
|
calculateCompressionStats,
|
|
457
488
|
needsCompression,
|
|
458
489
|
summarizeOldHistory,
|
|
490
|
+
// Distill re-exports for direct access
|
|
491
|
+
distill,
|
|
459
492
|
};
|