lynkr 9.9.1 → 9.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +50 -9
  2. package/bin/cli.js +2 -0
  3. package/bin/lynkr-init.js +11 -12
  4. package/bin/lynkr-reset.js +71 -0
  5. package/config/model-tiers.json +199 -52
  6. package/package.json +3 -3
  7. package/public/dashboard.html +29 -0
  8. package/scripts/validate-difficulty-classifier.js +27 -6
  9. package/src/api/providers-handler.js +0 -1
  10. package/src/api/router.js +309 -160
  11. package/src/clients/databricks.js +215 -71
  12. package/src/clients/gpt-utils.js +8 -1
  13. package/src/clients/ollama-utils.js +19 -10
  14. package/src/clients/prompt-cache-injection.js +93 -30
  15. package/src/config/index.js +82 -43
  16. package/src/context/compression.js +27 -8
  17. package/src/context/distill.js +2 -1
  18. package/src/dashboard/api.js +5 -1
  19. package/src/memory/distiller.js +350 -0
  20. package/src/memory/extractor.js +0 -8
  21. package/src/memory/format.js +17 -2
  22. package/src/memory/index.js +10 -0
  23. package/src/memory/retriever.js +7 -2
  24. package/src/memory/skills-cache.js +180 -0
  25. package/src/memory/tencentdb-launcher.js +459 -0
  26. package/src/memory/wiki.js +171 -0
  27. package/src/orchestrator/index.js +202 -1237
  28. package/src/orchestrator/passthrough-stream.js +382 -0
  29. package/src/orchestrator/sse-transformer.js +408 -0
  30. package/src/prompts/system.js +20 -6
  31. package/src/routing/affinity-store.js +85 -4
  32. package/src/routing/agentic-detector.js +9 -1
  33. package/src/routing/cache-economics.js +123 -0
  34. package/src/routing/cache-switch-cost.js +194 -0
  35. package/src/routing/calibration.js +8 -1
  36. package/src/routing/complexity-analyzer.js +7 -1
  37. package/src/routing/context-compressor.js +170 -0
  38. package/src/routing/difficulty-classifier.js +161 -17
  39. package/src/routing/index.js +196 -56
  40. package/src/routing/intent-score.js +20 -2
  41. package/src/routing/model-registry.js +35 -10
  42. package/src/routing/session-affinity.js +68 -1
  43. package/src/routing/side-channel-detector.js +103 -0
  44. package/src/routing/telemetry.js +139 -2
  45. package/src/routing/verifier.js +41 -2
  46. package/src/server.js +36 -49
  47. package/src/tools/code-graph.js +6 -4
  48. package/src/agents/context-manager.js +0 -236
  49. package/src/agents/decomposition/dispatcher.js +0 -185
  50. package/src/agents/decomposition/gate.js +0 -136
  51. package/src/agents/decomposition/index.js +0 -183
  52. package/src/agents/decomposition/model-call.js +0 -75
  53. package/src/agents/decomposition/planner.js +0 -223
  54. package/src/agents/decomposition/synthesizer.js +0 -89
  55. package/src/agents/decomposition/telemetry.js +0 -55
  56. package/src/agents/definitions/loader.js +0 -653
  57. package/src/agents/executor.js +0 -457
  58. package/src/agents/index.js +0 -165
  59. package/src/agents/parallel-coordinator.js +0 -68
  60. package/src/agents/reflector.js +0 -331
  61. package/src/agents/skillbook.js +0 -331
  62. package/src/agents/store.js +0 -259
  63. package/src/edits/index.js +0 -171
  64. package/src/indexer/babel-parser.js +0 -213
  65. package/src/indexer/index.js +0 -1629
  66. package/src/indexer/navigation/index.js +0 -32
  67. package/src/indexer/navigation/providers/treeSitter.js +0 -36
  68. package/src/indexer/parser.js +0 -443
  69. package/src/tasks/store.js +0 -349
  70. package/src/tests/coverage.js +0 -173
  71. package/src/tests/index.js +0 -171
  72. package/src/tests/store.js +0 -213
  73. package/src/tools/agent-task.js +0 -145
  74. package/src/tools/code-mode.js +0 -304
  75. package/src/tools/decompose.js +0 -91
  76. package/src/tools/edits.js +0 -94
  77. package/src/tools/execution.js +0 -171
  78. package/src/tools/git.js +0 -1346
  79. package/src/tools/index.js +0 -306
  80. package/src/tools/indexer.js +0 -360
  81. package/src/tools/lazy-loader.js +0 -366
  82. package/src/tools/mcp-remote.js +0 -88
  83. package/src/tools/mcp.js +0 -116
  84. package/src/tools/process.js +0 -167
  85. package/src/tools/smart-selection.js +0 -180
  86. package/src/tools/stubs.js +0 -55
  87. package/src/tools/tasks.js +0 -260
  88. package/src/tools/tests.js +0 -132
  89. package/src/tools/tinyfish.js +0 -358
  90. package/src/tools/truncate.js +0 -106
  91. package/src/tools/web-client.js +0 -71
  92. package/src/tools/web.js +0 -415
  93. package/src/tools/workspace.js +0 -204
