@tea-agent/loop-agent 0.33.3 → 0.33.4

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 CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.33.4] - 2026-08-10
6
+
7
+ ### 重点更新
8
+
9
+ - 修复后端测试场景参数评估器对 fallback 字段的误判问题,解除由此导致的错误执行阻断
10
+
11
+ ### 修复
12
+
13
+ - 修复 backend-test 场景参数评估器在 Markdown 无可解析场景意图时,将 TP-ID 推断的 fallback 字段(如 `TP-CREATE-APPROVER-FORMAT` → `cREATE`/`pattern-invalid`)被大写启发式误判为不匹配的问题;此类条目现统一判定为无法判定且不可修复,不再生成无效修复建议,由此导致的错误执行就绪阻断已解除
14
+ - 修复 fallback 场景参数状态被错误标记为失败的问题,现根据实际情况正确标记为不可用或部分可用,且不弱化任何就绪性门禁
15
+
5
16
  ## [0.33.3] - 2026-08-10
6
17
 
7
18
  ### 重点更新
@@ -135,6 +135,7 @@ export function inferScenarioParamIntent(input) {
135
135
  field,
136
136
  bound,
137
137
  example,
138
+ intentSource: "machine-line",
138
139
  };
139
140
  }
140
141
  return {
@@ -142,6 +143,7 @@ export function inferScenarioParamIntent(input) {
142
143
  field,
143
144
  bound,
144
145
  example,
146
+ intentSource: "machine-line",
145
147
  };
146
148
  }
147
149
  const upper = tpId.toUpperCase();
@@ -178,37 +180,39 @@ export function inferScenarioParamIntent(input) {
178
180
  const exampleMatch = /(?:反例|invalid example|example)\s*[=::]\s*[`"]?([^`"\n]+)[`"]?/i.exec(caseBody);
179
181
  const example = exampleMatch?.[1]?.trim();
180
182
  if (/EMPTY|空白|空串|空字符串/.test(upper) || /空字符串|空串|empty string/i.test(caseBody)) {
181
- return { intent: "empty", field, bound, example };
183
+ return { intent: "empty", field, bound, example, intentSource: "tp-fallback" };
182
184
  }
183
185
  if (/MISSING|缺省|缺失|省略/.test(upper) || /缺少字段|缺失字段|missing field/i.test(caseBody)) {
184
- return { intent: "missing", field, bound, example };
186
+ return { intent: "missing", field, bound, example, intentSource: "tp-fallback" };
185
187
  }
186
188
  if (/\bNULL\b|空值/.test(upper) || /\bnull\b/i.test(caseBody)) {
187
- return { intent: "null", field, bound, example };
189
+ return { intent: "null", field, bound, example, intentSource: "tp-fallback" };
188
190
  }
189
191
  if (/MAX[-_]?PLUS[-_]?1|MAX\+1|超长|越界/.test(upper)) {
190
- return { intent: "max+1", field, bound, example };
192
+ return { intent: "max+1", field, bound, example, intentSource: "tp-fallback" };
193
+ }
194
+ if (/\bMAX\b|最大/.test(upper)) {
195
+ return { intent: "max", field, bound, example, intentSource: "tp-fallback" };
191
196
  }
192
- if (/\bMAX\b|最大/.test(upper))
193
- return { intent: "max", field, bound, example };
194
197
  if (/MIN[-_]?1|最小减|min-1/i.test(upper)) {
195
- return { intent: "min-1", field, bound, example };
198
+ return { intent: "min-1", field, bound, example, intentSource: "tp-fallback" };
199
+ }
200
+ if (/\bMIN\b|最小/.test(upper)) {
201
+ return { intent: "min", field, bound, example, intentSource: "tp-fallback" };
196
202
  }
197
- if (/\bMIN\b|最小/.test(upper))
198
- return { intent: "min", field, bound, example };
199
203
  if (/WRONG[-_]?TYPE|类型错误/.test(upper)) {
200
- return { intent: "wrong-type", field, bound, example };
204
+ return { intent: "wrong-type", field, bound, example, intentSource: "tp-fallback" };
201
205
  }
202
206
  if (/ENUM.*INVALID|INVALID.*ENUM|非法枚举/.test(upper)) {
203
- return { intent: "enum-invalid", field, bound, example };
207
+ return { intent: "enum-invalid", field, bound, example, intentSource: "tp-fallback" };
204
208
  }
205
209
  if (/PATTERN|FORMAT|UPPERCASE|大写|非法格式/.test(upper)) {
206
- return { intent: "pattern-invalid", field, bound, example };
210
+ return { intent: "pattern-invalid", field, bound, example, intentSource: "tp-fallback" };
207
211
  }
208
212
  if (/NOMINAL|正常|合法|主路径|SUCCESS/.test(upper)) {
209
- return { intent: "nominal", field, bound, example };
213
+ return { intent: "nominal", field, bound, example, intentSource: "tp-fallback" };
210
214
  }
211
- return { intent: "unknown", field, bound, example };
215
+ return { intent: "unknown", field, bound, example, intentSource: "tp-fallback" };
212
216
  }
