@tea-agent/loop-agent 0.32.0 → 0.33.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/CHANGELOG.md +60 -0
- package/dist/executors/model-routing.js +14 -4
- package/dist/governance/manifest-types.js +34 -7
- package/dist/worker/console/chat/model-resolver.js +114 -34
- package/dist/worker/console/chat/workspace-landing.js +58 -22
- package/dist/worker/console/doctor.js +1 -0
- package/dist/worker/console/night-aux-ticker.js +5 -0
- package/dist/worker/console/pi-readiness.js +26 -17
- package/dist/worker/console/server.js +2 -0
- package/dist/worker/console/static/assets/index-3R-GT3a_.js +29 -0
- package/dist/worker/console/static/assets/index-B0EQt_yq.css +1 -0
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/app/console-types.js +140 -0
- package/dist/worker/console/static-src/app/useConsoleShell.js +101 -0
- package/dist/worker/console/static-src/app/useOperatorActions.js +304 -0
- package/dist/worker/console/static-src/app/usePrdImport.js +171 -0
- package/dist/worker/console/static-src/app/useRecoveryActions.js +257 -0
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +334 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +229 -0
- package/dist/worker/console/static-src/chat-view-types.js +2 -0
- package/dist/worker/console/static-src/night/night-types.js +24 -0
- package/dist/worker/console/static-src/night/useNightBoard.js +94 -0
- package/dist/worker/console/static-src/night/useNightWizard.js +171 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +253 -0
- package/dist/worker/console/static-src/operator-chat/format.js +71 -0
- package/dist/worker/console/static-src/operator-chat/refs.js +47 -0
- package/dist/worker/console/static-src/operator-chat/tools-catalog.js +77 -0
- package/dist/worker/console/static-src/operator-chat/types.js +1 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +320 -0
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +209 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +218 -0
- package/dist/worker/console/static-src/operator-chat/useComposer.js +125 -0
- package/dist/worker/console/static-src/operator-chat/useInterview.js +108 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +123 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +207 -0
- package/dist/worker/observability/read-model.js +106 -0
- package/dist/worker/observe/routes.js +19 -1
- package/dist/worker/observe/static/api.js +42 -3
- package/dist/worker/observe/static/app.js +4 -0
- package/dist/worker/observe/static/constants.js +10 -0
- package/dist/worker/observe/static/custom-select.js +567 -0
- package/dist/worker/observe/static/index.html +47 -6
- package/dist/worker/observe/static/router.js +54 -1
- package/dist/worker/observe/static/state.js +20 -0
- package/dist/worker/observe/static/styles.css +618 -30
- package/dist/worker/observe/static/views/dag-inspector.js +20 -17
- package/dist/worker/observe/static/views/dag.js +136 -59
- package/dist/worker/observe/static/views/dags.js +877 -0
- package/dist/worker/observe/static/views/dashboard.js +15 -4
- package/dist/workflows/dag/init-hybrid.js +58 -29
- package/docs/templates/agent-worker-production-readiness-checklist.md +26 -24
- package/docs/templates/harness.schema.json +5 -0
- package/harness.json +4 -4
- package/package.json +5 -4
- package/dist/worker/console/static/assets/index-D9gnJn_l.js +0 -29
- package/dist/worker/console/static/assets/index-rajoXwkM.css +0 -1
|
@@ -504,7 +504,19 @@ export async function renderDashboard(scrollTo) {
|
|
|
504
504
|
reconcileKeyed(dagsEl, [{ key: "__heading__" }], {
|
|
505
505
|
getKey: () => "__heading__",
|
|
506
506
|
scope: "heading",
|
|
507
|
-
create: () =>
|
|
507
|
+
create: () => {
|
|
508
|
+
const head = el("div", "section-heading-row");
|
|
509
|
+
head.appendChild(el("h3", "section-heading", UI_TEXT.recentDags));
|
|
510
|
+
const allLink = el("a", "section-heading-action", UI_TEXT.viewAllDags);
|
|
511
|
+
allLink.href = "#/dags?page=1&pageSize=20";
|
|
512
|
+
allLink.setAttribute("aria-label", "查看全部 DAG 运行历史");
|
|
513
|
+
allLink.addEventListener("click", (event) => {
|
|
514
|
+
event.preventDefault();
|
|
515
|
+
navigate("/dags?page=1&pageSize=20");
|
|
516
|
+
});
|
|
517
|
+
head.appendChild(allLink);
|
|
518
|
+
return head;
|
|
519
|
+
},
|
|
508
520
|
update: () => {},
|
|
509
521
|
});
|
|
510
522
|
reconcileKeyed(
|
|
@@ -588,9 +600,8 @@ export async function renderDashboard(scrollTo) {
|
|
|
588
600
|
},
|
|
589
601
|
);
|
|
590
602
|
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
}
|
|
603
|
+
// Legacy scrollTo=dags alias retired: #/dags is an independent history page.
|
|
604
|
+
void scrollTo;
|
|
594
605
|
}
|
|
595
606
|
|
|
596
607
|
function syncKpiGroup(group, legendText, cards) {
|
|
@@ -955,11 +955,7 @@ function verifyCommandKey(command) {
|
|
|
955
955
|
.sort(([left], [right]) => left.localeCompare(right))
|
|
956
956
|
.map(([key, value]) => `${key}=${value}`)
|
|
957
957
|
.join("\0");
|
|
958
|
-
return [
|
|
959
|
-
cwdKey,
|
|
960
|
-
normalizedArgs.join("\0"),
|
|
961
|
-
envKey,
|
|
962
|
-
].join("\u0001");
|
|
958
|
+
return [cwdKey, normalizedArgs.join("\0"), envKey].join("\u0001");
|
|
963
959
|
}
|
|
964
960
|
function normalizeVerifyCommandArgs(args) {
|
|
965
961
|
if (args.length === 3 &&
|
|
@@ -1055,7 +1051,10 @@ function canonicalizeVerificationCommands(commands) {
|
|
|
1055
1051
|
const canonicalKey = verifyCommandKey(command);
|
|
1056
1052
|
const retained = byKey.get(canonicalKey);
|
|
1057
1053
|
if (!retained) {
|
|
1058
|
-
byKey.set(canonicalKey, {
|
|
1054
|
+
byKey.set(canonicalKey, {
|
|
1055
|
+
...command,
|
|
1056
|
+
env: command.env ? { ...command.env } : undefined,
|
|
1057
|
+
});
|
|
1059
1058
|
merged.set(canonicalKey, {
|
|
1060
1059
|
canonicalKey,
|
|
1061
1060
|
keptLabel: command.label,
|
|
@@ -1079,7 +1078,10 @@ function canonicalizeVerificationCommands(commands) {
|
|
|
1079
1078
|
? retained.filter((command) => {
|
|
1080
1079
|
if (!isCheckRepoCoveredCommand(command))
|
|
1081
1080
|
return true;
|
|
1082
|
-
covered.push({
|
|
1081
|
+
covered.push({
|
|
1082
|
+
aggregateLabel: aggregate.label,
|
|
1083
|
+
coveredLabel: command.label,
|
|
1084
|
+
});
|
|
1083
1085
|
return false;
|
|
1084
1086
|
})
|
|
1085
1087
|
: retained;
|
|
@@ -1110,7 +1112,8 @@ function assertVerificationPlanPreflight(input) {
|
|
|
1110
1112
|
}
|
|
1111
1113
|
function isWithinRepo(root, candidate) {
|
|
1112
1114
|
const relative = path.relative(root, candidate);
|
|
1113
|
-
return relative === "" ||
|
|
1115
|
+
return (relative === "" ||
|
|
1116
|
+
(!relative.startsWith("..") && !path.isAbsolute(relative)));
|
|
1114
1117
|
}
|
|
1115
1118
|
function taskVerifyCommands(repoRoot, taskConfig) {
|
|
1116
1119
|
if (!repoRoot)
|
|
@@ -1173,7 +1176,10 @@ function buildVerifyEvidence(input) {
|
|
|
1173
1176
|
selectionReasons: selectedCommands?.map((command) => input.phase === "intermediate" && isFullSuiteVerifyCommand(command)
|
|
1174
1177
|
? `${command.label}: deferred-heavy`
|
|
1175
1178
|
: `${command.label}: kept`) ?? [],
|
|
1176
|
-
preflight: selectedCommands?.map((command) => ({
|
|
1179
|
+
preflight: selectedCommands?.map((command) => ({
|
|
1180
|
+
label: command.label,
|
|
1181
|
+
status: "ok",
|
|
1182
|
+
})) ?? [],
|
|
1177
1183
|
commandTimeoutMs: input.commandTimeoutMs,
|
|
1178
1184
|
totalTimeoutBudgetMs: commandCount * input.commandTimeoutMs,
|
|
1179
1185
|
finalFullRequired: input.finalFullRequired,
|
|
@@ -1433,9 +1439,7 @@ function buildSourceContextBlock(sources) {
|
|
|
1433
1439
|
const requirementExcerpt = excerptMarkdown(sources.requirementMarkdown, {
|
|
1434
1440
|
sourceRef: requirementRef,
|
|
1435
1441
|
});
|
|
1436
|
-
const boundReadPaths = [
|
|
1437
|
-
`- requirement: ${requirementRef}`,
|
|
1438
|
-
];
|
|
1442
|
+
const boundReadPaths = [`- requirement: ${requirementRef}`];
|
|
1439
1443
|
const parts = [
|
|
1440
1444
|
`## Task source: 需求.md`,
|
|
1441
1445
|
`Bound readPath (use for Pi read-tool calls): ${requirementRef}`,
|
|
@@ -1571,10 +1575,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
|
1571
1575
|
...taskVerifyCommands(repoRoot, taskConfig),
|
|
1572
1576
|
...adapterIntermediate,
|
|
1573
1577
|
],
|
|
1574
|
-
final: [
|
|
1575
|
-
...taskVerifyCommands(repoRoot, taskConfig),
|
|
1576
|
-
...adapterFinal,
|
|
1577
|
-
],
|
|
1578
|
+
final: [...taskVerifyCommands(repoRoot, taskConfig), ...adapterFinal],
|
|
1578
1579
|
};
|
|
1579
1580
|
if (verifyCommands.final.length === 0) {
|
|
1580
1581
|
throw new Error("adapter returned no final verification commands");
|
|
@@ -2637,7 +2638,10 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
2637
2638
|
frontendPrewriteGate: {
|
|
2638
2639
|
schemaVersion: 1,
|
|
2639
2640
|
planFromNodeId: "frontend-contract-json-pi",
|
|
2640
|
-
planFallbackFromNodeIds: [
|
|
2641
|
+
planFallbackFromNodeIds: [
|
|
2642
|
+
"frontend-plan-revision-pi",
|
|
2643
|
+
"frontend-plan-pi",
|
|
2644
|
+
],
|
|
2641
2645
|
reviewFromNodeId: "frontend-final-design-review-pi",
|
|
2642
2646
|
reviewFallbackFromNodeIds: ["frontend-design-review-pi"],
|
|
2643
2647
|
requiredRequirementIds: requirementIds,
|
|
@@ -3520,7 +3524,7 @@ const seen=new Set();const modules=[];
|
|
|
3520
3524
|
for(const r of raw){const st=norm(r);if(!seen.has(st)){seen.add(st);modules.push({stem:st});}}
|
|
3521
3525
|
process.stdout.write(JSON.stringify({modules}));
|
|
3522
3526
|
`;
|
|
3523
|
-
const encoded = Buffer.from(script,
|
|
3527
|
+
const encoded = Buffer.from(script, "utf8").toString("base64");
|
|
3524
3528
|
return `node -e "eval(Buffer.from('${encoded}','base64').toString('utf8'))"`;
|
|
3525
3529
|
}
|
|
3526
3530
|
const BACKEND_TEST_SKILLS_BY_ROLE = {
|
|
@@ -3632,7 +3636,9 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3632
3636
|
forbiddenPaths: forbidden,
|
|
3633
3637
|
outputContract: "Serial aggregate of sharded Markdown module case-card writers. Each child writes exactly one testcase/md/<stem>.md with its own 16K Pi budget.",
|
|
3634
3638
|
subtask_prompt: "Expand the README module manifest into one sharded Markdown writer child per module and run them serially. Child failures fail-close the map barrier.",
|
|
3635
|
-
static: {
|
|
3639
|
+
static: {
|
|
3640
|
+
resultMarkdown: "Backend-test Markdown case-card map expansion barrier.",
|
|
3641
|
+
},
|
|
3636
3642
|
dynamicExpansion: {
|
|
3637
3643
|
type: "map_agent",
|
|
3638
3644
|
workflowNodeId: "generate-backend-md-cases-map",
|
|
@@ -3663,7 +3669,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3663
3669
|
"Output budget protocol (hard, max output <=16K per turn): Never paste full Matrix, other modules' case bodies, or source text into assistant chat. Each write/edit tool call touches at most one file (this module). Compact tables/lists are required; omitting required sections or in-scope variants is forbidden. If a Completeness Gate / OUTPUT_LIMIT_RECOVERY retry is injected, continue only listed target paths.",
|
|
3664
3670
|
"The first non-empty response line must be exactly IMPLEMENTATION_OUTCOME: changed after the module file has been written, or IMPLEMENTATION_OUTCOME: blocked when precise missing evidence prevents safe generation. already-satisfied is not valid for this node.",
|
|
3665
3671
|
"Write human-readable content in Simplified Chinese by default. Keep English only for machine-readable IDs and technical literals such as Case/AC/REQ/BR IDs, HTTP methods, paths, field names, enum values, commands, filenames, code symbols and exact source citations.",
|
|
3666
|
-
|
|
3672
|
+
'Write the module {{item.stem}} as readable case cards covering every in-scope rule/Test Point the README Coverage Matrix assigns to this module. Every case starts with `## BE-<MODULE>-<NNN>|<中文用例名称>`. `<NNN>` is exactly three zero-padded digits (`001`, `002`, ...), never two digits (`01`), a bare number, or an alphabetic suffix such as `011A`. Every case must include `### 覆盖规则`, `### 测试点`, `### 场景类型`, `### 前置条件`, `### 操作步骤`, `### 预期结果`, and `### 自动化映射` Do not group cases under "## 测试类 ..." (or any h2 grouping) headings that force Cases down to h3; each Case must be a direct h2 (`##`), and its seven sections must be h3 (`###`) children of that Case. If you need to convey a pytest class, state it inside the Case\'s `### 自动化映射` instead. Forbidden: `## 测试类 X` then `### BE-PD-001` and `### 覆盖规则` at the same h3 level. Required: `## BE-PD-001` then `### 覆盖规则`.; `覆盖规则` and `测试点` must reference exact Matrix Rule Keys/Test Points. Add `测试目的`, `验收标准`, `需求依据`, and `测试数据` for readable evidence. The `验收标准` section must list the exact applicable `AC-...` IDs, and every explicit task AC must appear in at least one Case. Every automatable case explicitly names its target pytest script and exactly one primary symbol so traceability scans only that script/symbol.',
|
|
3667
3673
|
"Name this module file with the stable lowercase business stem `{{item.stem}}` (filename `testcase/md/{{item.stem}}.md`). Do not use Case-ID-like module filenames. For every automatable case, `自动化映射` must name exactly `testcase/test_{{item.stem}}.py`, where the module stem is this Markdown filename without `.md`, lowercased, with non-alphanumeric characters replaced by underscores. Example: `health` → `testcase/test_health.py`; `resource_notes` → `testcase/test_resource_notes.py`. Never invent a different pytest path in Markdown than the module stem implies.",
|
|
3668
3674
|
"Every Case must keep at least one numbered executable line under `### 操作步骤`; a compact variant/result table may follow but must not replace the numbered action anchor. Keep numbered/bulleted independently assertable results under `### 预期结果`. The exact `### 操作步骤` and `### 预期结果` headings must remain present for every Case, including compact/table-based Cases; never compress later Cases by dropping required headings. Every result must name the observable HTTP status, response field/value, state transition or membership condition, never vague wording such as ‘符合预期’.",
|
|
3669
3675
|
"In every `自动化映射`, use exactly these machine-readable list labels: `脚本`, `primary symbol`, `变体测试点`, `场景断言测试点`, `横切证据测试点`. Each Test Point from `### 测试点` must appear in exactly one binding list, and every Test Point named in any binding list must also be declared in that Case's `### 测试点`; write `无` for an empty list. A variant Test Point is atomic: one exact endpoint/input/precondition/outcome row equals one exact pytest item and one exact TP ID. If a parameter table has five rows, declare five distinct variant TP IDs in Markdown; never declare one family TP and append row suffixes only in pytest. Classify as `variant` only when endpoint, request input, precondition business state, or expected outcome genuinely changes and therefore needs an independent pytest parameter item. Classify CRUD checkpoints, status/body/header/schema assertions and multiple checks over the same response/journey as `assertion`; classify shared HTTP logging/redaction/truncation evidence as `cross-cutting`. Never create a Test Point merely to parameterize a checkpoint. Every non-cross-cutting TP ID is owned by exactly one Case; when the same response/schema/error assertion is needed in different Cases, use distinct Case-specific TP IDs instead of reusing one assertion TP across Cases. Keep the script path identical to the module one-to-one path and declare exactly one primary symbol named with the canonical Case prefix, for example `BE-RN-003` → `test_BE_RN_003_<description>`; non-Case-prefixed primary symbols are forbidden because parameterized item association must remain deterministic. For redaction scenarios, list sensitive header/field key names only. Never write any header-name-and-value pair, credential placeholder, fake token, anti-example, or other secret-shaped literal in Markdown; state only that a test-only value is supplied at runtime and omitted. Put implementation-only restrictions in a concise `<details>` block rather than dominating the main case flow. Use only environment-supported fixtures/targets/isolation, record evidence gaps in Chinese, and do not emit JSON, pytest, or execute commands.",
|
|
@@ -3760,7 +3766,9 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
3760
3766
|
forbiddenPaths: forbidden,
|
|
3761
3767
|
outputContract: "Serial aggregate of sharded pytest module writers. Each child writes exactly one self-contained testcase/test_<stem>.py with its own 16K Pi budget and no generated shared-asset dependency.",
|
|
3762
3768
|
subtask_prompt: "Expand the README module manifest into one sharded pytest writer child per module and run them serially. Child failures fail-close the map barrier.",
|
|
3763
|
-
static: {
|
|
3769
|
+
static: {
|
|
3770
|
+
resultMarkdown: "Backend-test pytest module map expansion barrier.",
|
|
3771
|
+
},
|
|
3764
3772
|
dynamicExpansion: {
|
|
3765
3773
|
type: "map_agent",
|
|
3766
3774
|
workflowNodeId: "generate-backend-pytest-cases-map",
|
|
@@ -4156,7 +4164,10 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4156
4164
|
subtask_prompt: "Prepare frontend-test package: materialize standard-scenarios.v1.json into the RAG package.",
|
|
4157
4165
|
shell: {
|
|
4158
4166
|
commands: [
|
|
4159
|
-
[
|
|
4167
|
+
[
|
|
4168
|
+
"node -e",
|
|
4169
|
+
JSON.stringify("const fs=require('fs'),path=require('path');const dest='testcase/frontend/rag/standard-scenarios.v1.json';const candidates=[path.join('docs','templates','frontend-test-standard-scenarios.v1.json')];let src=null;for(const c of candidates){if(fs.existsSync(c)){src=c;break;}}fs.mkdirSync(path.dirname(dest),{recursive:true});if(src){fs.copyFileSync(src,dest);process.stdout.write(JSON.stringify({status:'copied',from:src,to:dest}));}else{const minimal={schemaVersion:1,id:'frontend-test-standard-scenarios-v1',scenarios:[{id:'STD-FE-SMOKE-ENTRY',title:'入口可打开',category:'smoke',priority:'must',testPoints:['open'],minCases:1}]};fs.writeFileSync(dest,JSON.stringify(minimal,null,2)+'\n');process.stdout.write(JSON.stringify({status:'fallback',to:dest}));}"),
|
|
4170
|
+
].join(" "),
|
|
4160
4171
|
],
|
|
4161
4172
|
cwd: ".",
|
|
4162
4173
|
timeoutMs: 60_000,
|
|
@@ -4403,7 +4414,12 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4403
4414
|
subtask_prompt: "Select frontend-test cases eligible for bounded rerun.",
|
|
4404
4415
|
shell: {
|
|
4405
4416
|
commands: [
|
|
4406
|
-
[
|
|
4417
|
+
[
|
|
4418
|
+
"node -e",
|
|
4419
|
+
JSON.stringify("const fs=require('fs'),path=require('path');const manifestPath='testcase/frontend/cases/manifest.json';if(!fs.existsSync(manifestPath)){process.stdout.write(JSON.stringify({cases:[]}));process.exit(0);}const manifest=JSON.parse(fs.readFileSync(manifestPath,'utf8'));const cases=[];for(const c of (manifest.cases||[])){const evidenceDir=(c.evidenceDir||('testcase/frontend/evidence/'+c.caseId+'/')).replace(/\\/+$/,'')+'/';const resultPath=path.join(evidenceDir,'case-result.json');const execPath=path.join(evidenceDir,'execution.md');let reason=null;let attempt=0;let missing=false;if(!fs.existsSync(resultPath)){missing=true;reason='missing-result-files';}else{try{const r=JSON.parse(fs.readFileSync(resultPath,'utf8'));attempt=Number(r.rerunAttempt||0)||0;if(r.status==='blocked')reason='blocked';if(!r.status){missing=true;reason='missing-result-files';}}catch(e){missing=true;reason='missing-result-files';}}if(!fs.existsSync(execPath)&&reason!=='blocked'){missing=true;reason=reason||'missing-result-files';}const should=(reason==='blocked'||missing)&&attempt<" +
|
|
4420
|
+
maxRerunAttempts +
|
|
4421
|
+
";if(should){cases.push({caseId:c.caseId,casePath:c.casePath||('testcase/frontend/cases/'+c.caseId+'.md'),evidenceDir,dimension:c.dimension||'core',acIds:c.acIds||[],rerunAttempt:attempt+1,reason:reason||'blocked'});}}fs.mkdirSync('testcase/frontend/evidence',{recursive:true});fs.writeFileSync('testcase/frontend/evidence/rerun-candidates.json',JSON.stringify({schemaVersion:1,cases},null,2)+'\\n');process.stdout.write(JSON.stringify({cases}));"),
|
|
4422
|
+
].join(" "),
|
|
4407
4423
|
],
|
|
4408
4424
|
cwd: ".",
|
|
4409
4425
|
timeoutMs: 120_000,
|
|
@@ -4419,7 +4435,9 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4419
4435
|
forbiddenPaths: forbidden,
|
|
4420
4436
|
outputContract: "Serial rerun of blocked/missing-result frontend cases.",
|
|
4421
4437
|
subtask_prompt: "Expand rerun candidates into serial browser case children.",
|
|
4422
|
-
static: {
|
|
4438
|
+
static: {
|
|
4439
|
+
resultMarkdown: "Frontend case rerun map expansion barrier.",
|
|
4440
|
+
},
|
|
4423
4441
|
dynamicExpansion: {
|
|
4424
4442
|
type: "map_agent",
|
|
4425
4443
|
workflowNodeId: "rerun-frontend-cases-map",
|
|
@@ -4467,7 +4485,11 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4467
4485
|
}
|
|
4468
4486
|
tasks.push({
|
|
4469
4487
|
id: "finalize-frontend-test-result-shell",
|
|
4470
|
-
depends_on: [
|
|
4488
|
+
depends_on: [
|
|
4489
|
+
maxRerunAttempts > 0
|
|
4490
|
+
? "rerun-frontend-cases-map"
|
|
4491
|
+
: "execute-frontend-cases-map",
|
|
4492
|
+
],
|
|
4471
4493
|
role: "verifier",
|
|
4472
4494
|
executor: "shell",
|
|
4473
4495
|
complexity: "LOW",
|
|
@@ -4528,7 +4550,10 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
4528
4550
|
if (enableRetrospect) {
|
|
4529
4551
|
tasks.push({
|
|
4530
4552
|
id: "frontend-test-retrospect-pi",
|
|
4531
|
-
depends_on: [
|
|
4553
|
+
depends_on: [
|
|
4554
|
+
"finalize-frontend-test-result-shell",
|
|
4555
|
+
"frontend-test-reports-shell",
|
|
4556
|
+
],
|
|
4532
4557
|
role: "closeout",
|
|
4533
4558
|
executor: "pi",
|
|
4534
4559
|
toolProfile: "write",
|
|
@@ -6049,13 +6074,15 @@ async function buildSoftVerifyNode(sources) {
|
|
|
6049
6074
|
const implementId = implementationNodeId();
|
|
6050
6075
|
const strategy = resolveDagVerifyStrategy(sources.taskConfig, "1");
|
|
6051
6076
|
const fallbackCommands = sources.repoRoot
|
|
6052
|
-
? (await discoverFrontendFallbackVerifyCommands(sources.repoRoot))
|
|
6077
|
+
? (await discoverFrontendFallbackVerifyCommands(sources.repoRoot))
|
|
6078
|
+
.staticCommands
|
|
6053
6079
|
: [];
|
|
6054
6080
|
const candidatePlan = canonicalizeVerificationCommands([
|
|
6055
6081
|
...taskVerifyCommands(sources.repoRoot, sources.taskConfig),
|
|
6056
6082
|
...(sources.verifyCommands?.intermediate ?? []),
|
|
6057
6083
|
]);
|
|
6058
|
-
const focusedCandidates = candidatePlan.commands.filter((command) => !isFullSuiteVerifyCommand(command) &&
|
|
6084
|
+
const focusedCandidates = candidatePlan.commands.filter((command) => !isFullSuiteVerifyCommand(command) &&
|
|
6085
|
+
!isFrontendLintVerifyCommand(command));
|
|
6059
6086
|
const quota = verifyQuotaLimit(strategy.intermediateQuota ?? "full");
|
|
6060
6087
|
const focusedIntermediate = quota
|
|
6061
6088
|
? focusedCandidates.slice(0, quota)
|
|
@@ -6072,7 +6099,9 @@ async function buildSoftVerifyNode(sources) {
|
|
|
6072
6099
|
});
|
|
6073
6100
|
const commands = commandsWithoutFallback.length > 0
|
|
6074
6101
|
? commandsWithoutFallback
|
|
6075
|
-
: [
|
|
6102
|
+
: [
|
|
6103
|
+
"node -e \"console.log('No project verification command configured; verification skipped')\"",
|
|
6104
|
+
];
|
|
6076
6105
|
return {
|
|
6077
6106
|
id: "soft-verify-shell",
|
|
6078
6107
|
depends_on: [implementId],
|
|
@@ -2,44 +2,46 @@
|
|
|
2
2
|
|
|
3
3
|
用于 Phase 8 campaign / 发布门禁。勾选前必须有新鲜命令输出或 report ref。
|
|
4
4
|
|
|
5
|
+
正式 pin(2026-08-09):`@tea-agent/loop-agent@0.32.0`(tag `v0.32.0` / `b4d9e500`)。scorecard 在 burn-in / live 门禁完成前保持 **NOT_READY**。证据:`docs/reports/feature/2026-08-09-agent-worker-production-readiness-v1.md`。
|
|
6
|
+
|
|
5
7
|
## 包络与身份
|
|
6
8
|
|
|
7
|
-
- [
|
|
8
|
-
- [
|
|
9
|
-
- [
|
|
10
|
-
- [
|
|
9
|
+
- [x] 支持包络已冻结(单机/单用户/单仓)
|
|
10
|
+
- [x] controller packageVersion + fingerprint 已记录(`0.32.0`;npm shasum `8932749b1583523043a9889c84f9d316e57e451d`;canary fingerprint `sha256:b08404f5…`)
|
|
11
|
+
- [x] published canary / 隔离 install **无** npm link / workspace bin(live burn-in 仍须每日复核)
|
|
12
|
+
- [x] protocol/capabilities preflight(unit)通过;live preflight 仍待
|
|
11
13
|
|
|
12
14
|
## Ownership / Attempt
|
|
13
15
|
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
16
|
+
- [x] Running 投影不依赖 terminal ledger(unit / Phase 1)
|
|
17
|
+
- [x] Task-scoped events 含 featureId(unit)
|
|
18
|
+
- [x] dual Feature 同名 Task 隔离(unit C2)
|
|
19
|
+
- [x] ownership warning 按根因聚合(≤1 finding/根因)(unit)
|
|
18
20
|
|
|
19
21
|
## Lifecycle / Recovery
|
|
20
22
|
|
|
21
|
-
- [
|
|
22
|
-
- [
|
|
23
|
-
- [
|
|
23
|
+
- [x] safe run-error → Failed → `task retry` 无需 mark-failed(unit)
|
|
24
|
+
- [x] Blocked 保留 contract/human 语义(unit)
|
|
25
|
+
- [x] resume / retry / reconcile / revise / decide 决策一致(unit)
|
|
24
26
|
|
|
25
27
|
## Lease
|
|
26
28
|
|
|
27
|
-
- [
|
|
28
|
-
- [ ] begin/finalize crash injection 有 doctor finding
|
|
29
|
-
- [
|
|
29
|
+
- [x] `{featureId, taskId}` 单 active writer(unit C3)
|
|
30
|
+
- [ ] begin/finalize crash injection 有 doctor finding(进程级仍缺)
|
|
31
|
+
- [x] 不盲目偷取 live DAG lease(unit reclaim)
|
|
30
32
|
|
|
31
33
|
## Acceptance / Delivery
|
|
32
34
|
|
|
33
|
-
- [
|
|
34
|
-
- [
|
|
35
|
-
- [ ] verify-final → delivery → closeout 可收口
|
|
36
|
-
- [
|
|
35
|
+
- [x] Task Done ≠ AC covered(unit)
|
|
36
|
+
- [x] `feature verify-final` 唯一 writer(unit)
|
|
37
|
+
- [ ] verify-final → delivery → closeout **live** 可收口
|
|
38
|
+
- [x] dirty HEAD / tamper fail-closed(unit)
|
|
37
39
|
|
|
38
40
|
## Campaign / SLO
|
|
39
41
|
|
|
40
|
-
- [
|
|
41
|
-
- [ ] burn-in 窗口达标(7 日或 30 Attempt
|
|
42
|
-
- [ ] SLO
|
|
43
|
-
- [
|
|
44
|
-
- [ ] scorecard: READY 或合法 CONDITIONALLY_READY
|
|
45
|
-
- [
|
|
42
|
+
- [x] C0–C11 已执行或明确 blocked-external(partial:unit + published canary;live Pi blocked)
|
|
43
|
+
- [ ] burn-in 窗口达标(7 日或 30 Attempt)— **OPEN day 0**
|
|
44
|
+
- [ ] SLO 分子/分母已记录(正式样本不足)
|
|
45
|
+
- [x] promote + rollback drill 完成(install-level vs `0.31.1`)
|
|
46
|
+
- [ ] scorecard: READY 或合法 CONDITIONALLY_READY — 当前 **`NOT_READY`**
|
|
47
|
+
- [x] open P0 = 0
|
|
@@ -302,6 +302,11 @@
|
|
|
302
302
|
"additionalProperties": false,
|
|
303
303
|
"required": ["model"],
|
|
304
304
|
"properties": {
|
|
305
|
+
"provider": {
|
|
306
|
+
"type": "string",
|
|
307
|
+
"minLength": 1,
|
|
308
|
+
"description": "可选 Pi provider id。与 model 组合为无歧义的 provider/model 路由;省略时从 model 字符串中的 provider/model 或运行时 available 列表推断。"
|
|
309
|
+
},
|
|
305
310
|
"model": {
|
|
306
311
|
"type": "string",
|
|
307
312
|
"minLength": 1,
|
package/harness.json
CHANGED
|
@@ -79,10 +79,10 @@
|
|
|
79
79
|
},
|
|
80
80
|
"executors": {
|
|
81
81
|
"pi": {
|
|
82
|
-
"description": "Pi
|
|
83
|
-
"LOW": "minimax-m3",
|
|
84
|
-
"MED": "grok-4.5",
|
|
85
|
-
"HIGH": "gpt-5.6-sol"
|
|
82
|
+
"description": "Pi 负责规划、评审、诊断;当 DAG toolProfile=write 时也可做有界写入。模型按复杂度三档配置,格式为 provider/model 字符串(例:wizard-local/grok-4.5);斜杠前为 Pi provider,后为 modelId,勿只写裸 modelId。",
|
|
83
|
+
"LOW": "wizard-local/minimax-m3",
|
|
84
|
+
"MED": "wizard-local/grok-4.5",
|
|
85
|
+
"HIGH": "wizard-local/gpt-5.6-sol"
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tea-agent/loop-agent",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.33.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"loop-agent": "bin/loop-agent.js",
|
|
@@ -46,11 +46,12 @@
|
|
|
46
46
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
47
47
|
"brand:sync": "node scripts/sync-brand-assets.mjs",
|
|
48
48
|
"build": "npm run brand:sync && npm run clean && tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');const p='dist/worker/observe/static';fs.mkdirSync(p,{recursive:true});fs.cpSync('src/worker/observe/static',p,{recursive:true});\" && npm run console:build",
|
|
49
|
-
"console:
|
|
49
|
+
"console:typecheck": "tsc -p src/worker/console/tsconfig.json",
|
|
50
|
+
"console:build": "npm run console:typecheck && vite build --config src/worker/console/vite.config.ts",
|
|
50
51
|
"prepack": "npm run build",
|
|
51
52
|
"prepublishOnly": "node scripts/check-npm-publish-policy.mjs && npm run typecheck && npm test && npm run build",
|
|
52
|
-
"lint": "
|
|
53
|
-
"typecheck": "tsc --noEmit",
|
|
53
|
+
"lint": "npm run typecheck",
|
|
54
|
+
"typecheck": "tsc --noEmit && npm run console:typecheck",
|
|
54
55
|
"test": "node scripts/run-tests.mjs",
|
|
55
56
|
"test:host": "vitest run test/init-upgrade.test.ts --maxWorkers=1 -t \"records and reuses|controller-authorized semantic merge|authority-sensitive\"",
|
|
56
57
|
"test:fast": "vitest run --config vitest.fast.config.ts",
|