@@ -1,259 +0,0 @@
1
- let Database;
2
- try {
3
- Database = require("better-sqlite3");
4
- } catch {
5
- Database = null;
6
- }
7
- const path = require("path");
8
- const fs = require("fs");
9
- const logger = require("../logger");
10
-
11
- class AgentStore {
12
- constructor() {
13
- if (!Database) {
14
- this.db = null;
15
- return;
16
- }
17
-
18
- try {
19
- // Use same database location as main app
20
- const dbDir = path.join(process.cwd(), "data");
21
- if (!fs.existsSync(dbDir)) {
22
- fs.mkdirSync(dbDir, { recursive: true });
23
- }
24
-
25
- const dbPath = path.join(dbDir, "lynkr.db");
26
- this.db = new Database(dbPath, {
27
- verbose: process.env.DEBUG_SQL ? console.log : null,
28
- fileMustExist: false
29
- });
30
-
31
- this.initTables();
32
- this.prepareStatements();
33
- } catch (err) {
34
- logger.warn({ err: err.message }, "AgentStore: better-sqlite3 not available");
35
- this.db = null;
36
- }
37
- }
38
-
39
- initTables() {
40
- this.db.exec(`
41
- CREATE TABLE IF NOT EXISTS agent_executions (
42
- id INTEGER PRIMARY KEY AUTOINCREMENT,
43
- session_id TEXT,
44
- agent_type TEXT NOT NULL,
45
- prompt TEXT NOT NULL,
46
- model TEXT NOT NULL,
47
- status TEXT NOT NULL, -- 'pending', 'running', 'completed', 'failed'
48
- result TEXT,
49
- error TEXT,
50
- steps INTEGER DEFAULT 0,
51
- duration_ms INTEGER,
52
- input_tokens INTEGER DEFAULT 0,
53
- output_tokens INTEGER DEFAULT 0,
54
- created_at INTEGER NOT NULL,
55
- completed_at INTEGER
56
- );
57
-
58
- CREATE INDEX IF NOT EXISTS idx_agent_executions_session_id
59
- ON agent_executions(session_id);
60
-
61
- CREATE INDEX IF NOT EXISTS idx_agent_executions_agent_type
62
- ON agent_executions(agent_type);
63
-
64
- CREATE INDEX IF NOT EXISTS idx_agent_executions_status
65
- ON agent_executions(status);
66
-
67
- CREATE INDEX IF NOT EXISTS idx_agent_executions_created_at
68
- ON agent_executions(created_at DESC);
69
- `);
70
-
71
- logger.info("Agent store tables initialized");
72
- }
73
-
74
- prepareStatements() {
75
- this.stmts = {
76
- create: this.db.prepare(`
77
- INSERT INTO agent_executions (
78
- session_id, agent_type, prompt, model, status, created_at
79
- ) VALUES (?, ?, ?, ?, ?, ?)
80
- `),
81
-
82
- updateStatus: this.db.prepare(`
83
- UPDATE agent_executions
84
- SET status = ?, completed_at = ?
85
- WHERE id = ?
86
- `),
87
-
88
- complete: this.db.prepare(`
89
- UPDATE agent_executions
90
- SET status = 'completed',
91
- result = ?,
92
- steps = ?,
93
- duration_ms = ?,
94
- input_tokens = ?,
95
- output_tokens = ?,
96
- completed_at = ?
97
- WHERE id = ?
98
- `),
99
-
100
- fail: this.db.prepare(`
101
- UPDATE agent_executions
102
- SET status = 'failed',
103
- error = ?,
104
- steps = ?,
105
- duration_ms = ?,
106
- completed_at = ?
107
- WHERE id = ?
108
- `),
109
-
110
- get: this.db.prepare(`
111
- SELECT * FROM agent_executions WHERE id = ?
112
- `),
113
-
114
- getBySession: this.db.prepare(`
115
- SELECT * FROM agent_executions
116
- WHERE session_id = ?
117
- ORDER BY created_at DESC
118
- `),
119
-
120
- getRecent: this.db.prepare(`
121
- SELECT * FROM agent_executions
122
- ORDER BY created_at DESC
123
- LIMIT ?
124
- `),
125
-
126
- stats: this.db.prepare(`
127
- SELECT
128
- agent_type,
129
- COUNT(*) as total_executions,
130
- SUM(CASE WHEN status = 'completed' THEN 1 ELSE 0 END) as completed,
131
- SUM(CASE WHEN status = 'failed' THEN 1 ELSE 0 END) as failed,
132
- AVG(CASE WHEN status = 'completed' THEN duration_ms ELSE NULL END) as avg_duration_ms,
133
- SUM(input_tokens) as total_input_tokens,
134
- SUM(output_tokens) as total_output_tokens
135
- FROM agent_executions
136
- GROUP BY agent_type
137
- `)
138
- };
139
- }
140
-
141
- /**
142
- * Create new agent execution
143
- */
144
- createExecution({ sessionId, agentType, prompt, model }) {
145
- const now = Date.now();
146
- const result = this.stmts.create.run(
147
- sessionId || null,
148
- agentType,
149
- prompt,
150
- model,
151
- 'pending',
152
- now
153
- );
154
-
155
- logger.info({
156
- executionId: result.lastInsertRowid,
157
- agentType,
158
- sessionId
159
- }, "Created agent execution");
160
-
161
- return result.lastInsertRowid;
162
- }
163
-
164
- /**
165
- * Update execution status
166
- */
167
- updateStatus(executionId, status) {
168
- const now = Date.now();
169
- this.stmts.updateStatus.run(status, now, executionId);
170
- }
171
-
172
- /**
173
- * Mark execution as completed
174
- */
175
- completeExecution(executionId, result, stats = {}) {
176
- const now = Date.now();
177
- this.stmts.complete.run(
178
- result,
179
- stats.steps || 0,
180
- stats.durationMs || 0,
181
- stats.inputTokens || 0,
182
- stats.outputTokens || 0,
183
- now,
184
- executionId
185
- );
186
-
187
- logger.info({
188
- executionId,
189
- steps: stats.steps,
190
- durationMs: stats.durationMs
191
- }, "Agent execution completed");
192
- }
193
-
194
- /**
195
- * Mark execution as failed
196
- */
197
- failExecution(executionId, error, stats = {}) {
198
- const now = Date.now();
199
- this.stmts.fail.run(
200
- error.message || String(error),
201
- stats.steps || 0,
202
- stats.durationMs || 0,
203
- now,
204
- executionId
205
- );
206
-
207
- logger.warn({
208
- executionId,
209
- error: error.message
210
- }, "Agent execution failed");
211
- }
212
-
213
- /**
214
- * Get execution by ID
215
- */
216
- getExecution(executionId) {
217
- return this.stmts.get.get(executionId);
218
- }
219
-
220
- /**
221
- * Get executions by session
222
- */
223
- getSessionExecutions(sessionId) {
224
- return this.stmts.getBySession.all(sessionId);
225
- }
226
-
227
- /**
228
- * Get recent executions
229
- */
230
- getRecentExecutions(limit = 100) {
231
- return this.stmts.getRecent.all(limit);
232
- }
233
-
234
- /**
235
- * Get aggregate statistics
236
- */
237
- getStats() {
238
- return this.stmts.stats.all();
239
- }
240
-
241
- /**
242
- * Close database connection
243
- */
244
- close() {
245
- this.db.close();
246
- }
247
- }
248
-
249
- // Singleton instance
250
- let instance = null;
251
-
252
- function getInstance() {
253
- if (!instance) {
254
- instance = new AgentStore();
255
- }
256
- return instance;
257
- }
258
-
259
- module.exports = getInstance();
@@ -1,171 +0,0 @@
1
- const fs = require("fs/promises");
2
- const path = require("path");
3
- const { createTwoFilesPatch } = require("diff");
4
- const db = require("../db");
5
- const { resolveWorkspacePath } = require("../workspace");
6
- const logger = require("../logger");
7
-
8
- const insertEditStmt = db.prepare(
9
- `INSERT INTO edits (
10
- session_id,
11
- file_path,
12
- created_at,
13
- source,
14
- before_content,
15
- after_content,
16
- diff,
17
- metadata
18
- ) VALUES (
19
- @session_id,
20
- @file_path,
21
- @created_at,
22
- @source,
23
- @before_content,
24
- @after_content,
25
- @diff,
26
- @metadata
27
- )`,
28
- );
29
-
30
- const selectEditByIdStmt = db.prepare(
31
- `SELECT id, session_id, file_path, created_at, source,
32
- before_content, after_content, diff, metadata
33
- FROM edits WHERE id = ?`,
34
- );
35
-
36
- function buildHistoryQuery({ filePath, sessionId, limit }) {
37
- let query = `SELECT id, session_id, file_path, created_at, source,
38
- before_content, after_content, diff, metadata
39
- FROM edits`;
40
- const conditions = [];
41
- const params = [];
42
- if (filePath) {
43
- conditions.push("file_path = ?");
44
- params.push(filePath);
45
- }
46
- if (sessionId) {
47
- conditions.push("session_id = ?");
48
- params.push(sessionId);
49
- }
50
- if (conditions.length) {
51
- query += ` WHERE ${conditions.join(" AND ")}`;
52
- }
53
- query += " ORDER BY created_at DESC";
54
- if (limit) {
55
- query += ` LIMIT ${limit}`;
56
- }
57
- return { query, params };
58
- }
59
-
60
- function computeDiff(filePath, beforeContent, afterContent) {
61
- const before = beforeContent ?? "";
62
- const after = afterContent ?? "";
63
- if (before === after) return null;
64
- return createTwoFilesPatch(
65
- path.join("before", filePath),
66
- path.join("after", filePath),
67
- before,
68
- after,
69
- undefined,
70
- undefined,
71
- { context: 3 },
72
- );
73
- }
74
-
75
- function recordEdit({
76
- sessionId,
77
- filePath,
78
- source = "tool",
79
- beforeContent,
80
- afterContent,
81
- metadata,
82
- }) {
83
- if ((beforeContent ?? "") === (afterContent ?? "")) {
84
- return null;
85
- }
86
- const diff = computeDiff(filePath, beforeContent, afterContent);
87
- const createdAt = Date.now();
88
- insertEditStmt.run({
89
- session_id: sessionId ?? null,
90
- file_path: filePath,
91
- created_at: createdAt,
92
- source,
93
- before_content: beforeContent ?? null,
94
- after_content: afterContent ?? null,
95
- diff,
96
- metadata: metadata ? JSON.stringify(metadata) : null,
97
- });
98
- logger.info(
99
- {
100
- sessionId,
101
- filePath,
102
- source,
103
- },
104
- "Recorded workspace edit",
105
- );
106
- return {
107
- sessionId,
108
- filePath,
109
- createdAt,
110
- diff,
111
- };
112
- }
113
-
114
- function getEditHistory({ filePath, sessionId, limit }) {
115
- const { query, params } = buildHistoryQuery({
116
- filePath,
117
- sessionId,
118
- limit,
119
- });
120
- const rows = db.prepare(query).all(...params);
121
- return rows.map((row) => ({
122
- id: row.id,
123
- sessionId: row.session_id,
124
- filePath: row.file_path,
125
- createdAt: row.created_at,
126
- source: row.source,
127
- diff: row.diff,
128
- metadata: row.metadata ? JSON.parse(row.metadata) : null,
129
- }));
130
- }
131
-
132
- async function revertEdit({ editId, sessionId }) {
133
- const row = selectEditByIdStmt.get(editId);
134
- if (!row) {
135
- throw new Error(`Edit ${editId} not found.`);
136
- }
137
-
138
- const absolute = resolveWorkspacePath(row.file_path);
139
- if (row.before_content === null || row.before_content === undefined) {
140
- // Original file did not exist; delete if present.
141
- try {
142
- await fs.unlink(absolute);
143
- } catch (err) {
144
- if (err.code !== "ENOENT") {
145
- throw err;
146
- }
147
- }
148
- } else {
149
- await fs.writeFile(absolute, row.before_content, "utf8");
150
- }
151
-
152
- recordEdit({
153
- sessionId,
154
- filePath: row.file_path,
155
- source: "revert_edit",
156
- beforeContent: row.after_content,
157
- afterContent: row.before_content,
158
- metadata: { revertedEditId: row.id },
159
- });
160
-
161
- return {
162
- revertedEditId: row.id,
163
- filePath: row.file_path,
164
- };
165
- }
166
-
167
- module.exports = {
168
- recordEdit,
169
- getEditHistory,
170
- revertEdit,
171
- };
@@ -1,213 +0,0 @@
1
- /**
2
- * Babel-based Parser (Pure JS Fallback)
3
- *
4
- * Used when tree-sitter is unavailable (e.g., Node 25 without prebuilt binaries).
5
- * Supports JavaScript, TypeScript, JSX, and TSX.
6
- *
7
- * @module indexer/babel-parser
8
- */
9
-
10
- const logger = require("../logger");
11
-
12
- let babelParser = null;
13
- let babelTraverse = null;
14
-
15
- function loadBabel() {
16
- if (babelParser) return true;
17
- try {
18
- babelParser = require("@babel/parser");
19
- babelTraverse = require("@babel/traverse").default;
20
- return true;
21
- } catch (err) {
22
- logger.warn({ err: err.message }, "[BabelParser] Failed to load @babel/parser");
23
- return false;
24
- }
25
- }
26
-
27
- const LANGUAGE_TO_PLUGINS = {
28
- javascript: ["jsx"],
29
- "javascript-react": ["jsx"],
30
- typescript: ["typescript", "jsx"],
31
- "typescript-react": ["typescript", "jsx"],
32
- };
33
-
34
- /**
35
- * Parse JavaScript/TypeScript file using Babel
36
- */
37
- function parseFile(relativePath, content, language) {
38
- if (!loadBabel()) return null;
39
-
40
- const plugins = LANGUAGE_TO_PLUGINS[language];
41
- if (!plugins) {
42
- logger.debug({ language }, "[BabelParser] Unsupported language");
43
- return null;
44
- }
45
-
46
- try {
47
- const ast = babelParser.parse(content, {
48
- sourceType: "unambiguous",
49
- plugins: [...plugins, "decorators-legacy", "classProperties", "dynamicImport"],
50
- errorRecovery: true,
51
- });
52
-
53
- const symbols = [];
54
- const dependencies = [];
55
- const imports = [];
56
- const exports = [];
57
- const references = [];
58
-
59
- babelTraverse(ast, {
60
- // Functions
61
- FunctionDeclaration(path) {
62
- if (path.node.id) {
63
- symbols.push({
64
- name: path.node.id.name,
65
- kind: "function",
66
- line: path.node.loc?.start.line || 1,
67
- column: (path.node.loc?.start.column || 0) + 1,
68
- });
69
- }
70
- },
71
-
72
- // Arrow functions assigned to variables
73
- VariableDeclarator(path) {
74
- if (
75
- path.node.init &&
76
- (path.node.init.type === "ArrowFunctionExpression" ||
77
- path.node.init.type === "FunctionExpression") &&
78
- path.node.id?.type === "Identifier"
79
- ) {
80
- symbols.push({
81
- name: path.node.id.name,
82
- kind: "function",
83
- line: path.node.loc?.start.line || 1,
84
- column: (path.node.loc?.start.column || 0) + 1,
85
- });
86
- }
87
- },
88
-
89
- // Classes
90
- ClassDeclaration(path) {
91
- if (path.node.id) {
92
- symbols.push({
93
- name: path.node.id.name,
94
- kind: "class",
95
- line: path.node.loc?.start.line || 1,
96
- column: (path.node.loc?.start.column || 0) + 1,
97
- });
98
- }
99
- },
100
-
101
- // Class methods
102
- ClassMethod(path) {
103
- if (path.node.key?.type === "Identifier") {
104
- symbols.push({
105
- name: path.node.key.name,
106
- kind: "method",
107
- line: path.node.loc?.start.line || 1,
108
- column: (path.node.loc?.start.column || 0) + 1,
109
- });
110
- }
111
- },
112
-
113
- // Import statements
114
- ImportDeclaration(path) {
115
- const importPath = path.node.source.value;
116
- dependencies.push({
117
- kind: "import",
118
- path: importPath,
119
- metadata: { clause: content.slice(path.node.start, path.node.end) },
120
- });
121
- imports.push({
122
- path: importPath,
123
- clause: content.slice(path.node.start, path.node.end),
124
- line: path.node.loc?.start.line || 1,
125
- column: (path.node.loc?.start.column || 0) + 1,
126
- });
127
- },
128
-
129
- // require() calls
130
- CallExpression(path) {
131
- if (
132
- path.node.callee?.type === "Identifier" &&
133
- path.node.callee.name === "require" &&
134
- path.node.arguments[0]?.type === "StringLiteral"
135
- ) {
136
- const reqPath = path.node.arguments[0].value;
137
- dependencies.push({
138
- kind: "require",
139
- path: reqPath,
140
- metadata: { clause: content.slice(path.node.start, path.node.end) },
141
- });
142
- imports.push({
143
- path: reqPath,
144
- clause: content.slice(path.node.start, path.node.end),
145
- line: path.node.loc?.start.line || 1,
146
- column: (path.node.loc?.start.column || 0) + 1,
147
- });
148
- }
149
- },
150
-
151
- // Exports
152
- ExportNamedDeclaration(path) {
153
- exports.push({
154
- clause: content.slice(path.node.start, path.node.end).substring(0, 200),
155
- line: path.node.loc?.start.line || 1,
156
- column: (path.node.loc?.start.column || 0) + 1,
157
- });
158
- },
159
-
160
- ExportDefaultDeclaration(path) {
161
- exports.push({
162
- clause: content.slice(path.node.start, path.node.end).substring(0, 200),
163
- line: path.node.loc?.start.line || 1,
164
- column: (path.node.loc?.start.column || 0) + 1,
165
- });
166
- },
167
-
168
- // Track identifiers for references (limited to avoid noise)
169
- Identifier(path) {
170
- // Only track identifiers in call expressions or member expressions
171
- if (
172
- path.parent?.type === "CallExpression" &&
173
- path.parent.callee === path.node
174
- ) {
175
- references.push({
176
- name: path.node.name,
177
- line: path.node.loc?.start.line || 1,
178
- column: (path.node.loc?.start.column || 0) + 1,
179
- snippet: path.node.name,
180
- });
181
- }
182
- },
183
- });
184
-
185
- const langType = language.includes("typescript") ? "typescript" : "javascript";
186
-
187
- return {
188
- symbols,
189
- dependencies,
190
- references,
191
- imports,
192
- exports,
193
- language: langType,
194
- definitions: symbols,
195
- parser: "babel",
196
- };
197
- } catch (err) {
198
- logger.warn({ err: err.message, file: relativePath }, "[BabelParser] Parse failed");
199
- return null;
200
- }
201
- }
202
-
203
- /**
204
- * Check if Babel parser is available
205
- */
206
- function isBabelAvailable() {
207
- return loadBabel();
208
- }
209
-
210
- module.exports = {
211
- parseFile,
212
- isBabelAvailable,
213
- };