@kkeve/suite 0.0.1 → 0.0.2

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 (152) hide show
  1. package/README.md +53 -0
  2. package/dist/bin/keve.d.ts +13 -0
  3. package/dist/bin/keve.js +164 -0
  4. package/dist/commands/auth-update.d.ts +16 -0
  5. package/dist/commands/auth-update.js +88 -0
  6. package/dist/commands/clean.d.ts +10 -0
  7. package/dist/commands/clean.js +78 -0
  8. package/dist/commands/config.d.ts +17 -0
  9. package/dist/commands/config.js +99 -0
  10. package/dist/commands/init.d.ts +15 -0
  11. package/dist/commands/init.js +292 -0
  12. package/dist/commands/report.d.ts +12 -0
  13. package/dist/commands/report.js +159 -0
  14. package/dist/commands/run.d.ts +29 -0
  15. package/dist/commands/run.js +417 -0
  16. package/dist/commands/test.d.ts +12 -0
  17. package/dist/commands/test.js +102 -0
  18. package/dist/commands/vitest.d.ts +14 -0
  19. package/dist/commands/vitest.js +166 -0
  20. package/dist/config.d.ts +109 -0
  21. package/dist/config.js +291 -0
  22. package/dist/core/action-log-writer.d.ts +47 -0
  23. package/dist/{tools/write-file.js → core/action-log-writer.js} +42 -26
  24. package/dist/core/auth.d.ts +17 -0
  25. package/dist/core/auth.js +224 -0
  26. package/dist/core/cdp-resolver.d.ts +5 -0
  27. package/dist/core/cdp-resolver.js +93 -0
  28. package/dist/core/generateReport/report.d.ts +19 -0
  29. package/dist/core/generateReport/report.js +91 -0
  30. package/dist/core/generateReport/reportData.d.ts +215 -0
  31. package/dist/core/generateReport/reportData.js +735 -0
  32. package/dist/core/global-setup.d.ts +6 -0
  33. package/dist/core/global-setup.js +113 -0
  34. package/dist/core/keve-aspect.d.ts +54 -0
  35. package/dist/core/keve-aspect.js +52 -0
  36. package/dist/core/keve-decorators.d.ts +51 -0
  37. package/dist/core/keve-decorators.js +95 -0
  38. package/dist/core/keve-registry.d.ts +38 -0
  39. package/dist/core/keve-registry.js +25 -0
  40. package/dist/core/keve-report.d.ts +27 -0
  41. package/dist/core/keve-report.js +357 -0
  42. package/dist/core/keve-test.d.ts +36 -0
  43. package/dist/core/keve-test.js +364 -0
  44. package/dist/core/learned-actions.d.ts +24 -0
  45. package/dist/core/learned-actions.js +65 -0
  46. package/dist/core/pw-config.d.ts +19 -0
  47. package/dist/core/pw-config.js +180 -0
  48. package/dist/index.d.ts +8 -23
  49. package/dist/index.js +36 -43
  50. package/dist/page-agent/agent.d.ts +129 -0
  51. package/dist/page-agent/agent.js +585 -0
  52. package/dist/page-agent/hooks.d.ts +13 -0
  53. package/dist/page-agent/hooks.js +218 -0
  54. package/dist/page-agent/tools.d.ts +29 -0
  55. package/dist/page-agent/tools.js +297 -0
  56. package/doc/README.md +124 -0
  57. package/doc/agents/keve-dev-test-agent.md +140 -0
  58. package/doc/agents/keve-test-agent.md +178 -0
  59. package/doc/asserts//346/265/213/350/257/225/345/220/204/351/230/266/346/256/265/350/257/264/346/230/216.png +0 -0
  60. package/doc/keve-agent-demo.html +370 -0
  61. package/doc/skills/keve-bddcase-gen/SKILL.md +332 -0
  62. package/doc/skills/keve-bddcase-gen/references/test-case-rules.md +91 -0
  63. package/doc/skills/keve-bddcase-gen/references/test-cases-example.md +103 -0
  64. package/doc/skills/keve-component-gen/SKILL.md +255 -0
  65. package/doc/skills/keve-component-gen/references/component-mock-strategy.md +74 -0
  66. package/doc/skills/keve-component-gen/references/component-model-template.md +98 -0
  67. package/doc/skills/keve-component-gen/references/component-test-template.md +100 -0
  68. package/doc/skills/keve-component-gen/references/vue-test-utils-guide.md +91 -0
  69. package/doc/skills/keve-e2e-gen/SKILL.md +179 -0
  70. package/doc/skills/keve-e2e-gen/references/code-analysis-guide.md +208 -0
  71. package/doc/skills/keve-e2e-gen/references/decisions.md +36 -0
  72. package/doc/skills/keve-e2e-gen/references/pom-generation-guide.md +140 -0
  73. package/doc/skills/keve-e2e-gen/references/script-generation-guide.md +236 -0
  74. package/doc/skills/keve-e2e-gen/references/test-auditor-guide.md +189 -0
  75. package/doc/skills/keve-e2e-gen/scripts/extract-components.sh +85 -0
  76. package/doc/skills/keve-e2e-gen/scripts/inject-testids.sh +95 -0
  77. package/doc/skills/keve-static-analysis/SKILL.md +362 -0
  78. package/doc/skills/keve-static-analysis/references/codegraph-analysis-guide.md +108 -0
  79. package/doc/skills/keve-static-analysis/references/git-impact-analysis.md +81 -0
  80. package/doc/skills/keve-static-analysis/references/lint-fix-guide.md +69 -0
  81. package/doc/skills/keve-static-analysis/references/runtime-risk-patterns.md +92 -0
  82. package/doc/skills/keve-static-analysis/references/security-scan-guide.md +32 -0
  83. package/doc/skills/keve-static-analysis/references/ts-error-patterns.md +98 -0
  84. package/doc/skills/keve-static-analysis/references/vue-security-guide.md +72 -0
  85. package/doc/skills/keve-test-eval/SKILL.md +78 -0
  86. package/doc/skills/keve-test-eval/references/common-fix-guide.md +166 -0
  87. package/doc/skills/keve-test-eval/references/confidence-data-guide.md +78 -0
  88. package/doc/skills/keve-test-eval/references/fix-loop-guide.md +67 -0
  89. package/doc/skills/keve-unit-gen/SKILL.md +203 -0
  90. package/doc/skills/keve-unit-gen/references/boundary-conditions.md +78 -0
  91. package/doc/skills/keve-unit-gen/references/unit-mock-strategy.md +71 -0
  92. package/doc/skills/keve-unit-gen/references/unit-test-template.md +105 -0
  93. package/package.json +47 -25
  94. package/dist/agent/factory.d.ts +0 -25
  95. package/dist/agent/factory.js +0 -99
  96. package/dist/agent/index.d.ts +0 -3
  97. package/dist/agent/index.js +0 -7
  98. package/dist/agent/loader.d.ts +0 -22
  99. package/dist/agent/loader.js +0 -117
  100. package/dist/agent/types.d.ts +0 -9
  101. package/dist/agent/types.js +0 -2
  102. package/dist/agent-loop/complete-task.d.ts +0 -28
  103. package/dist/agent-loop/complete-task.js +0 -70
  104. package/dist/agent-loop/index.d.ts +0 -16
  105. package/dist/agent-loop/index.js +0 -26
  106. package/dist/agent-loop/loop.d.ts +0 -42
  107. package/dist/agent-loop/loop.js +0 -282
  108. package/dist/agent-loop/runtime.d.ts +0 -40
  109. package/dist/agent-loop/runtime.js +0 -163
  110. package/dist/agent-loop/types.d.ts +0 -161
  111. package/dist/agent-loop/types.js +0 -105
  112. package/dist/context/index.d.ts +0 -8
  113. package/dist/context/index.js +0 -29
  114. package/dist/context/tokenCalculation.d.ts +0 -55
  115. package/dist/context/tokenCalculation.js +0 -95
  116. package/dist/context/truncation.d.ts +0 -67
  117. package/dist/context/truncation.js +0 -142
  118. package/dist/context/types.d.ts +0 -7
  119. package/dist/context/types.js +0 -27
  120. package/dist/llm/OpenAIClient.d.ts +0 -31
  121. package/dist/llm/OpenAIClient.js +0 -455
  122. package/dist/llm/constants.d.ts +0 -2
  123. package/dist/llm/constants.js +0 -6
  124. package/dist/llm/errors.d.ts +0 -25
  125. package/dist/llm/errors.js +0 -51
  126. package/dist/llm/index.d.ts +0 -49
  127. package/dist/llm/index.js +0 -158
  128. package/dist/llm/types.d.ts +0 -124
  129. package/dist/llm/types.js +0 -2
  130. package/dist/llm/utils.d.ts +0 -29
  131. package/dist/llm/utils.js +0 -284
  132. package/dist/skill/frontmatter-parser.d.ts +0 -50
  133. package/dist/skill/frontmatter-parser.js +0 -152
  134. package/dist/skill/index.d.ts +0 -12
  135. package/dist/skill/index.js +0 -17
  136. package/dist/skill/loader.d.ts +0 -96
  137. package/dist/skill/loader.js +0 -256
  138. package/dist/tools/ask-user.d.ts +0 -8
  139. package/dist/tools/ask-user.js +0 -47
  140. package/dist/tools/bash.d.ts +0 -47
  141. package/dist/tools/bash.js +0 -213
  142. package/dist/tools/edit-file.d.ts +0 -9
  143. package/dist/tools/edit-file.js +0 -76
  144. package/dist/tools/glob.d.ts +0 -8
  145. package/dist/tools/glob.js +0 -101
  146. package/dist/tools/grep.d.ts +0 -10
  147. package/dist/tools/grep.js +0 -76
  148. package/dist/tools/index.d.ts +0 -24
  149. package/dist/tools/index.js +0 -47
  150. package/dist/tools/read-files.d.ts +0 -8
  151. package/dist/tools/read-files.js +0 -81
  152. package/dist/tools/write-file.d.ts +0 -8
