lynkr 9.9.0 → 9.10.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 (73) hide show
  1. package/README.md +92 -23
  2. package/bin/cli.js +13 -7
  3. package/bin/lynkr-init.js +34 -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/scripts/build-eval-set.js +256 -0
  8. package/scripts/mine-difficulty-anchors.js +288 -0
  9. package/scripts/validate-difficulty-classifier.js +144 -0
  10. package/scripts/validate-intent-anchors.js +186 -0
  11. package/src/api/providers-handler.js +0 -1
  12. package/src/api/router.js +292 -160
  13. package/src/clients/databricks.js +95 -6
  14. package/src/config/index.js +3 -43
  15. package/src/context/tool-result-compressor.js +883 -40
  16. package/src/orchestrator/index.js +117 -1235
  17. package/src/orchestrator/passthrough-stream.js +382 -0
  18. package/src/orchestrator/sse-transformer.js +408 -0
  19. package/src/routing/affinity-store.js +17 -3
  20. package/src/routing/classifier-setup.js +207 -0
  21. package/src/routing/complexity-analyzer.js +40 -4
  22. package/src/routing/difficulty-classifier.js +261 -0
  23. package/src/routing/index.js +70 -7
  24. package/src/routing/intent-score.js +132 -12
  25. package/src/routing/session-affinity.js +8 -1
  26. package/src/routing/side-channel-detector.js +103 -0
  27. package/src/server.js +20 -46
  28. package/src/agents/context-manager.js +0 -236
  29. package/src/agents/decomposition/dispatcher.js +0 -185
  30. package/src/agents/decomposition/gate.js +0 -136
  31. package/src/agents/decomposition/index.js +0 -183
  32. package/src/agents/decomposition/model-call.js +0 -75
  33. package/src/agents/decomposition/planner.js +0 -223
  34. package/src/agents/decomposition/synthesizer.js +0 -89
  35. package/src/agents/decomposition/telemetry.js +0 -55
  36. package/src/agents/definitions/loader.js +0 -653
  37. package/src/agents/executor.js +0 -457
  38. package/src/agents/index.js +0 -165
  39. package/src/agents/parallel-coordinator.js +0 -68
  40. package/src/agents/reflector.js +0 -331
  41. package/src/agents/skillbook.js +0 -331
  42. package/src/agents/store.js +0 -259
  43. package/src/edits/index.js +0 -171
  44. package/src/indexer/babel-parser.js +0 -213
  45. package/src/indexer/index.js +0 -1629
  46. package/src/indexer/navigation/index.js +0 -32
  47. package/src/indexer/navigation/providers/treeSitter.js +0 -36
  48. package/src/indexer/parser.js +0 -443
  49. package/src/tasks/store.js +0 -349
  50. package/src/tests/coverage.js +0 -173
  51. package/src/tests/index.js +0 -171
  52. package/src/tests/store.js +0 -213
  53. package/src/tools/agent-task.js +0 -145
  54. package/src/tools/code-mode.js +0 -304
  55. package/src/tools/decompose.js +0 -91
  56. package/src/tools/edits.js +0 -94
  57. package/src/tools/execution.js +0 -171
  58. package/src/tools/git.js +0 -1346
  59. package/src/tools/index.js +0 -306
  60. package/src/tools/indexer.js +0 -360
  61. package/src/tools/lazy-loader.js +0 -366
  62. package/src/tools/mcp-remote.js +0 -88
  63. package/src/tools/mcp.js +0 -116
  64. package/src/tools/process.js +0 -167
  65. package/src/tools/smart-selection.js +0 -180
  66. package/src/tools/stubs.js +0 -55
  67. package/src/tools/tasks.js +0 -260
  68. package/src/tools/tests.js +0 -132
  69. package/src/tools/tinyfish.js +0 -358
  70. package/src/tools/truncate.js +0 -106
  71. package/src/tools/web-client.js +0 -71
  72. package/src/tools/web.js +0 -415
  73. 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
- };