@tea-agent/loop-agent 0.28.2-beta.1 → 0.28.3

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 (67) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +37 -0
  3. package/README.md +11 -1
  4. package/dist/cli/command-definitions.js +2 -1
  5. package/dist/commands/client-recovery.js +111 -8
  6. package/dist/commands/dag-init-hybrid.js +1 -1
  7. package/dist/commands/init-upgrade.js +2479 -0
  8. package/dist/commands/init.js +120 -9
  9. package/dist/governance/manifest-types.js +65 -0
  10. package/dist/shared/operator/capabilities.js +350 -2
  11. package/dist/task/worktree.js +256 -39
  12. package/dist/worker/cli.js +22 -12
  13. package/dist/worker/console/chat/workspace-landing.js +16 -6
  14. package/dist/worker/console/observe-health-match.js +2 -0
  15. package/dist/worker/console/observe-link.js +4 -0
  16. package/dist/worker/console/operator-actions.js +183 -4
  17. package/dist/worker/console/operator-selection.js +13 -0
  18. package/dist/worker/console/static/assets/index-BfRgtLF4.js +29 -0
  19. package/dist/worker/console/static/index.html +1 -1
  20. package/dist/worker/observe/health.js +1 -0
  21. package/dist/worker/observe/night-jobs.js +104 -0
  22. package/dist/worker/observe/routes.js +48 -0
  23. package/dist/worker/observe/static/app.js +3 -0
  24. package/dist/worker/observe/static/constants.js +1 -0
  25. package/dist/worker/observe/static/index.html +47 -0
  26. package/dist/worker/observe/static/router.js +10 -0
  27. package/dist/worker/observe/static/shell-chrome.js +1 -0
  28. package/dist/worker/observe/static/views/night.js +201 -0
  29. package/dist/worker/report/morning-report.js +56 -16
  30. package/dist/worker/run-task/execute-prepared-task.js +153 -0
  31. package/dist/worker/runner/single-task-attempt.js +147 -0
  32. package/dist/worker/scheduler/admission.js +538 -0
  33. package/dist/worker/scheduler/auto-followup.js +99 -0
  34. package/dist/worker/scheduler/cli.js +539 -0
  35. package/dist/worker/scheduler/dispatcher.js +503 -0
  36. package/dist/worker/scheduler/doctor.js +346 -0
  37. package/dist/worker/scheduler/evidence.js +170 -0
  38. package/dist/worker/scheduler/git-base.js +90 -0
  39. package/dist/worker/scheduler/index.js +23 -0
  40. package/dist/worker/scheduler/lease.js +114 -0
  41. package/dist/worker/scheduler/lifecycle.js +348 -0
  42. package/dist/worker/scheduler/lock.js +80 -0
  43. package/dist/worker/scheduler/morning-window.js +161 -0
  44. package/dist/worker/scheduler/night-git-finalizer.js +88 -0
  45. package/dist/worker/scheduler/night-harvest.js +412 -0
  46. package/dist/worker/scheduler/paths.js +84 -0
  47. package/dist/worker/scheduler/prepared-attempt-recovery.js +471 -0
  48. package/dist/worker/scheduler/recovery.js +277 -0
  49. package/dist/worker/scheduler/reservation.js +146 -0
  50. package/dist/worker/scheduler/retry.js +199 -0
  51. package/dist/worker/scheduler/scheduler-loop.js +272 -0
  52. package/dist/worker/scheduler/store.js +275 -0
  53. package/dist/worker/scheduler/traceability.js +54 -0
  54. package/dist/worker/scheduler/trigger.js +258 -0
  55. package/dist/worker/scheduler/types.js +369 -0
  56. package/dist/worker/scheduler/workspace-adapter.js +91 -0
  57. package/dist/workflows/dag/frontend-implementation-contract.js +2 -102
  58. package/docs/architecture/runtime-boundaries.md +9 -0
  59. package/docs/init-surface.manifest.json +9 -2
  60. package/docs/templates/harness.schema.json +107 -0
  61. package/docs/templates/init-managed-agents.md +18 -8
  62. package/harness.json +22 -0
  63. package/package.json +1 -1
  64. package/skills/loop-agent/SKILL.md +28 -36
  65. package/skills/loop-agent/references/command-reference.md +40 -16
  66. package/skills/loop-agent/references/hybrid-dag.md +1 -1
  67. package/dist/worker/console/static/assets/index-CNO7n6qB.js +0 -29
