@tea-agent/loop-agent 0.26.5-beta.1 → 0.26.5-beta.2

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.
@@ -398,9 +398,23 @@ export function extractFrontendImplementationJson(text) {
398
398
  if (trimmed.startsWith("{") && trimmed.endsWith("}"))
399
399
  return parse(trimmed);
400
400
  const blocks = [...trimmed.matchAll(/```json\s*\n([\s\S]*?)\n```/gi)];
401
- if (blocks.length !== 1)
401
+ if (blocks.length === 0)
402
402
  throw new Error("output must contain exactly one fenced json object");
403
- return parse(blocks[0][1]);
403
+ const candidates = [];
404
+ for (const block of blocks) {
405
+ try {
406
+ const candidate = parse(block[1]);
407
+ if (candidate && typeof candidate === "object" && !Array.isArray(candidate))
408
+ candidates.push(candidate);
409
+ }
410
+ catch {
411
+ // Ignore incomplete model scratch blocks. A later complete contract
412
+ // block may still be deterministically recoverable.
413
+ }
414
+ }
415
+ if (candidates.length !== 1)
416
+ throw new Error(`output must contain exactly one valid fenced json object (found ${candidates.length})`);
417
+ return candidates[0];
404
418
  }
405
419
  /**
406
420
  * Build the authoritative frontend-implementation-contract sourceBinding from
@@ -1,7 +1,8 @@
1
1
  import { readFile, stat } from "node:fs/promises";
2
+ import { createHash } from "node:crypto";
2
3
  import path from "node:path";
3
4
  import { isOpenspecSpecFilePath } from "../../shared/openspec-spec.js";
4
- import { frontendImplementationContractSchema, materializeFrontendImplementationContract, assertFrontendSourceBindingFresh, } from "./frontend-implementation-contract.js";
5
+ import { frontendImplementationContractSchema, FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID, materializeFrontendImplementationContract, assertFrontendSourceBindingFresh, } from "./frontend-implementation-contract.js";
5
6
  import { captureFrontendWorktreeBaseline } from "./frontend-worktree-diff.js";
6
7
  import { pathMatchesPattern } from "../../shared/git-progress.js";
7
8
  async function selectNode(runDir, primary, fallbacks) {
@@ -160,13 +161,20 @@ export async function runFrontendPrewriteGate(input) {
160
161
  if (missingIds.length > 0) {
161
162
  throw new Error(`frontend prewrite gate missing requirement ids: ${missingIds.join(", ")}`);
162
163
  }
163
- const artifact = await materializeFrontendImplementationContract({
164
+ const artifactPath = path.join(input.runDir, input.config.outputDir, input.config.artifactName);
165
+ const artifact = await stat(artifactPath)
166
+ .then(async () => ({
167
+ path: artifactPath,
168
+ schemaId: FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID,
169
+ sha256: createHash("sha256").update(await readFile(artifactPath)).digest("hex"),
170
+ }))
171
+ .catch(() => materializeFrontendImplementationContract({
164
172
  runDir: input.runDir,
165
173
  fromNodeId: planNodeId,
166
174
  artifactName: input.config.artifactName,
167
175
  outputDir: input.config.outputDir,
168
176
  sourceBinding: input.sourceBinding,
169
- });
177
+ }));
170
178
  const raw = JSON.parse(await readFile(artifact.path, "utf8"));
171
179
  const contract = frontendImplementationContractSchema.parse(raw);
172
180
  if (!input.config.allowedMockStrategies.includes(contract.mockApi.strategy)) {
@@ -2483,10 +2483,34 @@ async function buildFrontendHybridDagFromTask(sources) {
2483
2483
  sourceContext,
2484
2484
  ].join("\n\n"),
2485
2485
  },
2486
+ {
2487
+ id: "frontend-contract-json-validate-shell",
2488
+ depends_on: ["frontend-contract-json-pi"],
2489
+ role: "verifier",
2490
+ executor: "shell",
2491
+ complexity: "LOW",
2492
+ writePolicy: "read-only",
2493
+ allowedPaths: readOnlyPaths,
2494
+ forbiddenPaths,
2495
+ outputContract: "Validated frontend implementation contract artifact with schema ID and SHA-256.",
2496
+ subtask_prompt: "Materialize and validate the structured frontend contract before prewrite authorization.",
2497
+ shell: {
2498
+ commands: [],
2499
+ jsonArtifactGate: {
2500
+ fromNodeId: "frontend-contract-json-pi",
2501
+ schemaId: "frontend-implementation-contract-v1",
2502
+ artifactName: "frontend-implementation-contract.json",
2503
+ outputDir: "contracts",
2504
+ },
2505
+ cwd: ".",
2506
+ timeoutMs: 60000,
2507
+ },
2508
+ },
2486
2509
  {
2487
2510
  id: "frontend-prewrite-gate-shell",
2488
2511
  depends_on: [
2489
2512
  "frontend-contract-json-pi",
2513
+ "frontend-contract-json-validate-shell",
2490
2514
  "frontend-final-design-review-pi",
2491
2515
  "frontend-design-review-pi",
2492
2516
  "frontend-plan-revision-pi",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.26.5-beta.1",
3
+ "version": "0.26.5-beta.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",