@tea-agent/loop-agent 0.13.0-beta.0 → 0.14.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 -0
- package/CHANGELOG.md +56 -305
- package/README.md +13 -19
- package/dist/commands/init.js +92 -23
- package/dist/executors/pi-event-serializer.js +33 -11
- package/dist/executors/shell-executor.js +200 -21
- package/dist/infrastructure/evaluation/candidate-store.js +5 -1
- package/dist/worker/observe/spec-evidence.js +19 -10
- package/dist/worker/observe/static/app.js +4 -3
- package/dist/worker/observe/static/constants.js +10 -2
- package/dist/worker/observe/static/dag-helpers.js +37 -8
- package/dist/worker/observe/static/dom.js +159 -0
- package/dist/worker/observe/static/format-pool.d.ts +71 -0
- package/dist/worker/observe/static/format-pool.js +67 -0
- package/dist/worker/observe/static/format.js +27 -2
- package/dist/worker/observe/static/index.html +76 -34
- package/dist/worker/observe/static/kpi.js +12 -6
- package/dist/worker/observe/static/markdown-render.js +124 -0
- package/dist/worker/observe/static/shell-chrome.js +8 -2
- package/dist/worker/observe/static/state.js +20 -0
- package/dist/worker/observe/static/styles.css +662 -60
- package/dist/worker/observe/static/views/dag-inspector.js +65 -142
- package/dist/worker/observe/static/views/dag.js +9 -0
- package/dist/worker/observe/static/views/dashboard.js +512 -269
- package/dist/worker/observe/static/views/pool.js +595 -237
- package/dist/worker/observe/static/views/session-timeline.js +577 -11
- package/dist/workflows/dag/backend-test-case-manifest.js +503 -0
- package/dist/workflows/dag/backend-test-execution-contract.js +353 -0
- package/dist/workflows/dag/backend-test-result-contract.js +568 -0
- package/dist/workflows/dag/decision-envelope.js +57 -2
- package/dist/workflows/dag/frontend-implementation-contract.js +240 -0
- package/dist/workflows/dag/frontend-project-capability.js +309 -0
- package/dist/workflows/dag/frontend-repair.js +341 -0
- package/dist/workflows/dag/frontend-risk.js +161 -0
- package/dist/workflows/dag/frontend-verification-trace.js +190 -0
- package/dist/workflows/dag/init-hybrid.js +1020 -125
- package/dist/workflows/dag/repair-artifact.js +43 -3
- package/dist/workflows/dag/skill-instructions.js +4 -2
- package/dist/workflows/dag/types.js +29 -8
- package/docs/README.md +2 -0
- package/docs/agent-dag-recovery-playbook.md +3 -3
- package/docs/agent-dag-runner.md +3 -3
- package/docs/architecture/README.md +3 -3
- package/docs/architecture/dag-execution.md +1 -1
- package/docs/architecture/evolution.md +13 -13
- package/docs/architecture/facts-and-state.md +1 -1
- package/docs/architecture/runtime-boundaries.md +7 -7
- package/docs/architecture/system-overview.md +3 -3
- package/docs/architecture/worker-and-feature.md +3 -3
- package/docs/design/README.md +7 -7
- package/docs/development-principles.md +4 -4
- package/docs/exec-plans/active/README.md +2 -4
- package/docs/exec-plans/completed/README.md +29 -6
- package/docs/feature-workflow.md +57 -32
- package/docs/init-surface.manifest.json +21 -3
- package/docs/loop-agent-harness.md +8 -8
- package/docs/production-readiness.md +1 -1
- package/docs/progress/README.md +20 -3
- package/docs/reports/README.md +53 -7
- package/docs/templates/agent-dag.supervised-implementation.json +127 -8
- package/docs/templates/backend-test-case-manifest.schema.json +190 -0
- package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
- package/docs/templates/backend-test-dag.generate-pytest.prompt.md +6 -4
- package/docs/templates/backend-test-dag.json +269 -21
- package/docs/templates/backend-test-dag.retrospect.prompt.md +44 -30
- package/docs/templates/backend-test-dag.review-cases.prompt.md +6 -4
- package/docs/templates/backend-test-execution.schema.json +133 -0
- package/docs/templates/backend-test-result.schema.json +99 -0
- package/docs/templates/branch-merge-report.md +93 -0
- package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
- package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
- package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
- package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
- package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
- package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
- package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
- package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
- package/docs/templates/frontend-eval/metrics.md +138 -0
- package/docs/templates/frontend-eval/smoke-targets.md +53 -0
- package/docs/templates/frontend-implementation-contract.schema.json +27 -0
- package/docs/verification-matrix.md +1 -1
- package/examples/decision-gate-agent-dag.json +4 -4
- package/examples/hybrid-loop-agent-dag.json +1 -1
- package/package.json +2 -2
- package/skills/ai-engineering-context/SKILL.md +2 -2
- package/skills/browser-tools/SKILL.md +196 -0
- package/skills/browser-tools/browser-content.js +103 -0
- package/skills/browser-tools/browser-cookies.js +35 -0
- package/skills/browser-tools/browser-eval.js +53 -0
- package/skills/browser-tools/browser-hn-scraper.js +108 -0
- package/skills/browser-tools/browser-nav.js +44 -0
- package/skills/browser-tools/browser-pick.js +162 -0
- package/skills/browser-tools/browser-screenshot.js +34 -0
- package/skills/browser-tools/browser-start.js +86 -0
- package/skills/browser-tools/package-lock.json +2556 -0
- package/skills/browser-tools/package.json +19 -0
- package/skills/frontend-implementation/SKILL.md +3 -1
- package/skills/frontend-implementation/references/node-contracts.md +17 -66
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/grill-with-docs/SKILL.md +5 -5
- package/skills/grill-with-docs/adr-format.md +3 -3
- package/skills/init-capability-evolution/SKILL.md +5 -5
- package/skills/loop-agent/SKILL.md +5 -5
- package/skills/loop-agent/references/README.md +3 -3
- package/skills/loop-agent/references/command-reference.md +39 -17
- package/skills/loop-agent/references/docs-converge.md +15 -15
- package/skills/loop-agent/references/harness-policy.md +2 -2
- package/skills/loop-agent/references/hybrid-dag.md +20 -15
- package/skills/loop-agent/references/multi-worktree.md +1 -1
- package/skills/loop-agent/references/orchestrator-and-interventions.md +8 -8
- package/skills/loop-agent/references/task-workflow.md +1 -1
- package/skills/loop-agent/references/verification-and-failure-handling.md +6 -4
- package/skills/requesting-code-review/SKILL.md +1 -1
- package/skills/systematic-debugging/CREATION-LOG.md +3 -3
- package/skills/systematic-debugging/SKILL.md +1 -1
- package/skills/systematic-debugging/test-academic.md +1 -1
- package/skills/systematic-debugging/test-pressure-1.md +1 -1
- package/skills/systematic-debugging/test-pressure-2.md +1 -1
- package/skills/systematic-debugging/test-pressure-3.md +1 -1
- package/skills/verification-before-completion/SKILL.md +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -679,9 +679,34 @@ function buildManagedAgentsBlock(input) {
|
|
|
679
679
|
"- 保留无关的用户改动,不要回退自己没有做的修改。",
|
|
680
680
|
"- 委托模型写入前,必须把写入边界写成结构化 `task.json.allowedPaths` / `task.json.forbiddenPaths`,再审查生成 DAG 的 writer `writeSet`;不要只依赖 `source/执行约束.md` 的自然语言约束。",
|
|
681
681
|
"",
|
|
682
|
+
"### 自然语言入口路由",
|
|
683
|
+
"",
|
|
684
|
+
"用户用中文表达意图时,按以下三类入口路由;只读与写入必须严格区分。",
|
|
685
|
+
"",
|
|
686
|
+
"| 用户表达 | 入口 | 执行动作 |",
|
|
687
|
+
"|---|---|---|",
|
|
688
|
+
"| loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 初始化 | 完成确定性初始化闭环 |",
|
|
689
|
+
"| 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 | 更新校验 | 只读报告,不写入 |",
|
|
690
|
+
"| 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 安全更新 | check-update 后只执行确定性安全动作 |",
|
|
691
|
+
"",
|
|
692
|
+
"**更新校验(只读)**:用户说“初始化更新校验”“检查初始化更新”时,只读执行下面命令,汇报 deterministic actions、model merge、human decisions 和 next steps。**这一步不得自动执行 `apply-safe` 或模型合并。**",
|
|
693
|
+
"",
|
|
694
|
+
"```bash",
|
|
695
|
+
"loop-agent init check-update --repo-root . --markdown",
|
|
696
|
+
"```",
|
|
697
|
+
"",
|
|
698
|
+
"**安全更新(写入型)**:用户明确说“初始化安全更新”“应用初始化更新”时,先 check-update,再执行确定性安全动作;surface 缺失时先 bootstrap,存在 human decisions 时停下等用户决策。model merge 任务必须带 allowed/forbidden paths,且不得自动决策。安全更新完成后必须复查 `loop-agent init doctor --repo-root .`、`loop-agent inspect --repo-root .`、`loop-agent docs audit --repo-root .` 与目标项目治理检查(如 `bash scripts/check-repo.sh`)。",
|
|
699
|
+
"",
|
|
700
|
+
"```bash",
|
|
701
|
+
"loop-agent init check-update --repo-root . --markdown",
|
|
702
|
+
"loop-agent init update --repo-root . --apply-safe",
|
|
703
|
+
"```",
|
|
704
|
+
"",
|
|
705
|
+
"仅当 check-update 报告 surface 缺失时才执行 `loop-agent init update --repo-root . --bootstrap-surface`。",
|
|
706
|
+
"",
|
|
682
707
|
"### 初始化闭环",
|
|
683
708
|
"",
|
|
684
|
-
|
|
709
|
+
`当用户要求初始化 loop-agent(“初始化 loop-agent”“loop agent 初始化”“loop agent初始化”“loop-agent 初始化”),或发现 \`README.md\` / \`${input.governanceRoot}/verification-matrix.md\` 仍停留在通用初始化状态时,模型应在同一轮工作中完成初始化闭环,不要把补全文档和验证矩阵留给用户交互。`,
|
|
685
710
|
"",
|
|
686
711
|
"标准流程:",
|
|
687
712
|
"",
|
|
@@ -731,6 +756,22 @@ function buildManagedAgentsBlock(input) {
|
|
|
731
756
|
"",
|
|
732
757
|
"执行 DAG 前必须审查 profile routing、governance profile、writer 的 `writeSet`、`allowedPaths`、`forbiddenPaths`、shell verification 和 decision gate mode。不要执行占位或过宽的写入范围。",
|
|
733
758
|
"",
|
|
759
|
+
"### 任务类型路由(taskKind)",
|
|
760
|
+
"",
|
|
761
|
+
"- 用户明确提出后端测试、接口/API 测试、pytest,或语境明确为后端的自动化测试时,必须把 `.harness/tasks/<task-id>/task.json` 的 `taskKind` 设置为 `\"backend-test\"`,不得保留默认 `standard`。",
|
|
762
|
+
"- `backend-test` 是 `taskKind`,不是 `--profile` 的可选值;运行 `dag run-task` 时继续使用 `--profile auto`,也可按需显式选择 `minimal` / `standard` / `reviewed` / `supervised`,不要把业务模板名当作 profile。",
|
|
763
|
+
"- 仅出现“自动化测试”且无法判断前后端时,先阅读任务源与目标项目技术栈再决定,禁止无条件路由到 `backend-test`。",
|
|
764
|
+
"",
|
|
765
|
+
"### 运行看板(只读)",
|
|
766
|
+
"",
|
|
767
|
+
"启动本地只读运行看板:",
|
|
768
|
+
"",
|
|
769
|
+
"```bash",
|
|
770
|
+
"agent-worker observe serve --repo . --port 8787",
|
|
771
|
+
"```",
|
|
772
|
+
"",
|
|
773
|
+
"浏览器打开 `http://127.0.0.1:8787/`。看板只读,默认只绑定本机 `127.0.0.1`;不要直接暴露到公开网络。",
|
|
774
|
+
"",
|
|
734
775
|
"### DAG 诊断与收口",
|
|
735
776
|
"",
|
|
736
777
|
"执行后优先使用 `loop-agent dag report --run-id <run-id> --markdown` 读取 run facts;失败或 paused run 使用 `loop-agent dag doctor --run-id <run-id> --markdown` 诊断。失败 DAG run 不应写成成功 closeout,应使用 `loop-agent dag closeout-draft --run-id <run-id>` 生成 failure handoff,保留 what failed、evidence、classification、recommended follow-up、safe retry conditions 和 human decision needed。",
|
|
@@ -822,6 +863,9 @@ export function buildManagedGitignoreBlock() {
|
|
|
822
863
|
".harness/task-pool/*",
|
|
823
864
|
".task-pool/",
|
|
824
865
|
"",
|
|
866
|
+
"# repo-local skill dependencies installed inside projected skills",
|
|
867
|
+
".agents/skills/*/node_modules/",
|
|
868
|
+
"",
|
|
825
869
|
"# multi-worktree parallel mode",
|
|
826
870
|
".worktrees/",
|
|
827
871
|
GITIGNORE_BLOCK_END,
|
|
@@ -1242,7 +1286,7 @@ async function buildCurrentSurfaceState(input) {
|
|
|
1242
1286
|
repoRoot: input.repoRoot,
|
|
1243
1287
|
projectName: input.projectName,
|
|
1244
1288
|
governanceRoot: input.governanceRoot,
|
|
1245
|
-
entry,
|
|
1289
|
+
entry: manifestEntry,
|
|
1246
1290
|
});
|
|
1247
1291
|
const sourceSha256 = desired.content === undefined ? undefined : sha256Text(desired.content);
|
|
1248
1292
|
const base = {
|
|
@@ -1318,6 +1362,19 @@ async function buildCurrentSurfaceState(input) {
|
|
|
1318
1362
|
}
|
|
1319
1363
|
async function writeInitSurfaceState(input) {
|
|
1320
1364
|
const state = await buildCurrentSurfaceState(input);
|
|
1365
|
+
if (input.stateKind === "recorded" && input.preserveOwnershipFrom) {
|
|
1366
|
+
for (const [pathName, file] of Object.entries(state.files)) {
|
|
1367
|
+
if (file.relationship !== "local-existing-unknown")
|
|
1368
|
+
continue;
|
|
1369
|
+
const priorHash = input.preserveOwnershipFrom.files[pathName]?.currentSha256;
|
|
1370
|
+
if (priorHash !== undefined) {
|
|
1371
|
+
file.currentSha256 = priorHash;
|
|
1372
|
+
}
|
|
1373
|
+
else {
|
|
1374
|
+
delete file.currentSha256;
|
|
1375
|
+
}
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1321
1378
|
const target = path.join(input.repoRoot, INIT_SURFACE_STATE_PATH);
|
|
1322
1379
|
await mkdir(path.dirname(target), { recursive: true });
|
|
1323
1380
|
await writeFile(target, `${JSON.stringify(state, null, 2)}\n`, "utf-8");
|
|
@@ -1867,9 +1924,28 @@ function buildTargetFeatureWorkflow(input) {
|
|
|
1867
1924
|
"",
|
|
1868
1925
|
"- `taskKind: \"frontend-implementation\"` explicitly selects the frontend DAG for compatibility or intentional override. Optional `frontendMock` config sets `policy: auto|required|disabled`, an existing `serviceRoot`, and generation-time-frozen `verifyCommands`; an unsafe or incomplete explicit required contract produces an assessment-only DAG with no writer, while a complete required contract adds Mock-specific verification only when trusted commands exist. Auto API tasks without a native service may use an existing browser interception harness or reversible request adapter, then continue through static and behavior verification.",
|
|
1869
1926
|
"- `taskKind: \"backend-test\"` selects the dedicated backend test DAG. Its Pi nodes analyze requirements, generate and review backend cases, generate pytest, and retrospect on results; shell gate/execution nodes enforce the review verdict and run the target project's pytest. The backend test templates (`backend-test-dag.json` and the `backend-test-dag.*.prompt.md` files) ship inside the loop-agent package as static references and are projected to target projects under the governance `templates/` directory.",
|
|
1927
|
+
"- `taskKind: \"knowledge-sync\"` selects the Feature-scoped test-knowledge write-back DAG (collect → draft → validate → apply → pointer). Bind `featureId` in `task.json` (or hardConstraints / requirement text). It writes only under `features/<featureId>/…` after final verification evidence exists.",
|
|
1928
|
+
"- `taskKind: \"knowledge-graph-bootstrap\"` selects the business knowledge-graph bootstrap DAG (preflight → inventory → propose → validate → review → gate → promote → materialize). AI writes only `knowledge/bootstrap/staging/**`; promote is merge-new-only.",
|
|
1870
1929
|
"- `taskKind: \"frontend-test\"` selects the FE-test RAG DAG. It writes a traceable frontend RAG package and Markdown case manifest, then executes manifest cases serially with `playwright-cli` in isolated test environments and retains per-case evidence. It never generates pytest or Playwright source code. `frontendTest.maxCasesPerBatch` defaults to 20 (maximum 50); optional `maxTokensPerCase` and `maxTotalTokens` stop only later cases after a completed case's token usage is recorded, marking them `blocked: token-budget-exhausted`. Generated browser startup uses `playwright-cli open --browser=chrome --headed <base-url>`; the generic playwright-cli skill is unchanged.",
|
|
1871
1930
|
"- Only eligible read-only Pi nodes (planner, scout, reviewer, verifier, closeout with no write-capable tool profile) receive the conservative automatic retry policy. Supervisor, implementer, writer, docs-only, dynamic, shell, static, and decision-gate nodes are not retried automatically. Eligible nodes cannot write repository files; the controller only records immutable attempt evidence under `.harness/dag-runs/<state>/<run-id>/<node-id>/attempt-<n>.json`.",
|
|
1872
1931
|
"",
|
|
1932
|
+
"Use the package-backed public knowledge CLI for graph operations. Do not require target projects to run package-only kb runtime scripts:",
|
|
1933
|
+
"",
|
|
1934
|
+
"```bash",
|
|
1935
|
+
"loop-agent knowledge graph-init --product-name <name>",
|
|
1936
|
+
"# edit knowledge/bootstrap/scope.yaml, then:",
|
|
1937
|
+
"loop-agent dag run-task <task-id> # taskKind: knowledge-graph-bootstrap",
|
|
1938
|
+
"loop-agent knowledge query --mode by_feature --feature F-2026-004 --json",
|
|
1939
|
+
"loop-agent knowledge query --mode by_id --id SVC-order --json",
|
|
1940
|
+
"loop-agent knowledge query --mode search --text \"keyword\" --json",
|
|
1941
|
+
"loop-agent knowledge graph-incremental-prepare --feature F-2026-004 --service <service>",
|
|
1942
|
+
"# review the prepared scope/staging; for a manual reviewed promotion:",
|
|
1943
|
+
"loop-agent knowledge graph-promote",
|
|
1944
|
+
"loop-agent knowledge graph-materialize",
|
|
1945
|
+
"```",
|
|
1946
|
+
"",
|
|
1947
|
+
"Daily Feature test-knowledge write-back still uses `taskKind: \"knowledge-sync\"` with a bound `featureId`, separate from graph bootstrap/incremental entry points.",
|
|
1948
|
+
"",
|
|
1873
1949
|
"## Verification",
|
|
1874
1950
|
"",
|
|
1875
1951
|
`Use \`${input.governanceRoot}/verification-matrix.md\` to choose the narrowest command that proves the claim.`,
|
|
@@ -2563,38 +2639,31 @@ export async function applyInitUpdate(input) {
|
|
|
2563
2639
|
});
|
|
2564
2640
|
}
|
|
2565
2641
|
if (input.applySafe) {
|
|
2642
|
+
const existingSurface = await readExistingSurfaceState(repoRoot);
|
|
2643
|
+
// Preserve source strength: recorded stays recorded so unchanged owned files
|
|
2644
|
+
// remain deterministic refresh candidates; bootstrap/inferred stays inferred.
|
|
2645
|
+
const preservedStateKind = existingSurface?.stateKind === "recorded" ? "recorded" : "inferred-baseline";
|
|
2566
2646
|
const report = await checkInitUpdate({ repoRoot, projectName, governanceRoot });
|
|
2567
2647
|
for (const action of report.deterministicActions) {
|
|
2568
2648
|
if (action.type === "bootstrap-surface") {
|
|
2569
2649
|
skipped.push(action);
|
|
2570
2650
|
continue;
|
|
2571
2651
|
}
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
action.type !== "migrate-harness-governance-root" &&
|
|
2576
|
-
action.type !== "migrate-harness-model-fields" &&
|
|
2577
|
-
action.type !== "migrate-owned-file" &&
|
|
2578
|
-
action.type !== "remove-owned-file" &&
|
|
2579
|
-
action.type !== "remove-empty-directory") {
|
|
2580
|
-
const currentState = await buildCurrentSurfaceState({
|
|
2581
|
-
repoRoot,
|
|
2582
|
-
projectName,
|
|
2583
|
-
governanceRoot,
|
|
2584
|
-
stateKind: "inferred-baseline",
|
|
2585
|
-
});
|
|
2586
|
-
const relationship = currentState.files[action.path]?.relationship;
|
|
2587
|
-
if (relationship === "local-existing-unknown") {
|
|
2588
|
-
skipped.push(action);
|
|
2589
|
-
continue;
|
|
2590
|
-
}
|
|
2591
|
-
}
|
|
2652
|
+
// The report was produced immediately before this batch. Each action also
|
|
2653
|
+
// performs its own target check; missing-file writes refuse existing paths,
|
|
2654
|
+
// while managed blocks merge into the latest user-authored content.
|
|
2592
2655
|
if (await applySafeAction({ repoRoot, projectName, governanceRoot, action }))
|
|
2593
2656
|
applied.push(action);
|
|
2594
2657
|
else
|
|
2595
2658
|
skipped.push(action);
|
|
2596
2659
|
}
|
|
2597
|
-
await writeInitSurfaceState({
|
|
2660
|
+
await writeInitSurfaceState({
|
|
2661
|
+
repoRoot,
|
|
2662
|
+
projectName,
|
|
2663
|
+
governanceRoot,
|
|
2664
|
+
stateKind: preservedStateKind,
|
|
2665
|
+
preserveOwnershipFrom: existingSurface,
|
|
2666
|
+
});
|
|
2598
2667
|
}
|
|
2599
2668
|
return {
|
|
2600
2669
|
applied,
|
|
@@ -5,39 +5,61 @@
|
|
|
5
5
|
function isRecord(value) {
|
|
6
6
|
return typeof value === 'object' && value !== null;
|
|
7
7
|
}
|
|
8
|
+
function unifyToolPayloadFields(event) {
|
|
9
|
+
const out = { ...event };
|
|
10
|
+
const args = out.args ?? out.input ?? out.toolInput;
|
|
11
|
+
if (args !== undefined) {
|
|
12
|
+
out.args = args;
|
|
13
|
+
if (out.input === undefined)
|
|
14
|
+
out.input = args;
|
|
15
|
+
}
|
|
16
|
+
const result = out.result ?? out.toolResult;
|
|
17
|
+
if (result !== undefined) {
|
|
18
|
+
out.result = result;
|
|
19
|
+
}
|
|
20
|
+
return out;
|
|
21
|
+
}
|
|
8
22
|
/** Map SDK event shapes to the JSONL format emitted by `pi -p --mode json`. */
|
|
9
23
|
export function normalizePiSessionEvent(event) {
|
|
10
24
|
const type = typeof event.type === 'string' ? event.type : undefined;
|
|
11
25
|
if (type === 'assistant_message' && isRecord(event.message)) {
|
|
12
|
-
return {
|
|
26
|
+
return unifyToolPayloadFields({
|
|
13
27
|
type: 'turn_end',
|
|
14
28
|
message: event.message,
|
|
15
29
|
usage: event.usage,
|
|
16
|
-
};
|
|
30
|
+
});
|
|
17
31
|
}
|
|
18
32
|
if (type === 'tool_start' && typeof event.toolName === 'string') {
|
|
19
|
-
|
|
33
|
+
const args = event.args ?? event.input ?? event.toolInput;
|
|
34
|
+
return unifyToolPayloadFields({
|
|
20
35
|
type: 'tool_execution_start',
|
|
21
36
|
toolName: event.toolName,
|
|
22
37
|
toolCallId: event.toolCallId,
|
|
23
|
-
input:
|
|
24
|
-
|
|
38
|
+
input: args,
|
|
39
|
+
args,
|
|
40
|
+
});
|
|
25
41
|
}
|
|
26
42
|
if (type === 'tool_end' && typeof event.toolName === 'string') {
|
|
27
|
-
|
|
43
|
+
const result = event.result ?? event.toolResult;
|
|
44
|
+
return unifyToolPayloadFields({
|
|
28
45
|
type: 'tool_execution_end',
|
|
29
46
|
toolName: event.toolName,
|
|
30
47
|
toolCallId: event.toolCallId,
|
|
31
48
|
isError: event.isError,
|
|
32
|
-
|
|
49
|
+
...(result !== undefined ? { result } : {}),
|
|
50
|
+
});
|
|
33
51
|
}
|
|
34
|
-
return event;
|
|
52
|
+
return unifyToolPayloadFields(event);
|
|
35
53
|
}
|
|
36
54
|
/** Serialize one SDK session event as a single JSONL line. */
|
|
37
|
-
export function serializeSessionEvent(event) {
|
|
38
|
-
|
|
55
|
+
export function serializeSessionEvent(event, options) {
|
|
56
|
+
const normalized = normalizePiSessionEvent(event);
|
|
57
|
+
if (typeof normalized.recordedAt !== 'string') {
|
|
58
|
+
normalized.recordedAt = options?.recordedAt ?? new Date().toISOString();
|
|
59
|
+
}
|
|
60
|
+
return JSON.stringify(normalized);
|
|
39
61
|
}
|
|
40
62
|
/** Serialize multiple events into newline-delimited JSON (no trailing newline). */
|
|
41
63
|
export function serializeSessionEvents(events) {
|
|
42
|
-
return events.map(serializeSessionEvent).join('\n');
|
|
64
|
+
return events.map((event) => serializeSessionEvent(event)).join('\n');
|
|
43
65
|
}
|
|
@@ -3,8 +3,14 @@ import { appendFileSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { writeDagNodeTextArtifact } from "../infrastructure/harness/artifact-store.js";
|
|
5
5
|
import { truncateOutput } from "../shared/output-truncation.js";
|
|
6
|
-
import { buildRequirementCoverageGateShellCommand, expandShellPreset, buildVerdictGateShellCommand } from "./shell-presets.js";
|
|
6
|
+
import { buildRequirementCoverageGateShellCommand, expandShellPreset, buildVerdictGateShellCommand, } from "./shell-presets.js";
|
|
7
7
|
import { materializeBackendTestAnalysisContract } from "../workflows/dag/backend-test-analysis-contract.js";
|
|
8
|
+
import { materializeFrontendImplementationContract } from "../workflows/dag/frontend-implementation-contract.js";
|
|
9
|
+
import { formatFrontendVerificationTraceStdout, runFrontendVerificationTraceGate, } from "../workflows/dag/frontend-verification-trace.js";
|
|
10
|
+
import { formatFrontendFailureAssessStdout, formatFrontendRepairContractStdout, runFrontendFailureAssessGate, runFrontendRepairContractGate, } from "../workflows/dag/frontend-repair.js";
|
|
11
|
+
import { formatTraceabilityGateStdout, materializeBackendTestCaseManifest, runBackendTestTraceabilityGate, } from "../workflows/dag/backend-test-case-manifest.js";
|
|
12
|
+
import { materializeBackendTestExecutionContract } from "../workflows/dag/backend-test-execution-contract.js";
|
|
13
|
+
import { materializeBackendTestResultFromRunDir } from "../workflows/dag/backend-test-result-contract.js";
|
|
8
14
|
import { pathsChangedDuringRun, snapshotGitStatusPorcelain, validateShellWriteGuard, } from "./shell-write-guard.js";
|
|
9
15
|
import { buildShellProcessEnv } from "./shell-verification.js";
|
|
10
16
|
const DEFAULT_SHELL_TIMEOUT_MS = 300_000;
|
|
@@ -24,7 +30,9 @@ function resolveBashExecutable() {
|
|
|
24
30
|
path.join(process.env["ProgramFiles(x86)"] ?? "C:\\Program Files (x86)", "Git", "bin", "bash.exe"),
|
|
25
31
|
path.join(process.env.LocalAppData ?? "", "Programs", "Git", "bin", "bash.exe"),
|
|
26
32
|
].filter(Boolean);
|
|
27
|
-
return candidates.find((candidate) => existsSync(candidate)) ??
|
|
33
|
+
return (candidates.find((candidate) => existsSync(candidate)) ??
|
|
34
|
+
envCandidate ??
|
|
35
|
+
"bash");
|
|
28
36
|
}
|
|
29
37
|
function isWithinRoot(root, candidate) {
|
|
30
38
|
const relative = path.relative(root, candidate);
|
|
@@ -48,7 +56,12 @@ export function resolveShellCommands(shell) {
|
|
|
48
56
|
const fromRequirementCoverageGate = shell.requirementCoverageGate
|
|
49
57
|
? [buildRequirementCoverageGateShellCommand(shell.requirementCoverageGate)]
|
|
50
58
|
: [];
|
|
51
|
-
return [
|
|
59
|
+
return [
|
|
60
|
+
...fromPreset,
|
|
61
|
+
...explicit,
|
|
62
|
+
...fromVerdictGate,
|
|
63
|
+
...fromRequirementCoverageGate,
|
|
64
|
+
];
|
|
52
65
|
}
|
|
53
66
|
async function readGitStatusPorcelain(cwd) {
|
|
54
67
|
return new Promise((resolve, reject) => {
|
|
@@ -145,13 +158,16 @@ export async function executeShellCommand(input) {
|
|
|
145
158
|
});
|
|
146
159
|
});
|
|
147
160
|
child.on("close", (code) => {
|
|
161
|
+
let failureCategory = "nonzero-exit";
|
|
162
|
+
if (timedOut) {
|
|
163
|
+
failureCategory = "timeout";
|
|
164
|
+
}
|
|
165
|
+
else if (code === 0) {
|
|
166
|
+
failureCategory = "success";
|
|
167
|
+
}
|
|
148
168
|
finish({
|
|
149
169
|
exitCode: code,
|
|
150
|
-
failureCategory
|
|
151
|
-
? "timeout"
|
|
152
|
-
: code === 0
|
|
153
|
-
? "success"
|
|
154
|
-
: "nonzero-exit",
|
|
170
|
+
failureCategory,
|
|
155
171
|
ok: code === 0 && !timedOut,
|
|
156
172
|
});
|
|
157
173
|
});
|
|
@@ -286,16 +302,164 @@ export async function executeDagShellNode(input, meta) {
|
|
|
286
302
|
if (shell?.jsonArtifactGate) {
|
|
287
303
|
const started = Date.now();
|
|
288
304
|
try {
|
|
289
|
-
const
|
|
305
|
+
const gate = shell.jsonArtifactGate;
|
|
306
|
+
let artifact;
|
|
307
|
+
switch (gate.schemaId) {
|
|
308
|
+
case "backend-test-analysis-v1":
|
|
309
|
+
artifact = await materializeBackendTestAnalysisContract({
|
|
310
|
+
runDir: meta.runDir,
|
|
311
|
+
fromNodeId: gate.fromNodeId,
|
|
312
|
+
artifactName: gate.artifactName,
|
|
313
|
+
outputDir: gate.outputDir,
|
|
314
|
+
sourceBinding: meta.spec.sourceBinding,
|
|
315
|
+
});
|
|
316
|
+
break;
|
|
317
|
+
case "backend-test-execution-v1":
|
|
318
|
+
artifact = await materializeBackendTestExecutionContract({
|
|
319
|
+
runDir: meta.runDir,
|
|
320
|
+
fromNodeId: gate.fromNodeId,
|
|
321
|
+
artifactName: gate.artifactName,
|
|
322
|
+
outputDir: gate.outputDir,
|
|
323
|
+
});
|
|
324
|
+
break;
|
|
325
|
+
case "backend-test-result-v1":
|
|
326
|
+
artifact = await materializeBackendTestResultFromRunDir({
|
|
327
|
+
runDir: meta.runDir,
|
|
328
|
+
fromNodeId: gate.fromNodeId,
|
|
329
|
+
artifactName: gate.artifactName,
|
|
330
|
+
outputDir: gate.outputDir,
|
|
331
|
+
});
|
|
332
|
+
break;
|
|
333
|
+
case "backend-test-case-manifest-v1":
|
|
334
|
+
artifact = await materializeBackendTestCaseManifest({
|
|
335
|
+
runDir: meta.runDir,
|
|
336
|
+
fromNodeId: gate.fromNodeId,
|
|
337
|
+
artifactName: gate.artifactName,
|
|
338
|
+
outputDir: gate.outputDir,
|
|
339
|
+
sourceBinding: meta.spec.sourceBinding,
|
|
340
|
+
});
|
|
341
|
+
break;
|
|
342
|
+
case "frontend-implementation-contract-v1":
|
|
343
|
+
artifact = await materializeFrontendImplementationContract({
|
|
344
|
+
runDir: meta.runDir,
|
|
345
|
+
fromNodeId: gate.fromNodeId,
|
|
346
|
+
artifactName: gate.artifactName,
|
|
347
|
+
outputDir: gate.outputDir,
|
|
348
|
+
sourceBinding: meta.spec.sourceBinding,
|
|
349
|
+
});
|
|
350
|
+
break;
|
|
351
|
+
default:
|
|
352
|
+
throw new Error(`unsupported jsonArtifactGate.schemaId: ${String(gate.schemaId)}`);
|
|
353
|
+
}
|
|
354
|
+
return {
|
|
355
|
+
ok: true,
|
|
356
|
+
stdout: `Structured artifact: ${artifact.path}\nSchema: ${artifact.schemaId}\nSHA-256: ${artifact.sha256}`,
|
|
357
|
+
stderr: "",
|
|
358
|
+
failureCategory: "success",
|
|
359
|
+
durationMs: Date.now() - started,
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
catch (error) {
|
|
363
|
+
return {
|
|
364
|
+
ok: false,
|
|
365
|
+
stdout: "",
|
|
366
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
367
|
+
failureCategory: "invalid-output",
|
|
368
|
+
durationMs: Date.now() - started,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (shell?.commands?.length === 1 &&
|
|
373
|
+
shell.commands[0] === "backend-test-traceability-gate") {
|
|
374
|
+
const started = Date.now();
|
|
375
|
+
try {
|
|
376
|
+
const result = await runBackendTestTraceabilityGate({
|
|
290
377
|
runDir: meta.runDir,
|
|
291
|
-
|
|
292
|
-
artifactName: shell.jsonArtifactGate.artifactName,
|
|
293
|
-
outputDir: shell.jsonArtifactGate.outputDir,
|
|
294
|
-
sourceBinding: meta.spec.sourceBinding,
|
|
378
|
+
workspaceRoot: input.cwd,
|
|
295
379
|
});
|
|
296
380
|
return {
|
|
297
381
|
ok: true,
|
|
298
|
-
stdout:
|
|
382
|
+
stdout: formatTraceabilityGateStdout(result),
|
|
383
|
+
stderr: "",
|
|
384
|
+
failureCategory: "success",
|
|
385
|
+
durationMs: Date.now() - started,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
catch (error) {
|
|
389
|
+
return {
|
|
390
|
+
ok: false,
|
|
391
|
+
stdout: "",
|
|
392
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
393
|
+
failureCategory: "invalid-output",
|
|
394
|
+
durationMs: Date.now() - started,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (shell?.commands?.length === 1 &&
|
|
399
|
+
shell.commands[0] === "frontend-verification-trace-gate") {
|
|
400
|
+
const started = Date.now();
|
|
401
|
+
try {
|
|
402
|
+
const result = await runFrontendVerificationTraceGate({
|
|
403
|
+
runDir: meta.runDir,
|
|
404
|
+
workspaceRoot: input.cwd,
|
|
405
|
+
});
|
|
406
|
+
return {
|
|
407
|
+
ok: true,
|
|
408
|
+
stdout: formatFrontendVerificationTraceStdout(result),
|
|
409
|
+
stderr: "",
|
|
410
|
+
failureCategory: "success",
|
|
411
|
+
durationMs: Date.now() - started,
|
|
412
|
+
};
|
|
413
|
+
}
|
|
414
|
+
catch (error) {
|
|
415
|
+
const stderr = error instanceof Error ? error.message : String(error);
|
|
416
|
+
const recordNonZero = shell.nonZeroExitPolicy === "record";
|
|
417
|
+
return {
|
|
418
|
+
ok: recordNonZero,
|
|
419
|
+
stdout: recordNonZero
|
|
420
|
+
? "nonZeroExitPolicy=record failureCategory=invalid-output\n"
|
|
421
|
+
: "",
|
|
422
|
+
stderr,
|
|
423
|
+
failureCategory: "invalid-output",
|
|
424
|
+
durationMs: Date.now() - started,
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
if (shell?.commands?.length === 1 &&
|
|
429
|
+
shell.commands[0] === "frontend-failure-assess-gate") {
|
|
430
|
+
const started = Date.now();
|
|
431
|
+
try {
|
|
432
|
+
const result = await runFrontendFailureAssessGate({
|
|
433
|
+
runDir: meta.runDir,
|
|
434
|
+
});
|
|
435
|
+
return {
|
|
436
|
+
ok: true,
|
|
437
|
+
stdout: formatFrontendFailureAssessStdout(result),
|
|
438
|
+
stderr: "",
|
|
439
|
+
failureCategory: "success",
|
|
440
|
+
durationMs: Date.now() - started,
|
|
441
|
+
};
|
|
442
|
+
}
|
|
443
|
+
catch (error) {
|
|
444
|
+
return {
|
|
445
|
+
ok: false,
|
|
446
|
+
stdout: "",
|
|
447
|
+
stderr: error instanceof Error ? error.message : String(error),
|
|
448
|
+
failureCategory: "invalid-output",
|
|
449
|
+
durationMs: Date.now() - started,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
if (shell?.commands?.length === 1 &&
|
|
454
|
+
shell.commands[0] === "frontend-repair-contract-gate") {
|
|
455
|
+
const started = Date.now();
|
|
456
|
+
try {
|
|
457
|
+
const result = await runFrontendRepairContractGate({
|
|
458
|
+
runDir: meta.runDir,
|
|
459
|
+
});
|
|
460
|
+
return {
|
|
461
|
+
ok: true,
|
|
462
|
+
stdout: formatFrontendRepairContractStdout(result.assessment),
|
|
299
463
|
stderr: "",
|
|
300
464
|
failureCategory: "success",
|
|
301
465
|
durationMs: Date.now() - started,
|
|
@@ -362,7 +526,8 @@ export async function executeDagShellNode(input, meta) {
|
|
|
362
526
|
}
|
|
363
527
|
}
|
|
364
528
|
const commandsOk = results.every((result) => result.ok);
|
|
365
|
-
const
|
|
529
|
+
const recordNonZero = shell.nonZeroExitPolicy === "record";
|
|
530
|
+
const ok = (commandsOk || recordNonZero) && writeGuardOk;
|
|
366
531
|
const firstFailure = results.find((result) => !result.ok);
|
|
367
532
|
await writeDagNodeTextArtifact(meta.runDir, input.task.id, "result.summary.md", buildShellResultSummaryMarkdown({
|
|
368
533
|
nodeId: input.task.id,
|
|
@@ -376,15 +541,29 @@ export async function executeDagShellNode(input, meta) {
|
|
|
376
541
|
if (!writeGuardOk) {
|
|
377
542
|
stderrParts.push(`write guard failed: ${writeGuardViolations.join(", ")}`);
|
|
378
543
|
}
|
|
544
|
+
let failureCategory = "success";
|
|
545
|
+
if (!commandsOk) {
|
|
546
|
+
failureCategory = firstFailure?.failureCategory ?? "nonzero-exit";
|
|
547
|
+
}
|
|
548
|
+
else if (!writeGuardOk) {
|
|
549
|
+
failureCategory = "write-guard";
|
|
550
|
+
}
|
|
551
|
+
const commandResults = results.map((result) => ({
|
|
552
|
+
ok: result.ok,
|
|
553
|
+
exitCode: result.exitCode,
|
|
554
|
+
failureCategory: result.failureCategory,
|
|
555
|
+
command: result.command,
|
|
556
|
+
}));
|
|
557
|
+
const recordPrefix = recordNonZero && !commandsOk
|
|
558
|
+
? `nonZeroExitPolicy=record exitCode=${firstFailure?.exitCode ?? "null"}\n`
|
|
559
|
+
: "";
|
|
379
560
|
return {
|
|
380
561
|
ok,
|
|
381
|
-
stdout: aggregate.stdout
|
|
562
|
+
stdout: `${recordPrefix}${aggregate.stdout}`,
|
|
382
563
|
stderr: stderrParts.filter(Boolean).join("\n\n"),
|
|
383
|
-
failureCategory
|
|
384
|
-
? (firstFailure?.failureCategory ?? "nonzero-exit")
|
|
385
|
-
: !writeGuardOk
|
|
386
|
-
? "write-guard"
|
|
387
|
-
: "success",
|
|
564
|
+
failureCategory,
|
|
388
565
|
durationMs: Date.now() - started,
|
|
566
|
+
// extra field consumed by node-execution if supported; also embedded in stdout marker
|
|
567
|
+
...{ commandResults },
|
|
389
568
|
};
|
|
390
569
|
}
|
|
@@ -272,7 +272,11 @@ export async function registerCandidateManifest(input) {
|
|
|
272
272
|
}
|
|
273
273
|
catch (error) {
|
|
274
274
|
const code = error.code;
|
|
275
|
-
|
|
275
|
+
const isCreateRace = code === "EEXIST" ||
|
|
276
|
+
code === "ENOTEMPTY" ||
|
|
277
|
+
(code === "EPERM" &&
|
|
278
|
+
(await pathExists(candidateDir(repoRoot, manifest.candidateId))));
|
|
279
|
+
if (!isCreateRace)
|
|
276
280
|
throw error;
|
|
277
281
|
await rm(stagingDir, { recursive: true, force: true });
|
|
278
282
|
return readExistingRegistration(repoRoot, manifest, manifestPath, lifecyclePath);
|
|
@@ -87,7 +87,11 @@ function resolveToolName(event) {
|
|
|
87
87
|
return (event.toolName ?? "").toLowerCase();
|
|
88
88
|
}
|
|
89
89
|
function eventTimestamp(event) {
|
|
90
|
-
return event.timestamp ?? event.at;
|
|
90
|
+
return event.timestamp ?? event.at ?? event.recordedAt;
|
|
91
|
+
}
|
|
92
|
+
function eventToolArgs(event) {
|
|
93
|
+
const args = event.args ?? event.toolInput ?? event.input;
|
|
94
|
+
return args && typeof args === "object" && !Array.isArray(args) ? args : {};
|
|
91
95
|
}
|
|
92
96
|
/**
|
|
93
97
|
* Parse a tool input path to a repo-relative path.
|
|
@@ -150,7 +154,7 @@ export async function extractSpecEvidence(repoRoot, dagRunId, nodeId) {
|
|
|
150
154
|
const type = event.type;
|
|
151
155
|
const toolName = resolveToolName(event);
|
|
152
156
|
const toolCallId = event.toolCallId;
|
|
153
|
-
const toolInput = event
|
|
157
|
+
const toolInput = eventToolArgs(event);
|
|
154
158
|
const ts = eventTimestamp(event);
|
|
155
159
|
// Collect tool_execution_start events by toolCallId
|
|
156
160
|
if (type === "tool_execution_start" && toolCallId) {
|
|
@@ -159,18 +163,21 @@ export async function extractSpecEvidence(repoRoot, dagRunId, nodeId) {
|
|
|
159
163
|
}
|
|
160
164
|
// Process tool_execution_end events
|
|
161
165
|
if (type === "tool_execution_end") {
|
|
166
|
+
const start = toolCallId ? startMap.get(toolCallId) : undefined;
|
|
167
|
+
const pairedInput = start?.input ?? toolInput;
|
|
162
168
|
// Successful read calls: must have matching start event and no error
|
|
163
169
|
if (toolName === "read") {
|
|
164
170
|
const isErrored = event.isError === true ||
|
|
165
171
|
event.toolResult?.error != null ||
|
|
166
|
-
event.toolResult?.ok === false
|
|
172
|
+
event.toolResult?.ok === false ||
|
|
173
|
+
event.result?.error != null ||
|
|
174
|
+
event.result?.ok === false;
|
|
167
175
|
if (isErrored)
|
|
168
176
|
continue;
|
|
169
177
|
let filePath;
|
|
170
178
|
// Only paired read calls qualify: must have toolCallId, matching start
|
|
171
179
|
// with same toolName, and start must be a "read" tool.
|
|
172
180
|
if (toolCallId) {
|
|
173
|
-
const start = startMap.get(toolCallId);
|
|
174
181
|
if (start && start.toolName === "read") {
|
|
175
182
|
const startInput = start.input ?? {};
|
|
176
183
|
filePath = typeof startInput.path === "string" ? startInput.path : undefined;
|
|
@@ -188,18 +195,20 @@ export async function extractSpecEvidence(repoRoot, dagRunId, nodeId) {
|
|
|
188
195
|
}
|
|
189
196
|
// Search/scan tool calls (grep, find, ls, glob)
|
|
190
197
|
if (["grep", "find", "ls", "glob"].includes(toolName)) {
|
|
191
|
-
const query = typeof
|
|
192
|
-
?
|
|
193
|
-
: typeof
|
|
194
|
-
?
|
|
195
|
-
: typeof
|
|
196
|
-
?
|
|
198
|
+
const query = typeof pairedInput.pattern === "string"
|
|
199
|
+
? pairedInput.pattern
|
|
200
|
+
: typeof pairedInput.query === "string"
|
|
201
|
+
? pairedInput.query
|
|
202
|
+
: typeof pairedInput.path === "string"
|
|
203
|
+
? pairedInput.path
|
|
197
204
|
: toolName;
|
|
198
205
|
searches.push({
|
|
199
206
|
tool: toolName,
|
|
200
207
|
query,
|
|
201
208
|
timestamp: ts,
|
|
202
209
|
});
|
|
210
|
+
if (toolCallId)
|
|
211
|
+
startMap.delete(toolCallId);
|
|
203
212
|
}
|
|
204
213
|
// Knowledge base connector calls
|
|
205
214
|
if (isKnowledgeBaseTool(toolName)) {
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
renderViewState,
|
|
30
30
|
mountViewState,
|
|
31
31
|
viewStateDescriptor,
|
|
32
|
+
uiState,
|
|
32
33
|
} from "./state.js";
|
|
33
34
|
import { sortPoolTasks, poolStatusCounts } from "./format-pool.js";
|
|
34
35
|
import {
|
|
@@ -46,7 +47,7 @@ import {
|
|
|
46
47
|
} from "./dag-model.js";
|
|
47
48
|
|
|
48
49
|
import { renderFailures } from "./views/failures.js";
|
|
49
|
-
import {
|
|
50
|
+
import { startPoolView } from "./views/pool.js";
|
|
50
51
|
import { startDashboardPolling } from "./views/dashboard.js";
|
|
51
52
|
import { renderFeatureDetail } from "./views/feature.js";
|
|
52
53
|
import { renderBatch } from "./views/batch.js";
|
|
@@ -126,7 +127,7 @@ function route() {
|
|
|
126
127
|
} else if (r.view === "failures") {
|
|
127
128
|
void renderFailures();
|
|
128
129
|
} else if (r.view === "pool") {
|
|
129
|
-
|
|
130
|
+
startPoolView();
|
|
130
131
|
} else if (r.view === "task") {
|
|
131
132
|
startTaskPolling(r.featureId, r.taskId);
|
|
132
133
|
} else if (r.view === "feature") {
|
|
@@ -144,7 +145,7 @@ if (typeof window !== "undefined") {
|
|
|
144
145
|
if (r.view === "dashboard") startDashboardPolling();
|
|
145
146
|
if (r.view === "run") startRunPolling(r.workerRunId, false);
|
|
146
147
|
if (r.view === "dag") startDagPolling(r.dagRunId, false);
|
|
147
|
-
if (r.view === "pool")
|
|
148
|
+
if (r.view === "pool" && uiState.poolAutoRefresh) startPoolView();
|
|
148
149
|
if (r.view === "task") startTaskPolling(r.featureId, r.taskId);
|
|
149
150
|
});
|
|
150
151
|
}
|