zapmyco 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.
@@ -0,0 +1,1030 @@
1
+ import { createRequire } from "node:module";
2
+ import { EventEmitter } from "node:events";
3
+ import { Agent } from "@mariozechner/pi-agent-core";
4
+ import { EventEmitter as EventEmitter$1 } from "eventemitter3";
5
+ import { existsSync } from "node:fs";
6
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import { cosmiconfig } from "cosmiconfig";
10
+
11
+ //#region \0rolldown/runtime.js
12
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
13
+
14
+ //#endregion
15
+ //#region src/infra/constants.ts
16
+ /**
17
+ * zapmyco 全局常量定义
18
+ */
19
+ /** 应用名称 */
20
+ const APP_NAME = "zapmyco";
21
+ /** 应用版本(由构建时注入,默认为 dev) */
22
+ const __VERSION__ = "0.0.0-dev";
23
+
24
+ //#endregion
25
+ //#region src/infra/event-bus.ts
26
+ /**
27
+ * zapmyco 事件总线
28
+ *
29
+ * 基于 EventEmitter3 的类型安全事件总线,
30
+ * 用于模块间解耦通信。
31
+ *
32
+ * 内部事件命名规范:`module:action`
33
+ * 例如:`task:started`, `agent:progress`, `goal:completed`
34
+ */
35
+ /**
36
+ * 全局事件总线实例
37
+ *
38
+ * 使用方式:
39
+ * ```typescript
40
+ * import { eventBus } from '@/infra/event-bus';
41
+ *
42
+ * eventBus.on('task:started', ({ taskId, agentId }) => {
43
+ * console.log(`任务 ${taskId} 已在 ${agentId} 上启动`);
44
+ * });
45
+ *
46
+ * eventBus.emit('task:started', { taskId: 'abc', agentId: 'code-1' });
47
+ * ```
48
+ */
49
+ const eventBus = new EventEmitter$1();
50
+
51
+ //#endregion
52
+ //#region src/core/agent-runtime/event-bridge.ts
53
+ /**
54
+ * 将 pi-agent-core AgentEvent 转换为 zapmyco 适配事件
55
+ *
56
+ * @param agentEvent - pi-agent-core 原始事件
57
+ * @param taskId - 关联的任务 ID
58
+ * @param agentId - Agent 标识
59
+ * @returns 适配后的事件,无法识别的返回 null
60
+ */
61
+ function adaptAgentEvent(agentEvent, taskId, agentId) {
62
+ switch (agentEvent.type) {
63
+ case "agent_start": return {
64
+ type: "agent:start",
65
+ taskId,
66
+ agentId
67
+ };
68
+ case "agent_end": return {
69
+ type: "agent:end",
70
+ taskId,
71
+ agentId
72
+ };
73
+ case "turn_start": return {
74
+ type: "turn:start",
75
+ taskId
76
+ };
77
+ case "turn_end": return {
78
+ type: "turn:end",
79
+ taskId
80
+ };
81
+ case "message_start": return {
82
+ type: "message:start",
83
+ taskId,
84
+ textPreview: extractTextContent(agentEvent.message).slice(0, 100)
85
+ };
86
+ case "message_update": return {
87
+ type: "message:update",
88
+ taskId,
89
+ delta: extractDelta(agentEvent.assistantMessageEvent)
90
+ };
91
+ case "message_end": return {
92
+ type: "message:end",
93
+ taskId,
94
+ fullMessage: extractTextContent(agentEvent.message)
95
+ };
96
+ case "tool_execution_start": return {
97
+ type: "tool:start",
98
+ taskId,
99
+ toolName: agentEvent.toolName ?? "unknown",
100
+ toolCallId: agentEvent.toolCallId
101
+ };
102
+ case "tool_execution_update": return {
103
+ type: "tool:update",
104
+ taskId,
105
+ toolName: agentEvent.toolName ?? "unknown"
106
+ };
107
+ case "tool_execution_end": return {
108
+ type: "tool:end",
109
+ taskId,
110
+ toolName: agentEvent.toolName ?? "unknown",
111
+ toolCallId: agentEvent.toolCallId,
112
+ success: !agentEvent.isError
113
+ };
114
+ default: return null;
115
+ }
116
+ }
117
+ /**
118
+ * 从 AgentMessage 中提取文本内容
119
+ */
120
+ function extractTextContent(message) {
121
+ if (!message || typeof message !== "object") return "";
122
+ const msg = message;
123
+ if (typeof msg.content === "string") return msg.content;
124
+ if (Array.isArray(msg.content)) return msg.content.filter((block) => typeof block === "object" && block !== null && block.type === "text").map((block) => block.text ?? "").join("");
125
+ return "";
126
+ }
127
+ /**
128
+ * 从 AssistantMessageEvent 中提取 delta 文本
129
+ */
130
+ function extractDelta(event) {
131
+ if (!event || typeof event !== "object") return "";
132
+ const evt = event;
133
+ if (typeof evt.delta === "string") return evt.delta;
134
+ if (evt.type === "text_delta" && typeof evt.text_delta === "string") return evt.text_delta;
135
+ return "";
136
+ }
137
+ /**
138
+ * 将适配后的事件分发到 zapmyco eventBus
139
+ *
140
+ * 映射规则:
141
+ * - agent:start/end → agent:online/offline(带 taskId 上下文)
142
+ * - turn:start/end → task:started/task:progress
143
+ * - message:* → task:output
144
+ * - tool:* → task:progress(含工具信息)
145
+ *
146
+ * @param event - 适配后的事件
147
+ */
148
+ function dispatchToEventBus(event) {
149
+ switch (event.type) {
150
+ case "agent:start":
151
+ eventBus.emit("agent:online", { agentId: event.agentId });
152
+ break;
153
+ case "agent:end":
154
+ eventBus.emit("task:completed", {
155
+ taskId: event.taskId,
156
+ result: {}
157
+ });
158
+ break;
159
+ case "turn:start":
160
+ eventBus.emit("task:started", {
161
+ taskId: event.taskId,
162
+ agentId: ""
163
+ });
164
+ break;
165
+ case "turn:end":
166
+ eventBus.emit("task:progress", {
167
+ taskId: event.taskId,
168
+ percent: 100,
169
+ message: "Turn completed"
170
+ });
171
+ break;
172
+ case "message:start":
173
+ case "message:update":
174
+ case "message:end": {
175
+ const text = event.type === "message:update" ? event.delta : event.type === "message:end" ? event.fullMessage : `[开始生成] ${event.textPreview}`;
176
+ if (text) eventBus.emit("task:output", {
177
+ taskId: event.taskId,
178
+ text
179
+ });
180
+ break;
181
+ }
182
+ case "tool:start":
183
+ eventBus.emit("task:progress", {
184
+ taskId: event.taskId,
185
+ percent: 0,
186
+ message: `执行工具: ${event.toolName}`
187
+ });
188
+ break;
189
+ case "tool:update":
190
+ eventBus.emit("task:progress", {
191
+ taskId: event.taskId,
192
+ percent: void 0,
193
+ message: `${event.toolName} 更新中`
194
+ });
195
+ break;
196
+ case "tool:end":
197
+ eventBus.emit("task:progress", {
198
+ taskId: event.taskId,
199
+ percent: 100,
200
+ message: `工具 ${event.toolName} ${event.success ? "完成" : "失败"}`
201
+ });
202
+ break;
203
+ case "error":
204
+ eventBus.emit("task:failed", {
205
+ taskId: event.taskId,
206
+ error: event.error,
207
+ retryable: false
208
+ });
209
+ break;
210
+ }
211
+ }
212
+ /**
213
+ * 创建 Agent 事件订阅桥接器
214
+ *
215
+ * 返回一个订阅函数,可直接传给 pi-agent-core Agent.subscribe()。
216
+ * 所有 Agent 事件会自动转换并分发到 zapmyco eventBus。
217
+ *
218
+ * @param taskId - 关联的任务 ID
219
+ * @param agentId - Agent 标识
220
+ * @returns 事件监听函数
221
+ */
222
+ function createEventBridgeListener(taskId, agentId) {
223
+ return (agentEvent) => {
224
+ const adapted = adaptAgentEvent(agentEvent, taskId, agentId);
225
+ if (adapted) dispatchToEventBus(adapted);
226
+ };
227
+ }
228
+
229
+ //#endregion
230
+ //#region src/core/agent-runtime/tool-bridge.ts
231
+ /**
232
+ * 将单个 ToolRegistration 转换为 pi-agent-core 的 AgentTool
233
+ *
234
+ * @param registration - 工具注册信息
235
+ * @returns pi-agent-core AgentTool 实例
236
+ */
237
+ function toAgentTool(registration) {
238
+ return {
239
+ name: registration.id,
240
+ description: registration.description,
241
+ label: registration.label,
242
+ parameters: registration.parameters ?? {
243
+ type: "object",
244
+ properties: {}
245
+ },
246
+ execute: registration.execute,
247
+ ...registration.executionMode != null ? { executionMode: registration.executionMode } : {}
248
+ };
249
+ }
250
+ /**
251
+ * 将工具注册列表批量转换为 AgentTool 数组
252
+ *
253
+ * @param registrations - 工具注册列表
254
+ * @returns AgentTool 数组
255
+ */
256
+ function toAgentTools(registrations) {
257
+ return registrations.map(toAgentTool);
258
+ }
259
+ /**
260
+ * 基于 Capability 创建默认工具注册模板
261
+ *
262
+ * 此函数提供从 Capability 到 ToolRegistration 的基础映射。
263
+ * 实际的 execute 函数需要由调用方根据具体能力注入。
264
+ *
265
+ * @param capability - 能力声明
266
+ * @param execute - 执行函数(必须由调用方提供)
267
+ * @returns ToolRegistration
268
+ */
269
+ function createToolFromCapability(capability, execute) {
270
+ return {
271
+ id: capability.id,
272
+ label: capability.name,
273
+ description: capability.description,
274
+ execute
275
+ };
276
+ }
277
+ /**
278
+ * 基于多个 Capability 批量创建工具注册模板
279
+ *
280
+ * @param capabilities - 能力声明列表
281
+ * @param executorFactory - 根据 Capability 生成执行函数的工厂
282
+ * @returns ToolRegistration 数组
283
+ */
284
+ function createToolsFromCapabilities(capabilities, executorFactory) {
285
+ return capabilities.map((cap) => createToolFromCapability(cap, executorFactory(cap)));
286
+ }
287
+
288
+ //#endregion
289
+ //#region src/core/agent-runtime/agent-adapter.ts
290
+ /**
291
+ * Agent Adapter — IAgent → pi-agent-core.Agent 适配器
292
+ *
293
+ * 将 pi-agent-core 的有状态 Agent 封装为 zapmyco 的 IAgent 接口实现。
294
+ * 这是 agent-runtime 层的核心集成点。
295
+ *
296
+ * @module core/agent-runtime/agent-adapter
297
+ */
298
+ const DEFAULT_RUNTIME_CONFIG = {
299
+ enabled: true,
300
+ toolExecution: "sequential",
301
+ maxTurns: 50,
302
+ thinkingLevel: "medium"
303
+ };
304
+ /**
305
+ * 基于 pi-agent-core 的 LLM 驱动 Agent 实现
306
+ *
307
+ * 将 zapmyco 的 IAgent 接口适配到 pi-agent-core 的 Agent 类,
308
+ * 实现完整的 Agent 生命周期管理:
309
+ * - execute(): 通过 Agent.prompt() 发起任务
310
+ * - cancel(): 通过 Agent.abort() 中止执行
311
+ * - healthCheck(): 检查 Agent 内部状态
312
+ * - 流式事件: 通过 EventEmitter + EventBridge 双通道输出
313
+ */
314
+ var LlmBasedAgent = class extends EventEmitter {
315
+ EVENT_PROGRESS = "progress";
316
+ EVENT_OUTPUT = "output";
317
+ EVENT_ERROR = "error";
318
+ agentId;
319
+ displayName;
320
+ capabilities;
321
+ inner;
322
+ config;
323
+ toolRegistrations = [];
324
+ _currentLoad = 0;
325
+ constructor(options) {
326
+ super();
327
+ this.agentId = options.agentId;
328
+ this.displayName = options.displayName;
329
+ this.capabilities = options.capabilities;
330
+ this.config = {
331
+ ...DEFAULT_RUNTIME_CONFIG,
332
+ ...options.runtimeConfig
333
+ };
334
+ this.inner = new Agent({ toolExecution: this.config.toolExecution });
335
+ }
336
+ get status() {
337
+ if (!this.config.enabled) return "offline";
338
+ if (this._currentLoad > 0) return "busy";
339
+ return "online";
340
+ }
341
+ get currentLoad() {
342
+ return this._currentLoad;
343
+ }
344
+ /**
345
+ * 访问内部 pi-agent-core Agent 实例(仅限高级用法)
346
+ *
347
+ * @internal 仅供测试和高级集成使用
348
+ */
349
+ get innerAgent() {
350
+ return this.inner;
351
+ }
352
+ /**
353
+ * 注册工具到 Agent
354
+ *
355
+ * @param tools - 工具注册列表
356
+ */
357
+ registerTools(tools) {
358
+ this.toolRegistrations.push(...tools);
359
+ const agentTools = toAgentTools(tools);
360
+ this.inner.state.tools = [...this.inner.state.tools, ...agentTools];
361
+ }
362
+ /**
363
+ * 清除所有已注册的工具
364
+ */
365
+ clearTools() {
366
+ this.toolRegistrations = [];
367
+ this.inner.state.tools = [];
368
+ }
369
+ /**
370
+ * 执行任务
371
+ *
372
+ * 将 AgentExecuteRequest 转换为 pi-agent-core Agent 的 prompt 调用:
373
+ * 1. 构建执行上下文(任务描述 + 上游结果)
374
+ * 2. 设置系统提示词
375
+ * 3. 绑定事件桥接到 eventBus + EventEmitter
376
+ * 4. 调用 Agent.prompt() 并等待完成
377
+ * 5. 提取结果并组装为 TaskResult
378
+ */
379
+ async execute(request) {
380
+ const startTime = Date.now();
381
+ this._currentLoad++;
382
+ const cleanupFns = [];
383
+ try {
384
+ this.inner.state.systemPrompt = this.buildSystemPrompt(request);
385
+ cleanupFns.push(this.inner.subscribe(createEventBridgeListener(request.taskId, this.agentId)));
386
+ cleanupFns.push(this.inner.subscribe((event) => {
387
+ if (event.type === "message_update") {
388
+ const delta = extractDeltaFromEvent(event);
389
+ if (delta) this.emit(this.EVENT_OUTPUT, {
390
+ taskId: request.taskId,
391
+ text: delta
392
+ });
393
+ }
394
+ if (event.type === "tool_execution_start") this.emit(this.EVENT_PROGRESS, {
395
+ taskId: request.taskId,
396
+ percent: 0,
397
+ message: `执行工具: ${event.toolName}`
398
+ });
399
+ if (event.type === "tool_execution_end") this.emit(this.EVENT_PROGRESS, {
400
+ taskId: request.taskId,
401
+ percent: 100,
402
+ message: `工具 ${event.toolName} 完成`
403
+ });
404
+ if (event.type === "agent_end") this.emit(this.EVENT_PROGRESS, {
405
+ taskId: request.taskId,
406
+ percent: 100,
407
+ message: "任务完成"
408
+ });
409
+ }));
410
+ await this.inner.prompt(request.taskDescription);
411
+ await this.inner.waitForIdle();
412
+ return this.extractTaskResult(request.taskId, startTime);
413
+ } catch (error) {
414
+ const err = error instanceof Error ? error : new Error(String(error));
415
+ this.emit(this.EVENT_ERROR, {
416
+ taskId: request.taskId,
417
+ error: err
418
+ });
419
+ return {
420
+ taskId: request.taskId,
421
+ status: "failure",
422
+ output: null,
423
+ artifacts: [],
424
+ duration: Date.now() - startTime,
425
+ tokenUsage: {
426
+ inputTokens: 0,
427
+ outputTokens: 0,
428
+ totalTokens: 0,
429
+ estimatedCostUsd: 0
430
+ },
431
+ error: {
432
+ code: "AGENT_EXECUTION_FAILED",
433
+ message: err.message,
434
+ retryable: false,
435
+ details: { stack: err.stack }
436
+ }
437
+ };
438
+ } finally {
439
+ for (const fn of cleanupFns) fn();
440
+ this._currentLoad--;
441
+ }
442
+ }
443
+ /**
444
+ * 取消正在执行的任务
445
+ */
446
+ async cancel(taskId) {
447
+ this.inner.abort();
448
+ this.emit(this.EVENT_PROGRESS, {
449
+ taskId,
450
+ percent: -1,
451
+ message: "任务已取消"
452
+ });
453
+ }
454
+ /**
455
+ * 健康检查
456
+ */
457
+ async healthCheck() {
458
+ const startTime = Date.now();
459
+ try {
460
+ const state = this.inner.state;
461
+ const latencyMs = Date.now() - startTime;
462
+ return {
463
+ 是否健康: this.config.enabled && !state.isStreaming,
464
+ latencyMs,
465
+ version: `pi-agent-core@${this.getPkgVersion()}`,
466
+ details: {
467
+ isStreaming: state.isStreaming,
468
+ messagesCount: state.messages.length,
469
+ toolsCount: state.tools.length,
470
+ currentLoad: this._currentLoad
471
+ }
472
+ };
473
+ } catch {
474
+ return {
475
+ 是否健康: false,
476
+ latencyMs: Date.now() - startTime,
477
+ version: "unknown",
478
+ details: { error: "Health check failed" }
479
+ };
480
+ }
481
+ }
482
+ /**
483
+ * 构建系统提示词
484
+ */
485
+ buildSystemPrompt(request) {
486
+ const parts = [`你是 ${this.displayName},一个专业的 AI 助手。`, `你的能力包括:${this.capabilities.map((c) => c.name).join("、")}。`];
487
+ if (request.upstreamResults?.length) parts.push("\n## 上游任务结果\n", ...request.upstreamResults.map((r, i) => `[上游任务 ${i + 1}] ${JSON.stringify(r.output)}`));
488
+ if (request.workdir) parts.push(`\n## 工作目录\n${request.workdir}`);
489
+ return parts.join("\n");
490
+ }
491
+ /**
492
+ * 从 Agent 状态中提取 TaskResult
493
+ */
494
+ extractTaskResult(taskId, startTime) {
495
+ const state = this.inner.state;
496
+ const duration = Date.now() - startTime;
497
+ const lastAssistantMessage = [...state.messages].reverse().find((m) => m.role === "assistant");
498
+ return {
499
+ taskId,
500
+ status: "success",
501
+ output: lastAssistantMessage ? extractTextFromMessage(lastAssistantMessage) : null,
502
+ artifacts: [],
503
+ duration,
504
+ tokenUsage: {
505
+ inputTokens: 0,
506
+ outputTokens: 0,
507
+ totalTokens: 0,
508
+ estimatedCostUsd: 0
509
+ }
510
+ };
511
+ }
512
+ /**
513
+ * 获取 pi-agent-core 包版本号
514
+ */
515
+ getPkgVersion() {
516
+ try {
517
+ return __require("@mariozechner/pi-agent-core/package.json").version ?? "unknown";
518
+ } catch {
519
+ return "unknown";
520
+ }
521
+ }
522
+ };
523
+ /**
524
+ * 创建 LlmBasedAgent 实例的工厂函数
525
+ *
526
+ * @param options - 适配器选项
527
+ * @returns 配置好的 LlmBasedAgent 实例
528
+ */
529
+ function createLlmBasedAgent(options) {
530
+ return new LlmBasedAgent(options);
531
+ }
532
+ /**
533
+ * 从 SubTask 创建 Agent 执行请求
534
+ *
535
+ * 将 zapmyco 的 SubTask 转换为 AgentExecuteRequest 格式。
536
+ *
537
+ * @param subTask - 子任务定义
538
+ * @param workdir - 项目工作目录
539
+ * @param options - 执行选项
540
+ * @returns AgentExecuteRequest
541
+ */
542
+ function createRequestFromSubTask(subTask, workdir, options) {
543
+ return {
544
+ taskId: subTask.id,
545
+ taskDescription: subTask.description,
546
+ workdir,
547
+ options: {
548
+ timeout: 3e5,
549
+ verbose: false,
550
+ ...options
551
+ }
552
+ };
553
+ }
554
+ /**
555
+ * 从 AgentMessage 中提取文本内容
556
+ */
557
+ function extractTextFromMessage(message) {
558
+ if (!message || typeof message !== "object") return null;
559
+ const msg = message;
560
+ if (typeof msg.content === "string") return msg.content;
561
+ if (Array.isArray(msg.content)) return msg.content.filter((block) => typeof block === "object" && block !== null && block.type === "text").map((block) => block.text ?? "").join("");
562
+ return null;
563
+ }
564
+ /**
565
+ * 从 message_update 事件中提取 delta 文本
566
+ */
567
+ function extractDeltaFromEvent(event) {
568
+ const evt = event.assistantMessageEvent;
569
+ if (!evt || typeof evt !== "object") return null;
570
+ if (typeof evt.delta === "string") return evt.delta;
571
+ if (evt.type === "text_delta" && typeof evt.text_delta === "string") return evt.text_delta;
572
+ return null;
573
+ }
574
+
575
+ //#endregion
576
+ //#region src/infra/errors.ts
577
+ /**
578
+ * zapmyco 统一错误类型
579
+ *
580
+ * 所有模块应使用或继承这些错误类型,确保错误信息一致且可追踪。
581
+ */
582
+ /** 错误码枚举 */
583
+ let ZapmycoErrorCode = /* @__PURE__ */ function(ZapmycoErrorCode) {
584
+ ZapmycoErrorCode["INTENT_PARSE_FAILED"] = "INTENT_PARSE_FAILED";
585
+ ZapmycoErrorCode["INTENT_LOW_CONFIDENCE"] = "INTENT_LOW_CONFIDENCE";
586
+ ZapmycoErrorCode["DECOMPOSE_FAILED"] = "DECOMPOSE_FAILED";
587
+ ZapmycoErrorCode["DECOMPOSE_INVALID_GRAPH"] = "DECOMPOSE_INVALID_GRAPH";
588
+ ZapmycoErrorCode["SCHEDULER_NO_AVAILABLE_AGENT"] = "SCHEDULER_NO_AVAILABLE_AGENT";
589
+ ZapmycoErrorCode["SCHEDULER_CAPABILITY_MISMATCH"] = "SCHEDULER_CAPABILITY_MISMATCH";
590
+ ZapmycoErrorCode["SCHEDULER_TASK_TIMEOUT"] = "SCHEDULER_TASK_TIMEOUT";
591
+ ZapmycoErrorCode["AGENT_NOT_FOUND"] = "AGENT_NOT_FOUND";
592
+ ZapmycoErrorCode["AGENT_OFFLINE"] = "AGENT_OFFLINE";
593
+ ZapmycoErrorCode["AGENT_EXECUTION_FAILED"] = "AGENT_EXECUTION_FAILED";
594
+ ZapmycoErrorCode["AGENT_HEALTH_CHECK_FAILED"] = "AGENT_HEALTH_CHECK_FAILED";
595
+ ZapmycoErrorCode["CONFIG_LOAD_FAILED"] = "CONFIG_LOAD_FAILED";
596
+ ZapmycoErrorCode["CONFIG_INVALID"] = "CONFIG_INVALID";
597
+ ZapmycoErrorCode["LLM_API_ERROR"] = "LLM_API_ERROR";
598
+ ZapmycoErrorCode["LLM_RATE_LIMITED"] = "LLM_RATE_LIMITED";
599
+ ZapmycoErrorCode["LLM_QUOTA_EXCEEDED"] = "LLM_QUOTA_EXCEEDED";
600
+ ZapmycoErrorCode["UNKNOWN"] = "UNKNOWN";
601
+ ZapmycoErrorCode["INTERNAL_ERROR"] = "INTERNAL_ERROR";
602
+ return ZapmycoErrorCode;
603
+ }({});
604
+ /**
605
+ * zapmyco 基础错误类
606
+ *
607
+ * 所有业务错误都应继承此类,提供结构化的错误信息。
608
+ */
609
+ var ZapmycoError = class extends Error {
610
+ /** 错误码 */
611
+ code;
612
+ /** 额外的上下文信息 */
613
+ context;
614
+ constructor(code, message, context) {
615
+ super(message);
616
+ this.name = "ZapmycoError";
617
+ this.code = code;
618
+ Object.assign(this, context !== void 0 ? { context } : {});
619
+ Object.setPrototypeOf(this, new.target.prototype);
620
+ }
621
+ /** 转换为可序列化的对象 */
622
+ toJSON() {
623
+ return {
624
+ name: this.name,
625
+ code: this.code,
626
+ message: this.message,
627
+ context: this.context,
628
+ stack: this.stack
629
+ };
630
+ }
631
+ };
632
+ /** 意图理解错误 */
633
+ var IntentError = class extends ZapmycoError {
634
+ constructor(code, message, context) {
635
+ super(code, message, context);
636
+ this.name = "IntentError";
637
+ }
638
+ };
639
+ /** 任务拆分错误 */
640
+ var DecomposeError = class extends ZapmycoError {
641
+ constructor(code, message, context) {
642
+ super(code, message, context);
643
+ this.name = "DecomposeError";
644
+ }
645
+ };
646
+ /** 调度错误 */
647
+ var SchedulerError = class extends ZapmycoError {
648
+ constructor(code, message, context) {
649
+ super(code, message, context);
650
+ this.name = "SchedulerError";
651
+ }
652
+ };
653
+ /** Agent 执行错误 */
654
+ var AgentError = class extends ZapmycoError {
655
+ constructor(code, message, context) {
656
+ super(code, message, context);
657
+ this.name = "AgentError";
658
+ }
659
+ };
660
+ /** LLM 调用错误 */
661
+ var LlmError = class extends ZapmycoError {
662
+ constructor(code, message, context) {
663
+ super(code, message, context);
664
+ this.name = "LlmError";
665
+ }
666
+ };
667
+
668
+ //#endregion
669
+ //#region src/infra/logger.ts
670
+ /**
671
+ * zapmyco 日志系统
672
+ *
673
+ * 提供结构化的日志输出,支持不同级别和格式化。
674
+ */
675
+ /** 级别对应的标签(用于终端输出) */
676
+ const LEVEL_LABELS = {
677
+ debug: "DBG",
678
+ info: "INF",
679
+ warn: "WRN",
680
+ error: "ERR"
681
+ };
682
+ /** 级别权重(用于过滤) */
683
+ const LEVEL_WEIGHTS = {
684
+ debug: 0,
685
+ info: 1,
686
+ warn: 2,
687
+ error: 3
688
+ };
689
+ /**
690
+ * Logger 实例
691
+ */
692
+ var Logger = class Logger {
693
+ minLevel;
694
+ entries = [];
695
+ constructor(minLevel = "info") {
696
+ this.minLevel = minLevel;
697
+ }
698
+ /** 设置日志级别 */
699
+ setLevel(level) {
700
+ this.minLevel = level;
701
+ }
702
+ /** 创建带前缀的子 logger */
703
+ child(prefix) {
704
+ const child = new Logger(this.minLevel);
705
+ const originalLog = child.log.bind(child);
706
+ child.log = (level, message, context, error) => {
707
+ originalLog(level, `[${prefix}] ${message}`, context, error);
708
+ };
709
+ return child;
710
+ }
711
+ /** 核心日志方法 */
712
+ log(level, message, context, error) {
713
+ if (LEVEL_WEIGHTS[level] < LEVEL_WEIGHTS[this.minLevel]) return;
714
+ const entry = {
715
+ level,
716
+ message,
717
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
718
+ ...context !== void 0 ? { context } : {},
719
+ ...error !== void 0 ? { error } : {}
720
+ };
721
+ this.entries.push(entry);
722
+ let output = `${entry.timestamp.replace("T", " ").slice(0, 19)} [${LEVEL_LABELS[level]}] ${message}`;
723
+ if (entry.context && Object.keys(entry.context).length > 0) output += ` ${JSON.stringify(entry.context)}`;
724
+ if (error instanceof ZapmycoError) output += ` (${error.code}) ${error.message}`;
725
+ else if (error) output += ` ${error.message}`;
726
+ (level === "error" ? process.stderr : process.stdout).write(`${output}\n`);
727
+ }
728
+ debug(message, context) {
729
+ this.log("debug", message, context);
730
+ }
731
+ info(message, context) {
732
+ this.log("info", message, context);
733
+ }
734
+ warn(message, context) {
735
+ this.log("warn", message, context);
736
+ }
737
+ error(message, context, error) {
738
+ this.log("error", message, context, error);
739
+ }
740
+ /** 获取所有日志条目 */
741
+ getEntries() {
742
+ return [...this.entries];
743
+ }
744
+ /** 清空日志条目 */
745
+ clear() {
746
+ this.entries = [];
747
+ }
748
+ };
749
+ /** 全局默认 logger 实例 */
750
+ const logger = new Logger();
751
+
752
+ //#endregion
753
+ //#region src/llm/cost-tracker.ts
754
+ /** 已知模型的定价表 */
755
+ const MODEL_PRICING = {
756
+ "claude-haiku-4-5-20251001": {
757
+ inputPricePer1M: .8,
758
+ outputPricePer1M: 4
759
+ },
760
+ "claude-sonnet-4-20250514": {
761
+ inputPricePer1M: 3,
762
+ outputPricePer1M: 15
763
+ },
764
+ "claude-opus-4-20250514": {
765
+ inputPricePer1M: 15,
766
+ outputPricePer1M: 75
767
+ },
768
+ "gpt-4o": {
769
+ inputPricePer1M: 2.5,
770
+ outputPricePer1M: 10
771
+ },
772
+ "gpt-4o-mini": {
773
+ inputPricePer1M: .15,
774
+ outputPricePer1M: .6
775
+ }
776
+ };
777
+ /**
778
+ * CostTracker 实例
779
+ *
780
+ * 使用方式:
781
+ * ```typescript
782
+ * import { costTracker } from '@/llm/cost-tracker';
783
+ *
784
+ * costTracker.record({ inputTokens: 1000, outputTokens: 500 }, 'claude-sonnet-4');
785
+ * const summary = costTracker.getSummary();
786
+ * console.log(`总花费: $${summary.totalCostUsd}`);
787
+ * ```
788
+ */
789
+ var CostTracker = class {
790
+ records = [];
791
+ /** 记录一次 LLM 调用的 Token 消耗 */
792
+ record(usage, model) {
793
+ const pricing = MODEL_PRICING[model] ?? {
794
+ inputPricePer1M: 0,
795
+ outputPricePer1M: 0
796
+ };
797
+ const estimatedCostUsd = usage.inputTokens / 1e6 * pricing.inputPricePer1M + usage.outputTokens / 1e6 * pricing.outputPricePer1M;
798
+ const fullUsage = {
799
+ ...usage,
800
+ totalTokens: usage.inputTokens + usage.outputTokens,
801
+ estimatedCostUsd
802
+ };
803
+ this.records.push({
804
+ usage: fullUsage,
805
+ model,
806
+ timestamp: Date.now()
807
+ });
808
+ logger.debug("Token 消耗记录", {
809
+ model,
810
+ tokens: fullUsage.totalTokens,
811
+ cost: `$${estimatedCostUsd.toFixed(4)}`
812
+ });
813
+ }
814
+ /** 获取累计摘要 */
815
+ getSummary() {
816
+ let totalInputTokens = 0;
817
+ let totalOutputTokens = 0;
818
+ let totalCostUsd = 0;
819
+ for (const record of this.records) {
820
+ totalInputTokens += record.usage.inputTokens;
821
+ totalOutputTokens += record.usage.outputTokens;
822
+ totalCostUsd += record.usage.estimatedCostUsd;
823
+ }
824
+ return {
825
+ totalInputTokens,
826
+ totalOutputTokens,
827
+ totalTokens: totalInputTokens + totalOutputTokens,
828
+ totalCostUsd,
829
+ callCount: this.records.length
830
+ };
831
+ }
832
+ /** 重置所有记录 */
833
+ reset() {
834
+ this.records = [];
835
+ }
836
+ /** 获取记录数 */
837
+ get count() {
838
+ return this.records.length;
839
+ }
840
+ };
841
+ /** 全局默认 CostTracker 实例 */
842
+ const costTracker = new CostTracker();
843
+
844
+ //#endregion
845
+ //#region src/config/defaults.ts
846
+ /** 默认配置 */
847
+ const DEFAULT_CONFIG = {
848
+ llm: {
849
+ defaultModel: "anthropic/claude-sonnet-4-20250514",
850
+ models: { "anthropic/claude-sonnet-4-20250514": {
851
+ provider: "anthropic",
852
+ modelId: "claude-sonnet-4-20250514",
853
+ description: "Anthropic Claude Sonnet 4 - 均衡模型,日常使用推荐"
854
+ } },
855
+ providers: { anthropic: { apiKey: "${ANTHROPIC_API_KEY}" } },
856
+ defaults: {
857
+ maxTokens: 8192,
858
+ temperature: .7
859
+ }
860
+ },
861
+ scheduler: {
862
+ maxConcurrency: 5,
863
+ maxPerAgent: 3,
864
+ taskTimeoutMs: 1800 * 1e3,
865
+ maxRetries: 3,
866
+ retryBaseDelayMs: 1e3
867
+ },
868
+ agents: [
869
+ {
870
+ id: "code-agent",
871
+ enabled: true
872
+ },
873
+ {
874
+ id: "security-scanner",
875
+ enabled: true
876
+ },
877
+ {
878
+ id: "research-agent",
879
+ enabled: true
880
+ },
881
+ {
882
+ id: "planning-agent",
883
+ enabled: true
884
+ }
885
+ ],
886
+ cli: {
887
+ color: true,
888
+ debug: false,
889
+ outputFormat: "text"
890
+ }
891
+ };
892
+
893
+ //#endregion
894
+ //#region src/config/loader.ts
895
+ /**
896
+ * zapmyco 配置加载器
897
+ *
898
+ * 使用 cosmiconfig 搜索并加载配置文件。
899
+ * 支持多路径搜索,包括用户家目录 ~/.zapmyco/zapmyco.json。
900
+ */
901
+ const EXPLORER_NAME = "zapmyco";
902
+ /** 用户家目录配置路径 */
903
+ const HOME_CONFIG_PATH = join(homedir(), ".zapmyco", "zapmyco.json");
904
+ /**
905
+ * 解析配置值中的环境变量引用
906
+ *
907
+ * 支持语法:${ENV_VAR_NAME}
908
+ * 未定义的环境变量会被替换为空字符串
909
+ */
910
+ function resolveEnvVars(value) {
911
+ if (typeof value === "string") return value.replace(/\$\{(\w+)\}/g, (_match, varName) => {
912
+ return process.env[varName] ?? "";
913
+ });
914
+ if (Array.isArray(value)) return value.map(resolveEnvVars);
915
+ if (value !== null && typeof value === "object") {
916
+ const result = {};
917
+ for (const [key, val] of Object.entries(value)) result[key] = resolveEnvVars(val);
918
+ return result;
919
+ }
920
+ return value;
921
+ }
922
+ /**
923
+ * 深度合并两个对象
924
+ */
925
+ function deepMerge(target, source) {
926
+ const result = { ...target };
927
+ for (const key of Object.keys(source)) {
928
+ const sourceValue = source[key];
929
+ const targetValue = target[key];
930
+ if (sourceValue !== void 0 && typeof sourceValue === "object" && sourceValue !== null && !Array.isArray(sourceValue) && typeof targetValue === "object" && targetValue !== null && !Array.isArray(targetValue)) result[key] = deepMerge(targetValue, sourceValue);
931
+ else if (sourceValue !== void 0) result[key] = sourceValue;
932
+ }
933
+ return result;
934
+ }
935
+ /**
936
+ * 确保家目录配置文件存在,不存在则创建模板
937
+ */
938
+ async function ensureHomeConfig() {
939
+ if (existsSync(HOME_CONFIG_PATH)) return;
940
+ const dir = join(homedir(), ".zapmyco");
941
+ if (!existsSync(dir)) await mkdir(dir, { recursive: true });
942
+ await writeFile(HOME_CONFIG_PATH, JSON.stringify({ llm: {
943
+ defaultModel: "anthropic/claude-sonnet-4-20250514",
944
+ models: {
945
+ "anthropic/claude-sonnet-4-20250514": {
946
+ provider: "anthropic",
947
+ modelId: "claude-sonnet-4-20250514",
948
+ description: "Anthropic Claude Sonnet 4 - 均衡模型,日常使用推荐"
949
+ },
950
+ "openai/gpt-4o": {
951
+ provider: "openai",
952
+ modelId: "gpt-4o",
953
+ description: "OpenAI GPT-4o - 多功能模型"
954
+ }
955
+ },
956
+ providers: {
957
+ anthropic: { apiKey: "${ANTHROPIC_API_KEY}" },
958
+ openai: { apiKey: "${OPENAI_API_KEY}" }
959
+ },
960
+ defaults: {
961
+ maxTokens: 8192,
962
+ temperature: .7
963
+ }
964
+ } }, null, 2) + "\n", "utf-8");
965
+ logger.info("已创建默认配置文件", { filepath: HOME_CONFIG_PATH });
966
+ }
967
+ /**
968
+ * 尝试从家目录加载配置
969
+ */
970
+ async function tryLoadHomeConfig() {
971
+ try {
972
+ if (!existsSync(HOME_CONFIG_PATH)) await ensureHomeConfig();
973
+ const content = await readFile(HOME_CONFIG_PATH, "utf-8");
974
+ return {
975
+ config: resolveEnvVars(JSON.parse(content)),
976
+ filepath: HOME_CONFIG_PATH
977
+ };
978
+ } catch {
979
+ return null;
980
+ }
981
+ }
982
+ /**
983
+ * 加载 zapmyco 配置
984
+ *
985
+ * 搜索优先级:
986
+ * 1. 显式指定的 configPath
987
+ * 2. 项目级配置文件(cosmiconfig 默认搜索)
988
+ * 3. 用户家目录 ~/.zapmyco/zapmyco.json
989
+ * 4. 默认值
990
+ *
991
+ * @param configPath - 可选的显式配置文件路径
992
+ * @returns 合并后的完整配置(用户配置 + 默认值深度合并)
993
+ */
994
+ async function loadConfig(configPath) {
995
+ if (configPath) {
996
+ const explorer = cosmiconfig(EXPLORER_NAME);
997
+ try {
998
+ const result = await explorer.load(configPath);
999
+ if (!result?.config) {
1000
+ logger.debug("指定路径未找到配置,使用默认配置");
1001
+ return { ...DEFAULT_CONFIG };
1002
+ }
1003
+ logger.info("已加载配置文件", { filepath: result.filepath });
1004
+ return deepMerge(DEFAULT_CONFIG, resolveEnvVars(result.config));
1005
+ } catch (error) {
1006
+ const message = error instanceof Error ? error.message : String(error);
1007
+ logger.warn("配置加载失败,使用默认配置", { error: message });
1008
+ return { ...DEFAULT_CONFIG };
1009
+ }
1010
+ }
1011
+ const explorer = cosmiconfig(EXPLORER_NAME);
1012
+ let result = null;
1013
+ try {
1014
+ result = await explorer.search();
1015
+ } catch {}
1016
+ if (!result?.config) {
1017
+ const homeResult = await tryLoadHomeConfig();
1018
+ if (homeResult?.config) result = homeResult;
1019
+ }
1020
+ if (!result?.config) {
1021
+ logger.debug("未找到配置文件,使用默认配置");
1022
+ return { ...DEFAULT_CONFIG };
1023
+ }
1024
+ logger.info("已加载配置文件", { filepath: result.filepath });
1025
+ return deepMerge(DEFAULT_CONFIG, result.config);
1026
+ }
1027
+
1028
+ //#endregion
1029
+ export { dispatchToEventBus as C, __VERSION__ as E, createEventBridgeListener as S, APP_NAME as T, createToolFromCapability as _, Logger as a, toAgentTools as b, DecomposeError as c, SchedulerError as d, ZapmycoError as f, createRequestFromSubTask as g, createLlmBasedAgent as h, costTracker as i, IntentError as l, LlmBasedAgent as m, DEFAULT_CONFIG as n, logger as o, ZapmycoErrorCode as p, CostTracker as r, AgentError as s, loadConfig as t, LlmError as u, createToolsFromCapabilities as v, eventBus as w, adaptAgentEvent as x, toAgentTool as y };
1030
+ //# sourceMappingURL=loader-D1dTKHK4.mjs.map