tracelattice 1.2.5

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 (294) hide show
  1. package/LICENSE +24 -0
  2. package/README.md +112 -0
  3. package/dist/ServerConfig.d.ts +229 -0
  4. package/dist/ServerConfig.d.ts.map +1 -0
  5. package/dist/ServerConfig.js +121 -0
  6. package/dist/ServerConfig.js.map +1 -0
  7. package/dist/__tests__/base-registry.test.d.ts +2 -0
  8. package/dist/__tests__/base-registry.test.d.ts.map +1 -0
  9. package/dist/__tests__/base-transport-cov.test.d.ts +2 -0
  10. package/dist/__tests__/base-transport-cov.test.d.ts.map +1 -0
  11. package/dist/__tests__/base-transport.test.d.ts +2 -0
  12. package/dist/__tests__/base-transport.test.d.ts.map +1 -0
  13. package/dist/__tests__/config-loader.test.d.ts +2 -0
  14. package/dist/__tests__/config-loader.test.d.ts.map +1 -0
  15. package/dist/__tests__/connection-pool-cov.test.d.ts +2 -0
  16. package/dist/__tests__/connection-pool-cov.test.d.ts.map +1 -0
  17. package/dist/__tests__/connection-pool.test.d.ts +2 -0
  18. package/dist/__tests__/connection-pool.test.d.ts.map +1 -0
  19. package/dist/__tests__/container.test.d.ts +2 -0
  20. package/dist/__tests__/container.test.d.ts.map +1 -0
  21. package/dist/__tests__/crud.test.d.ts +2 -0
  22. package/dist/__tests__/crud.test.d.ts.map +1 -0
  23. package/dist/__tests__/discovery-cache.test.d.ts +2 -0
  24. package/dist/__tests__/discovery-cache.test.d.ts.map +1 -0
  25. package/dist/__tests__/errors.test.d.ts +2 -0
  26. package/dist/__tests__/errors.test.d.ts.map +1 -0
  27. package/dist/__tests__/factories.test.d.ts +2 -0
  28. package/dist/__tests__/factories.test.d.ts.map +1 -0
  29. package/dist/__tests__/health-checker-cov.test.d.ts +2 -0
  30. package/dist/__tests__/health-checker-cov.test.d.ts.map +1 -0
  31. package/dist/__tests__/health-checker.test.d.ts +2 -0
  32. package/dist/__tests__/health-checker.test.d.ts.map +1 -0
  33. package/dist/__tests__/helpers/factories.d.ts +36 -0
  34. package/dist/__tests__/helpers/factories.d.ts.map +1 -0
  35. package/dist/__tests__/helpers/index.d.ts +3 -0
  36. package/dist/__tests__/helpers/index.d.ts.map +1 -0
  37. package/dist/__tests__/helpers/timers.d.ts +4 -0
  38. package/dist/__tests__/helpers/timers.d.ts.map +1 -0
  39. package/dist/__tests__/history-manager.test.d.ts +2 -0
  40. package/dist/__tests__/history-manager.test.d.ts.map +1 -0
  41. package/dist/__tests__/http-helpers-cov.test.d.ts +2 -0
  42. package/dist/__tests__/http-helpers-cov.test.d.ts.map +1 -0
  43. package/dist/__tests__/http-transport-cov.test.d.ts +2 -0
  44. package/dist/__tests__/http-transport-cov.test.d.ts.map +1 -0
  45. package/dist/__tests__/http-transport.test.d.ts +2 -0
  46. package/dist/__tests__/http-transport.test.d.ts.map +1 -0
  47. package/dist/__tests__/input-normalizer.test.d.ts +8 -0
  48. package/dist/__tests__/input-normalizer.test.d.ts.map +1 -0
  49. package/dist/__tests__/integration.test.d.ts +2 -0
  50. package/dist/__tests__/integration.test.d.ts.map +1 -0
  51. package/dist/__tests__/lib-server.test.d.ts +2 -0
  52. package/dist/__tests__/lib-server.test.d.ts.map +1 -0
  53. package/dist/__tests__/memory-persistence.test.d.ts +2 -0
  54. package/dist/__tests__/memory-persistence.test.d.ts.map +1 -0
  55. package/dist/__tests__/metrics-integration.test.d.ts +2 -0
  56. package/dist/__tests__/metrics-integration.test.d.ts.map +1 -0
  57. package/dist/__tests__/persistence.test.d.ts +2 -0
  58. package/dist/__tests__/persistence.test.d.ts.map +1 -0
  59. package/dist/__tests__/reasoning-integration.test.d.ts +11 -0
  60. package/dist/__tests__/reasoning-integration.test.d.ts.map +1 -0
  61. package/dist/__tests__/reasoning-types.test.d.ts +2 -0
  62. package/dist/__tests__/reasoning-types.test.d.ts.map +1 -0
  63. package/dist/__tests__/request-context.test.d.ts +2 -0
  64. package/dist/__tests__/request-context.test.d.ts.map +1 -0
  65. package/dist/__tests__/sanitize.test.d.ts +2 -0
  66. package/dist/__tests__/sanitize.test.d.ts.map +1 -0
  67. package/dist/__tests__/schema.test.d.ts +2 -0
  68. package/dist/__tests__/schema.test.d.ts.map +1 -0
  69. package/dist/__tests__/sequentialthinking-tools.test.d.ts +2 -0
  70. package/dist/__tests__/sequentialthinking-tools.test.d.ts.map +1 -0
  71. package/dist/__tests__/server-config.test.d.ts +2 -0
  72. package/dist/__tests__/server-config.test.d.ts.map +1 -0
  73. package/dist/__tests__/skill-discovery.test.d.ts +2 -0
  74. package/dist/__tests__/skill-discovery.test.d.ts.map +1 -0
  75. package/dist/__tests__/skill-registry.test.d.ts +2 -0
  76. package/dist/__tests__/skill-registry.test.d.ts.map +1 -0
  77. package/dist/__tests__/skill-watcher.test.d.ts +2 -0
  78. package/dist/__tests__/skill-watcher.test.d.ts.map +1 -0
  79. package/dist/__tests__/sqlite-persistence.test.d.ts +2 -0
  80. package/dist/__tests__/sqlite-persistence.test.d.ts.map +1 -0
  81. package/dist/__tests__/sse-transport-cov.test.d.ts +2 -0
  82. package/dist/__tests__/sse-transport-cov.test.d.ts.map +1 -0
  83. package/dist/__tests__/sse-transport.test.d.ts +2 -0
  84. package/dist/__tests__/sse-transport.test.d.ts.map +1 -0
  85. package/dist/__tests__/streamable-http-cov.test.d.ts +2 -0
  86. package/dist/__tests__/streamable-http-cov.test.d.ts.map +1 -0
  87. package/dist/__tests__/streamable-http-transport.test.d.ts +2 -0
  88. package/dist/__tests__/streamable-http-transport.test.d.ts.map +1 -0
  89. package/dist/__tests__/structured-logger.test.d.ts +2 -0
  90. package/dist/__tests__/structured-logger.test.d.ts.map +1 -0
  91. package/dist/__tests__/thought-evaluator.test.d.ts +2 -0
  92. package/dist/__tests__/thought-evaluator.test.d.ts.map +1 -0
  93. package/dist/__tests__/thought-formatter.test.d.ts +2 -0
  94. package/dist/__tests__/thought-formatter.test.d.ts.map +1 -0
  95. package/dist/__tests__/thought-processor.test.d.ts +8 -0
  96. package/dist/__tests__/thought-processor.test.d.ts.map +1 -0
  97. package/dist/__tests__/tool-registry-cov.test.d.ts +2 -0
  98. package/dist/__tests__/tool-registry-cov.test.d.ts.map +1 -0
  99. package/dist/__tests__/tool-registry.test.d.ts +2 -0
  100. package/dist/__tests__/tool-registry.test.d.ts.map +1 -0
  101. package/dist/__tests__/tool-watcher.test.d.ts +2 -0
  102. package/dist/__tests__/tool-watcher.test.d.ts.map +1 -0
  103. package/dist/__tests__/worker-manager-cov.test.d.ts +2 -0
  104. package/dist/__tests__/worker-manager-cov.test.d.ts.map +1 -0
  105. package/dist/__tests__/worker-manager.test.d.ts +2 -0
  106. package/dist/__tests__/worker-manager.test.d.ts.map +1 -0
  107. package/dist/cache/DiscoveryCache.d.ts +269 -0
  108. package/dist/cache/DiscoveryCache.d.ts.map +1 -0
  109. package/dist/cache/DiscoveryCache.js +100 -0
  110. package/dist/cache/DiscoveryCache.js.map +1 -0
  111. package/dist/cli.d.ts +3 -0
  112. package/dist/cli.d.ts.map +1 -0
  113. package/dist/cli.js +114 -0
  114. package/dist/cli.js.map +1 -0
  115. package/dist/cluster/WorkerManager.d.ts +166 -0
  116. package/dist/cluster/WorkerManager.d.ts.map +1 -0
  117. package/dist/cluster/WorkerManager.js +202 -0
  118. package/dist/cluster/WorkerManager.js.map +1 -0
  119. package/dist/cluster/worker.d.ts +11 -0
  120. package/dist/cluster/worker.d.ts.map +1 -0
  121. package/dist/cluster/worker.js +36 -0
  122. package/dist/cluster/worker.js.map +1 -0
  123. package/dist/config/ConfigLoader.d.ts +224 -0
  124. package/dist/config/ConfigLoader.d.ts.map +1 -0
  125. package/dist/config/ConfigLoader.js +85 -0
  126. package/dist/config/ConfigLoader.js.map +1 -0
  127. package/dist/context/RequestContext.d.ts +61 -0
  128. package/dist/context/RequestContext.d.ts.map +1 -0
  129. package/dist/context/RequestContext.js +17 -0
  130. package/dist/context/RequestContext.js.map +1 -0
  131. package/dist/contracts/index.d.ts +10 -0
  132. package/dist/contracts/index.d.ts.map +1 -0
  133. package/dist/contracts/index.js +1 -0
  134. package/dist/contracts/interfaces.d.ts +107 -0
  135. package/dist/contracts/interfaces.d.ts.map +1 -0
  136. package/dist/contracts/interfaces.js +1 -0
  137. package/dist/core/HistoryManager.d.ts +514 -0
  138. package/dist/core/HistoryManager.d.ts.map +1 -0
  139. package/dist/core/HistoryManager.js +331 -0
  140. package/dist/core/HistoryManager.js.map +1 -0
  141. package/dist/core/IHistoryManager.d.ts +100 -0
  142. package/dist/core/IHistoryManager.d.ts.map +1 -0
  143. package/dist/core/IHistoryManager.js +1 -0
  144. package/dist/core/InputNormalizer.d.ts +139 -0
  145. package/dist/core/InputNormalizer.d.ts.map +1 -0
  146. package/dist/core/InputNormalizer.js +101 -0
  147. package/dist/core/InputNormalizer.js.map +1 -0
  148. package/dist/core/ThoughtEvaluator.d.ts +127 -0
  149. package/dist/core/ThoughtEvaluator.d.ts.map +1 -0
  150. package/dist/core/ThoughtEvaluator.js +346 -0
  151. package/dist/core/ThoughtEvaluator.js.map +1 -0
  152. package/dist/core/ThoughtFormatter.d.ts +133 -0
  153. package/dist/core/ThoughtFormatter.d.ts.map +1 -0
  154. package/dist/core/ThoughtFormatter.js +70 -0
  155. package/dist/core/ThoughtFormatter.js.map +1 -0
  156. package/dist/core/ThoughtProcessor.d.ts +218 -0
  157. package/dist/core/ThoughtProcessor.d.ts.map +1 -0
  158. package/dist/core/ThoughtProcessor.js +205 -0
  159. package/dist/core/ThoughtProcessor.js.map +1 -0
  160. package/dist/core/reasoning.d.ts +169 -0
  161. package/dist/core/reasoning.d.ts.map +1 -0
  162. package/dist/core/reasoning.js +1 -0
  163. package/dist/core/step.d.ts +45 -0
  164. package/dist/core/step.d.ts.map +1 -0
  165. package/dist/core/step.js +1 -0
  166. package/dist/core/thought.d.ts +190 -0
  167. package/dist/core/thought.d.ts.map +1 -0
  168. package/dist/core/thought.js +1 -0
  169. package/dist/di/Container.d.ts +226 -0
  170. package/dist/di/Container.d.ts.map +1 -0
  171. package/dist/di/Container.js +96 -0
  172. package/dist/di/Container.js.map +1 -0
  173. package/dist/di/ServiceRegistry.d.ts +32 -0
  174. package/dist/di/ServiceRegistry.d.ts.map +1 -0
  175. package/dist/di/ServiceRegistry.js +1 -0
  176. package/dist/errors.d.ts +482 -0
  177. package/dist/errors.d.ts.map +1 -0
  178. package/dist/errors.js +108 -0
  179. package/dist/errors.js.map +1 -0
  180. package/dist/health/HealthChecker.d.ts +73 -0
  181. package/dist/health/HealthChecker.d.ts.map +1 -0
  182. package/dist/health/HealthChecker.js +69 -0
  183. package/dist/health/HealthChecker.js.map +1 -0
  184. package/dist/index.d.ts +2 -0
  185. package/dist/index.d.ts.map +1 -0
  186. package/dist/index.js +1 -0
  187. package/dist/lib.d.ts +205 -0
  188. package/dist/lib.d.ts.map +1 -0
  189. package/dist/lib.js +219 -0
  190. package/dist/lib.js.map +1 -0
  191. package/dist/logger/NullLogger.d.ts +154 -0
  192. package/dist/logger/NullLogger.d.ts.map +1 -0
  193. package/dist/logger/NullLogger.js +24 -0
  194. package/dist/logger/NullLogger.js.map +1 -0
  195. package/dist/logger/StructuredLogger.d.ts +327 -0
  196. package/dist/logger/StructuredLogger.d.ts.map +1 -0
  197. package/dist/logger/StructuredLogger.js +72 -0
  198. package/dist/logger/StructuredLogger.js.map +1 -0
  199. package/dist/metrics/__tests__/metrics.test.d.ts +2 -0
  200. package/dist/metrics/__tests__/metrics.test.d.ts.map +1 -0
  201. package/dist/metrics/metrics.impl.d.ts +252 -0
  202. package/dist/metrics/metrics.impl.d.ts.map +1 -0
  203. package/dist/metrics/metrics.impl.js +197 -0
  204. package/dist/metrics/metrics.impl.js.map +1 -0
  205. package/dist/persistence/FilePersistence.d.ts +66 -0
  206. package/dist/persistence/FilePersistence.d.ts.map +1 -0
  207. package/dist/persistence/FilePersistence.js +132 -0
  208. package/dist/persistence/FilePersistence.js.map +1 -0
  209. package/dist/persistence/MemoryPersistence.d.ts +68 -0
  210. package/dist/persistence/MemoryPersistence.d.ts.map +1 -0
  211. package/dist/persistence/MemoryPersistence.js +51 -0
  212. package/dist/persistence/MemoryPersistence.js.map +1 -0
  213. package/dist/persistence/PersistenceBackend.d.ts +69 -0
  214. package/dist/persistence/PersistenceBackend.d.ts.map +1 -0
  215. package/dist/persistence/PersistenceBackend.js +1 -0
  216. package/dist/persistence/PersistenceFactory.d.ts +21 -0
  217. package/dist/persistence/PersistenceFactory.d.ts.map +1 -0
  218. package/dist/persistence/PersistenceFactory.js +25 -0
  219. package/dist/persistence/PersistenceFactory.js.map +1 -0
  220. package/dist/persistence/SqlitePersistence.d.ts +60 -0
  221. package/dist/persistence/SqlitePersistence.d.ts.map +1 -0
  222. package/dist/persistence/SqlitePersistence.js +136 -0
  223. package/dist/persistence/SqlitePersistence.js.map +1 -0
  224. package/dist/pool/ConnectionPool.d.ts +215 -0
  225. package/dist/pool/ConnectionPool.d.ts.map +1 -0
  226. package/dist/pool/ConnectionPool.js +187 -0
  227. package/dist/pool/ConnectionPool.js.map +1 -0
  228. package/dist/registry/BaseRegistry.d.ts +203 -0
  229. package/dist/registry/BaseRegistry.d.ts.map +1 -0
  230. package/dist/registry/BaseRegistry.js +165 -0
  231. package/dist/registry/BaseRegistry.js.map +1 -0
  232. package/dist/registry/SkillRegistry.d.ts +69 -0
  233. package/dist/registry/SkillRegistry.d.ts.map +1 -0
  234. package/dist/registry/SkillRegistry.js +88 -0
  235. package/dist/registry/SkillRegistry.js.map +1 -0
  236. package/dist/registry/ToolRegistry.d.ts +69 -0
  237. package/dist/registry/ToolRegistry.d.ts.map +1 -0
  238. package/dist/registry/ToolRegistry.js +93 -0
  239. package/dist/registry/ToolRegistry.js.map +1 -0
  240. package/dist/sanitize.d.ts +63 -0
  241. package/dist/sanitize.d.ts.map +1 -0
  242. package/dist/sanitize.js +14 -0
  243. package/dist/sanitize.js.map +1 -0
  244. package/dist/schema.d.ts +531 -0
  245. package/dist/schema.d.ts.map +1 -0
  246. package/dist/schema.js +204 -0
  247. package/dist/schema.js.map +1 -0
  248. package/dist/telemetry/Telemetry.d.ts +36 -0
  249. package/dist/telemetry/Telemetry.d.ts.map +1 -0
  250. package/dist/telemetry/Telemetry.js +68 -0
  251. package/dist/telemetry/Telemetry.js.map +1 -0
  252. package/dist/telemetry/__tests__/Telemetry.test.d.ts +2 -0
  253. package/dist/telemetry/__tests__/Telemetry.test.d.ts.map +1 -0
  254. package/dist/transport/BaseTransport.d.ts +184 -0
  255. package/dist/transport/BaseTransport.d.ts.map +1 -0
  256. package/dist/transport/BaseTransport.js +200 -0
  257. package/dist/transport/BaseTransport.js.map +1 -0
  258. package/dist/transport/HttpHelpers.d.ts +60 -0
  259. package/dist/transport/HttpHelpers.d.ts.map +1 -0
  260. package/dist/transport/HttpHelpers.js +50 -0
  261. package/dist/transport/HttpHelpers.js.map +1 -0
  262. package/dist/transport/HttpTransport.d.ts +134 -0
  263. package/dist/transport/HttpTransport.d.ts.map +1 -0
  264. package/dist/transport/HttpTransport.js +175 -0
  265. package/dist/transport/HttpTransport.js.map +1 -0
  266. package/dist/transport/SseTransport.d.ts +133 -0
  267. package/dist/transport/SseTransport.d.ts.map +1 -0
  268. package/dist/transport/SseTransport.js +318 -0
  269. package/dist/transport/SseTransport.js.map +1 -0
  270. package/dist/transport/StreamableHttpTransport.d.ts +224 -0
  271. package/dist/transport/StreamableHttpTransport.d.ts.map +1 -0
  272. package/dist/transport/StreamableHttpTransport.js +407 -0
  273. package/dist/transport/StreamableHttpTransport.js.map +1 -0
  274. package/dist/types/disposable.d.ts +22 -0
  275. package/dist/types/disposable.d.ts.map +1 -0
  276. package/dist/types/disposable.js +1 -0
  277. package/dist/types/server-config.d.ts +32 -0
  278. package/dist/types/server-config.d.ts.map +1 -0
  279. package/dist/types/server-config.js +1 -0
  280. package/dist/types/skill.d.ts +69 -0
  281. package/dist/types/skill.d.ts.map +1 -0
  282. package/dist/types/skill.js +1 -0
  283. package/dist/types/tool.d.ts +68 -0
  284. package/dist/types/tool.d.ts.map +1 -0
  285. package/dist/types/tool.js +1 -0
  286. package/dist/watchers/SkillWatcher.d.ts +132 -0
  287. package/dist/watchers/SkillWatcher.d.ts.map +1 -0
  288. package/dist/watchers/SkillWatcher.js +73 -0
  289. package/dist/watchers/SkillWatcher.js.map +1 -0
  290. package/dist/watchers/ToolWatcher.d.ts +109 -0
  291. package/dist/watchers/ToolWatcher.d.ts.map +1 -0
  292. package/dist/watchers/ToolWatcher.js +71 -0
  293. package/dist/watchers/ToolWatcher.js.map +1 -0
  294. package/package.json +95 -0
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Input normalization for common LLM field name mistakes.
3
+ *
4
+ * This module provides normalization logic to handle common mistakes
5
+ * that LLMs make when generating field names, such as using singular
6
+ * instead of plural forms (e.g., `recommended_tool` vs `recommended_tools`).
7
+ *
8
+ * It also fills in sensible defaults for missing fields in `previous_steps`,
9
+ * which LLMs naturally provide as partial/skeletal data (historical context).
10
+ *
11
+ * @module processor
12
+ */
13
+ import type { ThoughtData } from './thought.js';
14
+ /**
15
+ * Recursively sanitizes all string values within an unknown structure.
16
+ * Walks into plain objects and arrays to reach deeply nested strings.
17
+ * Non-plain objects (Date, RegExp, etc.) are returned as-is.
18
+ *
19
+ * @param value - The value to sanitize recursively
20
+ * @returns The sanitized value with all nested strings cleaned
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * sanitizeRecursive('<script>x</script>'); // 'x'
25
+ * sanitizeRecursive({ a: { b: '<iframe>y' } }); // { a: { b: 'y' } }
26
+ * sanitizeRecursive(['a\x00b', 42]); // ['ab', 42]
27
+ * ```
28
+ */
29
+ export declare function sanitizeRecursive(value: unknown): unknown;
30
+ /**
31
+ * Sanitizes and validates a branch ID to prevent path traversal attacks.
32
+ *
33
+ * @param branchId - The branch ID to sanitize
34
+ * @returns The sanitized branch ID
35
+ * @throws ValidationError if the branch ID contains invalid characters
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * sanitizeBranchId('my-branch_01'); // 'my-branch_01'
40
+ * sanitizeBranchId('../etc/passwd'); // throws ValidationError
41
+ * ```
42
+ */
43
+ export declare function sanitizeBranchId(branchId: string): string;
44
+ /**
45
+ * Sanitizes and validates a session ID.
46
+ *
47
+ * @param sessionId - The session ID to sanitize
48
+ * @returns The sanitized session ID, or undefined if invalid after sanitization
49
+ *
50
+ * @example
51
+ * ```typescript
52
+ * sanitizeSessionId('analysis-task-42'); // 'analysis-task-42'
53
+ * sanitizeSessionId('bad session!'); // undefined (stripped)
54
+ * ```
55
+ */
56
+ export declare function sanitizeSessionId(sessionId: string): string | undefined;
57
+ /**
58
+ * Normalizes reasoning-specific fields on a thought input object.
59
+ * Always applies reasoning normalization — reasoning is the default pipeline.
60
+ * Applies the following normalization rules:
61
+ * - Defaults `thought_type` to `'regular'` if not provided
62
+ * - Clamps `quality_score` to [0, 1] range
63
+ * - Clamps `confidence` to [0, 1] range
64
+ * - Sanitizes `hypothesis_id` using `sanitizeBranchId` pattern
65
+ * - Filters `synthesis_sources` to positive integers only
66
+ * - Filters `merge_from_thoughts` to positive integers only
67
+ * - Sanitizes each entry in `merge_branch_ids`
68
+ * - Defaults `reasoning_depth` to `'moderate'` for hypothesis/verification types
69
+ *
70
+ * @param input - The mutable normalized input object to apply reasoning defaults to
71
+ *
72
+ * @example
73
+ * ```typescript
74
+ * const input: Record<string, unknown> = { thought_type: 'hypothesis', quality_score: 1.5 };
75
+ * normalizeReasoningFields(input);
76
+ * // input.quality_score === 1, input.reasoning_depth === 'moderate'
77
+ * ```
78
+ */
79
+ export declare function normalizeReasoningFields(input: Record<string, unknown>): void;
80
+ /**
81
+ * Normalizes thought input data by fixing common LLM field name mistakes.
82
+ *
83
+ * This function handles cases where LLMs incorrectly use singular forms
84
+ * of field names that should be plural. It applies normalization to both
85
+ * `current_step` and `previous_steps` fields.
86
+ *
87
+ * The normalization is applied BEFORE schema validation, allowing the
88
+ * strict Valibot schema to remain correct while still being tolerant
89
+ * of common LLM mistakes.
90
+ *
91
+ * @param input - The raw thought input data to normalize
92
+ * @returns Normalized thought data with correct field names
93
+ *
94
+ * @remarks
95
+ * **Normalization Rules:**
96
+ * - `recommended_tool` (singular) → `recommended_tools` (plural)
97
+ * - `recommended_skill` (singular) → `recommended_skills` (plural)
98
+ * - Applied to `current_step` if present (strict mode)
99
+ * - Applied to all items in `previous_steps` if present (lenient mode with defaults)
100
+ *
101
+ * **Design Rationale:**
102
+ * LLMs sometimes use singular field names even when the schema explicitly
103
+ * defines plural forms. Rather than forcing the LLM to be perfect (which
104
+ * leads to cryptic validation errors), we normalize the input to handle
105
+ * these common mistakes gracefully.
106
+ *
107
+ * Additionally, LLMs naturally provide complete data for `current_step`
108
+ * but only partial/skeletal data for `previous_steps` (historical context).
109
+ * The lenient mode for `previous_steps` fills in sensible defaults:
110
+ * - `confidence`: 0.5 for missing tool recommendation confidence
111
+ * - `priority`: 999 for missing tool recommendation priority
112
+ * - `rationale`: empty string for missing tool recommendation rationale
113
+ * - `expected_outcome`: empty string for missing step expected outcome
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * const input = {
118
+ * thought: 'I need to analyze the data',
119
+ * thought_number: 1,
120
+ * total_thoughts: 3,
121
+ * next_thought_needed: true,
122
+ * current_step: {
123
+ * step_description: 'Read the data file',
124
+ * recommended_tool: [{ tool_name: 'Read', confidence: 0.9, rationale: 'test', priority: 1 }],
125
+ * expected_outcome: 'Data loaded'
126
+ * },
127
+ * previous_steps: [{
128
+ * step_description: 'Previous step',
129
+ * recommended_tools: [{ tool_name: 'Grep', rationale: 'Search code' }]
130
+ * }]
131
+ * };
132
+ *
133
+ * const normalized = normalizeInput(input);
134
+ * // current_step: recommended_tools exists (plural form)
135
+ * // previous_steps[0]: confidence=0.5, priority=999, expected_outcome='' filled in
136
+ * ```
137
+ */
138
+ export declare function normalizeInput(input: unknown): ThoughtData;
139
+ //# sourceMappingURL=InputNormalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InputNormalizer.d.ts","sourceRoot":"","sources":["../../src/core/InputNormalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAiBhD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAuBzD;AAcD;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CASzD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAQvE;AA6JD;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAsD7E;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAiD1D"}
@@ -0,0 +1,101 @@
1
+ import { ValidationError } from "../errors.js";
2
+ import { sanitizeString } from "../sanitize.js";
3
+ const DEFAULT_TOOL_CONFIDENCE = 0.5;
4
+ const DEFAULT_TOOL_PRIORITY = 999;
5
+ const DEFAULT_TOOL_RATIONALE = '';
6
+ const DEFAULT_STEP_OUTCOME = '';
7
+ const DEFAULT_SKILL_CONFIDENCE = 0.5;
8
+ const DEFAULT_SKILL_PRIORITY = 999;
9
+ const DEFAULT_SKILL_RATIONALE = '';
10
+ function sanitizeRecursive(value) {
11
+ if (null == value) return value;
12
+ if ('string' == typeof value) return sanitizeString(value);
13
+ if (Array.isArray(value)) return value.map((item)=>sanitizeRecursive(item));
14
+ if ('object' == typeof value) {
15
+ const proto = Object.getPrototypeOf(value);
16
+ if (proto !== Object.prototype && null !== proto) return value;
17
+ const result = {};
18
+ for (const [key, val] of Object.entries(value))result[key] = sanitizeRecursive(val);
19
+ return result;
20
+ }
21
+ return value;
22
+ }
23
+ const BRANCH_ID_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;
24
+ const SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]{1,100}$/;
25
+ function sanitizeBranchId(branchId) {
26
+ if (!BRANCH_ID_PATTERN.test(branchId)) throw new ValidationError('branch_id', 'Invalid format - must be 1-64 alphanumeric characters, hyphens, or underscores only');
27
+ return branchId;
28
+ }
29
+ function sanitizeSessionId(sessionId) {
30
+ const cleaned = sanitizeString(sessionId);
31
+ if (!SESSION_ID_PATTERN.test(cleaned)) return;
32
+ return cleaned;
33
+ }
34
+ function normalizeToolRecommendation(tool) {
35
+ const normalized = {
36
+ ...tool
37
+ };
38
+ if (!('confidence' in normalized) || void 0 === normalized.confidence) normalized.confidence = DEFAULT_TOOL_CONFIDENCE;
39
+ if (!('priority' in normalized) || void 0 === normalized.priority) normalized.priority = DEFAULT_TOOL_PRIORITY;
40
+ if (!('rationale' in normalized) || void 0 === normalized.rationale) normalized.rationale = DEFAULT_TOOL_RATIONALE;
41
+ return normalized;
42
+ }
43
+ function normalizeSkillRecommendation(skill) {
44
+ const normalized = {
45
+ ...skill
46
+ };
47
+ if (!('confidence' in normalized) || void 0 === normalized.confidence) normalized.confidence = DEFAULT_SKILL_CONFIDENCE;
48
+ if (!('priority' in normalized) || void 0 === normalized.priority) normalized.priority = DEFAULT_SKILL_PRIORITY;
49
+ if (!('rationale' in normalized) || void 0 === normalized.rationale) normalized.rationale = DEFAULT_SKILL_RATIONALE;
50
+ return normalized;
51
+ }
52
+ function normalizeStepRecommendation(step, lenient) {
53
+ const normalized = {
54
+ ...step
55
+ };
56
+ if ('recommended_tool' in normalized && !('recommended_tools' in normalized)) {
57
+ normalized.recommended_tools = normalized.recommended_tool;
58
+ delete normalized.recommended_tool;
59
+ }
60
+ if ('recommended_skill' in normalized && !('recommended_skills' in normalized)) {
61
+ normalized.recommended_skills = normalized.recommended_skill;
62
+ delete normalized.recommended_skill;
63
+ }
64
+ if (Array.isArray(normalized.recommended_tools)) normalized.recommended_tools = normalized.recommended_tools.map((tool)=>'object' == typeof tool && null !== tool ? normalizeToolRecommendation(tool) : tool);
65
+ if (Array.isArray(normalized.recommended_skills)) normalized.recommended_skills = normalized.recommended_skills.map((skill)=>'object' == typeof skill && null !== skill ? normalizeSkillRecommendation(skill) : skill);
66
+ if (lenient && !('expected_outcome' in normalized)) normalized.expected_outcome = DEFAULT_STEP_OUTCOME;
67
+ return normalized;
68
+ }
69
+ function normalizeReasoningFields(input) {
70
+ if (!('thought_type' in input) || void 0 === input.thought_type) input.thought_type = 'regular';
71
+ if ('number' == typeof input.quality_score) input.quality_score = Math.max(0, Math.min(1, input.quality_score));
72
+ if ('number' == typeof input.confidence) input.confidence = Math.max(0, Math.min(1, input.confidence));
73
+ if ('string' == typeof input.hypothesis_id) input.hypothesis_id = sanitizeBranchId(input.hypothesis_id);
74
+ if (Array.isArray(input.synthesis_sources)) input.synthesis_sources = input.synthesis_sources.filter((v)=>'number' == typeof v && Number.isInteger(v) && v > 0);
75
+ if (Array.isArray(input.merge_from_thoughts)) input.merge_from_thoughts = input.merge_from_thoughts.filter((v)=>'number' == typeof v && Number.isInteger(v) && v > 0);
76
+ if (Array.isArray(input.merge_branch_ids)) input.merge_branch_ids = input.merge_branch_ids.map((id)=>{
77
+ if ('string' == typeof id) return sanitizeBranchId(id);
78
+ return id;
79
+ });
80
+ if (('hypothesis' === input.thought_type || 'verification' === input.thought_type) && !('reasoning_depth' in input)) input.reasoning_depth = 'moderate';
81
+ }
82
+ function normalizeInput(input) {
83
+ if ('object' != typeof input || null === input) return input;
84
+ const normalized = {
85
+ ...input
86
+ };
87
+ if (normalized.current_step && 'object' == typeof normalized.current_step) normalized.current_step = normalizeStepRecommendation(normalized.current_step, false);
88
+ if (Array.isArray(normalized.previous_steps) && normalized.previous_steps.length > 0) normalized.previous_steps = normalized.previous_steps.map((step)=>'object' == typeof step && null !== step ? normalizeStepRecommendation(step, true) : step);
89
+ if ('string' == typeof normalized.branch_id) normalized.branch_id = sanitizeBranchId(normalized.branch_id);
90
+ if ('string' == typeof normalized.session_id) {
91
+ const sanitized = sanitizeSessionId(normalized.session_id);
92
+ if (void 0 === sanitized) delete normalized.session_id;
93
+ else normalized.session_id = sanitized;
94
+ }
95
+ normalizeReasoningFields(normalized);
96
+ const sanitized = sanitizeRecursive(normalized);
97
+ return sanitized;
98
+ }
99
+ export { normalizeInput, normalizeReasoningFields, sanitizeBranchId, sanitizeRecursive, sanitizeSessionId };
100
+
101
+ //# sourceMappingURL=InputNormalizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core/InputNormalizer.js","sources":["../../src/core/InputNormalizer.ts"],"sourcesContent":["/**\n * Input normalization for common LLM field name mistakes.\n *\n * This module provides normalization logic to handle common mistakes\n * that LLMs make when generating field names, such as using singular\n * instead of plural forms (e.g., `recommended_tool` vs `recommended_tools`).\n *\n * It also fills in sensible defaults for missing fields in `previous_steps`,\n * which LLMs naturally provide as partial/skeletal data (historical context).\n *\n * @module processor\n */\n\nimport { ValidationError } from '../errors.js';\nimport { sanitizeString } from '../sanitize.js';\nimport type { ThoughtData } from './thought.js';\n\n/**\n * Default values for missing partial tool recommendation fields.\n */\nconst DEFAULT_TOOL_CONFIDENCE = 0.5;\nconst DEFAULT_TOOL_PRIORITY = 999;\nconst DEFAULT_TOOL_RATIONALE = '';\nconst DEFAULT_STEP_OUTCOME = '';\n\n/**\n * Default values for missing skill recommendation fields.\n */\nconst DEFAULT_SKILL_CONFIDENCE = 0.5;\nconst DEFAULT_SKILL_PRIORITY = 999;\nconst DEFAULT_SKILL_RATIONALE = '';\n\n/**\n * Recursively sanitizes all string values within an unknown structure.\n * Walks into plain objects and arrays to reach deeply nested strings.\n * Non-plain objects (Date, RegExp, etc.) are returned as-is.\n *\n * @param value - The value to sanitize recursively\n * @returns The sanitized value with all nested strings cleaned\n *\n * @example\n * ```typescript\n * sanitizeRecursive('<script>x</script>'); // 'x'\n * sanitizeRecursive({ a: { b: '<iframe>y' } }); // { a: { b: 'y' } }\n * sanitizeRecursive(['a\\x00b', 42]); // ['ab', 42]\n * ```\n */\nexport function sanitizeRecursive(value: unknown): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn value;\n\t}\n\tif (typeof value === 'string') {\n\t\treturn sanitizeString(value);\n\t}\n\tif (Array.isArray(value)) {\n\t\treturn value.map((item) => sanitizeRecursive(item));\n\t}\n\tif (typeof value === 'object') {\n\t\t// Only recurse into plain objects — skip Date, RegExp, Map, Set, etc.\n\t\tconst proto = Object.getPrototypeOf(value);\n\t\tif (proto !== Object.prototype && proto !== null) {\n\t\t\treturn value;\n\t\t}\n\t\tconst result: Record<string, unknown> = {};\n\t\tfor (const [key, val] of Object.entries(value as Record<string, unknown>)) {\n\t\t\tresult[key] = sanitizeRecursive(val);\n\t\t}\n\t\treturn result;\n\t}\n\treturn value;\n}\n\n/**\n * Valid branch ID pattern: alphanumeric, hyphens, underscores only.\n * Prevents path traversal attacks by rejecting special characters like / . \\ etc.\n */\nconst BRANCH_ID_PATTERN = /^[a-zA-Z0-9_-]{1,64}$/;\n\n/**\n * Valid session ID pattern: alphanumeric, hyphens, underscores, 1-100 chars.\n * Same as branch_id but with longer max length to allow compound identifiers.\n */\nconst SESSION_ID_PATTERN = /^[a-zA-Z0-9_-]{1,100}$/;\n\n/**\n * Sanitizes and validates a branch ID to prevent path traversal attacks.\n *\n * @param branchId - The branch ID to sanitize\n * @returns The sanitized branch ID\n * @throws ValidationError if the branch ID contains invalid characters\n *\n * @example\n * ```typescript\n * sanitizeBranchId('my-branch_01'); // 'my-branch_01'\n * sanitizeBranchId('../etc/passwd'); // throws ValidationError\n * ```\n */\nexport function sanitizeBranchId(branchId: string): string {\n\t// Validate format\n\tif (!BRANCH_ID_PATTERN.test(branchId)) {\n\t\tthrow new ValidationError(\n\t\t\t'branch_id',\n\t\t\t'Invalid format - must be 1-64 alphanumeric characters, hyphens, or underscores only'\n\t\t);\n\t}\n\treturn branchId;\n}\n\n/**\n * Sanitizes and validates a session ID.\n *\n * @param sessionId - The session ID to sanitize\n * @returns The sanitized session ID, or undefined if invalid after sanitization\n *\n * @example\n * ```typescript\n * sanitizeSessionId('analysis-task-42'); // 'analysis-task-42'\n * sanitizeSessionId('bad session!'); // undefined (stripped)\n * ```\n */\nexport function sanitizeSessionId(sessionId: string): string | undefined {\n\t// First sanitize control characters\n\tconst cleaned = sanitizeString(sessionId);\n\t// Validate format after sanitization\n\tif (!SESSION_ID_PATTERN.test(cleaned)) {\n\t\treturn undefined;\n\t}\n\treturn cleaned;\n}\n\n/**\n * Normalizes tool recommendation objects with default values.\n * Normalizes tool recommendation objects with default values.\n *\n * Fills in sensible defaults for missing optional fields:\n * - `confidence`: 0.5\n * - `priority`: 999\n * - `rationale`: empty string\n *\n * @param tool - The tool recommendation to normalize\n * @returns The normalized tool recommendation with defaults filled in\n *\n * @example\n * ```typescript\n * const input = { tool_name: 'Read', rationale: 'Read the file' };\n * const normalized = normalizeToolRecommendation(input);\n * // { tool_name: 'Read', rationale: 'Read the file', confidence: 0.5, priority: 999 }\n * ```\n */\nfunction normalizeToolRecommendation(tool: Record<string, unknown>): Record<string, unknown> {\n\tconst normalized: Record<string, unknown> = { ...tool };\n\n\t// Fill in default confidence if missing\n\tif (!('confidence' in normalized) || normalized.confidence === undefined) {\n\t\tnormalized.confidence = DEFAULT_TOOL_CONFIDENCE;\n\t}\n\n\t// Fill in default priority if missing\n\tif (!('priority' in normalized) || normalized.priority === undefined) {\n\t\tnormalized.priority = DEFAULT_TOOL_PRIORITY;\n\t}\n\n\t// Fill in default rationale if missing\n\tif (!('rationale' in normalized) || normalized.rationale === undefined) {\n\t\tnormalized.rationale = DEFAULT_TOOL_RATIONALE;\n\t}\n\n\treturn normalized;\n}\n\n/**\n * Normalizes skill recommendation objects with default values.\n *\n * Fills in sensible defaults for missing optional fields:\n * - `confidence`: 0.5\n * - `priority`: 999\n * - `rationale`: empty string\n *\n * @param skill - The skill recommendation to normalize\n * @returns The normalized skill recommendation with defaults filled in\n *\n * @example\n * ```typescript\n * const input = { skill_name: 'ast-grep' };\n * const normalized = normalizeSkillRecommendation(input);\n * // { skill_name: 'ast-grep', confidence: 0.5, priority: 999, rationale: '' }\n * ```\n */\nfunction normalizeSkillRecommendation(skill: Record<string, unknown>): Record<string, unknown> {\n\tconst normalized: Record<string, unknown> = { ...skill };\n\n\t// Fill in default confidence if missing\n\tif (!('confidence' in normalized) || normalized.confidence === undefined) {\n\t\tnormalized.confidence = DEFAULT_SKILL_CONFIDENCE;\n\t}\n\n\t// Fill in default priority if missing\n\tif (!('priority' in normalized) || normalized.priority === undefined) {\n\t\tnormalized.priority = DEFAULT_SKILL_PRIORITY;\n\t}\n\n\t// Fill in default rationale if missing\n\tif (!('rationale' in normalized) || normalized.rationale === undefined) {\n\t\tnormalized.rationale = DEFAULT_SKILL_RATIONALE;\n\t}\n\n\treturn normalized;\n}\n/**\n * Normalizes step recommendation objects.\n *\n * Handles common field name mistakes:\n * - `recommended_tool` (singular) → `recommended_tools` (plural)\n * - `recommended_skill` (singular) → `recommended_skills` (plural)\n *\n * Also normalizes tool recommendations within the step to fill in defaults.\n *\n * @param step - The step recommendation to normalize\n * @param lenient - Whether to use lenient mode (fill in defaults for missing fields)\n * @returns The normalized step recommendation\n *\n * @example\n * ```typescript\n * // Strict mode (for current_step)\n * const input = {\n * step_description: 'Analyze data',\n * recommended_tool: [{ tool_name: 'Read', confidence: 0.9, rationale: 'test', priority: 1 }],\n * expected_outcome: 'Data analyzed'\n * };\n * const normalized = normalizeStepRecommendation(input, false);\n * // normalized.recommended_tools exists (plural form)\n *\n * // Lenient mode (for previous_steps)\n * const partialInput = {\n * step_description: 'Read file',\n * recommended_tools: [{ tool_name: 'Read', rationale: 'Read file' }]\n * };\n * const normalized = normalizeStepRecommendation(partialInput, true);\n * // confidence: 0.5, priority: 999, expected_outcome: '' filled in\n * ```\n */\nfunction normalizeStepRecommendation(\n\tstep: Record<string, unknown>,\n\tlenient: boolean\n): Record<string, unknown> {\n\tconst normalized: Record<string, unknown> = { ...step };\n\n\t// Transform `recommended_tool` (singular) → `recommended_tools` (plural)\n\tif ('recommended_tool' in normalized && !('recommended_tools' in normalized)) {\n\t\tnormalized.recommended_tools = normalized.recommended_tool;\n\t\tdelete normalized.recommended_tool;\n\t}\n\n\t// Transform `recommended_skill` (singular) → `recommended_skills` (plural)\n\tif ('recommended_skill' in normalized && !('recommended_skills' in normalized)) {\n\t\tnormalized.recommended_skills = normalized.recommended_skill;\n\t\tdelete normalized.recommended_skill;\n\t}\n\n\t// Normalize recommended_tools array if present\n\tif (Array.isArray(normalized.recommended_tools)) {\n\t\tnormalized.recommended_tools = normalized.recommended_tools.map((tool) =>\n\t\t\ttypeof tool === 'object' && tool !== null\n\t\t\t\t? normalizeToolRecommendation(tool as Record<string, unknown>)\n\t\t\t\t: tool\n\t\t);\n\t}\n\n\t// Normalize recommended_skills array if present\n\tif (Array.isArray(normalized.recommended_skills)) {\n\t\tnormalized.recommended_skills = normalized.recommended_skills.map((skill) =>\n\t\t\ttypeof skill === 'object' && skill !== null\n\t\t\t\t? normalizeSkillRecommendation(skill as Record<string, unknown>)\n\t\t\t\t: skill\n\t\t);\n\t}\n\t// In lenient mode, fill in default expected_outcome if missing\n\tif (lenient && !('expected_outcome' in normalized)) {\n\t\tnormalized.expected_outcome = DEFAULT_STEP_OUTCOME;\n\t}\n\n\treturn normalized;\n}\n\n\n/**\n * Normalizes reasoning-specific fields on a thought input object.\n * Always applies reasoning normalization — reasoning is the default pipeline.\n * Applies the following normalization rules:\n * - Defaults `thought_type` to `'regular'` if not provided\n * - Clamps `quality_score` to [0, 1] range\n * - Clamps `confidence` to [0, 1] range\n * - Sanitizes `hypothesis_id` using `sanitizeBranchId` pattern\n * - Filters `synthesis_sources` to positive integers only\n * - Filters `merge_from_thoughts` to positive integers only\n * - Sanitizes each entry in `merge_branch_ids`\n * - Defaults `reasoning_depth` to `'moderate'` for hypothesis/verification types\n *\n * @param input - The mutable normalized input object to apply reasoning defaults to\n *\n * @example\n * ```typescript\n * const input: Record<string, unknown> = { thought_type: 'hypothesis', quality_score: 1.5 };\n * normalizeReasoningFields(input);\n * // input.quality_score === 1, input.reasoning_depth === 'moderate'\n * ```\n */\nexport function normalizeReasoningFields(input: Record<string, unknown>): void {\n\t// Always apply reasoning field normalization — reasoning is the default pipeline\n\t// Default thought_type to 'regular'\n\n\tif (!('thought_type' in input) || input.thought_type === undefined) {\n\t\tinput.thought_type = 'regular';\n\t}\n\n\t// Clamp quality_score to [0, 1]\n\tif (typeof input.quality_score === 'number') {\n\t\tinput.quality_score = Math.max(0, Math.min(1, input.quality_score));\n\t}\n\n\t// Clamp confidence to [0, 1]\n\tif (typeof input.confidence === 'number') {\n\t\tinput.confidence = Math.max(0, Math.min(1, input.confidence));\n\t}\n\n\t// Sanitize hypothesis_id (same rules as branch_id)\n\tif (typeof input.hypothesis_id === 'string') {\n\t\tinput.hypothesis_id = sanitizeBranchId(input.hypothesis_id);\n\t}\n\n\t// Filter synthesis_sources to positive integers only\n\tif (Array.isArray(input.synthesis_sources)) {\n\t\tinput.synthesis_sources = input.synthesis_sources.filter(\n\t\t\t(v: unknown) => typeof v === 'number' && Number.isInteger(v) && v > 0\n\t\t);\n\t}\n\n\t// Filter merge_from_thoughts to positive integers only\n\tif (Array.isArray(input.merge_from_thoughts)) {\n\t\tinput.merge_from_thoughts = input.merge_from_thoughts.filter(\n\t\t\t(v: unknown) => typeof v === 'number' && Number.isInteger(v) && v > 0\n\t\t);\n\t}\n\n\t// Sanitize merge_branch_ids entries\n\tif (Array.isArray(input.merge_branch_ids)) {\n\t\tinput.merge_branch_ids = input.merge_branch_ids.map((id: unknown) => {\n\t\t\tif (typeof id === 'string') {\n\t\t\t\treturn sanitizeBranchId(id);\n\t\t\t}\n\t\t\treturn id;\n\t\t});\n\t}\n\n\t// Default reasoning_depth to 'moderate' for hypothesis/verification types\n\tif (\n\t\t(input.thought_type === 'hypothesis' || input.thought_type === 'verification') &&\n\t\t!('reasoning_depth' in input)\n\t) {\n\t\tinput.reasoning_depth = 'moderate';\n\t}\n}\n\n/**\n * Normalizes thought input data by fixing common LLM field name mistakes.\n *\n * This function handles cases where LLMs incorrectly use singular forms\n * of field names that should be plural. It applies normalization to both\n * `current_step` and `previous_steps` fields.\n *\n * The normalization is applied BEFORE schema validation, allowing the\n * strict Valibot schema to remain correct while still being tolerant\n * of common LLM mistakes.\n *\n * @param input - The raw thought input data to normalize\n * @returns Normalized thought data with correct field names\n *\n * @remarks\n * **Normalization Rules:**\n * - `recommended_tool` (singular) → `recommended_tools` (plural)\n * - `recommended_skill` (singular) → `recommended_skills` (plural)\n * - Applied to `current_step` if present (strict mode)\n * - Applied to all items in `previous_steps` if present (lenient mode with defaults)\n *\n * **Design Rationale:**\n * LLMs sometimes use singular field names even when the schema explicitly\n * defines plural forms. Rather than forcing the LLM to be perfect (which\n * leads to cryptic validation errors), we normalize the input to handle\n * these common mistakes gracefully.\n *\n * Additionally, LLMs naturally provide complete data for `current_step`\n * but only partial/skeletal data for `previous_steps` (historical context).\n * The lenient mode for `previous_steps` fills in sensible defaults:\n * - `confidence`: 0.5 for missing tool recommendation confidence\n * - `priority`: 999 for missing tool recommendation priority\n * - `rationale`: empty string for missing tool recommendation rationale\n * - `expected_outcome`: empty string for missing step expected outcome\n *\n * @example\n * ```typescript\n * const input = {\n * thought: 'I need to analyze the data',\n * thought_number: 1,\n * total_thoughts: 3,\n * next_thought_needed: true,\n * current_step: {\n * step_description: 'Read the data file',\n * recommended_tool: [{ tool_name: 'Read', confidence: 0.9, rationale: 'test', priority: 1 }],\n * expected_outcome: 'Data loaded'\n * },\n * previous_steps: [{\n * step_description: 'Previous step',\n * recommended_tools: [{ tool_name: 'Grep', rationale: 'Search code' }]\n * }]\n * };\n *\n * const normalized = normalizeInput(input);\n * // current_step: recommended_tools exists (plural form)\n * // previous_steps[0]: confidence=0.5, priority=999, expected_outcome='' filled in\n * ```\n */\nexport function normalizeInput(input: unknown): ThoughtData {\n\tif (typeof input !== 'object' || input === null) {\n\t\treturn input as ThoughtData;\n\t}\n\n\tconst normalized = { ...input } as Record<string, unknown>;\n\n\t// Normalize current_step if present (strict mode - no defaults)\n\tif (normalized.current_step && typeof normalized.current_step === 'object') {\n\t\tnormalized.current_step = normalizeStepRecommendation(\n\t\t\tnormalized.current_step as Record<string, unknown>,\n\t\t\tfalse // strict mode\n\t\t);\n\t}\n\n\n\t// Normalize all items in previous_steps if present (lenient mode - with defaults)\n\tif (Array.isArray(normalized.previous_steps) && normalized.previous_steps.length > 0) {\n\t\tnormalized.previous_steps = normalized.previous_steps.map((step) =>\n\t\t\ttypeof step === 'object' && step !== null\n\t\t\t\t? normalizeStepRecommendation(step as Record<string, unknown>, true) // lenient mode\n\t\t\t\t: step\n\t\t);\n\t}\n\n\t// Sanitize branch_id to prevent path traversal attacks\n\tif (typeof normalized.branch_id === 'string') {\n\t\tnormalized.branch_id = sanitizeBranchId(normalized.branch_id);\n\t}\n\n\t// Sanitize session_id (same pattern as branch_id but allows 1-100 chars)\n\tif (typeof normalized.session_id === 'string') {\n\t\tconst sanitized = sanitizeSessionId(normalized.session_id);\n\t\tif (sanitized === undefined) {\n\t\t\tdelete normalized.session_id;\n\t\t} else {\n\t\t\tnormalized.session_id = sanitized;\n\t\t}\n\t}\n\n\t// Normalize reasoning fields\n\tnormalizeReasoningFields(normalized);\n\n\n\t// Sanitize all free-text string fields recursively (dangerous HTML tags + null bytes)\n\t// This was moved from schema transforms because v.transform() cannot be converted to JSON Schema\n\tconst sanitized = sanitizeRecursive(normalized);\n\n\treturn sanitized as unknown as ThoughtData;\n}\n"],"names":["DEFAULT_TOOL_CONFIDENCE","DEFAULT_TOOL_PRIORITY","DEFAULT_TOOL_RATIONALE","DEFAULT_STEP_OUTCOME","DEFAULT_SKILL_CONFIDENCE","DEFAULT_SKILL_PRIORITY","DEFAULT_SKILL_RATIONALE","sanitizeRecursive","value","sanitizeString","Array","item","proto","Object","result","key","val","BRANCH_ID_PATTERN","SESSION_ID_PATTERN","sanitizeBranchId","branchId","ValidationError","sanitizeSessionId","sessionId","cleaned","normalizeToolRecommendation","tool","normalized","undefined","normalizeSkillRecommendation","skill","normalizeStepRecommendation","step","lenient","normalizeReasoningFields","input","Math","v","Number","id","normalizeInput","sanitized"],"mappings":"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"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Quality signal computation for sequential thinking.
3
+ *
4
+ * Provides the {@link ThoughtEvaluator} class — a stateless service that computes
5
+ * confidence signals and reasoning analytics from thought history. Follows the
6
+ * ThoughtFormatter pattern as a composed dependency of ThoughtProcessor.
7
+ *
8
+ * @module core/evaluator
9
+ */
10
+ import type { ConfidenceSignals, PatternSignal, ReasoningStats } from './reasoning.js';
11
+ import type { ThoughtData } from './thought.js';
12
+ /**
13
+ * Stateless service that computes quality signals and reasoning analytics
14
+ * from thought history and branch data.
15
+ *
16
+ * @remarks
17
+ * All methods are pure computations — no side effects, no I/O, no internal state.
18
+ * Designed to be registered as transient in the DI container.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const evaluator = new ThoughtEvaluator();
23
+ *
24
+ * const signals = evaluator.computeConfidenceSignals(history, branches);
25
+ * console.log(signals.reasoning_depth); // 5
26
+ *
27
+ * const stats = evaluator.computeReasoningStats(history, branches);
28
+ * console.log(stats.total_thoughts); // 12
29
+ * ```
30
+ */
31
+ export declare class ThoughtEvaluator {
32
+ /**
33
+ * Compute confidence signals from history context.
34
+ * Pure computation — no side effects, no I/O.
35
+ *
36
+ * @param history - All thoughts in the current session
37
+ * @param branches - Map of branch IDs to their thought arrays
38
+ * @returns Computed confidence signals reflecting reasoning quality
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const evaluator = new ThoughtEvaluator();
43
+ * const signals = evaluator.computeConfidenceSignals(
44
+ * [thought1, thought2, thought3],
45
+ * { 'branch-a': [branchThought1] }
46
+ * );
47
+ *
48
+ * console.log(signals.reasoning_depth); // 3
49
+ * console.log(signals.branch_count); // 1
50
+ * console.log(signals.has_hypothesis); // false
51
+ * console.log(signals.average_confidence); // 0.85 or null
52
+ * ```
53
+ */
54
+ computeConfidenceSignals(history: ThoughtData[], branches: Record<string, ThoughtData[]>): ConfidenceSignals;
55
+ /**
56
+ * Compute aggregated reasoning analytics.
57
+ * Pure computation from history + branches.
58
+ *
59
+ * @param history - All thoughts in the current session
60
+ * @param branches - Map of branch IDs to their thought arrays
61
+ * @returns Aggregated reasoning statistics for the session
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const evaluator = new ThoughtEvaluator();
66
+ * const stats = evaluator.computeReasoningStats(
67
+ * [thought1, thought2, hypothesisThought, verificationThought],
68
+ * { 'explore-a': [branchThought] }
69
+ * );
70
+ *
71
+ * console.log(stats.total_thoughts); // 4
72
+ * console.log(stats.total_branches); // 1
73
+ * console.log(stats.hypothesis_count); // 1
74
+ * console.log(stats.verified_hypothesis_count); // 1
75
+ * console.log(stats.average_quality_score); // 0.78 or null
76
+ * ```
77
+ */
78
+ computeReasoningStats(history: ThoughtData[], branches: Record<string, ThoughtData[]>): ReasoningStats;
79
+ /**
80
+ * Detect reasoning patterns (anti-patterns and positive signals) from history.
81
+ * Pure computation — no side effects, no I/O.
82
+ *
83
+ * Detected patterns:
84
+ * - `consecutive_without_verification` (warning) — 3+ consecutive regular thoughts without a verification step
85
+ * - `unverified_hypothesis` (warning) — hypothesis not verified within 3 subsequent thoughts
86
+ * - `no_alternatives_explored` (info) — 5+ thoughts with no critique and no branches
87
+ * - `monotonic_type` (info) — 4+ consecutive thoughts with the same thought_type (requires ≥1 explicit type and ≥5 thoughts)
88
+ * - `confidence_drift` (warning) — 3+ consecutive thoughts with strictly decreasing confidence
89
+ * - `healthy_verification` (info) — hypothesis verified within 3 subsequent thoughts
90
+ *
91
+ * @param history - All thoughts in the current session
92
+ * @param branches - Map of branch IDs to their thought arrays
93
+ * @returns Array of detected pattern signals, possibly empty
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * const evaluator = new ThoughtEvaluator();
98
+ * const patterns = evaluator.computePatternSignals(history, branches);
99
+ * const warnings = patterns.filter(p => p.severity === 'warning');
100
+ * ```
101
+ */
102
+ computePatternSignals(history: ThoughtData[], branches: Record<string, ThoughtData[]>): PatternSignal[];
103
+ /** Detect runs of 3+ consecutive thoughts without verification. */
104
+ private _detectConsecutiveWithoutVerification;
105
+ /** Detect hypothesis thoughts not verified within 3 subsequent thoughts. */
106
+ private _detectUnverifiedHypothesis;
107
+ /** Detect 5+ thoughts with no critique and no branches. */
108
+ private _detectNoAlternativesExplored;
109
+ /**
110
+ * Detect runs of 4+ consecutive thoughts with the same thought_type.
111
+ * Only fires when history has ≥5 thoughts and at least one explicitly set thought_type.
112
+ */
113
+ private _detectMonotonicType;
114
+ /** Detect runs of 3+ consecutive thoughts with strictly decreasing confidence. */
115
+ private _detectConfidenceDrift;
116
+ /** Detect hypothesis verified within 3 subsequent thoughts — positive signal. */
117
+ private _detectHealthyVerification;
118
+ /** Count thoughts by type across history. */
119
+ private _countByType;
120
+ /** Find longest sequential chain depth (contiguous thoughts without branching). */
121
+ private _computeChainDepth;
122
+ /** Compute composite structural quality score from history. */
123
+ private _computeStructuralQuality;
124
+ /** Compute confidence stability: 1 - stddev(confidences). */
125
+ private _computeConfidenceStability;
126
+ }
127
+ //# sourceMappingURL=ThoughtEvaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ThoughtEvaluator.d.ts","sourceRoot":"","sources":["../../src/core/ThoughtEvaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,cAAc,EAAe,MAAM,gBAAgB,CAAC;AACpG,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAYhD;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,gBAAgB;IAC5B;;;;;;;;;;;;;;;;;;;;;OAqBG;IACI,wBAAwB,CAC9B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,iBAAiB;IAgCpB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACI,qBAAqB,CAC3B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,cAAc;IAuCjB;;;;;;;;;;;;;;;;;;;;;;GAsBE;IACK,qBAAqB,CAC3B,OAAO,EAAE,WAAW,EAAE,EACtB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,GACrC,aAAa,EAAE;IAalB,mEAAmE;IACnE,OAAO,CAAC,qCAAqC;IAwB7C,4EAA4E;IAC5E,OAAO,CAAC,2BAA2B;IAqBnC,2DAA2D;IAC3D,OAAO,CAAC,6BAA6B;IAmBrC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IA2C5B,kFAAkF;IAClF,OAAO,CAAC,sBAAsB;IAmE9B,iFAAiF;IACjF,OAAO,CAAC,0BAA0B;IAyBlC,6CAA6C;IAC7C,OAAO,CAAC,YAAY;IAgBpB,mFAAmF;IACnF,OAAO,CAAC,kBAAkB;IAmB1B,+DAA+D;IAC/D,OAAO,CAAC,yBAAyB;IAiEjC,6DAA6D;IAC7D,OAAO,CAAC,2BAA2B;CAUnC"}