@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.
Files changed (127) hide show
  1. package/AGENTS.md +2 -0
  2. package/CHANGELOG.md +56 -305
  3. package/README.md +13 -19
  4. package/dist/commands/init.js +92 -23
  5. package/dist/executors/pi-event-serializer.js +33 -11
  6. package/dist/executors/shell-executor.js +200 -21
  7. package/dist/infrastructure/evaluation/candidate-store.js +5 -1
  8. package/dist/worker/observe/spec-evidence.js +19 -10
  9. package/dist/worker/observe/static/app.js +4 -3
  10. package/dist/worker/observe/static/constants.js +10 -2
  11. package/dist/worker/observe/static/dag-helpers.js +37 -8
  12. package/dist/worker/observe/static/dom.js +159 -0
  13. package/dist/worker/observe/static/format-pool.d.ts +71 -0
  14. package/dist/worker/observe/static/format-pool.js +67 -0
  15. package/dist/worker/observe/static/format.js +27 -2
  16. package/dist/worker/observe/static/index.html +76 -34
  17. package/dist/worker/observe/static/kpi.js +12 -6
  18. package/dist/worker/observe/static/markdown-render.js +124 -0
  19. package/dist/worker/observe/static/shell-chrome.js +8 -2
  20. package/dist/worker/observe/static/state.js +20 -0
  21. package/dist/worker/observe/static/styles.css +662 -60
  22. package/dist/worker/observe/static/views/dag-inspector.js +65 -142
  23. package/dist/worker/observe/static/views/dag.js +9 -0
  24. package/dist/worker/observe/static/views/dashboard.js +512 -269
  25. package/dist/worker/observe/static/views/pool.js +595 -237
  26. package/dist/worker/observe/static/views/session-timeline.js +577 -11
  27. package/dist/workflows/dag/backend-test-case-manifest.js +503 -0
  28. package/dist/workflows/dag/backend-test-execution-contract.js +353 -0
  29. package/dist/workflows/dag/backend-test-result-contract.js +568 -0
  30. package/dist/workflows/dag/decision-envelope.js +57 -2
  31. package/dist/workflows/dag/frontend-implementation-contract.js +240 -0
  32. package/dist/workflows/dag/frontend-project-capability.js +309 -0
  33. package/dist/workflows/dag/frontend-repair.js +341 -0
  34. package/dist/workflows/dag/frontend-risk.js +161 -0
  35. package/dist/workflows/dag/frontend-verification-trace.js +190 -0
  36. package/dist/workflows/dag/init-hybrid.js +1020 -125
  37. package/dist/workflows/dag/repair-artifact.js +43 -3
  38. package/dist/workflows/dag/skill-instructions.js +4 -2
  39. package/dist/workflows/dag/types.js +29 -8
  40. package/docs/README.md +2 -0
  41. package/docs/agent-dag-recovery-playbook.md +3 -3
  42. package/docs/agent-dag-runner.md +3 -3
  43. package/docs/architecture/README.md +3 -3
  44. package/docs/architecture/dag-execution.md +1 -1
  45. package/docs/architecture/evolution.md +13 -13
  46. package/docs/architecture/facts-and-state.md +1 -1
  47. package/docs/architecture/runtime-boundaries.md +7 -7
  48. package/docs/architecture/system-overview.md +3 -3
  49. package/docs/architecture/worker-and-feature.md +3 -3
  50. package/docs/design/README.md +7 -7
  51. package/docs/development-principles.md +4 -4
  52. package/docs/exec-plans/active/README.md +2 -4
  53. package/docs/exec-plans/completed/README.md +29 -6
  54. package/docs/feature-workflow.md +57 -32
  55. package/docs/init-surface.manifest.json +21 -3
  56. package/docs/loop-agent-harness.md +8 -8
  57. package/docs/production-readiness.md +1 -1
  58. package/docs/progress/README.md +20 -3
  59. package/docs/reports/README.md +53 -7
  60. package/docs/templates/agent-dag.supervised-implementation.json +127 -8
  61. package/docs/templates/backend-test-case-manifest.schema.json +190 -0
  62. package/docs/templates/backend-test-dag.classify.prompt.md +75 -0
  63. package/docs/templates/backend-test-dag.generate-pytest.prompt.md +6 -4
  64. package/docs/templates/backend-test-dag.json +269 -21
  65. package/docs/templates/backend-test-dag.retrospect.prompt.md +44 -30
  66. package/docs/templates/backend-test-dag.review-cases.prompt.md +6 -4
  67. package/docs/templates/backend-test-execution.schema.json +133 -0
  68. package/docs/templates/backend-test-result.schema.json +99 -0
  69. package/docs/templates/branch-merge-report.md +93 -0
  70. package/docs/templates/frontend-eval/fixtures/failures/01-type-build-error.md +17 -0
  71. package/docs/templates/frontend-eval/fixtures/failures/02-unit-component-test-fail.md +16 -0
  72. package/docs/templates/frontend-eval/fixtures/failures/03-fixture-schema-drift.md +16 -0
  73. package/docs/templates/frontend-eval/fixtures/failures/04-missing-loading-empty-error-state.md +16 -0
  74. package/docs/templates/frontend-eval/fixtures/failures/05-forbidden-write-writeset-expansion.md +16 -0
  75. package/docs/templates/frontend-eval/fixtures/failures/06-unapproved-dependency-add.md +16 -0
  76. package/docs/templates/frontend-eval/fixtures/failures/07-mock-production-on.md +21 -0
  77. package/docs/templates/frontend-eval/fixtures/functional/01-simple-component-style.md +29 -0
  78. package/docs/templates/frontend-eval/fixtures/functional/02-form-validation.md +28 -0
  79. package/docs/templates/frontend-eval/fixtures/functional/03-list-detail-page.md +28 -0
  80. package/docs/templates/frontend-eval/fixtures/functional/04-api-mock.md +29 -0
  81. package/docs/templates/frontend-eval/fixtures/functional/05-permission-auth-gated-ui.md +27 -0
  82. package/docs/templates/frontend-eval/fixtures/functional/06-ssr-server-client-boundary.md +28 -0
  83. package/docs/templates/frontend-eval/fixtures/functional/07-shared-public-component-api.md +28 -0
  84. package/docs/templates/frontend-eval/fixtures/functional/08-pure-local-no-remote.md +27 -0
  85. package/docs/templates/frontend-eval/metrics.md +138 -0
  86. package/docs/templates/frontend-eval/smoke-targets.md +53 -0
  87. package/docs/templates/frontend-implementation-contract.schema.json +27 -0
  88. package/docs/verification-matrix.md +1 -1
  89. package/examples/decision-gate-agent-dag.json +4 -4
  90. package/examples/hybrid-loop-agent-dag.json +1 -1
  91. package/package.json +2 -2
  92. package/skills/ai-engineering-context/SKILL.md +2 -2
  93. package/skills/browser-tools/SKILL.md +196 -0
  94. package/skills/browser-tools/browser-content.js +103 -0
  95. package/skills/browser-tools/browser-cookies.js +35 -0
  96. package/skills/browser-tools/browser-eval.js +53 -0
  97. package/skills/browser-tools/browser-hn-scraper.js +108 -0
  98. package/skills/browser-tools/browser-nav.js +44 -0
  99. package/skills/browser-tools/browser-pick.js +162 -0
  100. package/skills/browser-tools/browser-screenshot.js +34 -0
  101. package/skills/browser-tools/browser-start.js +86 -0
  102. package/skills/browser-tools/package-lock.json +2556 -0
  103. package/skills/browser-tools/package.json +19 -0
  104. package/skills/frontend-implementation/SKILL.md +3 -1
  105. package/skills/frontend-implementation/references/node-contracts.md +17 -66
  106. package/skills/frontend-verification/SKILL.md +1 -1
  107. package/skills/grill-with-docs/SKILL.md +5 -5
  108. package/skills/grill-with-docs/adr-format.md +3 -3
  109. package/skills/init-capability-evolution/SKILL.md +5 -5
  110. package/skills/loop-agent/SKILL.md +5 -5
  111. package/skills/loop-agent/references/README.md +3 -3
  112. package/skills/loop-agent/references/command-reference.md +39 -17
  113. package/skills/loop-agent/references/docs-converge.md +15 -15
  114. package/skills/loop-agent/references/harness-policy.md +2 -2
  115. package/skills/loop-agent/references/hybrid-dag.md +20 -15
  116. package/skills/loop-agent/references/multi-worktree.md +1 -1
  117. package/skills/loop-agent/references/orchestrator-and-interventions.md +8 -8
  118. package/skills/loop-agent/references/task-workflow.md +1 -1
  119. package/skills/loop-agent/references/verification-and-failure-handling.md +6 -4
  120. package/skills/requesting-code-review/SKILL.md +1 -1
  121. package/skills/systematic-debugging/CREATION-LOG.md +3 -3
  122. package/skills/systematic-debugging/SKILL.md +1 -1
  123. package/skills/systematic-debugging/test-academic.md +1 -1
  124. package/skills/systematic-debugging/test-pressure-1.md +1 -1
  125. package/skills/systematic-debugging/test-pressure-2.md +1 -1
  126. package/skills/systematic-debugging/test-pressure-3.md +1 -1
  127. package/skills/verification-before-completion/SKILL.md +1 -1
