@lumenflow/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +119 -0
  3. package/dist/active-wu-detector.d.ts +33 -0
  4. package/dist/active-wu-detector.js +106 -0
  5. package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
  6. package/dist/adapters/filesystem-metrics.adapter.js +519 -0
  7. package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
  8. package/dist/adapters/terminal-renderer.adapter.js +337 -0
  9. package/dist/arg-parser.d.ts +63 -0
  10. package/dist/arg-parser.js +560 -0
  11. package/dist/backlog-editor.d.ts +98 -0
  12. package/dist/backlog-editor.js +179 -0
  13. package/dist/backlog-generator.d.ts +111 -0
  14. package/dist/backlog-generator.js +381 -0
  15. package/dist/backlog-parser.d.ts +45 -0
  16. package/dist/backlog-parser.js +102 -0
  17. package/dist/backlog-sync-validator.d.ts +78 -0
  18. package/dist/backlog-sync-validator.js +294 -0
  19. package/dist/branch-drift.d.ts +34 -0
  20. package/dist/branch-drift.js +51 -0
  21. package/dist/cleanup-install-config.d.ts +33 -0
  22. package/dist/cleanup-install-config.js +37 -0
  23. package/dist/cleanup-lock.d.ts +139 -0
  24. package/dist/cleanup-lock.js +313 -0
  25. package/dist/code-path-validator.d.ts +146 -0
  26. package/dist/code-path-validator.js +537 -0
  27. package/dist/code-paths-overlap.d.ts +55 -0
  28. package/dist/code-paths-overlap.js +245 -0
  29. package/dist/commands-logger.d.ts +77 -0
  30. package/dist/commands-logger.js +254 -0
  31. package/dist/commit-message-utils.d.ts +25 -0
  32. package/dist/commit-message-utils.js +41 -0
  33. package/dist/compliance-parser.d.ts +150 -0
  34. package/dist/compliance-parser.js +507 -0
  35. package/dist/constants/backlog-patterns.d.ts +20 -0
  36. package/dist/constants/backlog-patterns.js +23 -0
  37. package/dist/constants/dora-constants.d.ts +49 -0
  38. package/dist/constants/dora-constants.js +53 -0
  39. package/dist/constants/gate-constants.d.ts +15 -0
  40. package/dist/constants/gate-constants.js +15 -0
  41. package/dist/constants/linter-constants.d.ts +16 -0
  42. package/dist/constants/linter-constants.js +16 -0
  43. package/dist/constants/tokenizer-constants.d.ts +15 -0
  44. package/dist/constants/tokenizer-constants.js +15 -0
  45. package/dist/core/scope-checker.d.ts +97 -0
  46. package/dist/core/scope-checker.js +163 -0
  47. package/dist/core/tool-runner.d.ts +161 -0
  48. package/dist/core/tool-runner.js +393 -0
  49. package/dist/core/tool.constants.d.ts +105 -0
  50. package/dist/core/tool.constants.js +101 -0
  51. package/dist/core/tool.schemas.d.ts +226 -0
  52. package/dist/core/tool.schemas.js +226 -0
  53. package/dist/core/worktree-guard.d.ts +130 -0
  54. package/dist/core/worktree-guard.js +242 -0
  55. package/dist/coverage-gate.d.ts +108 -0
  56. package/dist/coverage-gate.js +196 -0
  57. package/dist/date-utils.d.ts +75 -0
  58. package/dist/date-utils.js +140 -0
  59. package/dist/dependency-graph.d.ts +142 -0
  60. package/dist/dependency-graph.js +550 -0
  61. package/dist/dependency-guard.d.ts +54 -0
  62. package/dist/dependency-guard.js +142 -0
  63. package/dist/dependency-validator.d.ts +105 -0
  64. package/dist/dependency-validator.js +154 -0
  65. package/dist/docs-path-validator.d.ts +36 -0
  66. package/dist/docs-path-validator.js +95 -0
  67. package/dist/domain/orchestration.constants.d.ts +99 -0
  68. package/dist/domain/orchestration.constants.js +97 -0
  69. package/dist/domain/orchestration.schemas.d.ts +280 -0
  70. package/dist/domain/orchestration.schemas.js +211 -0
  71. package/dist/domain/orchestration.types.d.ts +133 -0
  72. package/dist/domain/orchestration.types.js +12 -0
  73. package/dist/error-handler.d.ts +116 -0
  74. package/dist/error-handler.js +136 -0
  75. package/dist/file-classifiers.d.ts +62 -0
  76. package/dist/file-classifiers.js +108 -0
  77. package/dist/gates-agent-mode.d.ts +81 -0
  78. package/dist/gates-agent-mode.js +94 -0
  79. package/dist/generate-traceability.d.ts +107 -0
  80. package/dist/generate-traceability.js +411 -0
  81. package/dist/git-adapter.d.ts +395 -0
  82. package/dist/git-adapter.js +649 -0
  83. package/dist/git-staged-validator.d.ts +32 -0
  84. package/dist/git-staged-validator.js +48 -0
  85. package/dist/hardcoded-strings.d.ts +61 -0
  86. package/dist/hardcoded-strings.js +270 -0
  87. package/dist/incremental-lint.d.ts +78 -0
  88. package/dist/incremental-lint.js +129 -0
  89. package/dist/incremental-test.d.ts +39 -0
  90. package/dist/incremental-test.js +61 -0
  91. package/dist/index.d.ts +42 -0
  92. package/dist/index.js +61 -0
  93. package/dist/invariants/check-automated-tests.d.ts +50 -0
  94. package/dist/invariants/check-automated-tests.js +166 -0
  95. package/dist/invariants-runner.d.ts +103 -0
  96. package/dist/invariants-runner.js +527 -0
  97. package/dist/lane-checker.d.ts +50 -0
  98. package/dist/lane-checker.js +319 -0
  99. package/dist/lane-inference.d.ts +39 -0
  100. package/dist/lane-inference.js +195 -0
  101. package/dist/lane-lock.d.ts +211 -0
  102. package/dist/lane-lock.js +474 -0
  103. package/dist/lane-validator.d.ts +48 -0
  104. package/dist/lane-validator.js +114 -0
  105. package/dist/logs-lib.d.ts +104 -0
  106. package/dist/logs-lib.js +207 -0
  107. package/dist/lumenflow-config-schema.d.ts +272 -0
  108. package/dist/lumenflow-config-schema.js +207 -0
  109. package/dist/lumenflow-config.d.ts +95 -0
  110. package/dist/lumenflow-config.js +236 -0
  111. package/dist/manual-test-validator.d.ts +80 -0
  112. package/dist/manual-test-validator.js +200 -0
  113. package/dist/merge-lock.d.ts +115 -0
  114. package/dist/merge-lock.js +251 -0
  115. package/dist/micro-worktree.d.ts +159 -0
  116. package/dist/micro-worktree.js +427 -0
  117. package/dist/migration-deployer.d.ts +69 -0
  118. package/dist/migration-deployer.js +151 -0
  119. package/dist/orchestration-advisory-loader.d.ts +28 -0
  120. package/dist/orchestration-advisory-loader.js +87 -0
  121. package/dist/orchestration-advisory.d.ts +58 -0
  122. package/dist/orchestration-advisory.js +94 -0
  123. package/dist/orchestration-di.d.ts +48 -0
  124. package/dist/orchestration-di.js +57 -0
  125. package/dist/orchestration-rules.d.ts +57 -0
  126. package/dist/orchestration-rules.js +201 -0
  127. package/dist/orphan-detector.d.ts +131 -0
  128. package/dist/orphan-detector.js +226 -0
  129. package/dist/path-classifiers.d.ts +57 -0
  130. package/dist/path-classifiers.js +93 -0
  131. package/dist/piped-command-detector.d.ts +34 -0
  132. package/dist/piped-command-detector.js +64 -0
  133. package/dist/ports/dashboard-renderer.port.d.ts +112 -0
  134. package/dist/ports/dashboard-renderer.port.js +25 -0
  135. package/dist/ports/metrics-collector.port.d.ts +132 -0
  136. package/dist/ports/metrics-collector.port.js +26 -0
  137. package/dist/process-detector.d.ts +84 -0
  138. package/dist/process-detector.js +172 -0
  139. package/dist/prompt-linter.d.ts +72 -0
  140. package/dist/prompt-linter.js +312 -0
  141. package/dist/prompt-monitor.d.ts +15 -0
  142. package/dist/prompt-monitor.js +205 -0
  143. package/dist/rebase-artifact-cleanup.d.ts +145 -0
  144. package/dist/rebase-artifact-cleanup.js +433 -0
  145. package/dist/retry-strategy.d.ts +189 -0
  146. package/dist/retry-strategy.js +283 -0
  147. package/dist/risk-detector.d.ts +108 -0
  148. package/dist/risk-detector.js +252 -0
  149. package/dist/rollback-utils.d.ts +76 -0
  150. package/dist/rollback-utils.js +104 -0
  151. package/dist/section-headings.d.ts +43 -0
  152. package/dist/section-headings.js +49 -0
  153. package/dist/spawn-escalation.d.ts +90 -0
  154. package/dist/spawn-escalation.js +253 -0
  155. package/dist/spawn-monitor.d.ts +229 -0
  156. package/dist/spawn-monitor.js +672 -0
  157. package/dist/spawn-recovery.d.ts +82 -0
  158. package/dist/spawn-recovery.js +298 -0
  159. package/dist/spawn-registry-schema.d.ts +98 -0
  160. package/dist/spawn-registry-schema.js +108 -0
  161. package/dist/spawn-registry-store.d.ts +146 -0
  162. package/dist/spawn-registry-store.js +273 -0
  163. package/dist/spawn-tree.d.ts +121 -0
  164. package/dist/spawn-tree.js +285 -0
  165. package/dist/stamp-status-validator.d.ts +84 -0
  166. package/dist/stamp-status-validator.js +134 -0
  167. package/dist/stamp-utils.d.ts +100 -0
  168. package/dist/stamp-utils.js +229 -0
  169. package/dist/state-machine.d.ts +26 -0
  170. package/dist/state-machine.js +83 -0
  171. package/dist/system-map-validator.d.ts +80 -0
  172. package/dist/system-map-validator.js +272 -0
  173. package/dist/telemetry.d.ts +80 -0
  174. package/dist/telemetry.js +213 -0
  175. package/dist/token-counter.d.ts +51 -0
  176. package/dist/token-counter.js +145 -0
  177. package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
  178. package/dist/usecases/get-dashboard-data.usecase.js +61 -0
  179. package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
  180. package/dist/usecases/get-suggestions.usecase.js +153 -0
  181. package/dist/user-normalizer.d.ts +41 -0
  182. package/dist/user-normalizer.js +141 -0
  183. package/dist/validators/phi-constants.d.ts +97 -0
  184. package/dist/validators/phi-constants.js +152 -0
  185. package/dist/validators/phi-scanner.d.ts +58 -0
  186. package/dist/validators/phi-scanner.js +215 -0
  187. package/dist/worktree-ownership.d.ts +50 -0
  188. package/dist/worktree-ownership.js +74 -0
  189. package/dist/worktree-scanner.d.ts +103 -0
  190. package/dist/worktree-scanner.js +168 -0
  191. package/dist/worktree-symlink.d.ts +99 -0
  192. package/dist/worktree-symlink.js +359 -0
  193. package/dist/wu-backlog-updater.d.ts +17 -0
  194. package/dist/wu-backlog-updater.js +37 -0
  195. package/dist/wu-checkpoint.d.ts +124 -0
  196. package/dist/wu-checkpoint.js +233 -0
  197. package/dist/wu-claim-helpers.d.ts +26 -0
  198. package/dist/wu-claim-helpers.js +63 -0
  199. package/dist/wu-claim-resume.d.ts +106 -0
  200. package/dist/wu-claim-resume.js +276 -0
  201. package/dist/wu-consistency-checker.d.ts +95 -0
  202. package/dist/wu-consistency-checker.js +567 -0
  203. package/dist/wu-constants.d.ts +1275 -0
  204. package/dist/wu-constants.js +1382 -0
  205. package/dist/wu-create-validators.d.ts +42 -0
  206. package/dist/wu-create-validators.js +93 -0
  207. package/dist/wu-done-branch-only.d.ts +63 -0
  208. package/dist/wu-done-branch-only.js +191 -0
  209. package/dist/wu-done-messages.d.ts +119 -0
  210. package/dist/wu-done-messages.js +185 -0
  211. package/dist/wu-done-pr.d.ts +72 -0
  212. package/dist/wu-done-pr.js +174 -0
  213. package/dist/wu-done-retry-helpers.d.ts +85 -0
  214. package/dist/wu-done-retry-helpers.js +172 -0
  215. package/dist/wu-done-ui.d.ts +37 -0
  216. package/dist/wu-done-ui.js +69 -0
  217. package/dist/wu-done-validators.d.ts +411 -0
  218. package/dist/wu-done-validators.js +1229 -0
  219. package/dist/wu-done-worktree.d.ts +182 -0
  220. package/dist/wu-done-worktree.js +1097 -0
  221. package/dist/wu-helpers.d.ts +128 -0
  222. package/dist/wu-helpers.js +248 -0
  223. package/dist/wu-lint.d.ts +70 -0
  224. package/dist/wu-lint.js +234 -0
  225. package/dist/wu-paths.d.ts +171 -0
  226. package/dist/wu-paths.js +178 -0
  227. package/dist/wu-preflight-validators.d.ts +86 -0
  228. package/dist/wu-preflight-validators.js +251 -0
  229. package/dist/wu-recovery.d.ts +138 -0
  230. package/dist/wu-recovery.js +341 -0
  231. package/dist/wu-repair-core.d.ts +131 -0
  232. package/dist/wu-repair-core.js +669 -0
  233. package/dist/wu-schema-normalization.d.ts +17 -0
  234. package/dist/wu-schema-normalization.js +82 -0
  235. package/dist/wu-schema.d.ts +793 -0
  236. package/dist/wu-schema.js +881 -0
  237. package/dist/wu-spawn-helpers.d.ts +121 -0
  238. package/dist/wu-spawn-helpers.js +271 -0
  239. package/dist/wu-spawn.d.ts +158 -0
  240. package/dist/wu-spawn.js +1306 -0
  241. package/dist/wu-state-schema.d.ts +213 -0
  242. package/dist/wu-state-schema.js +156 -0
  243. package/dist/wu-state-store.d.ts +264 -0
  244. package/dist/wu-state-store.js +691 -0
  245. package/dist/wu-status-transition.d.ts +63 -0
  246. package/dist/wu-status-transition.js +382 -0
  247. package/dist/wu-status-updater.d.ts +25 -0
  248. package/dist/wu-status-updater.js +116 -0
  249. package/dist/wu-transaction-collectors.d.ts +116 -0
  250. package/dist/wu-transaction-collectors.js +272 -0
  251. package/dist/wu-transaction.d.ts +170 -0
  252. package/dist/wu-transaction.js +273 -0
  253. package/dist/wu-validation-constants.d.ts +60 -0
  254. package/dist/wu-validation-constants.js +66 -0
  255. package/dist/wu-validation.d.ts +118 -0
  256. package/dist/wu-validation.js +243 -0
  257. package/dist/wu-validator.d.ts +62 -0
  258. package/dist/wu-validator.js +325 -0
  259. package/dist/wu-yaml-fixer.d.ts +97 -0
  260. package/dist/wu-yaml-fixer.js +264 -0
  261. package/dist/wu-yaml.d.ts +86 -0
  262. package/dist/wu-yaml.js +222 -0
  263. package/package.json +114 -0
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telemetry Module - DORA/SPACE Metrics Emission
4
+ *
5
+ * Emits structured NDJSON telemetry for gates execution and WU flow metrics.
6
+ * Used by gates-local.mjs and flow-report.mjs.
7
+ */
8
+ /**
9
+ * Emit a telemetry event as NDJSON
10
+ * @param {string} filePath - Path to NDJSON file
11
+ * @param {object} event - Event data to emit
12
+ */
13
+ export declare function emit(filePath: any, event: any): void;
14
+ /**
15
+ * Emit a gates execution event
16
+ * @param {object} data - Gates event data
17
+ * @param {string} data.wu_id - Work Unit ID (e.g., 'WU-402')
18
+ * @param {string} data.lane - Lane name (e.g., 'Operations')
19
+ * @param {string} data.gate_name - Gate name (e.g., 'format:check')
20
+ * @param {boolean} data.passed - Whether gate passed
21
+ * @param {number} data.duration_ms - Execution duration in milliseconds
22
+ */
23
+ export declare function emitGateEvent(data: any): void;
24
+ /**
25
+ * Get current WU ID from git branch or environment
26
+ * @returns {string|null} WU ID or null
27
+ */
28
+ export declare function getCurrentWU(): string;
29
+ /**
30
+ * Get lane from git branch or environment
31
+ * @returns {string|null} Lane name or null
32
+ */
33
+ export declare function getCurrentLane(): string;
34
+ /**
35
+ * Emit LLM classification start event
36
+ * @param {object} data - Classification start data
37
+ * @param {string} data.classification_type - Type of classification (e.g., 'mode_detection', 'red_flag', 'phi_detection')
38
+ * @param {boolean} [data.has_context] - Whether conversation context was provided
39
+ * @param {string} [data.wu_id] - Work Unit ID
40
+ * @param {string} [data.lane] - Lane name
41
+ * @param {string} [logPath] - Optional log path override (for testing)
42
+ */
43
+ export declare function emitLLMClassificationStart(data: any, logPath?: string): void;
44
+ /**
45
+ * Emit LLM classification complete event
46
+ * @param {object} data - Classification completion data
47
+ * @param {string} data.classification_type - Type of classification
48
+ * @param {number} data.duration_ms - Processing duration in milliseconds
49
+ * @param {number} data.tokens_used - Total tokens consumed
50
+ * @param {number} data.estimated_cost_usd - Estimated cost in USD
51
+ * @param {number} data.confidence - Classification confidence score (0-1)
52
+ * @param {boolean} data.fallback_used - Whether fallback (regex) was used
53
+ * @param {string} [data.fallback_reason] - Reason fallback was triggered
54
+ * @param {string} [data.wu_id] - Work Unit ID
55
+ * @param {string} [data.lane] - Lane name
56
+ * @param {string} [logPath] - Optional log path override (for testing)
57
+ */
58
+ export declare function emitLLMClassificationComplete(data: any, logPath?: string): void;
59
+ /**
60
+ * Emit LLM classification error event
61
+ * @param {object} data - Classification error data
62
+ * @param {string} data.classification_type - Type of classification
63
+ * @param {string} data.error_type - Error type (e.g., 'timeout', 'rate_limit', 'validation')
64
+ * @param {string} data.error_message - Error message (must be PII-free)
65
+ * @param {number} [data.duration_ms] - Duration before error occurred
66
+ * @param {string} [data.wu_id] - Work Unit ID
67
+ * @param {string} [data.lane] - Lane name
68
+ * @param {string} [logPath] - Optional log path override (for testing)
69
+ */
70
+ export declare function emitLLMClassificationError(data: any, logPath?: string): void;
71
+ /**
72
+ * Emit WU flow telemetry event to .beacon/flow.log
73
+ *
74
+ * Used by wu-claim, wu-done, wu-unblock for workflow tracking.
75
+ * Centralized from duplicated emitTelemetry() functions (WU-1256).
76
+ *
77
+ * @param {object} event - Event data (script, wu_id, lane, step, etc.)
78
+ * @param {string} [logPath] - Optional log path override (for testing)
79
+ */
80
+ export declare function emitWUFlowEvent(event: any, logPath?: string): void;
@@ -0,0 +1,213 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Telemetry Module - DORA/SPACE Metrics Emission
4
+ *
5
+ * Emits structured NDJSON telemetry for gates execution and WU flow metrics.
6
+ * Used by gates-local.mjs and flow-report.mjs.
7
+ */
8
+ import { appendFileSync, mkdirSync, existsSync } from 'node:fs';
9
+ import { execSync } from 'node:child_process';
10
+ import path from 'node:path';
11
+ import { BEACON_PATHS, FILE_EXTENSIONS, STDIO, STRING_LITERALS, } from './wu-constants.js';
12
+ const TELEMETRY_DIR = BEACON_PATHS.TELEMETRY;
13
+ const GATES_LOG = `${TELEMETRY_DIR}/gates${FILE_EXTENSIONS.NDJSON}`;
14
+ const LLM_CLASSIFICATION_LOG = `${TELEMETRY_DIR}/llm-classification${FILE_EXTENSIONS.NDJSON}`;
15
+ const FLOW_LOG = BEACON_PATHS.FLOW_LOG;
16
+ /**
17
+ * Ensure telemetry directory exists
18
+ */
19
+ function ensureTelemetryDir() {
20
+ try {
21
+ mkdirSync(TELEMETRY_DIR, { recursive: true });
22
+ }
23
+ catch {
24
+ // Directory may already exist, ignore
25
+ }
26
+ }
27
+ /**
28
+ * Emit a telemetry event as NDJSON
29
+ * @param {string} filePath - Path to NDJSON file
30
+ * @param {object} event - Event data to emit
31
+ */
32
+ export function emit(filePath, event) {
33
+ ensureTelemetryDir();
34
+ const line = `${JSON.stringify(event)}${STRING_LITERALS.NEWLINE}`;
35
+ try {
36
+ appendFileSync(filePath, line, { encoding: 'utf-8' });
37
+ }
38
+ catch (err) {
39
+ const message = err instanceof Error ? err.message : String(err);
40
+ console.error(`[telemetry] Failed to emit to ${filePath}:`, message);
41
+ }
42
+ }
43
+ /**
44
+ * Emit a gates execution event
45
+ * @param {object} data - Gates event data
46
+ * @param {string} data.wu_id - Work Unit ID (e.g., 'WU-402')
47
+ * @param {string} data.lane - Lane name (e.g., 'Operations')
48
+ * @param {string} data.gate_name - Gate name (e.g., 'format:check')
49
+ * @param {boolean} data.passed - Whether gate passed
50
+ * @param {number} data.duration_ms - Execution duration in milliseconds
51
+ */
52
+ export function emitGateEvent(data) {
53
+ const event = {
54
+ timestamp: new Date().toISOString(),
55
+ wu_id: data.wu_id || null,
56
+ lane: data.lane || null,
57
+ gate_name: data.gate_name,
58
+ passed: data.passed,
59
+ duration_ms: data.duration_ms,
60
+ };
61
+ emit(GATES_LOG, event);
62
+ }
63
+ /**
64
+ * Get current WU ID from git branch or environment
65
+ * @returns {string|null} WU ID or null
66
+ */
67
+ export function getCurrentWU() {
68
+ try {
69
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
70
+ encoding: 'utf-8',
71
+ stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE],
72
+ }).trim();
73
+ // Extract WU ID from branch name (e.g., lane/operations/wu-402 -> WU-402)
74
+ const match = branch.match(/wu-(\d+)/i);
75
+ if (match) {
76
+ return `WU-${match[1]}`.toUpperCase();
77
+ }
78
+ }
79
+ catch {
80
+ // Not in a git repo or command failed
81
+ }
82
+ return null;
83
+ }
84
+ /**
85
+ * Get lane from git branch or environment
86
+ * @returns {string|null} Lane name or null
87
+ */
88
+ export function getCurrentLane() {
89
+ try {
90
+ const branch = execSync('git rev-parse --abbrev-ref HEAD', {
91
+ encoding: 'utf-8',
92
+ stdio: [STDIO.PIPE, STDIO.PIPE, STDIO.IGNORE],
93
+ }).trim();
94
+ // Extract lane from branch name (e.g., lane/operations/wu-402 -> Operations)
95
+ const match = branch.match(/^lane\/([^/]+)\//i);
96
+ if (match) {
97
+ return match[1].charAt(0).toUpperCase() + match[1].slice(1).toLowerCase();
98
+ }
99
+ }
100
+ catch {
101
+ // Not in a git repo or command failed
102
+ }
103
+ return null;
104
+ }
105
+ /**
106
+ * Emit LLM classification start event
107
+ * @param {object} data - Classification start data
108
+ * @param {string} data.classification_type - Type of classification (e.g., 'mode_detection', 'red_flag', 'phi_detection')
109
+ * @param {boolean} [data.has_context] - Whether conversation context was provided
110
+ * @param {string} [data.wu_id] - Work Unit ID
111
+ * @param {string} [data.lane] - Lane name
112
+ * @param {string} [logPath] - Optional log path override (for testing)
113
+ */
114
+ export function emitLLMClassificationStart(data, logPath = LLM_CLASSIFICATION_LOG) {
115
+ const event = {
116
+ timestamp: new Date().toISOString(),
117
+ event_type: 'llm.classification.start',
118
+ classification_type: data.classification_type,
119
+ has_context: data.has_context ?? false,
120
+ wu_id: data.wu_id || getCurrentWU(),
121
+ lane: data.lane || getCurrentLane(),
122
+ };
123
+ emit(logPath, event);
124
+ }
125
+ /**
126
+ * Emit LLM classification complete event
127
+ * @param {object} data - Classification completion data
128
+ * @param {string} data.classification_type - Type of classification
129
+ * @param {number} data.duration_ms - Processing duration in milliseconds
130
+ * @param {number} data.tokens_used - Total tokens consumed
131
+ * @param {number} data.estimated_cost_usd - Estimated cost in USD
132
+ * @param {number} data.confidence - Classification confidence score (0-1)
133
+ * @param {boolean} data.fallback_used - Whether fallback (regex) was used
134
+ * @param {string} [data.fallback_reason] - Reason fallback was triggered
135
+ * @param {string} [data.wu_id] - Work Unit ID
136
+ * @param {string} [data.lane] - Lane name
137
+ * @param {string} [logPath] - Optional log path override (for testing)
138
+ */
139
+ export function emitLLMClassificationComplete(data, logPath = LLM_CLASSIFICATION_LOG) {
140
+ // PII Protection: Explicitly exclude any user input fields
141
+ const event = {
142
+ timestamp: new Date().toISOString(),
143
+ event_type: 'llm.classification.complete',
144
+ classification_type: data.classification_type,
145
+ duration_ms: data.duration_ms,
146
+ tokens_used: data.tokens_used,
147
+ estimated_cost_usd: data.estimated_cost_usd,
148
+ confidence: data.confidence,
149
+ fallback_used: data.fallback_used,
150
+ wu_id: data.wu_id || getCurrentWU(),
151
+ lane: data.lane || getCurrentLane(),
152
+ };
153
+ // Add fallback_reason only if fallback was used
154
+ if (data.fallback_used && data.fallback_reason) {
155
+ event.fallback_reason = data.fallback_reason;
156
+ }
157
+ emit(logPath, event);
158
+ }
159
+ /**
160
+ * Emit LLM classification error event
161
+ * @param {object} data - Classification error data
162
+ * @param {string} data.classification_type - Type of classification
163
+ * @param {string} data.error_type - Error type (e.g., 'timeout', 'rate_limit', 'validation')
164
+ * @param {string} data.error_message - Error message (must be PII-free)
165
+ * @param {number} [data.duration_ms] - Duration before error occurred
166
+ * @param {string} [data.wu_id] - Work Unit ID
167
+ * @param {string} [data.lane] - Lane name
168
+ * @param {string} [logPath] - Optional log path override (for testing)
169
+ */
170
+ export function emitLLMClassificationError(data, logPath = LLM_CLASSIFICATION_LOG) {
171
+ // PII Protection: Never log user input or sensitive data
172
+ const event = {
173
+ timestamp: new Date().toISOString(),
174
+ event_type: 'llm.classification.error',
175
+ classification_type: data.classification_type,
176
+ error_type: data.error_type,
177
+ error_message: data.error_message, // Caller must ensure this is PII-free
178
+ wu_id: data.wu_id || getCurrentWU(),
179
+ lane: data.lane || getCurrentLane(),
180
+ };
181
+ if (data.duration_ms !== undefined) {
182
+ event.duration_ms = data.duration_ms;
183
+ }
184
+ // Explicitly redact any input_text_preview to prevent PII leakage
185
+ if (data.input_text_preview) {
186
+ event.input_text_preview = '[REDACTED]';
187
+ }
188
+ emit(logPath, event);
189
+ }
190
+ /**
191
+ * Emit WU flow telemetry event to .beacon/flow.log
192
+ *
193
+ * Used by wu-claim, wu-done, wu-unblock for workflow tracking.
194
+ * Centralized from duplicated emitTelemetry() functions (WU-1256).
195
+ *
196
+ * @param {object} event - Event data (script, wu_id, lane, step, etc.)
197
+ * @param {string} [logPath] - Optional log path override (for testing)
198
+ */
199
+ export function emitWUFlowEvent(event, logPath = FLOW_LOG) {
200
+ const logDir = path.dirname(logPath);
201
+ if (!existsSync(logDir)) {
202
+ mkdirSync(logDir, { recursive: true });
203
+ }
204
+ const line = JSON.stringify({ timestamp: new Date().toISOString(), ...event });
205
+ try {
206
+ appendFileSync(logPath, `${line}${STRING_LITERALS.NEWLINE}`, { encoding: 'utf-8' });
207
+ }
208
+ catch (err) {
209
+ // Silently fail - telemetry should not block workflow
210
+ const message = err instanceof Error ? err.message : String(err);
211
+ console.error(`[telemetry] Failed to emit flow event: ${message}`);
212
+ }
213
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Token Counter Utility
3
+ *
4
+ * Wraps tiktoken for counting tokens in prompts using the o200k_base encoding
5
+ * (used by gpt-5-nano). Provides caching and hash computation for stability checks.
6
+ *
7
+ * Part of WU-676: Single-Call LLM Orchestrator token budget enforcement.
8
+ */
9
+ /**
10
+ * Count tokens in text using gpt-5-nano tokenizer
11
+ * @param {string} text - Text to tokenize
12
+ * @returns {number} Token count
13
+ */
14
+ export declare function countTokens(text: any): any;
15
+ /**
16
+ * Compute SHA256 hash of text (for stability checks)
17
+ * @param {string} text - Text to hash
18
+ * @returns {string} Hex hash
19
+ */
20
+ export declare function computeHash(text: any): string;
21
+ /**
22
+ * Load and render a prompt file (resolves includes, strips comments)
23
+ * @param {string} promptPath - Absolute path to prompt YAML file
24
+ * @returns {{text: string, raw: string}} Rendered text and raw YAML
25
+ */
26
+ export declare function loadPrompt(promptPath: any): {
27
+ text: any;
28
+ raw: string;
29
+ };
30
+ /**
31
+ * Analyze a prompt file (count tokens, compute hash, extract metadata)
32
+ * @param {string} promptPath - Absolute path to prompt YAML file
33
+ * @returns {{tokenCount: number, hash: string, text: string, raw: string}}
34
+ */
35
+ export declare function analyzePrompt(promptPath: any): {
36
+ tokenCount: any;
37
+ hash: string;
38
+ text: any;
39
+ raw: string;
40
+ };
41
+ /**
42
+ * Get top N longest lines from text (for cleanup targeting)
43
+ * @param {string} text - Text to analyze
44
+ * @param {number} n - Number of lines to return
45
+ * @returns {Array<{line: string, length: number, number: number}>} Longest lines
46
+ */
47
+ export declare function getLongestLines(text: any, n?: number): any;
48
+ /**
49
+ * Cleanup function (call on process exit to free tokenizer)
50
+ */
51
+ export declare function cleanup(): void;
@@ -0,0 +1,145 @@
1
+ /**
2
+ * Token Counter Utility
3
+ *
4
+ * Wraps tiktoken for counting tokens in prompts using the o200k_base encoding
5
+ * (used by gpt-5-nano). Provides caching and hash computation for stability checks.
6
+ *
7
+ * Part of WU-676: Single-Call LLM Orchestrator token budget enforcement.
8
+ */
9
+ import { get_encoding } from 'tiktoken';
10
+ import { readFileSync } from 'fs';
11
+ import { createHash } from 'crypto';
12
+ import { load as loadYAML } from 'js-yaml';
13
+ import { createError, ErrorCodes } from './error-handler.js';
14
+ import { EXIT_CODES, STRING_LITERALS } from './wu-constants.js';
15
+ // Cache tokenizer instance (expensive to create)
16
+ let tokenizerCache = null;
17
+ /**
18
+ * Get or create tiktoken instance with o200k_base encoding (gpt-5-nano)
19
+ * @returns {Tiktoken} Tokenizer instance
20
+ */
21
+ function getTokenizer() {
22
+ if (!tokenizerCache) {
23
+ tokenizerCache = get_encoding('o200k_base');
24
+ }
25
+ return tokenizerCache;
26
+ }
27
+ /**
28
+ * Count tokens in text using gpt-5-nano tokenizer
29
+ * @param {string} text - Text to tokenize
30
+ * @returns {number} Token count
31
+ */
32
+ export function countTokens(text) {
33
+ if (!text || typeof text !== 'string') {
34
+ return 0;
35
+ }
36
+ const tokenizer = getTokenizer();
37
+ const tokens = tokenizer.encode(text);
38
+ return tokens.length;
39
+ }
40
+ /**
41
+ * Compute SHA256 hash of text (for stability checks)
42
+ * @param {string} text - Text to hash
43
+ * @returns {string} Hex hash
44
+ */
45
+ export function computeHash(text) {
46
+ return createHash('sha256').update(text).digest('hex').slice(0, 16);
47
+ }
48
+ /**
49
+ * Strip YAML comments from prompt text
50
+ * @param {string} text - YAML text
51
+ * @returns {string} Text with comments removed
52
+ */
53
+ function stripYAMLComments(text) {
54
+ return text
55
+ .split(STRING_LITERALS.NEWLINE)
56
+ .filter((line) => !line.trim().startsWith('#'))
57
+ .join(STRING_LITERALS.NEWLINE);
58
+ }
59
+ /**
60
+ * Load and render a prompt file (resolves includes, strips comments)
61
+ * @param {string} promptPath - Absolute path to prompt YAML file
62
+ * @returns {{text: string, raw: string}} Rendered text and raw YAML
63
+ */
64
+ export function loadPrompt(promptPath) {
65
+ try {
66
+ const raw = readFileSync(promptPath, { encoding: 'utf-8' });
67
+ // Parse YAML to access prompt structure
68
+ const parsed = loadYAML(raw);
69
+ // Extract prompt text (handle different YAML structures)
70
+ let promptText = '';
71
+ if (typeof parsed === 'string') {
72
+ promptText = parsed;
73
+ }
74
+ else if (parsed && parsed.prompt) {
75
+ promptText = parsed.prompt;
76
+ }
77
+ else if (parsed && parsed.system) {
78
+ promptText = parsed.system;
79
+ }
80
+ else if (parsed && parsed.content) {
81
+ promptText = parsed.content;
82
+ }
83
+ else {
84
+ // Fallback: use entire YAML stringified (for complex structures)
85
+ promptText = JSON.stringify(parsed, null, 2);
86
+ }
87
+ // Strip comments from rendered text
88
+ const renderedText = stripYAMLComments(promptText);
89
+ return { text: renderedText, raw };
90
+ }
91
+ catch (error) {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ throw createError(ErrorCodes.FILE_NOT_FOUND, `Failed to load prompt from ${promptPath}: ${message}`, { path: promptPath, originalError: message });
94
+ }
95
+ }
96
+ /**
97
+ * Analyze a prompt file (count tokens, compute hash, extract metadata)
98
+ * @param {string} promptPath - Absolute path to prompt YAML file
99
+ * @returns {{tokenCount: number, hash: string, text: string, raw: string}}
100
+ */
101
+ export function analyzePrompt(promptPath) {
102
+ const { text, raw } = loadPrompt(promptPath);
103
+ const tokenCount = countTokens(text);
104
+ const hash = computeHash(text);
105
+ return {
106
+ tokenCount,
107
+ hash,
108
+ text,
109
+ raw,
110
+ };
111
+ }
112
+ /**
113
+ * Get top N longest lines from text (for cleanup targeting)
114
+ * @param {string} text - Text to analyze
115
+ * @param {number} n - Number of lines to return
116
+ * @returns {Array<{line: string, length: number, number: number}>} Longest lines
117
+ */
118
+ export function getLongestLines(text, n = 3) {
119
+ const lines = text.split(STRING_LITERALS.NEWLINE);
120
+ const linesWithMetadata = lines.map((line, index) => ({
121
+ line: line.trim(),
122
+ length: line.length,
123
+ number: index + 1,
124
+ }));
125
+ // Sort by length descending, take top N
126
+ return linesWithMetadata
127
+ .filter((l) => l.line.length > 0)
128
+ .sort((a, b) => b.length - a.length)
129
+ .slice(0, n);
130
+ }
131
+ /**
132
+ * Cleanup function (call on process exit to free tokenizer)
133
+ */
134
+ export function cleanup() {
135
+ if (tokenizerCache) {
136
+ tokenizerCache.free();
137
+ tokenizerCache = null;
138
+ }
139
+ }
140
+ // Auto-cleanup on process exit
141
+ process.on('exit', cleanup);
142
+ process.on('SIGINT', () => {
143
+ cleanup();
144
+ process.exit(EXIT_CODES.SUCCESS);
145
+ });
@@ -0,0 +1,52 @@
1
+ /**
2
+ * GetDashboardData Use Case
3
+ *
4
+ * Orchestrates the collection of all dashboard metrics from the metrics collector.
5
+ * Follows hexagonal architecture - depends on port interface, not concrete implementation.
6
+ *
7
+ * @module get-dashboard-data.usecase
8
+ * @see {@link ../ports/metrics-collector.port.ts} - Port interface
9
+ * @see {@link ../domain/orchestration.types.ts} - Domain types
10
+ */
11
+ import type { IMetricsCollector } from '../ports/metrics-collector.port.js';
12
+ import type { DashboardData } from '../domain/orchestration.types.js';
13
+ /**
14
+ * Options for the GetDashboardData use case.
15
+ */
16
+ export interface GetDashboardDataOptions {
17
+ /**
18
+ * Number of hours to include in the timeline.
19
+ * @default TIMELINE_WINDOW_HOURS (24)
20
+ */
21
+ timelineHours?: number;
22
+ }
23
+ /**
24
+ * Use case for collecting all dashboard data.
25
+ *
26
+ * Orchestrates parallel calls to the metrics collector to gather:
27
+ * - Global status (active WUs, completed, blocked, etc.)
28
+ * - Agent metrics (invocation counts, pass rates, timing)
29
+ * - WU progress (DoD progress, agent statuses)
30
+ * - Timeline events (recent activity)
31
+ * - Alerts (items requiring attention)
32
+ *
33
+ * @example
34
+ * const collector = new FileSystemMetricsCollector(basePath);
35
+ * const useCase = new GetDashboardDataUseCase(collector);
36
+ * const data = await useCase.execute();
37
+ * console.log(data.globalStatus.activeWUs);
38
+ */
39
+ export declare class GetDashboardDataUseCase {
40
+ private readonly metricsCollector;
41
+ constructor(metricsCollector: IMetricsCollector);
42
+ /**
43
+ * Execute the use case to collect all dashboard data.
44
+ *
45
+ * Calls all collector methods in parallel for optimal performance.
46
+ *
47
+ * @param options - Optional configuration
48
+ * @returns Promise resolving to complete dashboard data
49
+ * @throws Error if any collector method fails
50
+ */
51
+ execute(options?: GetDashboardDataOptions): Promise<DashboardData>;
52
+ }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * GetDashboardData Use Case
3
+ *
4
+ * Orchestrates the collection of all dashboard metrics from the metrics collector.
5
+ * Follows hexagonal architecture - depends on port interface, not concrete implementation.
6
+ *
7
+ * @module get-dashboard-data.usecase
8
+ * @see {@link ../ports/metrics-collector.port.ts} - Port interface
9
+ * @see {@link ../domain/orchestration.types.ts} - Domain types
10
+ */
11
+ import { TIMELINE_WINDOW_HOURS } from '../domain/orchestration.constants.js';
12
+ /**
13
+ * Use case for collecting all dashboard data.
14
+ *
15
+ * Orchestrates parallel calls to the metrics collector to gather:
16
+ * - Global status (active WUs, completed, blocked, etc.)
17
+ * - Agent metrics (invocation counts, pass rates, timing)
18
+ * - WU progress (DoD progress, agent statuses)
19
+ * - Timeline events (recent activity)
20
+ * - Alerts (items requiring attention)
21
+ *
22
+ * @example
23
+ * const collector = new FileSystemMetricsCollector(basePath);
24
+ * const useCase = new GetDashboardDataUseCase(collector);
25
+ * const data = await useCase.execute();
26
+ * console.log(data.globalStatus.activeWUs);
27
+ */
28
+ export class GetDashboardDataUseCase {
29
+ metricsCollector;
30
+ constructor(metricsCollector) {
31
+ this.metricsCollector = metricsCollector;
32
+ }
33
+ /**
34
+ * Execute the use case to collect all dashboard data.
35
+ *
36
+ * Calls all collector methods in parallel for optimal performance.
37
+ *
38
+ * @param options - Optional configuration
39
+ * @returns Promise resolving to complete dashboard data
40
+ * @throws Error if any collector method fails
41
+ */
42
+ async execute(options = {}) {
43
+ const { timelineHours = TIMELINE_WINDOW_HOURS } = options;
44
+ const timelineSince = new Date();
45
+ timelineSince.setHours(timelineSince.getHours() - timelineHours);
46
+ const [globalStatus, agentMetrics, wuProgress, timeline, alerts] = await Promise.all([
47
+ this.metricsCollector.getGlobalStatus(),
48
+ this.metricsCollector.getAgentMetrics(),
49
+ this.metricsCollector.getWUProgress(),
50
+ this.metricsCollector.getTimeline(timelineSince),
51
+ this.metricsCollector.getAlerts(),
52
+ ]);
53
+ return {
54
+ globalStatus,
55
+ agentMetrics,
56
+ wuProgress,
57
+ timeline,
58
+ alerts,
59
+ };
60
+ }
61
+ }