@@ -0,0 +1,91 @@
1
+ import { access, symlink, unlink } from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { ensureOwnedWorktree, verifyOwnedWorktree, } from "../../task/worktree.js";
4
+ import { getSchedulerRoot } from "./paths.js";
5
+ export function defaultNightWorktreeRelativePath(scheduleId) {
6
+ return path.posix.join(".worktrees", "night", scheduleId);
7
+ }
8
+ export function defaultNightBranch(scheduleId) {
9
+ return `night/${scheduleId}`;
10
+ }
11
+ /**
12
+ * Create or reuse a schedule-owned worktree under control repo.
13
+ * Does not copy secrets/.env. Optional node_modules symlink only.
14
+ */
15
+ export async function prepareNightWorkspace(spec) {
16
+ const relativeWorktreePath = spec.worktreePath ?? defaultNightWorktreeRelativePath(spec.scheduleId);
17
+ const branch = spec.branch ?? defaultNightBranch(spec.scheduleId);
18
+ const expectedBranch = defaultNightBranch(spec.scheduleId);
19
+ if (branch !== expectedBranch) {
20
+ throw new Error(`night worktree branch must be ${expectedBranch}, got ${branch}`);
21
+ }
22
+ const absolutePath = path.isAbsolute(relativeWorktreePath)
23
+ ? relativeWorktreePath
24
+ : path.resolve(spec.controlRepoRoot, relativeWorktreePath);
25
+ // Ensure parent of worktree root exists (and keep scheduler root alive).
26
+ void getSchedulerRoot(spec.controlRepoRoot);
27
+ const ensured = await ensureOwnedWorktree(spec.controlRepoRoot, {
28
+ worktreePath: absolutePath,
29
+ branch,
30
+ baseBranch: spec.baseBranch,
31
+ baseCommit: spec.baseCommit,
32
+ scheduleId: spec.scheduleId,
33
+ ...(spec.ownershipToken ? { ownershipToken: spec.ownershipToken } : {}),
34
+ ...(spec.now ? { now: spec.now } : {}),
35
+ });
36
+ let nodeModulesLinked = false;
37
+ if (spec.hydrateNodeModules !== false) {
38
+ nodeModulesLinked = await linkNodeModulesIfPresent(spec.controlRepoRoot, ensured.path);
39
+ }
40
+ const relative = path
41
+ .relative(spec.controlRepoRoot, ensured.path)
42
+ .split(path.sep)
43
+ .join("/") || relativeWorktreePath;
44
+ return {
45
+ ...ensured,
46
+ relativeWorktreePath: relative.startsWith("..") ? ensured.path : relative,
47
+ relativeBranch: branch,
48
+ nodeModulesLinked,
49
+ };
50
+ }
51
+ export async function revalidateNightWorkspace(input) {
52
+ const result = await verifyOwnedWorktree({
53
+ repoRoot: input.controlRepoRoot,
54
+ worktreePath: input.worktreePath,
55
+ scheduleId: input.scheduleId,
56
+ branch: input.branch,
57
+ baseCommit: input.baseCommit,
58
+ ownershipToken: input.ownershipToken,
59
+ });
60
+ return { head: result.head };
61
+ }
62
+ async function linkNodeModulesIfPresent(controlRepoRoot, worktreePath) {
63
+ const host = path.join(controlRepoRoot, "node_modules");
64
+ const target = path.join(worktreePath, "node_modules");
65
+ try {
66
+ await access(host);
67
+ }
68
+ catch {
69
+ return false;
70
+ }
71
+ try {
72
+ await access(target);
73
+ return true;
74
+ }
75
+ catch {
76
+ // continue to link
77
+ }
78
+ try {
79
+ await symlink(host, target, "junction");
80
+ return true;
81
+ }
82
+ catch {
83
+ try {
84
+ await unlink(target);
85
+ }
86
+ catch {
87
+ // ignore
88
+ }
89
+ return false;
90
+ }
91
+ }
@@ -570,106 +570,6 @@ function canonicalizeVerificationTargetAliases(value) {
570
570
  : record.requirements;
571
571
  return { ...record, requirements };
572
572
  }
