@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" &&
|
|
387
|
-
return "MATCH";
|
|
388
|
-
|
|
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 === "
|
|
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
|
|
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:
|
|
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
|
-
|
|
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:")) {
|