@tea-agent/loop-agent 0.39.0-next.10 → 0.39.0-next.12

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.
Files changed (83) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/build-stamp.json +2 -2
  3. package/dist/worker/console/chat/mcp-inventory.js +221 -0
  4. package/dist/worker/console/chat/pi-runtime.js +144 -69
  5. package/dist/worker/console/chat/repo-walk.js +10 -1
  6. package/dist/worker/console/chat/routes.js +19 -8
  7. package/dist/worker/console/chat/turn-process.js +10 -10
  8. package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-B1E-cK2K.js → abnfDiagram-N423BO3Z-CRpBFiPS.js} +1 -1
  9. package/dist/worker/console/static/assets/{arc-BGzjO9rd.js → arc-CFq00qel.js} +1 -1
  10. package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DLdhmjAc.js → architectureDiagram-T3A2C74G-Dk_urLIc.js} +1 -1
  11. package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-MYV8D865.js → blockDiagram-VBNYF7ZC-CLM5ocwZ.js} +1 -1
  12. package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BzDorEwd.js → c4Diagram-5PPSVZJV-CWYcPCa3.js} +1 -1
  13. package/dist/worker/console/static/assets/channel-Dpjb6BMm.js +1 -0
  14. package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-C1X8DD9m.js → chunk-2GRJ4B5K-XILOozrE.js} +1 -1
  15. package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-_b3zpUSR.js → chunk-2Q5K7J3B-M4A5lfjn.js} +1 -1
  16. package/dist/worker/console/static/assets/{chunk-5RXB4S5H-B1nq6qNo.js → chunk-5RXB4S5H-CHq6wHgP.js} +1 -1
  17. package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BHy2jQUr.js → chunk-5VM5RSS4-COevfhfp.js} +1 -1
  18. package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-BDoLBPGY.js → chunk-6Q2QTUOP-CN1D-iO4.js} +1 -1
  19. package/dist/worker/console/static/assets/{chunk-GF5L2VYU-SDhf5BHa.js → chunk-GF5L2VYU-C6qAAYuy.js} +1 -1
  20. package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B2X9WLQ5.js → chunk-JWPE2WC7-DzjF9LjT.js} +1 -1
  21. package/dist/worker/console/static/assets/{chunk-KBJHAD2P-Y1pEsL6r.js → chunk-KBJHAD2P-B4vicTaj.js} +1 -1
  22. package/dist/worker/console/static/assets/{chunk-RYQCIY6F-BhlwtGnw.js → chunk-RYQCIY6F-D5nkzM8K.js} +1 -1
  23. package/dist/worker/console/static/assets/{chunk-XXDRQBXY-lFslfIU9.js → chunk-XXDRQBXY-CUgkbah9.js} +1 -1
  24. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-Cv4CdaZH.js +1 -0
  25. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-Cv4CdaZH.js +1 -0
  26. package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-ms_QSyzO.js → cose-bilkent-JH36ORCC-faupYrw-.js} +1 -1
  27. package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DVBoJPNo.js → cynefin-VYW2F7L2-Bev9OTKA.js} +1 -1
  28. package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-ClesFZ8l.js → cynefinDiagram-MW4NZA55-A0iHnoJm.js} +1 -1
  29. package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CwcFFdXS.js → dagre-VZM6K2ZE-Bboc-8dL.js} +1 -1
  30. package/dist/worker/console/static/assets/{diagram-7IWD3JNH-CK6fAQka.js → diagram-7IWD3JNH-CZcV9H_a.js} +1 -1
  31. package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CxBy8J_n.js → diagram-B4RE2ZJO-I1kV6LGQ.js} +1 -1
  32. package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Cn6E66dO.js → diagram-LBJQPF4R-BaSON7dV.js} +1 -1
  33. package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cep_FINW.js → diagram-Q27KOJAE-KUeIAdtX.js} +1 -1
  34. package/dist/worker/console/static/assets/{diagram-UB23O5K3-gzv9ZY8Y.js → diagram-UB23O5K3-DylPuZu-.js} +1 -1
  35. package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CZVlOlaj.js → ebnfDiagram-BXEA7PRR-DcWoMdm7.js} +1 -1
  36. package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-EGJvKAH5.js → erDiagram-JOGREHBK-BxJOdF_H.js} +1 -1
  37. package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DhfPgO79.js → flowDiagram-UKHOOZJN-DHxQgLL0.js} +1 -1
  38. package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BzJfLMVN.js → ganttDiagram-PKOTCBZU-CVz9xzIE.js} +1 -1
  39. package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-C5uHg3cm.js → gitGraphDiagram-DS77QQ5N-D4C2Yvbd.js} +1 -1
  40. package/dist/worker/console/static/assets/index-BA5-N5ER.css +1 -0
  41. package/dist/worker/console/static/assets/index-amy_wpJL.js +325 -0
  42. package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BFEs1rSc.js → infoDiagram-6WML65LV-Dr3ejwyd.js} +1 -1
  43. package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-DOY3aNcc.js → ishikawaDiagram-WSZJBQD7-2kJbNO0g.js} +1 -1
  44. package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CvEVBETJ.js → journeyDiagram-NVQOT4AX-CMfF4VjJ.js} +1 -1
  45. package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CvRuqo46.js → kanban-definition-27J2QSJJ-DNN-Q1z3.js} +1 -1
  46. package/dist/worker/console/static/assets/{linear-B8zO1HYx.js → linear-OB4Y8pOn.js} +1 -1
  47. package/dist/worker/console/static/assets/{mermaid.core-FLK4IOPu.js → mermaid.core-DFSjahzL.js} +5 -5
  48. package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-CwQ-RWrj.js → mindmap-definition-FAOFIHXS-D1WNklJu.js} +1 -1
  49. package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-H_S6wWna.js → pegDiagram-VL7TDLO6-Be2wdQXD.js} +1 -1
  50. package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-CZUOcDQQ.js → pieDiagram-7S7Q4E2Y-hhWe5f6C.js} +1 -1
  51. package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CeaPaSv9.js → quadrantDiagram-CIZ2JOQS-BT597AoO.js} +1 -1
  52. package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Cpcs9ua-.js → railroadDiagram-AXF67PYL-BjqVRkNc.js} +1 -1
  53. package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-D0_Aeyk6.js → requirementDiagram-LRYGKXZP-D964n5d3.js} +1 -1
  54. package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-73kWjUq_.js → sankeyDiagram-W5VNT64P-_aPpr_hL.js} +1 -1
  55. package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-41K29Ot4.js → sequenceDiagram-SI44F4Z6-BY0YUKL1.js} +1 -1
  56. package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-D1nO0oh9.js → sizeCapture-X5ZJPWSS-CgpnvQ-_.js} +1 -1
  57. package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-C77XHgDB.js → stateDiagram-OKZ733FA-BfWR9Od2.js} +1 -1
  58. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI--1Trd-FO.js +1 -0
  59. package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DC0D7jEO.js → swimlanes-SLNWSIFB-CWDqlFt_.js} +2 -2
  60. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BepA9ids.js +8 -0
  61. package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BLDHd_Uf.js → timeline-definition-Z64GVDOM-DjURY-Nw.js} +1 -1
  62. package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-eh6Y_q96.js → vennDiagram-T6HMQDX7-D1nP5tpF.js} +1 -1
  63. package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-Cj4Cy9tr.js → wardleyDiagram-T6FBY63Y-BfRbqp6h.js} +1 -1
  64. package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-DUIjXY1m.js → xychartDiagram-ELKLHX3M-D8FzmnEz.js} +1 -1
  65. package/dist/worker/console/static/index.html +2 -2
  66. package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +36 -25
  67. package/dist/worker/console/static-src/operator-chat/resource-diagnostics.js +204 -0
  68. package/dist/worker/console/static-src/operator-chat/useChatStream.js +17 -4
  69. package/dist/worker/console/static-src/operator-chat/useChatThread.js +28 -22
  70. package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +26 -1
  71. package/dist/workflows/dag/contract-validator-registrations.js +2 -1
  72. package/dist/workflows/dag/frontend-implementation-contract.js +40 -0
  73. package/dist/workflows/dag/init-hybrid.js +1 -1
  74. package/dist/workflows/dag/node-execution.js +15 -3
  75. package/dist/workflows/dag/types.js +1 -0
  76. package/package.json +1 -1
  77. package/dist/worker/console/static/assets/channel-jVvdIfCV.js +0 -1
  78. package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-VqdMg0Mg.js +0 -1
  79. package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-VqdMg0Mg.js +0 -1
  80. package/dist/worker/console/static/assets/index-0DWC8Vk9.js +0 -325
  81. package/dist/worker/console/static/assets/index-DAouGklf.css +0 -1
  82. package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B9GimbZV.js +0 -1
  83. package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-yxfxvdqm.js +0 -8
