@tea-agent/loop-agent 0.16.18 → 0.16.19
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
|
@@ -24,6 +24,12 @@
|
|
|
24
24
|
- Pi SDK 执行长推理或大段结构化输出时不再把高频流式增量事件无界累积到内存;同一响应在多个生命周期事件中重复出现的 Token 用量只统计一次,避免 `Invalid string length` 和成本数据虚高。
|
|
25
25
|
- 后端测试复合执行节点继续保持 clean environment、失败分类和 fail-closed outcome,并为 initial/final Result、repair eligibility、traceability 与 Observe 投影保留结构化运行证据。
|
|
26
26
|
|
|
27
|
+
## [0.16.19] - 2026-07-20
|
|
28
|
+
|
|
29
|
+
### 修复
|
|
30
|
+
|
|
31
|
+
- frontend-test result 物化在比较 evidence 路径前先 `realpath` 仓库与 evidence 根,避免 macOS `/var`→`/private/var` 导致的 `evidence realpath escapes its allowed root` 误拦。
|
|
32
|
+
|
|
27
33
|
## [0.16.18] - 2026-07-20
|
|
28
34
|
|
|
29
35
|
### 修复
|
|
@@ -75,27 +75,32 @@ async function readEvidenceFile(repoRoot, evidenceRoot, relative) {
|
|
|
75
75
|
if (!safeRelativePathSchema.safeParse(relative).success) {
|
|
76
76
|
throw new Error(`unsafe evidence path: ${relative}`);
|
|
77
77
|
}
|
|
78
|
+
// Resolve roots first so macOS /var vs /private/var does not look like an escape.
|
|
79
|
+
const repoRootResolved = await realpath(repoRoot);
|
|
80
|
+
const evidenceRootResolved = await realpath(evidenceRoot);
|
|
81
|
+
assertInside(repoRootResolved, evidenceRootResolved, "evidenceDir");
|
|
78
82
|
// Accept either evidence-root-relative paths or repo-relative paths under the case evidence root.
|
|
79
83
|
const normalized = relative.replaceAll("\\", "/");
|
|
80
|
-
const evidenceRootRel = path.relative(
|
|
84
|
+
const evidenceRootRel = path.relative(repoRootResolved, evidenceRootResolved).replaceAll("\\", "/");
|
|
81
85
|
let candidate = normalized;
|
|
82
86
|
if (normalized === evidenceRootRel || normalized.startsWith(`${evidenceRootRel}/`)) {
|
|
83
|
-
candidate = path.relative(
|
|
87
|
+
candidate = path.relative(evidenceRootResolved, path.resolve(repoRootResolved, normalized)).replaceAll("\\", "/");
|
|
84
88
|
}
|
|
85
89
|
else if (normalized.startsWith("testcase/frontend/evidence/")) {
|
|
86
|
-
candidate = path.relative(
|
|
90
|
+
candidate = path.relative(evidenceRootResolved, path.resolve(repoRootResolved, normalized)).replaceAll("\\", "/");
|
|
87
91
|
}
|
|
88
|
-
if (!candidate || candidate.startsWith("..")) {
|
|
92
|
+
if (!candidate || candidate.startsWith("..") || path.isAbsolute(candidate)) {
|
|
89
93
|
throw new Error(`evidence path escapes evidence root: ${relative}`);
|
|
90
94
|
}
|
|
91
|
-
const absolute = path.resolve(
|
|
92
|
-
assertInside(
|
|
95
|
+
const absolute = path.resolve(evidenceRootResolved, candidate);
|
|
96
|
+
assertInside(evidenceRootResolved, absolute, "evidence path");
|
|
93
97
|
const resolved = await realpath(absolute);
|
|
94
|
-
assertInside(
|
|
98
|
+
assertInside(repoRootResolved, resolved, "evidence realpath");
|
|
99
|
+
assertInside(evidenceRootResolved, resolved, "evidence realpath under case root");
|
|
95
100
|
const info = await lstat(resolved);
|
|
96
101
|
if (!info.isFile())
|
|
97
102
|
throw new Error(`evidence is not a file: ${relative}`);
|
|
98
|
-
const storedPath = path.relative(
|
|
103
|
+
const storedPath = path.relative(repoRootResolved, resolved).replaceAll("\\", "/");
|
|
99
104
|
return { path: storedPath, sha256: sha256(await readFile(resolved)) };
|
|
100
105
|
}
|
|
101
106
|
export async function materializeFrontendTestResult(input) {
|
|
@@ -129,8 +134,9 @@ export async function materializeFrontendTestResult(input) {
|
|
|
129
134
|
throw new Error(`unknown AC in manifest: ${acId}`);
|
|
130
135
|
covered.add(acId);
|
|
131
136
|
}
|
|
132
|
-
const
|
|
133
|
-
|
|
137
|
+
const workspaceRootResolved = await realpath(input.workspaceRoot);
|
|
138
|
+
const evidenceRoot = path.resolve(workspaceRootResolved, item.evidenceDir);
|
|
139
|
+
assertInside(workspaceRootResolved, evidenceRoot, "evidenceDir");
|
|
134
140
|
const resultRaw = JSON.parse(await readFile(path.join(evidenceRoot, "case-result.json"), "utf-8"));
|
|
135
141
|
const status = caseStatusSchema.parse(resultRaw.status);
|
|
136
142
|
if (resultRaw.caseId !== item.caseId || !Array.isArray(resultRaw.evidencePaths))
|