@yuaone/core 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.
Files changed (235) hide show
  1. package/LICENSE +663 -0
  2. package/README.md +15 -0
  3. package/dist/__tests__/context-manager.test.d.ts +6 -0
  4. package/dist/__tests__/context-manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/context-manager.test.js +220 -0
  6. package/dist/__tests__/context-manager.test.js.map +1 -0
  7. package/dist/__tests__/governor.test.d.ts +6 -0
  8. package/dist/__tests__/governor.test.d.ts.map +1 -0
  9. package/dist/__tests__/governor.test.js +210 -0
  10. package/dist/__tests__/governor.test.js.map +1 -0
  11. package/dist/__tests__/model-router.test.d.ts +6 -0
  12. package/dist/__tests__/model-router.test.d.ts.map +1 -0
  13. package/dist/__tests__/model-router.test.js +329 -0
  14. package/dist/__tests__/model-router.test.js.map +1 -0
  15. package/dist/agent-logger.d.ts +384 -0
  16. package/dist/agent-logger.d.ts.map +1 -0
  17. package/dist/agent-logger.js +820 -0
  18. package/dist/agent-logger.js.map +1 -0
  19. package/dist/agent-loop.d.ts +163 -0
  20. package/dist/agent-loop.d.ts.map +1 -0
  21. package/dist/agent-loop.js +609 -0
  22. package/dist/agent-loop.js.map +1 -0
  23. package/dist/agent-modes.d.ts +85 -0
  24. package/dist/agent-modes.d.ts.map +1 -0
  25. package/dist/agent-modes.js +418 -0
  26. package/dist/agent-modes.js.map +1 -0
  27. package/dist/approval.d.ts +137 -0
  28. package/dist/approval.d.ts.map +1 -0
  29. package/dist/approval.js +299 -0
  30. package/dist/approval.js.map +1 -0
  31. package/dist/async-completion-queue.d.ts +56 -0
  32. package/dist/async-completion-queue.d.ts.map +1 -0
  33. package/dist/async-completion-queue.js +77 -0
  34. package/dist/async-completion-queue.js.map +1 -0
  35. package/dist/auto-fix.d.ts +174 -0
  36. package/dist/auto-fix.d.ts.map +1 -0
  37. package/dist/auto-fix.js +319 -0
  38. package/dist/auto-fix.js.map +1 -0
  39. package/dist/codebase-context.d.ts +396 -0
  40. package/dist/codebase-context.d.ts.map +1 -0
  41. package/dist/codebase-context.js +1260 -0
  42. package/dist/codebase-context.js.map +1 -0
  43. package/dist/conflict-resolver.d.ts +191 -0
  44. package/dist/conflict-resolver.d.ts.map +1 -0
  45. package/dist/conflict-resolver.js +524 -0
  46. package/dist/conflict-resolver.js.map +1 -0
  47. package/dist/constants.d.ts +52 -0
  48. package/dist/constants.d.ts.map +1 -0
  49. package/dist/constants.js +141 -0
  50. package/dist/constants.js.map +1 -0
  51. package/dist/context-budget.d.ts +435 -0
  52. package/dist/context-budget.d.ts.map +1 -0
  53. package/dist/context-budget.js +903 -0
  54. package/dist/context-budget.js.map +1 -0
  55. package/dist/context-compressor.d.ts +143 -0
  56. package/dist/context-compressor.d.ts.map +1 -0
  57. package/dist/context-compressor.js +511 -0
  58. package/dist/context-compressor.js.map +1 -0
  59. package/dist/context-manager.d.ts +112 -0
  60. package/dist/context-manager.d.ts.map +1 -0
  61. package/dist/context-manager.js +247 -0
  62. package/dist/context-manager.js.map +1 -0
  63. package/dist/continuous-reflection.d.ts +267 -0
  64. package/dist/continuous-reflection.d.ts.map +1 -0
  65. package/dist/continuous-reflection.js +338 -0
  66. package/dist/continuous-reflection.js.map +1 -0
  67. package/dist/cross-file-refactor.d.ts +352 -0
  68. package/dist/cross-file-refactor.d.ts.map +1 -0
  69. package/dist/cross-file-refactor.js +1544 -0
  70. package/dist/cross-file-refactor.js.map +1 -0
  71. package/dist/dag-orchestrator.d.ts +138 -0
  72. package/dist/dag-orchestrator.d.ts.map +1 -0
  73. package/dist/dag-orchestrator.js +379 -0
  74. package/dist/dag-orchestrator.js.map +1 -0
  75. package/dist/debate-orchestrator.d.ts +301 -0
  76. package/dist/debate-orchestrator.d.ts.map +1 -0
  77. package/dist/debate-orchestrator.js +719 -0
  78. package/dist/debate-orchestrator.js.map +1 -0
  79. package/dist/dependency-analyzer.d.ts +113 -0
  80. package/dist/dependency-analyzer.d.ts.map +1 -0
  81. package/dist/dependency-analyzer.js +444 -0
  82. package/dist/dependency-analyzer.js.map +1 -0
  83. package/dist/design-loop.d.ts +59 -0
  84. package/dist/design-loop.d.ts.map +1 -0
  85. package/dist/design-loop.js +344 -0
  86. package/dist/design-loop.js.map +1 -0
  87. package/dist/doc-intelligence.d.ts +383 -0
  88. package/dist/doc-intelligence.d.ts.map +1 -0
  89. package/dist/doc-intelligence.js +1307 -0
  90. package/dist/doc-intelligence.js.map +1 -0
  91. package/dist/dynamic-role-generator.d.ts +76 -0
  92. package/dist/dynamic-role-generator.d.ts.map +1 -0
  93. package/dist/dynamic-role-generator.js +194 -0
  94. package/dist/dynamic-role-generator.js.map +1 -0
  95. package/dist/errors.d.ts +69 -0
  96. package/dist/errors.d.ts.map +1 -0
  97. package/dist/errors.js +102 -0
  98. package/dist/errors.js.map +1 -0
  99. package/dist/event-bus.d.ts +159 -0
  100. package/dist/event-bus.d.ts.map +1 -0
  101. package/dist/event-bus.js +305 -0
  102. package/dist/event-bus.js.map +1 -0
  103. package/dist/execution-engine.d.ts +425 -0
  104. package/dist/execution-engine.d.ts.map +1 -0
  105. package/dist/execution-engine.js +1555 -0
  106. package/dist/execution-engine.js.map +1 -0
  107. package/dist/git-intelligence.d.ts +306 -0
  108. package/dist/git-intelligence.d.ts.map +1 -0
  109. package/dist/git-intelligence.js +1099 -0
  110. package/dist/git-intelligence.js.map +1 -0
  111. package/dist/governor.d.ts +77 -0
  112. package/dist/governor.d.ts.map +1 -0
  113. package/dist/governor.js +161 -0
  114. package/dist/governor.js.map +1 -0
  115. package/dist/hierarchical-planner.d.ts +313 -0
  116. package/dist/hierarchical-planner.d.ts.map +1 -0
  117. package/dist/hierarchical-planner.js +981 -0
  118. package/dist/hierarchical-planner.js.map +1 -0
  119. package/dist/index.d.ts +121 -0
  120. package/dist/index.d.ts.map +1 -0
  121. package/dist/index.js +123 -0
  122. package/dist/index.js.map +1 -0
  123. package/dist/intent-inference.d.ts +103 -0
  124. package/dist/intent-inference.d.ts.map +1 -0
  125. package/dist/intent-inference.js +605 -0
  126. package/dist/intent-inference.js.map +1 -0
  127. package/dist/interrupt-manager.d.ts +143 -0
  128. package/dist/interrupt-manager.d.ts.map +1 -0
  129. package/dist/interrupt-manager.js +196 -0
  130. package/dist/interrupt-manager.js.map +1 -0
  131. package/dist/kernel.d.ts +564 -0
  132. package/dist/kernel.d.ts.map +1 -0
  133. package/dist/kernel.js +1419 -0
  134. package/dist/kernel.js.map +1 -0
  135. package/dist/language-support.d.ts +232 -0
  136. package/dist/language-support.d.ts.map +1 -0
  137. package/dist/language-support.js +1134 -0
  138. package/dist/language-support.js.map +1 -0
  139. package/dist/llm-client.d.ts +82 -0
  140. package/dist/llm-client.d.ts.map +1 -0
  141. package/dist/llm-client.js +475 -0
  142. package/dist/llm-client.js.map +1 -0
  143. package/dist/mcp-client.d.ts +232 -0
  144. package/dist/mcp-client.d.ts.map +1 -0
  145. package/dist/mcp-client.js +718 -0
  146. package/dist/mcp-client.js.map +1 -0
  147. package/dist/memory-manager.d.ts +200 -0
  148. package/dist/memory-manager.d.ts.map +1 -0
  149. package/dist/memory-manager.js +568 -0
  150. package/dist/memory-manager.js.map +1 -0
  151. package/dist/memory.d.ts +87 -0
  152. package/dist/memory.d.ts.map +1 -0
  153. package/dist/memory.js +341 -0
  154. package/dist/memory.js.map +1 -0
  155. package/dist/model-router.d.ts +245 -0
  156. package/dist/model-router.d.ts.map +1 -0
  157. package/dist/model-router.js +632 -0
  158. package/dist/model-router.js.map +1 -0
  159. package/dist/parallel-executor.d.ts +125 -0
  160. package/dist/parallel-executor.d.ts.map +1 -0
  161. package/dist/parallel-executor.js +201 -0
  162. package/dist/parallel-executor.js.map +1 -0
  163. package/dist/perf-optimizer.d.ts +212 -0
  164. package/dist/perf-optimizer.d.ts.map +1 -0
  165. package/dist/perf-optimizer.js +721 -0
  166. package/dist/perf-optimizer.js.map +1 -0
  167. package/dist/persona.d.ts +305 -0
  168. package/dist/persona.d.ts.map +1 -0
  169. package/dist/persona.js +887 -0
  170. package/dist/persona.js.map +1 -0
  171. package/dist/planner.d.ts +70 -0
  172. package/dist/planner.d.ts.map +1 -0
  173. package/dist/planner.js +264 -0
  174. package/dist/planner.js.map +1 -0
  175. package/dist/qa-pipeline.d.ts +365 -0
  176. package/dist/qa-pipeline.d.ts.map +1 -0
  177. package/dist/qa-pipeline.js +1352 -0
  178. package/dist/qa-pipeline.js.map +1 -0
  179. package/dist/reasoning-adapter.d.ts +116 -0
  180. package/dist/reasoning-adapter.d.ts.map +1 -0
  181. package/dist/reasoning-adapter.js +187 -0
  182. package/dist/reasoning-adapter.js.map +1 -0
  183. package/dist/role-registry.d.ts +55 -0
  184. package/dist/role-registry.d.ts.map +1 -0
  185. package/dist/role-registry.js +192 -0
  186. package/dist/role-registry.js.map +1 -0
  187. package/dist/sandbox-tiers.d.ts +327 -0
  188. package/dist/sandbox-tiers.d.ts.map +1 -0
  189. package/dist/sandbox-tiers.js +928 -0
  190. package/dist/sandbox-tiers.js.map +1 -0
  191. package/dist/security-scanner.d.ts +222 -0
  192. package/dist/security-scanner.d.ts.map +1 -0
  193. package/dist/security-scanner.js +1129 -0
  194. package/dist/security-scanner.js.map +1 -0
  195. package/dist/security.d.ts +93 -0
  196. package/dist/security.d.ts.map +1 -0
  197. package/dist/security.js +393 -0
  198. package/dist/security.js.map +1 -0
  199. package/dist/self-reflection.d.ts +397 -0
  200. package/dist/self-reflection.d.ts.map +1 -0
  201. package/dist/self-reflection.js +908 -0
  202. package/dist/self-reflection.js.map +1 -0
  203. package/dist/session-persistence.d.ts +191 -0
  204. package/dist/session-persistence.d.ts.map +1 -0
  205. package/dist/session-persistence.js +395 -0
  206. package/dist/session-persistence.js.map +1 -0
  207. package/dist/speculative-executor.d.ts +210 -0
  208. package/dist/speculative-executor.d.ts.map +1 -0
  209. package/dist/speculative-executor.js +618 -0
  210. package/dist/speculative-executor.js.map +1 -0
  211. package/dist/state-machine.d.ts +289 -0
  212. package/dist/state-machine.d.ts.map +1 -0
  213. package/dist/state-machine.js +695 -0
  214. package/dist/state-machine.js.map +1 -0
  215. package/dist/sub-agent.d.ts +177 -0
  216. package/dist/sub-agent.d.ts.map +1 -0
  217. package/dist/sub-agent.js +303 -0
  218. package/dist/sub-agent.js.map +1 -0
  219. package/dist/system-prompt.d.ts +26 -0
  220. package/dist/system-prompt.d.ts.map +1 -0
  221. package/dist/system-prompt.js +84 -0
  222. package/dist/system-prompt.js.map +1 -0
  223. package/dist/test-intelligence.d.ts +439 -0
  224. package/dist/test-intelligence.d.ts.map +1 -0
  225. package/dist/test-intelligence.js +1165 -0
  226. package/dist/test-intelligence.js.map +1 -0
  227. package/dist/types.d.ts +632 -0
  228. package/dist/types.d.ts.map +1 -0
  229. package/dist/types.js +6 -0
  230. package/dist/types.js.map +1 -0
  231. package/dist/vector-index.d.ts +314 -0
  232. package/dist/vector-index.d.ts.map +1 -0
  233. package/dist/vector-index.js +618 -0
  234. package/dist/vector-index.js.map +1 -0
  235. package/package.json +41 -0
