@tea-agent/loop-agent 0.37.0 → 0.37.1-beta.0
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/AGENTS.md +2 -6
- package/CHANGELOG.md +71 -144
- package/README.md +1 -1
- package/bin/loop-agent.js +1 -37
- package/dist/application/task-lifecycle/advance.js +0 -1
- package/dist/application/task-lifecycle/observe.js +0 -15
- package/dist/application/task-lifecycle/plan-transitions.js +0 -15
- package/dist/cli/command-definitions.js +0 -7
- package/dist/cli/program.js +3 -8
- package/dist/commands/init-upgrade.js +19 -351
- package/dist/commands/init.js +67 -14
- package/dist/commands/run-dag-progress.js +0 -14
- package/dist/commands/task-advance.js +3 -33
- package/dist/executors/dag-pi-executor.js +30 -47
- package/dist/executors/pi-sdk-executor.js +0 -34
- package/dist/executors/shell-executor.js +95 -6
- package/dist/shared/operator/capabilities.js +13 -200
- package/dist/shared/package-metadata.js +0 -42
- package/dist/task/contract/project.js +0 -4
- package/dist/task/source-prepare/completeness.js +0 -53
- package/dist/task/source-prepare/parse-intent.js +12 -82
- package/dist/task/source-prepare/prepare.js +5 -102
- package/dist/task/source-prepare/semantic-intake.js +23 -144
- package/dist/worker/console/chat/artifact-card.js +1 -8
- package/dist/worker/console/chat/chat-event-store.js +0 -152
- package/dist/worker/console/chat/human-gate-card.js +1 -9
- package/dist/worker/console/chat/operation-card.js +2 -71
- package/dist/worker/console/chat/pi-runtime.js +62 -111
- package/dist/worker/console/chat/routes.js +10 -43
- package/dist/worker/console/chat/session-store.js +1 -9
- package/dist/worker/console/chat/shortcuts.js +7 -9
- package/dist/worker/console/chat/turn-process.js +23 -204
- package/dist/worker/console/chat/workspace-landing.js +1 -2
- package/dist/worker/console/operation-runner.js +6 -155
- package/dist/worker/console/operator-actions.js +14 -391
- package/dist/worker/console/operator-user-error.js +0 -4
- package/dist/worker/console/recovery-cta.js +3 -50
- package/dist/worker/console/static/assets/index-HX1pbOyl.css +1 -0
- package/dist/worker/console/static/assets/index-M0BLEBfh.js +56 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +9 -13
- package/dist/worker/console/static-src/app/useOperatorActions.js +2 -4
- package/dist/worker/console/static-src/app/useRecoveryActions.js +56 -65
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +1 -73
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +30 -114
- package/dist/worker/console/static-src/operator-chat/refs.js +0 -3
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +13 -67
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -12
- package/dist/worker/console/static-src/operator-chat/useComposer.js +8 -45
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -7
- package/dist/worker/loop-agent/loop-agent-client.js +3 -17
- package/dist/worker/observability/read-model.js +0 -20
- package/dist/worker/observe/spec-evidence.js +8 -3
- package/dist/worker/observe/static/operator-chrome.d.ts +0 -1
- package/dist/worker/observe/static/operator-chrome.js +3 -10
- package/dist/worker/observe/static/views/dag-inspector.js +71 -6
- package/dist/worker/observe/static/views/dag.js +0 -12
- package/dist/worker/preflight.js +1 -2
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +707 -37
- package/dist/workflows/dag/backend-test-case-manifest.js +4 -0
- package/dist/workflows/dag/backend-test-markdown-workflow.js +109 -7
- package/dist/workflows/dag/backend-test-module-stem.js +5 -0
- package/dist/workflows/dag/backend-test-pytest-collection.js +345 -24
- package/dist/workflows/dag/backend-test-scenario-param.js +890 -164
- package/dist/workflows/dag/backend-test-writer-completeness.js +47 -16
- package/dist/workflows/dag/dynamic-runtime/map.js +24 -8
- package/dist/workflows/dag/dynamic-runtime/shared.js +1 -9
- package/dist/workflows/dag/frontend-implementation-contract.js +39 -233
- package/dist/workflows/dag/frontend-prewrite-gate.js +61 -364
- package/dist/workflows/dag/frontend-repair.js +18 -219
- package/dist/workflows/dag/frontend-verification-trace.js +32 -47
- package/dist/workflows/dag/init-hybrid.js +82 -165
- package/dist/workflows/dag/node-execution.js +0 -89
- package/dist/workflows/dag/recovery-recommendation.js +0 -58
- package/dist/workflows/dag/runner.js +15 -333
- package/dist/workflows/dag/scheduler.js +3 -257
- package/dist/workflows/dag/types.js +9 -130
- package/docs/README.md +3 -3
- package/docs/architecture/evolution.md +67 -102
- package/docs/templates/backend-test-dag.json +50 -32
- package/docs/templates/init-managed-agents.md +2 -5
- package/harness.json +2 -2
- package/package.json +3 -9
- package/skills/loop-agent/SKILL.md +0 -1
- package/skills/loop-agent/references/command-reference.md +0 -4
- package/dist/build-stamp.json +0 -6
- package/dist/commands/dag-request-interrupt.js +0 -20
- package/dist/worker/console/chat/assistant-content.js +0 -121
- package/dist/worker/console/chat/semantic-activity.js +0 -477
- package/dist/worker/console/operation-run-facts.js +0 -190
- package/dist/worker/console/operation-wait.js +0 -355
- package/dist/worker/console/recovery-error-copy.js +0 -198
- package/dist/worker/console/static/assets/abnfDiagram-N423BO3Z-DSvF6RXQ.js +0 -1
- package/dist/worker/console/static/assets/arc-DuI4ogtJ.js +0 -1
- package/dist/worker/console/static/assets/architectureDiagram-T3A2C74G-DdBbgD4y.js +0 -36
- package/dist/worker/console/static/assets/blockDiagram-VBNYF7ZC-xQC2HELm.js +0 -132
- package/dist/worker/console/static/assets/c4Diagram-5PPSVZJV-DPMSgwIZ.js +0 -10
- package/dist/worker/console/static/assets/channel-DLVz9TYW.js +0 -1
- package/dist/worker/console/static/assets/chunk-2GRJ4B5K-WTEKTxin.js +0 -1
- package/dist/worker/console/static/assets/chunk-2Q5K7J3B-B6vZ0Pv_.js +0 -1
- package/dist/worker/console/static/assets/chunk-5RXB4S5H-BrerNAix.js +0 -231
- package/dist/worker/console/static/assets/chunk-5VM5RSS4-D_ch2aaI.js +0 -15
- package/dist/worker/console/static/assets/chunk-6Q2QTUOP-VvfacNrq.js +0 -88
- package/dist/worker/console/static/assets/chunk-GF5L2VYU-B-uGFIg2.js +0 -206
- package/dist/worker/console/static/assets/chunk-JWPE2WC7-BlysCtod.js +0 -1
- package/dist/worker/console/static/assets/chunk-KBJHAD2P-BjAUAfcT.js +0 -1
- package/dist/worker/console/static/assets/chunk-RYQCIY6F-BeZnBmb-.js +0 -1
- package/dist/worker/console/static/assets/chunk-XXDRQBXY-Bbfe3UJG.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BeAuGcO9.js +0 -1
- package/dist/worker/console/static/assets/cose-bilkent-JH36ORCC-BKJQnYVM.js +0 -1
- package/dist/worker/console/static/assets/cynefin-VYW2F7L2-B1wtI3hU.js +0 -166
- package/dist/worker/console/static/assets/cynefinDiagram-MW4NZA55-DCF6DBSu.js +0 -62
- package/dist/worker/console/static/assets/cytoscape.esm-yzknjiTM.js +0 -321
- package/dist/worker/console/static/assets/dagre-VZM6K2ZE-BJyNGRlP.js +0 -4
- package/dist/worker/console/static/assets/defaultLocale-DX6XiGOO.js +0 -1
- package/dist/worker/console/static/assets/diagram-7IWD3JNH-B7j1-VsZ.js +0 -30
- package/dist/worker/console/static/assets/diagram-B4RE2ZJO-BREJ140a.js +0 -3
- package/dist/worker/console/static/assets/diagram-LBJQPF4R-CDPHmHGH.js +0 -24
- package/dist/worker/console/static/assets/diagram-Q27KOJAE-BtZLrez1.js +0 -24
- package/dist/worker/console/static/assets/diagram-UB23O5K3-mzybu8oU.js +0 -41
- package/dist/worker/console/static/assets/ebnfDiagram-BXEA7PRR-Dk3I2uO6.js +0 -1
- package/dist/worker/console/static/assets/erDiagram-JOGREHBK-BT97u_tQ.js +0 -85
- package/dist/worker/console/static/assets/flowDiagram-UKHOOZJN-CMOtYzui.js +0 -156
- package/dist/worker/console/static/assets/ganttDiagram-PKOTCBZU-DiNydkuY.js +0 -292
- package/dist/worker/console/static/assets/gitGraphDiagram-DS77QQ5N-Cwt6fLkl.js +0 -106
- package/dist/worker/console/static/assets/graph-DOmOIIwC.js +0 -1
- package/dist/worker/console/static/assets/index-DQxiso1w.js +0 -325
- package/dist/worker/console/static/assets/index-_j0Hzo69.css +0 -1
- package/dist/worker/console/static/assets/infoDiagram-6WML65LV-AsGIGKKB.js +0 -2
- package/dist/worker/console/static/assets/init-Gi6I4Gst.js +0 -1
- package/dist/worker/console/static/assets/ishikawaDiagram-WSZJBQD7-BL8ITZh3.js +0 -70
- package/dist/worker/console/static/assets/journeyDiagram-NVQOT4AX-VPKAUX8M.js +0 -139
- package/dist/worker/console/static/assets/kanban-definition-27J2QSJJ-BL56hfGp.js +0 -89
- package/dist/worker/console/static/assets/layout-D-LzfAck.js +0 -1
- package/dist/worker/console/static/assets/linear-BmSG3nIZ.js +0 -1
- package/dist/worker/console/static/assets/map-DxJ2ADlA.js +0 -1
- package/dist/worker/console/static/assets/mermaid.core-DWGK9kwP.js +0 -308
- package/dist/worker/console/static/assets/mindmap-definition-FAOFIHXS--vvKLjo0.js +0 -96
- package/dist/worker/console/static/assets/ordinal-Cboi1Yqb.js +0 -1
- package/dist/worker/console/static/assets/pegDiagram-VL7TDLO6-B41mxb2-.js +0 -1
- package/dist/worker/console/static/assets/pieDiagram-7S7Q4E2Y-DTlu77sw.js +0 -39
- package/dist/worker/console/static/assets/quadrantDiagram-CIZ2JOQS-Bk5A7alv.js +0 -7
- package/dist/worker/console/static/assets/railroadDiagram-AXF67PYL-Csf9jzQy.js +0 -1
- package/dist/worker/console/static/assets/requirementDiagram-LRYGKXZP-CeptoSNx.js +0 -84
- package/dist/worker/console/static/assets/sankeyDiagram-W5VNT64P-A-DLCLer.js +0 -40
- package/dist/worker/console/static/assets/sequenceDiagram-SI44F4Z6-22VbPdOI.js +0 -162
- package/dist/worker/console/static/assets/sizeCapture-X5ZJPWSS-DWvAyBC9.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-OKZ733FA-qICZ75yT.js +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DzKvfHTX.js +0 -1
- package/dist/worker/console/static/assets/swimlanes-SLNWSIFB-xi7lcMyp.js +0 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Cxqr_qM1.js +0 -8
- package/dist/worker/console/static/assets/timeline-definition-Z64GVDOM-Bu6Nl6O2.js +0 -120
- package/dist/worker/console/static/assets/vennDiagram-T6HMQDX7-BzMz4Bn9.js +0 -34
- package/dist/worker/console/static/assets/wardleyDiagram-T6FBY63Y-CxaJnzc5.js +0 -78
- package/dist/worker/console/static/assets/xychartDiagram-ELKLHX3M-5JK-omQK.js +0 -7
- package/dist/worker/console/static/fonts/katex/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/worker/console/static/fonts/katex/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/worker/console/static-src/operator-chat/activity-journey.js +0 -125
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/activity-references.js +0 -20
- package/dist/worker/console/static-src/operator-chat/slash-palette-layout.js +0 -24
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +0 -141
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
- package/dist/worker/observability/interrupt-eligibility.js +0 -264
- package/dist/workflows/dag/contract-output-registry.js +0 -14
- package/dist/workflows/dag/contract-validator-registrations.js +0 -8
- package/dist/workflows/dag/frontend-recovery-plan.js +0 -73
- package/dist/workflows/dag/frontend-recovery-root-manifest.js +0 -123
- package/dist/workflows/dag/frontend-recovery-run.js +0 -539
- package/dist/workflows/dag/frontend-writer-recovery.js +0 -106
- package/dist/workflows/dag/frontend-writer-rollback.js +0 -821
- package/dist/workflows/dag/interrupt-request.js +0 -559
|
@@ -1,10 +1,5 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import path from "node:path";
|
|
3
1
|
import { isPauseOnHumanDecisionGate } from "./decision-envelope.js";
|
|
4
2
|
import { evaluateConditionExpression } from "./dynamic-runtime/condition.js";
|
|
5
|
-
import { sha256Hex } from "./frontend-implementation-contract.js";
|
|
6
|
-
import { frontendPrewriteResultV1Schema, FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME, } from "./frontend-prewrite-gate.js";
|
|
7
|
-
import { FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH, FRONTEND_RECOVERY_INTENT_REL_DIR, } from "./frontend-recovery-plan.js";
|
|
8
3
|
export function isConditionSkippedReason(reason) {
|
|
9
4
|
return Boolean(reason?.startsWith("condition "));
|
|
10
5
|
}
|
|
@@ -109,58 +104,6 @@ async function mapConcurrent(items, limit, fn) {
|
|
|
109
104
|
}
|
|
110
105
|
await Promise.all(executing);
|
|
111
106
|
}
|
|
112
|
-
export const FRONTEND_WRITER_NODE_IDS = [
|
|
113
|
-
"frontend-implement-pi",
|
|
114
|
-
"frontend-repair-pi",
|
|
115
|
-
];
|
|
116
|
-
/** Run-relative artifact location written by the prewrite gate generator. */
|
|
117
|
-
export const FRONTEND_PREWRITE_RESULT_SOURCE_ARTIFACT = path.posix.join("contracts", FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME);
|
|
118
|
-
/**
|
|
119
|
-
* Read and validate the frontend-prewrite-result-v1 artifact. Fail-closed:
|
|
120
|
-
* a missing file or invalid payload returns ok:false and never throws.
|
|
121
|
-
*/
|
|
122
|
-
export async function readFrontendPrewriteResult(runDir) {
|
|
123
|
-
const artifactPath = path.join(runDir, "contracts", FRONTEND_PREWRITE_RESULT_ARTIFACT_NAME);
|
|
124
|
-
let raw;
|
|
125
|
-
try {
|
|
126
|
-
raw = await readFile(artifactPath, "utf8");
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
if (error.code === "ENOENT") {
|
|
130
|
-
return {
|
|
131
|
-
ok: false,
|
|
132
|
-
reason: `frontend prewrite result artifact missing: ${artifactPath}`,
|
|
133
|
-
};
|
|
134
|
-
}
|
|
135
|
-
throw error;
|
|
136
|
-
}
|
|
137
|
-
const artifactHash = sha256Hex(raw);
|
|
138
|
-
let parsed;
|
|
139
|
-
try {
|
|
140
|
-
parsed = JSON.parse(raw);
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
return {
|
|
144
|
-
ok: false,
|
|
145
|
-
reason: `frontend prewrite result artifact is not valid JSON: ${error instanceof Error ? error.message : String(error)}`,
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
const result = frontendPrewriteResultV1Schema.safeParse(parsed);
|
|
149
|
-
if (!result.success) {
|
|
150
|
-
return {
|
|
151
|
-
ok: false,
|
|
152
|
-
reason: "frontend prewrite result artifact failed schema validation",
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
return { ok: true, result: result.data, artifactHash };
|
|
156
|
-
}
|
|
157
|
-
/** Authorize only accepted / accepted-normalized classifications. */
|
|
158
|
-
export function isFrontendWriterAuthorized(result) {
|
|
159
|
-
return result.classification === "accepted" ||
|
|
160
|
-
result.classification === "accepted-normalized"
|
|
161
|
-
? "authorized"
|
|
162
|
-
: "denied";
|
|
163
|
-
}
|
|
164
107
|
/** Fail-closed: leave no PENDING nodes that look "still scheduled" after abort. */
|
|
165
108
|
export function markPendingNodesControllerInterrupted(state, reason = "run aborted by controller (abortSignal)") {
|
|
166
109
|
const affected = [];
|
|
@@ -179,166 +122,8 @@ export function markPendingNodesControllerInterrupted(state, reason = "run abort
|
|
|
179
122
|
}
|
|
180
123
|
return affected;
|
|
181
124
|
}
|
|
182
|
-
/**
|
|
183
|
-
* True when this run is a candidate-continuation child: it has a recovery
|
|
184
|
-
* lineage whose attemptIndex is >= 1 and whose recovery root is another run.
|
|
185
|
-
* The root/parent keeps `recoveryRootRunId === state.runId`.
|
|
186
|
-
*/
|
|
187
|
-
export function isFrontendRecoveryChild(state) {
|
|
188
|
-
const recovery = state.frontendRecoveryState;
|
|
189
|
-
return Boolean(recovery &&
|
|
190
|
-
recovery.attemptIndex >= 1 &&
|
|
191
|
-
recovery.recoveryRootRunId !== state.runId);
|
|
192
|
-
}
|
|
193
|
-
function isValidFrontendRecoveryActivationMarker(raw) {
|
|
194
|
-
return (raw.schemaVersion === 1 &&
|
|
195
|
-
typeof raw.requestId === "string" &&
|
|
196
|
-
raw.requestId.length > 0 &&
|
|
197
|
-
typeof raw.parentRunId === "string" &&
|
|
198
|
-
raw.parentRunId.length > 0 &&
|
|
199
|
-
typeof raw.recoveryRootRunId === "string" &&
|
|
200
|
-
raw.recoveryRootRunId.length > 0 &&
|
|
201
|
-
typeof raw.childRunId === "string" &&
|
|
202
|
-
raw.childRunId.length > 0 &&
|
|
203
|
-
typeof raw.importManifestSha256 === "string" &&
|
|
204
|
-
/^[a-f0-9]{64}$/.test(raw.importManifestSha256));
|
|
205
|
-
}
|
|
206
|
-
/**
|
|
207
|
-
* Activation marker gate (phase 3c AC-1). Fail-closed: an active child is only
|
|
208
|
-
* executable when its parent is `child-running`, points at this child, carries
|
|
209
|
-
* a structurally valid activation marker whose lineage matches, and the marker
|
|
210
|
-
* hash matches the child's import manifest. Read-only and never throws.
|
|
211
|
-
*/
|
|
212
|
-
export async function checkFrontendRecoveryActivation(state, childRunDir) {
|
|
213
|
-
if (!isFrontendRecoveryChild(state)) {
|
|
214
|
-
return { applicable: false };
|
|
215
|
-
}
|
|
216
|
-
const recovery = state.frontendRecoveryState;
|
|
217
|
-
const parentRunId = recovery.parentRunId;
|
|
218
|
-
const parentRunDir = path.join(path.dirname(childRunDir), parentRunId);
|
|
219
|
-
let parentState;
|
|
220
|
-
try {
|
|
221
|
-
parentState = JSON.parse(await readFile(path.join(parentRunDir, "state.json"), "utf8"));
|
|
222
|
-
}
|
|
223
|
-
catch {
|
|
224
|
-
return { applicable: true, ok: false, reason: "parent state unreadable" };
|
|
225
|
-
}
|
|
226
|
-
const parentRecovery = parentState.frontendRecoveryState;
|
|
227
|
-
if (parentRecovery?.phase !== "child-running") {
|
|
228
|
-
return {
|
|
229
|
-
applicable: true,
|
|
230
|
-
ok: false,
|
|
231
|
-
reason: "parent phase not child-running",
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
if (parentRecovery.childRunId !== state.runId) {
|
|
235
|
-
return {
|
|
236
|
-
applicable: true,
|
|
237
|
-
ok: false,
|
|
238
|
-
reason: "parent childRunId mismatch",
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
const markerPath = path.join(parentRunDir, FRONTEND_RECOVERY_INTENT_REL_DIR, `${recovery.requestId}.json`);
|
|
242
|
-
let markerRaw;
|
|
243
|
-
try {
|
|
244
|
-
markerRaw = JSON.parse(await readFile(markerPath, "utf8"));
|
|
245
|
-
}
|
|
246
|
-
catch {
|
|
247
|
-
return {
|
|
248
|
-
applicable: true,
|
|
249
|
-
ok: false,
|
|
250
|
-
reason: "activation marker missing",
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
if (typeof markerRaw !== "object" ||
|
|
254
|
-
markerRaw === null ||
|
|
255
|
-
!isValidFrontendRecoveryActivationMarker(markerRaw)) {
|
|
256
|
-
return {
|
|
257
|
-
applicable: true,
|
|
258
|
-
ok: false,
|
|
259
|
-
reason: "activation marker invalid",
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
const marker = markerRaw;
|
|
263
|
-
if (marker.requestId !== recovery.requestId) {
|
|
264
|
-
return {
|
|
265
|
-
applicable: true,
|
|
266
|
-
ok: false,
|
|
267
|
-
reason: "marker requestId mismatch",
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
if (marker.parentRunId !== parentRunId) {
|
|
271
|
-
return {
|
|
272
|
-
applicable: true,
|
|
273
|
-
ok: false,
|
|
274
|
-
reason: "marker parentRunId mismatch",
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
if (marker.recoveryRootRunId !== recovery.recoveryRootRunId) {
|
|
278
|
-
return {
|
|
279
|
-
applicable: true,
|
|
280
|
-
ok: false,
|
|
281
|
-
reason: "marker recoveryRootRunId mismatch",
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
|
-
if (marker.childRunId !== state.runId) {
|
|
285
|
-
return {
|
|
286
|
-
applicable: true,
|
|
287
|
-
ok: false,
|
|
288
|
-
reason: "marker childRunId mismatch",
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
let manifestSha256;
|
|
292
|
-
try {
|
|
293
|
-
manifestSha256 = sha256Hex(await readFile(path.join(childRunDir, FRONTEND_RECOVERY_IMPORT_MANIFEST_REL_PATH), "utf8"));
|
|
294
|
-
}
|
|
295
|
-
catch {
|
|
296
|
-
return {
|
|
297
|
-
applicable: true,
|
|
298
|
-
ok: false,
|
|
299
|
-
reason: "import manifest unreadable",
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
if (manifestSha256 !== marker.importManifestSha256) {
|
|
303
|
-
return {
|
|
304
|
-
applicable: true,
|
|
305
|
-
ok: false,
|
|
306
|
-
reason: "import manifest sha256 mismatch",
|
|
307
|
-
};
|
|
308
|
-
}
|
|
309
|
-
return {
|
|
310
|
-
applicable: true,
|
|
311
|
-
ok: true,
|
|
312
|
-
requestId: recovery.requestId,
|
|
313
|
-
childRunId: state.runId,
|
|
314
|
-
};
|
|
315
|
-
}
|
|
316
125
|
export async function executeDagRanksOnce(input) {
|
|
317
126
|
let pausedByNodeId;
|
|
318
|
-
// Activation marker gate (phase 3c AC-1): a recovery child without a valid
|
|
319
|
-
// activation marker must never be scheduled or executed. Fail-closed before
|
|
320
|
-
// any executeScheduledNode call, so zero writer/provider invocations happen.
|
|
321
|
-
if (input.runDir) {
|
|
322
|
-
const hasPending = Object.values(input.state.nodes).some((node) => node.status === "PENDING");
|
|
323
|
-
if (hasPending) {
|
|
324
|
-
const activation = await checkFrontendRecoveryActivation(input.state, input.runDir);
|
|
325
|
-
if (activation.applicable && !activation.ok) {
|
|
326
|
-
const finishedAt = new Date().toISOString();
|
|
327
|
-
let affected = 0;
|
|
328
|
-
for (const node of Object.values(input.state.nodes)) {
|
|
329
|
-
if (node.status !== "PENDING")
|
|
330
|
-
continue;
|
|
331
|
-
node.status = "SKIPPED";
|
|
332
|
-
node.skippedReason = "frontend-recovery-child-not-activated";
|
|
333
|
-
node.finishedAt = finishedAt;
|
|
334
|
-
affected += 1;
|
|
335
|
-
}
|
|
336
|
-
if (affected > 0)
|
|
337
|
-
await input.persistState();
|
|
338
|
-
return undefined;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
127
|
for (const rank of input.ranks) {
|
|
343
128
|
if (input.abortSignal?.aborted) {
|
|
344
129
|
const marked = markPendingNodesControllerInterrupted(input.state, input.abortSignal.reason
|
|
@@ -383,45 +168,6 @@ export async function executeDagRanksOnce(input) {
|
|
|
383
168
|
await input.persistState();
|
|
384
169
|
const conditionSkippedSet = new Set(conditionSettled);
|
|
385
170
|
const actuallyRunnable = runnable.filter((id) => !conditionSkippedSet.has(id));
|
|
386
|
-
const frontendAdmissionSettled = [];
|
|
387
|
-
if (input.runDir) {
|
|
388
|
-
for (const id of actuallyRunnable) {
|
|
389
|
-
if (!FRONTEND_WRITER_NODE_IDS.includes(id))
|
|
390
|
-
continue;
|
|
391
|
-
const node = input.state.nodes[id];
|
|
392
|
-
const checkedAt = new Date().toISOString();
|
|
393
|
-
const admission = await readFrontendPrewriteResult(input.runDir);
|
|
394
|
-
if (!admission.ok) {
|
|
395
|
-
node.status = "SKIPPED";
|
|
396
|
-
node.skippedReason = "frontend-prewrite-not-authorized";
|
|
397
|
-
node.finishedAt = checkedAt;
|
|
398
|
-
frontendAdmissionSettled.push(id);
|
|
399
|
-
continue;
|
|
400
|
-
}
|
|
401
|
-
const decision = isFrontendWriterAuthorized(admission.result);
|
|
402
|
-
node.frontendWriterAdmission = {
|
|
403
|
-
schemaVersion: 1,
|
|
404
|
-
writerNodeId: id,
|
|
405
|
-
decision,
|
|
406
|
-
sourceArtifact: FRONTEND_PREWRITE_RESULT_SOURCE_ARTIFACT,
|
|
407
|
-
artifactHash: admission.artifactHash,
|
|
408
|
-
checkedAt,
|
|
409
|
-
reason: decision === "denied"
|
|
410
|
-
? `classification: ${admission.result.classification}`
|
|
411
|
-
: null,
|
|
412
|
-
};
|
|
413
|
-
if (decision === "denied") {
|
|
414
|
-
node.status = "SKIPPED";
|
|
415
|
-
node.skippedReason = "frontend-prewrite-not-authorized";
|
|
416
|
-
node.finishedAt = checkedAt;
|
|
417
|
-
frontendAdmissionSettled.push(id);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
if (frontendAdmissionSettled.length > 0)
|
|
421
|
-
await input.persistState();
|
|
422
|
-
}
|
|
423
|
-
const frontendAdmissionSkippedSet = new Set(frontendAdmissionSettled);
|
|
424
|
-
const runnableAfterAdmission = actuallyRunnable.filter((id) => !frontendAdmissionSkippedSet.has(id));
|
|
425
171
|
const blocked = pending.filter((id) => {
|
|
426
172
|
const task = input.tasksById.get(id);
|
|
427
173
|
return (dependencyReadiness(task, input.state.nodes, input.tasksById) === "skip");
|
|
@@ -436,12 +182,12 @@ export async function executeDagRanksOnce(input) {
|
|
|
436
182
|
if (blocked.length > 0) {
|
|
437
183
|
await input.persistState();
|
|
438
184
|
}
|
|
439
|
-
const pauseGateRunnable =
|
|
185
|
+
const pauseGateRunnable = actuallyRunnable.filter((id) => {
|
|
440
186
|
const task = input.tasksById.get(id);
|
|
441
187
|
return isPauseOnHumanDecisionGate(task);
|
|
442
188
|
});
|
|
443
|
-
const regularRunnable =
|
|
444
|
-
const rankWriterNodeIds =
|
|
189
|
+
const regularRunnable = actuallyRunnable.filter((id) => !pauseGateRunnable.includes(id));
|
|
190
|
+
const rankWriterNodeIds = actuallyRunnable.filter((id) => {
|
|
445
191
|
const task = input.tasksById.get(id);
|
|
446
192
|
return (task?.executor === "pi" &&
|
|
447
193
|
task.toolProfile === "write" &&
|
|
@@ -557,6 +557,13 @@ export const dagDynamicExpansionSchema = z.object({
|
|
|
557
557
|
maxTotalTokens: z.number().int().positive().optional(),
|
|
558
558
|
})
|
|
559
559
|
.optional(),
|
|
560
|
+
/**
|
|
561
|
+
* When true, any child blocked by the aggregate token budget fails the map
|
|
562
|
+
* barrier instead of being retained as a case-level blocked outcome.
|
|
563
|
+
* Backend-test enables this because partial Markdown/pytest module sets are
|
|
564
|
+
* invalid generation assets; frontend-test keeps the default false.
|
|
565
|
+
*/
|
|
566
|
+
failOnTokenBudgetExhaustion: z.boolean().optional(),
|
|
560
567
|
/**
|
|
561
568
|
* When true, map child ERROR/auth/timeout is recorded as case-level
|
|
562
569
|
* failed/blocked evidence and the map barrier still succeeds (frontend-test).
|
|
@@ -654,15 +661,8 @@ export const dagConvergenceSpecSchema = z
|
|
|
654
661
|
chainNodeIds: z.array(z.string()).optional(),
|
|
655
662
|
})
|
|
656
663
|
.optional();
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
* self-validation. Validators are registered in the contract output registry
|
|
660
|
-
* (src/workflows/dag/contract-output-registry.ts).
|
|
661
|
-
*/
|
|
662
|
-
export const structuredContractOutputSchemaIds = [
|
|
663
|
-
"frontend-implementation-contract-v1",
|
|
664
|
-
];
|
|
665
|
-
export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
|
|
664
|
+
export const dagTaskSchema = z.object({
|
|
665
|
+
id: z.string().regex(/^[a-z][a-z0-9-]*$/, "task id must be kebab-case"),
|
|
666
666
|
depends_on: z.array(z.string()).default([]),
|
|
667
667
|
/**
|
|
668
668
|
* How SKIPPED upstreams affect readiness.
|
|
@@ -711,20 +711,6 @@ export const dagTaskSchema = z.object({ id: z.string().regex(/^[a-z][a-z0-9-]*$/
|
|
|
711
711
|
* on safe read-only Pi nodes before the node becomes ERROR.
|
|
712
712
|
*/
|
|
713
713
|
outputProtocol: dagOutputProtocolSchema.optional(),
|
|
714
|
-
/**
|
|
715
|
-
* Node-level structured contract self-check: after the Pi output is
|
|
716
|
-
* produced, validate it against the named contract schema at the node so
|
|
717
|
-
* schema/typo/null violations become invalid-output (retryable on the node)
|
|
718
|
-
* instead of failing later at a downstream deterministic gate. Validators
|
|
719
|
-
* are resolved via the contract output registry keyed by schemaId.
|
|
720
|
-
*/
|
|
721
|
-
structuredContractOutput: z
|
|
722
|
-
.object({
|
|
723
|
-
schemaId: z.enum(structuredContractOutputSchemaIds),
|
|
724
|
-
retryOnInvalid: z.boolean().default(true),
|
|
725
|
-
})
|
|
726
|
-
.strict()
|
|
727
|
-
.optional(),
|
|
728
714
|
/**
|
|
729
715
|
* Fail-closed outcome/diff consistency contract for bounded Pi writers.
|
|
730
716
|
* The writer must begin with IMPLEMENTATION_OUTCOME: changed,
|
|
@@ -895,113 +881,6 @@ export const dagSpecSchema = z
|
|
|
895
881
|
});
|
|
896
882
|
}
|
|
897
883
|
});
|
|
898
|
-
export const FRONTEND_RECOVERY_STATE_SCHEMA_VERSION = 1;
|
|
899
|
-
export const FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION = 1;
|
|
900
|
-
/**
|
|
901
|
-
* Frontend candidate-continuation recovery phase (decision B, phase-3 subset).
|
|
902
|
-
* Phase 3a parents converge first, so `rollback-pending` is not produced and
|
|
903
|
-
* was removed by AC-1; the phase starts at `child-staging` and ends at
|
|
904
|
-
* `settled`.
|
|
905
|
-
*/
|
|
906
|
-
export const frontendRecoveryPhaseSchema = z.enum([
|
|
907
|
-
"child-staging",
|
|
908
|
-
"child-activating",
|
|
909
|
-
"child-running",
|
|
910
|
-
"settled",
|
|
911
|
-
]);
|
|
912
|
-
/**
|
|
913
|
-
* Single-writer recovery intent/lineage stored on `DagRunState`. The parent run
|
|
914
|
-
* owns the authoritative copy and advances it via revision-guarded CAS writes;
|
|
915
|
-
* the child carries a frozen lineage snapshot so the activation gate can prove
|
|
916
|
-
* it is the reserved child of a child-running parent.
|
|
917
|
-
*
|
|
918
|
-
* Invariants: `attemptIndex ∈ {0,1}`; `continuationCount ∈ {0,1}`; a child's
|
|
919
|
-
* `recoveryRootRunId` always equals the root runId; `revision` is the CAS
|
|
920
|
-
* pre-comparison counter. `revision` monotonicity is enforced at runtime by the
|
|
921
|
-
* runner's `(existing?.revision ?? 0) + 1` CAS write (the schema only bounds it
|
|
922
|
-
* non-negative). `childRunId` may be absent through `child-activating` and must
|
|
923
|
-
* be non-empty from `child-running` onward.
|
|
924
|
-
*/
|
|
925
|
-
export const frontendRecoveryStateSchema = z
|
|
926
|
-
.object({
|
|
927
|
-
schemaVersion: z.literal(FRONTEND_RECOVERY_STATE_SCHEMA_VERSION),
|
|
928
|
-
phase: frontendRecoveryPhaseSchema,
|
|
929
|
-
requestId: z.string().min(1),
|
|
930
|
-
recoveryRootRunId: z.string().min(1),
|
|
931
|
-
parentRunId: z.string().min(1),
|
|
932
|
-
childRunId: z.string().min(1).optional(),
|
|
933
|
-
attemptId: z.string().min(1),
|
|
934
|
-
attemptIndex: z.number().int().min(0).max(1),
|
|
935
|
-
continuationCount: z.number().int().min(0).max(1),
|
|
936
|
-
/** Node id of the reset-closure root: frontend-plan-pi /
|
|
937
|
-
* frontend-plan-revision-pi / frontend-prewrite-gate-shell /
|
|
938
|
-
* frontend-implement-pi (writer partial write). */
|
|
939
|
-
failureSource: z.string().min(1).optional(),
|
|
940
|
-
revision: z.number().int().nonnegative(),
|
|
941
|
-
})
|
|
942
|
-
.strict()
|
|
943
|
-
.superRefine((value, ctx) => {
|
|
944
|
-
const requiresChild = value.phase === "child-running" || value.phase === "settled";
|
|
945
|
-
if (requiresChild && !value.childRunId) {
|
|
946
|
-
ctx.addIssue({
|
|
947
|
-
code: z.ZodIssueCode.custom,
|
|
948
|
-
message: `phase ${value.phase} requires a non-empty childRunId`,
|
|
949
|
-
path: ["childRunId"],
|
|
950
|
-
});
|
|
951
|
-
}
|
|
952
|
-
});
|
|
953
|
-
export const frontendRecoveryOutcomeSchema = z.enum([
|
|
954
|
-
"none",
|
|
955
|
-
"recovered",
|
|
956
|
-
"candidate-contract-invalid",
|
|
957
|
-
"prewrite-blocked",
|
|
958
|
-
"repair-exhausted",
|
|
959
|
-
"auto-recovery-blocked",
|
|
960
|
-
]);
|
|
961
|
-
export const frontendRecoveryOriginSchema = z
|
|
962
|
-
.object({
|
|
963
|
-
kind: z.enum(["frontend-prewrite-gate", "frontend-writer"]),
|
|
964
|
-
parentRunId: z.string().min(1),
|
|
965
|
-
childRunId: z.string().min(1).optional(),
|
|
966
|
-
requestId: z.string().min(1),
|
|
967
|
-
failedNodeId: z.string(),
|
|
968
|
-
})
|
|
969
|
-
.strict();
|
|
970
|
-
export const frontendRecoveryFailureClassSchema = z
|
|
971
|
-
.object({
|
|
972
|
-
code: z.enum([
|
|
973
|
-
"candidate-contract-invalid",
|
|
974
|
-
"prewrite-blocked",
|
|
975
|
-
"staging-failed",
|
|
976
|
-
"writer-transient-partial-write",
|
|
977
|
-
]),
|
|
978
|
-
classification: z.string(),
|
|
979
|
-
reason: z.string(),
|
|
980
|
-
})
|
|
981
|
-
.strict();
|
|
982
|
-
export const frontendRecoveryEvidenceRefSchema = z
|
|
983
|
-
.object({
|
|
984
|
-
runId: z.string().min(1),
|
|
985
|
-
relativePath: z.string().min(1),
|
|
986
|
-
sha256: z.string().min(1),
|
|
987
|
-
})
|
|
988
|
-
.strict();
|
|
989
|
-
/**
|
|
990
|
-
* Terminal frontend recovery result. `failureClass` is absent for `none`,
|
|
991
|
-
* `recovered` and `auto-recovery-blocked` outcomes. The schema intentionally
|
|
992
|
-
* stays permissive (failureClass optional, evidenceRefs may be empty) so it
|
|
993
|
-
* accepts the runner's actual phase-3a products, which do not always carry a
|
|
994
|
-
* failure class or evidence reference.
|
|
995
|
-
*/
|
|
996
|
-
export const frontendRecoveryResultSchema = z
|
|
997
|
-
.object({
|
|
998
|
-
schemaVersion: z.literal(FRONTEND_RECOVERY_RESULT_SCHEMA_VERSION),
|
|
999
|
-
outcome: frontendRecoveryOutcomeSchema,
|
|
1000
|
-
origin: frontendRecoveryOriginSchema,
|
|
1001
|
-
failureClass: frontendRecoveryFailureClassSchema.optional(),
|
|
1002
|
-
evidenceRefs: z.array(frontendRecoveryEvidenceRefSchema),
|
|
1003
|
-
})
|
|
1004
|
-
.strict();
|
|
1005
884
|
export const DEFAULT_DAG_EXECUTOR_MODELS = {
|
|
1006
885
|
pi: {
|
|
1007
886
|
LOW: "gpt-5.3-codex-spark",
|
package/docs/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`docs/` 是 loop-agent 源仓的治理文档根目录。新读者不需要先浏览所有文件,先按任务选择入口,再由目录 README 或 `docs/document-catalog.json` 判断文档身份。
|
|
4
4
|
|
|
5
|
-
**维护日:2026-
|
|
5
|
+
**维护日:2026-07-27** — 对齐 `@tea-agent/loop-agent@0.24.0`。版本事实以根 `CHANGELOG.md` 与 `package.json` 为准;能力短摘要见 `reports/current-capability-summary.md`。
|
|
6
6
|
|
|
7
7
|
## 我现在要做什么?
|
|
8
8
|
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
| --- | --- | --- |
|
|
11
11
|
| 理解当前能力与边界 | `reports/current-capability-summary.md` | `architecture/README.md`、`website/docs/overview/roadmap.md` |
|
|
12
12
|
| 开始一个受治理功能变更 | `governance/feature-workflow.md` | `governance/verification-matrix.md`、`templates/exec-plan.md` |
|
|
13
|
-
| 跑或恢复 Agent DAG | `runtime/agent-dag-runner.md` | `runtime/agent-dag-recovery-playbook.md`、`runtime/loop-agent-harness.md
|
|
13
|
+
| 跑或恢复 Agent DAG | `runtime/agent-dag-runner.md` | `runtime/agent-dag-recovery-playbook.md`、`runtime/loop-agent-harness.md` |
|
|
14
14
|
| 判断架构边界 | `architecture/README.md` | `architecture/runtime-boundaries.md` |
|
|
15
15
|
| 查设计输入而非实现证明 | `design/README.md` | 对应设计文档;视觉参考见 `design/visual-references/README.md`,归档设计见 `design/archive/README.md` |
|
|
16
16
|
| 接手进行中任务 | `exec-plans/active/README.md` | 对应 active plan,再读 `progress/README.md` |
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
| 选择 taskKind、治理 profile 和 Agent DAG 路径 | `governance/feature-workflow.md` |
|
|
27
27
|
| 选择快速、标准或完整验证命令 | `governance/verification-matrix.md` |
|
|
28
28
|
| 理解 CLI、runtime 和初始化能力边界 | `runtime/loop-agent-harness.md` |
|
|
29
|
-
| 运行 DAG、查看报告或处理失败 | `runtime/agent-dag-runner.md`、`runtime/agent-dag-recovery-playbook.md
|
|
29
|
+
| 运行 DAG、查看报告或处理失败 | `runtime/agent-dag-runner.md`、`runtime/agent-dag-recovery-playbook.md` |
|
|
30
30
|
| 按 TDD、验证和调试纪律推进修复 | `governance/harness-methodology-tdd.md`、`governance/harness-methodology-verification.md`、`governance/harness-methodology-debugging.md` |
|
|
31
31
|
| 执行真实 backend-test provider 验证 | `runtime/backend-test-live-campaign.md` |
|
|
32
32
|
| 判断单任务 loop-agent Production Readiness | `operations/production-readiness.md` |
|