@brutalist/mcp 1.8.1 → 1.9.1

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 (118) hide show
  1. package/README.md +32 -0
  2. package/dist/brutalist-server.d.ts +31 -9
  3. package/dist/brutalist-server.d.ts.map +1 -1
  4. package/dist/brutalist-server.js +107 -673
  5. package/dist/brutalist-server.js.map +1 -1
  6. package/dist/cli-adapters/claude-adapter.d.ts +25 -0
  7. package/dist/cli-adapters/claude-adapter.d.ts.map +1 -0
  8. package/dist/cli-adapters/claude-adapter.js +245 -0
  9. package/dist/cli-adapters/claude-adapter.js.map +1 -0
  10. package/dist/cli-adapters/codex-adapter.d.ts +23 -0
  11. package/dist/cli-adapters/codex-adapter.d.ts.map +1 -0
  12. package/dist/cli-adapters/codex-adapter.js +173 -0
  13. package/dist/cli-adapters/codex-adapter.js.map +1 -0
  14. package/dist/cli-adapters/gemini-adapter.d.ts +50 -0
  15. package/dist/cli-adapters/gemini-adapter.d.ts.map +1 -0
  16. package/dist/cli-adapters/gemini-adapter.js +196 -0
  17. package/dist/cli-adapters/gemini-adapter.js.map +1 -0
  18. package/dist/cli-adapters/index.d.ts +75 -0
  19. package/dist/cli-adapters/index.d.ts.map +1 -0
  20. package/dist/cli-adapters/index.js +29 -0
  21. package/dist/cli-adapters/index.js.map +1 -0
  22. package/dist/cli-adapters/shared.d.ts +12 -0
  23. package/dist/cli-adapters/shared.d.ts.map +1 -0
  24. package/dist/cli-adapters/shared.js +99 -0
  25. package/dist/cli-adapters/shared.js.map +1 -0
  26. package/dist/cli-agents.d.ts +69 -2
  27. package/dist/cli-agents.d.ts.map +1 -1
  28. package/dist/cli-agents.js +358 -394
  29. package/dist/cli-agents.js.map +1 -1
  30. package/dist/debate/constitutional.d.ts +27 -0
  31. package/dist/debate/constitutional.d.ts.map +1 -0
  32. package/dist/debate/constitutional.js +74 -0
  33. package/dist/debate/constitutional.js.map +1 -0
  34. package/dist/debate/debate-orchestrator.d.ts +154 -0
  35. package/dist/debate/debate-orchestrator.d.ts.map +1 -0
  36. package/dist/debate/debate-orchestrator.js +699 -0
  37. package/dist/debate/debate-orchestrator.js.map +1 -0
  38. package/dist/debate/index.d.ts +18 -0
  39. package/dist/debate/index.d.ts.map +1 -0
  40. package/dist/debate/index.js +18 -0
  41. package/dist/debate/index.js.map +1 -0
  42. package/dist/debate/refusal-detection.d.ts +27 -0
  43. package/dist/debate/refusal-detection.d.ts.map +1 -0
  44. package/dist/debate/refusal-detection.js +62 -0
  45. package/dist/debate/refusal-detection.js.map +1 -0
  46. package/dist/debate/synthesis.d.ts +22 -0
  47. package/dist/debate/synthesis.d.ts.map +1 -0
  48. package/dist/debate/synthesis.js +117 -0
  49. package/dist/debate/synthesis.js.map +1 -0
  50. package/dist/logger.d.ts +204 -1
  51. package/dist/logger.d.ts.map +1 -1
  52. package/dist/logger.js +398 -18
  53. package/dist/logger.js.map +1 -1
  54. package/dist/metrics/counter.d.ts +24 -0
  55. package/dist/metrics/counter.d.ts.map +1 -0
  56. package/dist/metrics/counter.js +60 -0
  57. package/dist/metrics/counter.js.map +1 -0
  58. package/dist/metrics/histogram.d.ts +42 -0
  59. package/dist/metrics/histogram.d.ts.map +1 -0
  60. package/dist/metrics/histogram.js +114 -0
  61. package/dist/metrics/histogram.js.map +1 -0
  62. package/dist/metrics/index.d.ts +26 -0
  63. package/dist/metrics/index.d.ts.map +1 -0
  64. package/dist/metrics/index.js +22 -0
  65. package/dist/metrics/index.js.map +1 -0
  66. package/dist/metrics/registry.d.ts +96 -0
  67. package/dist/metrics/registry.d.ts.map +1 -0
  68. package/dist/metrics/registry.js +113 -0
  69. package/dist/metrics/registry.js.map +1 -0
  70. package/dist/metrics/safe-metric.d.ts +25 -0
  71. package/dist/metrics/safe-metric.d.ts.map +1 -0
  72. package/dist/metrics/safe-metric.js +41 -0
  73. package/dist/metrics/safe-metric.js.map +1 -0
  74. package/dist/metrics/types.d.ts +82 -0
  75. package/dist/metrics/types.d.ts.map +1 -0
  76. package/dist/metrics/types.js +121 -0
  77. package/dist/metrics/types.js.map +1 -0
  78. package/dist/registry/argument-spaces.d.ts.map +1 -1
  79. package/dist/registry/argument-spaces.js +20 -0
  80. package/dist/registry/argument-spaces.js.map +1 -1
  81. package/dist/registry/domains.d.ts.map +1 -1
  82. package/dist/registry/domains.js +17 -1
  83. package/dist/registry/domains.js.map +1 -1
  84. package/dist/streaming/circuit-breaker.d.ts +13 -1
  85. package/dist/streaming/circuit-breaker.d.ts.map +1 -1
  86. package/dist/streaming/circuit-breaker.js +13 -1
  87. package/dist/streaming/circuit-breaker.js.map +1 -1
  88. package/dist/streaming/intelligent-buffer.d.ts +13 -1
  89. package/dist/streaming/intelligent-buffer.d.ts.map +1 -1
  90. package/dist/streaming/intelligent-buffer.js +13 -1
  91. package/dist/streaming/intelligent-buffer.js.map +1 -1
  92. package/dist/streaming/output-parser.d.ts +16 -2
  93. package/dist/streaming/output-parser.d.ts.map +1 -1
  94. package/dist/streaming/output-parser.js +16 -2
  95. package/dist/streaming/output-parser.js.map +1 -1
  96. package/dist/streaming/progress-tracker.d.ts +14 -1
  97. package/dist/streaming/progress-tracker.d.ts.map +1 -1
  98. package/dist/streaming/progress-tracker.js +14 -1
  99. package/dist/streaming/progress-tracker.js.map +1 -1
  100. package/dist/streaming/session-manager.d.ts +14 -1
  101. package/dist/streaming/session-manager.d.ts.map +1 -1
  102. package/dist/streaming/session-manager.js +14 -1
  103. package/dist/streaming/session-manager.js.map +1 -1
  104. package/dist/streaming/sse-transport.d.ts +12 -1
  105. package/dist/streaming/sse-transport.d.ts.map +1 -1
  106. package/dist/streaming/sse-transport.js +12 -1
  107. package/dist/streaming/sse-transport.js.map +1 -1
  108. package/dist/streaming/streaming-orchestrator.d.ts +15 -1
  109. package/dist/streaming/streaming-orchestrator.d.ts.map +1 -1
  110. package/dist/streaming/streaming-orchestrator.js +15 -1
  111. package/dist/streaming/streaming-orchestrator.js.map +1 -1
  112. package/dist/system-prompts.d.ts.map +1 -1
  113. package/dist/system-prompts.js +490 -4
  114. package/dist/system-prompts.js.map +1 -1
  115. package/dist/tool-definitions-generated.d.ts.map +1 -1
  116. package/dist/tool-definitions-generated.js +3 -1
  117. package/dist/tool-definitions-generated.js.map +1 -1
  118. package/package.json +1 -1
