@dommaker/harness 1.4.0 → 1.6.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.
- package/CHANGELOG.md +23 -0
- package/README.md +0 -2
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +28 -0
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/definitions.d.ts.map +1 -1
- package/dist/cli/commands/definitions.js +1 -39
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts +0 -19
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +0 -105
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/passes-gate.d.ts +0 -2
- package/dist/cli/commands/passes-gate.d.ts.map +1 -1
- package/dist/cli/commands/passes-gate.js +0 -1
- package/dist/cli/commands/passes-gate.js.map +1 -1
- package/dist/cli/commands/performance.d.ts +0 -4
- package/dist/cli/commands/performance.d.ts.map +1 -1
- package/dist/cli/commands/performance.js +10 -20
- package/dist/cli/commands/performance.js.map +1 -1
- package/dist/cli/commands/report.d.ts +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +2 -55
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +4 -8
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/context/index.d.ts +2 -5
- package/dist/context/index.d.ts.map +1 -1
- package/dist/context/index.js +2 -8
- package/dist/context/index.js.map +1 -1
- package/dist/context/token-budget.d.ts +0 -29
- package/dist/context/token-budget.d.ts.map +1 -1
- package/dist/context/token-budget.js +1 -65
- package/dist/context/token-budget.js.map +1 -1
- package/dist/context/types.d.ts +0 -1
- package/dist/context/types.d.ts.map +1 -1
- package/dist/context/types.js +0 -9
- package/dist/context/types.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +14 -0
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +27 -5
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.d.ts +17 -6
- package/dist/core/constraints/checkers/capability-sync.d.ts.map +1 -1
- package/dist/core/constraints/checkers/capability-sync.js +48 -19
- package/dist/core/constraints/checkers/capability-sync.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.d.ts.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +15 -6
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +2 -2
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +2 -1
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +41 -2
- package/dist/core/constraints/checkers/types.d.ts.map +1 -1
- package/dist/core/constraints/checkers/types.js +33 -0
- package/dist/core/constraints/checkers/types.js.map +1 -1
- package/dist/core/constraints/git-evidence.d.ts +6 -3
- package/dist/core/constraints/git-evidence.d.ts.map +1 -1
- package/dist/core/constraints/git-evidence.js +6 -3
- package/dist/core/constraints/git-evidence.js.map +1 -1
- package/dist/core/index.d.ts +1 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +1 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/spec/validator.d.ts +2 -0
- package/dist/core/spec/validator.d.ts.map +1 -1
- package/dist/core/spec/validator.js +9 -11
- package/dist/core/spec/validator.js.map +1 -1
- package/dist/core/validators/index.d.ts +1 -1
- package/dist/core/validators/index.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +1 -15
- package/dist/core/validators/passes-gate.d.ts.map +1 -1
- package/dist/core/validators/passes-gate.js +0 -125
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/failure/index.d.ts +0 -1
- package/dist/failure/index.d.ts.map +1 -1
- package/dist/failure/index.js +1 -7
- package/dist/failure/index.js.map +1 -1
- package/dist/gates/checker-gate.d.ts +2 -1
- package/dist/gates/checker-gate.d.ts.map +1 -1
- package/dist/gates/checker-gate.js +10 -7
- package/dist/gates/checker-gate.js.map +1 -1
- package/dist/gates/definitions.d.ts.map +1 -1
- package/dist/gates/definitions.js +0 -4
- package/dist/gates/definitions.js.map +1 -1
- package/dist/gates/performance.d.ts +7 -22
- package/dist/gates/performance.d.ts.map +1 -1
- package/dist/gates/performance.js +7 -89
- package/dist/gates/performance.js.map +1 -1
- package/dist/gates/types.d.ts +2 -8
- package/dist/gates/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -22
- package/dist/index.js.map +1 -1
- package/dist/knowledge/index.d.ts +0 -1
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/index.js +1 -3
- package/dist/knowledge/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts +25 -10
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +147 -116
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/types/constraint.d.ts +8 -0
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +6 -1
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +0 -19
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/trace.d.ts +9 -0
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/CONTEXT.md +1 -0
- package/src/__tests__/passes-gate-check.test.ts +20 -4
- package/src/__tests__/passes-gate.test.ts +18 -119
- package/src/__tests__/performance-gate.test.ts +3 -11
- package/src/__tests__/public-exports.test.ts +46 -16
- package/src/__tests__/trace-analyzer.test.ts +52 -2
- package/src/cli/commands/CONTEXT.md +5 -4
- package/src/cli/commands/__tests__/bin-exit-mapping.test.ts +6 -0
- package/src/cli/commands/__tests__/check.test.ts +74 -0
- package/src/cli/commands/__tests__/passes-gate-dead-options.test.ts +70 -0
- package/src/cli/commands/__tests__/passes-gate.test.ts +7 -3
- package/src/cli/commands/__tests__/performance.test.ts +5 -16
- package/src/cli/commands/__tests__/project-path-convention.test.ts +85 -23
- package/src/cli/commands/__tests__/registry.test.ts +52 -3
- package/src/cli/commands/__tests__/report-extra.test.ts +0 -19
- package/src/cli/commands/__tests__/report-false-green.test.ts +46 -0
- package/src/cli/commands/__tests__/report.test.ts +0 -20
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +68 -0
- package/src/cli/commands/__tests__/status-extra.test.ts +63 -116
- package/src/cli/commands/__tests__/status.test.ts +66 -148
- package/src/cli/commands/check.ts +39 -1
- package/src/cli/commands/definitions.ts +1 -39
- package/src/cli/commands/knowledge.ts +0 -119
- package/src/cli/commands/passes-gate.ts +0 -3
- package/src/cli/commands/performance.ts +13 -27
- package/src/cli/commands/report.ts +3 -61
- package/src/cli/commands/status.ts +5 -10
- package/src/context/CONTEXT.md +2 -3
- package/src/context/__tests__/token-budget.test.ts +1 -81
- package/src/context/index.ts +2 -7
- package/src/context/token-budget.ts +0 -73
- package/src/context/types.ts +0 -9
- package/src/core/CONTEXT.md +6 -6
- package/src/core/constraints/__tests__/collect-constraints.test.ts +66 -0
- package/src/core/constraints/checker.ts +41 -6
- package/src/core/constraints/checkers/__tests__/capability-sync.test.ts +90 -31
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +176 -0
- package/src/core/constraints/checkers/__tests__/docs-freshness.test.ts +18 -9
- package/src/core/constraints/checkers/__tests__/three-state-consistency.test.ts +12 -4
- package/src/core/constraints/checkers/capability-sync.ts +56 -18
- package/src/core/constraints/checkers/docs-freshness.ts +18 -5
- package/src/core/constraints/checkers/index.ts +9 -2
- package/src/core/constraints/checkers/types.ts +58 -2
- package/src/core/constraints/git-evidence.ts +6 -3
- package/src/core/index.ts +0 -11
- package/src/core/spec/__tests__/validator.test.ts +24 -0
- package/src/core/spec/validator.ts +9 -10
- package/src/core/validators/__tests__/passes-gate.test.ts +2 -9
- package/src/core/validators/index.ts +0 -1
- package/src/core/validators/passes-gate.ts +1 -149
- package/src/failure/CONTEXT.md +8 -6
- package/src/failure/index.ts +0 -10
- package/src/gates/CONTEXT.md +4 -3
- package/src/gates/__tests__/checker-gate.test.ts +24 -0
- package/src/gates/__tests__/performance.test.ts +6 -74
- package/src/gates/checker-gate.ts +11 -15
- package/src/gates/definitions.ts +0 -4
- package/src/gates/performance.ts +9 -111
- package/src/gates/types.ts +2 -8
- package/src/index.ts +1 -36
- package/src/knowledge/CONTEXT.md +1 -2
- package/src/knowledge/index.ts +0 -1
- package/src/monitoring/CONTEXT.md +2 -2
- package/src/monitoring/trace-analyzer.ts +160 -126
- package/src/types/constraint.ts +16 -2
- package/src/types/passes-gate.ts +0 -26
- package/src/types/trace.ts +10 -0
- package/src/utils/__tests__/jsonl-skip-disposition.test.ts +0 -1
- package/dist/cli/commands/analyze-sessions.d.ts +0 -20
- package/dist/cli/commands/analyze-sessions.d.ts.map +0 -1
- package/dist/cli/commands/analyze-sessions.js +0 -290
- package/dist/cli/commands/analyze-sessions.js.map +0 -1
- package/dist/cli/commands/update-user-model.d.ts +0 -25
- package/dist/cli/commands/update-user-model.d.ts.map +0 -1
- package/dist/cli/commands/update-user-model.js +0 -397
- package/dist/cli/commands/update-user-model.js.map +0 -1
- package/dist/cli/session-mining/corrections.d.ts +0 -17
- package/dist/cli/session-mining/corrections.d.ts.map +0 -1
- package/dist/cli/session-mining/corrections.js +0 -58
- package/dist/cli/session-mining/corrections.js.map +0 -1
- package/dist/cli/session-mining/index.d.ts +0 -13
- package/dist/cli/session-mining/index.d.ts.map +0 -1
- package/dist/cli/session-mining/index.js +0 -29
- package/dist/cli/session-mining/index.js.map +0 -1
- package/dist/cli/session-mining/text.d.ts +0 -31
- package/dist/cli/session-mining/text.d.ts.map +0 -1
- package/dist/cli/session-mining/text.js +0 -126
- package/dist/cli/session-mining/text.js.map +0 -1
- package/dist/cli/session-mining/transcript.d.ts +0 -34
- package/dist/cli/session-mining/transcript.d.ts.map +0 -1
- package/dist/cli/session-mining/transcript.js +0 -130
- package/dist/cli/session-mining/transcript.js.map +0 -1
- package/dist/context/compaction.d.ts +0 -57
- package/dist/context/compaction.d.ts.map +0 -1
- package/dist/context/compaction.js +0 -165
- package/dist/context/compaction.js.map +0 -1
- package/dist/core/session/clean-state.d.ts +0 -17
- package/dist/core/session/clean-state.d.ts.map +0 -1
- package/dist/core/session/clean-state.js +0 -192
- package/dist/core/session/clean-state.js.map +0 -1
- package/dist/core/session/index.d.ts +0 -6
- package/dist/core/session/index.d.ts.map +0 -1
- package/dist/core/session/index.js +0 -15
- package/dist/core/session/index.js.map +0 -1
- package/dist/core/session/startup.d.ts +0 -47
- package/dist/core/session/startup.d.ts.map +0 -1
- package/dist/core/session/startup.js +0 -263
- package/dist/core/session/startup.js.map +0 -1
- package/dist/failure/constraint-handler.d.ts +0 -83
- package/dist/failure/constraint-handler.d.ts.map +0 -1
- package/dist/failure/constraint-handler.js +0 -138
- package/dist/failure/constraint-handler.js.map +0 -1
- package/dist/knowledge/lifecycle-hooks.d.ts +0 -62
- package/dist/knowledge/lifecycle-hooks.d.ts.map +0 -1
- package/dist/knowledge/lifecycle-hooks.js +0 -143
- package/dist/knowledge/lifecycle-hooks.js.map +0 -1
- package/dist/types/session.d.ts +0 -113
- package/dist/types/session.d.ts.map +0 -1
- package/dist/types/session.js +0 -6
- package/dist/types/session.js.map +0 -1
- package/src/__tests__/clean-state-extra.test.ts +0 -301
- package/src/__tests__/clean-state.test.ts +0 -169
- package/src/__tests__/constraint-handler.test.ts +0 -132
- package/src/__tests__/passes-gate-extra.test.ts +0 -93
- package/src/__tests__/session-startup.test.ts +0 -334
- package/src/cli/commands/__tests__/analyze-sessions.test.ts +0 -184
- package/src/cli/commands/__tests__/update-user-model.test.ts +0 -234
- package/src/cli/commands/analyze-sessions.ts +0 -335
- package/src/cli/commands/update-user-model.ts +0 -461
- package/src/cli/session-mining/__tests__/transcript.test.ts +0 -118
- package/src/cli/session-mining/corrections.ts +0 -56
- package/src/cli/session-mining/index.ts +0 -13
- package/src/cli/session-mining/text.ts +0 -117
- package/src/cli/session-mining/transcript.ts +0 -125
- package/src/context/__tests__/compaction.test.ts +0 -169
- package/src/context/compaction.ts +0 -201
- package/src/core/session/__tests__/startup.test.ts +0 -281
- package/src/core/session/clean-state.ts +0 -178
- package/src/core/session/index.ts +0 -15
- package/src/core/session/startup.ts +0 -253
- package/src/failure/constraint-handler.ts +0 -171
- package/src/knowledge/__tests__/lifecycle-hooks.test.ts +0 -140
- package/src/knowledge/lifecycle-hooks.ts +0 -202
- package/src/types/session.ts +0 -132
|
@@ -128,7 +128,6 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
128
128
|
{ flags: '-t, --test-command <command>', description: '测试命令' },
|
|
129
129
|
{ flags: '-p, --project-path <path>', description: '项目路径' },
|
|
130
130
|
{ flags: '--allow-partial', description: '允许部分测试通过', defaultValue: false },
|
|
131
|
-
{ flags: '--max-retries <n>', description: '最大重试次数', defaultValue: '2' },
|
|
132
131
|
{ flags: '--coverage', description: '检查测试覆盖率' },
|
|
133
132
|
{ flags: '--coverage-threshold <n>', description: '覆盖率阈值', defaultValue: '80' },
|
|
134
133
|
],
|
|
@@ -157,7 +156,7 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
157
156
|
description: '生成检查报告',
|
|
158
157
|
options: [
|
|
159
158
|
{ flags: '-o, --output <path>', description: '输出文件路径' },
|
|
160
|
-
{ flags: '-f, --format <format>', description: '输出格式 (json/markdown
|
|
159
|
+
{ flags: '-f, --format <format>', description: '输出格式 (json/markdown)', defaultValue: 'markdown' },
|
|
161
160
|
{ flags: '-p, --project-path <path>', description: '项目路径' },
|
|
162
161
|
],
|
|
163
162
|
action: { module: 'report', export: 'report' },
|
|
@@ -235,11 +234,6 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
235
234
|
{ flags: '--sources <sources>', description: '导入源(逗号分隔: code,git,docs)' },
|
|
236
235
|
{ flags: '--limit <n>', description: '结果数量限制', defaultValue: '20' },
|
|
237
236
|
{ flags: '--reset', description: '重置导入状态', defaultValue: false },
|
|
238
|
-
{ flags: '--scope <scope>', description: '知识范围(用于 upsert 去重)' },
|
|
239
|
-
{ flags: '--title <title>', description: '知识标题(用于 upsert)' },
|
|
240
|
-
{ flags: '--content <content>', description: '知识内容 Markdown(用于 upsert)' },
|
|
241
|
-
{ flags: '--file <path>', description: '从文件读取内容(用于 upsert)' },
|
|
242
|
-
{ flags: '--source <source>', description: '知识来源 (cli/design)', defaultValue: 'cli' },
|
|
243
237
|
{ flags: '--fix', description: '自动修复(用于 audit)', defaultValue: false },
|
|
244
238
|
{ flags: '--dry-run', description: '只输出报告不修改(用于 audit)', defaultValue: false },
|
|
245
239
|
{ flags: '--threshold <n>', description: '短内容阈值(字符数,用于 audit)', defaultValue: '50' },
|
|
@@ -258,8 +252,6 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
258
252
|
decay: { impl: { module: 'knowledge', export: 'knowledgeDecay' }, aliases: ['d'] },
|
|
259
253
|
stats: { impl: { module: 'knowledge', export: 'knowledgeStats' }, aliases: ['st'] },
|
|
260
254
|
'sync-rag': { impl: { module: 'knowledge', export: 'knowledgeSyncRag' } },
|
|
261
|
-
'sync-status': { impl: { module: 'knowledge', export: 'knowledgeSyncStatus' }, aliases: ['sync'] },
|
|
262
|
-
upsert: { impl: { module: 'knowledge', export: 'knowledgeUpsert' }, aliases: ['up'] },
|
|
263
255
|
audit: { impl: { module: 'knowledge', export: 'knowledgeAudit' }, aliases: ['a'] },
|
|
264
256
|
snapshot: { impl: { module: 'knowledge', export: 'knowledgeSnapshot' } },
|
|
265
257
|
migrate: { impl: { module: 'knowledge', export: 'knowledgeMigrate' } },
|
|
@@ -305,22 +297,6 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
305
297
|
action: { module: 'posteval-plan', export: 'postevalPlan' },
|
|
306
298
|
mapActionArgs: (positionals) => [{ planPath: positionals[0] }],
|
|
307
299
|
},
|
|
308
|
-
{
|
|
309
|
-
command: 'update-user-model',
|
|
310
|
-
alias: 'uum',
|
|
311
|
-
description: '从新对话中提取信号,更新用户思维模型(增量演化)',
|
|
312
|
-
options: [
|
|
313
|
-
{ flags: '--json', description: 'JSON 格式输出', defaultValue: false },
|
|
314
|
-
{ flags: '--dry-run', description: '只显示变化,不更新状态', defaultValue: false },
|
|
315
|
-
{ flags: '--days <n>', description: '只处理最近 N 天的会话(自然日,含今天)' },
|
|
316
|
-
],
|
|
317
|
-
action: { module: 'update-user-model', export: 'updateUserModel' },
|
|
318
|
-
mapActionArgs: (_pos, options) => [{
|
|
319
|
-
days: options.days !== undefined ? parseInt(String(options.days), 10) : undefined,
|
|
320
|
-
json: options.json,
|
|
321
|
-
dryRun: options.dryRun,
|
|
322
|
-
}],
|
|
323
|
-
},
|
|
324
300
|
{
|
|
325
301
|
command: 'release',
|
|
326
302
|
description: 'npm 发布流水线:tsc → dist 验证 → npm version → git push → npm publish → gh release。不依赖 Studio API。',
|
|
@@ -331,20 +307,6 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
331
307
|
action: { module: 'release', export: 'release' },
|
|
332
308
|
mapActionArgs: (_pos, options) => [{ bumpType: options.bump, dryRun: options.dryRun }],
|
|
333
309
|
},
|
|
334
|
-
{
|
|
335
|
-
command: 'analyze-sessions',
|
|
336
|
-
alias: 'analyze',
|
|
337
|
-
description: '分析 Claude Code 对话,挖掘纠正模式和高频概念,生成规则候选',
|
|
338
|
-
options: [
|
|
339
|
-
{ flags: '-d, --days <n>', description: '分析最近 N 天的会话', defaultValue: '7' },
|
|
340
|
-
{ flags: '--json', description: 'JSON 格式输出', defaultValue: false },
|
|
341
|
-
],
|
|
342
|
-
action: { module: 'analyze-sessions', export: 'analyzeSessions' },
|
|
343
|
-
mapActionArgs: (_pos, options) => [{
|
|
344
|
-
days: parseInt(String(options.days), 10),
|
|
345
|
-
json: options.json,
|
|
346
|
-
}],
|
|
347
|
-
},
|
|
348
310
|
{
|
|
349
311
|
command: 'constraints',
|
|
350
312
|
description: '输出约束集合元数据(版本、hash、计数、文本大小)',
|
|
@@ -281,76 +281,6 @@ export async function knowledgeStats(options: KnowledgeOptions, io: CommandIO =
|
|
|
281
281
|
return { kind: 'ok' };
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
-
export interface KnowledgeUpsertOptions {
|
|
285
|
-
scope?: string;
|
|
286
|
-
title?: string;
|
|
287
|
-
content?: string;
|
|
288
|
-
file?: string;
|
|
289
|
-
type?: string;
|
|
290
|
-
source?: string;
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
/**
|
|
294
|
-
* 设计时知识沉淀:写入 KnowledgeStore + 同步 Prisma Document(Studio UI 可见)
|
|
295
|
-
*
|
|
296
|
-
* 调用 Studio API POST /api/knowledge/upsert(内部端点,无 auth)
|
|
297
|
-
*/
|
|
298
|
-
export async function knowledgeUpsert(options: KnowledgeUpsertOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
299
|
-
const apiPort = process.env.API_PORT || '3001';
|
|
300
|
-
const url = `http://localhost:${apiPort}/api/knowledge/upsert`;
|
|
301
|
-
|
|
302
|
-
// Read content from file if --file specified
|
|
303
|
-
let content = options.content || '';
|
|
304
|
-
if (options.file && !content) {
|
|
305
|
-
try {
|
|
306
|
-
content = fs.readFileSync(options.file, 'utf-8');
|
|
307
|
-
} catch (e: any) {
|
|
308
|
-
logError(io, chalk.red(`Failed to read file: ${options.file}`));
|
|
309
|
-
logError(io, chalk.red(String(e)));
|
|
310
|
-
return { kind: 'fail', reason: `无法读取 --file ${options.file}: ${String(e)}` };
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
if (!options.scope || !options.title || !content) {
|
|
315
|
-
logError(io, chalk.red('--scope, --title, and --content (or --file) are required'));
|
|
316
|
-
return { kind: 'usage-error', reason: '--scope/--title/--content(--file) 缺一不可' };
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
try {
|
|
320
|
-
const res = await fetch(url, {
|
|
321
|
-
method: 'POST',
|
|
322
|
-
headers: { 'Content-Type': 'application/json' },
|
|
323
|
-
body: JSON.stringify({
|
|
324
|
-
scope: options.scope,
|
|
325
|
-
title: options.title,
|
|
326
|
-
content,
|
|
327
|
-
type: options.type || 'architecture',
|
|
328
|
-
source: options.source || 'cli',
|
|
329
|
-
}),
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
if (!res.ok) {
|
|
333
|
-
const err: any = await res.json().catch(() => ({ error: res.statusText }));
|
|
334
|
-
logError(io, chalk.red(`API error ${res.status}: ${err.error || res.statusText}`));
|
|
335
|
-
return { kind: 'fail', reason: `upsert API error ${res.status}: ${err.error || res.statusText}` };
|
|
336
|
-
}
|
|
337
|
-
|
|
338
|
-
const result: any = await res.json();
|
|
339
|
-
log(io, chalk.green(`✅ Knowledge upserted`));
|
|
340
|
-
log(io, ` KnowledgeStore: ${result.knowledgeStore?.action} → ${result.knowledgeStore?.entryId}`);
|
|
341
|
-
log(io, ` Studio UI: ${result.prismaDocument?.action} → ${result.prismaDocument?.docId || 'skipped'}`);
|
|
342
|
-
return { kind: 'ok' };
|
|
343
|
-
} catch (e: any) {
|
|
344
|
-
if (e?.code === 'ECONNREFUSED') {
|
|
345
|
-
logError(io, chalk.red(`Cannot reach Studio API at ${url}. Is the API running?`));
|
|
346
|
-
} else {
|
|
347
|
-
logError(io, chalk.red(`Upsert failed: ${e.message}`));
|
|
348
|
-
}
|
|
349
|
-
return { kind: 'fail', reason: `knowledge upsert 失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
284
|
/**
|
|
355
285
|
* RAG 同步:扫描 .harness/knowledge-docs/ 输出需要 ingest 的文件列表
|
|
356
286
|
*/
|
|
@@ -383,55 +313,6 @@ export async function knowledgeSyncRag(options: KnowledgeOptions, io: CommandIO
|
|
|
383
313
|
return { kind: 'ok' };
|
|
384
314
|
}
|
|
385
315
|
|
|
386
|
-
/**
|
|
387
|
-
* 知识同步状态:检测所有 tracked scope 的新鲜度
|
|
388
|
-
*/
|
|
389
|
-
export async function knowledgeSyncStatus(options: KnowledgeOptions, io: CommandIO = processIO): Promise<CommandResult> {
|
|
390
|
-
const apiPort = process.env.API_PORT || '3001';
|
|
391
|
-
const url = `http://localhost:${apiPort}/api/knowledge/sync-status`;
|
|
392
|
-
|
|
393
|
-
try {
|
|
394
|
-
const res = await fetch(url);
|
|
395
|
-
if (!res.ok) {
|
|
396
|
-
const err: any = await res.json().catch(() => ({ error: res.statusText }));
|
|
397
|
-
logError(io, chalk.red(`API error ${res.status}: ${err.error || res.statusText}`));
|
|
398
|
-
return { kind: 'fail', reason: `sync-status API error ${res.status}: ${err.error || res.statusText}` };
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
const data: any = await res.json();
|
|
402
|
-
|
|
403
|
-
if (options.json) {
|
|
404
|
-
log(io, JSON.stringify(data, null, 2));
|
|
405
|
-
return { kind: 'ok' };
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
log(io, chalk.blue(`🔄 KnowledgeSync Status\n`));
|
|
409
|
-
log(io, chalk.bold(`Tracked scopes: ${data.trackedScopes?.join(', ') || 'none'}`));
|
|
410
|
-
log(io, chalk.bold(`Stale entries: ${data.stale?.length || 0}`));
|
|
411
|
-
|
|
412
|
-
if (data.stale?.length > 0) {
|
|
413
|
-
log(io, chalk.yellow(`\n⚠️ Stale knowledge:\n`));
|
|
414
|
-
for (const s of data.stale) {
|
|
415
|
-
log(io, chalk.yellow(` ${s.scope} (${s.title}): ${s.stalenessHours}h old, files changed: ${s.changedFiles.join(', ')}`));
|
|
416
|
-
}
|
|
417
|
-
} else {
|
|
418
|
-
log(io, chalk.green('\n✅ All knowledge fresh'));
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
if (data.healed?.length > 0) {
|
|
422
|
-
log(io, chalk.cyan(`\n🩹 Auto-healed: ${data.healed.join(', ')}`));
|
|
423
|
-
}
|
|
424
|
-
} catch (e: any) {
|
|
425
|
-
if (e?.code === 'ECONNREFUSED') {
|
|
426
|
-
logError(io, chalk.red(`Cannot reach Studio API at ${url}`));
|
|
427
|
-
} else {
|
|
428
|
-
logError(io, chalk.red(`Sync check failed: ${e.message}`));
|
|
429
|
-
}
|
|
430
|
-
return { kind: 'fail', reason: `knowledge sync status 检查失败: ${e?.code === 'ECONNREFUSED' ? `Studio API 不可达 ${url}` : e.message}` };
|
|
431
|
-
}
|
|
432
|
-
return { kind: 'ok' };
|
|
433
|
-
}
|
|
434
|
-
|
|
435
316
|
/**
|
|
436
317
|
* 飞轮健康检查 — 零 token 检测知识飞轮数据流状态
|
|
437
318
|
*/
|
|
@@ -19,8 +19,6 @@ export interface PassesGateOptions {
|
|
|
19
19
|
projectPath?: string;
|
|
20
20
|
/** 是否允许部分通过 */
|
|
21
21
|
allowPartial?: boolean;
|
|
22
|
-
/** 最大重试次数 */
|
|
23
|
-
maxRetries?: number;
|
|
24
22
|
}
|
|
25
23
|
|
|
26
24
|
/**
|
|
@@ -54,7 +52,6 @@ export async function runPassesGate(
|
|
|
54
52
|
testCommand,
|
|
55
53
|
requireEvidence: false,
|
|
56
54
|
allowPartialPass: options.allowPartial || false,
|
|
57
|
-
maxRetries: options.maxRetries || 2,
|
|
58
55
|
};
|
|
59
56
|
|
|
60
57
|
// 执行测试
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
|
-
import { PerformanceGate } from '../../gates/performance';
|
|
8
|
+
import { PerformanceGate, type ExtendedPerformanceGateConfig } from '../../gates/performance';
|
|
9
|
+
import type { GateContext, PerformanceThresholds } from '../../gates/types';
|
|
9
10
|
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
10
11
|
|
|
11
12
|
export interface PerformanceOptions {
|
|
@@ -19,10 +20,6 @@ export interface PerformanceOptions {
|
|
|
19
20
|
bundle?: boolean;
|
|
20
21
|
/** 打包大小阈值(KB) */
|
|
21
22
|
bundleThreshold?: number;
|
|
22
|
-
/** 是否检查基准测试 */
|
|
23
|
-
benchmark?: boolean;
|
|
24
|
-
/** 基准测试超时(秒) */
|
|
25
|
-
benchmarkTimeout?: number;
|
|
26
23
|
}
|
|
27
24
|
|
|
28
25
|
/**
|
|
@@ -36,34 +33,28 @@ export async function performance(
|
|
|
36
33
|
|
|
37
34
|
const projectPath = options.projectPath || process.cwd();
|
|
38
35
|
|
|
39
|
-
//
|
|
40
|
-
|
|
36
|
+
// 构建配置(架构评审候选2:键名对齐 PerformanceThresholds 真字段——
|
|
37
|
+
// 原 thresholds.coverage / bundleSize 字节换算是错位键,旗帜恒不生效)
|
|
38
|
+
const config: Partial<ExtendedPerformanceGateConfig> = {};
|
|
39
|
+
const thresholds: PerformanceThresholds = {};
|
|
41
40
|
|
|
42
41
|
if (options.coverage && options.coverageThreshold) {
|
|
43
|
-
|
|
44
|
-
coverage: options.coverageThreshold,
|
|
45
|
-
};
|
|
42
|
+
thresholds.minCoverage = options.coverageThreshold;
|
|
46
43
|
}
|
|
47
44
|
|
|
48
45
|
if (options.bundleThreshold) {
|
|
49
|
-
|
|
50
|
-
config.thresholds.bundleSize = options.bundleThreshold * 1024; // KB to bytes
|
|
46
|
+
thresholds.maxBundleSize = options.bundleThreshold; // KB 直传,gate 按 KB 比较
|
|
51
47
|
}
|
|
52
48
|
|
|
53
|
-
if (
|
|
54
|
-
config.
|
|
49
|
+
if (Object.keys(thresholds).length > 0) {
|
|
50
|
+
config.thresholds = thresholds;
|
|
55
51
|
}
|
|
56
52
|
|
|
57
53
|
// 创建性能门控实例
|
|
58
54
|
const gate = new PerformanceGate(config);
|
|
59
55
|
|
|
60
56
|
try {
|
|
61
|
-
const result = await gate.check({
|
|
62
|
-
projectPath,
|
|
63
|
-
checkCoverage: options.coverage,
|
|
64
|
-
checkBundle: options.bundle,
|
|
65
|
-
checkBenchmark: options.benchmark,
|
|
66
|
-
} as any);
|
|
57
|
+
const result = await gate.check({ projectPath } as GateContext);
|
|
67
58
|
|
|
68
59
|
if (result.passed) {
|
|
69
60
|
log(io);
|
|
@@ -83,13 +74,8 @@ export async function performance(
|
|
|
83
74
|
|
|
84
75
|
if (metrics.bundleSize !== undefined) {
|
|
85
76
|
const thresholdKB = options.bundleThreshold || 500;
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
log(io, chalk.gray(` 打包大小: ${actualKB.toFixed(2)} KB ${status}`));
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (metrics.benchmarkTime !== undefined) {
|
|
92
|
-
log(io, chalk.gray(` 基准测试: ${metrics.benchmarkTime}ms`));
|
|
77
|
+
const status = metrics.bundleSize <= thresholdKB ? '✅' : '❌';
|
|
78
|
+
log(io, chalk.gray(` 打包大小: ${metrics.bundleSize} KB ${status}`));
|
|
93
79
|
}
|
|
94
80
|
}
|
|
95
81
|
} else {
|
|
@@ -8,7 +8,6 @@ import chalk from 'chalk';
|
|
|
8
8
|
import * as fs from 'fs/promises';
|
|
9
9
|
import { ConstraintChecker } from '../../core/constraints/checker';
|
|
10
10
|
import { IRON_LAWS, GUIDELINES, PROMPTS } from '../../core/constraints/definitions';
|
|
11
|
-
import { executeWithCollect } from '../../failure/constraint-handler';
|
|
12
11
|
import { getTraceCollector } from '../../monitoring/traces';
|
|
13
12
|
import type { ConstraintContext } from '../../types/constraint';
|
|
14
13
|
import { log, processIO, type CommandIO, type CommandResult } from '../command-contract';
|
|
@@ -17,7 +16,7 @@ export interface ReportOptions {
|
|
|
17
16
|
/** 输出文件路径 */
|
|
18
17
|
output?: string;
|
|
19
18
|
/** 输出格式 */
|
|
20
|
-
format: 'json' | 'markdown'
|
|
19
|
+
format: 'json' | 'markdown';
|
|
21
20
|
/** 项目路径 */
|
|
22
21
|
projectPath?: string;
|
|
23
22
|
}
|
|
@@ -59,12 +58,8 @@ export async function report(options: ReportOptions, io: CommandIO = processIO):
|
|
|
59
58
|
projectPath,
|
|
60
59
|
};
|
|
61
60
|
|
|
62
|
-
//
|
|
63
|
-
const
|
|
64
|
-
checker.checkConstraints(context)
|
|
65
|
-
);
|
|
66
|
-
// COLLECT 策略始终返回 checkResult
|
|
67
|
-
const result = checkResult!;
|
|
61
|
+
// 收集模式(架构评审候选1):不抛、全量收集,铁律违规如实进报告
|
|
62
|
+
const result = await checker.collectConstraints(context);
|
|
68
63
|
|
|
69
64
|
const failedIronLaws = result.ironLaws.filter(r => !r.satisfied);
|
|
70
65
|
const failedGuidelines = result.guidelines.filter(r => !r.satisfied);
|
|
@@ -99,9 +94,6 @@ export async function report(options: ReportOptions, io: CommandIO = processIO):
|
|
|
99
94
|
case 'markdown':
|
|
100
95
|
content = generateMarkdownReport(reportData);
|
|
101
96
|
break;
|
|
102
|
-
case 'html':
|
|
103
|
-
content = generateHtmlReport(reportData);
|
|
104
|
-
break;
|
|
105
97
|
default:
|
|
106
98
|
content = JSON.stringify(reportData, null, 2);
|
|
107
99
|
}
|
|
@@ -150,53 +142,3 @@ function generateMarkdownReport(data: ReportData): string {
|
|
|
150
142
|
return lines.join('\n');
|
|
151
143
|
}
|
|
152
144
|
|
|
153
|
-
function generateHtmlReport(data: ReportData): string {
|
|
154
|
-
const violationsHtml = data.constraints.violations.length > 0
|
|
155
|
-
? `<h3>违规项</h3><ul>${data.constraints.violations.map(v =>
|
|
156
|
-
`<li><strong>${v.id}</strong> (${v.level}): ${v.message}</li>`
|
|
157
|
-
).join('')}</ul>`
|
|
158
|
-
: '';
|
|
159
|
-
|
|
160
|
-
return `<!DOCTYPE html>
|
|
161
|
-
<html lang="zh-CN">
|
|
162
|
-
<head>
|
|
163
|
-
<meta charset="UTF-8">
|
|
164
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
165
|
-
<title>Harness 检查报告</title>
|
|
166
|
-
<style>
|
|
167
|
-
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
|
|
168
|
-
h1 { color: #333; border-bottom: 2px solid #4CAF50; padding-bottom: 10px; }
|
|
169
|
-
h2 { color: #555; margin-top: 30px; }
|
|
170
|
-
table { width: 100%; border-collapse: collapse; margin: 15px 0; }
|
|
171
|
-
th, td { border: 1px solid #ddd; padding: 8px; text-align: left; }
|
|
172
|
-
th { background-color: #4CAF50; color: white; }
|
|
173
|
-
tr:nth-child(even) { background-color: #f2f2f2; }
|
|
174
|
-
.passed { color: #4CAF50; }
|
|
175
|
-
.failed { color: #f44336; }
|
|
176
|
-
.meta { color: #666; font-size: 0.9em; margin-bottom: 20px; }
|
|
177
|
-
</style>
|
|
178
|
-
</head>
|
|
179
|
-
<body>
|
|
180
|
-
<h1>Harness 检查报告</h1>
|
|
181
|
-
<p class="meta">生成时间: ${data.timestamp}<br>项目路径: ${data.projectPath}</p>
|
|
182
|
-
|
|
183
|
-
<h2>约束检查</h2>
|
|
184
|
-
<table>
|
|
185
|
-
<tr><th>指标</th><th>数值</th></tr>
|
|
186
|
-
<tr><td>总约束</td><td>${data.constraints.total}</td></tr>
|
|
187
|
-
<tr><td>Iron Laws</td><td>${data.constraints.ironLaws}</td></tr>
|
|
188
|
-
<tr><td>Guidelines</td><td>${data.constraints.guidelines}</td></tr>
|
|
189
|
-
<tr><td>Prompts</td><td>${data.constraints.prompts}</td></tr>
|
|
190
|
-
<tr><td>通过</td><td class="passed">${data.constraints.passed}</td></tr>
|
|
191
|
-
<tr><td>失败</td><td class="failed">${data.constraints.failed}</td></tr>
|
|
192
|
-
<tr><td>警告</td><td>${data.constraints.warnings}</td></tr>
|
|
193
|
-
</table>
|
|
194
|
-
|
|
195
|
-
${violationsHtml}
|
|
196
|
-
|
|
197
|
-
<footer style="margin-top: 40px; color: #999; text-align: center;">
|
|
198
|
-
报告由 @dommaker/harness 生成
|
|
199
|
-
</footer>
|
|
200
|
-
</body>
|
|
201
|
-
</html>`;
|
|
202
|
-
}
|
|
@@ -7,8 +7,7 @@
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import * as fs from 'fs';
|
|
9
9
|
import * as path from 'path';
|
|
10
|
-
import {
|
|
11
|
-
import { TraceAnalyzer } from '../../monitoring/trace-analyzer';
|
|
10
|
+
import { summarizeTraces, detectTraceAnomalies } from '../../monitoring/trace-analyzer';
|
|
12
11
|
import { readJsonl } from '../../utils/jsonl';
|
|
13
12
|
import { DEFAULT_TRACE_FILE } from '../../types/trace';
|
|
14
13
|
import type { ExecutionTrace } from '../../types/trace';
|
|
@@ -57,18 +56,14 @@ export async function status(options: StatusOptions, io: CommandIO = processIO):
|
|
|
57
56
|
// filter(Boolean) 沿用原语义:parse 成功但值为 falsy 的行(如 "null")不进分析
|
|
58
57
|
// 计数去向:harness#100——skippedLines 另起一行走 stderr 告知(口径不改,只补告知)。
|
|
59
58
|
// 不经 TraceCollector.readReport():本命令按 -p 的 projectPath 直读读链正本,且要的是
|
|
60
|
-
// records + skippedLines
|
|
59
|
+
// records + skippedLines 的原始行数口径;下游只做纯判定(ADR-0020 直调模块级函数)
|
|
61
60
|
const { records, skippedLines } = readJsonl<ExecutionTrace>(tracesPath, 'skip');
|
|
62
61
|
const traces = records.filter(Boolean);
|
|
63
62
|
const traceCount = records.length + skippedLines;
|
|
64
63
|
|
|
65
|
-
//
|
|
66
|
-
const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
// 生成统计
|
|
70
|
-
const summaries = analyzer.summarize(traces);
|
|
71
|
-
const anomalies = analyzer.detectAnomalies(summaries);
|
|
64
|
+
// 生成统计(数据进数据出,无需 collector/分析器实例)
|
|
65
|
+
const summaries = summarizeTraces(traces);
|
|
66
|
+
const anomalies = detectTraceAnomalies(summaries);
|
|
72
67
|
|
|
73
68
|
// 基本统计
|
|
74
69
|
log(io, chalk.gray(`记录数: ${traceCount} 条`));
|
package/src/context/CONTEXT.md
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
# context/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
上下文管理:Token 预算(多级分配)
|
|
4
|
+
上下文管理:Token 预算(多级分配)、会话管理、知识注入。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
7
|
- `TokenBudget` — 多级 token 预算(system/user/tool/reserve)
|
|
8
|
-
- `Compaction` — 会话压缩(摘要/截断/滑动窗口)
|
|
9
8
|
- `SessionManager` — 会话管理
|
|
10
9
|
- `KnowledgeInjector` — 知识注入引擎
|
|
11
10
|
|
|
@@ -16,7 +15,7 @@
|
|
|
16
15
|
|
|
17
16
|
## 约定
|
|
18
17
|
- Token 预算策略由配置文件控制
|
|
19
|
-
-
|
|
18
|
+
- 压缩只剩词汇不留引擎:`types.ts` 保留 `CompactionLevel`(被 `ContextUsageSnapshot.compactionLevel` 引用)与 `CompactionConfig`,但 `SessionCompaction` 引擎、`CompactionResult`、`DEFAULT_COMPACTION_CONFIG` 与 `AdaptiveTokenBudget` 已随 ADR-0022(零生产消费者)删除,本目录不再提供压缩策略组合点
|
|
20
19
|
|
|
21
20
|
## 注意事项
|
|
22
21
|
- 零 Token 成本:纯计算,不调用 LLM
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* TokenBudget 和 TokenEstimator 测试
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { TokenBudget, TokenEstimator
|
|
5
|
+
import { TokenBudget, TokenEstimator } from '../token-budget';
|
|
6
6
|
|
|
7
7
|
describe('TokenEstimator', () => {
|
|
8
8
|
describe('estimateText()', () => {
|
|
@@ -256,83 +256,3 @@ describe('TokenBudget', () => {
|
|
|
256
256
|
});
|
|
257
257
|
});
|
|
258
258
|
});
|
|
259
|
-
|
|
260
|
-
describe('AdaptiveTokenBudget', () => {
|
|
261
|
-
describe('recordActualUsage()', () => {
|
|
262
|
-
it('should record usage history', () => {
|
|
263
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
264
|
-
budget.recordActualUsage(500);
|
|
265
|
-
budget.recordActualUsage(600);
|
|
266
|
-
expect(budget.getAverageUsage()).toBe(550);
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
it('should trim history when exceeding max size', () => {
|
|
270
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
271
|
-
for (let i = 0; i < 15; i++) {
|
|
272
|
-
budget.recordActualUsage(100 + i);
|
|
273
|
-
}
|
|
274
|
-
expect(budget.getAverageUsage()).toBeGreaterThan(0);
|
|
275
|
-
});
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
describe('getAverageUsage()', () => {
|
|
279
|
-
it('should return 0 when no history', () => {
|
|
280
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
281
|
-
expect(budget.getAverageUsage()).toBe(0);
|
|
282
|
-
});
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
describe('predictNeed()', () => {
|
|
286
|
-
it('should return total when no history', () => {
|
|
287
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
288
|
-
expect(budget.predictNeed()).toBe(1000);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
it('should predict based on history', () => {
|
|
292
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
293
|
-
budget.recordActualUsage(400);
|
|
294
|
-
budget.recordActualUsage(500);
|
|
295
|
-
budget.recordActualUsage(600);
|
|
296
|
-
const predicted = budget.predictNeed(0.9);
|
|
297
|
-
expect(predicted).toBeGreaterThan(0);
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
describe('suggestBudgetAdjustment()', () => {
|
|
302
|
-
it('should suggest increase when ratio > 0.9', () => {
|
|
303
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
304
|
-
for (let i = 0; i < 5; i++) {
|
|
305
|
-
budget.recordActualUsage(950);
|
|
306
|
-
}
|
|
307
|
-
const suggestion = budget.suggestBudgetAdjustment();
|
|
308
|
-
expect(suggestion.action).toBe('increase');
|
|
309
|
-
expect(suggestion.suggestedBudget).toBeGreaterThan(1000);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
it('should suggest decrease when ratio < 0.5 and history >= 5', () => {
|
|
313
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
314
|
-
for (let i = 0; i < 5; i++) {
|
|
315
|
-
budget.recordActualUsage(100);
|
|
316
|
-
}
|
|
317
|
-
const suggestion = budget.suggestBudgetAdjustment();
|
|
318
|
-
expect(suggestion.action).toBe('decrease');
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
it('should maintain when ratio is moderate', () => {
|
|
322
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
323
|
-
for (let i = 0; i < 5; i++) {
|
|
324
|
-
budget.recordActualUsage(600);
|
|
325
|
-
}
|
|
326
|
-
const suggestion = budget.suggestBudgetAdjustment();
|
|
327
|
-
expect(suggestion.action).toBe('maintain');
|
|
328
|
-
});
|
|
329
|
-
|
|
330
|
-
it('should maintain when history < 5 even if low usage', () => {
|
|
331
|
-
const budget = new AdaptiveTokenBudget(1000);
|
|
332
|
-
budget.recordActualUsage(100);
|
|
333
|
-
budget.recordActualUsage(100);
|
|
334
|
-
const suggestion = budget.suggestBudgetAdjustment();
|
|
335
|
-
expect(suggestion.action).toBe('maintain');
|
|
336
|
-
});
|
|
337
|
-
});
|
|
338
|
-
});
|
package/src/context/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* 上下文管理模块(ADR-0003:显式清单,禁 export *)
|
|
3
3
|
*
|
|
4
|
-
* Token 预算 +
|
|
4
|
+
* Token 预算 + 会话管理 + 知识注入
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
// 类型
|
|
@@ -17,14 +17,9 @@ export type {
|
|
|
17
17
|
SessionHandle,
|
|
18
18
|
SessionMessage,
|
|
19
19
|
} from './types';
|
|
20
|
-
export { DEFAULT_COMPACTION_CONFIG } from './types';
|
|
21
20
|
|
|
22
21
|
// Token 预算
|
|
23
|
-
export {
|
|
24
|
-
|
|
25
|
-
// 会话压缩
|
|
26
|
-
export { SessionCompaction } from './compaction';
|
|
27
|
-
export type { CompactionResult } from './compaction';
|
|
22
|
+
export { TokenBudget, TokenEstimator } from './token-budget';
|
|
28
23
|
|
|
29
24
|
// 会话管理
|
|
30
25
|
export { SessionManager } from './session-manager';
|
|
@@ -220,76 +220,3 @@ export class TokenBudget {
|
|
|
220
220
|
};
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* 自适应预算管理器
|
|
226
|
-
*
|
|
227
|
-
* 根据历史使用情况动态调整预算
|
|
228
|
-
*/
|
|
229
|
-
export class AdaptiveTokenBudget extends TokenBudget {
|
|
230
|
-
private history: number[] = [];
|
|
231
|
-
private maxHistorySize: number = 10;
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* 记录实际使用情况
|
|
235
|
-
*/
|
|
236
|
-
recordActualUsage(actualTokens: number): void {
|
|
237
|
-
this.history.push(actualTokens);
|
|
238
|
-
if (this.history.length > this.maxHistorySize) {
|
|
239
|
-
this.history.shift();
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* 获取平均使用量
|
|
245
|
-
*/
|
|
246
|
-
getAverageUsage(): number {
|
|
247
|
-
if (this.history.length === 0) return 0;
|
|
248
|
-
return this.history.reduce((a, b) => a + b, 0) / this.history.length;
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 预测未来需求
|
|
253
|
-
*/
|
|
254
|
-
predictNeed(confidence: number = 0.9): number {
|
|
255
|
-
if (this.history.length === 0) return this.total;
|
|
256
|
-
|
|
257
|
-
const avg = this.getAverageUsage();
|
|
258
|
-
const max = Math.max(...this.history);
|
|
259
|
-
|
|
260
|
-
// 简单预测:平均值 + 一定比例的最大值
|
|
261
|
-
return Math.ceil(avg + (max - avg) * confidence);
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
/**
|
|
265
|
-
* 建议预算调整
|
|
266
|
-
*/
|
|
267
|
-
suggestBudgetAdjustment(): {
|
|
268
|
-
action: 'increase' | 'decrease' | 'maintain';
|
|
269
|
-
suggestedBudget: number;
|
|
270
|
-
reason: string;
|
|
271
|
-
} {
|
|
272
|
-
const avg = this.getAverageUsage();
|
|
273
|
-
const ratio = avg / this.total;
|
|
274
|
-
|
|
275
|
-
if (ratio > 0.9) {
|
|
276
|
-
return {
|
|
277
|
-
action: 'increase',
|
|
278
|
-
suggestedBudget: Math.ceil(avg * 1.2),
|
|
279
|
-
reason: '平均使用量接近预算上限,建议增加 20%'
|
|
280
|
-
};
|
|
281
|
-
} else if (ratio < 0.5 && this.history.length >= 5) {
|
|
282
|
-
return {
|
|
283
|
-
action: 'decrease',
|
|
284
|
-
suggestedBudget: Math.ceil(avg * 1.2),
|
|
285
|
-
reason: '平均使用量远低于预算,可减少预算'
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
return {
|
|
290
|
-
action: 'maintain',
|
|
291
|
-
suggestedBudget: this.total,
|
|
292
|
-
reason: '预算使用合理'
|
|
293
|
-
};
|
|
294
|
-
}
|
|
295
|
-
}
|
package/src/context/types.ts
CHANGED
|
@@ -19,15 +19,6 @@ export interface CompactionConfig {
|
|
|
19
19
|
fallbackStrategy: 'truncate-middle' | 'head-drop' | 'retry-with-clamp';
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
export const DEFAULT_COMPACTION_CONFIG: CompactionConfig = {
|
|
23
|
-
triggerRatio: 0.8,
|
|
24
|
-
level: 'eviction',
|
|
25
|
-
preserveToolCallPairs: true,
|
|
26
|
-
structuredSummary: true,
|
|
27
|
-
maxSummaryTokens: 2000,
|
|
28
|
-
fallbackStrategy: 'truncate-middle',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
22
|
export interface SessionMessage {
|
|
32
23
|
role: 'user' | 'assistant' | 'tool';
|
|
33
24
|
content: string;
|