@dommaker/harness 0.19.0 → 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 (348) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/bin/harness.js +136 -557
  3. package/dist/cli/commands/check.d.ts.map +1 -1
  4. package/dist/cli/commands/check.js +2 -13
  5. package/dist/cli/commands/check.js.map +1 -1
  6. package/dist/cli/commands/definitions.d.ts +90 -0
  7. package/dist/cli/commands/definitions.d.ts.map +1 -0
  8. package/dist/cli/commands/definitions.js +519 -0
  9. package/dist/cli/commands/definitions.js.map +1 -0
  10. package/dist/cli/commands/knowledge.js.map +1 -1
  11. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +3 -25
  12. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  13. package/dist/cli/commands/sync-docs/capabilities-syncer.js +3 -93
  14. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  15. package/dist/cli/commands/sync-docs/index.d.ts +1 -1
  16. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  17. package/dist/cli/commands/sync-docs/index.js +4 -4
  18. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  19. package/dist/context/index.d.ts +1 -5
  20. package/dist/context/index.d.ts.map +1 -1
  21. package/dist/context/index.js +1 -5
  22. package/dist/context/index.js.map +1 -1
  23. package/dist/context/knowledge-injector.d.ts +3 -3
  24. package/dist/context/knowledge-injector.d.ts.map +1 -1
  25. package/dist/context/knowledge-injector.js +1 -1
  26. package/dist/context/session-manager.d.ts +1 -12
  27. package/dist/context/session-manager.d.ts.map +1 -1
  28. package/dist/context/session-manager.js +0 -63
  29. package/dist/context/session-manager.js.map +1 -1
  30. package/dist/context/types.d.ts +1 -39
  31. package/dist/context/types.d.ts.map +1 -1
  32. package/dist/context/types.js +2 -15
  33. package/dist/context/types.js.map +1 -1
  34. package/dist/core/constraints/agent-prompt-renderer.d.ts +1 -1
  35. package/dist/core/constraints/agent-prompt-renderer.js +1 -1
  36. package/dist/core/constraints/capabilities-parser.d.ts +17 -2
  37. package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
  38. package/dist/core/constraints/capabilities-parser.js +84 -2
  39. package/dist/core/constraints/capabilities-parser.js.map +1 -1
  40. package/dist/core/constraints/checker.d.ts +3 -17
  41. package/dist/core/constraints/checker.d.ts.map +1 -1
  42. package/dist/core/constraints/checker.js +7 -47
  43. package/dist/core/constraints/checker.js.map +1 -1
  44. package/dist/core/constraints/definitions.d.ts +0 -7
  45. package/dist/core/constraints/definitions.d.ts.map +1 -1
  46. package/dist/core/constraints/definitions.js +1 -8
  47. package/dist/core/constraints/definitions.js.map +1 -1
  48. package/dist/core/constraints/doc-freshness/runner.d.ts +0 -2
  49. package/dist/core/constraints/doc-freshness/runner.d.ts.map +1 -1
  50. package/dist/core/constraints/doc-freshness/runner.js.map +1 -1
  51. package/dist/core/constraints/index.d.ts +1 -1
  52. package/dist/core/constraints/index.d.ts.map +1 -1
  53. package/dist/core/constraints/index.js +2 -3
  54. package/dist/core/constraints/index.js.map +1 -1
  55. package/dist/core/constraints/interceptor.d.ts.map +1 -1
  56. package/dist/core/constraints/interceptor.js +1 -5
  57. package/dist/core/constraints/interceptor.js.map +1 -1
  58. package/dist/core/effective-constraints.d.ts.map +1 -1
  59. package/dist/core/effective-constraints.js +0 -1
  60. package/dist/core/effective-constraints.js.map +1 -1
  61. package/dist/core/project-config-loader.d.ts.map +1 -1
  62. package/dist/core/project-config-loader.js +1 -8
  63. package/dist/core/project-config-loader.js.map +1 -1
  64. package/dist/dashboard/stats.d.ts.map +1 -1
  65. package/dist/dashboard/stats.js.map +1 -1
  66. package/dist/dashboard/types.d.ts +2 -2
  67. package/dist/dashboard/types.d.ts.map +1 -1
  68. package/dist/failure/constraint-handler.d.ts.map +1 -1
  69. package/dist/failure/constraint-handler.js +0 -2
  70. package/dist/failure/constraint-handler.js.map +1 -1
  71. package/dist/gates/acceptance.d.ts +9 -1
  72. package/dist/gates/acceptance.d.ts.map +1 -1
  73. package/dist/gates/acceptance.js +21 -0
  74. package/dist/gates/acceptance.js.map +1 -1
  75. package/dist/gates/checker-gate.d.ts +23 -0
  76. package/dist/gates/checker-gate.d.ts.map +1 -0
  77. package/dist/gates/checker-gate.js +53 -0
  78. package/dist/gates/checker-gate.js.map +1 -0
  79. package/dist/gates/command.d.ts +9 -2
  80. package/dist/gates/command.d.ts.map +1 -1
  81. package/dist/gates/command.js +10 -0
  82. package/dist/gates/command.js.map +1 -1
  83. package/dist/gates/contract.d.ts +8 -2
  84. package/dist/gates/contract.d.ts.map +1 -1
  85. package/dist/gates/contract.js +9 -0
  86. package/dist/gates/contract.js.map +1 -1
  87. package/dist/gates/decision.d.ts +17 -0
  88. package/dist/gates/decision.d.ts.map +1 -0
  89. package/dist/gates/decision.js +25 -0
  90. package/dist/gates/decision.js.map +1 -0
  91. package/dist/gates/definitions.d.ts +63 -0
  92. package/dist/gates/definitions.d.ts.map +1 -0
  93. package/dist/gates/definitions.js +160 -0
  94. package/dist/gates/definitions.js.map +1 -0
  95. package/dist/gates/effective-gates.d.ts +30 -0
  96. package/dist/gates/effective-gates.d.ts.map +1 -0
  97. package/dist/gates/effective-gates.js +85 -0
  98. package/dist/gates/effective-gates.js.map +1 -0
  99. package/dist/gates/index.d.ts +25 -1
  100. package/dist/gates/index.d.ts.map +1 -1
  101. package/dist/gates/index.js +37 -1
  102. package/dist/gates/index.js.map +1 -1
  103. package/dist/gates/performance.d.ts +8 -2
  104. package/dist/gates/performance.d.ts.map +1 -1
  105. package/dist/gates/performance.js +9 -0
  106. package/dist/gates/performance.js.map +1 -1
  107. package/dist/gates/registry.d.ts +32 -0
  108. package/dist/gates/registry.d.ts.map +1 -0
  109. package/dist/gates/registry.js +90 -0
  110. package/dist/gates/registry.js.map +1 -0
  111. package/dist/gates/review.d.ts +8 -2
  112. package/dist/gates/review.d.ts.map +1 -1
  113. package/dist/gates/review.js +9 -0
  114. package/dist/gates/review.js.map +1 -1
  115. package/dist/gates/runner.d.ts +30 -0
  116. package/dist/gates/runner.d.ts.map +1 -0
  117. package/dist/gates/runner.js +38 -0
  118. package/dist/gates/runner.js.map +1 -0
  119. package/dist/gates/security.d.ts +8 -2
  120. package/dist/gates/security.d.ts.map +1 -1
  121. package/dist/gates/security.js +9 -0
  122. package/dist/gates/security.js.map +1 -1
  123. package/dist/gates/types.d.ts +41 -1
  124. package/dist/gates/types.d.ts.map +1 -1
  125. package/dist/hooks/bootstrap.d.ts.map +1 -1
  126. package/dist/hooks/bootstrap.js +0 -2
  127. package/dist/hooks/bootstrap.js.map +1 -1
  128. package/dist/hooks/config.d.ts +26 -0
  129. package/dist/hooks/config.d.ts.map +1 -0
  130. package/dist/hooks/config.js +30 -0
  131. package/dist/hooks/config.js.map +1 -0
  132. package/dist/hooks/index.d.ts +2 -1
  133. package/dist/hooks/index.d.ts.map +1 -1
  134. package/dist/hooks/index.js +4 -1
  135. package/dist/hooks/index.js.map +1 -1
  136. package/dist/hooks/registry.d.ts +18 -1
  137. package/dist/hooks/registry.d.ts.map +1 -1
  138. package/dist/hooks/registry.js +45 -0
  139. package/dist/hooks/registry.js.map +1 -1
  140. package/dist/hooks/types.d.ts +20 -0
  141. package/dist/hooks/types.d.ts.map +1 -1
  142. package/dist/index.d.ts +4 -8
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +5 -20
  145. package/dist/index.js.map +1 -1
  146. package/dist/knowledge/import.d.ts +2 -2
  147. package/dist/knowledge/import.d.ts.map +1 -1
  148. package/dist/knowledge/ingest.js.map +1 -1
  149. package/dist/knowledge/lifecycle-hooks.d.ts +2 -2
  150. package/dist/knowledge/lifecycle-hooks.d.ts.map +1 -1
  151. package/dist/knowledge/types.d.ts +5 -7
  152. package/dist/knowledge/types.d.ts.map +1 -1
  153. package/dist/knowledge/types.js +1 -1
  154. package/dist/knowledge/types.js.map +1 -1
  155. package/dist/monitoring/constraint-doctor.d.ts +4 -22
  156. package/dist/monitoring/constraint-doctor.d.ts.map +1 -1
  157. package/dist/monitoring/constraint-doctor.js +7 -94
  158. package/dist/monitoring/constraint-doctor.js.map +1 -1
  159. package/dist/presets/standard.d.ts +1 -1
  160. package/dist/presets/standard.d.ts.map +1 -1
  161. package/dist/presets/standard.js +1 -2
  162. package/dist/presets/standard.js.map +1 -1
  163. package/dist/spec/annotation-checker.d.ts.map +1 -1
  164. package/dist/spec/annotation-checker.js +6 -26
  165. package/dist/spec/annotation-checker.js.map +1 -1
  166. package/dist/tools/index.d.ts +0 -4
  167. package/dist/tools/index.d.ts.map +1 -1
  168. package/dist/tools/index.js +0 -4
  169. package/dist/tools/index.js.map +1 -1
  170. package/dist/types/constraint.d.ts +1 -12
  171. package/dist/types/constraint.d.ts.map +1 -1
  172. package/dist/types/constraint.js +0 -3
  173. package/dist/types/constraint.js.map +1 -1
  174. package/dist/types/project-config.d.ts +0 -2
  175. package/dist/types/project-config.d.ts.map +1 -1
  176. package/dist/utils/file-walk.d.ts +2 -2
  177. package/dist/utils/file-walk.js +2 -2
  178. package/package.json +6 -1
  179. package/src/CONTEXT.md +14 -0
  180. package/src/__tests__/checker-extra.test.ts +2 -59
  181. package/src/__tests__/constraint-doctor.test.ts +1 -140
  182. package/src/__tests__/constraint-handler.test.ts +0 -4
  183. package/src/__tests__/project-config-loader-extra.test.ts +0 -20
  184. package/src/__tests__/project-config-loader.test.ts +0 -1
  185. package/src/cli/commands/CONTEXT.md +15 -7
  186. package/src/cli/commands/__tests__/check-drift.test.ts +0 -3
  187. package/src/cli/commands/__tests__/check-skip-output.test.ts +0 -5
  188. package/src/cli/commands/__tests__/check.test.ts +7 -75
  189. package/src/cli/commands/__tests__/registry.test.ts +201 -0
  190. package/src/cli/commands/check.ts +2 -14
  191. package/src/cli/commands/definitions.ts +593 -0
  192. package/src/cli/commands/knowledge.ts +3 -3
  193. package/src/cli/commands/sync-docs/capabilities-syncer.ts +4 -110
  194. package/src/cli/commands/sync-docs/index.ts +8 -5
  195. package/src/context/CONTEXT.md +2 -7
  196. package/src/context/__tests__/session-manager.test.ts +0 -91
  197. package/src/context/index.ts +1 -5
  198. package/src/context/knowledge-injector.ts +3 -3
  199. package/src/context/session-manager.ts +0 -74
  200. package/src/context/types.ts +2 -63
  201. package/src/core/constraints/__tests__/interceptor.test.ts +0 -2
  202. package/src/core/constraints/agent-prompt-renderer.ts +1 -1
  203. package/src/core/constraints/capabilities-parser.ts +95 -2
  204. package/src/core/constraints/checker.ts +8 -53
  205. package/src/core/constraints/definitions.ts +0 -8
  206. package/src/core/constraints/doc-freshness/runner.ts +1 -2
  207. package/src/core/constraints/index.ts +1 -2
  208. package/src/core/constraints/interceptor.ts +1 -6
  209. package/src/core/effective-constraints.ts +0 -1
  210. package/src/core/project-config-loader.ts +1 -7
  211. package/src/dashboard/stats.ts +2 -2
  212. package/src/dashboard/types.ts +2 -2
  213. package/src/failure/constraint-handler.ts +0 -2
  214. package/src/gates/CONTEXT.md +16 -13
  215. package/src/gates/__tests__/checker-gate.test.ts +45 -0
  216. package/src/gates/__tests__/contract.test.ts +73 -0
  217. package/src/gates/__tests__/effective-gates.test.ts +82 -0
  218. package/src/gates/__tests__/gate-interface.test.ts +202 -0
  219. package/src/gates/__tests__/registry.test.ts +87 -0
  220. package/src/gates/__tests__/runner.test.ts +81 -0
  221. package/src/gates/acceptance.ts +24 -1
  222. package/src/gates/checker-gate.ts +54 -0
  223. package/src/gates/command.ts +13 -2
  224. package/src/gates/contract.ts +12 -2
  225. package/src/gates/decision.ts +27 -0
  226. package/src/gates/definitions.ts +207 -0
  227. package/src/gates/effective-gates.ts +106 -0
  228. package/src/gates/index.ts +38 -0
  229. package/src/gates/performance.ts +12 -2
  230. package/src/gates/registry.ts +107 -0
  231. package/src/gates/review.ts +12 -2
  232. package/src/gates/runner.ts +57 -0
  233. package/src/gates/security.ts +12 -2
  234. package/src/gates/types.ts +48 -1
  235. package/src/hooks/CONTEXT.md +12 -2
  236. package/src/hooks/__tests__/bootstrap.test.ts +0 -1
  237. package/src/hooks/__tests__/config.test.ts +19 -0
  238. package/src/hooks/__tests__/pipeline.test.ts +79 -0
  239. package/src/hooks/__tests__/registry.test.ts +67 -0
  240. package/src/hooks/bootstrap.ts +0 -2
  241. package/src/hooks/config.ts +29 -0
  242. package/src/hooks/index.ts +2 -1
  243. package/src/hooks/registry.ts +60 -1
  244. package/src/hooks/types.ts +21 -0
  245. package/src/index.ts +5 -22
  246. package/src/knowledge/import.ts +3 -3
  247. package/src/knowledge/ingest.ts +4 -4
  248. package/src/knowledge/lifecycle-hooks.ts +3 -3
  249. package/src/knowledge/types.ts +5 -8
  250. package/src/monitoring/constraint-doctor.ts +7 -106
  251. package/src/presets/__tests__/standard.test.ts +0 -2
  252. package/src/presets/standard.ts +1 -2
  253. package/src/spec/__tests__/annotation-checker.test.ts +9 -3
  254. package/src/spec/annotation-checker.ts +7 -29
  255. package/src/tools/CONTEXT.md +7 -14
  256. package/src/tools/index.ts +0 -4
  257. package/src/types/constraint.ts +1 -14
  258. package/src/types/project-config.ts +0 -3
  259. package/src/utils/file-walk.ts +2 -2
  260. package/dist/architecture/constraint-engine.d.ts +0 -67
  261. package/dist/architecture/constraint-engine.d.ts.map +0 -1
  262. package/dist/architecture/constraint-engine.js +0 -295
  263. package/dist/architecture/constraint-engine.js.map +0 -1
  264. package/dist/architecture/cross-project-checker.d.ts +0 -54
  265. package/dist/architecture/cross-project-checker.d.ts.map +0 -1
  266. package/dist/architecture/cross-project-checker.js +0 -467
  267. package/dist/architecture/cross-project-checker.js.map +0 -1
  268. package/dist/cli/commands/index.d.ts +0 -30
  269. package/dist/cli/commands/index.d.ts.map +0 -1
  270. package/dist/cli/commands/index.js +0 -91
  271. package/dist/cli/commands/index.js.map +0 -1
  272. package/dist/context/file-budget.d.ts +0 -40
  273. package/dist/context/file-budget.d.ts.map +0 -1
  274. package/dist/context/file-budget.js +0 -147
  275. package/dist/context/file-budget.js.map +0 -1
  276. package/dist/context/progressive-loader.d.ts +0 -109
  277. package/dist/context/progressive-loader.d.ts.map +0 -1
  278. package/dist/context/progressive-loader.js +0 -166
  279. package/dist/context/progressive-loader.js.map +0 -1
  280. package/dist/context/token-pipeline.d.ts +0 -55
  281. package/dist/context/token-pipeline.d.ts.map +0 -1
  282. package/dist/context/token-pipeline.js +0 -220
  283. package/dist/context/token-pipeline.js.map +0 -1
  284. package/dist/context/tool-output-budget.d.ts +0 -59
  285. package/dist/context/tool-output-budget.d.ts.map +0 -1
  286. package/dist/context/tool-output-budget.js +0 -177
  287. package/dist/context/tool-output-budget.js.map +0 -1
  288. package/dist/core/constraints/default-executors.d.ts +0 -10
  289. package/dist/core/constraints/default-executors.d.ts.map +0 -1
  290. package/dist/core/constraints/default-executors.js +0 -120
  291. package/dist/core/constraints/default-executors.js.map +0 -1
  292. package/dist/llm/adapter.d.ts +0 -36
  293. package/dist/llm/adapter.d.ts.map +0 -1
  294. package/dist/llm/adapter.js +0 -118
  295. package/dist/llm/adapter.js.map +0 -1
  296. package/dist/llm/index.d.ts +0 -3
  297. package/dist/llm/index.d.ts.map +0 -1
  298. package/dist/llm/index.js +0 -19
  299. package/dist/llm/index.js.map +0 -1
  300. package/dist/llm/types.d.ts +0 -62
  301. package/dist/llm/types.d.ts.map +0 -1
  302. package/dist/llm/types.js +0 -8
  303. package/dist/llm/types.js.map +0 -1
  304. package/dist/tools/core/index.d.ts +0 -13
  305. package/dist/tools/core/index.d.ts.map +0 -1
  306. package/dist/tools/core/index.js +0 -198
  307. package/dist/tools/core/index.js.map +0 -1
  308. package/dist/tools/loader.d.ts +0 -21
  309. package/dist/tools/loader.d.ts.map +0 -1
  310. package/dist/tools/loader.js +0 -199
  311. package/dist/tools/loader.js.map +0 -1
  312. package/dist/tools/registry.d.ts +0 -59
  313. package/dist/tools/registry.d.ts.map +0 -1
  314. package/dist/tools/registry.js +0 -115
  315. package/dist/tools/registry.js.map +0 -1
  316. package/dist/tools/types.d.ts +0 -38
  317. package/dist/tools/types.d.ts.map +0 -1
  318. package/dist/tools/types.js +0 -6
  319. package/dist/tools/types.js.map +0 -1
  320. package/src/__tests__/architecture-engine.test.ts +0 -619
  321. package/src/__tests__/context/progressive-loader.test.ts +0 -131
  322. package/src/__tests__/cross-project-checker.test.ts +0 -318
  323. package/src/architecture/CONTEXT.md +0 -22
  324. package/src/architecture/constraint-engine.ts +0 -351
  325. package/src/architecture/cross-project-checker.ts +0 -545
  326. package/src/cli/commands/index.ts +0 -30
  327. package/src/context/__tests__/file-budget.test.ts +0 -142
  328. package/src/context/__tests__/progressive-loader.test.ts +0 -264
  329. package/src/context/__tests__/token-pipeline.test.ts +0 -178
  330. package/src/context/__tests__/tool-output-budget.test.ts +0 -95
  331. package/src/context/file-budget.ts +0 -142
  332. package/src/context/progressive-loader.ts +0 -260
  333. package/src/context/token-pipeline.ts +0 -265
  334. package/src/context/tool-output-budget.ts +0 -178
  335. package/src/core/constraints/__tests__/default-executors.test.ts +0 -57
  336. package/src/core/constraints/default-executors.ts +0 -92
  337. package/src/llm/CONTEXT.md +0 -21
  338. package/src/llm/__tests__/adapter.test.ts +0 -172
  339. package/src/llm/adapter.ts +0 -149
  340. package/src/llm/index.ts +0 -2
  341. package/src/llm/types.ts +0 -81
  342. package/src/tools/__tests__/loader.test.ts +0 -252
  343. package/src/tools/__tests__/registry.test.ts +0 -157
  344. package/src/tools/core/__tests__/core.test.ts +0 -122
  345. package/src/tools/core/index.ts +0 -201
  346. package/src/tools/loader.ts +0 -216
  347. package/src/tools/registry.ts +0 -128
  348. package/src/tools/types.ts +0 -46