package/dist/logger.js CHANGED
@@ -21,9 +21,147 @@ import { homedir } from 'os';
21
21
  * BRUTALIST_LOG_MAX_SIZE – max MB per file (default 5)
22
22
  * BRUTALIST_LOG_MAX_FILES – rotated files to keep (default 3)
23
23
  * BRUTALIST_LOG_LEVEL – minimum file log level (default "info")
24
+ *
25
+ * Structured fields (intents.md #3):
26
+ * Every call carries {ts, level, msg, pid}. Callers that use
27
+ * `logger.for({module, operation})` additionally bind `module` and
28
+ * `operation` so records are queryable by subsystem without reading
29
+ * source. Base methods remain source-compatible with pre-migration
30
+ * call sites — module/operation are simply absent on unbound calls.
24
31
  */
25
32
  const LOG_LEVELS = { debug: 0, info: 1, warn: 2, error: 3 };
26
33
  const LOG_FILENAME = 'brutalist.log';
34
+ /** Max length for sanitized scope fields on the stderr prefix (RL4). */
35
+ const STDERR_SCOPE_MAX_LEN = 64;
36
+ /** Placeholder written when JSON serialization throws (RL3). */
37
+ const UNSERIALIZABLE_PLACEHOLDER = '[unserializable]';
38
+ /**
39
+ * Safely stringify arbitrary user-supplied data. JSON.stringify throws on
40
+ * circular structures and on objects with toJSON methods that throw —
41
+ * callers into the logger must never have their requests killed by a
42
+ * logging call, so on failure we fall back to a stable placeholder.
43
+ *
44
+ * Used for stderr payload rendering (RL3). The NDJSON file path already
45
+ * wraps JSON.stringify in the writeToFile try/catch block.
46
+ */
47
+ function safeStringify(value) {
48
+ try {
49
+ return JSON.stringify(value);
50
+ }
51
+ catch {
52
+ return UNSERIALIZABLE_PLACEHOLDER;
53
+ }
54
+ }
55
+ /**
56
+ * Sanitize a scope field (module/operation) for stderr interpolation (RL4).
57
+ *
58
+ * Callers of the logger can pass attacker-influenced strings into module
59
+ * or operation (tool names, provider IDs, error categories bound at
60
+ * integration time). The stderr layout `[BRUTALIST MCP] LEVEL [m/o]:`
61
+ * is a flat line format, so a `\n` in a scope field forges a second
62
+ * log record, a `\x1b` injects ANSI escapes, and `]` breaks the
63
+ * delimiter. This function strips/escapes those characters and caps
64
+ * length to bound the attack surface.
65
+ *
66
+ * Applied ONLY at the stderr emission path — the NDJSON file record
67
+ * preserves the original unescaped values because JSON.stringify
68
+ * already escapes control characters safely.
69
+ *
70
+ * RL4-extension (Cycle 6): also escape C1 control chars (0x80–0x9f),
71
+ * notably U+009B CSI which some terminals interpret as an ANSI control
72
+ * introducer without a literal ESC byte.
73
+ *
74
+ * RL6 (Cycle 6): also escape `%`. The returned string becomes the first
75
+ * argument to `console.error`, which Node's `util.format` treats as a
76
+ * format string — `%j`/`%s`/`%d`/`%o` would consume subsequent
77
+ * arguments. Doubling `%` as `%%` is the standard `util.format` escape.
78
+ *
79
+ * RL7 (Cycle 6): defense-in-depth — accept null/undefined (returns '')
80
+ * so an upstream merge bug that propagates undefined through scope
81
+ * cannot crash the logger when the caller is in an error handler.
82
+ */
83
+ function sanitizeScopeForStderr(value) {
84
+ // RL7: defense-in-depth null/undefined guard — never throw from inside
85
+ // the logger because of a bad scope field (loggers run in error handlers).
86
+ if (value == null)
87
+ return '';
88
+ // Replace CR, LF, tab, ESC, and other C0 control chars (0x00-0x1f, 0x7f)
89
+ // and C1 control chars (0x80-0x9f) plus the ']' delimiter and `%`. Using
90
+ // explicit replacements so escapes stay visible rather than silently
91
+ // dropped.
92
+ let sanitized = '';
93
+ for (const ch of value) {
94
+ const code = ch.charCodeAt(0);
95
+ if (ch === ']') {
96
+ sanitized += '\\]';
97
+ }
98
+ else if (ch === '%') {
99
+ // RL6: escape per util.format convention so the prefix string
100
+ // cannot be interpreted as a format spec when used as the first
101
+ // arg to console.error.
102
+ sanitized += '%%';
103
+ }
104
+ else if (code < 0x20 || code === 0x7f || (code >= 0x80 && code <= 0x9f)) {
105
+ // Escape as \xHH so operators can see what was there.
106
+ // C0 (0x00-0x1f), DEL (0x7f), and C1 (0x80-0x9f) all hex-escaped —
107
+ // C1 includes U+009B CSI which several terminals treat as an
108
+ // ANSI control introducer without a literal ESC.
109
+ sanitized += '\\x' + code.toString(16).padStart(2, '0');
110
+ }
111
+ else {
112
+ sanitized += ch;
113
+ }
114
+ }
115
+ if (sanitized.length > STDERR_SCOPE_MAX_LEN) {
116
+ sanitized = sanitized.slice(0, STDERR_SCOPE_MAX_LEN);
117
+ }
118
+ return sanitized;
119
+ }
120
+ /**
121
+ * Sanitize the caller-provided `message` and string error payloads before
122
+ * they hit `console.error` (RL5, Cycle 6).
123
+ *
124
+ * RL4 closed the scope-field injection vector at `stderrPrefix`, but the
125
+ * `message` argument restored by RL1 (and the string-error payload in
126
+ * `emitError`) is a parallel raw channel into stderr. Without
127
+ * sanitization, a `message` containing `\n[BRUTALIST MCP] ERROR ...`
128
+ * forges an additional stderr line that downstream log scrapers will
129
+ * parse as a separate record.
130
+ *
131
+ * Distinct from `sanitizeScopeForStderr`:
132
+ * - No length cap — messages are intentionally free-form text and
133
+ * operators rely on full-message visibility for triage.
134
+ * - No `]` escape — `]` is not a delimiter inside the message body.
135
+ * - No `%` escape — `message` is the SECOND console.error argument and
136
+ * is never treated as a format string (RL6 handles the prefix, which
137
+ * is the only arg position where util.format parses specifiers).
138
+ * - CR/LF replaced with visible `\n` / `\r` rather than `\xHH` because
139
+ * these are by far the most common chars in a message body and the
140
+ * short escape keeps the stderr line readable.
141
+ * - Other C0 controls and DEL hex-escaped; C1 controls hex-escaped
142
+ * (parity with `sanitizeScopeForStderr`).
143
+ */
144
+ function sanitizeMessageForStderr(value) {
145
+ if (value == null)
146
+ return '';
147
+ let sanitized = '';
148
+ for (const ch of value) {
149
+ const code = ch.charCodeAt(0);
150
+ if (ch === '\n') {
151
+ sanitized += '\\n';
152
+ }
153
+ else if (ch === '\r') {
154
+ sanitized += '\\r';
155
+ }
156
+ else if (code < 0x20 || code === 0x7f || (code >= 0x80 && code <= 0x9f)) {
157
+ sanitized += '\\x' + code.toString(16).padStart(2, '0');
158
+ }
159
+ else {
160
+ sanitized += ch;
161
+ }
162
+ }
163
+ return sanitized;
164
+ }
27
165
  export class Logger {
28
166
  static instance;
29
167
  debugMode;
@@ -51,35 +189,191 @@ export class Logger {
51
189
  // Public API — unchanged signatures, all call sites continue to work
52
190
  // ---------------------------------------------------------------------------
53
191
  info(message, data) {
54
- console.error(`[BRUTALIST MCP] INFO: ${message}`, data ? JSON.stringify(data) : '');
55
- this.writeToFile('info', message, data);
192
+ this.emit('info', message, data);
56
193
  }
57
194
  warn(message, data) {
58
- console.error(`[BRUTALIST MCP] WARN: ${message}`, data ? JSON.stringify(data) : '');
59
- this.writeToFile('warn', message, data);
195
+ this.emit('warn', message, data);
60
196
  }
61
197
  error(message, error) {
62
- console.error(`[BRUTALIST MCP] ERROR: ${message}`, error instanceof Error ? error.message : error);
198
+ this.emitError(message, error);
199
+ }
200
+ debug(message, data) {
201
+ this.emit('debug', message, data);
202
+ }
203
+ /**
204
+ * Produce a child logger that binds `module` and `operation` to every
205
+ * record it emits. Child loggers delegate to the same file + stderr
206
+ * pipeline as the root, so `BRUTALIST_LOG_LEVEL`, `DEBUG`, and
207
+ * `shutdown()` behavior are shared.
208
+ *
209
+ * Callers should bind once per subsystem scope (e.g., in a class
210
+ * constructor) and reuse the returned logger, rather than creating a
211
+ * new child per call.
212
+ *
213
+ * RL8 (Cycle 6): signature accepts `Partial<LogScope>` to match the
214
+ * `StructuredLogger` interface, but the root `Logger` has no parent
215
+ * scope to inherit from — both `module` and `operation` must be
216
+ * provided. A missing field throws so that an integration-phase
217
+ * caller cannot silently produce a child with `undefined` fields
218
+ * (which would defeat the structured-logging guarantee).
219
+ */
220
+ for(scope) {
221
+ if (!scope.module || !scope.operation) {
222
+ throw new Error(`Logger.for() requires both module and operation at the root ` +
223
+ `(no parent scope to inherit from). Got module=${JSON.stringify(scope.module)} ` +
224
+ `operation=${JSON.stringify(scope.operation)}.`);
225
+ }
226
+ return new ScopedLogger(this, { module: scope.module, operation: scope.operation });
227
+ }
228
+ /**
229
+ * Root-level `forOperation` is intentionally a thrower: there is no
230
+ * parent module to inherit from, so the resulting child would have
231
+ * `module=undefined`. Use `logger.for({ module, operation })` to bind
232
+ * the scope at the root and `child.forOperation(op)` thereafter.
233
+ *
234
+ * This exists on the interface (RL8) so that DI-typed dependencies
235
+ * can call `forOperation` after they have been narrowed once. Calling
236
+ * it on the bare root logger is a programming error.
237
+ */
238
+ forOperation(_operation) {
239
+ throw new Error(`Logger.forOperation() cannot be called on the root logger — ` +
240
+ `bind a module first via logger.for({ module, operation }).`);
241
+ }
242
+ /** No-op kept for API compatibility. Writes are synchronous, nothing to flush. */
243
+ shutdown() {
244
+ // All writes use appendFileSync — every line is already on disk.
245
+ }
246
+ // ---------------------------------------------------------------------------
247
+ // Internal emit pipeline — shared by root methods and ScopedLogger
248
+ // ---------------------------------------------------------------------------
249
+ /**
250
+ * Emit a non-error record. Private so that the only externally visible
251
+ * methods keep their original signatures.
252
+ *
253
+ * Stderr layout (RL1): `{prefix} {message}` followed by the serialized
254
+ * data when present. The message text must appear on stderr — it is
255
+ * the guaranteed operational sink since file logging is opt-in via
256
+ * BRUTALIST_LOG_FILE=true.
257
+ *
258
+ * RL5/RL6 (Cycle 6): the prefix is sanitized for `%` (RL6) by
259
+ * `sanitizeScopeForStderr` so it cannot drive `util.format` substitution
260
+ * even though it is the first console.error argument. The caller-
261
+ * supplied `message` is sanitized via `sanitizeMessageForStderr`
262
+ * (RL5) so CR/LF in `message` cannot forge a second log line. The
263
+ * NDJSON file path receives the ORIGINAL `message` and `data`
264
+ * because JSON.stringify already escapes control chars safely.
265
+ */
266
+ /** @internal */
267
+ emit(level, message, data, scope) {
268
+ const prefix = this.stderrPrefix(level, scope);
269
+ const safeMessage = sanitizeMessageForStderr(message);
270
+ // RL3: safeStringify guards against circular structures and toJSON
271
+ // throws. If data is absent we pass through the empty string to
272
+ // preserve the two-argument call shape tests assert against.
273
+ //
274
+ // RL9 (Cycle 8): JSON.stringify escapes C0 (0x00-0x1f) natively but
275
+ // does NOT escape C1 controls (0x80-0x9f) — those emit as raw UTF-8
276
+ // bytes. Several terminals interpret U+009B CSI as an ANSI control
277
+ // introducer without a literal ESC. Pipe the stringified payload
278
+ // through `sanitizeMessageForStderr` so C1 bytes are hex-escaped
279
+ // before they reach `console.error`. The NDJSON file path below
280
+ // still receives the ORIGINAL `data` object (unsanitized) — parity
281
+ // with the RL4/RL5 stderr-vs-file split: operators consume files
282
+ // with jq/grep, raw bytes are tool-safe there; stderr is the
283
+ // channel terminals render.
284
+ const payload = data !== undefined
285
+ ? sanitizeMessageForStderr(safeStringify(data))
286
+ : '';
287
+ if (level === 'debug') {
288
+ if (this.debugMode) {
289
+ console.error(prefix, safeMessage, payload);
290
+ }
291
+ }
292
+ else {
293
+ console.error(prefix, safeMessage, payload);
294
+ }
295
+ this.writeToFile(level, message, data, scope);
296
+ }
297
+ /**
298
+ * Emit an error record. Splits stderr vs file formatting the same way
299
+ * the original `error()` did — Error instances get `.message` on
300
+ * stderr and the full shape in the NDJSON record.
301
+ *
302
+ * Stderr layout (RL1): `{prefix} {message} {error.message|payload}`.
303
+ * The caller-supplied `message` must appear alongside the error detail
304
+ * so the human-readable line is symmetric with emit().
305
+ *
306
+ * RL5 (Cycle 6): both the caller-supplied `message` and the string
307
+ * forms of the error payload (`Error.message` and string error
308
+ * arguments) are passed through `sanitizeMessageForStderr` before
309
+ * `console.error`. CR/LF in either field would otherwise forge a
310
+ * second stderr log line. Non-Error object payloads go through
311
+ * `safeStringify` (RL3) and JSON.stringify already escapes control
312
+ * chars, so they are stderr-safe without further sanitization.
313
+ *
314
+ * The file path (`writeToFile`) receives the ORIGINAL `message` and
315
+ * the structured `fileData` shape so NDJSON consumers see the raw
316
+ * values that were logged (JSON.stringify handles control char
317
+ * escaping safely on the file side).
318
+ */
319
+ /** @internal */
320
+ emitError(message, error, scope) {
321
+ const prefix = this.stderrPrefix('error', scope);
322
+ // RL3: Error.message is always a string so JSON stringify is not
323
+ // invoked. For non-Error payloads we safeStringify defensively,
324
+ // since error?: any permits arbitrary user-supplied objects.
325
+ let errPayload;
326
+ if (error instanceof Error) {
327
+ // RL5: Error.message can carry user-controlled CR/LF (e.g., a
328
+ // remote API error message echoed back into an exception).
329
+ errPayload = sanitizeMessageForStderr(error.message);
330
+ }
331
+ else if (error === undefined) {
332
+ errPayload = '';
333
+ }
334
+ else if (typeof error === 'string') {
335
+ // RL5: string error payloads are also raw caller input.
336
+ errPayload = sanitizeMessageForStderr(error);
337
+ }
338
+ else {
339
+ // RL9 (Cycle 8): JSON.stringify escapes C0 but NOT C1 controls
340
+ // (0x80-0x9f). Non-Error object payloads reach stderr via
341
+ // safeStringify, so an attacker-controlled object field like
342
+ // { csi: '\u009b2J' } would render raw bytes that some
343
+ // terminals interpret as ANSI control introducers without a
344
+ // literal ESC. Pipe the JSON through `sanitizeMessageForStderr`
345
+ // to hex-escape C1 (and any stray C0/DEL) before the byte
346
+ // hits `console.error`. File-side NDJSON below still receives
347
+ // the ORIGINAL `fileData` object — stderr-vs-file parity.
348
+ errPayload = sanitizeMessageForStderr(safeStringify(error));
349
+ }
350
+ const safeMessage = sanitizeMessageForStderr(message);
351
+ console.error(prefix, safeMessage, errPayload);
63
352
  if (this.debugMode && error instanceof Error && error.stack) {
353
+ // Stack traces include filenames and line numbers from the runtime
354
+ // and are intentionally multi-line — sanitizing them would defeat
355
+ // operator triage. The debug-mode gate is acceptable because the
356
+ // stack is only emitted in DEBUG=true, an operator-controlled path.
64
357
  console.error(error.stack);
65
358
  }
66
- // File output always gets the full error shape for post-mortem debugging
67
359
  const fileData = error instanceof Error
68
360
  ? { message: error.message, stack: error.stack, name: error.name }
69
361
  : error;
70
- this.writeToFile('error', message, fileData);
362
+ this.writeToFile('error', message, fileData, scope);
71
363
  }
72
- debug(message, data) {
73
- if (this.debugMode) {
74
- console.error(`[BRUTALIST MCP] DEBUG: ${message}`, data ? JSON.stringify(data) : '');
364
+ stderrPrefix(level, scope) {
365
+ const upper = level.toUpperCase();
366
+ if (scope) {
367
+ // Human-readable tag — keeps the original `[BRUTALIST MCP] LEVEL:`
368
+ // prefix so log scrapers still match, but adds `[module/operation]`
369
+ // so the subsystem is visible without parsing NDJSON.
370
+ // RL4: sanitize scope fields against CR/LF/ANSI/tab/control-char
371
+ // injection. The NDJSON file record preserves the originals.
372
+ const mod = sanitizeScopeForStderr(scope.module);
373
+ const op = sanitizeScopeForStderr(scope.operation);
374
+ return `[BRUTALIST MCP] ${upper} [${mod}/${op}]:`;
75
375
  }
76
- // File always receives debug lines if the configured level allows it,
77
- // regardless of the stderr debug gate
78
- this.writeToFile('debug', message, data);
79
- }
80
- /** No-op kept for API compatibility. Writes are synchronous, nothing to flush. */
81
- shutdown() {
82
- // All writes use appendFileSync — every line is already on disk.
376
+ return `[BRUTALIST MCP] ${upper}:`;
83
377
  }
84
378
  // ---------------------------------------------------------------------------
85
379
  // File logging internals
@@ -122,15 +416,21 @@ export class Logger {
122
416
  this.fileEnabled = false;
123
417
  }
124
418
  }
125
- writeToFile(level, message, data) {
419
+ writeToFile(level, message, data, scope) {
126
420
  if (!this.fileEnabled)
127
421
  return;
128
422
  if (LOG_LEVELS[level] < this.fileLogLevel)
129
423
  return;
130
424
  try {
425
+ // Field order is stable for operators grepping NDJSON:
426
+ // ts → level → module → operation → msg → data → pid.
427
+ // `module`/`operation` are omitted entirely when no scope is bound
428
+ // so pre-migration call sites produce exactly the same record as
429
+ // before.
131
430
  const entry = {
132
431
  ts: new Date().toISOString(),
133
432
  level,
433
+ ...(scope && { module: scope.module, operation: scope.operation }),
134
434
  msg: message,
135
435
  ...(data !== undefined && { data }),
136
436
  pid: this.pid
@@ -194,5 +494,85 @@ export class Logger {
194
494
  console.error(`[BRUTALIST MCP] WARN: File logging disabled (${reason})`);
195
495
  }
196
496
  }
497
+ /**
498
+ * Scoped child logger returned by `Logger.for(...)`. Binds `module` and
499
+ * `operation` to every record so integration-time call sites only need
500
+ * to create the child once per subsystem, not thread the scope through
501
+ * every call.
502
+ *
503
+ * Delegates to the root Logger for the actual stderr + file write so
504
+ * all env-var behavior (BRUTALIST_LOG_LEVEL, DEBUG, BRUTALIST_LOG_FILE,
505
+ * rotation, shutdown) is shared and there is no duplicated pipeline.
506
+ */
507
+ export class ScopedLogger {
508
+ root;
509
+ scope;
510
+ constructor(root, scope) {
511
+ this.root = root;
512
+ this.scope = scope;
513
+ }
514
+ info(message, data) {
515
+ this.root.emit('info', message, data, this.scope);
516
+ }
517
+ warn(message, data) {
518
+ this.root.emit('warn', message, data, this.scope);
519
+ }
520
+ error(message, error) {
521
+ this.root.emitError(message, error, this.scope);
522
+ }
523
+ debug(message, data) {
524
+ this.root.emit('debug', message, data, this.scope);
525
+ }
526
+ /**
527
+ * Narrow an existing scoped logger to a new operation while preserving
528
+ * the bound module (RL2). Accepts a partial scope so common usage like
529
+ * `.for({ operation: 'orchestrateRound' })` keeps the class-bound
530
+ * module and only overrides the operation. Passing both fields fully
531
+ * replaces the scope — making renaming the module explicit.
532
+ *
533
+ * Resolution (a): module is preserved by default. This is the shape
534
+ * integrate_observability will consume — bind module at the class
535
+ * level, bind operation per method — so typo'd modules across call
536
+ * sites can't split log streams.
537
+ *
538
+ * Method parameter type is `Partial<LogScope>` which is wider than
539
+ * `LogScope` (every `LogScope` is assignable to `Partial<LogScope>`),
540
+ * so this override still satisfies the `StructuredLogger.for`
541
+ * contract — method parameters are contravariant.
542
+ *
543
+ * RL7 (Cycle 6): explicit `undefined` is filtered so it does not
544
+ * overwrite parent fields. The previous `??` form already handled
545
+ * undefined in the override expression, but a caller writing
546
+ * `.for({ module: undefined, operation: 'x' })` would still produce
547
+ * `module: this.scope.module` (correct) — and the same caller writing
548
+ * `.for({ module: undefined })` correctly preserves the parent. The
549
+ * defense-in-depth here is making the intent explicit so future
550
+ * refactors that switch to `Object.assign`-style merge can't silently
551
+ * regress, and so `null` is also handled (TypeScript allows `null`
552
+ * to satisfy an optional field if `strictNullChecks` is off in some
553
+ * consumer). `sanitizeScopeForStderr` also has a null/undefined
554
+ * guard for paranoid defense.
555
+ */
556
+ for(scope) {
557
+ const nextModule = scope.module != null ? scope.module : this.scope.module;
558
+ const nextOperation = scope.operation != null ? scope.operation : this.scope.operation;
559
+ return new ScopedLogger(this.root, {
560
+ module: nextModule,
561
+ operation: nextOperation,
562
+ });
563
+ }
564
+ /**
565
+ * Grep-friendly shorthand for the common pattern of binding module
566
+ * at class construction and narrowing by operation per method (RL2).
567
+ * Equivalent to `.for({ operation })` but more discoverable and
568
+ * avoids accidental `.for({ module })` typos stripping the operation.
569
+ */
570
+ forOperation(operation) {
571
+ return new ScopedLogger(this.root, {
572
+ module: this.scope.module,
573
+ operation,
574
+ });
575
+ }
576
+ }
197
577
  export const logger = Logger.getInstance();
198
578
  //# sourceMappingURL=logger.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,QAAQ,EACR,UAAU,EACV,UAAU,EACV,SAAS,EACT,UAAU,EACV,QAAQ,EACR,SAAS,EACV,MAAM,IAAI,CAAC;AACZ,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,MAAM,UAAU,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAW,CAAC;AAGrE,MAAM,YAAY,GAAG,eAAe,CAAC;AAErC,wEAAwE;AACxE,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,gEAAgE;AAChE,MAAM,0BAA0B,GAAG,kBAAkB,CAAC;AAEtD;;;;;;;;GAQG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,0BAA0B,CAAC;IACpC,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,SAAS,sBAAsB,CAAC,KAAgC;IAC9D,uEAAuE;IACvE,2EAA2E;IAC3E,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,yEAAyE;IACzE,yEAAyE;IACzE,qEAAqE;IACrE,WAAW;IACX,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACf,SAAS,IAAI,KAAK,CAAC;QACrB,CAAC;aAAM,IAAI,EAAE,KAAK,GAAG,EAAE,CAAC;YACtB,8DAA8D;YAC9D,gEAAgE;YAChE,wBAAwB;YACxB,SAAS,IAAI,IAAI,CAAC;QACpB,CAAC;aAAM,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YAC1E,sDAAsD;YACtD,mEAAmE;YACnE,6DAA6D;YAC7D,iDAAiD;YACjD,SAAS,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,SAAS,IAAI,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IACD,IAAI,SAAS,CAAC,MAAM,GAAG,oBAAoB,EAAE,CAAC;QAC5C,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,oBAAoB,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,SAAS,wBAAwB,CAAC,KAAgC;IAChE,IAAI,KAAK,IAAI,IAAI;QAAE,OAAO,EAAE,CAAC;IAC7B,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,IAAI,GAAG,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC9B,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YAChB,SAAS,IAAI,KAAK,CAAC;QACrB,CAAC;aAAM,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;YACvB,SAAS,IAAI,KAAK,CAAC;QACrB,CAAC;aAAM,IAAI,IAAI,GAAG,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,CAAC;YAC1E,SAAS,IAAI,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC1D,CAAC;aAAM,CAAC;YACN,SAAS,IAAI,EAAE,CAAC;QAClB,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAyCD,MAAM,OAAO,MAAM;IACT,MAAM,CAAC,QAAQ,CAAS;IACxB,SAAS,CAAU;IAE3B,qBAAqB;IACb,WAAW,GAAY,KAAK,CAAC;IAC7B,MAAM,GAAW,EAAE,CAAC;IACpB,WAAW,GAAW,EAAE,CAAC;IACzB,WAAW,GAAW,CAAC,GAAG,IAAI,GAAG,IAAI,CAAC,CAAC,OAAO;IAC9C,QAAQ,GAAW,CAAC,CAAC;IACrB,YAAY,GAAW,UAAU,CAAC,IAAI,CAAC;IACvC,eAAe,GAAW,CAAC,CAAC;IAC5B,QAAQ,GAAY,KAAK,CAAC;IAC1B,GAAG,GAAW,OAAO,CAAC,GAAG,CAAC;IAElC;QACE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,CAAC;QACxF,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED,MAAM,CAAC,WAAW;QAChB,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YACrB,MAAM,CAAC,QAAQ,GAAG,IAAI,MAAM,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,MAAM,CAAC,QAAQ,CAAC;IACzB,CAAC;IAED,8EAA8E;IAC9E,qEAAqE;IACrE,8EAA8E;IAE9E,IAAI,CAAC,OAAe,EAAE,IAAU;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAU;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,KAAmB;QACxC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACjC,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAU;QAC/B,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACH,GAAG,CAAC,KAAwB;QAC1B,IAAI,CAAC,KAAK,CAAC,MAAM,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;YACtC,MAAM,IAAI,KAAK,CACb,8DAA8D;gBAC9D,iDAAiD,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG;gBAChF,aAAa,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAChD,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,YAAY,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;IACtF,CAAC;IAED;;;;;;;;;OASG;IACH,YAAY,CAAC,UAAkB;QAC7B,MAAM,IAAI,KAAK,CACb,8DAA8D;YAC9D,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IAED,kFAAkF;IAClF,QAAQ;QACN,iEAAiE;IACnE,CAAC;IAED,8EAA8E;IAC9E,mEAAmE;IACnE,8EAA8E;IAE9E;;;;;;;;;;;;;;;;OAgBG;IACH,gBAAgB;IAChB,IAAI,CAAC,KAAe,EAAE,OAAe,EAAE,IAAU,EAAE,KAAgB;QACjE,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAC/C,MAAM,WAAW,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;QACtD,mEAAmE;QACnE,gEAAgE;QAChE,6DAA6D;QAC7D,EAAE;QACF,oEAAoE;QACpE,oEAAoE;QACpE,mEAAmE;QACnE,iEAAiE;QACjE,iEAAiE;QACjE,gEAAgE;QAChE,mEAAmE;QACnE,iEAAiE;QACjE,6DAA6D;QAC7D,4BAA4B;QAC5B,MAAM,OAAO,GAAG,IAAI,KAAK,SAAS;YAChC,CAAC,CAAC,wBAAwB,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;YAC/C,CAAC,CAAC,EAAE,CAAC;QACP,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnB,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;QACH,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;QACD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC;IAChD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,gBAAgB;IAChB,SAAS,CAAC,OAAe,EAAE,KAAmB,EAAE,KAAgB;QAC9D,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;QACjD,iEAAiE;QACjE,gEAAgE;QAChE,6DAA6D;QAC7D,IAAI,UAAkB,CAAC;QACvB,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;YAC3B,8DAA8D;YAC9D,2DAA2D;YAC3D,UAAU,GAAG,wBAAwB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACvD,CAAC;aAAM,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YAC/B,UAAU,GAAG,EAAE,CAAC;QAClB,CAAC;aAAM,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;YACrC,wDAAwD;YACxD,UAAU,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,+DAA+D;YAC/D,0DAA0D;YAC1D,6DAA6D;YAC7D,uDAAuD;YACvD,4DAA4D;YAC5D,gEAAgE;YAChE,0DAA0D;YAC1D,8DAA8D;YAC9D,0DAA0D;YAC1D,UAAU,GAAG,wBAAwB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9D,CAAC;QACD,MAAM,WAAW,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;QACtD,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QAC/C,IAAI,IAAI,CAAC,SAAS,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC5D,mEAAmE;YACnE,kEAAkE;YAClE,iEAAiE;YACjE,oEAAoE;YACpE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7B,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,YAAY,KAAK;YACrC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE;YAClE,CAAC,CAAC,KAAK,CAAC;QACV,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;IACtD,CAAC;IAEO,YAAY,CAAC,KAAe,EAAE,KAAgB;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,IAAI,KAAK,EAAE,CAAC;YACV,mEAAmE;YACnE,oEAAoE;YACpE,sDAAsD;YACtD,iEAAiE;YACjE,6DAA6D;YAC7D,MAAM,GAAG,GAAG,sBAAsB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACjD,MAAM,EAAE,GAAG,sBAAsB,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;YACnD,OAAO,mBAAmB,KAAK,KAAK,GAAG,IAAI,EAAE,IAAI,CAAC;QACpD,CAAC;QACD,OAAO,mBAAmB,KAAK,GAAG,CAAC;IACrC,CAAC;IAED,8EAA8E;IAC9E,yBAAyB;IACzB,8EAA8E;IAEtE,eAAe;QACrB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,MAAM,CAAC;QAC1D,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,oBAAoB,KAAK,GAAG,CAAC;QAE9D,IAAI,CAAC,OAAO,IAAI,YAAY;YAAE,OAAO;QAErC,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB;mBACtC,IAAI,CAAC,OAAO,EAAE,EAAE,gBAAgB,EAAE,MAAM,CAAC,CAAC;YAE/C,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC,IAAI,CAAC,CAAC;YAClE,IAAI,CAAC,WAAW,GAAG,SAAS,GAAG,IAAI,GAAG,IAAI,CAAC;YAC3C,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,CAAC,CAAC;YAEjE,MAAM,QAAQ,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,mBAAmB,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,CAAC;YAC3E,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,QAAoB,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC;YAExE,0BAA0B;YAC1B,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAE5C,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;YAEnD,+CAA+C;YAC/C,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC;oBACH,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC;gBACzD,CAAC;gBAAC,MAAM,CAAC;oBACP,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,+DAA+D;gBAC/D,MAAM,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC;gBAC3C,SAAS,CAAC,EAAE,CAAC,CAAC;YAChB,CAAC;YAED,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qDAAqD;YACrD,OAAO,CAAC,KAAK,CACX,mDAAmD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAC9F,CAAC;YACF,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QAC3B,CAAC;IACH,CAAC;IAEO,WAAW,CAAC,KAAe,EAAE,OAAe,EAAE,IAAU,EAAE,KAAgB;QAChF,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,UAAU,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,YAAY;YAAE,OAAO;QAElD,IAAI,CAAC;YACH,uDAAuD;YACvD,sDAAsD;YACtD,mEAAmE;YACnE,iEAAiE;YACjE,UAAU;YACV,MAAM,KAAK,GAA4B;gBACrC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBAC5B,KAAK;gBACL,GAAG,CAAC,KAAK,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC;gBAClE,GAAG,EAAE,OAAO;gBACZ,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;gBACnC,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC;YAEF,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;YAC1C,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;YAE1C,0DAA0D;YAC1D,IAAI,IAAI,CAAC,eAAe,GAAG,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;gBACxD,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,CAAC;YAED,cAAc,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;YACvC,IAAI,CAAC,eAAe,IAAI,SAAS,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,IAAI,CAAC,kBAAkB,CAAC,eAAe,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED;;;;;;;OAOG;IACK,MAAM;QACZ,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;QAErB,IAAI,CAAC;YACH,+BAA+B;YAC/B,KAAK,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;gBACpD,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBACxD,IAAI,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACpB,UAAU,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;gBACvB,CAAC;YACH,CAAC;YAED,oCAAoC;YACpC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,aAAa,IAAI,CAAC,QAAQ,MAAM,CAAC,CAAC;YACnE,IAAI,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBACvB,UAAU,CAAC,MAAM,CAAC,CAAC;YACrB,CAAC;YAED,eAAe;YACf,IAAI,UAAU,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC;gBACjC,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,CAAC;YACrE,CAAC;YAED,IAAI,CAAC,eAAe,GAAG,CAAC,CAAC;QAC3B,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,kBAAkB,CAAC,oBAAoB,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QAC1F,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;IACH,CAAC;IAEO,kBAAkB,CAAC,MAAc;QACvC,IAAI,CAAC,IAAI,CAAC,WAAW;YAAE,OAAO;QAC9B,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;QACzB,OAAO,CAAC,KAAK,CAAC,gDAAgD,MAAM,GAAG,CAAC,CAAC;IAC3E,CAAC;CACF;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,YAAY;IAEJ;IACA;IAFnB,YACmB,IAAY,EACZ,KAAe;QADf,SAAI,GAAJ,IAAI,CAAQ;QACZ,UAAK,GAAL,KAAK,CAAU;IAC/B,CAAC;IAEJ,IAAI,CAAC,OAAe,EAAE,IAAU;QAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,IAAI,CAAC,OAAe,EAAE,IAAU;QAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,KAAmB;QACxC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,OAAe,EAAE,IAAU;QAC/B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACrD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,GAAG,CAAC,KAAwB;QAC1B,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;QAC3E,MAAM,aAAa,GAAG,KAAK,CAAC,SAAS,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC;QACvF,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE;YACjC,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,aAAa;SACzB,CAAC,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACH,YAAY,CAAC,SAAiB;QAC5B,OAAO,IAAI,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE;YACjC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM;YACzB,SAAS;SACV,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Counter — a monotonically increasing cumulative metric.
3
+ *
4
+ * Counters are the simplest Prometheus primitive: a numeric value that only
5
+ * goes up. Each distinct label-value combination gets its own independent
6
+ * value so labeled increments never collide.
7
+ */
8
+ import type { LabelValues, MetricDescriptor } from './types.js';
9
+ import { type LabelKey } from './types.js';
10
+ /**
11
+ * Opaque handle to a counter metric. Consumers of the module import these
12
+ * via `createMetricsRegistry()` and call `inc()` at instrumentation points.
13
+ */
14
+ export interface Counter<TLabels extends readonly string[]> {
15
+ readonly descriptor: MetricDescriptor<TLabels>;
16
+ /** Increment by 1 (default) or by an explicit non-negative delta. */
17
+ inc(labels: LabelValues<TLabels>, delta?: number): void;
18
+ /** Test-only: read current values keyed by serialized label string. */
19
+ snapshot(): ReadonlyMap<LabelKey, number>;
20
+ /** Render this counter as a Prometheus text-exposition block (without trailing newline). */
21
+ render(): string;
22
+ }
23
+ export declare function createCounter<TLabels extends readonly string[]>(descriptor: MetricDescriptor<TLabels>): Counter<TLabels>;
24
+ //# sourceMappingURL=counter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.d.ts","sourceRoot":"","sources":["../../src/metrics/counter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAA+B,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAExE;;;GAGG;AACH,MAAM,WAAW,OAAO,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IACxD,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,CAAC;IAC/C,qEAAqE;IACrE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxD,uEAAuE;IACvE,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC1C,4FAA4F;IAC5F,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,EAC7D,UAAU,EAAE,gBAAgB,CAAC,OAAO,CAAC,GACpC,OAAO,CAAC,OAAO,CAAC,CA2ClB"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Counter — a monotonically increasing cumulative metric.
3
+ *
4
+ * Counters are the simplest Prometheus primitive: a numeric value that only
5
+ * goes up. Each distinct label-value combination gets its own independent
6
+ * value so labeled increments never collide.
7
+ */
8
+ import { escapeHelp, serializeLabels } from './types.js';
9
+ export function createCounter(descriptor) {
10
+ const values = new Map();
11
+ const labelSets = new Map();
12
+ return {
13
+ descriptor,
14
+ inc(labels, delta = 1) {
15
+ if (!Number.isFinite(delta)) {
16
+ throw new Error(`metrics: counter "${descriptor.name}" delta must be finite`);
17
+ }
18
+ if (delta < 0) {
19
+ throw new Error(`metrics: counter "${descriptor.name}" delta must be >= 0`);
20
+ }
21
+ const key = serializeLabels(descriptor.labelNames, labels);
22
+ values.set(key, (values.get(key) ?? 0) + delta);
23
+ if (!labelSets.has(key)) {
24
+ // Preserve the first-seen label map for rendering (values stringified).
25
+ labelSets.set(key, labels);
26
+ }
27
+ },
28
+ snapshot() {
29
+ return new Map(values);
30
+ },
31
+ render() {
32
+ const lines = [];
33
+ lines.push(`# HELP ${descriptor.name} ${escapeHelp(descriptor.help)}`);
34
+ lines.push(`# TYPE ${descriptor.name} counter`);
35
+ if (values.size === 0) {
36
+ return lines.join('\n');
37
+ }
38
+ // Sort for deterministic output — test assertions rely on this.
39
+ const keys = Array.from(values.keys()).sort();
40
+ for (const key of keys) {
41
+ const value = values.get(key);
42
+ if (key === '') {
43
+ lines.push(`${descriptor.name} ${formatNumber(value)}`);
44
+ }
45
+ else {
46
+ lines.push(`${descriptor.name}{${key}} ${formatNumber(value)}`);
47
+ }
48
+ }
49
+ return lines.join('\n');
50
+ }
51
+ };
52
+ }
53
+ function formatNumber(value) {
54
+ if (Number.isInteger(value)) {
55
+ return value.toString();
56
+ }
57
+ // Prometheus accepts Go-style float literals; JS number.toString() is compatible.
58
+ return value.toString();
59
+ }
60
+ //# sourceMappingURL=counter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"counter.js","sourceRoot":"","sources":["../../src/metrics/counter.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,UAAU,EAAE,eAAe,EAAiB,MAAM,YAAY,CAAC;AAgBxE,MAAM,UAAU,aAAa,CAC3B,UAAqC;IAErC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC3C,MAAM,SAAS,GAAG,IAAI,GAAG,EAAkC,CAAC;IAE5D,OAAO;QACL,UAAU;QACV,GAAG,CAAC,MAA4B,EAAE,QAAgB,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC5B,MAAM,IAAI,KAAK,CAAC,qBAAqB,UAAU,CAAC,IAAI,wBAAwB,CAAC,CAAC;YAChF,CAAC;YACD,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,MAAM,IAAI,KAAK,CAAC,qBAAqB,UAAU,CAAC,IAAI,sBAAsB,CAAC,CAAC;YAC9E,CAAC;YACD,MAAM,GAAG,GAAG,eAAe,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC3D,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC;YAChD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBACxB,wEAAwE;gBACxE,SAAS,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC7B,CAAC;QACH,CAAC;QACD,QAAQ;YACN,OAAO,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,MAAM;YACJ,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,KAAK,CAAC,IAAI,CAAC,UAAU,UAAU,CAAC,IAAI,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACvE,KAAK,CAAC,IAAI,CAAC,UAAU,UAAU,CAAC,IAAI,UAAU,CAAC,CAAC;YAChD,IAAI,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACtB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;YACD,gEAAgE;YAChE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;YAC9C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACvB,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC;gBAC/B,IAAI,GAAG,KAAK,EAAE,EAAE,CAAC;oBACf,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,IAAI,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAC1D,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,IAAI,CAAC,GAAG,UAAU,CAAC,IAAI,IAAI,GAAG,KAAK,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBAClE,CAAC;YACH,CAAC;YACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;KACF,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;IAC1B,CAAC;IACD,kFAAkF;IAClF,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Histogram — a cumulative histogram of observation values.
3
+ *
4
+ * The Prometheus convention: one `_bucket` series per upper bound plus a
5
+ * mandatory `+Inf` bucket, a `_count` series, and a `_sum` series. Buckets
6
+ * are CUMULATIVE — each bucket includes every lower bucket's observations.
7
+ *
8
+ * Buckets are declared once at construction. Per-label-set state includes:
9
+ * - counts[i] — cumulative count for the i-th bucket (counts[bucketsAscending.length] = +Inf).
10
+ * - sum — cumulative sum of observed values (for quantile approximation).
11
+ * - count — total observations (equals counts[+Inf bucket]).
12
+ */
13
+ import type { LabelValues, MetricDescriptor } from './types.js';
14
+ import { type LabelKey } from './types.js';
15
+ export interface HistogramDescriptor<TLabels extends readonly string[]> extends MetricDescriptor<TLabels> {
16
+ /** Upper bounds in strictly ascending order; a `+Inf` bucket is added automatically. */
17
+ readonly buckets: readonly number[];
18
+ }
19
+ export interface Histogram<TLabels extends readonly string[]> {
20
+ readonly descriptor: HistogramDescriptor<TLabels>;
21
+ /**
22
+ * Record an observation.
23
+ *
24
+ * The value must be a FINITE, NON-NEGATIVE number. Negative values are
25
+ * rejected symmetrically with `Counter.inc()`'s rejection of negative
26
+ * deltas — callers that mis-compute a duration (e.g., `Date.now() -
27
+ * futureTimestamp`) must not silently poison bucket counts or `_sum`
28
+ * (which would break SLO math downstream). Rejection mode: throw.
29
+ */
30
+ observe(labels: LabelValues<TLabels>, value: number): void;
31
+ /** Test-only: cumulative counts per bucket, keyed by serialized label string. */
32
+ snapshot(): ReadonlyMap<LabelKey, HistogramSnapshot>;
33
+ render(): string;
34
+ }
35
+ export interface HistogramSnapshot {
36
+ /** cumulative[i] = number of observations <= buckets[i]; last entry is +Inf. */
37
+ readonly cumulative: readonly number[];
38
+ readonly sum: number;
39
+ readonly count: number;
40
+ }
41
+ export declare function createHistogram<TLabels extends readonly string[]>(descriptor: HistogramDescriptor<TLabels>): Histogram<TLabels>;
42
+ //# sourceMappingURL=histogram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"histogram.d.ts","sourceRoot":"","sources":["../../src/metrics/histogram.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAChE,OAAO,EAAiD,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE1F,MAAM,WAAW,mBAAmB,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,CACpE,SAAQ,gBAAgB,CAAC,OAAO,CAAC;IACjC,wFAAwF;IACxF,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,SAAS,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE;IAC1D,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAClD;;;;;;;;OAQG;IACH,OAAO,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3D,iFAAiF;IACjF,QAAQ,IAAI,WAAW,CAAC,QAAQ,EAAE,iBAAiB,CAAC,CAAC;IACrD,MAAM,IAAI,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,iBAAiB;IAChC,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,CAAC;IACvC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AASD,wBAAgB,eAAe,CAAC,OAAO,SAAS,SAAS,MAAM,EAAE,EAC/D,UAAU,EAAE,mBAAmB,CAAC,OAAO,CAAC,GACvC,SAAS,CAAC,OAAO,CAAC,CA0FpB"}