@tea-agent/loop-agent 0.16.11 → 0.16.13
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
|
@@ -18,6 +18,18 @@
|
|
|
18
18
|
- Pi SDK 执行长推理或大段结构化输出时不再把高频流式增量事件无界累积到内存;同一响应在多个生命周期事件中重复出现的 Token 用量只统计一次,避免 `Invalid string length` 和成本数据虚高。
|
|
19
19
|
- 后端测试复合执行节点继续保持 clean environment、失败分类和 fail-closed outcome,并为 initial/final Result、repair eligibility、traceability 与 Observe 投影保留结构化运行证据。
|
|
20
20
|
|
|
21
|
+
## [0.16.13] - 2026-07-20
|
|
22
|
+
|
|
23
|
+
### 修复
|
|
24
|
+
|
|
25
|
+
- backend-test 的 `finalize-effective-result` 管道在节点完成后绑定 `contracts/backend-test-result.json` 为 structured artifact,使 Outcome 投影出 `backend-test-result`,解除 FE-TEST Ready Planner 对 BE-TEST 产物门的误阻断。
|
|
26
|
+
|
|
27
|
+
## [0.16.12] - 2026-07-20
|
|
28
|
+
|
|
29
|
+
### 修复
|
|
30
|
+
|
|
31
|
+
- `backend-test` materialize / Git checkpoint 自动并入 `testcase/**`(与 `docs/test-reports/**`)运行时写根,避免 TaskSpec 仍写 `tests/api/**` 时绿跑无法 promote、任务卡在 Running。
|
|
32
|
+
|
|
21
33
|
## [0.16.11] - 2026-07-20
|
|
22
34
|
|
|
23
35
|
### 修复
|
|
@@ -6,6 +6,8 @@ import path from "node:path";
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { getTaskPoolRoot } from "../pool/run-store.js";
|
|
8
8
|
import { assertSafeRuntimeId } from "../follow-up/paths.js";
|
|
9
|
+
import { expandAllowedPathsForWorkflow } from "../materialize/harness-task-materializer.js";
|
|
10
|
+
import { resolveWorkflow } from "../task-spec/workflow-routing.js";
|
|
9
11
|
const execFileAsync = promisify(execFile);
|
|
10
12
|
const checkpointSchema = z.object({
|
|
11
13
|
taskId: z.string().min(1),
|
|
@@ -275,13 +277,14 @@ function auditChangedPaths(changes, taskSpec) {
|
|
|
275
277
|
throw new Error(audit.violations[0]);
|
|
276
278
|
}
|
|
277
279
|
function auditChangedPathsReport(changes, taskSpec) {
|
|
280
|
+
const allowedPaths = expandAllowedPathsForWorkflow(resolveWorkflow(taskSpec).workflow, taskSpec.constraints.allowed_paths);
|
|
278
281
|
const violations = [];
|
|
279
282
|
for (const changed of changes) {
|
|
280
283
|
if (/(^|\/)(\.env(?:\.|$)|[^/]*\.(?:pem|key|p12|pfx))$/i.test(changed))
|
|
281
284
|
violations.push(`changed path may contain sensitive material: ${changed}`);
|
|
282
285
|
else if (taskSpec.constraints.forbidden_paths.some((glob) => matchesGlob(changed, glob)))
|
|
283
286
|
violations.push(`changed path is forbidden for ${taskSpec.id}: ${changed}`);
|
|
284
|
-
else if (!
|
|
287
|
+
else if (!allowedPaths.some((glob) => matchesGlob(changed, glob)))
|
|
285
288
|
violations.push(`changed path is outside allowed_paths for ${taskSpec.id}: ${changed}`);
|
|
286
289
|
}
|
|
287
290
|
return { ok: violations.length === 0, violations };
|
|
@@ -34,13 +34,17 @@ export async function materializeTaskSpec(options) {
|
|
|
34
34
|
}
|
|
35
35
|
const paths = getTaskPaths(options.repoRoot, harnessTaskId);
|
|
36
36
|
const baseConfig = await loadTaskConfig(options.repoRoot, harnessTaskId);
|
|
37
|
+
// backend-test hybrid DAG writes under testcase/** (pytest + md cases + helpers).
|
|
38
|
+
// TaskSpecs often list legacy tests/api/** surfaces; union runtime write roots so
|
|
39
|
+
// git checkpoint / path guard can promote green BE-TEST runs.
|
|
40
|
+
const allowedPaths = expandAllowedPathsForWorkflow(resolvedWorkflow.workflow, options.taskSpec.constraints.allowed_paths);
|
|
37
41
|
const taskConfig = {
|
|
38
42
|
...baseConfig,
|
|
39
43
|
taskId: harnessTaskId,
|
|
40
44
|
title: options.taskSpec.title,
|
|
41
45
|
taskKind: resolvedWorkflow.taskKind,
|
|
42
46
|
featureId: options.taskSpec.feature_id,
|
|
43
|
-
allowedPaths
|
|
47
|
+
allowedPaths,
|
|
44
48
|
forbiddenPaths: options.taskSpec.constraints.forbidden_paths,
|
|
45
49
|
hardConstraints: options.taskSpec.constraints.hard_constraints,
|
|
46
50
|
complexity: mapRiskLevelToComplexity(options.taskSpec.risk_level),
|
|
@@ -312,6 +316,13 @@ function bulletLines(values) {
|
|
|
312
316
|
return ["- None"];
|
|
313
317
|
return values.map((value) => `- ${value}`);
|
|
314
318
|
}
|
|
319
|
+
/** Union TaskSpec allowed_paths with workflow-owned write roots. */
|
|
320
|
+
export function expandAllowedPathsForWorkflow(workflow, allowedPaths) {
|
|
321
|
+
const extras = workflow === "backend-test"
|
|
322
|
+
? ["testcase/**", "docs/test-reports/**"]
|
|
323
|
+
: [];
|
|
324
|
+
return Array.from(new Set([...allowedPaths, ...extras]));
|
|
325
|
+
}
|
|
315
326
|
async function loadExistingTaskConfig(repoRoot, taskId) {
|
|
316
327
|
try {
|
|
317
328
|
return await loadTaskConfig(repoRoot, taskId);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import path from "node:path";
|
|
4
5
|
import { recordDecisionEnvelopeForNode, shouldPauseOnHumanEscalation, writeHumanEscalationArtifacts, } from "./decision-envelope.js";
|
|
@@ -421,6 +422,19 @@ export async function executeDagNode(input) {
|
|
|
421
422
|
node.structuredArtifactSha256 = createHash("sha256").update(bytes).digest("hex");
|
|
422
423
|
node.structuredArtifactSchemaId = task.shell.jsonArtifactGate.schemaId;
|
|
423
424
|
}
|
|
425
|
+
else if (task.shell?.backendTestPipeline === "finalize-effective-result") {
|
|
426
|
+
// Pipeline materializes contracts/backend-test-result.json without jsonArtifactGate.
|
|
427
|
+
// Bind it so Outcome adapters project kind=backend-test-result for Ready Planner.
|
|
428
|
+
const artifactPath = path.join(runDir, "contracts", "backend-test-result.json");
|
|
429
|
+
if (existsSync(artifactPath)) {
|
|
430
|
+
const bytes = await readFile(artifactPath);
|
|
431
|
+
node.structuredArtifactPath = artifactPath;
|
|
432
|
+
node.structuredArtifactSha256 = createHash("sha256")
|
|
433
|
+
.update(bytes)
|
|
434
|
+
.digest("hex");
|
|
435
|
+
node.structuredArtifactSchemaId = "backend-test-result-v1";
|
|
436
|
+
}
|
|
437
|
+
}
|
|
424
438
|
}
|
|
425
439
|
state.nodes[nodeId].nodeRecordPath = path.join(runDir, `${nodeId}.json`);
|
|
426
440
|
await writeNodeRecord(runDir, nodeId, state.nodes[nodeId]);
|