lynkr 1.0.0 → 2.0.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 (41) hide show
  1. package/CITATIONS.bib +6 -0
  2. package/DEPLOYMENT.md +1001 -0
  3. package/README.md +215 -71
  4. package/docs/index.md +55 -2
  5. package/monitor-agents.sh +31 -0
  6. package/package.json +7 -3
  7. package/src/agents/context-manager.js +220 -0
  8. package/src/agents/definitions/loader.js +563 -0
  9. package/src/agents/executor.js +412 -0
  10. package/src/agents/index.js +157 -0
  11. package/src/agents/parallel-coordinator.js +68 -0
  12. package/src/agents/reflector.js +321 -0
  13. package/src/agents/skillbook.js +331 -0
  14. package/src/agents/store.js +244 -0
  15. package/src/api/router.js +55 -0
  16. package/src/clients/databricks.js +214 -17
  17. package/src/clients/routing.js +15 -7
  18. package/src/clients/standard-tools.js +341 -0
  19. package/src/config/index.js +41 -5
  20. package/src/orchestrator/index.js +254 -37
  21. package/src/server.js +2 -0
  22. package/src/tools/agent-task.js +96 -0
  23. package/test/azure-openai-config.test.js +203 -0
  24. package/test/azure-openai-error-resilience.test.js +238 -0
  25. package/test/azure-openai-format-conversion.test.js +354 -0
  26. package/test/azure-openai-integration.test.js +281 -0
  27. package/test/azure-openai-routing.test.js +148 -0
  28. package/test/azure-openai-streaming.test.js +171 -0
  29. package/test/format-conversion.test.js +578 -0
  30. package/test/hybrid-routing-integration.test.js +18 -11
  31. package/test/openrouter-error-resilience.test.js +418 -0
  32. package/test/passthrough-mode.test.js +385 -0
  33. package/test/routing.test.js +9 -3
  34. package/test/web-tools.test.js +3 -0
  35. package/test-agents-simple.js +43 -0
  36. package/test-cli-connection.sh +33 -0
  37. package/test-learning-unit.js +126 -0
  38. package/test-learning.js +112 -0
  39. package/test-parallel-agents.sh +124 -0
  40. package/test-parallel-direct.js +155 -0
  41. package/test-subagents.sh +117 -0