@@ -0,0 +1,357 @@
1
+ "use strict";
2
+ /**
3
+ * keve-report: Playwright Reporter that collects test execution data
4
+ *
5
+ * After EACH test completes (onTestEnd), records test result metadata
6
+ * including step results, error category, AI exploration steps, and diagnostics.
7
+ * Results are appended to confidence-data.jsonl (one line per test).
8
+ *
9
+ * After ALL tests complete (onEnd), generates report-data.json directly,
10
+ * eliminating the need for a separate `keve report` command.
11
+ *
12
+ * Usage in playwright config:
13
+ * reporter: [
14
+ * ['list'],
15
+ * ['@ks-data/keve-suite/keve-report'],
16
+ * ]
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ const fs = __importStar(require("fs"));
53
+ const path = __importStar(require("path"));
54
+ const reportData_1 = require("./generateReport/reportData");
55
+ const reportData_2 = require("./generateReport/reportData");
56
+ // Error classification patterns (duplicated from keve-decorators.ts for onTestEnd primary classification)
57
+ const ENV_ERROR_PATTERNS = /timeout|timed out|locator.*not found|navigation.*intercepted|net::|ERR_|authenticate|redirect|ECONNREFUSED|socket hang up|Cannot navigate to invalid URL|Protocol error.*Page\.navigate/i;
58
+ class KeveReporter {
59
+ outputPath = '';
60
+ resultDir = '';
61
+ onBegin(config, suite) {
62
+ // Resolve resultDir: taskDir/test-artifacts/round-N (matches Playwright config)
63
+ const taskDir = process.env.KEVE_TASK_DIR || '.keve';
64
+ const round = process.env.KEVE_ROUND || 'latest';
65
+ this.resultDir = path.join(taskDir, 'test-artifacts', `round-${round}`);
66
+ this.outputPath = path.join(this.resultDir, 'confidence-data.jsonl');
67
+ if (!fs.existsSync(this.resultDir)) {
68
+ fs.mkdirSync(this.resultDir, { recursive: true });
69
+ }
70
+ console.log(`[keve-reporter] Confidence evaluation will be saved to: ${this.outputPath}`);
71
+ }
72
+ // Confidence values by errorCategory — gives meaningful scores instead of always 0
73
+ static CONFIDENCE_MAP = {
74
+ pass: 100, // 测试通过,确定性最高
75
+ script: 95, // 脚本代码错误,结果确定性高
76
+ visual: 80, // 视觉断言已评估,置信度较高
77
+ 'text-mismatch': 70, // UI文案格式不匹配(功能逻辑正确,文案措辞变了)
78
+ assert: 50, // Playwright 断言失败,需人工确认
79
+ 'react-fail': 40, // AI 探索未达预期,需人工确认
80
+ env: 0, // 环境问题,测试结果不可信
81
+ unknown: 0, // 未知异常,无参考价值
82
+ };
83
+ async onTestEnd(test, result) {
84
+ // ── Step 0: Extract Agent conclusion from keveGoalResult attachments ──
85
+ const steps = parseSteps(result.attachments);
86
+ const agentConclusion = extractAgentConclusion(steps);
87
+ // Error classification: Agent conclusion takes priority over fnError
88
+ let effectiveCategory = 'unknown';
89
+ let skipAI = false;
90
+ let classifiedMessage = '';
91
+ if (result.status === 'skipped') {
92
+ skipAI = true;
93
+ effectiveCategory = 'unknown';
94
+ }
95
+ else if (result.status === 'passed') {
96
+ effectiveCategory = 'pass';
97
+ }
98
+ else if (agentConclusion) {
99
+ // ── Agent provided a conclusion — use it directly (most reliable) ──
100
+ classifiedMessage = agentConclusion.text.substring(0, 500);
101
+ if (agentConclusion.result === 'pass') {
102
+ // Agent said pass but test still failed → fn script issue (not env)
103
+ effectiveCategory = 'pass';
104
+ skipAI = true;
105
+ }
106
+ else if (agentConclusion.result === 'blocked') {
107
+ effectiveCategory = 'env';
108
+ skipAI = true;
109
+ }
110
+ else {
111
+ // Agent said fail → assert/text-mismatch depending on diagnostic hints
112
+ const hint = buildDiagnosticHint(result.attachments);
113
+ if (hint && (hint.includes('实际显示') || hint.includes('页面实际') || hint.includes('而非预期'))) {
114
+ effectiveCategory = 'text-mismatch';
115
+ skipAI = false;
116
+ }
117
+ else {
118
+ effectiveCategory = 'assert';
119
+ skipAI = false;
120
+ }
121
+ }
122
+ }
123
+ else if (result.error) {
124
+ // ── No Agent conclusion — fall back to fnError classification ──
125
+ const rawError = result.error.message || String(result.error);
126
+ classifiedMessage = rawError.substring(0, 500);
127
+ // 1. 脚本错误:确定性短路
128
+ const err = result.error;
129
+ if (err instanceof TypeError || err instanceof ReferenceError || err instanceof SyntaxError) {
130
+ effectiveCategory = 'script';
131
+ skipAI = true;
132
+ }
133
+ // 2. 视觉断言失败(keveAssert 抛出的特定错误)
134
+ else if (rawError.includes('视觉断言失败')) {
135
+ effectiveCategory = 'visual';
136
+ skipAI = true;
137
+ }
138
+ // 3. Playwright expect 断言失败
139
+ else if (rawError.includes('expect(') || err?.constructor?.name === 'AssertionError' || err?.name === 'AssertionError') {
140
+ effectiveCategory = 'assert';
141
+ skipAI = false;
142
+ }
143
+ // 4. 环境/基础设施错误
144
+ else if (ENV_ERROR_PATTERNS.test(rawError.substring(0, 300))) {
145
+ effectiveCategory = 'env';
146
+ skipAI = true;
147
+ }
148
+ // 5. Re-Act loop 失败:AI 探索未达到预期(关键分类)
149
+ else if (rawError.includes('Re-Act loop did not achieve') || rawError.includes('Expected not achieved after agent explore')) {
150
+ effectiveCategory = 'react-fail';
151
+ skipAI = false;
152
+ }
153
+ // 5.5 文案不匹配
154
+ else if (rawError.includes('Expected not achieved')) {
155
+ const hint = buildDiagnosticHint(result.attachments);
156
+ if (hint && (hint.includes('实际显示') || hint.includes('页面实际'))) {
157
+ effectiveCategory = 'text-mismatch';
158
+ skipAI = false;
159
+ }
160
+ else {
161
+ effectiveCategory = 'react-fail';
162
+ skipAI = false;
163
+ }
164
+ }
165
+ // 6. 兜底
166
+ else {
167
+ effectiveCategory = 'unknown';
168
+ skipAI = true;
169
+ }
170
+ }
171
+ // Script/visual/env error short-circuit — skip AI evaluation
172
+ if (skipAI) {
173
+ const thought = effectiveCategory === 'script'
174
+ ? `脚本本身存在代码错误(${classifiedMessage}),测试结果无效`
175
+ : effectiveCategory === 'env'
176
+ ? `环境/基础设施异常(${classifiedMessage}),非应用功能问题`
177
+ : effectiveCategory === 'visual'
178
+ ? `视觉断言失败(${classifiedMessage}),已由keveAssert评估`
179
+ : effectiveCategory === 'unknown'
180
+ ? `无法分类的异常(${classifiedMessage}),跳过AI评估`
181
+ : effectiveCategory === 'pass'
182
+ ? `Agent判定通过但fn脚本执行失败(${classifiedMessage}),脚本需修复`
183
+ : '用例被跳过';
184
+ // AI评估列显示"跳过"(表示AI未参与评估),而非"不通过"
185
+ const aiData = (effectiveCategory !== 'assert' && effectiveCategory !== 'react-fail' && effectiveCategory !== 'text-mismatch') ? '跳过' : '不通过';
186
+ const keveScreenshots = (0, reportData_1.parseKeveAsserts)(result.attachments).map(ka => ka.label);
187
+ this.appendRecord({
188
+ title: test.title,
189
+ data: aiData,
190
+ confidence: KeveReporter.CONFIDENCE_MAP[effectiveCategory] ?? 0,
191
+ thought,
192
+ errorCategory: effectiveCategory,
193
+ ...(keveScreenshots.length > 0 ? { keveScreenshots } : {}), // 仅非空时写入
194
+ steps,
195
+ diagnosticHint: buildDiagnosticHint(result.attachments),
196
+ });
197
+ console.log(`[keve-reporter] ${test.title}: SHORT-CIRCUIT skipAI (category=${effectiveCategory})`);
198
+ return;
199
+ }
200
+ // Record result without AI confidence evaluation
201
+ const keveScreenshots = (0, reportData_1.parseKeveAsserts)(result.attachments).map(ka => ka.label);
202
+ this.appendRecord({
203
+ title: test.title,
204
+ data: result.status === 'passed' ? '通过' : '不通过',
205
+ confidence: KeveReporter.CONFIDENCE_MAP[effectiveCategory] ?? 0,
206
+ thought: '',
207
+ errorCategory: effectiveCategory,
208
+ ...(keveScreenshots.length > 0 ? { keveScreenshots } : {}),
209
+ steps,
210
+ diagnosticHint: buildDiagnosticHint(result.attachments),
211
+ });
212
+ console.log(`[keve-reporter] ${test.title}: ${result.status} (category=${effectiveCategory})`);
213
+ }
214
+ async onEnd(result) {
215
+ console.log(`[keve-reporter] All tests completed. Generating report-data.json...`);
216
+ // ── 直接生成 report-data.json(不再需要 keve report 命令) ──
217
+ try {
218
+ const taskDir = process.env.KEVE_TASK_DIR || '.keve';
219
+ const projectRoot = taskDir;
220
+ // 找到 test-results.json(Playwright JSON Reporter 写入)
221
+ const testResultsPath = path.join(this.resultDir, 'test-results.json');
222
+ const confidencePath = this.outputPath;
223
+ if (fs.existsSync(testResultsPath) && fs.existsSync(confidencePath)) {
224
+ // YAML 用例文件在 {taskDir}/cases/test-cases.yaml
225
+ const casesYamlPath = path.join(taskDir, 'cases', 'test-cases.yaml');
226
+ const reportData = await (0, reportData_2.generateReportData)({
227
+ projectRoot,
228
+ resultsPath: testResultsPath,
229
+ confidencePath,
230
+ casesPath: fs.existsSync(casesYamlPath) ? casesYamlPath : undefined,
231
+ });
232
+ // 写入 report-data.json
233
+ const reportDataPath = path.join(this.resultDir, 'report-data.json');
234
+ fs.writeFileSync(reportDataPath, JSON.stringify(reportData, null, 2));
235
+ // 更新 latest 目录 → symlink 指向最新 round(避免重复拷贝 34MB+)
236
+ const latestDir = path.join(taskDir, 'test-artifacts', 'latest');
237
+ if (latestDir !== this.resultDir) {
238
+ try {
239
+ if (fs.existsSync(latestDir)) {
240
+ const stat = fs.lstatSync(latestDir);
241
+ if (stat.isSymbolicLink()) {
242
+ fs.unlinkSync(latestDir);
243
+ }
244
+ else {
245
+ fs.rmSync(latestDir, { recursive: true, force: true });
246
+ }
247
+ }
248
+ fs.symlinkSync(path.basename(this.resultDir), latestDir, 'junction');
249
+ }
250
+ catch { /* ignore latest symlink error */ }
251
+ }
252
+ console.log(`[keve-reporter] report-data.json generated at: ${reportDataPath}`);
253
+ }
254
+ else {
255
+ console.log(`[keve-reporter] Skipped report-data.json generation (missing test-results.json or confidence-data.jsonl)`);
256
+ }
257
+ }
258
+ catch (err) {
259
+ console.error(`[keve-reporter] Failed to generate report-data.json: ${err.message}`);
260
+ }
261
+ }
262
+ appendRecord(record) {
263
+ // 清洗 steps:移除 screenshotBase64(截图已有文件路径保存)
264
+ // 移除 snapshotPreview / finalSnapshot(冗余大字段,截图路径可获取相同信息)
265
+ if (record.steps) {
266
+ record.steps = record.steps.map((step) => {
267
+ const { screenshotBase64, finalSnapshot, ...stepRest } = step;
268
+ if (stepRest.actions) {
269
+ stepRest.actions = stepRest.actions.map((a) => {
270
+ const { snapshotPreview, ...actionRest } = a;
271
+ return actionRest;
272
+ });
273
+ }
274
+ return stepRest;
275
+ });
276
+ }
277
+ fs.appendFileSync(this.outputPath, JSON.stringify(record) + '\n');
278
+ }
279
+ }
280
+ // ── 解析 keveGoalResult attachment(合并后统一步骤数据) ──────────────
281
+ /** Parse all keveGoalResult attachments into a unified steps array */
282
+ function parseSteps(attachments) {
283
+ const results = [];
284
+ for (const att of attachments) {
285
+ if (att.name !== 'keveGoalResult' || !att.body)
286
+ continue;
287
+ try {
288
+ let jsonStr;
289
+ if (typeof att.body === 'string') {
290
+ // Playwright JSON Reporter may base64-encode Buffer bodies
291
+ try {
292
+ jsonStr = Buffer.from(att.body, 'base64').toString('utf-8');
293
+ JSON.parse(jsonStr); // validate it's JSON
294
+ }
295
+ catch {
296
+ jsonStr = att.body; // fallback: treat as raw JSON string
297
+ }
298
+ }
299
+ else {
300
+ jsonStr = att.body.toString('utf-8');
301
+ }
302
+ results.push(JSON.parse(jsonStr));
303
+ }
304
+ catch { /* skip malformed attachment */ }
305
+ }
306
+ return results;
307
+ }
308
+ /** Extract Agent conclusion (result + text) from keveGoalResult steps */
309
+ function extractAgentConclusion(steps) {
310
+ // Walk steps in reverse to find the most recent conclusion
311
+ for (let i = steps.length - 1; i >= 0; i--) {
312
+ const step = steps[i];
313
+ // Step-level conclusion (from agent.ts buildResult → keveGoalResult attachment)
314
+ if (step.conclusion) {
315
+ // Find the done action text for context
316
+ const doneAction = step.actions?.find(a => a.tool === 'done');
317
+ return { result: step.conclusion, text: doneAction?.text || '' };
318
+ }
319
+ // Action-level conclusion (from done action's 'result' field)
320
+ if (step.actions) {
321
+ for (let j = step.actions.length - 1; j >= 0; j--) {
322
+ const action = step.actions[j];
323
+ if (action?.tool === 'done' && action?.conclusion) {
324
+ return { result: action.conclusion, text: action.text || '' };
325
+ }
326
+ }
327
+ }
328
+ }
329
+ return undefined;
330
+ }
331
+ /** Build diagnosticHint from keveGoalResult attachments (primary) + fallback to keveDiagnosticHint */
332
+ function buildDiagnosticHint(attachments) {
333
+ // 优先从 keveGoalResult 中提取 diagnosticHints
334
+ const goalResults = parseSteps(attachments);
335
+ const allHints = [];
336
+ for (const gr of goalResults) {
337
+ if (gr.success)
338
+ continue;
339
+ if (gr.diagnosticHints?.length) {
340
+ allHints.push(...gr.diagnosticHints);
341
+ }
342
+ }
343
+ if (allHints.length)
344
+ return allHints.join(';');
345
+ // 兜底:从旧的 keveDiagnosticHint attachment 提取
346
+ const hint = attachments.find(a => a.name === 'keveDiagnosticHint');
347
+ if (!hint || !hint.body)
348
+ return undefined;
349
+ try {
350
+ const data = typeof hint.body === 'string' ? JSON.parse(hint.body) : JSON.parse(hint.body.toString('utf-8'));
351
+ return data.hints?.join(';') || undefined;
352
+ }
353
+ catch {
354
+ return undefined;
355
+ }
356
+ }
357
+ exports.default = KeveReporter;
@@ -0,0 +1,36 @@
1
+ /**
2
+ * keve-test — Playwright test fixture: keveGoal (唯一操作原语)
3
+ *
4
+ * keveGoal 执行 fn 后 → 截图+fn逻辑+expected → AI判断是否一致
5
+ * 一致 → 通过
6
+ * 不一致 → AI 告诉你还差什么 → Re-Act 探索 → 探索成功
7
+ * fn 无 → Re-Act 直接探索
8
+ * fn 异常 → Re-Act 自愈
9
+ *
10
+ * keveAssert 已被吸收:keveGoal 的 expected 即断言语义
11
+ *
12
+ * Usage:
13
+ * import { test, expect } from '@ks-data/keve-suite/keve-test';
14
+ *
15
+ * test('my test', async ({ page, keveGoal }) => {
16
+ * await keveGoal({ step: '导航到列表页', expected: '页面加载完成' }, async () => {
17
+ * await page.goto('/agents');
18
+ * });
19
+ *
20
+ * // AI explore (no fn):
21
+ * await keveGoal({ step: '点击新建按钮', expected: '弹出创建对话框' });
22
+ * });
23
+ */
24
+ import { expect } from '@playwright/test';
25
+ export { expect };
26
+ export { keveModel, keveScene, getModelScenes } from './keve-decorators';
27
+ export type { KeveSceneMeta } from './keve-decorators';
28
+ export { sceneCodeMap, sceneEvalMetaMap } from './keve-registry';
29
+ export type { KeveEvalMeta, ErrorCategory } from './keve-registry';
30
+ export { sceneGoalsMap, type KeveGoalMeta } from './keve-registry';
31
+ export interface KeveGoalCallOptions {
32
+ precondition?: string;
33
+ step: string;
34
+ expected: string;
35
+ }
36
+ export declare const test: any;