573
- /**
574
- * Model-written evidence gaps are hypotheses, not authoritative coverage
575
- * facts. If the plan contains a real verification target that explicitly
576
- * names a requirement, the executor can prove that the requirement has a
577
- * verification path even when the model forgot to wire the target into the
578
- * requirement entry or conservatively marked its gap as blocking.
579
- *
580
- * Keep gaps blocking when that proof cannot be derived. This is deliberately
581
- * narrow: it uses only sourceBinding requirement IDs and structurally present
582
- * verification targets, and never invents a command, file, or target.
583
- */
584
- function deriveFrontendVerificationCoverage(value, canonicalBinding) {
585
- const record = asRecord(value);
586
- if (!record)
587
- return value;
588
- const rawTargets = Array.isArray(record.verificationTargets)
589
- ? record.verificationTargets
590
- : [];
591
- const targetIds = new Set(rawTargets
592
- .map((item) => asRecord(item))
593
- .filter((item) => Boolean(item))
594
- .map((item) => asString(item.id))
595
- .filter(Boolean));
596
- const targetIdsByRequirement = new Map();
597
- for (const item of rawTargets) {
598
- const target = asRecord(item);
599
- if (!target)
600
- continue;
601
- const targetId = asString(target.id);
602
- const commandLabel = asString(target.commandLabel) || asString(target.command);
603
- const file = asString(target.file);
604
- // A target is usable evidence only when it has an identity, command and
605
- // file. The strict schema will validate the final shape afterwards.
606
- if (!targetId || !commandLabel || !file)
607
- continue;
608
- for (const requirementId of asStringArray(target.requirementIds)) {
609
- const canonicalId = canonicalizeRequirementId(requirementId);
610
- if (!canonicalBinding.requirementIds.includes(canonicalId))
611
- continue;
612
- const ids = targetIdsByRequirement.get(canonicalId) ?? [];
613
- if (!ids.includes(targetId))
614
- ids.push(targetId);
615
- targetIdsByRequirement.set(canonicalId, ids);
616
- }
617
- }
618
- const provenRequirementIds = new Set(targetIdsByRequirement.keys());
619
- const requirements = Array.isArray(record.requirements)
620
- ? record.requirements.map((item) => {
621
- const requirement = asRecord(item);
622
- if (!requirement)
623
- return item;
624
- const requirementId = canonicalizeRequirementId(asString(requirement.id));
625
- const inferredTargetIds = targetIdsByRequirement.get(requirementId) ?? [];
626
- const existingTargetIds = asStringArray(requirement.verificationTargetIds)
627
- .filter((id) => targetIds.has(id));
628
- const verificationTargetIds = [...new Set([
629
- ...existingTargetIds,
630
- ...inferredTargetIds,
631
- ])];
632
- const evidenceGap = asRecord(requirement.evidenceGap);
633
- const hasProof = provenRequirementIds.has(requirementId);
634
- return {
635
- ...requirement,
636
- ...(verificationTargetIds.length > 0 ? { verificationTargetIds } : {}),
637
- ...(hasProof
638
- ? (evidenceGap ? { evidenceGap: { ...evidenceGap, blocking: false } } : {})
639
- : {
640
- evidenceGap: {
641
- requirementId,
642
- description: `No executable verification target can be derived for ${requirementId}`,
643
- blocking: true,
644
- },
645
- }),
646
- };
647
- })
648
- : record.requirements;
649
- const modelEvidenceGaps = Array.isArray(record.evidenceGaps)
650
- ? record.evidenceGaps.map((item) => {
651
- const gap = asRecord(item);
652
- if (!gap)
653
- return item;
654
- const requirementId = canonicalizeRequirementId(asString(gap.requirementId));
655
- // Model gaps are advisory. Blocking status is reconstructed below
656
- // from the source binding and executable verification targets.
657
- return { ...gap, blocking: false };
658
- })
659
- : [];
660
- const derivedBlockingGaps = canonicalBinding.requirementIds
661
- .filter((requirementId) => !provenRequirementIds.has(requirementId))
662
- .map((requirementId) => ({
663
- requirementId,
664
- description: `No executable verification target can be derived for ${requirementId}`,
665
- blocking: true,
666
- }));
667
- return {
668
- ...record,
669
- requirements,
670
- evidenceGaps: [...modelEvidenceGaps, ...derivedBlockingGaps],
671
- };
672
- }
673
573
  function assertFrontendContractPathsSafe(value) {
674
574
  const record = asRecord(value);
675
575
  if (!record)
@@ -1169,10 +1069,10 @@ export async function materializeFrontendImplementationContract(input) {
1169
1069
  const normalizedContract = coerceFrontendImplementationContractInput(parsed, canonicalBinding);
1170
1070
  // There is exactly one post-security candidate. A fallback candidate would
1171
1071
  // allow malformed raw fields to bypass the boundary checks above.
1172
- const candidate = deriveFrontendVerificationCoverage({
1072
+ const candidate = {
1173
1073
  ...(asRecord(normalizedContract) ?? parsed),
1174
1074
  sourceBinding: canonicalBinding,
1175
- }, canonicalBinding);
1075
+ };
1176
1076
  const result = frontendImplementationContractSchema.safeParse(candidate);
1177
1077
  if (!result.success)
1178
1078
  throw new Error(`invalid-output: ${result.error.issues.map((issue) => `${issue.path.join(".")}: ${issue.message}`).join("; ")}`);
@@ -12,6 +12,9 @@ Skill layer
12
12
  CLI layer (src/cli/)
13
13
  └─ argv 解析、adapter 解析、调用 application / command handler、格式化输出
14
14
 
15
+ Init-upgrade command layer (src/commands/init-upgrade.ts)
16
+ └─ controller-owned init-upgrade state machine、frozen identity、target `.harness/init-upgrades/` facts、safe action receipts and bounded merge contracts
17
+
15
18
  Application layer (src/application/,逐步引入)
16
19
  └─ 一次用户意图的 typed use-case interface,供 CLI、Loop、Workflow 复用
17
20
 
@@ -54,6 +57,12 @@ Governance (scripts/check-*.sh, src/governance/)
54
57
  - **允许依赖**:`src/commands/**`(handler wiring)、`src/adapters/**`、`src/shared/**`、逐步引入的 `src/application/**`。
55
58
  - **禁止**:在 router 中维护与 catalog 独立的 command 事实源(Phase 1 整改目标)。
56
59
 
60
+ ### Init-upgrade command boundary
61
+
62
+ - **位置**:`src/commands/init-upgrade.ts`,复用 `src/commands/init.ts` 的 ownership scan / safe action 与 `src/commands/client-recovery.ts` 的 generated recovery builders。
63
+ - **职责**:拥有可恢复升级状态机与目标项目 run facts;version gate、identity drift、active runtime/lock、workspace re-scan、single-file merge boundary、home zero-write fingerprint 和 completion invariant 都由 controller 决定。
64
+ - **禁止**:OpenCode/Pi plugin、主会话 skill 或 notifier 拥有升级状态机;命令不得修改业务源码、用户 home 或以宽泛路径授权模型。主会话仅可按 controller 返回的具体 `allowedPaths` 合并 init surface,随后必须 `--continue`。
65
+
57
66
  ### Application layer
58
67
 
59
68
  - **位置**:`src/application/**`(按计划逐步引入)
@@ -56,6 +56,7 @@
56
56
  "skills/agent-worker/references/agent-worker-operator.md",
57
57
  "skills/loop-agent/SKILL.md",
58
58
  "skills/loop-agent/references/command-reference.md",
59
+ "skills/loop-agent/references/hybrid-dag.md",
59
60
  "skills/loop-agent/references/source-and-plan-practice.md",
60
61
  "skills/loop-agent/references/docs-converge.md",
61
62
  "skills/ai-engineering-context/SKILL.md",
@@ -135,6 +136,7 @@
135
136
  ".agents/skills/agent-worker/references/agent-worker-operator.md",
136
137
  ".agents/skills/loop-agent/SKILL.md",
137
138
  ".agents/skills/loop-agent/references/command-reference.md",
139
+ ".agents/skills/loop-agent/references/hybrid-dag.md",
138
140
  ".agents/skills/loop-agent/references/source-and-plan-practice.md",
139
141
  ".agents/skills/loop-agent/references/docs-converge.md",
140
142
  ".agents/skills/ai-engineering-context/SKILL.md",
@@ -168,7 +170,8 @@
168
170
  "docs/templates/backend-test-case-manifest.schema.json",
169
171
  ".opencode/plugins/loop-agent-transient-retry.js",
170
172
  ".opencode/plugins/loop-agent-context-overflow-compact.js",
171
- ".pi/extensions/loop-agent-context-overflow.js"
173
+ ".pi/extensions/loop-agent-context-overflow.js",
174
+ ".pi/settings.json"
172
175
  ],
173
176
  "initSurface": {
174
177
  "README.md": "managed-block",
@@ -218,6 +221,7 @@
218
221
  ".agents/skills/agent-worker/references/agent-worker-operator.md": "copied",
219
222
  ".agents/skills/loop-agent/SKILL.md": "copied",
220
223
  ".agents/skills/loop-agent/references/command-reference.md": "copied",
224
+ ".agents/skills/loop-agent/references/hybrid-dag.md": "copied",
221
225
  ".agents/skills/loop-agent/references/source-and-plan-practice.md": "copied",
222
226
  ".agents/skills/loop-agent/references/docs-converge.md": "copied",
223
227
  ".agents/skills/ai-engineering-context/SKILL.md": "copied",
@@ -251,7 +255,8 @@
251
255
  "docs/templates/backend-test-case-manifest.schema.json": "copied",
252
256
  ".opencode/plugins/loop-agent-transient-retry.js": "generated",
253
257
  ".opencode/plugins/loop-agent-context-overflow-compact.js": "generated",
254
- ".pi/extensions/loop-agent-context-overflow.js": "generated"
258
+ ".pi/extensions/loop-agent-context-overflow.js": "generated",
259
+ ".pi/settings.json": "generated"
255
260
  },
256
261
  "packageExcluded": [
257
262
  "docs/progress/20*.md",
@@ -273,6 +278,7 @@
273
278
  "package.json",
274
279
  "src/commands/init.ts",
275
280
  "src/commands/client-recovery.ts",
281
+ "src/commands/init-upgrade.ts",
276
282
  "src/cli.ts",
277
283
  "src/cli/update/init-surface-notifier.ts",
278
284
  "src/cli/update/policy.ts",
@@ -297,6 +303,7 @@
297
303
  "patterns": [
298
304
  "src/commands/init.ts",
299
305
  "src/commands/client-recovery.ts",
306
+ "src/commands/init-upgrade.ts",
300
307
  "src/cli.ts",
301
308
  "src/cli/update/init-surface-notifier.ts",
302
309
  "src/cli/update/policy.ts",
@@ -111,6 +111,113 @@
111
111
  }
112
112
  }
113
113
  },
