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.
- package/CITATIONS.bib +6 -0
- package/DEPLOYMENT.md +1001 -0
- package/README.md +215 -71
- package/docs/index.md +55 -2
- package/monitor-agents.sh +31 -0
- package/package.json +7 -3
- package/src/agents/context-manager.js +220 -0
- package/src/agents/definitions/loader.js +563 -0
- package/src/agents/executor.js +412 -0
- package/src/agents/index.js +157 -0
- package/src/agents/parallel-coordinator.js +68 -0
- package/src/agents/reflector.js +321 -0
- package/src/agents/skillbook.js +331 -0
- package/src/agents/store.js +244 -0
- package/src/api/router.js +55 -0
- package/src/clients/databricks.js +214 -17
- package/src/clients/routing.js +15 -7
- package/src/clients/standard-tools.js +341 -0
- package/src/config/index.js +41 -5
- package/src/orchestrator/index.js +254 -37
- package/src/server.js +2 -0
- package/src/tools/agent-task.js +96 -0
- package/test/azure-openai-config.test.js +203 -0
- package/test/azure-openai-error-resilience.test.js +238 -0
- package/test/azure-openai-format-conversion.test.js +354 -0
- package/test/azure-openai-integration.test.js +281 -0
- package/test/azure-openai-routing.test.js +148 -0
- package/test/azure-openai-streaming.test.js +171 -0
- package/test/format-conversion.test.js +578 -0
- package/test/hybrid-routing-integration.test.js +18 -11
- package/test/openrouter-error-resilience.test.js +418 -0
- package/test/passthrough-mode.test.js +385 -0
- package/test/routing.test.js +9 -3
- package/test/web-tools.test.js +3 -0
- package/test-agents-simple.js +43 -0
- package/test-cli-connection.sh +33 -0
- package/test-learning-unit.js +126 -0
- package/test-learning.js +112 -0
- package/test-parallel-agents.sh +124 -0
- package/test-parallel-direct.js +155 -0
- package/test-subagents.sh +117 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const logger = require("../logger");
|
|
4
|
+
|
|
5
|
+
class ContextManager {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.transcriptsDir = path.join(process.cwd(), "data", "agent-transcripts");
|
|
8
|
+
this.ensureTranscriptsDir();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
ensureTranscriptsDir() {
|
|
12
|
+
if (!fs.existsSync(this.transcriptsDir)) {
|
|
13
|
+
fs.mkdirSync(this.transcriptsDir, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Create fresh context for subagent
|
|
19
|
+
* @param {Object} agentDef - Agent definition
|
|
20
|
+
* @param {string} taskPrompt - Task from main agent
|
|
21
|
+
* @param {Object} mainContext - Minimal context from main agent
|
|
22
|
+
* @returns {Object} - Fresh context for subagent
|
|
23
|
+
*/
|
|
24
|
+
createSubagentContext(agentDef, taskPrompt, mainContext = {}) {
|
|
25
|
+
const agentId = this.generateAgentId();
|
|
26
|
+
const transcriptPath = path.join(this.transcriptsDir, `agent-${agentId}.jsonl`);
|
|
27
|
+
|
|
28
|
+
// Initialize transcript file
|
|
29
|
+
fs.writeFileSync(transcriptPath, "");
|
|
30
|
+
|
|
31
|
+
// Build minimal context (NOT full main agent history)
|
|
32
|
+
const messages = [];
|
|
33
|
+
|
|
34
|
+
// System prompt from agent definition
|
|
35
|
+
messages.push({
|
|
36
|
+
role: "system",
|
|
37
|
+
content: agentDef.systemPrompt
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
// Optional: Add minimal context from main agent
|
|
41
|
+
if (mainContext.relevant_context) {
|
|
42
|
+
messages.push({
|
|
43
|
+
role: "system",
|
|
44
|
+
content: `Context from main agent:\n${mainContext.relevant_context}`
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Task prompt
|
|
49
|
+
messages.push({
|
|
50
|
+
role: "user",
|
|
51
|
+
content: taskPrompt
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const context = {
|
|
55
|
+
agentId,
|
|
56
|
+
agentName: agentDef.name,
|
|
57
|
+
transcriptPath,
|
|
58
|
+
messages,
|
|
59
|
+
steps: 0,
|
|
60
|
+
maxSteps: agentDef.maxSteps,
|
|
61
|
+
model: agentDef.model,
|
|
62
|
+
allowedTools: agentDef.allowedTools,
|
|
63
|
+
startTime: Date.now(),
|
|
64
|
+
|
|
65
|
+
// Token tracking
|
|
66
|
+
inputTokens: 0,
|
|
67
|
+
outputTokens: 0,
|
|
68
|
+
|
|
69
|
+
// State
|
|
70
|
+
terminated: false,
|
|
71
|
+
result: null
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
this.writeTranscriptEntry(transcriptPath, {
|
|
75
|
+
type: "agent_start",
|
|
76
|
+
agentId,
|
|
77
|
+
agentName: agentDef.name,
|
|
78
|
+
taskPrompt,
|
|
79
|
+
timestamp: Date.now()
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
logger.info({ agentId, agentName: agentDef.name }, "Created fresh subagent context");
|
|
83
|
+
|
|
84
|
+
return context;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Add message to subagent context
|
|
89
|
+
*/
|
|
90
|
+
addMessage(context, message) {
|
|
91
|
+
context.messages.push(message);
|
|
92
|
+
|
|
93
|
+
this.writeTranscriptEntry(context.transcriptPath, {
|
|
94
|
+
type: "message",
|
|
95
|
+
agentId: context.agentId,
|
|
96
|
+
message,
|
|
97
|
+
timestamp: Date.now()
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Record tool execution in transcript
|
|
103
|
+
*/
|
|
104
|
+
recordToolCall(context, toolName, input, output, error = null) {
|
|
105
|
+
this.writeTranscriptEntry(context.transcriptPath, {
|
|
106
|
+
type: "tool_call",
|
|
107
|
+
agentId: context.agentId,
|
|
108
|
+
step: context.steps,
|
|
109
|
+
toolName,
|
|
110
|
+
input,
|
|
111
|
+
output: error ? null : output,
|
|
112
|
+
error: error ? error.message : null,
|
|
113
|
+
timestamp: Date.now()
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Complete subagent execution
|
|
119
|
+
*/
|
|
120
|
+
completeExecution(context, result) {
|
|
121
|
+
context.terminated = true;
|
|
122
|
+
context.result = result;
|
|
123
|
+
|
|
124
|
+
this.writeTranscriptEntry(context.transcriptPath, {
|
|
125
|
+
type: "agent_complete",
|
|
126
|
+
agentId: context.agentId,
|
|
127
|
+
result,
|
|
128
|
+
stats: {
|
|
129
|
+
steps: context.steps,
|
|
130
|
+
durationMs: Date.now() - context.startTime,
|
|
131
|
+
inputTokens: context.inputTokens,
|
|
132
|
+
outputTokens: context.outputTokens
|
|
133
|
+
},
|
|
134
|
+
timestamp: Date.now()
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
logger.info({
|
|
138
|
+
agentId: context.agentId,
|
|
139
|
+
steps: context.steps,
|
|
140
|
+
durationMs: Date.now() - context.startTime
|
|
141
|
+
}, "Subagent execution completed");
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Fail subagent execution
|
|
146
|
+
*/
|
|
147
|
+
failExecution(context, error) {
|
|
148
|
+
context.terminated = true;
|
|
149
|
+
|
|
150
|
+
this.writeTranscriptEntry(context.transcriptPath, {
|
|
151
|
+
type: "agent_failed",
|
|
152
|
+
agentId: context.agentId,
|
|
153
|
+
error: error.message,
|
|
154
|
+
stats: {
|
|
155
|
+
steps: context.steps,
|
|
156
|
+
durationMs: Date.now() - context.startTime
|
|
157
|
+
},
|
|
158
|
+
timestamp: Date.now()
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
logger.error({
|
|
162
|
+
agentId: context.agentId,
|
|
163
|
+
error: error.message
|
|
164
|
+
}, "Subagent execution failed");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Write entry to transcript file (JSONL format)
|
|
169
|
+
*/
|
|
170
|
+
writeTranscriptEntry(transcriptPath, entry) {
|
|
171
|
+
try {
|
|
172
|
+
fs.appendFileSync(transcriptPath, JSON.stringify(entry) + "\n");
|
|
173
|
+
} catch (error) {
|
|
174
|
+
logger.warn({ error: error.message }, "Failed to write transcript entry");
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Read transcript for debugging
|
|
180
|
+
*/
|
|
181
|
+
readTranscript(agentId) {
|
|
182
|
+
const transcriptPath = path.join(this.transcriptsDir, `agent-${agentId}.jsonl`);
|
|
183
|
+
|
|
184
|
+
if (!fs.existsSync(transcriptPath)) {
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const lines = fs.readFileSync(transcriptPath, "utf8").split("\n").filter(l => l.trim());
|
|
189
|
+
return lines.map(line => JSON.parse(line));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Generate unique agent ID
|
|
194
|
+
*/
|
|
195
|
+
generateAgentId() {
|
|
196
|
+
return `${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Clean old transcripts (older than 7 days)
|
|
201
|
+
*/
|
|
202
|
+
cleanOldTranscripts() {
|
|
203
|
+
const maxAge = 7 * 24 * 60 * 60 * 1000; // 7 days
|
|
204
|
+
const now = Date.now();
|
|
205
|
+
|
|
206
|
+
const files = fs.readdirSync(this.transcriptsDir);
|
|
207
|
+
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
const filePath = path.join(this.transcriptsDir, file);
|
|
210
|
+
const stats = fs.statSync(filePath);
|
|
211
|
+
|
|
212
|
+
if (now - stats.mtimeMs > maxAge) {
|
|
213
|
+
fs.unlinkSync(filePath);
|
|
214
|
+
logger.debug({ file }, "Cleaned old transcript");
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
module.exports = ContextManager;
|