@tea-agent/loop-agent 0.9.0 → 0.10.0-alpha.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.
Files changed (183) hide show
  1. package/AGENTS.md +8 -0
  2. package/CHANGELOG.md +92 -11
  3. package/README.md +69 -5
  4. package/dist/application/dag/args.js +4 -14
  5. package/dist/application/dag/generate-task-dag.js +32 -2
  6. package/dist/application/dag/run-dag.js +1 -27
  7. package/dist/application/dag/validate-dag.js +2 -2
  8. package/dist/application/loop/run-action.js +0 -4
  9. package/dist/cli/command-definitions.js +7 -11
  10. package/dist/cli/program.js +9 -21
  11. package/dist/commands/cursor-prompt.js +42 -82
  12. package/dist/commands/dag-approve.js +36 -0
  13. package/dist/commands/dag-reconcile-run.js +118 -0
  14. package/dist/commands/delegate.js +75 -77
  15. package/dist/commands/doctor.js +0 -18
  16. package/dist/commands/init.js +60 -40
  17. package/dist/commands/instructions.js +7 -10
  18. package/dist/commands/loop.js +4 -20
  19. package/dist/executors/config-core.js +0 -51
  20. package/dist/executors/dag-pi-executor.js +1 -1
  21. package/dist/executors/dag.js +0 -1
  22. package/dist/executors/index.js +0 -2
  23. package/dist/executors/model-routing.js +9 -9
  24. package/dist/executors/shell-executor.js +1 -1
  25. package/dist/governance/checks.js +6 -3
  26. package/dist/governance/manifest-types.js +33 -2
  27. package/dist/infrastructure/harness/loop-action-store.js +0 -3
  28. package/dist/records/harvest.js +2 -23
  29. package/dist/records/one-shot-runs.js +1 -1
  30. package/dist/shared/artifacts-core.js +24 -5
  31. package/dist/shared/output-truncation.js +37 -0
  32. package/dist/shared/package-metadata.js +353 -0
  33. package/dist/{executors/cursor-executor.js → sidecars/cursor-prompt/executor.js} +2 -42
  34. package/dist/sidecars/cursor-prompt/index.js +3 -0
  35. package/dist/sidecars/cursor-prompt/stream.js +121 -0
  36. package/dist/task/config-types.js +29 -12
  37. package/dist/task/delegate.js +9 -21
  38. package/dist/task/runtime.js +1 -2
  39. package/dist/worker/cli.js +243 -0
  40. package/dist/worker/closeout/apply.js +73 -0
  41. package/dist/worker/closeout/preview.js +30 -0
  42. package/dist/worker/delivery/final-verification.js +194 -0
  43. package/dist/worker/delivery/git-transaction.js +354 -0
  44. package/dist/worker/delivery/package.js +502 -0
  45. package/dist/worker/feature/decision-loader.js +68 -0
  46. package/dist/worker/feature/discover.js +14 -0
  47. package/dist/worker/feature/next-action.js +74 -0
  48. package/dist/worker/feature/reducer.js +133 -0
  49. package/dist/worker/feature/review.js +502 -0
  50. package/dist/worker/feature/run.js +365 -0
  51. package/dist/worker/feature/types.js +1 -0
  52. package/dist/worker/follow-up/approve.js +270 -0
  53. package/dist/worker/follow-up/factory.js +234 -0
  54. package/dist/worker/follow-up/paths.js +25 -0
  55. package/dist/worker/follow-up/policy.js +26 -0
  56. package/dist/worker/follow-up/schema.js +93 -0
  57. package/dist/worker/follow-up/store.js +96 -0
  58. package/dist/worker/loop-agent/loop-agent-client.js +329 -126
  59. package/dist/worker/metrics/projector.js +139 -0
  60. package/dist/worker/observability/read-model.js +282 -15
  61. package/dist/worker/observe/paths.js +17 -5
  62. package/dist/worker/observe/static/app.js +443 -61
  63. package/dist/worker/observe/static/index.html +3 -1
  64. package/dist/worker/observe/static/styles.css +85 -18
  65. package/dist/worker/pool/run-store.js +14 -2
  66. package/dist/worker/pool/validation.js +59 -0
  67. package/dist/worker/preflight.js +49 -1
  68. package/dist/worker/report/morning-report.js +41 -6
  69. package/dist/worker/run-task/run-task.js +23 -13
  70. package/dist/worker/runner/run-ready.js +89 -11
  71. package/dist/worker/task-spec/schema.js +0 -1
  72. package/dist/workflows/dag/convergence/controller.js +1 -1
  73. package/dist/workflows/dag/executor-registry.js +0 -2
  74. package/dist/workflows/dag/governance-profile.js +10 -0
  75. package/dist/workflows/dag/init-hybrid.js +601 -26
  76. package/dist/workflows/dag/lifecycle.js +146 -0
  77. package/dist/workflows/dag/node-execution.js +64 -7
  78. package/dist/workflows/dag/prompt.js +16 -0
  79. package/dist/workflows/dag/report.js +2 -0
  80. package/dist/workflows/dag/runner.js +176 -119
  81. package/dist/workflows/dag/scheduler.js +7 -2
  82. package/dist/workflows/dag/skill-snapshot.js +527 -0
  83. package/dist/workflows/dag/types.js +45 -9
  84. package/dist/workflows/dag/validate.js +5 -8
  85. package/dist/workflows/loop/actions/dag-action.js +0 -2
  86. package/dist/workflows/loop/actions/shared.js +1 -1
  87. package/dist/workflows/loop/actions.js +14 -31
  88. package/dist/workflows/loop/benchmark.js +1 -1
  89. package/dist/workflows/loop/index.js +1 -1
  90. package/dist/workflows/loop/policy/auto-policy.js +22 -14
  91. package/dist/workflows/loop/policy/path-patterns.js +13 -0
  92. package/docs/README.md +35 -16
  93. package/docs/agent-dag-recovery-playbook.md +1 -1
  94. package/docs/architecture/README.md +26 -0
  95. package/docs/architecture/dag-execution.md +134 -0
  96. package/docs/architecture/evolution.md +52 -0
  97. package/docs/architecture/facts-and-state.md +58 -0
  98. package/docs/architecture/runtime-boundaries.md +41 -15
  99. package/docs/architecture/system-overview.md +93 -0
  100. package/docs/architecture/worker-and-feature.md +81 -0
  101. package/docs/cursor-prompt-sidecar.md +36 -0
  102. package/docs/decisions/README.md +13 -1
  103. package/docs/design/README.md +39 -13
  104. package/docs/development-principles.md +1 -1
  105. package/docs/exec-plans/active/README.md +2 -2
  106. package/docs/exec-plans/completed/README.md +14 -1
  107. package/docs/feature-workflow.md +44 -4
  108. package/docs/init-surface.manifest.json +63 -1
  109. package/docs/loop-agent-harness.md +65 -3
  110. package/docs/progress/README.md +27 -0
  111. package/docs/reports/README.md +74 -8
  112. package/docs/skills/README.md +2 -1
  113. package/docs/skills/vetted-skill-registry.md +2 -1
  114. package/docs/templates/agent-dag-report.schema.json +4 -2
  115. package/docs/templates/agent-dag.base.json +0 -5
  116. package/docs/templates/agent-dag.final-verification.json +0 -5
  117. package/docs/templates/agent-dag.schema.json +1 -2
  118. package/docs/templates/agent-dag.supervised-implementation.json +1 -6
  119. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +131 -0
  120. package/docs/templates/backend-test-dag.json +213 -0
  121. package/docs/templates/backend-test-dag.retrospect.prompt.md +128 -0
  122. package/docs/templates/backend-test-dag.review-cases.prompt.md +85 -0
  123. package/docs/templates/frontend-design-contract.md +33 -0
  124. package/docs/templates/frontend-task-constraints.md +25 -0
  125. package/docs/templates/frontend-task-requirement.md +61 -0
  126. package/docs/templates/harness.schema.json +8 -5
  127. package/docs/templates/hybrid-dag.json +1 -6
  128. package/docs/templates/init-evolution-review.md +4 -2
  129. package/docs/templates/interactive-ui-round2-experiment.md +1 -1
  130. package/docs/templates/product-line/task.yaml +0 -1
  131. package/docs/templates/worker-dogfood-evidence.md +28 -0
  132. package/docs/templates/worker-dogfood-setup.md +20 -0
  133. package/docs/verification-matrix.md +17 -0
  134. package/examples/decision-gate-agent-dag.json +87 -33
  135. package/examples/example-dag.json +0 -5
  136. package/examples/hybrid-loop-agent-dag.json +0 -5
  137. package/harness.json +6 -11
  138. package/package.json +22 -44
  139. package/scripts/check-product-line-docs.sh +10 -3
  140. package/skills/agent-worker/SKILL.md +37 -0
  141. package/skills/agent-worker/references/agent-worker-operator.md +43 -0
  142. package/skills/frontend-design-review/SKILL.md +59 -0
  143. package/skills/frontend-design-review/references/review-checklist.md +37 -0
  144. package/skills/frontend-implementation/SKILL.md +48 -0
  145. package/skills/frontend-implementation/references/code-standards.md +34 -0
  146. package/skills/frontend-implementation/references/design-spec.md +46 -0
  147. package/skills/frontend-implementation/references/node-contracts.md +32 -0
  148. package/skills/frontend-review/SKILL.md +53 -0
  149. package/skills/frontend-review/references/review-findings.md +42 -0
  150. package/skills/frontend-verification/SKILL.md +40 -0
  151. package/skills/frontend-verification/references/verification-checklist.md +56 -0
  152. package/skills/grill-me/SKILL.md +10 -0
  153. package/skills/grill-with-docs/SKILL.md +88 -0
  154. package/skills/grill-with-docs/adr-format.md +47 -0
  155. package/skills/grill-with-docs/context-format.md +60 -0
  156. package/skills/init-capability-evolution/SKILL.md +1 -0
  157. package/skills/loop-agent/SKILL.md +11 -9
  158. package/skills/loop-agent/references/command-reference.md +28 -15
  159. package/skills/loop-agent/references/docs-converge.md +126 -0
  160. package/skills/loop-agent/references/harness-policy.md +7 -7
  161. package/skills/loop-agent/references/hybrid-dag.md +13 -15
  162. package/skills/loop-agent/references/long-running-loop.md +4 -6
  163. package/skills/loop-agent/references/multi-worktree.md +6 -6
  164. package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
  165. package/skills/loop-agent/references/pi-subagent-assisted-mode.md +14 -11
  166. package/skills/using-git-worktrees/SKILL.md +215 -0
  167. package/dist/commands/cursor-worker.js +0 -43
  168. package/dist/cursor-worker-entry.js +0 -8
  169. package/dist/executors/cursor-artifacts.js +0 -33
  170. package/dist/executors/cursor-execution-log.js +0 -81
  171. package/dist/executors/cursor-executor-artifacts.js +0 -134
  172. package/dist/executors/cursor-run.js +0 -115
  173. package/dist/executors/cursor-tool.js +0 -94
  174. package/dist/executors/cursor-worker-client.js +0 -223
  175. package/dist/executors/cursor-worker-protocol.js +0 -18
  176. package/dist/executors/cursor-worker-server.js +0 -54
  177. package/dist/executors/cursor-worker.js +0 -3
  178. package/dist/executors/cursor.js +0 -6
  179. package/dist/executors/dag-cursor-executor.js +0 -87
  180. package/dist/workflows/loop/actions/cursor-fix.js +0 -191
  181. package/dist/workflows/loop/policy/cursor-fix-policy.js +0 -31
  182. package/docs/cursor-executor-usage.md +0 -25
  183. package/docs/dynamic-workflow-dag-engine-roadmap.md +0 -1749