@@ -1,178 +0,0 @@
1
- /**
2
- * 工具输出预算
3
- *
4
- * 控制工具输出对上下文的占用
5
- * 策略:full / preview / overflow / dedup
6
- */
7
-
8
- import * as crypto from 'crypto';
9
- import { TokenEstimator } from './token-budget';
10
- import type { ToolOutputBudgetConfig } from './types';
11
- import { DEFAULT_TOOL_OUTPUT_BUDGET } from './types';
12
-
13
- export type OutputStrategy = 'full' | 'preview' | 'overflow' | 'dedup';
14
-
15
- export interface OutputCheckResult {
16
- allowed: boolean;
17
- strategy: OutputStrategy;
18
- reason?: string;
19
- }
20
-
21
- export interface OutputApplyResult {
22
- content: string;
23
- offloaded?: { path: string };
24
- originalTokens: number;
25
- keptTokens: number;
26
- }
27
-
28
- export class ToolOutputBudget {
29
- private config: ToolOutputBudgetConfig;
30
- private seenHashes: Map<string, boolean> = new Map();
31
-
32
- constructor(config?: Partial<ToolOutputBudgetConfig>) {
33
- this.config = { ...DEFAULT_TOOL_OUTPUT_BUDGET, ...config };
34
- }
35
-
36
- /**
37
- * 检查工具输出应该使用什么策略
38
- */
39
- checkOutput(output: string, totalBudget?: number): OutputCheckResult {
40
- const charCount = output.length;
41
- const tokens = TokenEstimator.estimateText(output);
42
-
43
- // 去重检查
44
- if (this.config.dedup) {
45
- const hash = this.computeHash(output);
46
- if (this.seenHashes.has(hash)) {
47
- return { allowed: true, strategy: 'dedup', reason: '重复内容,将被去重' };
48
- }
49
- // 记录 hash(无论最终策略是什么)
50
- this.seenHashes.set(hash, true);
51
- }
52
-
53
- // 硬上限检查
54
- if (charCount > this.config.maxChars) {
55
- return {
56
- allowed: true,
57
- strategy: this.config.overflowToDisk ? 'overflow' : 'preview',
58
- reason: `输出 ${charCount} 字符超过限制 ${this.config.maxChars}`,
59
- };
60
- }
61
-
62
- // Token 比例检查
63
- if (totalBudget) {
64
- const maxTokens = totalBudget * this.config.maxTokenRatio;
65
- if (tokens > maxTokens) {
66
- return {
67
- allowed: true,
68
- strategy: 'preview',
69
- reason: `输出 ${tokens} tokens 超过预算比例 ${(this.config.maxTokenRatio * 100).toFixed(0)}% (${Math.floor(maxTokens)} tokens)`,
70
- };
71
- }
72
- }
73
-
74
- return { allowed: true, strategy: 'full' };
75
- }
76
-
77
- /**
78
- * 应用预算策略
79
- */
80
- applyBudget(output: string, strategy: OutputStrategy, _totalBudget?: number): OutputApplyResult {
81
- const originalTokens = TokenEstimator.estimateText(output);
82
-
83
- switch (strategy) {
84
- case 'full': {
85
- if (this.config.dedup) {
86
- this.recordHash(output);
87
- }
88
- return { content: output, originalTokens, keptTokens: originalTokens };
89
- }
90
-
91
- case 'preview': {
92
- const preview = this.generatePreview(output);
93
- const keptTokens = TokenEstimator.estimateText(preview);
94
- return { content: preview, originalTokens, keptTokens };
95
- }
96
-
97
- case 'overflow': {
98
- const path = this.writeToOverflow(output);
99
- const summary = `[输出已写入磁盘: ${path}]\n前 ${this.config.previewLines} 行预览:\n${output.split('\n').slice(0, this.config.previewLines).join('\n')}`;
100
- const keptTokens = TokenEstimator.estimateText(summary);
101
- return { content: summary, offloaded: { path }, originalTokens, keptTokens };
102
- }
103
-
104
- case 'dedup': {
105
- const dedupContent = `[重复内容,已省略。原始大小: ${output.length} 字符]`;
106
- return { content: dedupContent, originalTokens, keptTokens: TokenEstimator.estimateText(dedupContent) };
107
- }
108
-
109
- default:
110
- return { content: output, originalTokens, keptTokens: originalTokens };
111
- }
112
- }
113
-
114
- /**
115
- * 生成预览内容
116
- */
117
- private generatePreview(output: string): string {
118
- const lines = output.split('\n');
119
- const previewLines = this.config.previewLines;
120
-
121
- if (lines.length <= previewLines) {
122
- return output;
123
- }
124
-
125
- const head = lines.slice(0, previewLines).join('\n');
126
- const omitted = lines.length - previewLines;
127
- return `${head}\n\n... [省略 ${omitted} 行] ...\n\n${lines.slice(-3).join('\n')}`;
128
- }
129
-
130
- /**
131
- * 写入溢出文件
132
- */
133
- private writeToOverflow(output: string): string {
134
- const hash = this.computeHash(output).slice(0, 8);
135
- const path = `.harness/overflow/tool-output-${hash}.txt`;
136
-
137
- try {
138
- const fs = require('fs');
139
- const dir = require('path').dirname(path);
140
- fs.mkdirSync(dir, { recursive: true });
141
- fs.writeFileSync(path, output, 'utf-8');
142
- } catch {
143
- // 写入失败时回退到 preview
144
- return '';
145
- }
146
-
147
- return path;
148
- }
149
-
150
- /**
151
- * 计算内容 hash
152
- */
153
- private computeHash(content: string): string {
154
- return crypto.createHash('md5').update(content).digest('hex');
155
- }
156
-
157
- /**
158
- * 记录已见 hash
159
- */
160
- private recordHash(content: string): void {
161
- const hash = this.computeHash(content);
162
- this.seenHashes.set(hash, true);
163
- }
164
-
165
- /**
166
- * 清除去重缓存
167
- */
168
- clearDedupCache(): void {
169
- this.seenHashes.clear();
170
- }
171
-
172
- /**
173
- * 获取配置
174
- */
175
- getConfig(): ToolOutputBudgetConfig {
176
- return { ...this.config };
177
- }
178
- }
@@ -1,57 +0,0 @@
1
- /**
2
- * default-executors 注册测试
3
- */
4
-
5
- import { constraintInterceptor } from '../interceptor';
6
-
7
- describe('registerDefaultExecutors', () => {
8
- beforeEach(() => {
9
- jest.clearAllMocks();
10
- // Reset the singleton state for clean test
11
- (constraintInterceptor as any).executors.clear();
12
- (constraintInterceptor as any).executorSources.clear();
13
- });
14
-
15
- it('应该注册 architecture-check executor', async () => {
16
- const { registerDefaultExecutors } = await import('../default-executors');
17
- registerDefaultExecutors();
18
-
19
- expect(constraintInterceptor.hasExecutor('architecture-check')).toBe(true);
20
- });
21
-
22
- it('应该注册 cross-project-check executor', async () => {
23
- const { registerDefaultExecutors } = await import('../default-executors');
24
- registerDefaultExecutors();
25
-
26
- expect(constraintInterceptor.hasExecutor('cross-project-check')).toBe(true);
27
- });
28
-
29
- it('架构检查 executor 应有描述', async () => {
30
- const { registerDefaultExecutors } = await import('../default-executors');
31
- registerDefaultExecutors();
32
-
33
- const executor = constraintInterceptor.getExecutor('architecture-check');
34
- expect(executor?.description).toBeDefined();
35
- expect(executor?.description).toContain('架构');
36
- });
37
-
38
- it('跨工程检查 executor 应有描述', async () => {
39
- const { registerDefaultExecutors } = await import('../default-executors');
40
- registerDefaultExecutors();
41
-
42
- const executor = constraintInterceptor.getExecutor('cross-project-check');
43
- expect(executor?.description).toBeDefined();
44
- expect(executor?.description).toContain('跨工程');
45
- });
46
-
47
- it('重复注册应该覆盖', async () => {
48
- const { registerDefaultExecutors } = await import('../default-executors');
49
- registerDefaultExecutors();
50
- registerDefaultExecutors(); // Second call
51
-
52
- const registrations = constraintInterceptor.getRegistrations();
53
- const archRegs = registrations.filter(r => r.id === 'architecture-check');
54
- // Should only have one registration (last one wins)
55
- expect(archRegs.length).toBe(1);
56
- });
57
- });
@@ -1,92 +0,0 @@
1
- /**
2
- * 内置 enforcement executors
3
- *
4
- * 将约束定义中的 enforcement ID 连接到实际的检查逻辑。
5
- */
6
-
7
- import type { EnforcementExecutor, EnforcementResult, EnforcementContext } from '../../types/enforcement';
8
- import { constraintInterceptor } from './interceptor';
9
-
10
- /**
11
- * architecture-check: 调用 ArchitectureConstraintEngine
12
- */
13
- const architectureCheckExecutor: EnforcementExecutor = {
14
- description: '架构规则引擎检查',
15
- async execute(context: EnforcementContext): Promise<EnforcementResult> {
16
- const { ArchitectureConstraintEngine } = await import('../../architecture/constraint-engine');
17
- const { loadArchitectureRules } = await import('../../architecture/constraint-engine');
18
-
19
- const configPath = context.projectPath
20
- ? `${context.projectPath}/.architect/rules.yml`
21
- : '.architect/rules.yml';
22
-
23
- try {
24
- const rules = await loadArchitectureRules(configPath);
25
- if (rules.length === 0) {
26
- return { passed: true, message: '无架构规则配置,跳过检查' };
27
- }
28
-
29
- const engine = new ArchitectureConstraintEngine(rules);
30
- const result = await engine.check({
31
- files: context.params?.changedFiles || [],
32
- diff: '',
33
- });
34
-
35
- return {
36
- passed: result.passed,
37
- message: result.passed
38
- ? '架构检查通过'
39
- : `${result.violations.length} 个架构违规`,
40
- evidence: result.violations.map(v => `${v.ruleId}: ${v.message}`).join('\n'),
41
- };
42
- } catch (error) {
43
- return {
44
- passed: false,
45
- message: `架构规则加载失败: ${error instanceof Error ? error.message : String(error)}`,
46
- };
47
- }
48
- },
49
- };
50
-
51
- /**
52
- * cross-project-check: 调用 checkCrossProjectContracts
53
- */
54
- const crossProjectCheckExecutor: EnforcementExecutor = {
55
- description: '跨工程接口契约检查',
56
- async execute(context: EnforcementContext): Promise<EnforcementResult> {
57
- const { checkCrossProjectContracts } = await import('../../architecture/cross-project-checker');
58
- const params = context.params || {};
59
-
60
- try {
61
- const violations = await checkCrossProjectContracts({
62
- baseBranch: params.baseBranch || 'main',
63
- changedProjects: params.changedProjects || [],
64
- changedFiles: params.changedFiles || [],
65
- }, {
66
- dependencies: params.dependencies || {},
67
- contractLocations: params.contractLocations || {},
68
- });
69
-
70
- return {
71
- passed: violations.length === 0,
72
- message: violations.length === 0
73
- ? '跨工程检查通过'
74
- : `${violations.length} 个跨工程违规`,
75
- evidence: violations.map(v => `${v.type}: ${v.message}`).join('\n'),
76
- };
77
- } catch (error) {
78
- return {
79
- passed: false,
80
- message: `跨工程检查失败: ${error instanceof Error ? error.message : String(error)}`,
81
- };
82
- }
83
- },
84
- };
85
-
86
- /**
87
- * 注册所有内置 executors
88
- */
89
- export function registerDefaultExecutors(): void {
90
- constraintInterceptor.register('architecture-check', architectureCheckExecutor, 'builtin');
91
- constraintInterceptor.register('cross-project-check', crossProjectCheckExecutor, 'builtin');
92
- }
@@ -1,21 +0,0 @@
1
- # llm/
2
-
3
- ## 职责
4
- LLM 适配层:统一的 LLM 调用接口(Adapter 模式),支持多模型切换。
5
-
6
- ## 核心导出
7
- - `types.ts` — LLM 相关类型定义
8
- - `adapter.ts` — LLM Adapter: 统一调用接口
9
-
10
- ## 依赖关系
11
- - 依赖 `src/types/` 公共类型
12
- - 被上层需要 LLM 调用的模块消费
13
-
14
- ## 约定
15
- - Adapter 模式:统一接口屏蔽底层模型差异
16
- - 不直接依赖特定 LLM SDK
17
- - 类型定义与实现分离
18
-
19
- ## 注意事项
20
- - 轻量模块,提供 LLM 调用抽象
21
- - 具体模型配置由环境变量/配置文件提供
@@ -1,172 +0,0 @@
1
- /**
2
- * LLM Adapter 测试
3
- */
4
-
5
- import { createLLMAdapter, DefaultLLMAdapter } from '../adapter';
6
- import type { LLMAdapter, Message } from '../types';
7
-
8
- describe('LLM Adapter', () => {
9
- describe('createLLMAdapter', () => {
10
- it('应该创建默认适配器', () => {
11
- const adapter = createLLMAdapter({ provider: 'openai' });
12
- expect(adapter).toBeInstanceOf(DefaultLLMAdapter);
13
- });
14
-
15
- it('应该使用自定义适配器', () => {
16
- const custom: LLMAdapter = {
17
- complete: async () => 'custom',
18
- chat: async () => 'custom',
19
- streamChat: async function* () { yield 'custom'; },
20
- summarize: async () => 'custom summary',
21
- extract: async () => ({}),
22
- };
23
-
24
- const adapter = createLLMAdapter({
25
- provider: 'custom',
26
- customAdapter: custom,
27
- });
28
-
29
- expect(adapter).toBe(custom);
30
- });
31
- });
32
-
33
- describe('DefaultLLMAdapter', () => {
34
- let adapter: DefaultLLMAdapter;
35
-
36
- beforeEach(() => {
37
- adapter = new DefaultLLMAdapter({ provider: 'openai', model: 'gpt-4' });
38
- });
39
-
40
- describe('complete', () => {
41
- it('应该抛出未实现错误', async () => {
42
- await expect(adapter.complete('test')).rejects.toThrow('LLM complete 未实现');
43
- });
44
- });
45
-
46
- describe('chat', () => {
47
- it('应该抛出未实现错误', async () => {
48
- await expect(adapter.chat([{ role: 'user', content: 'test' }])).rejects.toThrow('LLM chat 未实现');
49
- });
50
- });
51
-
52
- describe('streamChat', () => {
53
- it('应该抛出未实现错误', async () => {
54
- const gen = adapter.streamChat([{ role: 'user', content: 'test' }]);
55
- await expect(async () => {
56
- for await (const _ of gen) { void _; }
57
- }).rejects.toThrow('LLM streamChat 未实现');
58
- });
59
- });
60
-
61
- describe('summarize', () => {
62
- it('应该提取用户目标', async () => {
63
- const messages: Message[] = [
64
- { role: 'user', content: '帮我修复登录 bug' },
65
- { role: 'assistant', content: '好的,我来查看代码' },
66
- { role: 'user', content: '问题出在 token 验证' },
67
- ];
68
-
69
- const summary = await adapter.summarize(messages, {
70
- maxTokens: 200,
71
- preserve: ['user_goals'],
72
- });
73
-
74
- expect(summary).toContain('用户目标');
75
- expect(summary).toContain('登录 bug');
76
- });
77
-
78
- it('应该提取工具调用', async () => {
79
- const messages: Message[] = [
80
- { role: 'user', content: 'test' },
81
- { role: 'tool', content: 'result 1' },
82
- { role: 'tool', content: 'result 2' },
83
- ];
84
-
85
- const summary = await adapter.summarize(messages, {
86
- maxTokens: 200,
87
- preserve: ['tool_calls'],
88
- });
89
-
90
- expect(summary).toContain('工具调用');
91
- expect(summary).toContain('2');
92
- });
93
-
94
- it('应该提取错误信息', async () => {
95
- const messages: Message[] = [
96
- { role: 'user', content: 'test' },
97
- { role: 'assistant', content: 'Error: connection failed' },
98
- ];
99
-
100
- const summary = await adapter.summarize(messages, {
101
- maxTokens: 200,
102
- preserve: ['errors'],
103
- });
104
-
105
- expect(summary).toContain('错误');
106
- });
107
-
108
- it('应该提取决策', async () => {
109
- const messages: Message[] = [
110
- { role: 'user', content: '选择方案 A 还是方案 B?' },
111
- { role: 'assistant', content: '决策:使用方案 A,因为性能更好' },
112
- ];
113
-
114
- const summary = await adapter.summarize(messages, {
115
- maxTokens: 200,
116
- preserve: ['decisions'],
117
- });
118
-
119
- expect(summary).toContain('决策');
120
- });
121
-
122
- it('应该限制长度', async () => {
123
- const messages: Message[] = [
124
- { role: 'user', content: 'x'.repeat(10000) },
125
- ];
126
-
127
- const summary = await adapter.summarize(messages, {
128
- maxTokens: 100,
129
- preserve: ['user_goals'],
130
- });
131
-
132
- expect(summary.length).toBeLessThanOrEqual(400); // 100 tokens * 4 chars
133
- });
134
- });
135
-
136
- describe('extract', () => {
137
- it('应该提取结构化数据', async () => {
138
- const content = 'name: John\nage: 30\ncity: Beijing';
139
- const result = await adapter.extract(content, {
140
- name: 'string',
141
- age: 'string',
142
- city: 'string',
143
- }) as Record<string, string>;
144
-
145
- expect(result.name).toBe('John');
146
- expect(result.age).toBe('30');
147
- expect(result.city).toBe('Beijing');
148
- });
149
-
150
- it('应该处理缺失字段', async () => {
151
- const content = 'name: John';
152
- const result = await adapter.extract(content, {
153
- name: 'string',
154
- missing: 'string',
155
- }) as Record<string, string>;
156
-
157
- expect(result.name).toBe('John');
158
- expect(result.missing).toBeUndefined();
159
- });
160
- });
161
-
162
- describe('getters', () => {
163
- it('应该返回 provider', () => {
164
- expect(adapter.getProvider()).toBe('openai');
165
- });
166
-
167
- it('应该返回 model', () => {
168
- expect(adapter.getModel()).toBe('gpt-4');
169
- });
170
- });
171
- });
172
- });
@@ -1,149 +0,0 @@
1
- /**
2
- * 默认 LLM 适配器
3
- *
4
- * 提供基础实现,使用者可以替换为自己的实现
5
- */
6
-
7
- import type {
8
- LLMAdapter,
9
- LLMAdapterConfig,
10
- LLMOptions,
11
- Message,
12
- SummarizeConfig,
13
- LLMProvider,
14
- } from './types';
15
-
16
- /**
17
- * 创建 LLM 适配器
18
- */
19
- export function createLLMAdapter(config: LLMAdapterConfig): LLMAdapter {
20
- if (config.customAdapter) {
21
- return config.customAdapter;
22
- }
23
-
24
- return new DefaultLLMAdapter(config);
25
- }
26
-
27
- /**
28
- * 默认 LLM 适配器
29
- *
30
- * 使用零 token 成本的结构化方法:
31
- * - summarize: 基于模板提取关键信息
32
- * - extract: 基于正则提取结构化数据
33
- * - complete/chat: 需要注入具体实现
34
- */
35
- export class DefaultLLMAdapter implements LLMAdapter {
36
- private config: LLMAdapterConfig;
37
-
38
- constructor(config: LLMAdapterConfig) {
39
- this.config = config;
40
- }
41
-
42
- async complete(_prompt: string, options?: LLMOptions): Promise<string> {
43
- throw new Error(
44
- `LLM complete 未实现。请通过 createLLMAdapter({ customAdapter }) 注入具体实现。` +
45
- `\n提供商: ${this.config.provider}, 模型: ${options?.model ?? this.config.model ?? '未指定'}`,
46
- );
47
- }
48
-
49
- async chat(messages: Message[], _options?: LLMOptions): Promise<string> {
50
- throw new Error(
51
- `LLM chat 未实现。请通过 createLLMAdapter({ customAdapter }) 注入具体实现。` +
52
- `\n提供商: ${this.config.provider}, 消息数: ${messages.length}`,
53
- );
54
- }
55
-
56
- async *streamChat(_messages: Message[], _options?: LLMOptions): AsyncIterable<string> {
57
- throw new Error(
58
- `LLM streamChat 未实现。请通过 createLLMAdapter({ customAdapter }) 注入具体实现。`,
59
- );
60
- }
61
-
62
- /**
63
- * 结构化总结(零 token 成本,基于模板)
64
- */
65
- async summarize(messages: Message[], config: SummarizeConfig): Promise<string> {
66
- const parts: string[] = [];
67
-
68
- // 提取用户目标
69
- if (config.preserve.includes('user_goals')) {
70
- const userMessages = messages.filter(m => m.role === 'user');
71
- if (userMessages.length > 0) {
72
- parts.push('## 用户目标');
73
- for (const msg of userMessages.slice(-3)) {
74
- parts.push(`- ${msg.content.slice(0, 200)}`);
75
- }
76
- }
77
- }
78
-
79
- // 提取工具调用
80
- if (config.preserve.includes('tool_calls')) {
81
- const toolMessages = messages.filter(m => m.role === 'tool');
82
- if (toolMessages.length > 0) {
83
- parts.push('\n## 工具调用');
84
- parts.push(`共 ${toolMessages.length} 次工具调用`);
85
- }
86
- }
87
-
88
- // 提取错误
89
- if (config.preserve.includes('errors')) {
90
- const errorMessages = messages.filter(m =>
91
- m.content.toLowerCase().includes('error') ||
92
- m.content.toLowerCase().includes('failed'),
93
- );
94
- if (errorMessages.length > 0) {
95
- parts.push('\n## 错误');
96
- for (const msg of errorMessages.slice(-3)) {
97
- parts.push(`- ${msg.content.slice(0, 200)}`);
98
- }
99
- }
100
- }
101
-
102
- // 提取决策
103
- if (config.preserve.includes('decisions')) {
104
- const decisionMessages = messages.filter(m =>
105
- m.content.includes('决策') ||
106
- m.content.includes('decision') ||
107
- m.content.includes('选择') ||
108
- m.content.includes('方案'),
109
- );
110
- if (decisionMessages.length > 0) {
111
- parts.push('\n## 决策');
112
- for (const msg of decisionMessages.slice(-3)) {
113
- parts.push(`- ${msg.content.slice(0, 200)}`);
114
- }
115
- }
116
- }
117
-
118
- const summary = parts.join('\n');
119
- return summary.slice(0, config.maxTokens * 4); // 粗略字符/token 比
120
- }
121
-
122
- /**
123
- * 结构化提取(基于正则,零 token 成本)
124
- */
125
- async extract(content: string, schema: Record<string, unknown>): Promise<unknown> {
126
- const result: Record<string, unknown> = {};
127
-
128
- for (const [key, type] of Object.entries(schema)) {
129
- if (typeof type === 'string') {
130
- // 简单类型提取
131
- const pattern = new RegExp(`${key}[:\\s]+(.+)`, 'i');
132
- const match = content.match(pattern);
133
- if (match) {
134
- result[key] = match[1].trim();
135
- }
136
- }
137
- }
138
-
139
- return result;
140
- }
141
-
142
- getProvider(): LLMProvider {
143
- return this.config.provider;
144
- }
145
-
146
- getModel(): string | undefined {
147
- return this.config.model;
148
- }
149
- }
package/src/llm/index.ts DELETED
@@ -1,2 +0,0 @@
1
- export * from './types';
2
- export * from './adapter';