@@ -242,9 +242,14 @@ export function useChatStream(params) {
242
242
  /** Issue real POST /abort for a known turnId (Stop never disposes Session). */
243
243
  const postAbortForTurn = useCallback(async (sessionId, generation, turnId) => {
244
244
  refs.stoppingTurnIdRef.current = turnId;
245
- // Keep streaming/busy while abort is in flight (UIS-stopping).
246
- refs.streamingRef.current = true;
247
- setStreaming(true);
245
+ // Optimistic stop (pi-web parity): the abort signal kills the SDK
246
+ // process tree promptly, so flip the UI idle NOW instead of holding
247
+ // the busy spinner through the whole POST /abort round-trip. If the
248
+ // server later reports the turn still running (TURN_ABORT_PENDING
249
+ // or owner-lost reconcile), applyAbortDecision restores busy state.
250
+ refs.streamingRef.current = false;
251
+ setStreaming(false);
252
+ setStreamingAssistantId(null);
248
253
  try {
249
254
  const res = await fetch(operatorChatAbortUrl(origin, sessionId, turnId), {
250
255
  method: "POST",
@@ -285,6 +290,10 @@ export function useChatStream(params) {
285
290
  markIdle: () => markIdleIfCurrent(sessionId, generation, refs.assistantIdRef.current),
286
291
  });
287
292
  applyOwnershipBagToRefs(refs, bag);
293
+ // Sync the post-decision busy state back to React: optimistic stop
294
+ // flipped it idle up front; pending/keep-busy outcomes restore it.
295
+ if (bag.streaming)
296
+ setStreaming(true);
288
297
  }
289
298
  catch {
290
299
  // Abort network failure: keep busy (UIS-abort-network-fail).
@@ -297,6 +306,10 @@ export function useChatStream(params) {
297
306
  networkError: true,
298
307
  }), noticeSink());
299
308
  applyOwnershipBagToRefs(refs, bag);
309
+ // Network failure: the optimistic idle was premature — restore busy
310
+ // so the turn is not silently dropped; /state reconcile converges.
311
+ refs.streamingRef.current = true;
312
+ setStreaming(true);
300
313
  }
301
314
  }, [
302
315
  origin,
@@ -371,7 +384,7 @@ export function useChatStream(params) {
371
384
  refs.streamingRef.current ||
372
385
  refs.submitInFlightRef.current ||
373
386
  // AC-R5-002: ownership lease blocks send until terminal release.
374
- Boolean(refs.pendingSubmissionRef.current))
387
+ refs.pendingSubmissionRef.current)
375
388
  return;
376
389
  // AC-001: acquire synchronous submit ownership before the first await.
377
390
  refs.submitInFlightRef.current = true;
@@ -1,6 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useState } from "react";
2
- import { parseAssistantContent } from "../../chat/assistant-content.js";
3
- import { buildTurnProcessGroups } from "../../chat/turn-process.js";
2
+ import { buildTurnProcessGroups, hydrateSessionThread, selectRecentTurnGroups, } from "../../chat/turn-process.js";
4
3
  import { createChatSseEventApplier, } from "./chat-sse-events.js";
