klaus-agent 0.1.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/README.md +685 -0
- package/dist/approval/approval.d.ts +18 -0
- package/dist/approval/approval.js +93 -0
- package/dist/approval/approval.js.map +1 -0
- package/dist/approval/types.d.ts +21 -0
- package/dist/approval/types.js +3 -0
- package/dist/approval/types.js.map +1 -0
- package/dist/background/task-manager.d.ts +14 -0
- package/dist/background/task-manager.js +89 -0
- package/dist/background/task-manager.js.map +1 -0
- package/dist/background/tools.d.ts +4 -0
- package/dist/background/tools.js +74 -0
- package/dist/background/tools.js.map +1 -0
- package/dist/background/types.d.ts +31 -0
- package/dist/background/types.js +3 -0
- package/dist/background/types.js.map +1 -0
- package/dist/checkpoint/checkpoint-manager.d.ts +19 -0
- package/dist/checkpoint/checkpoint-manager.js +49 -0
- package/dist/checkpoint/checkpoint-manager.js.map +1 -0
- package/dist/checkpoint/dmail.d.ts +10 -0
- package/dist/checkpoint/dmail.js +26 -0
- package/dist/checkpoint/dmail.js.map +1 -0
- package/dist/checkpoint/types.d.ts +12 -0
- package/dist/checkpoint/types.js +3 -0
- package/dist/checkpoint/types.js.map +1 -0
- package/dist/compaction/compaction.d.ts +6 -0
- package/dist/compaction/compaction.js +104 -0
- package/dist/compaction/compaction.js.map +1 -0
- package/dist/compaction/summarizer.d.ts +10 -0
- package/dist/compaction/summarizer.js +75 -0
- package/dist/compaction/summarizer.js.map +1 -0
- package/dist/compaction/types.d.ts +23 -0
- package/dist/compaction/types.js +3 -0
- package/dist/compaction/types.js.map +1 -0
- package/dist/core/agent-loop.d.ts +37 -0
- package/dist/core/agent-loop.js +337 -0
- package/dist/core/agent-loop.js.map +1 -0
- package/dist/core/agent.d.ts +97 -0
- package/dist/core/agent.js +335 -0
- package/dist/core/agent.js.map +1 -0
- package/dist/extensions/runner.d.ts +19 -0
- package/dist/extensions/runner.js +88 -0
- package/dist/extensions/runner.js.map +1 -0
- package/dist/extensions/types.d.ts +179 -0
- package/dist/extensions/types.js +3 -0
- package/dist/extensions/types.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/injection/history-normalizer.d.ts +2 -0
- package/dist/injection/history-normalizer.js +34 -0
- package/dist/injection/history-normalizer.js.map +1 -0
- package/dist/injection/injection-manager.d.ts +8 -0
- package/dist/injection/injection-manager.js +28 -0
- package/dist/injection/injection-manager.js.map +1 -0
- package/dist/injection/types.d.ts +8 -0
- package/dist/injection/types.js +3 -0
- package/dist/injection/types.js.map +1 -0
- package/dist/llm/provider.d.ts +16 -0
- package/dist/llm/provider.js +233 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/llm/types.d.ts +110 -0
- package/dist/llm/types.js +3 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/multi-agent/labor-market.d.ts +16 -0
- package/dist/multi-agent/labor-market.js +40 -0
- package/dist/multi-agent/labor-market.js.map +1 -0
- package/dist/multi-agent/task-executor.d.ts +12 -0
- package/dist/multi-agent/task-executor.js +38 -0
- package/dist/multi-agent/task-executor.js.map +1 -0
- package/dist/multi-agent/task-tool.d.ts +4 -0
- package/dist/multi-agent/task-tool.js +46 -0
- package/dist/multi-agent/task-tool.js.map +1 -0
- package/dist/multi-agent/types.d.ts +7 -0
- package/dist/multi-agent/types.js +3 -0
- package/dist/multi-agent/types.js.map +1 -0
- package/dist/session/session-context-builder.d.ts +2 -0
- package/dist/session/session-context-builder.js +52 -0
- package/dist/session/session-context-builder.js.map +1 -0
- package/dist/session/session-manager.d.ts +30 -0
- package/dist/session/session-manager.js +209 -0
- package/dist/session/session-manager.js.map +1 -0
- package/dist/session/types.d.ts +61 -0
- package/dist/session/types.js +3 -0
- package/dist/session/types.js.map +1 -0
- package/dist/skills/discovery.d.ts +2 -0
- package/dist/skills/discovery.js +29 -0
- package/dist/skills/discovery.js.map +1 -0
- package/dist/skills/loader.d.ts +3 -0
- package/dist/skills/loader.js +46 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/skill-tool.d.ts +3 -0
- package/dist/skills/skill-tool.js +38 -0
- package/dist/skills/skill-tool.js.map +1 -0
- package/dist/skills/types.d.ts +14 -0
- package/dist/skills/types.js +3 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/executor.d.ts +37 -0
- package/dist/tools/executor.js +131 -0
- package/dist/tools/executor.js.map +1 -0
- package/dist/tools/mcp-adapter.d.ts +57 -0
- package/dist/tools/mcp-adapter.js +113 -0
- package/dist/tools/mcp-adapter.js.map +1 -0
- package/dist/tools/types.d.ts +43 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/types.d.ts +96 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/id.d.ts +1 -0
- package/dist/utils/id.js +9 -0
- package/dist/utils/id.js.map +1 -0
- package/dist/utils/jsonl.d.ts +3 -0
- package/dist/utils/jsonl.js +17 -0
- package/dist/utils/jsonl.js.map +1 -0
- package/dist/wire/types.d.ts +10 -0
- package/dist/wire/types.js +3 -0
- package/dist/wire/types.js.map +1 -0
- package/dist/wire/wire.d.ts +20 -0
- package/dist/wire/wire.js +71 -0
- package/dist/wire/wire.js.map +1 -0
- package/package.json +26 -0
- package/src/approval/approval.ts +108 -0
- package/src/approval/types.ts +26 -0
- package/src/background/task-manager.ts +112 -0
- package/src/background/tools.ts +84 -0
- package/src/background/types.ts +29 -0
- package/src/checkpoint/checkpoint-manager.ts +60 -0
- package/src/checkpoint/dmail.ts +35 -0
- package/src/checkpoint/types.ts +16 -0
- package/src/compaction/compaction.ts +119 -0
- package/src/compaction/summarizer.ts +83 -0
- package/src/compaction/types.ts +29 -0
- package/src/core/agent-loop.ts +427 -0
- package/src/core/agent.ts +430 -0
- package/src/extensions/runner.ts +138 -0
- package/src/extensions/types.ts +177 -0
- package/src/index.ts +221 -0
- package/src/injection/history-normalizer.ts +44 -0
- package/src/injection/injection-manager.ts +34 -0
- package/src/injection/types.ts +12 -0
- package/src/llm/provider.ts +254 -0
- package/src/llm/types.ts +146 -0
- package/src/multi-agent/labor-market.ts +54 -0
- package/src/multi-agent/task-executor.ts +49 -0
- package/src/multi-agent/task-tool.ts +58 -0
- package/src/multi-agent/types.ts +10 -0
- package/src/session/session-context-builder.ts +65 -0
- package/src/session/session-manager.ts +258 -0
- package/src/session/types.ts +93 -0
- package/src/skills/discovery.ts +32 -0
- package/src/skills/loader.ts +54 -0
- package/src/skills/skill-tool.ts +50 -0
- package/src/skills/types.ts +18 -0
- package/src/tools/executor.ts +196 -0
- package/src/tools/mcp-adapter.ts +185 -0
- package/src/tools/types.ts +64 -0
- package/src/types.ts +96 -0
- package/src/utils/id.ts +8 -0
- package/src/utils/jsonl.ts +19 -0
- package/src/wire/types.ts +14 -0
- package/src/wire/wire.ts +79 -0
- package/tsconfig.json +19 -0
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
// Agent class — the runtime object with full module integration
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
AgentMessage,
|
|
5
|
+
AgentEvent,
|
|
6
|
+
AgentState,
|
|
7
|
+
AgentHooks,
|
|
8
|
+
ModelConfig,
|
|
9
|
+
ThinkingLevel,
|
|
10
|
+
UserMessage,
|
|
11
|
+
} from "../types.js";
|
|
12
|
+
import type { AgentTool } from "../tools/types.js";
|
|
13
|
+
import type { Approval } from "../approval/types.js";
|
|
14
|
+
import type { LLMProvider } from "../llm/types.js";
|
|
15
|
+
import type { SessionConfig } from "../session/types.js";
|
|
16
|
+
import type { CompactionConfig } from "../compaction/types.js";
|
|
17
|
+
import type { CheckpointConfig } from "../checkpoint/types.js";
|
|
18
|
+
import type { DynamicInjectionProvider } from "../injection/types.js";
|
|
19
|
+
import type { ExtensionFactory } from "../extensions/types.js";
|
|
20
|
+
import type { SubagentConfig } from "../multi-agent/types.js";
|
|
21
|
+
import type { SkillSource } from "../skills/types.js";
|
|
22
|
+
import type { MCPServerConfig, MCPClient } from "../tools/mcp-adapter.js";
|
|
23
|
+
import type { TaskFactory } from "../background/types.js";
|
|
24
|
+
import { SessionManager } from "../session/session-manager.js";
|
|
25
|
+
import { CheckpointManager } from "../checkpoint/checkpoint-manager.js";
|
|
26
|
+
import { InjectionManager } from "../injection/injection-manager.js";
|
|
27
|
+
import { ExtensionRunner } from "../extensions/runner.js";
|
|
28
|
+
import { LaborMarket } from "../multi-agent/labor-market.js";
|
|
29
|
+
import { TaskExecutor } from "../multi-agent/task-executor.js";
|
|
30
|
+
import { createTaskTool } from "../multi-agent/task-tool.js";
|
|
31
|
+
import { MCPAdapter } from "../tools/mcp-adapter.js";
|
|
32
|
+
import { discoverSkills } from "../skills/discovery.js";
|
|
33
|
+
import { createInvokeSkillTool } from "../skills/skill-tool.js";
|
|
34
|
+
import { LLMSummarizer, agentMessagesToCompactionInput } from "../compaction/summarizer.js";
|
|
35
|
+
import { Wire } from "../wire/wire.js";
|
|
36
|
+
import { BackgroundTaskManager } from "../background/task-manager.js";
|
|
37
|
+
import { createBackgroundTaskTools } from "../background/tools.js";
|
|
38
|
+
import { runAgentLoop } from "./agent-loop.js";
|
|
39
|
+
|
|
40
|
+
export interface AgentConfig {
|
|
41
|
+
model: ModelConfig;
|
|
42
|
+
systemPrompt: string | (() => string | Promise<string>);
|
|
43
|
+
tools: AgentTool[];
|
|
44
|
+
provider: LLMProvider;
|
|
45
|
+
approval: Approval;
|
|
46
|
+
name?: string;
|
|
47
|
+
toolExecution?: "sequential" | "parallel";
|
|
48
|
+
maxStepsPerTurn?: number;
|
|
49
|
+
thinkingLevel?: ThinkingLevel;
|
|
50
|
+
hooks?: AgentHooks;
|
|
51
|
+
|
|
52
|
+
// Advanced modules
|
|
53
|
+
session?: SessionConfig;
|
|
54
|
+
compaction?: CompactionConfig;
|
|
55
|
+
checkpoint?: CheckpointConfig;
|
|
56
|
+
injection?: DynamicInjectionProvider[];
|
|
57
|
+
extensions?: ExtensionFactory[];
|
|
58
|
+
subagents?: Record<string, SubagentConfig>;
|
|
59
|
+
skills?: SkillSource[];
|
|
60
|
+
mcp?: { servers: MCPServerConfig[]; clientFactory: (config: MCPServerConfig) => MCPClient };
|
|
61
|
+
wire?: { bufferSize?: number };
|
|
62
|
+
backgroundTasks?: { factories?: Record<string, TaskFactory> };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export class Agent {
|
|
66
|
+
private _state: AgentState;
|
|
67
|
+
private _provider: LLMProvider;
|
|
68
|
+
private _approval: Approval;
|
|
69
|
+
private _listeners = new Set<(event: AgentEvent) => void>();
|
|
70
|
+
private _steeringQueue: AgentMessage[] = [];
|
|
71
|
+
private _followUpQueue: AgentMessage[] = [];
|
|
72
|
+
private _abortController: AbortController | null = null;
|
|
73
|
+
private _config: AgentConfig;
|
|
74
|
+
private _name: string;
|
|
75
|
+
|
|
76
|
+
// Integrated modules
|
|
77
|
+
private _sessionManager: SessionManager | undefined;
|
|
78
|
+
private _checkpointManager: CheckpointManager | undefined;
|
|
79
|
+
private _injectionManager: InjectionManager | undefined;
|
|
80
|
+
private _extensionRunner: ExtensionRunner | undefined;
|
|
81
|
+
private _laborMarket: LaborMarket | undefined;
|
|
82
|
+
private _taskExecutor: TaskExecutor | undefined;
|
|
83
|
+
private _mcpAdapter: MCPAdapter | undefined;
|
|
84
|
+
private _wire: Wire;
|
|
85
|
+
private _backgroundTaskManager: BackgroundTaskManager | undefined;
|
|
86
|
+
private _initialized = false;
|
|
87
|
+
|
|
88
|
+
constructor(config: AgentConfig) {
|
|
89
|
+
this._config = config;
|
|
90
|
+
this._provider = config.provider;
|
|
91
|
+
this._approval = config.approval;
|
|
92
|
+
this._name = config.name ?? "agent";
|
|
93
|
+
this._wire = new Wire({ bufferSize: config.wire?.bufferSize ?? 0 });
|
|
94
|
+
|
|
95
|
+
this._state = {
|
|
96
|
+
systemPrompt: typeof config.systemPrompt === "string" ? config.systemPrompt : "",
|
|
97
|
+
model: config.model,
|
|
98
|
+
thinkingLevel: config.thinkingLevel ?? "off",
|
|
99
|
+
tools: config.tools,
|
|
100
|
+
messages: [],
|
|
101
|
+
isRunning: false,
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
// Session
|
|
105
|
+
if (config.session) {
|
|
106
|
+
this._sessionManager = new SessionManager(config.session);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Checkpoint (requires session)
|
|
110
|
+
if (config.checkpoint?.enabled !== false && this._sessionManager) {
|
|
111
|
+
this._checkpointManager = new CheckpointManager(this._sessionManager);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Injection
|
|
115
|
+
if (config.injection?.length) {
|
|
116
|
+
this._injectionManager = new InjectionManager(config.injection);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// MCP
|
|
120
|
+
if (config.mcp?.servers.length) {
|
|
121
|
+
this._mcpAdapter = new MCPAdapter(config.mcp.servers, config.mcp.clientFactory);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Multi-agent
|
|
125
|
+
if (config.subagents && Object.keys(config.subagents).length > 0) {
|
|
126
|
+
this._laborMarket = new LaborMarket();
|
|
127
|
+
this._taskExecutor = new TaskExecutor(this._laborMarket, (name, event) => {
|
|
128
|
+
this._emit({ ...event, type: event.type } as AgentEvent);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Background tasks
|
|
133
|
+
if (config.backgroundTasks) {
|
|
134
|
+
this._backgroundTaskManager = new BackgroundTaskManager((taskEvent) => {
|
|
135
|
+
const { task } = taskEvent;
|
|
136
|
+
if (taskEvent.type === "task_started") {
|
|
137
|
+
this._emit({ type: "task_started", taskId: task.id, taskName: task.name });
|
|
138
|
+
this._extensionRunner?.emitSimple("task_started", { taskId: task.id, taskName: task.name });
|
|
139
|
+
} else if (taskEvent.type === "task_completed") {
|
|
140
|
+
this._emit({ type: "task_completed", taskId: task.id, taskName: task.name });
|
|
141
|
+
this._extensionRunner?.emitSimple("task_completed", { taskId: task.id, taskName: task.name, result: taskEvent.result });
|
|
142
|
+
} else if (taskEvent.type === "task_failed") {
|
|
143
|
+
this._emit({ type: "task_failed", taskId: task.id, taskName: task.name, error: taskEvent.error });
|
|
144
|
+
this._extensionRunner?.emitSimple("task_failed", { taskId: task.id, taskName: task.name, error: taskEvent.error });
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// --- Public API ---
|
|
151
|
+
|
|
152
|
+
async prompt(input: string | AgentMessage | AgentMessage[]): Promise<AgentMessage[]> {
|
|
153
|
+
if (this._state.isRunning) {
|
|
154
|
+
throw new Error("Agent is already running. Use steer() or followUp() to inject messages.");
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
await this._ensureInitialized();
|
|
158
|
+
|
|
159
|
+
// Resolve system prompt if dynamic
|
|
160
|
+
if (typeof this._config.systemPrompt === "function") {
|
|
161
|
+
this._state.systemPrompt = await this._config.systemPrompt();
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Normalize input to messages
|
|
165
|
+
const inputMessages = this._normalizeInput(input);
|
|
166
|
+
this._state.messages.push(...inputMessages);
|
|
167
|
+
|
|
168
|
+
// Persist input to session
|
|
169
|
+
for (const msg of inputMessages) {
|
|
170
|
+
await this._sessionManager?.appendMessage(msg);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return this._runLoop();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async continue(): Promise<AgentMessage[]> {
|
|
177
|
+
if (this._state.isRunning) {
|
|
178
|
+
throw new Error("Agent is already running.");
|
|
179
|
+
}
|
|
180
|
+
await this._ensureInitialized();
|
|
181
|
+
return this._runLoop();
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
steer(message: AgentMessage): void {
|
|
185
|
+
this._steeringQueue.push(message);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
followUp(message: AgentMessage): void {
|
|
189
|
+
this._followUpQueue.push(message);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
abort(): void {
|
|
193
|
+
this._abortController?.abort();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
subscribe(fn: (event: AgentEvent) => void): () => void {
|
|
197
|
+
this._listeners.add(fn);
|
|
198
|
+
return () => this._listeners.delete(fn);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
get state(): AgentState {
|
|
202
|
+
return this._state;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
get approval(): Approval {
|
|
206
|
+
return this._approval;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
get session(): SessionManager | undefined {
|
|
210
|
+
return this._sessionManager;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
get checkpoints(): CheckpointManager | undefined {
|
|
214
|
+
return this._checkpointManager;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
get extensions(): ExtensionRunner | undefined {
|
|
218
|
+
return this._extensionRunner;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
get injections(): InjectionManager | undefined {
|
|
222
|
+
return this._injectionManager;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
get laborMarket(): LaborMarket | undefined {
|
|
226
|
+
return this._laborMarket;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
get mcpAdapter(): MCPAdapter | undefined {
|
|
230
|
+
return this._mcpAdapter;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
get wire(): Wire {
|
|
234
|
+
return this._wire;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
get backgroundTasks(): BackgroundTaskManager | undefined {
|
|
238
|
+
return this._backgroundTaskManager;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
setSystemPrompt(prompt: string): void {
|
|
242
|
+
this._state.systemPrompt = prompt;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
setModel(model: ModelConfig): void {
|
|
246
|
+
this._state.model = model;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
setTools(tools: AgentTool[]): void {
|
|
250
|
+
if (this._state.isRunning) {
|
|
251
|
+
throw new Error("Cannot change tools while agent is running.");
|
|
252
|
+
}
|
|
253
|
+
this._state.tools = tools;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
setThinkingLevel(level: ThinkingLevel): void {
|
|
257
|
+
if (this._state.isRunning) {
|
|
258
|
+
throw new Error("Cannot change thinking level while agent is running.");
|
|
259
|
+
}
|
|
260
|
+
this._state.thinkingLevel = level;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
async dispose(): Promise<void> {
|
|
264
|
+
this.abort();
|
|
265
|
+
this._listeners.clear();
|
|
266
|
+
this._steeringQueue = [];
|
|
267
|
+
this._followUpQueue = [];
|
|
268
|
+
await this._mcpAdapter?.dispose();
|
|
269
|
+
this._backgroundTaskManager?.dispose();
|
|
270
|
+
this._wire.dispose();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// --- Internal ---
|
|
274
|
+
|
|
275
|
+
private async _ensureInitialized(): Promise<void> {
|
|
276
|
+
if (this._initialized) return;
|
|
277
|
+
this._initialized = true;
|
|
278
|
+
|
|
279
|
+
// Init session and restore messages
|
|
280
|
+
if (this._sessionManager) {
|
|
281
|
+
await this._sessionManager.init();
|
|
282
|
+
const ctx = this._sessionManager.buildSessionContext();
|
|
283
|
+
if (ctx.messages.length > 0) {
|
|
284
|
+
this._state.messages = ctx.messages;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// Load extensions
|
|
289
|
+
if (this._config.extensions?.length) {
|
|
290
|
+
this._extensionRunner = new ExtensionRunner();
|
|
291
|
+
await this._extensionRunner.loadExtensions(this._config.extensions);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Notify extensions of session start
|
|
295
|
+
if (this._sessionManager && this._extensionRunner) {
|
|
296
|
+
await this._extensionRunner.emitSimple("session_start", {
|
|
297
|
+
sessionId: this._sessionManager.getSessionId(),
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Load MCP tools in background
|
|
302
|
+
if (this._mcpAdapter) {
|
|
303
|
+
this._mcpAdapter.startLoading();
|
|
304
|
+
await this._mcpAdapter.waitForReady();
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Discover skills and register skill tool
|
|
308
|
+
if (this._config.skills?.length) {
|
|
309
|
+
const skills = await discoverSkills(this._config.skills);
|
|
310
|
+
if (skills.length > 0) {
|
|
311
|
+
// Add skill list to system prompt for awareness
|
|
312
|
+
const skillList = skills.map((s) => `- /${s.name}: ${s.description}`).join("\n");
|
|
313
|
+
this._state.systemPrompt += `\n\nAvailable skills (use invoke_skill tool to invoke):\n${skillList}`;
|
|
314
|
+
// Add built-in InvokeSkillTool so LLM can actually invoke skills
|
|
315
|
+
this._state.tools = [...this._state.tools, createInvokeSkillTool(skills)];
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Init fixed subagents and create TaskTool
|
|
320
|
+
if (this._config.subagents && this._laborMarket && this._taskExecutor) {
|
|
321
|
+
for (const [name, subConfig] of Object.entries(this._config.subagents)) {
|
|
322
|
+
const subAgent = new Agent({
|
|
323
|
+
model: this._config.model,
|
|
324
|
+
systemPrompt: subConfig.systemPrompt,
|
|
325
|
+
tools: subConfig.tools ?? [],
|
|
326
|
+
provider: this._provider,
|
|
327
|
+
approval: this._approval.share(),
|
|
328
|
+
name,
|
|
329
|
+
});
|
|
330
|
+
this._laborMarket.addFixed(name, subAgent, subConfig.description);
|
|
331
|
+
}
|
|
332
|
+
// Add built-in TaskTool so LLM can delegate to subagents
|
|
333
|
+
this._state.tools = [...this._state.tools, createTaskTool(this._laborMarket, this._taskExecutor)];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Background task tools
|
|
337
|
+
if (this._backgroundTaskManager) {
|
|
338
|
+
const bgTools = createBackgroundTaskTools(this._backgroundTaskManager, this._config.backgroundTasks?.factories);
|
|
339
|
+
this._state.tools = [...this._state.tools, ...bgTools];
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
private _normalizeInput(input: string | AgentMessage | AgentMessage[]): AgentMessage[] {
|
|
344
|
+
if (typeof input === "string") {
|
|
345
|
+
const msg: UserMessage = { role: "user", content: input };
|
|
346
|
+
return [msg];
|
|
347
|
+
}
|
|
348
|
+
if (Array.isArray(input)) return input;
|
|
349
|
+
return [input];
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private _emit(event: AgentEvent): void {
|
|
353
|
+
for (const fn of this._listeners) {
|
|
354
|
+
try {
|
|
355
|
+
fn(event);
|
|
356
|
+
} catch {
|
|
357
|
+
// Listener errors should not break the loop
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
this._wire.publish(event.type, event);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
private async _runLoop(): Promise<AgentMessage[]> {
|
|
364
|
+
this._state.isRunning = true;
|
|
365
|
+
this._state.error = undefined;
|
|
366
|
+
this._abortController = new AbortController();
|
|
367
|
+
|
|
368
|
+
// Collect all tools: config tools + MCP tools + extension tools
|
|
369
|
+
const mcpTools = this._mcpAdapter?.getAllTools() ?? [];
|
|
370
|
+
const allConfigTools = [...this._state.tools, ...mcpTools];
|
|
371
|
+
|
|
372
|
+
// Build compaction config with summarizer
|
|
373
|
+
const compactionWithSummarizer = this._config.compaction ? {
|
|
374
|
+
...this._config.compaction,
|
|
375
|
+
maxContextTokens: this._config.compaction.maxContextTokens ?? this._config.model.maxContextTokens,
|
|
376
|
+
summarize: this._config.compaction.customSummarizer
|
|
377
|
+
? (msgs: AgentMessage[]) => this._config.compaction!.customSummarizer!.summarize(
|
|
378
|
+
agentMessagesToCompactionInput(msgs),
|
|
379
|
+
)
|
|
380
|
+
: (msgs: AgentMessage[]) => {
|
|
381
|
+
const summarizer = new LLMSummarizer(this._provider, this._state.model.model);
|
|
382
|
+
return summarizer.summarize(agentMessagesToCompactionInput(msgs));
|
|
383
|
+
},
|
|
384
|
+
} : undefined;
|
|
385
|
+
|
|
386
|
+
try {
|
|
387
|
+
const result = await runAgentLoop(this._state.messages, {
|
|
388
|
+
provider: this._provider,
|
|
389
|
+
modelId: this._state.model.model,
|
|
390
|
+
systemPrompt: this._state.systemPrompt,
|
|
391
|
+
tools: allConfigTools,
|
|
392
|
+
approval: this._approval,
|
|
393
|
+
agentName: this._name,
|
|
394
|
+
toolExecution: this._config.toolExecution ?? "parallel",
|
|
395
|
+
maxStepsPerTurn: this._config.maxStepsPerTurn ?? 50,
|
|
396
|
+
hooks: this._config.hooks,
|
|
397
|
+
thinkingLevel: this._state.thinkingLevel,
|
|
398
|
+
capabilities: this._state.model.capabilities,
|
|
399
|
+
signal: this._abortController.signal,
|
|
400
|
+
getSteeringMessages: () => {
|
|
401
|
+
const msgs = [...this._steeringQueue];
|
|
402
|
+
this._steeringQueue = [];
|
|
403
|
+
return msgs;
|
|
404
|
+
},
|
|
405
|
+
getFollowUpMessages: () => {
|
|
406
|
+
const msgs = [...this._followUpQueue];
|
|
407
|
+
this._followUpQueue = [];
|
|
408
|
+
return msgs;
|
|
409
|
+
},
|
|
410
|
+
onEvent: (event) => this._emit(event),
|
|
411
|
+
|
|
412
|
+
// Integrated modules
|
|
413
|
+
sessionManager: this._sessionManager,
|
|
414
|
+
checkpointManager: this._checkpointManager,
|
|
415
|
+
injectionManager: this._injectionManager,
|
|
416
|
+
extensionRunner: this._extensionRunner,
|
|
417
|
+
compaction: compactionWithSummarizer,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
this._state.messages = result;
|
|
421
|
+
return result;
|
|
422
|
+
} catch (err) {
|
|
423
|
+
this._state.error = err instanceof Error ? err.message : String(err);
|
|
424
|
+
throw err;
|
|
425
|
+
} finally {
|
|
426
|
+
this._state.isRunning = false;
|
|
427
|
+
this._abortController = null;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// Extension runner — event dispatch, tool registration, lifecycle
|
|
2
|
+
|
|
3
|
+
import type {
|
|
4
|
+
ExtensionFactory,
|
|
5
|
+
ExtensionAPI,
|
|
6
|
+
ExtensionEventType,
|
|
7
|
+
ExtensionHandler,
|
|
8
|
+
ExtensionEventMap,
|
|
9
|
+
BeforeAgentStartEvent,
|
|
10
|
+
BeforeAgentStartResult,
|
|
11
|
+
BeforeProviderRequestEvent,
|
|
12
|
+
BeforeProviderRequestResult,
|
|
13
|
+
ToolCallEvent,
|
|
14
|
+
ToolCallEventResult,
|
|
15
|
+
ToolResultEvent,
|
|
16
|
+
ToolResultEventResult,
|
|
17
|
+
ContextEvent,
|
|
18
|
+
ContextEventResult,
|
|
19
|
+
BeforeCompactEvent,
|
|
20
|
+
BeforeCompactResult,
|
|
21
|
+
CommandHandler,
|
|
22
|
+
} from "./types.js";
|
|
23
|
+
import type { AgentTool } from "../tools/types.js";
|
|
24
|
+
import type { AgentMessage } from "../types.js";
|
|
25
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
26
|
+
|
|
27
|
+
interface RegisteredExtension {
|
|
28
|
+
handlers: Map<ExtensionEventType, ExtensionHandler<any>[]>;
|
|
29
|
+
tools: AgentTool[];
|
|
30
|
+
commands: Map<string, CommandHandler>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export class ExtensionRunner {
|
|
34
|
+
private _extensions: RegisteredExtension[] = [];
|
|
35
|
+
private _pendingMessages: AgentMessage[] = [];
|
|
36
|
+
|
|
37
|
+
async loadExtensions(factories: ExtensionFactory[]): Promise<void> {
|
|
38
|
+
for (const factory of factories) {
|
|
39
|
+
const ext: RegisteredExtension = {
|
|
40
|
+
handlers: new Map(),
|
|
41
|
+
tools: [],
|
|
42
|
+
commands: new Map(),
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const api: ExtensionAPI = {
|
|
46
|
+
on: <E extends ExtensionEventType>(event: E, handler: ExtensionHandler<E>) => {
|
|
47
|
+
if (!ext.handlers.has(event)) ext.handlers.set(event, []);
|
|
48
|
+
ext.handlers.get(event)!.push(handler);
|
|
49
|
+
},
|
|
50
|
+
registerTool: <TParams extends TSchema>(tool: AgentTool<TParams>) => {
|
|
51
|
+
ext.tools.push(tool as AgentTool);
|
|
52
|
+
},
|
|
53
|
+
registerCommand: (name: string, handler: CommandHandler) => {
|
|
54
|
+
ext.commands.set(name, handler);
|
|
55
|
+
},
|
|
56
|
+
sendMessage: (message: AgentMessage) => {
|
|
57
|
+
this._pendingMessages.push(message);
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await factory(api);
|
|
62
|
+
this._extensions.push(ext);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
getRegisteredTools(): AgentTool[] {
|
|
67
|
+
return this._extensions.flatMap((ext) => ext.tools);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
getCommand(name: string): CommandHandler | undefined {
|
|
71
|
+
for (const ext of this._extensions) {
|
|
72
|
+
const handler = ext.commands.get(name);
|
|
73
|
+
if (handler) return handler;
|
|
74
|
+
}
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
drainPendingMessages(): AgentMessage[] {
|
|
79
|
+
const msgs = [...this._pendingMessages];
|
|
80
|
+
this._pendingMessages = [];
|
|
81
|
+
return msgs;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// --- Interceptable event emitters (type-safe, return results) ---
|
|
85
|
+
|
|
86
|
+
async emitBeforeAgentStart(event: BeforeAgentStartEvent): Promise<BeforeAgentStartResult | void> {
|
|
87
|
+
return this._emit("before_agent_start", event);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async emitBeforeProviderRequest(event: BeforeProviderRequestEvent): Promise<BeforeProviderRequestResult | void> {
|
|
91
|
+
return this._emit("before_provider_request", event);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async emitToolCall(event: ToolCallEvent): Promise<ToolCallEventResult | void> {
|
|
95
|
+
return this._emit("tool_call", event);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async emitToolResult(event: ToolResultEvent): Promise<ToolResultEventResult | void> {
|
|
99
|
+
return this._emit("tool_result", event);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async emitContext(event: ContextEvent): Promise<ContextEventResult | void> {
|
|
103
|
+
return this._emit("context", event);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async emitBeforeCompact(event: BeforeCompactEvent): Promise<BeforeCompactResult | void> {
|
|
107
|
+
return this._emit("before_compact", event);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// --- Notification emitter (fire-and-forget) ---
|
|
111
|
+
|
|
112
|
+
async emitSimple(eventType: ExtensionEventType, payload?: any): Promise<void> {
|
|
113
|
+
await this._emit(eventType, payload);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// --- Internal ---
|
|
117
|
+
|
|
118
|
+
private async _emit<E extends ExtensionEventType>(
|
|
119
|
+
eventType: E,
|
|
120
|
+
payload: ExtensionEventMap[E]["payload"],
|
|
121
|
+
): Promise<ExtensionEventMap[E]["result"]> {
|
|
122
|
+
let lastResult: any;
|
|
123
|
+
|
|
124
|
+
for (const ext of this._extensions) {
|
|
125
|
+
const handlers = ext.handlers.get(eventType);
|
|
126
|
+
if (!handlers) continue;
|
|
127
|
+
|
|
128
|
+
for (const handler of handlers) {
|
|
129
|
+
const result = await handler(payload);
|
|
130
|
+
if (result !== undefined && result !== null) {
|
|
131
|
+
lastResult = result;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return lastResult;
|
|
137
|
+
}
|
|
138
|
+
}
|