114
+ "workerPolicy": {
115
+ "type": "object",
116
+ "additionalProperties": false,
117
+ "description": "agent-worker 侧声明式策略(含 Night Scheduler)。运行时以 Zod harnessManifestSchema 为准。",
118
+ "properties": {
119
+ "nightScheduler": {
120
+ "type": "object",
121
+ "additionalProperties": false,
122
+ "description": "本地夜间 once 调度默认策略;每个 schedule 会冻结 effective snapshot。",
123
+ "properties": {
124
+ "enabled": {
125
+ "type": "boolean",
126
+ "description": "是否启用 Night Scheduler 能力面。",
127
+ "default": false
128
+ },
129
+ "timezone": {
130
+ "type": "string",
131
+ "minLength": 1,
132
+ "description": "展示与 allowedHours 解释使用的 IANA 时区。",
133
+ "default": "Asia/Shanghai"
134
+ },
135
+ "allowedHours": {
136
+ "type": "array",
137
+ "description": "允许派发的本地时间窗,格式 HH:MM-HH:MM,可跨午夜。",
138
+ "items": {
139
+ "type": "string",
140
+ "pattern": "^\\d{2}:\\d{2}-\\d{2}:\\d{2}$"
141
+ },
142
+ "default": ["00:00-08:00"]
143
+ },
144
+ "maxConcurrency": {
145
+ "type": "integer",
146
+ "minimum": 1,
147
+ "description": "全局同时 running 的 schedule 上限(按有效 execution lease 计数)。",
148
+ "default": 1
149
+ },
150
+ "maxAutoAttempts": {
151
+ "type": "integer",
152
+ "minimum": 1,
153
+ "description": "独立 Worker execution attempt 上限;MVP 为 1。",
154
+ "default": 1
155
+ },
156
+ "misfirePolicy": {
157
+ "enum": ["run-immediately", "wait-next-window", "human-required"],
158
+ "description": "机器休眠或 tick 延迟后的 misfire 策略。",
159
+ "default": "wait-next-window"
160
+ },
161
+ "keepFailedWorktree": {
162
+ "type": "boolean",
163
+ "description": "失败后是否默认保留 worktree 供人工 inspect。",
164
+ "default": true
165
+ },
166
+ "mergePolicy": {
167
+ "enum": ["manual-on-success", "auto-on-success"],
168
+ "description": "成功后的 merge 策略;MVP 仅 manual-on-success。",
169
+ "default": "manual-on-success"
170
+ },
171
+ "autoFollowUp": {
172
+ "type": "object",
173
+ "additionalProperties": false,
174
+ "description": "ADR 0010:失败后是否自动 draft Follow-up(永不 auto-approve)。默认 off。",
175
+ "properties": {
176
+ "enabled": {
177
+ "type": "boolean",
178
+ "description": "为 true 时,terminal failed night execution 可调用 draftFollowUpDecision。",
179
+ "default": false
180
+ },
181
+ "mode": {
182
+ "enum": ["draft-only"],
183
+ "description": "MVP 仅 draft-only;自动 Ready/approve 需另 ADR。",
184
+ "default": "draft-only"
185
+ }
186
+ }
187
+ },
188
+ "traceability": {
189
+ "type": "object",
190
+ "additionalProperties": false,
191
+ "description": "可 push commit 的工作项可追溯策略(任务卡片)。",
192
+ "properties": {
193
+ "required": {
194
+ "type": "boolean",
195
+ "description": "创建 schedule 时是否强制任务卡片。",
196
+ "default": false
197
+ },
198
+ "label": {
199
+ "type": "string",
200
+ "minLength": 1,
201
+ "default": "Task Card"
202
+ },
203
+ "pattern": {
204
+ "type": "string",
205
+ "minLength": 1,
206
+ "description": "workItemRef 正则;默认通用公司卡片格式。",
207
+ "default": "^[A-Z][A-Z0-9]+-[A-Z0-9]+#[0-9]+$"
208
+ },
209
+ "commitMessageTemplate": {
210
+ "type": "string",
211
+ "minLength": 1,
212
+ "description": "必须包含 {workItemRef} 占位符。",
213
+ "default": "{summary}\n\n{workItemRef}"
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+ }
220
+ },
114
221
  "entrypoints": {
115
222
  "type": "object",
116
223
  "description": "具名的仓库相对文件路径,用作人类和 agent 的入口。",
@@ -42,24 +42,34 @@
42
42
  | 用户表达 | 入口 | 执行动作 |
43
43
  | --- | --- | --- |
44
44
  | loop-agent 初始化 / loop agent 初始化 / loop agent初始化 / 初始化 loop-agent | 初始化 | 完成确定性初始化闭环 |
45
- | 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 | 更新校验 | 只读报告,不写入 |
46
- | 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 升级对齐 | 自动应用确定性安全动作;surface 缺失、人工决策、活跃 DAG/Worker 或 Worker 状态无法确认时零写入 |
47
- | 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 安全更新 | check-update,再只执行确定性安全动作 |
45
+ | loop-agent初始化更新 / loop-agent 初始化更新 / 更新 loop-agent 初始化内容 / 升级 loop-agent 初始化 | 初始化升级闭环 | 直接运行 `init upgrade`;自动安全更新、返回单文件语义合并任务并 `--continue` 至稳定终态,禁止停在只读检查或 `needs-model-merge` |
46
+ | 初始化更新校验 / 检查初始化更新 / loop-agent 初始化更新校验 / loop agent初始化更新校验 / 只检查,不要修改 | 更新校验 | 严格只读报告,不创建 upgrade run、不写入 |
47
+ | 初始化对齐 / 升级后对齐 / reconcile 初始化 / loop-agent 初始化对齐 | 初始化升级闭环 | 直接运行 `init upgrade`,而非拼接低层 safe-update 命令 |
48
+ | 初始化安全更新 / 应用初始化更新 / loop-agent 初始化安全更新 / loop agent初始化安全更新 | 初始化升级闭环 | 直接运行 `init upgrade`,由 controller 处理安全更新与验证 |
48
49
  | loop-agent 帮我完成 / 帮我实现 / 帮我修复 / 帮我开发 <需求>;使用 loop-agent 完成 <X>;按 loop-agent 流程处理 <X> | 通用需求实现 | 先提供 PRD 与 `allowedPaths` / `forbiddenPaths` / verify,再 `task advance` → 审查 writeSet gate → `task advance --approve-gate`;主会话不得直接修改业务实现 |
49
50
 
50
- **更新校验(只读)**:只读执行下面命令;**不得自动**执行 `apply-safe` 或模型合并。
51
+ **初始化升级闭环(写入型)**:主会话收到上述写入型表达时自动调用统一入口,并在 controller 返回的具体单文件 `allowedPaths` 内完成必要语义合并后使用 `--continue`。不得停在 `check-update`、`needs-safe-update`、`needs-model-merge` 或 `verification-pending`。默认安装/合并项目 `.opencode/plugins/`、`.pi/extensions/` 与 `.pi/settings.json`;Pi 必须 trust 项目才能加载,默认严禁写 `~/.pi/agent/settings.json`。
51
52
 
52
53
  ```bash
53
- loop-agent init check-update --repo-root . --markdown
54
+ loop-agent init upgrade --repo-root . --json
55
+ loop-agent init upgrade --repo-root . --run-id <run-id> --continue --json
56
+ loop-agent init upgrade --repo-root . --run-id <run-id> --report --markdown
54
57
  ```
55
58
 
56
- **升级对齐**:`loop-agent init reconcile --repo-root .`;surface 缺失、human decisions、活跃 DAG/Worker 或 Worker 状态无法确认时必须零写入。
59
+ **更新校验(只读)**:只读执行下面命令;**不得自动**执行 `apply-safe`、模型合并或创建 upgrade run。
60
+
61
+ ```bash
62
+ loop-agent init check-update --repo-root . --markdown
63
+ ```
57
64
 
58
- **安全更新**:先 check-update;surface 缺失时仅先建立 inferred baseline,再执行确定性 safe actions。不得自动处理 model merge tasks。
65
+ **低层兼容命令(仅诊断/恢复,不是自然语言写入入口)**:`init reconcile`、`init update --bootstrap-surface` `init update --apply-safe` 保留给 controller 诊断、旧自动化和人工排障。主会话收到任何写入型“初始化更新 / 初始化对齐 / 安全更新”表达时,必须唯一进入 `init upgrade`,不得改为直接调用这些低层命令,也不得在它们返回 `needs-model-merge` 后停止。surface 缺失、human decisions、活跃 DAG/Worker 或 Worker 状态无法确认时仍必须零写入。
59
66
 
60
67
  ```bash
68
+ # 只读诊断
61
69
  loop-agent init check-update --repo-root . --markdown
62
- loop-agent init update --repo-root . --bootstrap-surface # 仅 surface 缺失时
70
+ # 低层兼容恢复;不得替代写入型自然语言的统一 init upgrade 入口
71
+ loop-agent init reconcile --repo-root .
72
+ loop-agent init update --repo-root . --bootstrap-surface
63
73
  loop-agent init update --repo-root . --apply-safe
64
74
  ```
65
75
 
package/harness.json CHANGED
@@ -26,6 +26,28 @@
26
26
  ]
27
27
  }
28
28
  },
29
+ "workerPolicy": {
30
+ "nightScheduler": {
31
+ "enabled": true,
32
+ "timezone": "Asia/Shanghai",
33
+ "allowedHours": ["00:00-08:00"],
34
+ "maxConcurrency": 1,
35
+ "maxAutoAttempts": 1,
36
+ "misfirePolicy": "wait-next-window",
37
+ "keepFailedWorktree": true,
38
+ "mergePolicy": "manual-on-success",
39
+ "autoFollowUp": {
40
+ "enabled": false,
41
+ "mode": "draft-only"
42
+ },
43
+ "traceability": {
44
+ "required": true,
45
+ "label": "Task Card",
46
+ "pattern": "^[A-Z][A-Z0-9]+-[A-Z0-9]+#[0-9]+$",
47
+ "commitMessageTemplate": "{summary}\n\n{workItemRef}"
48
+ }
49
+ }
50
+ },
29
51
  "entrypoints": {
30
52
  "readme": "README.md",
31
53
  "agents": "AGENTS.md",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.28.2-beta.1",
3
+ "version": "0.28.3",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: loop-agent
3
3
  description: >-
4
- Use when implementing features, processing PRDs or requirements, running structured loop-agent workflows, creating harness tasks, using Agent DAG / task advance lifecycle, pi-prompt planning/review, cursor-prompt one-shot sidecar intervention, initializing a target project with loop-agent, checking init update status, applying a safe init update, or converging website/governance docs after user-visible changes in loop-agent. Triggers: loop-agent, workflow, structured development, harness task, Agent DAG, task advance, task status, dag validate, dag execute, docs converge, Converge Docs, 文档收敛, 结构化开发, 工作流, 需求实现, PRD 实现, 初始化 loop-agent, loop agent 初始化, loop agent初始化, loop-agent 初始化, 初始化更新校验, loop agent初始化更新校验, 检查初始化更新, 初始化安全更新, loop agent初始化安全更新, 应用初始化更新. 强路由:loop-agent 帮我完成需求, 帮我实现, 帮我修复, 帮我开发, 使用 loop-agent 完成, 按 loop-agent 流程处理, 通用需求实现;这些表达确定性地进入 task advance / Agent DAG CLI,主会话编排而不直接写业务实现.
4
+ Use when implementing features, processing PRDs or requirements, running structured loop-agent workflows, creating harness tasks, using Agent DAG / task advance lifecycle, pi-prompt planning/review, cursor-prompt one-shot sidecar intervention, initializing a target project with loop-agent, checking init update status, applying a safe init update, or converging website/governance docs after user-visible changes in loop-agent. Triggers: loop-agent, workflow, structured development, harness task, Agent DAG, task advance, task status, dag validate, dag execute, docs converge, Converge Docs, 文档收敛, 结构化开发, 工作流, 需求实现, PRD 实现, 初始化 loop-agent, loop agent 初始化, loop agent初始化, loop-agent 初始化, 初始化更新校验, loop agent初始化更新校验, 检查初始化更新, 初始化安全更新, loop agent初始化安全更新, 应用初始化更新, loop-agent初始化更新, loop-agent 初始化更新, 更新 loop-agent 初始化内容, 升级后对齐. 强路由:loop-agent 帮我完成需求, 帮我实现, 帮我修复, 帮我开发, 使用 loop-agent 完成, 按 loop-agent 流程处理, 通用需求实现;这些表达确定性地进入 task advance / Agent DAG CLI,主会话编排而不直接写业务实现.
5
5
  references:
6
6
  - path: references/harness-policy.md
7
7
  required: true
@@ -13,61 +13,53 @@ references:
13
13
 
14
14
  # loop-agent Workflow
15
15
 
16
- Entry: routing and hard rules. Required details come from frontmatter references;`command-reference.md` stays optional below.
16
+ 入口只保留路由与硬约束;细节由 frontmatter required references 注入,`command-reference.md` 保持可选。
17
17
 
18
18
  ## 默认立场
19
19
 
20
- - 主入口是 **Agent DAG**(经 `loop-agent` / 可选 `agent-worker` CLI)。
21
- - 宿主仅作 **Compatibility / Operator Assist**:主会话编排 CLI、审 writeSet、读 status/doctor/report、处理 human gate、验证与 handoff;不写业务代码。
22
- - **严禁**主会话绕过 CLI 用宿主 Edit/Write 改业务实现,或 CLI 失败后「救火改文件」。
23
- - DAG `pi` executor 默认 read-only;`toolProfile: "write"` 时为 bounded writer;sidecar 须 per call 收窄。
24
- - Shell verification 是事实源;长期结论写回 `ai_workspace/loop-agent/`、`docs/decisions/` 或 `skills/`。
20
+ - 主入口是 **Agent DAG**;主会话只编排 CLI、审 writeSet、处理 gate、验证与 handoff,不写业务实现。
21
+ - 禁止绕过 CLI 用宿主 Edit/Write「救火」;失败走 doctor/reconcile/human gate/重跑。
22
+ - `pi` 默认 read-only;仅 `toolProfile: "write"` bounded writer。Shell verification 是事实源。
25
23
 
26
- ## 唯一推荐执行路径
24
+ ## 初始化升级路由
27
25
 
28
- ```bash
29
- # 标准 mutation:task advance(create/import/prepare/generate/validate writeSet gate)
30
- loop-agent task advance <task-id> "Title" \
31
- --prd <prd.md> \
32
- --allowed-path "<glob>" \
33
- --forbidden-path ".harness/**" \
34
- --verify "typecheck:npm run typecheck" \
35
- --json
26
+ - 写入型「**loop-agent初始化更新**」「loop-agent 初始化更新」「更新 loop-agent 初始化内容」「升级后对齐」统一运行 `loop-agent init upgrade --repo-root . --json`;处理 controller 返回的单文件 `allowedPaths`,以同一 run `--continue` 推进至终态,不停在中间状态。
27
+ - 「检查初始化更新」「初始化更新校验」「只检查,不要修改」仅运行 `loop-agent init check-update --repo-root . --markdown`,不得创建 run 或写入。
28
+ - facts 位于 `.harness/init-upgrades/<run-id>/`;禁止业务源码和宽泛 glob。默认管理项目 `.opencode/plugins/`、`.pi/extensions/`、`.pi/settings.json`;Pi trust 后加载,默认不读写 `~/.pi/agent/settings.json`。
36
29
 
37
- # 审查 JSON 中的 gate.writeSet / gate.digest 后批准并长跑
38
- loop-agent task advance <task-id> \
39
- --approve-gate "write-set-review:<digest>" \
40
- --json
30
+ ## 标准路径
41
31
 
42
- # 可选只读(无 refresh 副作用)
32
+ ```bash
33
+ loop-agent task advance <task-id> "Title" --prd <prd.md> \
34
+ --allowed-path "<glob>" --forbidden-path ".harness/**" \
35
+ --verify "typecheck:npm run typecheck" --json
36
+ loop-agent task advance <task-id> \
37
+ --approve-gate "write-set-review:<digest>" --json
43
38
  loop-agent task status <task-id> --json
44
39
  ```
45
40
 
46
- 非微小跨会话任务仍可先 `plan create`(见 references/source-and-plan-practice.md)。主会话只审 gate/writeSet 与验证证据,不拼低层 prepare/run-task/run-dag/promote/closeout 命令串。
41
+ 非微小跨会话任务可先 `plan create`。主会话只审 gate/writeSet 与验证证据,不拼低层 prepare/run-task/run-dag/promote/closeout 命令串。
47
42
 
48
- ## 进阶主题路由
43
+ ## 主题路由
49
44
 
50
45
  | 主题 | Reference |
51
46
  | --- | --- |
52
47
  | Harness policy、Loop、SePO-lite | `references/harness-policy.md` |
53
- | Agent DAG topology、writeSet、recovery | `references/hybrid-dag.md` |
48
+ | DAG topology、writeSet、recovery | `references/hybrid-dag.md` |
54
49
  | Operator commands、`agent-worker` | `references/command-reference.md` |
55
- | 独立验证、failure handling、closeout | `references/verification-and-failure-handling.md` |
50
+ | 验证、failure、closeout | `references/verification-and-failure-handling.md` |
56
51
 
57
52
  ## Hard Rules
58
53
 
59
54
  1. Use vertical tracer bullets across real integration layers;each needs independent acceptance and verification.
60
- 2. Autonomy ≠ governance profile: declare AFK/HITL in human gate; choose `--profile` by risk.
61
- 3. Source materials mandatory: `source/需求.md`、`source/执行约束.md`、优先 `source/references/*` originals。
62
- 4. Structured task config only: `referenceDocs` uses `{ path, name? }[]`, never a string array;`verifyCommands` uses `{ label, command, timeoutMs? }[]`, never a string array。
63
- 5. Agent DAG is the implementation workflow;review three-way checks references + derived source + implementation。
64
- 6. 主会话不绕过 CLI 直接写业务代码;失败只走 doctor/reconcile/human gate/重跑。
65
- 7. DAG `pi` executor read-only unless `toolProfile: "write"`;completed run facts read-only;不得从 read-only DAG/sidecar 写 root `artifacts/`。
66
- 8. No hidden state in chat only;verify before completion.
67
- 9. Client recovery:`init --client-recovery=auto|project|user|off` 投影 transient-retry + overflow-compact + Pi overflow extension;只有 `user` 写 Pi settings;overflow 与 transient 职责分离;check/update 遵守 ownership。
55
+ 2. Autonomy ≠ governance profileAFK/HITL human gate,`--profile` 按风险选择。
56
+ 3. 必须读取 `source/需求.md`、`source/执行约束.md`,优先原始 `source/references/*`。
57
+ 4. `referenceDocs` uses `{ path, name? }[]`, never a string array;`verifyCommands` uses `{ label, command, timeoutMs? }[]`, never a string array。
58
+ 5. Agent DAG 才是实现流;review 三向核对 referencesderived sourceimplementation。
59
+ 6. read-only DAG/sidecar 不得写 root `artifacts/`;completed run facts 只读。
60
+ 7. 关键状态不得只留在聊天;完成前必须验证。
61
+ 8. Client recovery `auto|project|user|off` 遵守 ownership;仅 `user` 写用户 Pi settings,overflow 与 transient 分工。
68
62
 
69
63
  ## References
70
64
 
71
- Required(按需 inline):`references/harness-policy.md`、`references/hybrid-dag.md`、`references/verification-and-failure-handling.md`
72
-
73
- Optional 索引见 `references/README.md`;常用:`command-reference.md`、`long-running-loop.md`、`orchestrator-and-interventions.md`、`docs-converge.md`。
65
+ Required:`references/harness-policy.md`、`references/hybrid-dag.md`、`references/verification-and-failure-handling.md`。Optional 见 `references/README.md`;常用 `references/command-reference.md`、`references/source-and-plan-practice.md`、`references/long-running-loop.md`、`references/orchestrator-and-interventions.md`、`references/docs-converge.md`。