5
4
  import { taskContextUnchanged } from "./format.js";
6
5
  const EMPTY_USAGE = {
@@ -121,9 +120,7 @@ export function useChatThread(params) {
121
120
  }, 350);
122
121
  return () => window.clearTimeout(timer);
123
122
  }, [sessionId, operationCards, humanGateCards, interview, loadTaskContext]);
124
- const visibleMessages = useMemo(() => messages
125
- .filter((m) => m.role !== "system" || showSystemInMessages)
126
- .slice(-historyLimit), [messages, showSystemInMessages, historyLimit]);
123
+ const timelineMessages = useMemo(() => messages.filter((m) => m.role !== "system" || showSystemInMessages), [messages, showSystemInMessages]);
127
124
  useEffect(() => {
128
125
  const onVisibility = () => {
129
126
  if (!document.hidden)
@@ -158,12 +155,30 @@ export function useChatThread(params) {
158
155
  statusKind,
159
156
  };
160
157
  }, [toolCalls]);
161
- const turnGroups = useMemo(() => buildTurnProcessGroups({
162
- messages: visibleMessages,
158
+ const turnGroups = useMemo(() => {
159
+ const all = buildTurnProcessGroups({
160
+ messages: timelineMessages,
161
+ toolCalls,
162
+ streaming,
163
+ activeAssistantId: streamingAssistantId,
164
+ });
165
+ return selectRecentTurnGroups(all, historyLimit);
166
+ }, [
167
+ timelineMessages,
163
168
  toolCalls,
164
169
  streaming,
165
- activeAssistantId: streamingAssistantId,
166
- }), [visibleMessages, toolCalls, streaming, streamingAssistantId]);
170
+ streamingAssistantId,
171
+ historyLimit,
172
+ ]);
173
+ const visibleMessages = useMemo(() => {
174
+ const next = [];
175
+ for (const group of turnGroups) {
176
+ if (group.user)
177
+ next.push(group.user);
178
+ next.push(...group.assistants);
179
+ }
180
+ return next;
181
+ }, [turnGroups]);
167
182
  /** Reset all session-scoped projections before adopting a fresh session. */
168
183
  const resetForNewSession = useCallback(() => {
169
184
  setMessages([]);
@@ -220,18 +235,9 @@ export function useChatThread(params) {
220
235
  refs.seenEventIdsRef.current = new Set();
221
236
  refs.turnAssistantIdsRef.current = new Map();
222
237
  refs.sealedAssistantIdsRef.current = new Set();
223
- setMessages((body.session.messages ?? [])
224
- .filter((message) => message.role === "user" || message.role === "assistant")
225
- .map((message) => {
226
- const content = parseAssistantContent(message.content);
227
- return {
228
- id: message.id,
229
- role: message.role,
230
- text: message.text,
231
- createdAt: Date.parse(message.at) || Date.now(),
232
- ...(content && content.length > 0 ? { content } : {}),
233
- };
234
- }));
238
+ const hydrated = hydrateSessionThread(body.session.messages ?? []);
239
+ setMessages(hydrated.messages);
240
+ setToolCalls(hydrated.toolCalls);
235
241
  setStreamingAssistantId(null);
236
242
  return true;
237
243
  }
@@ -240,7 +246,7 @@ export function useChatThread(params) {
240
246
  // trigger another reconcile and retry (best-effort recovery).
241
247
  return false;
242
248
  }
243
- }, [origin, refs, setMessages, setStreamingAssistantId]);
249
+ }, [origin, refs, setMessages, setToolCalls, setStreamingAssistantId]);
244
250
  return {
245
251
  messages,
246
252
  setMessages,
@@ -1,4 +1,4 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { ACTIVE_CHAT_SESSION_STORAGE_KEY } from "../../chat/workspace-landing.js";
3
3
  /** Left-rail repo tree + file preview tabs state. */
4
4
  export function useRepoBrowser(params) {
@@ -9,6 +9,8 @@ export function useRepoBrowser(params) {
9
9
  const [previews, setPreviews] = useState([]);
10
10
  const [activePreviewPath, setActivePreviewPath] = useState(null);
11
11
  const [previewLoading, setPreviewLoading] = useState(false);
12
+ const expandedDirsRef = useRef(new Set());
13
+ expandedDirsRef.current = expandedDirs;
12
14
  const loadTree = useCallback(async (directory = "") => {
13
15
  try {
14
16
  setTreeError(null);
@@ -28,6 +30,28 @@ export function useRepoBrowser(params) {
28
30
  useEffect(() => {
29
31
  void loadTree();
30
32
  }, [loadTree]);
33
+ /** pi-web parity: re-fetch the root + every expanded directory. Bump
34
+ * `refreshTick` to trigger; cached entries are NOT reused, so newly
35
+ * created/deleted files appear without a manual refresh. */
36
+ const [refreshTick, setRefreshTick] = useState(0);
37
+ const refreshTree = useCallback(() => {
38
+ setRefreshTick((tick) => tick + 1);
39
+ }, []);
40
+ useEffect(() => {
41
+ const dirs = ["", ...expandedDirsRef.current];
42
+ for (const dir of dirs)
43
+ void loadTree(dir);
44
+ }, [loadTree, refreshTick]);
45
+ // Also refresh when the tab regains focus: external tools (editor, git,
46
+ // CLI) may have changed the tree while the console was hidden.
47
+ useEffect(() => {
48
+ const onVisibility = () => {
49
+ if (!document.hidden)
50
+ refreshTree();
51
+ };
52
+ document.addEventListener("visibilitychange", onVisibility);
53
+ return () => document.removeEventListener("visibilitychange", onVisibility);
54
+ }, [refreshTree]);
31
55
  const openPreview = useCallback(async (file) => {
32
56
  setPreviewLoading(true);
33
57
  try {
@@ -116,6 +140,7 @@ export function useRepoBrowser(params) {
116
140
  previewLoading,
117
141
  setPreviews,
118
142
  loadTree,
143
+ refreshTree,
119
144
  openPreview,
120
145
  closePreview,
121
146
  toggleDir,
@@ -3,6 +3,7 @@
3
3
  * its schemaId in the contract output registry. Imported once by the node
4
4
  * executor; keeps per-contract imports out of the executor.
5
5
  */
6
- import { validateFrontendContractNodeOutput } from "./frontend-implementation-contract.js";
6
+ import { validateFrontendContractNodeOutput, validateFrontendRevisionPatchNodeOutput, } from "./frontend-implementation-contract.js";
7
7
  import { registerStructuredContractValidator } from "./contract-output-registry.js";
8
8
  registerStructuredContractValidator("frontend-implementation-contract-v1", validateFrontendContractNodeOutput);
9
+ registerStructuredContractValidator("frontend-implementation-contract-revision-patch-v1", validateFrontendRevisionPatchNodeOutput);
@@ -1632,6 +1632,46 @@ export async function validateFrontendContractNodeOutput(input) {
1632
1632
  };
1633
1633
  }
1634
1634
  }
1635
+ /**
1636
+ * Node-output self-check for the plan revision node, which emits an RFC 7386
1637
+ * merge-patch delta against the original contract instead of a full contract.
1638
+ * Deliberately loose and format-level: the output must contain at least one
1639
+ * ```openspec-citations fenced block and must extract (after the citations
1640
+ * blocks are stripped) to exactly one JSON object. The delta carries no
1641
+ * schemaVersion/targets, so no full-contract schema/semantic checks run here;
1642
+ * content parsing, patch application, and merged-contract validation stay with
1643
+ * the prewrite gate, which remains the only authority.
1644
+ */
1645
+ export async function validateFrontendRevisionPatchNodeOutput(input) {
1646
+ // Mirror the prewrite gate's block-presence semantics (a fenced
1647
+ // ```openspec-citations block with a newline-terminated body).
1648
+ if (!/```openspec-citations[ \t]*\r?\n[\s\S]*?\r?\n```/.test(input.text)) {
1649
+ return {
1650
+ ok: false,
1651
+ reason: "revision patch output must include at least one ```openspec-citations fenced block",
1652
+ };
1653
+ }
1654
+ // Strip the citation blocks before extraction: their per-row JSON objects
1655
+ // would otherwise count as competing balanced objects in the extractor.
1656
+ const textWithoutCitations = input.text.replace(/```openspec-citations[ \t]*\r?\n[\s\S]*?\r?\n```/g, "");
1657
+ let patch;
1658
+ try {
1659
+ patch = extractFrontendImplementationJson(textWithoutCitations);
1660
+ }
1661
+ catch (error) {
1662
+ return {
1663
+ ok: false,
1664
+ reason: `revision patch output must contain exactly one valid JSON object: ${error instanceof Error ? error.message : String(error)}`,
1665
+ };
1666
+ }
1667
+ if (!patch || typeof patch !== "object" || Array.isArray(patch)) {
1668
+ return {
1669
+ ok: false,
1670
+ reason: "revision patch output must extract to exactly one JSON object (RFC 7386 merge-patch delta)",
1671
+ };
1672
+ }
1673
+ return { ok: true, contract: patch };
1674
+ }
1635
1675
  export async function materializeFrontendImplementationContract(input) {
1636
1676
  const analysis = await analyzeFrontendImplementationContract(input);
1637
1677
  return writeFrontendImplementationContractArtifact({
@@ -2859,7 +2859,7 @@ async function buildFrontendHybridDagFromTask(sources) {
2859
2859
  outputMode: "structured-required",
2860
2860
  retryPolicy: STRUCTURED_REQUIRED_PI_RETRY_POLICY,
2861
2861
  structuredContractOutput: {
2862
- schemaId: "frontend-implementation-contract-v1",
2862
+ schemaId: "frontend-implementation-contract-revision-patch-v1",
2863
2863
  retryOnInvalid: true,
2864
2864
  },
2865
2865
  allowedPaths: readOnlyPaths,
@@ -144,15 +144,27 @@ function buildAttemptPrompt(task, basePrompt, attemptNumber, previousFailureCate
144
144
  if (previousFailureCategory === "invalid-output" &&
145
145
  task.structuredContractOutput &&
146
146
  previousProtocolReason) {
147
+ // Revision nodes emit an RFC 7386 merge-patch delta (no schemaVersion /
148
+ // targets), so the retry instruction must not push them toward a full
149
+ // contract rewrite; plan nodes keep the strict v1 guidance.
150
+ const guidance = task.structuredContractOutput.schemaId ===
151
+ "frontend-implementation-contract-revision-patch-v1"
152
+ ? [
153
+ "Return exactly one fenced json object containing the RFC 7386 merge-patch delta against the original contract (null deletes a key; arrays and scalars replace; plain objects merge recursively) + exactly one openspec-citations block.",
154
+ "The delta contains only the fields you change; do not emit a full contract or a schemaVersion/targets section.",
155
+ ]
156
+ : [
157
+ "Return exactly one fenced json block conforming to the frontend-implementation-contract-v1 schema.",
158
+ "Fix every reported field violation: do not emit null for optional fields, do not misspell field names, and match the required types exactly.",
159
+ "The Markdown explanation may be omitted; prioritize a complete contract.",
160
+ ];
147
161
  return [
148
162
  basePrompt,
149
163
  "",
150
164
  "<retry_instruction>",
151
165
  "Previous attempt produced an invalid frontend implementation contract:",
152
166
  previousProtocolReason,
153
- "Return exactly one fenced json block conforming to the frontend-implementation-contract-v1 schema.",
154
- "Fix every reported field violation: do not emit null for optional fields, do not misspell field names, and match the required types exactly.",
155
- "The Markdown explanation may be omitted; prioritize a complete contract.",
167
+ ...guidance,
156
168
  "</retry_instruction>",
157
169
  ].join("\n");
158
170
  }
@@ -704,6 +704,7 @@ export const dagConvergenceSpecSchema = z
704
704
  */
705
705
  export const structuredContractOutputSchemaIds = [
706
706
  "frontend-implementation-contract-v1",
707
+ "frontend-implementation-contract-revision-patch-v1",
707
708
  ];
708
709
  export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
709
710
  depends_on: z.array(z.string()).default([]),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.39.0-next.10",
3
+ "version": "0.39.0-next.12",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -1 +0,0 @@
1
- import{ag as o,ah as n}from"./mermaid.core-FLK4IOPu.js";const t=(a,r)=>o.lang.round(n.parse(a)[r]);export{t as c};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-SDhf5BHa.js";import{_ as i}from"./mermaid.core-FLK4IOPu.js";import"./chunk-5VM5RSS4-BHy2jQUr.js";import"./chunk-XXDRQBXY-lFslfIU9.js";import"./chunk-KBJHAD2P-Y1pEsL6r.js";import"./chunk-2GRJ4B5K-C1X8DD9m.js";import"./index-0DWC8Vk9.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};
@@ -1 +0,0 @@
1
- import{s as a,c as s,a as e,C as t}from"./chunk-GF5L2VYU-SDhf5BHa.js";import{_ as i}from"./mermaid.core-FLK4IOPu.js";import"./chunk-5VM5RSS4-BHy2jQUr.js";import"./chunk-XXDRQBXY-lFslfIU9.js";import"./chunk-KBJHAD2P-Y1pEsL6r.js";import"./chunk-2GRJ4B5K-C1X8DD9m.js";import"./index-0DWC8Vk9.js";var n={parser:e,get db(){return new t},renderer:s,styles:a,init:i(r=>{r.class||(r.class={}),r.class.arrowMarkerAbsolute=r.arrowMarkerAbsolute},"init")};export{n as diagram};