213
217
  export function extractPytestParamBlock(source, tpId) {
214
218
  const idPattern = new RegExp(`id\\s*=\\s*["']${tpId.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}["']`);
@@ -316,7 +320,7 @@ export function observeParamFeatures(block, field) {
316
320
  }
317
321
  return { kind: "unknown", text: compact.slice(0, 160) };
318
322
  }
319
- function compareIntent(intent, observed, bound) {
323
+ function compareIntent(intent, observed, bound, example) {
320
324
  if (intent === "unknown")
321
325
  return "UNDETERMINED";
322
326
  if (observed.kind === "call" || observed.kind === "name") {
@@ -383,10 +387,9 @@ function compareIntent(intent, observed, bound) {
383
387
  ? "MISMATCH"
384
388
  : "UNDETERMINED";
385
389
  case "pattern-invalid":
386
- if (observed.kind === "string" && observed.hasUppercase)
387
- return "MATCH";
388
- if (observed.kind === "string")
389
- return "MISMATCH";
390
+ if (observed.kind === "string" && example !== undefined) {
391
+ return observed.literal === example ? "MATCH" : "MISMATCH";
392
+ }
390
393
  return "UNDETERMINED";
391
394
  case "wrong-type":
392
395
  return observed.kind === "number" ||
@@ -433,14 +436,9 @@ function repairabilityFor(status, intent, observed, example) {
433
436
  }
434
437
  if (intent === "unknown")
435
438
  return "blocked";
436
- if ((intent === "pattern-invalid" ||
437
- intent === "enum-invalid" ||
438
- intent === "wrong-type") &&
439
+ if ((intent === "enum-invalid" || intent === "wrong-type") &&
439
440
  !example &&
440
441
  !intent.startsWith("custom-literal:")) {
441
- // pattern-invalid with uppercase heuristic is still repairable to a fixed anti-example.
442
- if (intent === "pattern-invalid")
443
- return "repairable";
444
442
  return "blocked";
445
443
  }
446
444
  if (CLOSED_SET.has(intent) || intent.startsWith("custom-literal:")) {
@@ -475,8 +473,8 @@ function suggestedFixFor(intent, field, bound, example) {
475
473
  ? `set ${field}="x"*${Math.max(0, bound - 1)}`
476
474
  : undefined;
477
475
  case "pattern-invalid":
478
- return field
479
- ? `set ${field}=${JSON.stringify(example ?? "INVALID")}`
476
+ return field && example
477
+ ? `set ${field}=${JSON.stringify(example)}`
480
478
  : undefined;
481
479
  case "enum-invalid":
482
480
  case "wrong-type":
@@ -509,11 +507,12 @@ export async function assessBackendScenarioParamConsistency(input) {
509
507
  tpId,
510
508
  caseBody: testCase.body,
511
509
  });
510
+ const fallbackSourced = inferred.intentSource === "tp-fallback";
512
511
  const block = source ? extractPytestParamBlock(source, tpId) : undefined;
513
512
  const observed = observeParamFeatures(block, inferred.field);
514
- const status = !source
513
+ const status = !source || fallbackSourced
515
514
  ? "UNDETERMINED"
516
- : compareIntent(inferred.intent, observed, inferred.bound);
515
+ : compareIntent(inferred.intent, observed, inferred.bound, inferred.example);
517
516
  const repairability = repairabilityFor(status, inferred.intent, observed, inferred.example);
518
517
  entries.push({
519
518
  caseId: testCase.caseId,
@@ -523,7 +522,9 @@ export async function assessBackendScenarioParamConsistency(input) {
523
522
  observed: observed.text,
524
523
  status,
525
524
  repairability,
526
- suggestedFix: suggestedFixFor(inferred.intent, inferred.field, inferred.bound, inferred.example),
525
+ suggestedFix: fallbackSourced
526
+ ? undefined
527
+ : suggestedFixFor(inferred.intent, inferred.field, inferred.bound, inferred.example),
527
528
  scriptPath: testCase.scriptPath,
528
529
  bound: inferred.bound,
529
530
  example: inferred.example,
@@ -691,7 +692,14 @@ export function deterministicRewriteScenarioParam(input) {
691
692
  valueLiteral = `"x" * ${Math.max(0, bound - 1)}`;
692
693
  break;
693
694
  case "pattern-invalid":
694
- valueLiteral = JSON.stringify(entry.example ?? "INVALID");
695
+ if (entry.example === undefined) {
696
+ return {
697
+ ok: false,
698
+ source: input.source,
699
+ detail: "missing example for pattern-invalid",
700
+ };
701
+ }
702
+ valueLiteral = JSON.stringify(entry.example);
695
703
  break;
696
704
  default:
697
705
  if (entry.intent.startsWith("custom-literal:")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.33.3",
3
+ "version": "0.33.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",