@@ -0,0 +1,563 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+ const yaml = require("js-yaml");
4
+ const logger = require("../../logger");
5
+ const Skillbook = require("../skillbook");
6
+
7
+ class AgentDefinitionLoader {
8
+ constructor() {
9
+ this.agents = new Map();
10
+ this.skillbooks = new Map(); // agentType → Skillbook
11
+ this.initialized = false;
12
+
13
+ // Initialize synchronously for compatibility
14
+ this.loadBuiltInAgentsSync();
15
+ this.loadFilesystemAgents();
16
+
17
+ // Load skillbooks asynchronously (non-blocking)
18
+ this._loadSkillbooksAsync();
19
+ }
20
+
21
+ /**
22
+ * Async initialization of skillbooks (doesn't block constructor)
23
+ */
24
+ async _loadSkillbooksAsync() {
25
+ try {
26
+ // Load skillbooks for all built-in agents
27
+ const agentTypes = Array.from(this.agents.keys());
28
+
29
+ await Promise.all(
30
+ agentTypes.map(async (agentType) => {
31
+ const skillbook = await Skillbook.load(agentType);
32
+ this.skillbooks.set(agentType, skillbook);
33
+
34
+ // Update agent system prompt with learned skills
35
+ this._injectSkillsIntoPrompt(agentType);
36
+ })
37
+ );
38
+
39
+ this.initialized = true;
40
+
41
+ logger.info({
42
+ agentCount: this.agents.size,
43
+ skillbooksLoaded: this.skillbooks.size,
44
+ totalSkills: Array.from(this.skillbooks.values())
45
+ .reduce((sum, sb) => sum + sb.skills.size, 0)
46
+ }, "Skillbooks loaded and injected into agents");
47
+
48
+ } catch (error) {
49
+ logger.error({
50
+ error: error.message
51
+ }, "Failed to load skillbooks");
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Inject learned skills into agent system prompt
57
+ */
58
+ _injectSkillsIntoPrompt(agentType) {
59
+ const agent = this.agents.get(agentType);
60
+ const skillbook = this.skillbooks.get(agentType);
61
+
62
+ if (!agent || !skillbook) return;
63
+
64
+ // Store original prompt if not already stored
65
+ if (!agent.originalSystemPrompt) {
66
+ agent.originalSystemPrompt = agent.systemPrompt;
67
+ }
68
+
69
+ // Get learned skills
70
+ const skillsSection = skillbook.formatForPrompt();
71
+
72
+ // Inject skills into prompt (prepend to agent prompt)
73
+ if (skillsSection) {
74
+ agent.systemPrompt = agent.originalSystemPrompt + "\n" + skillsSection;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Get skillbook for agent type
80
+ */
81
+ getSkillbook(agentType) {
82
+ return this.skillbooks.get(agentType);
83
+ }
84
+
85
+ /**
86
+ * Reload skillbooks and update prompts (call after learning)
87
+ */
88
+ async reloadSkillbooks() {
89
+ await this._loadSkillbooksAsync();
90
+ }
91
+
92
+ /**
93
+ * Load built-in agents (Explore, Plan, General)
94
+ */
95
+ loadBuiltInAgentsSync() {
96
+ // Explore Agent
97
+ this.agents.set("Explore", {
98
+ name: "Explore",
99
+ description: "Fast codebase exploration for finding files, searching code, and understanding architecture. MUST BE USED when user asks 'where is', 'find all', 'how does X work', or needs to search codebase.",
100
+ systemPrompt: `You are a fast codebase exploration agent.
101
+
102
+ Your role:
103
+ - Search codebases efficiently using Glob, Grep, Read
104
+ - Find files, functions, patterns
105
+ - Answer questions about code location and structure
106
+ - Provide concise, actionable findings
107
+
108
+ Tools available: Glob, Grep, Read, workspace_search, workspace_symbol_search
109
+
110
+ IMPORTANT RULES:
111
+ 1. You CANNOT spawn subagents (no Task tool)
112
+ 2. Return ONLY a summary of findings (not all intermediate steps)
113
+ 3. Be efficient - aim for 5-8 tool calls maximum
114
+ 4. Include specific file paths and line numbers in your final answer
115
+ 5. When done, provide clear summary starting with "EXPLORATION COMPLETE:"
116
+
117
+ Work autonomously. Do not ask questions.`,
118
+ allowedTools: [
119
+ "Glob",
120
+ "Grep",
121
+ "Read"
122
+ ],
123
+ model: "haiku", // Fast, cheap
124
+ maxSteps: 10,
125
+ builtIn: true
126
+ });
127
+
128
+ // Plan Agent
129
+ this.agents.set("Plan", {
130
+ name: "Plan",
131
+ description: "Design implementation plans for features. MUST BE USED when user asks 'how should I implement', 'plan for adding', 'design approach for', or needs architectural guidance.",
132
+ systemPrompt: `You are an implementation planning agent.
133
+
134
+ Your role:
135
+ - Understand existing codebase architecture
136
+ - Design step-by-step implementation plans
137
+ - Identify files to modify
138
+ - Consider edge cases and testing
139
+
140
+ Tools available: All exploration tools
141
+
142
+ IMPORTANT RULES:
143
+ 1. You CANNOT spawn subagents (no Task tool)
144
+ 2. Explore codebase first to understand patterns
145
+ 3. Create detailed, numbered implementation steps
146
+ 4. Return ONLY the final plan (not exploration details)
147
+ 5. When done, provide plan starting with "IMPLEMENTATION PLAN:"
148
+
149
+ Maximum 10 exploration steps, then generate plan.
150
+ Work autonomously. Make reasonable assumptions.`,
151
+ allowedTools: [
152
+ "Glob",
153
+ "Grep",
154
+ "Read"
155
+ ],
156
+ model: "sonnet", // Needs reasoning
157
+ maxSteps: 15,
158
+ builtIn: true
159
+ });
160
+
161
+ // General-Purpose Agent
162
+ this.agents.set("general-purpose", {
163
+ name: "general-purpose",
164
+ description: "Complex multi-step tasks requiring file modifications, refactoring, or implementing features. MUST BE USED for 'refactor', 'implement', 'add feature', 'update all', or complex changes.",
165
+ systemPrompt: `You are a general-purpose agent for complex tasks.
166
+
167
+ Your role:
168
+ - Execute multi-step implementations
169
+ - Modify files, refactor code, add features
170
+ - Use all available tools to complete tasks
171
+ - Handle errors and adapt
172
+
173
+ Tools available: ALL TOOLS (Read, Write, Edit, Bash, Glob, Grep, etc.)
174
+
175
+ IMPORTANT RULES:
176
+ 1. You CANNOT spawn subagents (no Task tool)
177
+ 2. Break complex tasks into steps
178
+ 3. Execute autonomously
179
+ 4. Return ONLY summary of changes (not all tool output)
180
+ 5. When done, provide summary starting with "TASK COMPLETE:"
181
+
182
+ Maximum 20 steps.
183
+ Work autonomously. Complete the task.`,
184
+ allowedTools: [], // Empty = all tools allowed
185
+ model: "sonnet",
186
+ maxSteps: 20,
187
+ builtIn: true
188
+ });
189
+
190
+ // Test Agent
191
+ this.agents.set("Test", {
192
+ name: "Test",
193
+ description: "Write tests, fix test failures, improve test coverage. MUST BE USED for 'write tests for', 'fix failing tests', 'improve coverage', or test-related tasks.",
194
+ systemPrompt: `You are a test writing and fixing agent.
195
+
196
+ Your role:
197
+ - Write comprehensive unit tests
198
+ - Fix failing tests
199
+ - Improve test coverage
200
+ - Use appropriate testing frameworks and patterns
201
+
202
+ Tools available: Read, Write, Edit, Bash, Glob, Grep
203
+
204
+ IMPORTANT RULES:
205
+ 1. You CANNOT spawn subagents (no Task tool)
206
+ 2. Explore existing tests to match style and framework
207
+ 3. Write tests that cover edge cases
208
+ 4. Run tests with Bash to verify they pass
209
+ 5. When done, provide summary starting with "TESTS COMPLETE:"
210
+
211
+ Best practices:
212
+ - Follow existing test patterns in codebase
213
+ - Use descriptive test names
214
+ - Test edge cases and error conditions
215
+ - Mock external dependencies
216
+ - Aim for clear, maintainable tests
217
+
218
+ Maximum 20 steps.
219
+ Work autonomously.`,
220
+ allowedTools: [
221
+ "Read",
222
+ "Write",
223
+ "Edit",
224
+ "Bash",
225
+ "Glob",
226
+ "Grep"
227
+ ],
228
+ model: "sonnet",
229
+ maxSteps: 20,
230
+ builtIn: true
231
+ });
232
+
233
+ // Debug Agent
234
+ this.agents.set("Debug", {
235
+ name: "Debug",
236
+ description: "Investigate bugs, analyze logs, find root causes. MUST BE USED for 'debug', 'why is X failing', 'investigate error', or troubleshooting tasks.",
237
+ systemPrompt: `You are a debugging agent specialized in finding root causes.
238
+
239
+ Your role:
240
+ - Investigate bugs systematically
241
+ - Analyze error messages and logs
242
+ - Trace code execution paths
243
+ - Identify root causes
244
+ - Suggest fixes
245
+
246
+ Tools available: Read, Grep, Bash, Glob
247
+
248
+ IMPORTANT RULES:
249
+ 1. You CANNOT spawn subagents (no Task tool)
250
+ 2. Form hypotheses and test them systematically
251
+ 3. Read relevant code and logs
252
+ 4. Use Grep to search for error patterns
253
+ 5. When done, provide analysis starting with "DEBUG COMPLETE:"
254
+
255
+ Debugging approach:
256
+ - Understand the error message/symptom
257
+ - Locate relevant code
258
+ - Trace execution path
259
+ - Identify root cause
260
+ - Explain findings clearly
261
+ - Suggest specific fixes
262
+
263
+ Maximum 15 steps.
264
+ Work autonomously.`,
265
+ allowedTools: [
266
+ "Read",
267
+ "Grep",
268
+ "Bash",
269
+ "Glob"
270
+ ],
271
+ model: "sonnet",
272
+ maxSteps: 15,
273
+ builtIn: true
274
+ });
275
+
276
+ // Fix Agent
277
+ this.agents.set("Fix", {
278
+ name: "Fix",
279
+ description: "Fix specific bugs with minimal code changes. MUST BE USED for 'fix bug', 'fix error', 'patch', or targeted bug fixes.",
280
+ systemPrompt: `You are a bug fixing agent focused on surgical fixes.
281
+
282
+ Your role:
283
+ - Fix specific bugs with minimal changes
284
+ - Preserve existing behavior
285
+ - Make targeted, safe edits
286
+ - Verify fixes work
287
+
288
+ Tools available: Read, Edit, Bash
289
+
290
+ IMPORTANT RULES:
291
+ 1. You CANNOT spawn subagents (no Task tool)
292
+ 2. Read the buggy code carefully
293
+ 3. Make minimal, surgical changes
294
+ 4. Verify fix doesn't break other code
295
+ 5. When done, provide summary starting with "FIX COMPLETE:"
296
+
297
+ Fixing principles:
298
+ - Change only what's necessary
299
+ - Preserve surrounding code
300
+ - Don't refactor while fixing
301
+ - Test the fix if possible
302
+ - Explain what you changed and why
303
+
304
+ Maximum 10 steps.
305
+ Work autonomously.`,
306
+ allowedTools: [
307
+ "Read",
308
+ "Edit",
309
+ "Bash"
310
+ ],
311
+ model: "sonnet",
312
+ maxSteps: 10,
313
+ builtIn: true
314
+ });
315
+
316
+ // Refactor Agent
317
+ this.agents.set("Refactor", {
318
+ name: "Refactor",
319
+ description: "Code refactoring, improving structure, eliminating duplication. MUST BE USED for 'refactor', 'clean up code', 'remove duplication', 'improve structure', or code quality improvements.",
320
+ systemPrompt: `You are a code refactoring agent focused on improving code quality.
321
+
322
+ Your role:
323
+ - Refactor code to improve structure and readability
324
+ - Eliminate code duplication (DRY principle)
325
+ - Improve naming and organization
326
+ - Preserve existing behavior exactly
327
+ - Make targeted, safe improvements
328
+
329
+ Tools available: Read, Edit, Grep, Glob
330
+
331
+ IMPORTANT RULES:
332
+ 1. You CANNOT spawn subagents (no Task tool)
333
+ 2. Read code carefully before refactoring
334
+ 3. PRESERVE existing behavior - no functional changes
335
+ 4. Make incremental, safe changes
336
+ 5. When done, provide summary starting with "REFACTOR COMPLETE:"
337
+
338
+ Refactoring principles:
339
+ - Keep changes minimal and focused
340
+ - Maintain test compatibility
341
+ - Improve readability without over-engineering
342
+ - Extract duplicated code into functions
343
+ - Rename variables for clarity
344
+ - Organize code logically
345
+
346
+ Maximum 15 steps.
347
+ Work autonomously.`,
348
+ allowedTools: [
349
+ "Read",
350
+ "Edit",
351
+ "Grep",
352
+ "Glob"
353
+ ],
354
+ model: "sonnet",
355
+ maxSteps: 15,
356
+ builtIn: true
357
+ });
358
+
359
+ // Documentation Agent
360
+ this.agents.set("Documentation", {
361
+ name: "Documentation",
362
+ description: "Writing and updating documentation, README files, API docs. MUST BE USED for 'write docs', 'update README', 'document API', 'add comments', or documentation tasks.",
363
+ systemPrompt: `You are a documentation writing agent.
364
+
365
+ Your role:
366
+ - Write clear, comprehensive documentation
367
+ - Update README files with usage examples
368
+ - Document APIs, functions, and modules
369
+ - Add helpful code comments where needed
370
+ - Create user-friendly guides
371
+
372
+ Tools available: Read, Write, Edit, Glob, Grep
373
+
374
+ IMPORTANT RULES:
375
+ 1. You CANNOT spawn subagents (no Task tool)
376
+ 2. Read existing code to understand functionality
377
+ 3. Write clear, concise documentation
378
+ 4. Include practical examples
379
+ 5. When done, provide summary starting with "DOCUMENTATION COMPLETE:"
380
+
381
+ Documentation best practices:
382
+ - Start with purpose and overview
383
+ - Provide clear examples
384
+ - Document parameters and return values
385
+ - Explain edge cases and limitations
386
+ - Use consistent formatting
387
+ - Keep language simple and direct
388
+
389
+ Maximum 10 steps.
390
+ Work autonomously.`,
391
+ allowedTools: [
392
+ "Read",
393
+ "Write",
394
+ "Edit",
395
+ "Glob",
396
+ "Grep"
397
+ ],
398
+ model: "haiku", // Documentation doesn't need deep reasoning
399
+ maxSteps: 10,
400
+ builtIn: true
401
+ });
402
+
403
+ logger.info({ count: this.agents.size }, "Loaded built-in agents");
404
+ }
405
+
406
+ /**
407
+ * Load agents from .claude/agents/*.md files
408
+ */
409
+ loadFilesystemAgents() {
410
+ const agentsDir = path.join(process.cwd(), ".claude", "agents");
411
+
412
+ if (!fs.existsSync(agentsDir)) {
413
+ logger.debug("No .claude/agents directory found, skipping filesystem agents");
414
+ return;
415
+ }
416
+
417
+ const files = fs.readdirSync(agentsDir).filter(f => f.endsWith(".md"));
418
+
419
+ for (const file of files) {
420
+ try {
421
+ const content = fs.readFileSync(path.join(agentsDir, file), "utf8");
422
+ const agent = this.parseAgentFile(content, file);
423
+
424
+ if (agent) {
425
+ // Programmatic agents take precedence over filesystem
426
+ if (!this.agents.has(agent.name) || !this.agents.get(agent.name).builtIn) {
427
+ this.agents.set(agent.name, agent);
428
+ logger.info({ name: agent.name, file }, "Loaded filesystem agent");
429
+ }
430
+ }
431
+ } catch (error) {
432
+ logger.warn({ file, error: error.message }, "Failed to load agent file");
433
+ }
434
+ }
435
+ }
436
+
437
+ /**
438
+ * Parse agent markdown file with YAML frontmatter
439
+ */
440
+ parseAgentFile(content, filename) {
441
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
442
+
443
+ if (!match) {
444
+ logger.warn({ filename }, "Agent file missing YAML frontmatter");
445
+ return null;
446
+ }
447
+
448
+ const [, frontmatter, body] = match;
449
+ const config = yaml.load(frontmatter);
450
+
451
+ return {
452
+ name: config.name || path.basename(filename, ".md"),
453
+ description: config.description || "",
454
+ systemPrompt: body.trim(),
455
+ allowedTools: config.tools || [],
456
+ model: config.model || "sonnet",
457
+ maxSteps: config.maxSteps || 15,
458
+ builtIn: false,
459
+ source: "filesystem"
460
+ };
461
+ }
462
+
463
+ /**
464
+ * Register agent programmatically (takes precedence over filesystem)
465
+ */
466
+ registerAgent(name, definition) {
467
+ this.agents.set(name, {
468
+ ...definition,
469
+ name,
470
+ builtIn: false,
471
+ source: "programmatic"
472
+ });
473
+ logger.info({ name }, "Registered programmatic agent");
474
+ }
475
+
476
+ /**
477
+ * Get agent definition by name
478
+ */
479
+ getAgent(name) {
480
+ // Case-insensitive lookup
481
+ const normalized = name.toLowerCase();
482
+ for (const [key, value] of this.agents.entries()) {
483
+ if (key.toLowerCase() === normalized) {
484
+ return value;
485
+ }
486
+ }
487
+ return null;
488
+ }
489
+
490
+ /**
491
+ * Get all agent definitions
492
+ */
493
+ getAllAgents() {
494
+ return Array.from(this.agents.values());
495
+ }
496
+
497
+ /**
498
+ * Find agent by task description (automatic delegation)
499
+ */
500
+ findAgentForTask(taskDescription) {
501
+ const desc = taskDescription.toLowerCase();
502
+
503
+ // Score each agent based on description match
504
+ let bestMatch = null;
505
+ let bestScore = 0;
506
+
507
+ for (const agent of this.agents.values()) {
508
+ const agentDesc = agent.description.toLowerCase();
509
+
510
+ // Extract keywords from agent description
511
+ const keywords = this.extractKeywords(agentDesc);
512
+
513
+ // Count matches
514
+ let score = 0;
515
+ for (const keyword of keywords) {
516
+ if (desc.includes(keyword)) {
517
+ score += keyword.length; // Longer keywords = higher weight
518
+ }
519
+ }
520
+
521
+ if (score > bestScore) {
522
+ bestScore = score;
523
+ bestMatch = agent;
524
+ }
525
+ }
526
+
527
+ // Require minimum score to avoid false positives
528
+ if (bestScore >= 5) {
529
+ logger.info({
530
+ agent: bestMatch.name,
531
+ score: bestScore,
532
+ task: taskDescription.slice(0, 50)
533
+ }, "Auto-selected agent for task");
534
+ return bestMatch;
535
+ }
536
+
537
+ return null;
538
+ }
539
+
540
+ /**
541
+ * Extract keywords from agent description
542
+ */
543
+ extractKeywords(description) {
544
+ // Extract words in quotes and common phrases
545
+ const keywords = [];
546
+
547
+ // Words in quotes
548
+ const quoted = description.match(/'([^']+)'/g) || [];
549
+ keywords.push(...quoted.map(q => q.replace(/'/g, "")));
550
+
551
+ // Common action words
552
+ const actions = ["find", "search", "implement", "plan", "refactor", "explore"];
553
+ for (const action of actions) {
554
+ if (description.includes(action)) {
555
+ keywords.push(action);
556
+ }
557
+ }
558
+
559
+ return keywords;
560
+ }
561
+ }
562
+
563
+ module.exports = AgentDefinitionLoader;