@@ -0,0 +1,820 @@
1
+ /**
2
+ * @module agent-logger
3
+ * @description YUAN Agent Logger — Structured, layered logging for agent execution.
4
+ *
5
+ * Provides:
6
+ * - Layer-transition logging (fires ONCE per layer entry)
7
+ * - Internal reasoning/decision tracking
8
+ * - Parallel agent log separation
9
+ * - Formatted human-readable output with tree-drawing characters
10
+ * - JSONL file output for machine parsing
11
+ * - Query and summary APIs for debugging
12
+ *
13
+ * Only depends on Node builtins (fs, path, crypto).
14
+ */
15
+ import { randomUUID } from "node:crypto";
16
+ import { appendFileSync, mkdirSync, writeFileSync } from "node:fs";
17
+ import { dirname, join } from "node:path";
18
+ // ══════════════════════════════════════════════════════════════════════
19
+ // Constants
20
+ // ══════════════════════════════════════════════════════════════════════
21
+ const LOG_LEVEL_ORDER = {
22
+ trace: 0,
23
+ debug: 1,
24
+ info: 2,
25
+ warn: 3,
26
+ error: 4,
27
+ fatal: 5,
28
+ };
29
+ const LOG_LEVEL_LABELS = {
30
+ trace: "TRACE",
31
+ debug: "DEBUG",
32
+ info: "INFO",
33
+ warn: "WARN",
34
+ error: "ERROR",
35
+ fatal: "FATAL",
36
+ };
37
+ /** ANSI color codes for console output */
38
+ const COLORS = {
39
+ reset: "\x1b[0m",
40
+ dim: "\x1b[2m",
41
+ red: "\x1b[31m",
42
+ green: "\x1b[32m",
43
+ yellow: "\x1b[33m",
44
+ blue: "\x1b[34m",
45
+ magenta: "\x1b[35m",
46
+ cyan: "\x1b[36m",
47
+ white: "\x1b[37m",
48
+ bgRed: "\x1b[41m",
49
+ };
50
+ const LEVEL_COLORS = {
51
+ trace: COLORS.dim,
52
+ debug: COLORS.cyan,
53
+ info: COLORS.green,
54
+ warn: COLORS.yellow,
55
+ error: COLORS.red,
56
+ fatal: COLORS.bgRed + COLORS.white,
57
+ };
58
+ // ══════════════════════════════════════════════════════════════════════
59
+ // AgentLogger
60
+ // ══════════════════════════════════════════════════════════════════════
61
+ /**
62
+ * Structured logger for YUAN agent execution.
63
+ *
64
+ * Tracks layer transitions (firing once per entry), internal reasoning,
65
+ * decisions, tool calls, parallel agent lifecycles, and conflicts.
66
+ * Supports memory, file (JSONL), console, and callback outputs.
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * const logger = new AgentLogger({ sessionId: "sess-1" });
71
+ * logger.logInput("Add OAuth to CLI");
72
+ * const exit = logger.enterLayer("analyze");
73
+ * logger.logReasoning("Need to check existing auth...");
74
+ * exit(); // logs duration
75
+ * console.log(logger.getFormattedLog());
76
+ * ```
77
+ */
78
+ export class AgentLogger {
79
+ sessionId;
80
+ agentId;
81
+ parentAgentId;
82
+ level;
83
+ outputs;
84
+ fireOncePerLayer;
85
+ includeReasoning;
86
+ includeParallel;
87
+ maxLogSize;
88
+ logDir;
89
+ separateParallelLogs;
90
+ /** In-memory log storage */
91
+ entries = [];
92
+ /** Number of entries already flushed to file (prevents duplicate writes) */
93
+ flushedCount = 0;
94
+ /** Layers that have already emitted an "entered" log */
95
+ enteredLayers = new Set();
96
+ /** Current layer nesting stack */
97
+ layerStack = [];
98
+ /** Child loggers (parallel agents) keyed by agentId */
99
+ children = new Map();
100
+ /** Monotonic counter for ordering */
101
+ idCounter = 0;
102
+ constructor(config) {
103
+ this.sessionId = config.sessionId;
104
+ this.agentId = config.agentId;
105
+ this.parentAgentId = config.parentAgentId;
106
+ this.level = config.level ?? "info";
107
+ this.outputs = config.outputs ?? [{ type: "memory" }];
108
+ this.fireOncePerLayer = config.fireOncePerLayer ?? true;
109
+ this.includeReasoning = config.includeReasoning ?? true;
110
+ this.includeParallel = config.includeParallel ?? true;
111
+ this.maxLogSize = config.maxLogSize ?? 5000;
112
+ this.logDir = config.logDir;
113
+ this.separateParallelLogs = config.separateParallelLogs ?? true;
114
+ }
115
+ // ════════════════════════════════════════════════════════════════════
116
+ // Core Level Methods
117
+ // ════════════════════════════════════════════════════════════════════
118
+ /** Log at trace level */
119
+ trace(category, message, data) {
120
+ this.log("trace", category, message, data);
121
+ }
122
+ /** Log at debug level */
123
+ debug(category, message, data) {
124
+ this.log("debug", category, message, data);
125
+ }
126
+ /** Log at info level */
127
+ info(category, message, data) {
128
+ this.log("info", category, message, data);
129
+ }
130
+ /** Log at warn level */
131
+ warn(category, message, data) {
132
+ this.log("warn", category, message, data);
133
+ }
134
+ /** Log at error level */
135
+ error(category, message, data) {
136
+ this.log("error", category, message, data);
137
+ }
138
+ /** Log at fatal level */
139
+ fatal(category, message, data) {
140
+ this.log("fatal", category, message, data);
141
+ }
142
+ // ════════════════════════════════════════════════════════════════════
143
+ // Layer Tracking
144
+ // ════════════════════════════════════════════════════════════════════
145
+ /**
146
+ * Enter a layer. Logs the entry ONCE (if fireOncePerLayer is true).
147
+ * Returns an exit function that logs duration when called.
148
+ *
149
+ * @param layer - Layer name (e.g., "analyze", "plan", "implement", "verify")
150
+ * @param message - Optional entry message
151
+ * @returns Exit function — call it when the layer completes
152
+ */
153
+ enterLayer(layer, message) {
154
+ const startTime = Date.now();
155
+ const depth = this.layerStack.length;
156
+ this.layerStack.push(layer);
157
+ // Fire entry log only once per layer (unless disabled)
158
+ if (!this.fireOncePerLayer || !this.enteredLayers.has(layer)) {
159
+ this.enteredLayers.add(layer);
160
+ const entry = this.createEntry("info", "layer", message ?? `${layer.toUpperCase()} (entered)`);
161
+ entry.layer = layer;
162
+ entry.layerDepth = depth;
163
+ this.emit(entry);
164
+ }
165
+ // Return exit function
166
+ return () => {
167
+ const durationMs = Date.now() - startTime;
168
+ // Pop the layer from stack
169
+ const idx = this.layerStack.lastIndexOf(layer);
170
+ if (idx !== -1)
171
+ this.layerStack.splice(idx, 1);
172
+ const exitEntry = this.createEntry("info", "layer", `${layer.toUpperCase()} (${(durationMs / 1000).toFixed(2)}s)`);
173
+ exitEntry.layer = layer;
174
+ exitEntry.layerDepth = depth;
175
+ exitEntry.durationMs = durationMs;
176
+ this.emit(exitEntry);
177
+ };
178
+ }
179
+ // ════════════════════════════════════════════════════════════════════
180
+ // Reasoning & Decisions
181
+ // ════════════════════════════════════════════════════════════════════
182
+ /**
183
+ * Log internal reasoning — what the agent is thinking.
184
+ *
185
+ * @param thought - The reasoning text
186
+ * @param options - Options being considered
187
+ * @param chosen - Which option was chosen
188
+ * @param confidence - Confidence level 0–1
189
+ * @param why - Explanation for the choice
190
+ */
191
+ logReasoning(thought, options, chosen, confidence, why) {
192
+ if (!this.includeReasoning)
193
+ return;
194
+ const entry = this.createEntry("debug", "reasoning", `Thinking: "${thought}"`);
195
+ entry.reasoning = { thought, options, chosen, confidence, why };
196
+ this.emit(entry);
197
+ }
198
+ /**
199
+ * Log a decision point — a fork where the agent chose one path.
200
+ *
201
+ * @param question - What decision was made
202
+ * @param options - Available options
203
+ * @param chosen - Selected option
204
+ * @param why - Rationale
205
+ * @param confidence - Confidence level 0–1
206
+ */
207
+ logDecision(question, options, chosen, why, confidence) {
208
+ const entry = this.createEntry("info", "decision", `Decision: "${chosen}"`);
209
+ entry.reasoning = {
210
+ thought: question,
211
+ options,
212
+ chosen,
213
+ confidence: confidence ?? 0.5,
214
+ why,
215
+ };
216
+ this.emit(entry);
217
+ }
218
+ // ════════════════════════════════════════════════════════════════════
219
+ // Parallel Agent Tracking
220
+ // ════════════════════════════════════════════════════════════════════
221
+ /**
222
+ * Create a child logger for a parallel agent.
223
+ * The child has its own in-memory buffer and optionally a separate log file.
224
+ *
225
+ * @param agentId - Unique ID for the child agent
226
+ * @param task - What this agent is doing
227
+ * @param groupId - DAG execution group ID
228
+ * @param agentIndex - Agent index within the group
229
+ * @param totalAgents - Total agents in the group
230
+ * @returns A new AgentLogger for the child agent
231
+ */
232
+ createChildLogger(agentId, task, groupId, agentIndex, totalAgents) {
233
+ const childOutputs = [{ type: "memory" }];
234
+ // Add file output if logDir is set and separateParallelLogs is on
235
+ if (this.logDir && this.separateParallelLogs) {
236
+ const filePath = join(this.logDir, `agent-${agentId}.jsonl`);
237
+ childOutputs.push({ type: "file", path: filePath });
238
+ }
239
+ // Forward callbacks from parent
240
+ for (const output of this.outputs) {
241
+ if (output.type === "callback") {
242
+ childOutputs.push(output);
243
+ }
244
+ if (output.type === "console") {
245
+ childOutputs.push(output);
246
+ }
247
+ }
248
+ const child = new AgentLogger({
249
+ sessionId: this.sessionId,
250
+ agentId,
251
+ parentAgentId: this.agentId ?? this.sessionId,
252
+ level: this.level,
253
+ outputs: childOutputs,
254
+ fireOncePerLayer: this.fireOncePerLayer,
255
+ includeReasoning: this.includeReasoning,
256
+ includeParallel: this.includeParallel,
257
+ maxLogSize: this.maxLogSize,
258
+ logDir: this.logDir,
259
+ separateParallelLogs: this.separateParallelLogs,
260
+ });
261
+ this.children.set(agentId, child);
262
+ // Log spawn on the parent
263
+ this.logParallelSpawn(groupId, agentId, task, agentIndex, totalAgents);
264
+ return child;
265
+ }
266
+ /**
267
+ * Log parallel agent spawning.
268
+ */
269
+ logParallelSpawn(groupId, agentId, task, index, total) {
270
+ if (!this.includeParallel)
271
+ return;
272
+ const entry = this.createEntry("info", "parallel", `Agent[${index}]: "${task}"`);
273
+ entry.parallel = {
274
+ groupId,
275
+ agentIndex: index,
276
+ totalAgents: total,
277
+ task,
278
+ status: "spawned",
279
+ };
280
+ entry.agentId = agentId;
281
+ this.emit(entry);
282
+ }
283
+ /**
284
+ * Log parallel agent completion.
285
+ */
286
+ logParallelComplete(groupId, agentId, result, tokens) {
287
+ if (!this.includeParallel)
288
+ return;
289
+ const totalTokens = tokens ? tokens.input + tokens.output : 0;
290
+ const entry = this.createEntry("info", "parallel", `Agent ${agentId}: completed (${totalTokens.toLocaleString()} tokens)`);
291
+ entry.parallel = {
292
+ groupId,
293
+ agentIndex: 0,
294
+ totalAgents: 0,
295
+ task: result,
296
+ status: "completed",
297
+ };
298
+ entry.agentId = agentId;
299
+ if (tokens)
300
+ entry.tokens = tokens;
301
+ this.emit(entry);
302
+ }
303
+ /**
304
+ * Log parallel agent failure.
305
+ */
306
+ logParallelFailed(groupId, agentId, error) {
307
+ if (!this.includeParallel)
308
+ return;
309
+ const entry = this.createEntry("error", "parallel", `Agent ${agentId}: FAILED — ${error}`);
310
+ entry.parallel = {
311
+ groupId,
312
+ agentIndex: 0,
313
+ totalAgents: 0,
314
+ task: error,
315
+ status: "failed",
316
+ };
317
+ entry.agentId = agentId;
318
+ this.emit(entry);
319
+ }
320
+ // ════════════════════════════════════════════════════════════════════
321
+ // Input / Output
322
+ // ════════════════════════════════════════════════════════════════════
323
+ /**
324
+ * Log user input — the very first thing on every request.
325
+ *
326
+ * @param goal - The user's goal
327
+ * @param mode - Agent mode (e.g., "code", "architect")
328
+ * @param model - Model being used
329
+ */
330
+ logInput(goal, mode, model) {
331
+ const parts = [`Goal: "${goal}"`];
332
+ if (mode)
333
+ parts.push(`mode=${mode}`);
334
+ if (model)
335
+ parts.push(`model=${model}`);
336
+ const entry = this.createEntry("info", "input", parts.join(", "));
337
+ entry.data = { goal, mode, model };
338
+ this.emit(entry);
339
+ }
340
+ /**
341
+ * Log final output — the last entry for a request.
342
+ *
343
+ * @param result - Summary of the result
344
+ * @param success - Whether the agent succeeded
345
+ * @param tokens - Total token usage
346
+ */
347
+ logOutput(result, success, tokens) {
348
+ const totalTokens = tokens ? tokens.input + tokens.output : 0;
349
+ const status = success ? "Success" : "Failed";
350
+ const msg = `Output: ${status} (${totalTokens.toLocaleString()} tokens)`;
351
+ const entry = this.createEntry("info", "input", msg);
352
+ entry.data = { result, success };
353
+ if (tokens)
354
+ entry.tokens = tokens;
355
+ this.emit(entry);
356
+ }
357
+ // ════════════════════════════════════════════════════════════════════
358
+ // Tool Tracking
359
+ // ════════════════════════════════════════════════════════════════════
360
+ /** Log a tool call (before execution) */
361
+ logToolCall(toolName, input) {
362
+ const entry = this.createEntry("info", "tool", `Calling ${toolName}`);
363
+ entry.data = { toolName, input };
364
+ this.emit(entry);
365
+ }
366
+ /** Log a tool result (after execution) */
367
+ logToolResult(toolName, output, durationMs, success) {
368
+ const status = success ? "OK" : "FAILED";
369
+ const entry = this.createEntry(success ? "info" : "error", "tool", `${toolName} ${status} (${durationMs}ms)`);
370
+ entry.data = { toolName, output: output.slice(0, 500), success };
371
+ entry.durationMs = durationMs;
372
+ this.emit(entry);
373
+ }
374
+ // ════════════════════════════════════════════════════════════════════
375
+ // Conflict Tracking
376
+ // ════════════════════════════════════════════════════════════════════
377
+ /** Log a detected conflict between agents/files */
378
+ logConflict(type, fileA, fileB, severity) {
379
+ const entry = this.createEntry(severity === "critical" ? "error" : "warn", "conflict", `Conflict [${type}]: ${fileA} <-> ${fileB} (${severity})`);
380
+ entry.data = { type, fileA, fileB, severity };
381
+ this.emit(entry);
382
+ }
383
+ /** Log conflict resolution */
384
+ logConflictResolved(strategy, result) {
385
+ const entry = this.createEntry("info", "conflict", `Resolved via ${strategy}: ${result}`);
386
+ entry.data = { strategy, result };
387
+ this.emit(entry);
388
+ }
389
+ // ════════════════════════════════════════════════════════════════════
390
+ // Reflection Tracking
391
+ // ════════════════════════════════════════════════════════════════════
392
+ /** Log self-reflection result */
393
+ logReflection(verdict, score, issues) {
394
+ const entry = this.createEntry("info", "reflection", `Verdict: ${verdict} (score: ${score})`);
395
+ entry.data = { verdict, score, issues };
396
+ this.emit(entry);
397
+ }
398
+ // ════════════════════════════════════════════════════════════════════
399
+ // Query & Read Back
400
+ // ════════════════════════════════════════════════════════════════════
401
+ /**
402
+ * Get log entries, optionally filtered by a query.
403
+ *
404
+ * @param query - Filter criteria
405
+ * @returns Matching log entries
406
+ */
407
+ getEntries(query) {
408
+ let result = this.entries;
409
+ if (query) {
410
+ result = result.filter((e) => {
411
+ if (query.sessionId && e.sessionId !== query.sessionId)
412
+ return false;
413
+ if (query.agentId && e.agentId !== query.agentId)
414
+ return false;
415
+ if (query.category && e.category !== query.category)
416
+ return false;
417
+ if (query.level && LOG_LEVEL_ORDER[e.level] < LOG_LEVEL_ORDER[query.level])
418
+ return false;
419
+ if (query.layer && e.layer !== query.layer)
420
+ return false;
421
+ if (query.fromTimestamp && e.timestamp < query.fromTimestamp)
422
+ return false;
423
+ if (query.toTimestamp && e.timestamp > query.toTimestamp)
424
+ return false;
425
+ return true;
426
+ });
427
+ const offset = query.offset ?? 0;
428
+ const limit = query.limit ?? result.length;
429
+ result = result.slice(offset, offset + limit);
430
+ }
431
+ return result;
432
+ }
433
+ /**
434
+ * Get formatted, human-readable text output.
435
+ * Uses tree-drawing characters to visualize layer nesting.
436
+ *
437
+ * @param query - Optional filter
438
+ * @returns Formatted multi-line string
439
+ */
440
+ getFormattedLog(query) {
441
+ const entries = this.getEntries(query);
442
+ return entries.map((e) => this.formatEntry(e)).join("\n");
443
+ }
444
+ /**
445
+ * Get a summary overview of the logging session.
446
+ */
447
+ getSummary() {
448
+ const layerMap = new Map();
449
+ const decisions = [];
450
+ const parallelMap = new Map();
451
+ const errors = [];
452
+ let totalInput = 0;
453
+ let totalOutput = 0;
454
+ for (const entry of this.entries) {
455
+ // Layer tracking
456
+ if (entry.layer) {
457
+ const existing = layerMap.get(entry.layer);
458
+ if (!existing) {
459
+ layerMap.set(entry.layer, {
460
+ name: entry.layer,
461
+ enteredAt: entry.timestamp,
462
+ exitedAt: entry.durationMs != null ? entry.timestamp : undefined,
463
+ duration: entry.durationMs,
464
+ entriesCount: 1,
465
+ });
466
+ }
467
+ else {
468
+ existing.entriesCount++;
469
+ if (entry.durationMs != null) {
470
+ existing.exitedAt = entry.timestamp;
471
+ existing.duration = entry.durationMs;
472
+ }
473
+ }
474
+ }
475
+ // Decision tracking
476
+ if (entry.category === "decision" && entry.reasoning) {
477
+ decisions.push({
478
+ question: entry.reasoning.thought,
479
+ chosen: entry.reasoning.chosen ?? "",
480
+ confidence: entry.reasoning.confidence ?? 0,
481
+ });
482
+ }
483
+ // Parallel tracking
484
+ if (entry.parallel) {
485
+ const gid = entry.parallel.groupId;
486
+ if (!parallelMap.has(gid)) {
487
+ parallelMap.set(gid, { agents: 0, completed: 0, failed: 0 });
488
+ }
489
+ const pg = parallelMap.get(gid);
490
+ if (entry.parallel.status === "spawned")
491
+ pg.agents++;
492
+ if (entry.parallel.status === "completed")
493
+ pg.completed++;
494
+ if (entry.parallel.status === "failed")
495
+ pg.failed++;
496
+ }
497
+ // Errors
498
+ if (entry.level === "error" || entry.level === "fatal") {
499
+ errors.push(entry);
500
+ }
501
+ // Tokens
502
+ if (entry.tokens) {
503
+ totalInput += entry.tokens.input;
504
+ totalOutput += entry.tokens.output;
505
+ }
506
+ }
507
+ const firstTs = this.entries.length > 0 ? this.entries[0].timestamp : Date.now();
508
+ const lastTs = this.entries.length > 0 ? this.entries[this.entries.length - 1].timestamp : Date.now();
509
+ return {
510
+ sessionId: this.sessionId,
511
+ totalEntries: this.entries.length,
512
+ duration: lastTs - firstTs,
513
+ layers: Array.from(layerMap.values()),
514
+ decisions,
515
+ parallelGroups: Array.from(parallelMap.entries()).map(([groupId, v]) => ({
516
+ groupId,
517
+ ...v,
518
+ })),
519
+ errors,
520
+ tokenUsage: { input: totalInput, output: totalOutput },
521
+ };
522
+ }
523
+ /**
524
+ * Get only decision-type log entries (the decision trail).
525
+ */
526
+ getDecisionTrail() {
527
+ return this.entries.filter((e) => e.category === "decision");
528
+ }
529
+ /**
530
+ * Get parallel agent log entries from this logger and all children.
531
+ *
532
+ * @param groupId - Optional filter by group ID
533
+ */
534
+ getParallelLogs(groupId) {
535
+ const ownParallel = this.entries.filter((e) => {
536
+ if (e.category !== "parallel")
537
+ return false;
538
+ if (groupId && e.parallel?.groupId !== groupId)
539
+ return false;
540
+ return true;
541
+ });
542
+ // Collect from children
543
+ const childEntries = [];
544
+ for (const child of this.children.values()) {
545
+ const childAll = child.getEntries();
546
+ for (const entry of childAll) {
547
+ if (groupId && entry.parallel?.groupId !== groupId)
548
+ continue;
549
+ childEntries.push(entry);
550
+ }
551
+ }
552
+ return [...ownParallel, ...childEntries].sort((a, b) => a.timestamp - b.timestamp);
553
+ }
554
+ /**
555
+ * Get error and fatal entries only.
556
+ */
557
+ getErrors() {
558
+ return this.entries.filter((e) => e.level === "error" || e.level === "fatal");
559
+ }
560
+ // ════════════════════════════════════════════════════════════════════
561
+ // File Output
562
+ // ════════════════════════════════════════════════════════════════════
563
+ /**
564
+ * Flush all in-memory entries to file outputs.
565
+ * Appends JSONL to file outputs defined in the config.
566
+ */
567
+ async flush() {
568
+ // Only flush entries that haven't been flushed yet
569
+ const unflushed = this.entries.slice(this.flushedCount);
570
+ if (unflushed.length === 0 && this.children.size === 0)
571
+ return;
572
+ for (const output of this.outputs) {
573
+ if (output.type === "file" && unflushed.length > 0) {
574
+ this.ensureDir(dirname(output.path));
575
+ const lines = unflushed.map((e) => JSON.stringify(e)).join("\n") + "\n";
576
+ appendFileSync(output.path, lines, "utf-8");
577
+ }
578
+ }
579
+ this.flushedCount = this.entries.length;
580
+ // Flush children
581
+ for (const child of this.children.values()) {
582
+ await child.flush();
583
+ }
584
+ }
585
+ /**
586
+ * Write the full session log to a specific file path.
587
+ * Writes both JSONL (machine) and .txt (human-readable) versions.
588
+ *
589
+ * @param filePath - Base file path (will create .jsonl and .txt)
590
+ */
591
+ async writeToFile(filePath) {
592
+ this.ensureDir(dirname(filePath));
593
+ // JSONL version
594
+ const jsonl = this.entries.map((e) => JSON.stringify(e)).join("\n") + "\n";
595
+ writeFileSync(filePath, jsonl, "utf-8");
596
+ // Human-readable text version
597
+ const txtPath = filePath.replace(/\.\w+$/, ".txt");
598
+ if (txtPath !== filePath) {
599
+ const formatted = this.getFormattedLog();
600
+ writeFileSync(txtPath, formatted, "utf-8");
601
+ }
602
+ }
603
+ // ════════════════════════════════════════════════════════════════════
604
+ // Cleanup
605
+ // ════════════════════════════════════════════════════════════════════
606
+ /** Clear all log entries and reset layer tracking */
607
+ clear() {
608
+ this.entries = [];
609
+ this.enteredLayers.clear();
610
+ this.layerStack = [];
611
+ this.idCounter = 0;
612
+ for (const child of this.children.values()) {
613
+ child.clear();
614
+ }
615
+ this.children.clear();
616
+ }
617
+ /**
618
+ * Prune entries to keep only the most recent N.
619
+ *
620
+ * @param keepLast - Number of entries to keep (default: maxLogSize / 2)
621
+ * @returns Number of entries pruned
622
+ */
623
+ prune(keepLast) {
624
+ const keep = keepLast ?? Math.floor(this.maxLogSize / 2);
625
+ if (this.entries.length <= keep)
626
+ return 0;
627
+ const pruned = this.entries.length - keep;
628
+ this.entries = this.entries.slice(-keep);
629
+ return pruned;
630
+ }
631
+ // ════════════════════════════════════════════════════════════════════
632
+ // Private
633
+ // ════════════════════════════════════════════════════════════════════
634
+ /**
635
+ * Core log method — creates an entry and emits to all outputs.
636
+ */
637
+ log(level, category, message, data) {
638
+ if (!this.shouldLog(level))
639
+ return;
640
+ const entry = this.createEntry(level, category, message);
641
+ if (data)
642
+ entry.data = data;
643
+ this.emit(entry);
644
+ }
645
+ /**
646
+ * Create a new LogEntry with session/agent metadata.
647
+ */
648
+ createEntry(level, category, message) {
649
+ const entry = {
650
+ id: `log-${this.idCounter++}-${randomUUID().slice(0, 8)}`,
651
+ timestamp: Date.now(),
652
+ level,
653
+ category,
654
+ sessionId: this.sessionId,
655
+ message,
656
+ };
657
+ if (this.agentId)
658
+ entry.agentId = this.agentId;
659
+ if (this.parentAgentId)
660
+ entry.parentAgentId = this.parentAgentId;
661
+ // Attach current layer context
662
+ if (this.layerStack.length > 0) {
663
+ entry.layer = this.layerStack[this.layerStack.length - 1];
664
+ entry.layerDepth = this.layerStack.length - 1;
665
+ }
666
+ return entry;
667
+ }
668
+ /**
669
+ * Check if a given level meets the minimum threshold.
670
+ */
671
+ shouldLog(level) {
672
+ return LOG_LEVEL_ORDER[level] >= LOG_LEVEL_ORDER[this.level];
673
+ }
674
+ /**
675
+ * Emit a log entry to all configured outputs.
676
+ */
677
+ emit(entry) {
678
+ // Store in memory
679
+ const hasMemory = this.outputs.some((o) => o.type === "memory");
680
+ if (hasMemory) {
681
+ this.entries.push(entry);
682
+ // Enforce max size
683
+ if (this.entries.length > this.maxLogSize) {
684
+ this.entries = this.entries.slice(-Math.floor(this.maxLogSize * 0.8));
685
+ }
686
+ }
687
+ // Send to other outputs
688
+ for (const output of this.outputs) {
689
+ switch (output.type) {
690
+ case "memory":
691
+ // Already handled above
692
+ break;
693
+ case "file":
694
+ try {
695
+ this.ensureDir(dirname(output.path));
696
+ appendFileSync(output.path, JSON.stringify(entry) + "\n", "utf-8");
697
+ }
698
+ catch {
699
+ // Silently fail file writes to avoid crashing the agent
700
+ }
701
+ break;
702
+ case "console":
703
+ process.stderr.write((output.colorize ? this.formatEntryColor(entry) : this.formatEntry(entry)) + "\n");
704
+ break;
705
+ case "callback":
706
+ try {
707
+ output.fn(entry);
708
+ }
709
+ catch {
710
+ // Silently fail callbacks
711
+ }
712
+ break;
713
+ }
714
+ }
715
+ }
716
+ /**
717
+ * Format a single log entry as a human-readable line with tree characters.
718
+ */
719
+ formatEntry(entry) {
720
+ const time = this.formatTime(entry.timestamp);
721
+ const level = LOG_LEVEL_LABELS[entry.level].padEnd(5);
722
+ const cat = entry.category;
723
+ // Determine prefix based on category and content
724
+ let prefix = "";
725
+ let body = entry.message;
726
+ if (entry.category === "input") {
727
+ // Input/output markers
728
+ if (entry.message.startsWith("Output:")) {
729
+ prefix = "\u25C0 ";
730
+ }
731
+ else {
732
+ prefix = "\u25B6 ";
733
+ }
734
+ }
735
+ else if (entry.category === "layer") {
736
+ // Layer entry/exit with box-drawing
737
+ if (entry.durationMs != null) {
738
+ prefix = "\u2514\u2500 ";
739
+ }
740
+ else {
741
+ prefix = "\u250C\u2500 ";
742
+ }
743
+ }
744
+ else if (entry.category === "parallel") {
745
+ if (entry.parallel?.status === "spawned" && entry.message.includes("Agent[0]")) {
746
+ prefix = "\u2502 \u250C\u2500 ";
747
+ }
748
+ else if (entry.parallel?.status === "completed" || entry.parallel?.status === "failed") {
749
+ const icon = entry.parallel.status === "completed" ? "\u2705" : "\u274C";
750
+ prefix = `\u2502 \u2502 ${icon} `;
751
+ }
752
+ else {
753
+ prefix = "\u2502 \u2502 ";
754
+ }
755
+ }
756
+ else if (entry.category === "decision" && entry.reasoning) {
757
+ // Decision with indented details
758
+ const opts = entry.reasoning.options?.join(", ") ?? "";
759
+ const conf = entry.reasoning.confidence != null
760
+ ? ` (confidence: ${entry.reasoning.confidence.toFixed(2)})`
761
+ : "";
762
+ body = `Decision: "${entry.reasoning.chosen}"`;
763
+ const lines = [
764
+ `[${time}] [${level}] [${cat}] \u2502 ${body}`,
765
+ `\u2502 Options: [${opts}]`,
766
+ `\u2502 Chosen: ${entry.reasoning.chosen}${conf}`,
767
+ ];
768
+ if (entry.reasoning.why) {
769
+ lines.push(`\u2502 Why: "${entry.reasoning.why}"`);
770
+ }
771
+ return lines.join("\n");
772
+ }
773
+ else {
774
+ // Inside a layer — use pipe prefix
775
+ if (this.layerStack.length > 0 || entry.layer) {
776
+ prefix = "\u2502 ";
777
+ }
778
+ }
779
+ return `[${time}] [${level}] [${cat}] ${prefix}${body}`;
780
+ }
781
+ /**
782
+ * Format a log entry with ANSI colors for console output.
783
+ */
784
+ formatEntryColor(entry) {
785
+ const plain = this.formatEntry(entry);
786
+ const color = LEVEL_COLORS[entry.level] ?? "";
787
+ return `${color}${plain}${COLORS.reset}`;
788
+ }
789
+ /**
790
+ * Format epoch ms to HH:MM:SS.mmm
791
+ */
792
+ formatTime(timestamp) {
793
+ const d = new Date(timestamp);
794
+ const h = String(d.getHours()).padStart(2, "0");
795
+ const m = String(d.getMinutes()).padStart(2, "0");
796
+ const s = String(d.getSeconds()).padStart(2, "0");
797
+ const ms = String(d.getMilliseconds()).padStart(3, "0");
798
+ return `${h}:${m}:${s}.${ms}`;
799
+ }
800
+ /**
801
+ * Ensure a directory exists, creating it recursively if needed.
802
+ */
803
+ ensureDir(dir) {
804
+ try {
805
+ mkdirSync(dir, { recursive: true });
806
+ }
807
+ catch {
808
+ // Ignore — directory may already exist
809
+ }
810
+ }
811
+ /**
812
+ * Get the log file path for a given agent ID.
813
+ */
814
+ getLogFilePath(agentId) {
815
+ const dir = this.logDir ?? "/tmp/yuan-logs";
816
+ const name = agentId ? `agent-${agentId}` : `session-${this.sessionId}`;
817
+ return join(dir, `${name}.jsonl`);
818
+ }
819
+ }
820
+ //# sourceMappingURL=agent-logger.js.map