@@ -0,0 +1,27 @@
1
+ # Functional fixture: pure local no-remote interaction
2
+
3
+ - **fixtureId**: `fe-func-pure-local-no-remote`
4
+ - **taskTitle**: 实现纯前端计数器与本地 localStorage 偏好
5
+ - **expectedRoute**: `frontend-implementation`
6
+ - **expectedRiskLevel**: `small`(M4 前仅标注)
7
+ - **expectedGateIntensity**: mock assess 仍存在但策略应为 not-needed
8
+ - **expectedMockStrategy**: `not-needed`(正证据:无远程 API)
9
+ - **Browser**: **out-of-scope / not-run**
10
+
11
+ ## 需求要点
12
+
13
+ - 状态仅存组件 state / localStorage。
14
+ - 不添加 Mock 框架或 API client。
15
+ - 交互可用 vitest + testing-library 验证。
16
+
17
+ ## allowedPaths 建议
18
+
19
+ ```text
20
+ src/widgets/Counter/**
21
+ test/widgets/Counter/**
22
+ ```
23
+
24
+ ## expected verification 形态
25
+
26
+ - Static + behavior(本地交互)
27
+ - 无 mock-verify 节点
@@ -0,0 +1,138 @@
1
+ # Frontend-implementation Eval 指标合同(M0)
2
+
3
+ 状态:M0 冻结定义;M3 前部分指标仅定义不采集。
4
+ 边界:**不包含 Browser / 视觉 / 真实页面启动**;Browser 相关一律记 `not-run` 或从分母排除(见下)。
5
+
6
+ ## 通用规则
7
+
8
+ ### 样本量
9
+
10
+ - **功能通过率类**:以选定 functional fixture 集为分母单元(每个 fixture × 每个 smoke target 记一次 trial,或按计划固定 trial 表)。
11
+ - **失败行为类**:以 failure fixture 集为单元;期望「正确阻断 / 正确分类」为成功,误放行为失败。
12
+ - **基线批次**:记录 `sampleSetId`、fixture 列表 hash、controller 版本、commit SHA。无记录则整批指标 `unavailable`。
13
+
14
+ ### 缺失数据
15
+
16
+ | 符号 | 含义 |
17
+ |------|------|
18
+ | `N/A` | 能力尚未实现,指标定义保留但**禁止填 0** 伪装基线(例:M3 前 repair 后通过率) |
19
+ | `unavailable` | 实现已存在但本批 run 未记录分子或分母所需字段(例:无 token 账本) |
20
+ | `not-run` | 明确未执行的检查(例:Browser);不得计入「通过」 |
21
+ | `0` | 仅当分子与分母均有完整证据且分子确实为零 |
22
+
23
+ 报告必须同时写出 **分子、分母、比率、样本量 n、缺失原因**。禁止只写百分比。
24
+
25
+ ### 耗时 / token 来源
26
+
27
+ | 字段 | 来源(优先序) |
28
+ |------|----------------|
29
+ | 墙钟耗时 | DAG run 起止时间戳(run.json / harness run record);否则 shell `date` 外包测量 |
30
+ | 节点耗时 | 各 node start/end;缺失则节点级 `unavailable`,仅汇总 run 级 |
31
+ | token | executor / model usage 汇总(若 run 记录 `tokensUsed`);否则 `unavailable`,**不得估计** |
32
+
33
+ ### Browser 边界
34
+
35
+ - 任何指标不得因「未跑浏览器」而扣分或加分。
36
+ - 不得将 Mock-backed 或 component test 记为 Browser pass。
37
+ - 指标表中 Browser 行固定 `not-run`(本计划范围内)。
38
+
39
+ ---
40
+
41
+ ## 最低指标集
42
+
43
+ ### 1. 首次静态通过率
44
+
45
+ - **名称**:`first_static_pass_rate`
46
+ - **分子**:第一次执行 `frontend-static-verify-shell` 即 exit 0 的 trial 数
47
+ - **分母**:完成到 static verify 的 trial 数(writer 已跑且 static 已调度)
48
+ - **排除**:DAG 在 writer 前被 Mock/design gate 阻断的 trial(记入 gate 阻断类,不进分母)
49
+ - **M0**:定义 + 手工/脚本采集方式;基线可 `unavailable` 直至有 fixture dogfood
50
+
51
+ ### 2. 首次行为通过率
52
+
53
+ - **名称**:`first_behavior_pass_rate`
54
+ - **分子**:第一次执行 `frontend-behavior-verify-shell` 即 exit 0 的 trial 数
55
+ - **分母**:完成到 behavior verify 的 trial 数
56
+ - **说明**:行为通过不等于真实 API 联调;Mock-backed 仍可计入 behavior pass,但须另计「误报」指标
57
+
58
+ ### 3. repair 后最终通过率
59
+
60
+ - **名称**:`post_repair_final_pass_rate`
61
+ - **分子**:在 ≤ max repair attempts 后 static+behavior+review gate 均通过的 trial 数
62
+ - **分母**:进入 repair 资格判定的 trial 数(repairable 失败)
63
+ - **M0–M2**:固定记 **`N/A`**(无 frontend repair runtime)
64
+ - **M3+**:按 failure taxonomy 采集
65
+
66
+ ### 4. 需求追踪完整率
67
+
68
+ - **名称**:`requirement_trace_completeness_rate`
69
+ - **M0 分子**:`frontend-requirement-coverage-shell` 通过(或无显式 ID 时记 `not-applicable-trial`)且 closeout/plan 仍含全部绑定 ID 的 trial 数
70
+ - **M0 分母**:含显式 REQ/BR/AC 的 trial 数
71
+ - **语义**:M0 为 **identifier-presence**,不是文件/UI state 完整映射
72
+ - **M1+**:改为 validated contract 中每条 ID → target/state/test 映射完整率(届时更新本文件版本)
73
+
74
+ ### 5. 越界写入次数
75
+
76
+ - **名称**:`forbidden_write_count`
77
+ - **分子**:write-guard / exclusive writeSet 拒绝次数 + review 确认的越界路径写入次数(按 trial 计数事件,可 >1)
78
+ - **分母**:含 writer 的 trial 数(用于率:`forbidden_write_rate = 分子/分母`)
79
+ - **期望基线**:治理应使成功 closeout 的 trial 分子为 0
80
+
81
+ ### 6. 错误依赖次数
82
+
83
+ - **名称**:`unapproved_dependency_count`
84
+ - **分子**:未批准新增 dependency(package.json 变更未授权、review 标记 Important 等)事件数
85
+ - **分母**:含 writer 的 trial 数
86
+ - **采集**:diff 检查 + review findings 标签(需约定 finding code,M0 用人工标注)
87
+
88
+ ### 7. Mock / 真实联调误报次数
89
+
90
+ - **名称**:`mock_real_integration_false_claim_count`
91
+ - **分子**:closeout 或报告将 Mock-backed 证据描述为真实 API 已联通,或省略 `Real integration: pending` 的 trial 数
92
+ - **分母**:`MOCK_STRATEGY` 为 native | browser-intercept | request-adapter 且无真实后端证据的 trial 数
93
+ - **期望**:0
94
+
95
+ ### 8. 节点数 / 模型节点数
96
+
97
+ - **名称**:`node_count`, `model_node_count`
98
+ - **定义**:
99
+ - `node_count` = `spec.tasks.length`
100
+ - `model_node_count` = `executor === "pi"`(或非 shell/static)的节点数
101
+ - **报告**:按 topology 变体分别记录(standard / +coverage / +mock-verify / blocked)
102
+ - **M0 基线参考(standard 无 optional)**:`node_count=16`;模型节点含 contract/scout/mock-assess/plan/design/plan-revision/final-design-review/implement/review/closeout(以 builder 为准,测试锁定 id 列表)
103
+
104
+ ### 9. 墙钟耗时
105
+
106
+ - **名称**:`wall_clock_ms`
107
+ - **分子/分母**:不适用;报告 **p50 / p95 / max** 与 **n**
108
+ - **来源**:见通用规则;缺失 → `unavailable`
109
+
110
+ ### 10. Token
111
+
112
+ - **名称**:`tokens_total`
113
+ - **报告**:sum 与 per-role breakdown(若可得);缺失 → `unavailable`
114
+ - **禁止**:用字符数或经验公式估算后当作实测
115
+
116
+ ---
117
+
118
+ ## 建议采集表头(CSV / Markdown)
119
+
120
+ ```text
121
+ sampleSetId, trialId, fixtureId, targetId, controllerVersion, commitSha,
122
+ topologyVariant, mockStrategy,
123
+ first_static_pass (0|1|unavailable),
124
+ first_behavior_pass (0|1|unavailable),
125
+ post_repair_final_pass (0|1|N/A|unavailable),
126
+ requirement_trace_ok (0|1|not-applicable|unavailable),
127
+ forbidden_write_count,
128
+ unapproved_dependency_count,
129
+ mock_real_false_claim (0|1|n/a),
130
+ node_count, model_node_count,
131
+ wall_clock_ms, tokens_total,
132
+ browser_status (not-run)
133
+ ```
134
+
135
+ ## 与 M1+ 的关系
136
+
137
+ - 指标名称稳定;分子定义可在 M1(contract)、M3(repair)版本化增补,须在 CHANGELOG/本文件注明 **metrics schema version**。
138
+ - 当前 **metrics schema version: m0.1**。
@@ -0,0 +1,53 @@
1
+ # Frontend-implementation Smoke Targets 策略(M0)
2
+
3
+ ## 原则
4
+
5
+ 1. **仅使用平台临时目录**(`os.tmpdir()` / CI runner temp),**不得**在 loop-agent 仓库根写入可运行 app、`node_modules` 或构建产物。
6
+ 2. **不启动浏览器**;不安装 Playwright/Cypress;不声明 Browser verification。
7
+ 3. M0 只冻结**目标描述与生成约定**;真正从 fixture 物化临时项目在 **M1+ dogfood** 时实施。
8
+ 4. 临时项目生命周期:创建 → 最小依赖安装(仅 temp)→ 跑冻结 static/behavior 命令 → 删除;失败日志可复制到 run-owned harness 目录,不进 git。
9
+
10
+ ## 三类可控目标
11
+
12
+ | targetId | 栈 | 最小信号 | 建议 static | 建议 behavior | Browser |
13
+ |----------|----|----------|-------------|---------------|---------|
14
+ | `react-vitest-min` | React + Vitest + TypeScript | `package.json` scripts: `typecheck`, `build`/`vite build`, `test`;`src/**/*.tsx` | `npm run typecheck`(+ build 若存在) | `npm test` / `npx vitest run` | not-run |
15
+ | `nextjs-min` | Next.js(App Router 信号) | `app/` 或 `pages/` + `"next"` dependency;`"use client"` 边界样例 | `npm run typecheck` / `next build`(temp only) | 聚焦 unit/component test,**不** `next start` 作完成证据 | not-run |
16
+ | `vue-vitest-min` | Vue 3 + Vitest | `*.vue` + vitest config | `npm run typecheck` 或 `vue-tsc` | `npm test` | not-run |
17
+
18
+ ## 临时项目生成约定(M1+ 实施)
19
+
20
+ ```text
21
+ ROOT="$(mktemp -d "${TMPDIR:-/tmp}/fe-eval-XXXXXX")"
22
+ # 从 docs/templates/frontend-eval/fixtures/... 渲染 package.json / 源文件骨架
23
+ # npm install --prefix "$ROOT" # 仅 temp
24
+ # 在 $ROOT 执行冻结 verify 命令
25
+ # rm -rf "$ROOT"
26
+ ```
27
+
28
+ 约束:
29
+
30
+ - fixture **不得**内嵌密钥、真实 PII、恶意脚本。
31
+ - 依赖版本钉死在 fixture 清单,避免 eval 漂移。
32
+ - 不得 `npm link` 工作区 loop-agent 作为被测 app 依赖(controller 版本另按任务约束)。
33
+
34
+ ## 与 functional / failure fixtures 映射
35
+
36
+ | fixture 类别 | 优先 target |
37
+ |--------------|-------------|
38
+ | simple component / style | react-vitest-min, vue-vitest-min |
39
+ | form validation | react-vitest-min |
40
+ | list/detail | react-vitest-min, nextjs-min |
41
+ | API + Mock | react-vitest-min(+ MSW 骨架) |
42
+ | permission UI | react-vitest-min, nextjs-min |
43
+ | SSR / server-client boundary | nextjs-min |
44
+ | shared component API | react-vitest-min |
45
+ | pure local no-remote | 任一 |
46
+ | failure: type/build | 任一 |
47
+ | failure: mock production-on | react-vitest-min + mock 骨架 |
48
+
49
+ ## 明确不做
50
+
51
+ - 不在本仓库 `website/**` 或 examples 中落永久 dogfood app 作为 M0 必需项。
52
+ - 不把 `scripts/check-repo.sh` 全量当作前端 app 验证。
53
+ - 不提交 temp 安装树或截图基线。
@@ -0,0 +1,27 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "frontend-implementation-contract-v1",
4
+ "title": "Frontend Implementation Contract v1",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": ["schemaVersion", "sourceBinding", "riskLevel", "targets", "requirements", "uiStates", "interactions", "mockApi", "designEvidence", "verificationTargets", "evidenceGaps"],
8
+ "properties": {
9
+ "schemaVersion": { "const": 1 },
10
+ "sourceBinding": { "$ref": "#/$defs/sourceBinding" },
11
+ "riskLevel": { "enum": ["small", "standard", "high-risk"] },
12
+ "targets": { "type": "object", "additionalProperties": false, "required": ["files"], "properties": { "files": { "type": "array", "minItems": 1, "items": { "$ref": "#/$defs/path" } }, "routes": { "type": "array", "items": { "type": "string", "pattern": "^/" } }, "publicApiChanges": { "type": "array", "items": { "type": "string", "minLength": 1 } } } },
13
+ "requirements": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "implementationTargets", "verificationTargetIds"], "properties": { "id": { "$ref": "#/$defs/requirementId" }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "evidenceGap": { "$ref": "#/$defs/gap" } } } },
14
+ "uiStates": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "applicable"], "properties": { "name": { "type": "string", "minLength": 1 }, "applicable": { "type": "boolean" }, "expectedBehavior": { "type": "string", "minLength": 1 }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string", "minLength": 1 } }, "notApplicableReason": { "type": "string", "minLength": 1 } } } },
15
+ "interactions": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["name", "implementationTargets", "verificationTargetIds"], "properties": { "name": { "type": "string", "minLength": 1 }, "implementationTargets": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "verificationTargetIds": { "type": "array", "items": { "type": "string" } } } } },
16
+ "mockApi": { "type": "object", "additionalProperties": false, "required": ["strategy", "productionDefaultOff", "activation", "endpoints"], "properties": { "strategy": { "enum": ["native", "browser-intercept", "request-adapter", "not-needed"] }, "productionDefaultOff": { "const": true }, "activation": { "type": "string", "minLength": 1 }, "endpoints": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["method", "path"], "properties": { "method": { "enum": ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"] }, "path": { "type": "string", "pattern": "^/" }, "fixture": { "$ref": "#/$defs/path" }, "consumer": { "$ref": "#/$defs/path" } } } } } },
17
+ "designEvidence": { "type": "object", "additionalProperties": false, "required": ["source", "paths", "conflicts"], "properties": { "source": { "type": "string", "minLength": 1 }, "paths": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "conflicts": { "type": "array", "items": { "type": "string", "minLength": 1 } } } },
18
+ "verificationTargets": { "type": "array", "items": { "type": "object", "additionalProperties": false, "required": ["id", "type", "commandLabel", "file", "requirementIds", "uiStates"], "properties": { "id": { "type": "string", "minLength": 1 }, "type": { "enum": ["static", "unit", "component", "integration", "mock"] }, "commandLabel": { "type": "string", "minLength": 1 }, "file": { "$ref": "#/$defs/path" }, "symbol": { "type": "string", "minLength": 1 }, "requirementIds": { "type": "array", "items": { "$ref": "#/$defs/requirementId" } }, "uiStates": { "type": "array", "items": { "type": "string", "minLength": 1 } } } } },
19
+ "evidenceGaps": { "type": "array", "items": { "$ref": "#/$defs/gap" } }
20
+ },
21
+ "$defs": {
22
+ "path": { "type": "string", "minLength": 1, "pattern": "^(?!/)(?!.*(?:^|/)\\.\\.(?:/|$))(?!.*\\\\).+$" },
23
+ "requirementId": { "type": "string", "pattern": "^(?:REQ|BR|AC)-[A-Z0-9]+(?:-[A-Z0-9]+)*$" },
24
+ "gap": { "type": "object", "additionalProperties": false, "required": ["description", "blocking"], "properties": { "requirementId": { "$ref": "#/$defs/requirementId" }, "description": { "type": "string", "minLength": 1 }, "blocking": { "type": "boolean" } } },
25
+ "sourceBinding": { "type": "object", "additionalProperties": false, "required": ["taskId", "requirementPath", "requirementSha256", "referencePaths", "requirementIds"], "properties": { "taskId": { "type": "string", "minLength": 1 }, "requirementPath": { "$ref": "#/$defs/path" }, "requirementSha256": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, "referencePaths": { "type": "array", "items": { "$ref": "#/$defs/path" } }, "requirementIds": { "type": "array", "items": { "$ref": "#/$defs/requirementId" } } } }
26
+ }
27
+ }
@@ -59,7 +59,7 @@ Windows 上通过 Git Bash 或已配置的兼容 Bash 运行 `scripts/*.sh`。
59
59
 
60
60
  没有相关门禁的新鲜命令输出,不得声明完成。
61
61
 
62
- Production Readiness v0.1 工作以 `docs/production-readiness.md` 与 `docs/templates/production-readiness-checklist.md` 为验收契约。最终 hardening closeout 需要:
62
+ Production Readiness v0.1 工作以 `ai_workspace/loop-agent/production-readiness.md` 与 `ai_workspace/loop-agent/templates/production-readiness-checklist.md` 为验收契约。最终 hardening closeout 需要:
63
63
 
64
64
  ```bash
65
65
  bash scripts/ci.sh
@@ -6,7 +6,7 @@
6
6
  "contract-pi returns a read-only implementation contract",
7
7
  "implement-pi writes only inside the declared writeSet",
8
8
  "verify-shell archives deterministic verification outputs",
9
- "decision-pi returns a DECISION_ENVELOPE_JSON block matching docs/templates/agent-dag-decision-envelope.schema.json",
9
+ "decision-pi returns a DECISION_ENVELOPE_JSON block matching ai_workspace/loop-agent/templates/agent-dag-decision-envelope.schema.json",
10
10
  "closeout-pi summarizes the result without writing files"
11
11
  ],
12
12
  "globalConstraints": [
@@ -77,7 +77,7 @@
77
77
  "artifacts/**"
78
78
  ],
79
79
  "outputContract": "Plain Markdown implementation contract; no file writes.",
80
- "subtask_prompt": "Read the DAG objective, success criteria, docs/loop-agent-harness.md, and docs/agent-dag-runner.md. Return a concise contract covering scope, write boundaries, risks, and verification expectations. Do not edit files."
80
+ "subtask_prompt": "Read the DAG objective, success criteria, ai_workspace/loop-agent/loop-agent-harness.md, and ai_workspace/loop-agent/agent-dag-runner.md. Return a concise contract covering scope, write boundaries, risks, and verification expectations. Do not edit files."
81
81
  },
82
82
  {
83
83
  "id": "implement-pi",
@@ -144,8 +144,8 @@
144
144
  ".harness/**",
145
145
  "artifacts/**"
146
146
  ],
147
- "outputContract": "Markdown with exactly one ```DECISION_ENVELOPE_JSON fenced block (info string DECISION_ENVELOPE_JSON, not json) matching docs/templates/agent-dag-decision-envelope.schema.json, plus a short evidence/risk summary. No file writes.",
148
- "subtask_prompt_markdown": "../docs/templates/agent-dag-decision-gate.prompt.md",
147
+ "outputContract": "Markdown with exactly one ```DECISION_ENVELOPE_JSON fenced block (info string DECISION_ENVELOPE_JSON, not json) matching ai_workspace/loop-agent/templates/agent-dag-decision-envelope.schema.json, plus a short evidence/risk summary. No file writes.",
148
+ "subtask_prompt_markdown": "../ai_workspace/loop-agent/templates/agent-dag-decision-gate.prompt.md",
149
149
  "decisionGate": {
150
150
  "enabled": true,
151
151
  "schemaVersion": 1,
@@ -77,7 +77,7 @@
77
77
  "artifacts/**"
78
78
  ],
79
79
  "outputContract": "Plain Markdown implementation contract summary; no file writes.",
80
- "subtask_prompt": "阅读 ./README.md 与 docs/agent-dag-runner.md,返回 10 行以内的实现契约摘要(只读分析 + 文档建议,不改代码/文档/artifacts)。指出是否适合并行 scout 与窄 writeSet。"
80
+ "subtask_prompt": "阅读 ./README.md 与 ai_workspace/loop-agent/agent-dag-runner.md,返回 10 行以内的实现契约摘要(只读分析 + 文档建议,不改代码/文档/artifacts)。指出是否适合并行 scout 与窄 writeSet。"
81
81
  },
82
82
  {
83
83
  "id": "scout-src",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tea-agent/loop-agent",
3
- "version": "0.13.0-beta.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "loop-agent": "bin/loop-agent.js",
@@ -55,7 +55,7 @@
55
55
  "clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
56
56
  "build": "npm run clean && tsc -p tsconfig.build.json && node -e \"const fs=require('node:fs');const p='dist/worker/observe/static';fs.mkdirSync(p,{recursive:true});fs.cpSync('src/worker/observe/static',p,{recursive:true});\"",
57
57
  "prepack": "npm run build",
58
- "prepublishOnly": "node scripts/check-npm-publish-policy.mjs && npm run typecheck && npm test && npm run build",
58
+ "prepublishOnly": "npm run typecheck && npm test && npm run build",
59
59
  "lint": "tsc --noEmit",
60
60
  "typecheck": "tsc --noEmit",
61
61
  "test": "node scripts/run-tests.mjs",
@@ -15,7 +15,7 @@ Context 是工程 artifact,不是 chat 残留。须显式保留 requirements
15
15
 
16
16
  ## Context Priority
17
17
 
18
- 按以下顺序优先采信 facts:最新 user instruction;task source/contract;DAG/Loop artifacts;docs/plans/ADRs;code/tests;chat history 仅作 hint。
18
+ 按以下顺序优先采信 facts:最新 user instruction;task source/contract;DAG/Loop artifacts;ai_workspace/loop-agent/plans/ADRs;code/tests;chat history 仅作 hint。
19
19
 
20
20
  若 sources 冲突,停止并点明冲突。
21
21
 
@@ -37,7 +37,7 @@ read-only node 只能在 node output 返回 findings。不得在 repo 中创建
37
37
 
38
38
  ## Persistence Rules
39
39
 
40
- Requirements 与 constraints 写入 task `source/`。Execution state 与 node artifacts 写入 `.harness/`。Durable plans、reports、decisions 写入 `docs/`。可复用 process guidance 写入 `skills/`。Chat 仅 transient。
40
+ Requirements 与 constraints 写入 task `source/`。Execution state 与 node artifacts 写入 `.harness/`。Durable plans、reports、decisions 写入 `ai_workspace/loop-agent/`。可复用 process guidance 写入 `.agents/skills/`。Chat 仅 transient。
41
41
 
42
42
  ## Failure Handling
43
43
 
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: browser-tools
3
+ description: Interactive browser automation via Chrome DevTools Protocol. Use when you need to interact with web pages, test frontends, or when user interaction with a visible browser is required.
4
+ ---
5
+
6
+ # Browser Tools
7
+
8
+ Chrome DevTools Protocol tools for agent-assisted web automation. These tools connect to Chrome running on `:9222` with remote debugging enabled.
9
+
10
+ ## Setup
11
+
12
+ Run once before first use:
13
+
14
+ ```bash
15
+ cd {baseDir}/browser-tools
16
+ npm install
17
+ ```
18
+
19
+ ## Start Chrome
20
+
21
+ ```bash
22
+ {baseDir}/browser-start.js # Fresh profile
23
+ {baseDir}/browser-start.js --profile # Copy user's profile (cookies, logins)
24
+ ```
25
+
26
+ Launch Chrome with remote debugging on `:9222`. Use `--profile` to preserve user's authentication state.
27
+
28
+ ## Navigate
29
+
30
+ ```bash
31
+ {baseDir}/browser-nav.js https://example.com
32
+ {baseDir}/browser-nav.js https://example.com --new
33
+ ```
34
+
35
+ Navigate to URLs. Use `--new` flag to open in a new tab instead of reusing current tab.
36
+
37
+ ## Evaluate JavaScript
38
+
39
+ ```bash
40
+ {baseDir}/browser-eval.js 'document.title'
41
+ {baseDir}/browser-eval.js 'document.querySelectorAll("a").length'
42
+ ```
43
+
44
+ Execute JavaScript in the active tab. Code runs in async context. Use this to extract data, inspect page state, or perform DOM operations programmatically.
45
+
46
+ ## Screenshot
47
+
48
+ ```bash
49
+ {baseDir}/browser-screenshot.js
50
+ ```
51
+
52
+ Capture current viewport and return temporary file path. Use this to visually inspect page state or verify UI changes.
53
+
54
+ ## Pick Elements
55
+
56
+ ```bash
57
+ {baseDir}/browser-pick.js "Click the submit button"
58
+ ```
59
+
60
+ **IMPORTANT**: Use this tool when the user wants to select specific DOM elements on the page. This launches an interactive picker that lets the user click elements to select them. The user can select multiple elements (Cmd/Ctrl+Click) and press Enter when done. The tool returns CSS selectors for the selected elements.
61
+
62
+ Common use cases:
63
+ - User says "I want to click that button" → Use this tool to let them select it
64
+ - User says "extract data from these items" → Use this tool to let them select the elements
65
+ - When you need specific selectors but the page structure is complex or ambiguous
66
+
67
+ ## Cookies
68
+
69
+ ```bash
70
+ {baseDir}/browser-cookies.js
71
+ ```
72
+
73
+ Display all cookies for the current tab including domain, path, httpOnly, and secure flags. Use this to debug authentication issues or inspect session state.
74
+
75
+ ## Extract Page Content
76
+
77
+ ```bash
78
+ {baseDir}/browser-content.js https://example.com
79
+ ```
80
+
81
+ Navigate to a URL and extract readable content as markdown. Uses Mozilla Readability for article extraction and Turndown for HTML-to-markdown conversion. Works on pages with JavaScript content (waits for page to load).
82
+
83
+ ## When to Use
84
+
85
+ - Testing frontend code in a real browser
86
+ - Interacting with pages that require JavaScript
87
+ - When user needs to visually see or interact with a page
88
+ - Debugging authentication or session issues
89
+ - Scraping dynamic content that requires JS execution
90
+
91
+ ---
92
+
93
+ ## Efficiency Guide
94
+
95
+ ### DOM Inspection Over Screenshots
96
+
97
+ **Don't** take screenshots to see page state. **Do** parse the DOM directly:
98
+
99
+ ```javascript
100
+ // Get page structure
101
+ document.body.innerHTML.slice(0, 5000)
102
+
103
+ // Find interactive elements
104
+ Array.from(document.querySelectorAll('button, input, [role="button"]')).map(e => ({
105
+ id: e.id,
106
+ text: e.textContent.trim(),
107
+ class: e.className
108
+ }))
109
+ ```
110
+
111
+ ### Complex Scripts in Single Calls
112
+
113
+ Wrap everything in an IIFE to run multi-statement code:
114
+
115
+ ```javascript
116
+ (function() {
117
+ // Multiple operations
118
+ const data = document.querySelector('#target').textContent;
119
+ const buttons = document.querySelectorAll('button');
120
+
121
+ // Interactions
122
+ buttons[0].click();
123
+
124
+ // Return results
125
+ return JSON.stringify({ data, buttonCount: buttons.length });
126
+ })()
127
+ ```
128
+
129
+ ### Batch Interactions
130
+
131
+ **Don't** make separate calls for each click. **Do** batch them:
132
+
133
+ ```javascript
134
+ (function() {
135
+ const actions = ["btn1", "btn2", "btn3"];
136
+ actions.forEach(id => document.getElementById(id).click());
137
+ return "Done";
138
+ })()
139
+ ```
140
+
141
+ ### Typing/Input Sequences
142
+
143
+ ```javascript
144
+ (function() {
145
+ const text = "HELLO";
146
+ for (const char of text) {
147
+ document.getElementById("key-" + char).click();
148
+ }
149
+ document.getElementById("submit").click();
150
+ return "Submitted: " + text;
151
+ })()
152
+ ```
153
+
154
+ ### Reading App/Game State
155
+
156
+ Extract structured state in one call:
157
+
158
+ ```javascript
159
+ (function() {
160
+ const state = {
161
+ score: document.querySelector('.score')?.textContent,
162
+ status: document.querySelector('.status')?.className,
163
+ items: Array.from(document.querySelectorAll('.item')).map(el => ({
164
+ text: el.textContent,
165
+ active: el.classList.contains('active')
166
+ }))
167
+ };
168
+ return JSON.stringify(state, null, 2);
169
+ })()
170
+ ```
171
+
172
+ ### Waiting for Updates
173
+
174
+ If DOM updates after actions, add a small delay with bash:
175
+
176
+ ```bash
177
+ sleep 0.5 && {baseDir}/browser-eval.js '...'
178
+ ```
179
+
180
+ ### Investigate Before Interacting
181
+
182
+ Always start by understanding the page structure:
183
+
184
+ ```javascript
185
+ (function() {
186
+ return {
187
+ title: document.title,
188
+ forms: document.forms.length,
189
+ buttons: document.querySelectorAll('button').length,
190
+ inputs: document.querySelectorAll('input').length,
191
+ mainContent: document.body.innerHTML.slice(0, 3000)
192
+ };
193
+ })()
194
+ ```
195
+
196
+ Then target specific elements based on what you find.
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env node
2
+
3
+ import puppeteer from "puppeteer-core";
4
+ import { Readability } from "@mozilla/readability";
5
+ import { JSDOM } from "jsdom";
6
+ import TurndownService from "turndown";
7
+ import { gfm } from "turndown-plugin-gfm";
8
+
9
+ // Global timeout - exit if script takes too long
10
+ const TIMEOUT = 30000;
11
+ const timeoutId = setTimeout(() => {
12
+ console.error("✗ Timeout after 30s");
13
+ process.exit(1);
14
+ }, TIMEOUT).unref();
15
+
16
+ const url = process.argv[2];
17
+
18
+ if (!url) {
19
+ console.log("Usage: browser-content.js <url>");
20
+ console.log("\nExtracts readable content from a URL as markdown.");
21
+ console.log("\nExamples:");
22
+ console.log(" browser-content.js https://example.com");
23
+ console.log(" browser-content.js https://en.wikipedia.org/wiki/Rust_(programming_language)");
24
+ process.exit(1);
25
+ }
26
+
27
+ const b = await Promise.race([
28
+ puppeteer.connect({
29
+ browserURL: "http://localhost:9222",
30
+ defaultViewport: null,
31
+ }),
32
+ new Promise((_, reject) => setTimeout(() => reject(new Error("timeout")), 5000)),
33
+ ]).catch((e) => {
34
+ console.error("✗ Could not connect to browser:", e.message);
35
+ console.error(" Run: browser-start.js");
36
+ process.exit(1);
37
+ });
38
+
39
+ const p = (await b.pages()).at(-1);
40
+ if (!p) {
41
+ console.error("✗ No active tab found");
42
+ process.exit(1);
43
+ }
44
+
45
+ await Promise.race([
46
+ p.goto(url, { waitUntil: "networkidle2" }),
47
+ new Promise((r) => setTimeout(r, 10000)),
48
+ ]).catch(() => {});
49
+
50
+ // Get HTML via CDP (works even with TrustedScriptURL restrictions)
51
+ const client = await p.createCDPSession();
52
+ const { root } = await client.send("DOM.getDocument", { depth: -1, pierce: true });
53
+ const { outerHTML } = await client.send("DOM.getOuterHTML", { nodeId: root.nodeId });
54
+ await client.detach();
55
+
56
+ const finalUrl = p.url();
57
+
58
+ // Extract with Readability
59
+ const doc = new JSDOM(outerHTML, { url: finalUrl });
60
+ const reader = new Readability(doc.window.document);
61
+ const article = reader.parse();
62
+
63
+ // Convert to markdown
64
+ function htmlToMarkdown(html) {
65
+ const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
66
+ turndown.use(gfm);
67
+ turndown.addRule("removeEmptyLinks", {
68
+ filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
69
+ replacement: () => "",
70
+ });
71
+ return turndown
72
+ .turndown(html)
73
+ .replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
74
+ .replace(/ +/g, " ")
75
+ .replace(/\s+,/g, ",")
76
+ .replace(/\s+\./g, ".")
77
+ .replace(/\n{3,}/g, "\n\n")
78
+ .trim();
79
+ }
80
+
81
+ let content;
82
+ if (article && article.content) {
83
+ content = htmlToMarkdown(article.content);
84
+ } else {
85
+ // Fallback
86
+ const fallbackDoc = new JSDOM(outerHTML, { url: finalUrl });
87
+ const fallbackBody = fallbackDoc.window.document;
88
+ fallbackBody.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach((el) => el.remove());
89
+ const main = fallbackBody.querySelector("main, article, [role='main'], .content, #content") || fallbackBody.body;
90
+ const fallbackHtml = main?.innerHTML || "";
91
+ if (fallbackHtml.trim().length > 100) {
92
+ content = htmlToMarkdown(fallbackHtml);
93
+ } else {
94
+ content = "(Could not extract content)";
95
+ }
96
+ }
97
+
98
+ console.log(`URL: ${finalUrl}`);
99
+ if (article?.title) console.log(`Title: ${article.title}`);
100
+ console.log("");
101
+ console.log(content);
102
+
103
+ process.exit(0);