@@ -367,41 +367,54 @@ set -euo pipefail
367
367
  ROOT_DIR="$(cd "$(dirname "\${BASH_SOURCE[0]}")/.." && pwd)"
368
368
  cd "\${ROOT_DIR}"
369
369
 
370
- skill_md="skills/loop-agent/SKILL.md"
371
- if [[ ! -f "\${skill_md}" ]]; then
372
- echo "skill entry 检查失败:缺少 \${skill_md}" >&2
373
- exit 1
374
- fi
375
-
376
- required_refs=(
377
- "references/harness-policy.md"
378
- "references/hybrid-dag.md"
379
- "references/verification-and-failure-handling.md"
380
- "references/command-reference.md"
381
- )
382
-
383
- missing=()
384
- for ref in "\${required_refs[@]}"; do
385
- [[ -f "skills/loop-agent/\${ref}" ]] || missing+=("\${ref}")
386
- done
370
+ check_skill() {
371
+ local skill_name="$1"
372
+ shift
373
+ local skill_dir="skills/\${skill_name}"
374
+ local skill_md="\${skill_dir}/SKILL.md"
375
+ local missing=()
376
+
377
+ if [[ ! -f "\${skill_md}" ]]; then
378
+ echo "skill entry 检查失败:缺少 \${skill_md}" >&2
379
+ exit 1
380
+ fi
381
+ grep -Eq "^name:[[:space:]]*\${skill_name}[[:space:]]*$" "\${skill_md}" || missing+=("frontmatter name")
382
+ grep -Eq '^description:[[:space:]]*[^[:space:]]' "\${skill_md}" || missing+=("frontmatter description")
387
383
 
388
- while IFS= read -r ref; do
389
- [[ -z "\${ref}" ]] && continue
390
- [[ -f "skills/loop-agent/\${ref}" ]] || missing+=("\${ref}")
391
- done < <(grep -Eo 'references/[A-Za-z0-9._/-]+\\.md' "\${skill_md}" | sort -u || true)
384
+ local ref
385
+ for ref in "$@"; do
386
+ [[ -f "\${skill_dir}/\${ref}" ]] || missing+=("\${ref}")
387
+ grep -Fq "\${ref}" "\${skill_md}" || missing+=("SKILL.md -> \${ref}")
388
+ done
389
+ if (( \${#missing[@]} > 0 )); then
390
+ echo "skill entry 检查失败(\${skill_name}):" >&2
391
+ printf ' - %s\\n' "\${missing[@]}" >&2
392
+ exit 1
393
+ fi
392
394
 
393
- if (( \${#missing[@]} > 0 )); then
394
- echo "skill entry 检查失败:引用的 reference 文件不存在:" >&2
395
- printf ' - %s\\n' "\${missing[@]}" >&2
396
- exit 1
397
- fi
395
+ local line_count
396
+ line_count="$(wc -l < "\${skill_md}" | tr -d ' ')"
397
+ if [[ "\${line_count}" =~ ^[0-9]+$ && "\${line_count}" -gt 180 ]]; then
398
+ echo "skill entry 检查失败:\${skill_md} 行数为 \${line_count},入口应保持精简" >&2
399
+ exit 1
400
+ fi
401
+ echo "skill entry 检查通过:\${skill_name} references ok, lines=\${line_count}"
402
+ }
398
403
 
399
- line_count="$(wc -l < "\${skill_md}" | tr -d ' ')"
400
- if [[ "\${line_count}" =~ ^[0-9]+$ && "\${line_count}" -gt 220 ]]; then
401
- echo "skill entry 检查警告:\${skill_md} 行数为 \${line_count},建议保持入口精简并把细节放入 references/" >&2
402
- fi
404
+ check_skill "loop-agent" \\
405
+ "references/harness-policy.md" \\
406
+ "references/hybrid-dag.md" \\
407
+ "references/verification-and-failure-handling.md" \\
408
+ "references/command-reference.md"
409
+ check_skill "agent-worker" "references/agent-worker-operator.md"
403
410
 
404
- echo "skill entry 检查通过:references ok, lines=\${line_count}"
411
+ description_line="$(grep -E '^description:' skills/agent-worker/SKILL.md | head -n 1 | tr '[:upper:]' '[:lower:]')"
412
+ for term in "agent-worker" "feature packet" "taskspec" "task pool" "self-host" "candidate" "loop-agent"; do
413
+ if [[ "\${description_line}" != *"\${term}"* ]]; then
414
+ echo "skill entry 检查失败(agent-worker):description 缺少触发/路由词 \${term}" >&2
415
+ exit 1
416
+ fi
417
+ done
405
418
  `;
406
419
  const INIT_CHECK_REPO_SH = `#!/usr/bin/env bash
407
420
  set -euo pipefail
@@ -443,11 +456,20 @@ fi
443
456
  feature_dirs=()
444
457
  for root in features product/features dogfood/features; do
445
458
  [[ -d "\${root}" ]] || continue
446
- while IFS= read -r acceptance; do
447
- feature_dirs+=("$(dirname "\${acceptance}")")
448
- done < <(find "\${root}" -mindepth 2 -maxdepth 2 -name acceptance.yaml -type f | sort)
459
+ while IFS= read -r feature_dir; do
460
+ if [[ ! -f "\${feature_dir}/acceptance.yaml" ]]; then
461
+ echo "product-line docs check failed: incomplete feature packet missing acceptance.yaml: \${feature_dir}" >&2
462
+ exit 1
463
+ fi
464
+ feature_dirs+=("\${feature_dir}")
465
+ done < <(find "\${root}" -mindepth 1 -maxdepth 1 -type d | sort)
449
466
  done
450
467
 
468
+ if [[ \${#feature_dirs[@]} -eq 0 ]]; then
469
+ echo "product-line docs check skipped: no feature packets found"
470
+ exit 0
471
+ fi
472
+
451
473
  for feature_dir in "\${feature_dirs[@]}"; do
452
474
  echo "==> validate product-line feature: \${feature_dir}"
453
475
  agent-worker task validate-feature "\${feature_dir}"
@@ -811,11 +833,9 @@ function buildHarness(input) {
811
833
  const existingExecutors = isRecord(input.existing.executors)
812
834
  ? input.existing.executors
813
835
  : {};
814
- // Cursor executor is opt-in for package developers; do not project it to target apps.
815
- // Keep it only when the target already configured executors.cursor.
816
- if (!isRecord(existingExecutors.cursor)) {
817
- delete templateExecutors.cursor;
818
- }
836
+ // Cursor executor must never be projected; Pi-only governed runtime.
837
+ delete templateExecutors.cursor;
838
+ delete existingExecutors.cursor;
819
839
  const mergedExecutors = mergeRecord(templateExecutors, existingExecutors);
820
840
  // requiresApiKey is Cursor-facing only; never project it on pi.
821
841
  if (isRecord(mergedExecutors.pi) && "requiresApiKey" in mergedExecutors.pi) {
@@ -1937,7 +1957,7 @@ export async function checkInitUpdate(input) {
1937
1957
  deterministicActions.push({
1938
1958
  type: "strip-pi-requires-api-key",
1939
1959
  path: "harness.json",
1940
- reason: "executors.pi.requiresApiKey is unused; strip it while keeping user model config and cursor.requiresApiKey",
1960
+ reason: "executors.pi.requiresApiKey is unused; strip it while preserving Pi model configuration",
1941
1961
  });
1942
1962
  }
1943
1963
  }
@@ -153,23 +153,20 @@ export async function buildInstructions(repoRoot, stage, taskId) {
153
153
  ],
154
154
  }));
155
155
  }
156
- const artifactSteps = [
157
- "analyze",
158
- "plan",
159
- "implement",
160
- "verify",
161
- "retrospective",
162
- ];
163
156
  return refreshInstructionBlockers(withMissingDependencies({
164
157
  ...base,
165
158
  outputPath: path.join(taskDir, "artifacts"),
166
159
  dependencies: [paths.taskConfigPath, paths.statePath],
167
160
  template: {
168
- requiredHeadings: artifactSteps.map((step) => getArtifactRelativePath(step)),
161
+ requiredHeadings: [
162
+ getArtifactRelativePath("implement"),
163
+ getArtifactRelativePath("verify"),
164
+ ],
169
165
  },
170
166
  completionCriteria: [
171
- "artifact content is substantive and not the new-task template",
172
- "verification artifact names exact commands and pass/fail results",
167
+ "修改记录.md and 验证结果.md are promote-run bridge artifacts filled from completed DAG/one-shot evidence",
168
+ "do not hand-write L1 分析报告/实现计划/复盘报告; those are not seeded or required",
169
+ "verification artifact summarizes available completed-run evidence; inspect the run facts for exact shell commands and full output",
173
170
  ],
174
171
  }));
175
172
  }
@@ -7,10 +7,9 @@ function usage() {
7
7
  " loop status <task-id> [--json]",
8
8
  " loop run <task-id> --action shell-verify [--command <cmd>]... [--timeout-ms N] [--cwd <repo-relative-dir>]",
9
9
  " loop run <task-id> --action pi-review [--provider <id>] [--model <id>] [--timeout-ms N] [--cwd <repo-relative-dir>]",
10
- " loop run <task-id> --action cursor-fix [--model <id>] [--timeout-ms N] [--cwd <repo-relative-dir>]",
11
- " loop run <task-id> --action dag [--execute] [--dag <path>] [--run-id <id>] [--max-concurrent N] [--cwd <repo-relative-dir>] [--no-cursor]",
10
+ " loop run <task-id> --action dag [--execute] [--dag <path>] [--run-id <id>] [--max-concurrent N] [--cwd <repo-relative-dir>]",
12
11
  " loop run <task-id> --action workflow [--profile pr-review|--workflow <path>] [--execute] [--run-id <id>] [--changed-file <path>]... [--cwd <repo-relative-dir>]",
13
- " loop run <task-id> --auto [--max-rounds N] [--allow-cursor-fix]",
12
+ " loop run <task-id> --auto [--max-rounds N] [--approval-granted]",
14
13
  " loop record-round <task-id> --action <name> --result <summary> --lesson <summary> --next <summary> --decision <continue|pause|blocked|complete> [--verification <summary>] [--failure-category <category>] [--ref <path>]...",
15
14
  " loop add-signal <task-id> --type <human_followup|approval|scope_changed|review_feedback> --message <text> [--urgent] [--ref <path>]...",
16
15
  " loop closeout <task-id>",
@@ -144,7 +143,7 @@ export async function runLoop(repoRoot, args) {
144
143
  }
145
144
  const record = await runLoopAuto(repoRoot, taskId, {
146
145
  maxRounds,
147
- allowCursorFix: rest.includes("--allow-cursor-fix"),
146
+ approvalGranted: rest.includes("--approval-granted"),
148
147
  });
149
148
  console.log(JSON.stringify(record, null, 2));
150
149
  return;
@@ -174,20 +173,6 @@ export async function runLoop(repoRoot, args) {
174
173
  console.log(JSON.stringify(record, null, 2));
175
174
  return;
176
175
  }
177
- if (action === "cursor-fix") {
178
- const record = await runLoopAction({
179
- action,
180
- repoRoot,
181
- taskId,
182
- options: {
183
- timeoutMs,
184
- cwd: readFlag(rest, "--cwd"),
185
- model: readFlag(rest, "--model"),
186
- },
187
- });
188
- console.log(JSON.stringify(record, null, 2));
189
- return;
190
- }
191
176
  if (action === "dag") {
192
177
  const maxConcurrentRaw = readFlag(rest, "--max-concurrent");
193
178
  let maxConcurrent;
@@ -208,7 +193,6 @@ export async function runLoop(repoRoot, args) {
208
193
  dagPath: readFlag(rest, "--dag"),
209
194
  runId: readFlag(rest, "--run-id"),
210
195
  maxConcurrent,
211
- noCursor: rest.includes("--no-cursor"),
212
196
  },
213
197
  });
214
198
  console.log(JSON.stringify(record, null, 2));
@@ -248,7 +232,7 @@ export async function runLoop(repoRoot, args) {
248
232
  return;
249
233
  }
250
234
  if (action !== "shell-verify") {
251
- throw new Error("loop run currently supports --action shell-verify|pi-review|cursor-fix|dag|workflow");
235
+ throw new Error("loop run currently supports --action shell-verify|pi-review|dag|workflow");
252
236
  }
253
237
  const record = await runLoopAction({
254
238
  action: "shell-verify",
@@ -1,56 +1,5 @@
1
- import { DEFAULT_CURSOR_MODEL } from './cursor-executor.js';
2
- import { resolveExecutorModelMatrix } from './model-routing.js';
3
1
  export const TASK_COMPLEXITY_TO_DAG = {
4
2
  small: 'LOW',
5
3
  medium: 'MED',
6
4
  large: 'HIGH',
7
5
  };
8
- export function resolveCursorModel(overrides = {}, cursorConfig) {
9
- return overrides.taskModel ?? cursorConfig?.defaultModel ?? DEFAULT_CURSOR_MODEL;
10
- }
11
- export function resolveCursorModelForTaskConfig(taskConfig, cursorConfig) {
12
- if (taskConfig.cursorModel?.trim()) {
13
- return taskConfig.cursorModel.trim();
14
- }
15
- const dagLevel = TASK_COMPLEXITY_TO_DAG[taskConfig.complexity ?? 'medium'];
16
- const matrix = resolveExecutorModelMatrix('cursor', cursorConfig);
17
- return matrix[dagLevel] ?? resolveCursorModel({}, cursorConfig);
18
- }
19
- export function resolveTaskExecutor(taskConfig, override) {
20
- if (override)
21
- return override;
22
- return taskConfig.executor ?? 'pi';
23
- }
24
- export function resolveCursorExecutorConfig(manifest) {
25
- const config = manifest.executors?.cursor;
26
- if (config?.enabled === false)
27
- return undefined;
28
- return config;
29
- }
30
- export function assertCursorExecutorAvailable(manifest) {
31
- const config = resolveCursorExecutorConfig(manifest);
32
- if (!config) {
33
- throw new Error('cursor executor is not registered in harness.json executors.cursor');
34
- }
35
- return config;
36
- }
37
- export function validateCursorTaskPreflight(taskConfig) {
38
- if (taskConfig.executor !== 'cursor')
39
- return;
40
- const allowed = taskConfig.allowedPaths ?? [];
41
- if (allowed.length === 0) {
42
- // Read-only analysis tasks may omit allowedPaths; write tasks must declare scope explicitly.
43
- return;
44
- }
45
- }
46
- export function assertCursorWriteTaskScope(taskConfig, changedPaths) {
47
- if (taskConfig.executor !== 'cursor')
48
- return;
49
- if ((taskConfig.allowedPaths ?? []).length > 0)
50
- return;
51
- const codeChanges = changedPaths.filter((filePath) => !filePath.startsWith('.harness/tasks/'));
52
- if (codeChanges.length > 0) {
53
- throw new Error('cursor write task refused: allowedPaths is empty but git diff contains code changes. ' +
54
- 'Declare allowedPaths explicitly or keep the task read-only.');
55
- }
56
- }
@@ -1,7 +1,7 @@
1
1
  import path from "node:path";
2
2
  import { writeTextArtifactFile } from "../infrastructure/harness/artifact-store.js";
3
3
  import { executePiStep, } from "./pi-executor.js";
4
- import { redactPromptForLog, truncateOutput } from "./cursor-executor.js";
4
+ import { redactPromptForLog, truncateOutput, } from "../shared/output-truncation.js";
5
5
  import { readGitStatusPorcelain, runPostRunWriteGuard, } from "./shell-write-guard.js";
6
6
  export const DAG_PI_READONLY_TOOLS = ["read", "grep", "find", "ls"];
7
7
  export const DAG_PI_WRITE_TOOLS = [
@@ -1,4 +1,3 @@
1
- export * from "./dag-cursor-executor.js";
2
1
  export * from "../workflows/dag/executor-registry.js";
3
2
  export * from "./dag-pi-executor.js";
4
3
  export * from "./dag-static-executor.js";
@@ -1,7 +1,5 @@
1
1
  export * from "./pi.js";
2
2
  export * from "./pi-sdk.js";
3
- export * from "./cursor.js";
4
- export * from "./cursor-worker.js";
5
3
  export * from "./shell.js";
6
4
  export * from "./static.js";
7
5
  export * from "./dag.js";
@@ -1,22 +1,20 @@
1
1
  import { DEFAULT_DAG_EXECUTOR_MODELS, } from '../workflows/dag/types.js';
2
- export const DEFAULT_DAG_CURSOR_MODEL = "composer-2.5";
3
2
  export const DEFAULT_DAG_MODELS = {
4
3
  HIGH: "gpt-5.5",
5
- MED: "composer-2.5",
6
- LOW: "composer-2.5",
4
+ MED: "glm-5.2",
5
+ LOW: "gpt-5.3-codex-spark",
7
6
  };
8
7
  /**
9
8
  * DAG executor model tier keys that may carry a per-complexity override.
10
9
  */
11
10
  const EXECUTOR_MODEL_TIERS = ["LOW", "MED", "HIGH"];
12
11
  /**
13
- * Resolve the DAG executor model matrix for a single executor from its harness
14
- * `executors.<name>` config.
12
+ * Resolve the DAG executor model matrix for Pi from harness `executors.pi`.
15
13
  *
16
14
  * Priority per tier (LOW/MED/HIGH):
17
15
  * 1. execConfig[tier] (truthy and !== "default" sentinel)
18
16
  * 2. execConfig.defaultModel (truthy and !== "default" sentinel)
19
- * 3. DEFAULT_DAG_EXECUTOR_MODELS[executor][tier]
17
+ * 3. DEFAULT_DAG_EXECUTOR_MODELS.pi[tier]
20
18
  *
21
19
  * The "default" literal (injected by the schema `.default("default")`) and
22
20
  * absent/undefined both mean "no override, fall through".
@@ -38,15 +36,17 @@ export function resolveExecutorModelMatrix(executor, execConfig) {
38
36
  };
39
37
  }
40
38
  /**
41
- * Resolve both pi and cursor DAG executor model matrices from a harness manifest.
42
- * Single entry point for DAG generation and --strict-models baseline resolution.
39
+ * Resolve the Pi DAG executor model matrix from a harness manifest.
43
40
  */
44
41
  export function resolveExecutorModelMatrices(manifest) {
45
42
  return {
46
43
  pi: resolveExecutorModelMatrix("pi", manifest.executors?.pi),
47
- cursor: resolveExecutorModelMatrix("cursor", manifest.executors?.cursor),
48
44
  };
49
45
  }
46
+ /** @deprecated Prefer resolveExecutorModelMatrices; alias kept for call-site clarity. */
47
+ export function resolvePiModelMatrix(manifest) {
48
+ return resolveExecutorModelMatrices(manifest).pi;
49
+ }
50
50
  export function resolveModelSelection(manifest, taskConfig, step, options) {
51
51
  const retryAttempt = options?.retryAttempt ?? 0;
52
52
  if (Object.keys(manifest.modelProfiles ?? {}).length === 0) {
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  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
- import { truncateOutput } from "./cursor-executor.js";
5
+ import { truncateOutput } from "../shared/output-truncation.js";
6
6
  import { expandShellPreset, buildVerdictGateShellCommand } from "./shell-presets.js";
7
7
  import { pathsChangedDuringRun, snapshotGitStatusPorcelain, validateShellWriteGuard, } from "./shell-write-guard.js";
8
8
  import { buildShellProcessEnv } from "./shell-verification.js";
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { repoRelativePath } from '../shared/path-refs.js';
4
4
  import { loadHarnessManifest } from './harness.js';
5
5
  import { auditLoopAgentSkillSafety } from './skill-safety.js';
6
- import { getTaskPaths, getTaskStatus, loadTaskConfig, shouldRunRetrospective } from '../task/runtime.js';
6
+ import { getTaskPaths, getTaskStatus, loadTaskConfig } from '../task/runtime.js';
7
7
  import { buildTaskReadModel } from '../task/read-model.js';
8
8
  const SKIP_DIRS = new Set([
9
9
  '.git',
@@ -244,12 +244,15 @@ export async function checkTaskHandoff(repoRoot, taskId) {
244
244
  message: '任务已 completed,但验证工件未标记完成',
245
245
  });
246
246
  }
247
- if (state.status === 'completed' && shouldRunRetrospective(taskConfig) && !state.artifacts.retrospective) {
247
+ // Standard DAG tasks do not require a retrospective artifact. Preserve an
248
+ // explicit legacy task contract, however, so historical tasks that opted
249
+ // into requireRetrospective do not silently lose their handoff warning.
250
+ if (state.status === 'completed' && taskConfig.requireRetrospective && !state.artifacts.retrospective) {
248
251
  issues.push({
249
252
  type: 'missing-retrospective-artifact',
250
253
  severity: 'warning',
251
254
  file: toRepoRelative(repoRoot, path.join(taskPaths.taskDir, 'artifacts', '复盘报告.md')),
252
- message: '任务应包含 retrospective,但复盘工件未完成',
255
+ message: '任务显式要求 retrospective,但复盘工件未完成',
253
256
  });
254
257
  }
255
258
  if (readModel.promotion.required && !readModel.promotion.ready) {
@@ -16,7 +16,8 @@ export const complexityRouteSchema = z.object({
16
16
  export const worktreeManifestConfigSchema = z.object({
17
17
  rootRelativePath: z.string().optional().default(".worktrees"),
18
18
  });
19
- export const taskExecutorSchema = z.enum(["pi", "cursor"]);
19
+ export const taskExecutorSchema = z.enum(["pi"]);
20
+ export const CURSOR_TASK_EXECUTOR_REMOVED_ERROR = 'task executor "cursor" is no longer supported; governed runtime is Pi-only';
20
21
  export const executorManifestSchema = z.object({
21
22
  description: z.string().optional(),
22
23
  enabled: z.boolean().optional(),
@@ -36,12 +37,16 @@ export const workflowPolicyProfileNameSchema = z.enum([
36
37
  "standard",
37
38
  "reviewed",
38
39
  "supervised",
40
+ "backend-test",
39
41
  ]);
40
42
  export const workflowPolicyDagTemplateSchema = z.enum([
41
43
  "standard-dag",
42
44
  "review-gated-dag",
43
45
  "supervised-implementation",
46
+ "frontend-implementation",
47
+ "backend-test-dag",
44
48
  ]);
49
+ export const workflowPolicyOutputLanguageSchema = z.enum(["zh-CN", "en"]);
45
50
  export const workflowPolicySchema = z
46
51
  .object({
47
52
  /** Preferred implementation workflow for recoverable loop-agent work. Declarative policy; callers may still require explicit CLI flags. */
@@ -51,6 +56,9 @@ export const workflowPolicySchema = z
51
56
  .default("agent-dag"),
52
57
  dag: z
53
58
  .object({
59
+ outputLanguage: workflowPolicyOutputLanguageSchema
60
+ .optional()
61
+ .default("zh-CN"),
54
62
  defaultEntry: z
55
63
  .enum(["dag run-task", "run-dag"])
56
64
  .optional()
@@ -69,6 +77,9 @@ export const workflowPolicySchema = z
69
77
  supervised: workflowPolicyDagTemplateSchema
70
78
  .optional()
71
79
  .default("supervised-implementation"),
80
+ "backend-test": workflowPolicyDagTemplateSchema
81
+ .optional()
82
+ .default("backend-test-dag"),
72
83
  })
73
84
  .optional()
74
85
  .default({}),
@@ -88,7 +99,9 @@ export const workflowPolicySchema = z
88
99
  })
89
100
  .optional()
90
101
  .default({});
91
- export const harnessManifestSchema = z.object({
102
+ export const CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR = 'harness executors.cursor is no longer supported; remove it and use executors.pi only (Cursor is available only via cursor-prompt sidecar)';
103
+ export const harnessManifestSchema = z
104
+ .object({
92
105
  version: z.number(),
93
106
  project: z.string(),
94
107
  adapter: z.string().optional(),
@@ -134,4 +147,22 @@ export const harnessManifestSchema = z.object({
134
147
  .record(z.string(), executorManifestSchema)
135
148
  .optional()
136
149
  .default({}),
150
+ })
151
+ .superRefine((manifest, ctx) => {
152
+ const executors = manifest.executors ?? {};
153
+ if ("cursor" in executors) {
154
+ ctx.addIssue({
155
+ code: z.ZodIssueCode.custom,
156
+ message: CURSOR_HARNESS_EXECUTOR_REMOVED_ERROR,
157
+ path: ["executors", "cursor"],
158
+ });
159
+ }
160
+ const entrypoints = manifest.entrypoints ?? {};
161
+ if ("cursorExecutorUsage" in entrypoints) {
162
+ ctx.addIssue({
163
+ code: z.ZodIssueCode.custom,
164
+ message: 'entrypoints.cursorExecutorUsage is no longer supported; remove it (Cursor is cursor-prompt sidecar only)',
165
+ path: ["entrypoints", "cursorExecutorUsage"],
166
+ });
167
+ }
137
168
  });
@@ -9,9 +9,6 @@ function loopActionWriteOptions(repoRoot) {
9
9
  export async function writeLoopShellVerifyRecord(repoRoot, taskId, round, record) {
10
10
  await writeJsonAtomic(loopActionFile(repoRoot, taskId, "verification", `round-${round}.json`), record, loopActionWriteOptions(repoRoot));
11
11
  }
12
- export async function writeLoopCursorFixRecord(repoRoot, taskId, round, record) {
13
- await writeJsonAtomic(loopActionFile(repoRoot, taskId, "reviews", `round-${round}-cursor-fix.json`), record, loopActionWriteOptions(repoRoot));
14
- }
15
12
  export async function writeLoopDagRecord(repoRoot, taskId, round, record) {
16
13
  await writeJsonAtomic(loopActionFile(repoRoot, taskId, "dag", `round-${round}.json`), record, loopActionWriteOptions(repoRoot));
17
14
  }
@@ -1,12 +1,10 @@
1
- import { readFile, stat } from 'node:fs/promises';
1
+ import { stat } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { loadHarnessManifest } from '../governance/harness.js';
5
5
  import { resolveWorktreePath } from '../task/worktree.js';
6
6
  import { getTaskPaths, getTaskStatus, loadTaskConfig } from '../task/runtime.js';
7
7
  import { validateDiffPathGuard, listDiffPaths } from '../governance/path-guard.js';
8
- import { validateCursorArtifacts } from '../executors/cursor-artifacts.js';
9
- import { checkTaskHandoff } from '../governance/checks.js';
10
8
  import { archiveTaskDirFromWorktree } from '../task/worktree-cleanup.js';
11
9
  export { copyDir } from '../shared/copy-dir.js';
12
10
  /**
@@ -154,8 +152,7 @@ async function runHarvestGates(repoRoot, taskId, taskRoot, baseBranch, branch) {
154
152
  catch {
155
153
  return;
156
154
  }
157
- const shouldRunPathGuard = taskConfig.executor === 'cursor'
158
- || (taskConfig.allowedPaths ?? []).length > 0
155
+ const shouldRunPathGuard = (taskConfig.allowedPaths ?? []).length > 0
159
156
  || (taskConfig.forbiddenPaths ?? []).length > 0;
160
157
  if (shouldRunPathGuard) {
161
158
  const changedPaths = await listDiffPaths(repoRoot, baseBranch, branch);
@@ -165,24 +162,6 @@ async function runHarvestGates(repoRoot, taskId, taskRoot, baseBranch, branch) {
165
162
  'Worktree preserved for manual review.');
166
163
  }
167
164
  }
168
- if (taskConfig.executor !== 'cursor') {
169
- return;
170
- }
171
- const paths = getTaskPaths(taskRoot, taskId);
172
- const modifyLog = await readFile(path.join(paths.taskDir, 'artifacts', '修改记录.md'), 'utf-8');
173
- const verifyResult = await readFile(path.join(paths.taskDir, 'artifacts', '验证结果.md'), 'utf-8');
174
- const artifactIssues = validateCursorArtifacts(modifyLog, verifyResult);
175
- if (artifactIssues.length > 0) {
176
- throw new Error(`harvest artifact validation failed for cursor task "${taskId}": ${artifactIssues.join('; ')}. ` +
177
- 'Worktree preserved for manual review.');
178
- }
179
- const handoff = await checkTaskHandoff(taskRoot, taskId);
180
- const taskHandoff = handoff.tasks.find((task) => task.taskId === taskId);
181
- const blockingIssues = (taskHandoff?.issues ?? []).filter((issue) => issue.severity === 'error');
182
- if (blockingIssues.length > 0) {
183
- throw new Error(`harvest handoff check failed for task "${taskId}": ${blockingIssues.map((issue) => issue.message).join('; ')}. ` +
184
- 'Worktree preserved for manual review.');
185
- }
186
165
  }
187
166
  // ---------------------------------------------------------------------------
188
167
  // Helpers
@@ -1,6 +1,6 @@
1
1
  import { access, readdir, readFile } from 'node:fs/promises';
2
2
  import path from 'node:path';
3
- import { truncateOutput } from '../executors/cursor-executor.js';
3
+ import { truncateOutput } from '../shared/output-truncation.js';
4
4
  import { createOneShotActiveRunDir, ensureOneShotRunDirs, finalizeLegacyRunLogToCompleted, transferOneShotRunToCompleted, transferOneShotRunToFailed, writeOneShotActiveLogAtPath, writeOneShotRunMarkdown, writeOneShotRunMeta, } from '../infrastructure/harness/one-shot-run-store.js';
5
5
  /** Maximum chars for result summary in the run log. */
6
6
  export const RUN_LOG_RESULT_MAX = 4_000;
@@ -2,13 +2,25 @@ import path from 'node:path';
2
2
  import { resolveTaskContextFromDir, writeTaskArtifactFile, } from '../infrastructure/harness/task-store.js';
3
3
  export const ARTIFACTS_DIR = 'artifacts';
4
4
  const SPEC_ARTIFACT_STEPS = ['analyze', 'plan'];
5
- const DEFAULT_ARTIFACTS = {
6
- '分析报告.md': '# 分析报告\n\n## 1. 任务目标\n\n## 2. 当前现状\n\n## 3. 受影响模块与文件\n\n## 4. 关键约束与不变式\n\n## 5. 硬约束摘要\n\n## 6. 允许修改范围 / 禁止修改范围\n\n## 7. 风险点\n\n## 8. 待确认问题\n',
7
- '实现计划.md': '# 实现计划\n\n## 1. 修改目标\n\n## 2. 修改范围\n\n## 3. 允许修改文件 / 目录\n\n## 4. 明确不做的内容\n\n## 5. 关键决策\n\n## 6. 不变式\n\n## 7. 风险与回滚点\n\n## 8. 测试与验证方案\n\n## 9. 不在本次范围内的事项\n\n## 10. 待确认事项\n',
5
+ /** Promote-run / closeout bridge templates seeded by new-task. */
6
+ const SEEDED_ARTIFACTS = {
8
7
  '修改记录.md': '# 修改记录\n\n## 1. 修改文件清单\n\n## 2. 关键改动说明\n',
9
8
  '验证结果.md': '# 验证结果\n\n## 1. 执行的命令\n\n## 2. 通过项\n\n## 3. 失败项\n',
9
+ };
10
+ /**
11
+ * Legacy L1 analyze/plan/retrospective templates.
12
+ * Kept only so validateArtifactContent can recognize unchanged historical files.
13
+ * new-task must not seed these.
14
+ */
15
+ const LEGACY_ARTIFACT_TEMPLATES = {
16
+ '分析报告.md': '# 分析报告\n\n## 1. 任务目标\n\n## 2. 当前现状\n\n## 3. 受影响模块与文件\n\n## 4. 关键约束与不变式\n\n## 5. 硬约束摘要\n\n## 6. 允许修改范围 / 禁止修改范围\n\n## 7. 风险点\n\n## 8. 待确认问题\n',
17
+ '实现计划.md': '# 实现计划\n\n## 1. 修改目标\n\n## 2. 修改范围\n\n## 3. 允许修改文件 / 目录\n\n## 4. 明确不做的内容\n\n## 5. 关键决策\n\n## 6. 不变式\n\n## 7. 风险与回滚点\n\n## 8. 测试与验证方案\n\n## 9. 不在本次范围内的事项\n\n## 10. 待确认事项\n',
10
18
  '复盘报告.md': '# 复盘报告\n\n## 1. 质量复盘\n\n## 2. 流程问题\n\n## 3. 下次建议\n',
11
19
  };
20
+ const ARTIFACT_TEMPLATES = {
21
+ ...LEGACY_ARTIFACT_TEMPLATES,
22
+ ...SEEDED_ARTIFACTS,
23
+ };
12
24
  const ARTIFACT_NAME_BY_STEP = {
13
25
  analyze: '分析报告.md',
14
26
  plan: '实现计划.md',
@@ -16,6 +28,8 @@ const ARTIFACT_NAME_BY_STEP = {
16
28
  verify: '验证结果.md',
17
29
  retrospective: '复盘报告.md',
18
30
  };
31
+ /** Steps whose artifact files are seeded by new-task (promote/closeout bridge). */
32
+ export const SEEDED_ARTIFACT_STEPS = ['implement', 'verify'];
19
33
  export function getArtifactFileName(step) {
20
34
  return ARTIFACT_NAME_BY_STEP[step];
21
35
  }
@@ -35,6 +49,9 @@ export function formatSpecArtifactRelativePaths() {
35
49
  export function formatSpecArtifactDisplayNames() {
36
50
  return SPEC_ARTIFACT_STEPS.map((step) => getArtifactDisplayName(step)).join(' + ');
37
51
  }
52
+ export function listSeededArtifactFileNames() {
53
+ return Object.keys(SEEDED_ARTIFACTS);
54
+ }
38
55
  const REQUIRED_HEADINGS = {
39
56
  analyze: ['# 分析报告', '## 1. 任务目标'],
40
57
  plan: ['# 实现计划', '## 1. 修改目标'],
@@ -43,7 +60,7 @@ const REQUIRED_HEADINGS = {
43
60
  };
44
61
  export async function initializeArtifacts(taskDir) {
45
62
  const { repoRoot, taskId } = resolveTaskContextFromDir(taskDir);
46
- await Promise.all(Object.entries(DEFAULT_ARTIFACTS).map(([name, content]) => writeTaskArtifactFile(repoRoot, taskId, name, content)));
63
+ await Promise.all(Object.entries(SEEDED_ARTIFACTS).map(([name, content]) => writeTaskArtifactFile(repoRoot, taskId, name, content)));
47
64
  }
48
65
  export function getArtifactPath(taskDir, step) {
49
66
  return path.join(taskDir, ARTIFACTS_DIR, getArtifactFileName(step));
@@ -57,7 +74,9 @@ function normalizeArtifactContent(content) {
57
74
  }
58
75
  function isUnchangedNewTaskTemplate(step, content) {
59
76
  const artifactName = ARTIFACT_NAME_BY_STEP[step];
60
- const template = DEFAULT_ARTIFACTS[artifactName];
77
+ const template = ARTIFACT_TEMPLATES[artifactName];
78
+ if (!template)
79
+ return false;
61
80
  return normalizeArtifactContent(content) === normalizeArtifactContent(template);
62
81
  }
63
82
  function firstRequiredSectionIsEmpty(step, content) {