@kkeve/suite 0.0.23 → 0.0.24

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.
@@ -160,7 +160,17 @@ function renderStep(d, ctx) {
160
160
  const executableLineCount = [...body, ...expLines].length;
161
161
  // P1 纵深防御:断言里若残留未解析占位符,说明解析链路有漏(正常已在
162
162
  // stringExpr 处编译期报错),一律不许短路 —— 自证式断言不能当作确定性事实。
163
- const hasUnresolved = [...body, ...expLines].some((b) => /\$\{[^}]+\}/.test(b));
163
+ // stringExpr 对已知合法引用生成 ${String(X)} 模板,运行时变量在这里不是
164
+ // 「未解析占位符」,不应阻断确定性短路。其余残留 ${...} 仍是解析漏洞。
165
+ const hasUnresolved = [...body, ...expLines].some((b) => {
166
+ for (const m of String(b).matchAll(/\$\{([^}]+)\}/g)) {
167
+ const raw = String(m[1]).trim();
168
+ const name = /^String\(\s*([A-Za-z_$][A-Za-z0-9_$]*)\s*\)$/.exec(raw)?.[1] || raw;
169
+ if (!ctx.runtimeVars.has(name))
170
+ return true;
171
+ }
172
+ return false;
173
+ });
164
174
  const deterministic = expectedFullyCovered && !pageLevelOp && executableLineCount > 0 && !hasUnresolved;
165
175
  out.push(`await keveGoal({ step: ${q(stepText)}, expected: ${q(expected)}${deterministic ? ', deterministic: true' : ''} }, async () => {`);
166
176
  for (const b of [...body, ...expLines])
@@ -285,7 +295,7 @@ function renderStep(d, ctx) {
285
295
  : undefined);
286
296
  const targetCtx = valueCtx || d.child?.context || d.child;
287
297
  if (Array.isArray(targetCtx) && targetCtx.length) {
288
- emit([`await engine.drag(${chain}, ${chainExpr(targetCtx, ctx)});`, ...waitExprs(d)]);
298
+ emit([`await engine.drag(${chainExpr(normalizeDragSource(d.context), ctx)}, ${chainExpr(targetCtx, ctx)});`, ...waitExprs(d)]);
289
299
  }
290
300
  else {
291
301
  throw new Error(`DRAGGING 缺少目标元素,拒绝退化为悬停: ${stepText}`);
@@ -1063,6 +1073,23 @@ function collectHops(context) {
1063
1073
  }
1064
1074
  return out;
1065
1075
  }
1076
+ /**
1077
+ * 拖拽源链归一化:录制器常见的「父容器 → 文本 → 列表项」链会把列表项限制在
1078
+ * 文本叶节点后代内,导致确定性定位落空。同用例里可稳定命中的点击链是
1079
+ * 「列表项 → 文本/includes」,拖拽源也应优先使用该形态。
1080
+ */
1081
+ function normalizeDragSource(context) {
1082
+ const hops = Array.isArray(context) ? context : [];
1083
+ if (hops.length < 2)
1084
+ return context;
1085
+ const prev = hops[hops.length - 2];
1086
+ const tail = hops[hops.length - 1];
1087
+ const isListItemTail = String(tail?.value ?? '') === '[data-e2e-name="列表项"]';
1088
+ const isTextHop = String(prev?.type || '') === 'includes' || String(prev?.type || '') === 'text';
1089
+ if (isListItemTail && isTextHop)
1090
+ return [tail, prev];
1091
+ return context;
1092
+ }
1066
1093
  /** 截图基线 key 兜底:步骤文案 → 稳定 key(同步骤重复生成不变) */
1067
1094
  function sanitizeShotKey(text) {
1068
1095
  return String(text || 'shot').replace(/[^a-zA-Z0-9\u4e00-\u9fff]/g, '_').slice(0, 40);
@@ -158,7 +158,9 @@ exports.tools.set('done', tool({
158
158
  execute: async function (input) {
159
159
  const icons = { pass: '✅', fail: '❌', blocked: '🚫' };
160
160
  let verdict = input.verdict;
161
- // 强制校验:verdict 缺失或非法时从 text 推断(MacroTool 扁平化导致 verdict 变 optional)
161
+ // 强制校验:verdict 缺失或非法时从 text 推断(MacroTool 扁平化导致 verdict 变 optional)。
162
+ // verdict 与可推断文本都缺失时不能默认 pass —— Agent 已把动作做完但没给出结论,
163
+ // 交给上层按保守语义判定,而不是在工具层制造假绿。
162
164
  if (!icons[verdict]) {
163
165
  console.warn(`[done] ⚠️ verdict missing or invalid ("${input.verdict}"), inferring from text`);
164
166
  const t = String(input.text || '').toLowerCase();
@@ -168,9 +170,12 @@ exports.tools.set('done', tool({
168
170
  else if (t.includes('blocked') || t.includes('阻塞') || t.includes('阻止')) {
169
171
  verdict = 'blocked';
170
172
  }
171
- else {
173
+ else if (t.length > 0) {
172
174
  verdict = 'pass'; // 有 text 且无否定词 → 默认 pass
173
175
  }
176
+ else {
177
+ throw new Error('done 缺少 verdict 且没有可推断的 text,不能默认判定通过');
178
+ }
174
179
  }
175
180
  return `${icons[verdict]} Task ${verdict}: ${input.text}`;
176
181
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kkeve/suite",
3
- "version": "0.0.23",
3
+ "version": "0.0.24",
4
4
  "description": "AI-powered full-stack test agent CLI - static analysis, unit test, component test, E2E test, execute, evaluate, and report",
5
5
  "license": "MIT",
6
6
  "bin": {