@maxiaochao/pi-toolkit 0.9.3 → 0.9.6

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 (85) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +40 -90
  3. package/extensions/{btw.ts → btw/index.ts} +1 -1
  4. package/extensions/{tests/btw-history.mjs → btw/tests/history.mjs} +1 -1
  5. package/package.json +19 -10
  6. package/packages/cache-export/CHANGELOG.md +5 -0
  7. package/packages/cache-export/README.md +70 -0
  8. package/packages/cache-export/TESTING.md +17 -0
  9. package/packages/cache-export/docs/demo.gif +0 -0
  10. package/packages/cache-export/docs/screenshot.png +0 -0
  11. package/{extensions/cache-export/index.ts → packages/cache-export/extensions/cache-export.ts} +1 -1
  12. package/packages/cache-export/package.json +22 -0
  13. package/{extensions/cache-export → packages/cache-export/src}/render.ts +1 -1
  14. package/{extensions → packages}/cache-export/tests/e2e-browser.mjs +2 -2
  15. package/{extensions → packages}/cache-export/tests/run-tests.mjs +2 -2
  16. package/packages/codex-edit/CHANGELOG.md +47 -0
  17. package/packages/codex-edit/README.md +130 -0
  18. package/packages/codex-edit/TESTING.md +51 -0
  19. package/packages/codex-edit/benchmark/explicit-edit-100.json +2824 -0
  20. package/packages/codex-edit/benchmark/export-evidence.mjs +77 -0
  21. package/packages/codex-edit/benchmark/incident-cases.mjs +37 -0
  22. package/packages/codex-edit/benchmark/prepare.mjs +181 -0
  23. package/packages/codex-edit/benchmark/results/explicit-edit-100-sol-low-v3.json +4562 -0
  24. package/packages/codex-edit/benchmark/run-with-budget.mjs +77 -0
  25. package/packages/codex-edit/benchmark/schedule.mjs +19 -0
  26. package/packages/codex-edit/benchmark/select-tasks.mjs +167 -0
  27. package/{extensions → packages}/codex-edit/codex-edit-explainer.html +43 -22
  28. package/packages/codex-edit/docs/demo.gif +0 -0
  29. package/packages/codex-edit/docs/screenshot.png +0 -0
  30. package/packages/codex-edit/docs/test-report.html +181 -0
  31. package/packages/codex-edit/docs/test-report.md +242 -0
  32. package/packages/codex-edit/docs/test-summary.md +9 -0
  33. package/{extensions/codex-edit/index.ts → packages/codex-edit/extensions/codex-edit.ts} +7 -5
  34. package/packages/codex-edit/package.json +54 -0
  35. package/{extensions/codex-edit → packages/codex-edit/src}/parser.ts +27 -5
  36. package/{extensions → packages}/codex-edit/summary.md +1 -1
  37. package/packages/codex-edit/tests/benchmark.test.mjs +81 -0
  38. package/packages/codex-edit/tests/comparison-cases.mjs +396 -0
  39. package/packages/codex-edit/tests/comparison-harness.mjs +44 -0
  40. package/packages/codex-edit/tests/generate-test-report.mjs +589 -0
  41. package/packages/codex-edit/tests/legacy-parser-v0.1.7.mjs +128 -0
  42. package/packages/codex-edit/tests/native-vs-codex.ts +109 -0
  43. package/packages/codex-edit/tests/package.test.mjs +51 -0
  44. package/packages/codex-edit/tests/parser.test.mjs +308 -0
  45. package/packages/codex-edit/tests/released-vs-local.test.mjs +31 -0
  46. package/packages/tiny-subagent/CHANGELOG.md +12 -0
  47. package/packages/tiny-subagent/README.md +128 -0
  48. package/packages/tiny-subagent/TESTING.md +20 -0
  49. package/packages/tiny-subagent/agents/worker.md +9 -0
  50. package/packages/tiny-subagent/docs/demo.gif +0 -0
  51. package/packages/tiny-subagent/docs/screenshot.png +0 -0
  52. package/packages/tiny-subagent/extensions/tiny-subagent.ts +228 -0
  53. package/packages/tiny-subagent/package.json +44 -0
  54. package/packages/tiny-subagent/src/personas.ts +110 -0
  55. package/packages/tiny-subagent/src/runner.ts +319 -0
  56. package/packages/tiny-subagent/tests/run-tests.mjs +111 -0
  57. package/packages/todo/CHANGELOG.md +5 -0
  58. package/packages/todo/README.md +63 -0
  59. package/packages/todo/TESTING.md +20 -0
  60. package/packages/todo/docs/demo.gif +0 -0
  61. package/packages/todo/docs/screenshot.png +0 -0
  62. package/{extensions → packages/todo/extensions}/todo.ts +1 -1
  63. package/packages/todo/package.json +25 -0
  64. package/{extensions → packages/todo}/tests/todo-state.mjs +1 -1
  65. package/packages/worktree/CHANGELOG.md +5 -0
  66. package/packages/worktree/README.md +60 -0
  67. package/packages/worktree/TESTING.md +21 -0
  68. package/packages/worktree/docs/demo.gif +0 -0
  69. package/packages/worktree/docs/screenshot.png +0 -0
  70. package/packages/worktree/package.json +20 -0
  71. package/packages/worktree/scripts/install.mjs +26 -0
  72. package/packages/worktree/tests/install.test.mjs +25 -0
  73. package/packages/worktree/tests/run-tests.sh +46 -0
  74. package/scripts/install-agents.mjs +8 -18
  75. package/extensions/codex-edit/README.md +0 -53
  76. package/extensions/codex-edit/tests/package.test.mjs +0 -38
  77. package/extensions/codex-edit/tests/parser.test.mjs +0 -89
  78. /package/extensions/{btw-scroll.ts → btw/scroll.ts} +0 -0
  79. /package/{extensions/cache-export → packages/cache-export/src}/tau-assets.ts +0 -0
  80. /package/{extensions → packages}/codex-edit/config.json +0 -0
  81. /package/{extensions/codex-edit → packages/codex-edit/src}/model-routing.ts +0 -0
  82. /package/{extensions → packages/todo/src}/todo-state.ts +0 -0
  83. /package/{bin → packages/worktree/bin}/pi-worktree +0 -0
  84. /package/{bin → packages/worktree/bin}/pi-worktree-completion.bash +0 -0
  85. /package/{skills → packages/worktree/skills}/pi-worktree/SKILL.md +0 -0
@@ -0,0 +1,181 @@
1
+ <!doctype html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>Codex Edit 测试报告</title>
7
+ <style>
8
+ :root{--ink:#17221f;--muted:#65736d;--paper:#f5f7f2;--panel:#fff;--line:#d9e1db;--green:#0e6b50;--green-soft:#dcefe7;--amber:#a96518;--amber-soft:#fff0d9;--red:#a33c32;--blue:#245f8c;--blue-soft:#e2eff8;--shadow:0 12px 32px rgba(23,34,31,.08);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
9
+ *{box-sizing:border-box}html{scroll-behavior:smooth}body{margin:0;color:var(--ink);background:var(--paper);line-height:1.58}.topbar{position:sticky;top:0;z-index:5;background:rgba(245,247,242,.94);border-bottom:1px solid var(--line);backdrop-filter:blur(12px)}.topbar-inner{max-width:1180px;margin:auto;padding:14px 26px;display:flex;align-items:center;justify-content:space-between;gap:18px}.brand{font-weight:800;letter-spacing:.02em}.brand span{color:var(--green)}.nav{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}.nav a{color:var(--muted);text-decoration:none;padding:6px 10px;border-radius:7px;font-size:13px}.nav a:hover,.nav a:focus-visible{color:var(--green);background:var(--green-soft);outline:none}.status{color:var(--amber);background:var(--amber-soft);padding:5px 10px;border-radius:999px;font-weight:800;font-size:12px;white-space:nowrap}main{max-width:1180px;margin:auto;padding:58px 26px 90px}.eyebrow{color:var(--green);font-size:12px;text-transform:uppercase;letter-spacing:.14em;font-weight:800}h1{font-size:clamp(38px,6vw,70px);line-height:1.03;max-width:900px;margin:12px 0 24px;letter-spacing:-.04em}h2{font-size:28px;line-height:1.12;margin:70px 0 20px;padding-top:12px;letter-spacing:-.025em;border-top:3px solid var(--green)}h3{margin:34px 0 12px;font-size:18px;color:var(--green)}p{max-width:860px;color:#42514a}main>p:first-of-type{font-size:18px;color:var(--ink);background:var(--amber-soft);border-left:4px solid var(--amber);padding:18px 20px}strong{color:var(--amber)}code{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;background:#edf1ee;padding:2px 5px;border-radius:4px;font-size:.92em}a{color:var(--green);overflow-wrap:anywhere}.table-wrap{overflow-x:auto;margin:20px 0 32px;border:1px solid var(--line);box-shadow:var(--shadow);background:var(--panel)}table{width:100%;border-collapse:collapse;min-width:760px;font-size:14px}th,td{text-align:left;vertical-align:top;padding:14px 16px;border-bottom:1px solid var(--line)}th{position:sticky;top:58px;background:#edf3ee;color:#405148;font-size:12px;text-transform:uppercase;letter-spacing:.05em}tr:last-child td{border-bottom:0}tbody tr:hover{background:#f3f8f4}ul{max-width:920px;background:var(--panel);border:1px solid var(--line);box-shadow:var(--shadow);padding:18px 22px 18px 42px}li{margin:8px 0;color:#42514a}
10
+ @media(max-width:820px){.topbar-inner{padding:12px 17px;align-items:flex-start;flex-direction:column}.nav{justify-content:flex-start}.status{position:absolute;right:17px;top:12px}.nav a{padding-left:0;margin-right:8px}main{padding:40px 17px 70px}h1{font-size:40px}.table-wrap{margin-left:-17px;margin-right:-17px;border-left:0;border-right:0}th{top:101px}}
11
+ @media print{.topbar{position:static}.nav{display:none}main{padding-top:30px}.table-wrap{overflow:visible;box-shadow:none}table{min-width:0;font-size:10px}h2{break-after:avoid}.table-wrap,ul{break-inside:avoid;box-shadow:none}}
12
+ </style>
13
+ </head>
14
+ <body>
15
+ <header class="topbar"><div class="topbar-inner"><div class="brand">Pi <span>Codex Edit</span> 测试报告</div><nav class="nav" aria-label="页面导航"><a href="#summary">摘要</a><a href="#benchmark">真实模型</a><a href="#protocol">协议</a><a href="#safety">安全</a><a href="#incidents">事故</a><a href="#comparison">对比</a><a href="#reproducibility">复现</a></nav><span class="status">通过,有共同例外</span></div></header>
16
+ <main><div class="eyebrow">GPT-5.6 Sol / Pi 0.85.1 / Explicit Edit Benchmark</div><h1 id="top">Codex Edit 测试报告</h1>
17
+ <p><strong>回归检查:通过;Agent Benchmark:通过(有共同例外)</strong></p>
18
+ <p>被测包:<code>@maxiaochao/pi-codex-edit</code>(当前工作区)</p>
19
+ <p>本报告严格区分真实模型基准、确定性协议回归和安全回归,三类成功率不会混算。</p>
20
+ <h2 id="summary">执行摘要</h2>
21
+ <ul><li>Agent Benchmark:<strong>通过(有共同例外)</strong> — 已完成 300/300 条 chain;本地版 First Exact 92/100、Final Exact 92/100,全部正式门槛通过。</li><li>协议/包回归:<strong>通过</strong> — 4/4 项检查通过。</li><li>安全回归:<strong>5/5 正确拒绝且零写入</strong>,包括工作区外路径。</li><li>下文提供 Agent、协议、安全、效率、版本、integrity 和任务来源的完整证据。</li></ul>
22
+ <h2 id="benchmark">Agent Benchmark:真实模型基准</h2>
23
+ <p><strong>状态:通过(有共同例外)</strong></p>
24
+ <p>三个测试臂均完成全部 100 个配对任务(共 300 条 chain),实际成本为 $12.388。本地版满足全部正式验收门槛;8 个最终失败均为三臂共同失败,不是本地版独有回归。</p>
25
+ <div class="table-wrap"><table><thead><tr><th>测试臂</th><th>覆盖率</th><th>First Exact</th><th>Final Exact</th><th>恢复次数 / 净增益</th><th>Score v2</th><th>成本</th><th>Token</th><th>耗时</th><th>工具调用</th><th>失败调用</th><th>Provider 失败</th></tr></thead><tbody>
26
+ <tr><td>原生 edit</td><td>100/100</td><td>91/100 (91.00%)</td><td>92/100 (92.00%)</td><td>41 / +1</td><td>91.25%</td><td>$4.104</td><td>1,253,279</td><td>2887.2s</td><td>225</td><td>0</td><td>0</td></tr>
27
+ <tr><td>发布版 0.1.7</td><td>100/100</td><td>91/100 (91.00%)</td><td>92/100 (92.00%)</td><td>41 / +1</td><td>91.25%</td><td>$4.139</td><td>1,080,588</td><td>2777.4s</td><td>198</td><td>0</td><td>0</td></tr>
28
+ <tr><td>本地修改版</td><td>100/100</td><td>92/100 (92.00%)</td><td>92/100 (92.00%)</td><td>40 / +0</td><td>92.00%</td><td>$4.145</td><td>1,049,619</td><td>2838.0s</td><td>194</td><td>0</td><td>0</td></tr>
29
+ </tbody></table></div>
30
+ <p>Score v2 = <code>覆盖率 ×(0.75 × 首次精确率 + 0.25 × 最终精确率)</code>。本地版为 92.00%,严格高于原生 edit 和发布版的 91.25%。</p>
31
+ <h3 id="section-1">完整配对结果</h3>
32
+ <div class="table-wrap"><table><thead><tr><th>对比</th><th>指标</th><th>本地版胜</th><th>本地版负</th><th>持平</th></tr></thead><tbody>
33
+ <tr><td>本地版 vs 原生 edit</td><td>首次精确</td><td>1</td><td>0</td><td>99</td></tr>
34
+ <tr><td>本地版 vs 原生 edit</td><td>最终精确</td><td>0</td><td>0</td><td>100</td></tr>
35
+ <tr><td>本地版 vs 发布版</td><td>首次精确</td><td>1</td><td>0</td><td>99</td></tr>
36
+ <tr><td>本地版 vs 发布版</td><td>最终精确</td><td>0</td><td>0</td><td>100</td></tr>
37
+ </tbody></table></div>
38
+ <p>首次精确的两个本地版净胜任务分别是:相对原生 edit 的 <code>delete-subset-1000-plain</code>,以及相对发布版的 <code>copy-block-10-plain</code>。最终精确没有配对差异。</p>
39
+ <h3 id="section-2">正式验收门槛</h3>
40
+ <div class="table-wrap"><table><thead><tr><th>门槛</th><th>结果</th></tr></thead><tbody>
41
+ <tr><td>本地 Score v2 严格高于原生 edit</td><td>通过</td></tr>
42
+ <tr><td>本地 First Exact 不低于原生 edit</td><td>通过</td></tr>
43
+ <tr><td>本地 Final Exact 距最佳对照不超过 2/100</td><td>通过</td></tr>
44
+ <tr><td>相对发布版的改善数不少于回归数</td><td>通过</td></tr>
45
+ <tr><td>本地独有最终回归不超过 2 个</td><td>通过</td></tr>
46
+ <tr><td>四项效率至少三项不差于原生,且单项恶化不超过 10%</td><td>通过</td></tr>
47
+ </tbody></table></div>
48
+ <h3 id="edit">相对原生 edit 的效率</h3>
49
+ <div class="table-wrap"><table><thead><tr><th>指标</th><th>原生 edit</th><th>本地版</th><th>变化</th><th>判定</th></tr></thead><tbody>
50
+ <tr><td>成本</td><td>$4.104</td><td>$4.145</td><td>1.01%</td><td>轻微恶化,低于 10% 上限</td></tr>
51
+ <tr><td>Token</td><td>1,253,279</td><td>1,049,619</td><td>-16.25%</td><td>改善</td></tr>
52
+ <tr><td>耗时</td><td>2887.2s</td><td>2838.0s</td><td>-1.70%</td><td>改善</td></tr>
53
+ <tr><td>工具调用</td><td>225</td><td>194</td><td>-13.78%</td><td>改善</td></tr>
54
+ </tbody></table></div>
55
+ <p>本地版四项效率中三项优于原生 edit;成本增加约 1%,没有任何指标恶化超过 10%,也没有超过 20% 的警告项。</p>
56
+ <h3 id="cohort">Cohort 覆盖</h3>
57
+ <div class="table-wrap"><table><thead><tr><th>Cohort</th><th>计划任务</th><th>已完成配对任务</th><th>原生 First / Final</th><th>发布版 First / Final</th><th>本地版 First / Final</th></tr></thead><tbody>
58
+ <tr><td>neutral</td><td>80</td><td>80</td><td>73/74</td><td>73/74</td><td>74/74</td></tr>
59
+ <tr><td>challenge</td><td>20</td><td>20</td><td>18/18</td><td>18/18</td><td>18/18</td></tr>
60
+ </tbody></table></div>
61
+ <h3 id="first-final">任务类别明细(First / Final / 总数)</h3>
62
+ <div class="table-wrap"><table><thead><tr><th>类别</th><th>配对任务</th><th>原生 edit</th><th>发布版</th><th>本地版</th></tr></thead><tbody>
63
+ <tr><td>copy-block</td><td>3</td><td>3/3/3</td><td>2/3/3</td><td>3/3/3</td></tr>
64
+ <tr><td>copy-within</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
65
+ <tr><td>delete-block</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
66
+ <tr><td>delete-subset</td><td>3</td><td>2/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
67
+ <tr><td>distinct-edits</td><td>4</td><td>4/4/4</td><td>4/4/4</td><td>4/4/4</td></tr>
68
+ <tr><td>insert-block</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
69
+ <tr><td>insert-payload</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
70
+ <tr><td>insert-subset</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
71
+ <tr><td>language-insert</td><td>13</td><td>13/13/13</td><td>13/13/13</td><td>13/13/13</td></tr>
72
+ <tr><td>language-replace</td><td>13</td><td>13/13/13</td><td>13/13/13</td><td>13/13/13</td></tr>
73
+ <tr><td>language-select</td><td>13</td><td>13/13/13</td><td>13/13/13</td><td>13/13/13</td></tr>
74
+ <tr><td>literal</td><td>5</td><td>5/5/5</td><td>5/5/5</td><td>5/5/5</td></tr>
75
+ <tr><td>move-between</td><td>4</td><td>4/4/4</td><td>4/4/4</td><td>4/4/4</td></tr>
76
+ <tr><td>move-block</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
77
+ <tr><td>multi-file</td><td>4</td><td>4/4/4</td><td>4/4/4</td><td>4/4/4</td></tr>
78
+ <tr><td>replace-all</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
79
+ <tr><td>replace-block</td><td>3</td><td>3/3/3</td><td>3/3/3</td><td>3/3/3</td></tr>
80
+ <tr><td>select-one</td><td>4</td><td>4/4/4</td><td>4/4/4</td><td>4/4/4</td></tr>
81
+ <tr><td>select-subset</td><td>4</td><td>2/2/4</td><td>2/2/4</td><td>2/2/4</td></tr>
82
+ <tr><td>unicode-fix</td><td>4</td><td>0/0/4</td><td>0/0/4</td><td>0/0/4</td></tr>
83
+ <tr><td>unique</td><td>2</td><td>0/0/2</td><td>0/0/2</td><td>0/0/2</td></tr>
84
+ </tbody></table></div>
85
+ <h3 id="section-3">共同例外</h3>
86
+ <p>三臂最终均失败的 8 个任务为:<code>select-subset-1000-unicode</code>、<code>select-subset-1000-plain</code>、4 个 <code>unicode-fix-*</code> 任务,以及 <code>unique-10-plain</code>、<code>unique-100-unicode</code>。它们不构成本地修改版回归;本地独有最终回归为 0。</p>
87
+ <h3 id="reproducibility">可复现信息</h3>
88
+ <ul><li>数据源:<a href="https://github.com/alexshpunt/explicit-edit-benchmark">https://github.com/alexshpunt/explicit-edit-benchmark</a>,commit <code>5827e3fb65845f4b76adb06e5919d94ec4ffd5cf</code>。</li><li>冻结任务:80 Neutral + 20 Challenge;seed 为 <code>pi-toolkit-codex-edit-100-v1</code>。</li><li>模型:<code>openai-codex/gpt-5.6-sol</code>;reasoning:<code>low</code>;Pi:<code>0.85.1</code>;最多 5 次 Oracle recovery。</li><li>发布版:<code>@maxiaochao/pi-codex-edit@0.1.7 (sha512-hfNfx20h51FLCueXaMqBUAVYnxoCakuI306rzuFPI/lS0HT1jE/wQ8xkKl9PwEXboVVD6ZzS/YB0VVrd67V7cw==)</code>。</li><li>本地版:<code>@maxiaochao/pi-codex-edit@0.1.7+local (sha512-Mf21ZfSwOOgaH2lSiF/DpoA8Jk5jH84MaZUX1abkMop3HST3xt2YtCw2NKwrRJpvmJB02bl9wwYyFlAVI68Qwg==)</code>。</li><li>Run ID:<code>explicit-edit-100-sol-low-v3-complete</code>;运行完整性:300/300。</li><li>Identity:task manifest <code>cab0278f530271c7bb3de3e4dba6ab252fa2b748d00164e749ecdbd8f9f7f431</code>;config <code>739bb323275fdcead7709f19a894b0dee57062f40064c04d182d96474535328c</code>;verifier <code>8bd4be15c886bba84e8a6fa46576c43f2ca0f02d2536d98403f97361914a166d</code>。</li><li>Provider 失败:0;失败工具调用:0。</li></ul>
89
+ <h2 id="protocol">协议与包验证摘要</h2>
90
+ <div class="table-wrap"><table><thead><tr><th>检查项</th><th>结果</th><th>命令</th></tr></thead><tbody>
91
+ <tr><td>Node 回归测试</td><td>通过</td><td><code>node --experimental-strip-types --test tests/package.test.mjs tests/parser.test.mjs tests/released-vs-local.test.mjs tests/benchmark.test.mjs</code></td></tr>
92
+ <tr><td>原生 edit 与本地 Codex Edit 对比</td><td>通过</td><td><code>pi -ne -e ./tests/native-vs-codex.ts</code></td></tr>
93
+ <tr><td>扩展加载</td><td>通过</td><td><code>pi -ne -e ./extensions/codex-edit.ts</code></td></tr>
94
+ <tr><td>独立包 tarball</td><td>通过</td><td><code>npm pack --dry-run --json</code></td></tr>
95
+ </tbody></table></div>
96
+ <h2 id="section-4">确定性回归基线</h2>
97
+ <ul><li>Node 测试:预期 22 个,覆盖 parser、matcher、路由、包契约、resolved-path 防护、发布版对比、分层调度和生产事故语料。</li><li>共享对比 corpus:12 个 case。</li><li>原生 edit:5 通过 / 2 失败或正确拒绝 / 5 不适用。</li><li>Codex Edit 发布版 v0.1.7:6 通过 / 6 失败或正确拒绝。</li><li>Codex Edit 本地版:7 通过 / 5 正确安全拒绝。</li></ul>
98
+ <p>“正确拒绝”表示无效或陈旧操作被拒绝,且工作区没有变化。“不适用”表示 Pi 原生 <code>edit</code> 无法用一次编辑协议调用表达该操作,不计为工具缺陷。</p>
99
+ <h2 id="safety">安全回归</h2>
100
+ <p>本地版 5/5 个安全 case 均正确拒绝,且工作区零写入。特别是 <code>outside-workspace-absolute-path</code> 的结果为 <strong>工作区外路径 — 正确拒绝</strong>;放行该操作会允许任意写入 benchmark sandbox 之外。安全拒绝不计入编辑失败率。</p>
101
+ <h2 id="incidents">生产事故回归</h2>
102
+ <p>这些 case 来自实际报告过的问题。Agent-and-protocol case 使用不包含工具语法提示的用户任务,可进入后续付费模型运行;protocol-only case 只验证指定工具形态,不用于宣称模型质量提升。</p>
103
+ <div class="table-wrap"><table><thead><tr><th>Incident</th><th>层级</th><th>对应回归</th><th>问题</th></tr></thead><tbody>
104
+ <tr><td><code>incident-repeated-markdown-context</code></td><td>agent-and-protocol</td><td><code>repeated-markdown-separator</code></td><td>重复 Markdown 分隔符曾触发 ambiguous match</td></tr>
105
+ <tr><td><code>incident-repeated-update-same-path</code></td><td>protocol-only</td><td><code>repeated-update-same-path</code></td><td>同一个 raw path 的多个 Update File 块在发布版中冲突</td></tr>
106
+ <tr><td><code>incident-runner-type-rename</code></td><td>agent-and-protocol</td><td><code>ordered-runner-type-renames</code></td><td>runner.ts 中重复类型名的大范围重命名曾触发 ambiguous match</td></tr>
107
+ <tr><td><code>incident-multi-file-structural-edit</code></td><td>agent-and-protocol</td><td><code>multi-file-add-delete-move</code></td><td>一次 patch 中的 add/delete/move 需要原子预校验和执行</td></tr>
108
+ <tr><td><code>incident-bom-crlf</code></td><td>agent-and-protocol</td><td><code>bom-and-crlf</code></td><td>UTF-8 BOM 与 CRLF 曾导致原生精确文本匹配失败</td></tr>
109
+ </tbody></table></div>
110
+ <p>冻结的 100-task 清单按每 5 个任务包含 4 Neutral + 1 Challenge 的顺序调度;本次完整运行覆盖全部 80 个 Neutral 和 20 个 Challenge。</p>
111
+ <h2 id="comparison">确定性对比结论</h2>
112
+ <p>每个测试臂运行同一组 12 个 case。对比 1 执行 24 次工具调用(原生 + 本地);对比 2 执行 24 次 parser/执行评估(发布版 + 本地)。下表以 7 个有效编辑任务为完成率分母;5 个故意无效或危险的 patch 单独计入安全检查。</p>
113
+ <h3 id="1-pi-edit-vs-codex-edit">1. Pi 原生 edit vs Codex Edit 本地版</h3>
114
+ <div class="table-wrap"><table><thead><tr><th>指标</th><th>Pi 原生 edit</th><th>Codex Edit 本地版</th><th>变化</th></tr></thead><tbody>
115
+ <tr><td>有效任务完成</td><td>5/7</td><td>7/7</td><td>+2 个</td></tr>
116
+ <tr><td>有效任务完成率</td><td>71.4%</td><td>100.0%</td><td>+28.6 pp</td></tr>
117
+ <tr><td>有效任务失败</td><td>1/7</td><td>0/7</td><td>减少 1 个</td></tr>
118
+ <tr><td>有效任务不适用</td><td>1/7</td><td>0/7</td><td>减少 1 个</td></tr>
119
+ <tr><td>正确拒绝适用的危险/陈旧 patch</td><td>1/1 (100.0%)</td><td>5/5 (100.0%)</td><td>Codex 覆盖全部 5 个</td></tr>
120
+ </tbody></table></div>
121
+ <h3 id="2-codex-edit-v0-1-7-vs">2. Codex Edit 发布版 v0.1.7 vs 本地修改版</h3>
122
+ <div class="table-wrap"><table><thead><tr><th>指标</th><th>发布版 v0.1.7</th><th>本地修改版</th><th>变化</th></tr></thead><tbody>
123
+ <tr><td>有效任务完成</td><td>6/7</td><td>7/7</td><td>+1 个</td></tr>
124
+ <tr><td>有效任务完成率</td><td>85.7%</td><td>100.0%</td><td>+14.3 pp</td></tr>
125
+ <tr><td>安全 case 正确拒绝</td><td>5/5 (100.0%)</td><td>5/5 (100.0%)</td><td>无变化</td></tr>
126
+ <tr><td>保留原有通过任务</td><td>6/6</td><td>6/6</td><td>100% 保留</td></tr>
127
+ </tbody></table></div>
128
+ <p>这些是确定性协议/执行成功率,不是独立模型采样成功率。Agent 质量、token、成本和耗时只在 Agent Benchmark 章节报告。</p>
129
+ <h2 id="case-edit-vs-codex-edit">逐 case:原生 edit vs 本地 Codex Edit</h2>
130
+ <p>该对比在隔离临时工作区中执行 Pi 导出的 <code>createEditToolDefinition</code>,以及本地扩展实际注册的 <code>apply_patch</code>。</p>
131
+ <div class="table-wrap"><table><thead><tr><th>Case</th><th>原生 edit</th><th>本地 Codex Edit</th><th>说明</th></tr></thead><tbody>
132
+ <tr><td><code>unique-replacement</code></td><td>通过</td><td>通过</td><td>预期行为一致</td></tr>
133
+ <tr><td><code>disjoint-edits-one-file</code></td><td>通过</td><td>通过</td><td>预期行为一致</td></tr>
134
+ <tr><td><code>repeated-markdown-separator</code></td><td>正确拒绝/失败</td><td>通过</td><td>Codex 协议可处理该形态</td></tr>
135
+ <tr><td><code>repeated-update-same-path</code></td><td>通过</td><td>通过</td><td>预期行为一致</td></tr>
136
+ <tr><td><code>ordered-runner-type-renames</code></td><td>通过</td><td>通过</td><td>预期行为一致</td></tr>
137
+ <tr><td><code>multi-file-add-delete-move</code></td><td>不适用</td><td>通过</td><td>Codex 协议可处理该形态</td></tr>
138
+ <tr><td><code>stale-expected-lines</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>预期行为一致</td></tr>
139
+ <tr><td><code>conflicting-targets</code></td><td>不适用</td><td>正确拒绝/失败</td><td>两者均避免危险写入</td></tr>
140
+ <tr><td><code>empty-update-chunk</code></td><td>不适用</td><td>正确拒绝/失败</td><td>两者均避免危险写入</td></tr>
141
+ <tr><td><code>resolved-path-alias-conflict</code></td><td>不适用</td><td>正确拒绝/失败</td><td>两者均避免危险写入</td></tr>
142
+ <tr><td><code>outside-workspace-absolute-path</code></td><td>不适用</td><td>正确拒绝/失败</td><td>两者均避免危险写入</td></tr>
143
+ <tr><td><code>bom-and-crlf</code></td><td>通过</td><td>通过</td><td>预期行为一致</td></tr>
144
+ </tbody></table></div>
145
+ <p>关键结果:本地 Codex Edit 可以处理重复 Markdown 上下文和多文件 add/delete/move;原生精确替换无法用一次调用安全表达这些形态。两者都会拒绝陈旧的预期文本。</p>
146
+ <h2 id="case-v0-1-7-vs-codex-edit">逐 case:发布版 v0.1.7 vs 本地 Codex Edit</h2>
147
+ <p>发布版测试臂使用从 tag <code>codex-edit-v0.1.7</code> 冻结的语义基线(source blob <code>e2896b59d40b5f6f01b3a4a7918476f4acc28cd8</code>)。</p>
148
+ <div class="table-wrap"><table><thead><tr><th>Case</th><th>发布版 v0.1.7</th><th>本地版</th><th>回归状态</th></tr></thead><tbody>
149
+ <tr><td><code>unique-replacement</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
150
+ <tr><td><code>disjoint-edits-one-file</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
151
+ <tr><td><code>repeated-markdown-separator</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
152
+ <tr><td><code>repeated-update-same-path</code></td><td>正确拒绝/失败</td><td>通过</td><td>已修复</td></tr>
153
+ <tr><td><code>ordered-runner-type-renames</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
154
+ <tr><td><code>multi-file-add-delete-move</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
155
+ <tr><td><code>stale-expected-lines</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>已保留</td></tr>
156
+ <tr><td><code>conflicting-targets</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>已保留</td></tr>
157
+ <tr><td><code>empty-update-chunk</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>已保留</td></tr>
158
+ <tr><td><code>resolved-path-alias-conflict</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>已保留</td></tr>
159
+ <tr><td><code>outside-workspace-absolute-path</code></td><td>正确拒绝/失败</td><td>正确拒绝/失败</td><td>已保留</td></tr>
160
+ <tr><td><code>bom-and-crlf</code></td><td>通过</td><td>通过</td><td>已保留</td></tr>
161
+ </tbody></table></div>
162
+ <p>关键结果:<code>repeated-update-same-path</code> 从 v0.1.7 的失败变为本地版通过。既有成功 case 全部保留;陈旧、空 chunk、冲突和 resolved-alias 目标仍会被安全拒绝。</p>
163
+ <h2 id="coverage">已报告错误覆盖</h2>
164
+ <div class="table-wrap"><table><thead><tr><th>错误形态</th><th>当前策略</th><th>Corpus 覆盖</th></tr></thead><tbody>
165
+ <tr><td>重复 Markdown/HTML 区域或重复类型名(<code>Ambiguous ... match</code>)</td><td>从当前 hunk cursor 起选择第一个匹配,hunk 按源位置排序</td><td><code>repeated-markdown-separator</code>、<code>ordered-runner-type-renames</code> 及 parser HTML 测试</td></tr>
166
+ <tr><td>同一 raw path 出现多个普通 <code>Update File</code></td><td>合并后按顺序执行</td><td><code>repeated-update-same-path</code></td></tr>
167
+ <tr><td>Add/delete/move 或 resolved path alias 冲突</td><td>写入前拒绝</td><td><code>conflicting-targets</code>、<code>resolved-path-alias-conflict</code></td></tr>
168
+ <tr><td>工作区外绝对路径</td><td>读取或写入前正确拒绝</td><td><code>outside-workspace-absolute-path</code></td></tr>
169
+ <tr><td>空 update chunk</td><td>拒绝,不猜测意图</td><td><code>empty-update-chunk</code></td></tr>
170
+ <tr><td>陈旧预期行</td><td>拒绝;调用方必须重新读取并生成 patch</td><td><code>stale-expected-lines</code></td></tr>
171
+ <tr><td>缺少 <code>patch</code> 参数</td><td>必填 TypeBox schema 在扩展执行前拒绝</td><td>包契约测试</td></tr>
172
+ </tbody></table></div>
173
+ <h2 id="section-5">上游实现对齐</h2>
174
+ <p>报告结构遵循 OpenAI Codex 的 scenario-test 模式:输入文件 + patch + 精确预期的最终文件系统。新旧 parser 对比采用 Codex 统一 streaming parser 时使用的差分测试方法。</p>
175
+ <p>本地 repeated-header 支持是有意加入的兼容扩展。当前 Codex 已验证实现会拒绝多个操作解析到同一路径,并推荐在一个 <code>Update File</code> 下使用多个 <code>@@</code> chunk。本实现只容忍 raw path 完全相同的重复普通 update;resolved alias 和破坏性操作冲突仍会拒绝。</p>
176
+ <p>来源:</p>
177
+ <ul><li>OpenAI Codex scenario tests: <a href="https://github.com/openai/codex/blob/9a8730f3/codex-rs/apply-patch/tests/suite/scenarios.rs">https://github.com/openai/codex/blob/9a8730f3/codex-rs/apply-patch/tests/suite/scenarios.rs</a></li><li>OpenAI Codex parser differential validation: <a href="https://github.com/openai/codex/commit/e26f734f919819bdd802b1b67ad653863c7a90e5">https://github.com/openai/codex/commit/e26f734f919819bdd802b1b67ad653863c7a90e5</a></li><li>Duplicate resolved-path guard: <a href="https://github.com/openai/codex/pull/37867">https://github.com/openai/codex/pull/37867</a></li></ul>
178
+ <h2 id="section-6">维护规则</h2>
179
+ <p>每个新的生产失败都必须先转化为 <code>tests/comparison-cases.mjs</code> 中的最小 case,并明确原生、发布版和本地版预期。已有 case 保持不变。发布前运行两组对比并重新生成本报告。模型路由、prompt 或 schema 变化还必须运行独立的真实模型 A/B benchmark。</p></main>
180
+ </body>
181
+ </html>
@@ -0,0 +1,242 @@
1
+ # Codex Edit 测试报告
2
+
3
+ **回归检查:通过;Agent Benchmark:通过(有共同例外)**
4
+
5
+ 被测包:`@maxiaochao/pi-codex-edit`(当前工作区)
6
+
7
+ 本报告严格区分真实模型基准、确定性协议回归和安全回归,三类成功率不会混算。
8
+
9
+ ## 执行摘要
10
+
11
+ - Agent Benchmark:**通过(有共同例外)** — 已完成 300/300 条 chain;本地版 First Exact 92/100、Final Exact 92/100,全部正式门槛通过。
12
+ - 协议/包回归:**通过** — 4/4 项检查通过。
13
+ - 安全回归:**5/5 正确拒绝且零写入**,包括工作区外路径。
14
+ - 下文提供 Agent、协议、安全、效率、版本、integrity 和任务来源的完整证据。
15
+
16
+ ## Agent Benchmark:真实模型基准
17
+
18
+ **状态:通过(有共同例外)**
19
+
20
+ 三个测试臂均完成全部 100 个配对任务(共 300 条 chain),实际成本为 $12.388。本地版满足全部正式验收门槛;8 个最终失败均为三臂共同失败,不是本地版独有回归。
21
+
22
+ | 测试臂 | 覆盖率 | First Exact | Final Exact | 恢复次数 / 净增益 | Score v2 | 成本 | Token | 耗时 | 工具调用 | 失败调用 | Provider 失败 |
23
+ |---|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|---:|
24
+ | 原生 edit | 100/100 | 91/100 (91.00%) | 92/100 (92.00%) | 41 / +1 | 91.25% | $4.104 | 1,253,279 | 2887.2s | 225 | 0 | 0 |
25
+ | 发布版 0.1.7 | 100/100 | 91/100 (91.00%) | 92/100 (92.00%) | 41 / +1 | 91.25% | $4.139 | 1,080,588 | 2777.4s | 198 | 0 | 0 |
26
+ | 本地修改版 | 100/100 | 92/100 (92.00%) | 92/100 (92.00%) | 40 / +0 | 92.00% | $4.145 | 1,049,619 | 2838.0s | 194 | 0 | 0 |
27
+
28
+ Score v2 = `覆盖率 ×(0.75 × 首次精确率 + 0.25 × 最终精确率)`。本地版为 92.00%,严格高于原生 edit 和发布版的 91.25%。
29
+
30
+ ### 完整配对结果
31
+
32
+ | 对比 | 指标 | 本地版胜 | 本地版负 | 持平 |
33
+ |---|---|---:|---:|---:|
34
+ | 本地版 vs 原生 edit | 首次精确 | 1 | 0 | 99 |
35
+ | 本地版 vs 原生 edit | 最终精确 | 0 | 0 | 100 |
36
+ | 本地版 vs 发布版 | 首次精确 | 1 | 0 | 99 |
37
+ | 本地版 vs 发布版 | 最终精确 | 0 | 0 | 100 |
38
+
39
+ 首次精确的两个本地版净胜任务分别是:相对原生 edit 的 `delete-subset-1000-plain`,以及相对发布版的 `copy-block-10-plain`。最终精确没有配对差异。
40
+
41
+ ### 正式验收门槛
42
+
43
+ | 门槛 | 结果 |
44
+ |---|---|
45
+ | 本地 Score v2 严格高于原生 edit | 通过 |
46
+ | 本地 First Exact 不低于原生 edit | 通过 |
47
+ | 本地 Final Exact 距最佳对照不超过 2/100 | 通过 |
48
+ | 相对发布版的改善数不少于回归数 | 通过 |
49
+ | 本地独有最终回归不超过 2 个 | 通过 |
50
+ | 四项效率至少三项不差于原生,且单项恶化不超过 10% | 通过 |
51
+
52
+ ### 相对原生 edit 的效率
53
+
54
+ | 指标 | 原生 edit | 本地版 | 变化 | 判定 |
55
+ |---|---:|---:|---:|---|
56
+ | 成本 | $4.104 | $4.145 | 1.01% | 轻微恶化,低于 10% 上限 |
57
+ | Token | 1,253,279 | 1,049,619 | -16.25% | 改善 |
58
+ | 耗时 | 2887.2s | 2838.0s | -1.70% | 改善 |
59
+ | 工具调用 | 225 | 194 | -13.78% | 改善 |
60
+
61
+ 本地版四项效率中三项优于原生 edit;成本增加约 1%,没有任何指标恶化超过 10%,也没有超过 20% 的警告项。
62
+
63
+ ### Cohort 覆盖
64
+
65
+ | Cohort | 计划任务 | 已完成配对任务 | 原生 First / Final | 发布版 First / Final | 本地版 First / Final |
66
+ |---|---:|---:|---:|---:|---:|
67
+ | neutral | 80 | 80 | 73/74 | 73/74 | 74/74 |
68
+ | challenge | 20 | 20 | 18/18 | 18/18 | 18/18 |
69
+
70
+
71
+ ### 任务类别明细(First / Final / 总数)
72
+
73
+ | 类别 | 配对任务 | 原生 edit | 发布版 | 本地版 |
74
+ |---|---:|---:|---:|---:|
75
+ | copy-block | 3 | 3/3/3 | 2/3/3 | 3/3/3 |
76
+ | copy-within | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
77
+ | delete-block | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
78
+ | delete-subset | 3 | 2/3/3 | 3/3/3 | 3/3/3 |
79
+ | distinct-edits | 4 | 4/4/4 | 4/4/4 | 4/4/4 |
80
+ | insert-block | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
81
+ | insert-payload | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
82
+ | insert-subset | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
83
+ | language-insert | 13 | 13/13/13 | 13/13/13 | 13/13/13 |
84
+ | language-replace | 13 | 13/13/13 | 13/13/13 | 13/13/13 |
85
+ | language-select | 13 | 13/13/13 | 13/13/13 | 13/13/13 |
86
+ | literal | 5 | 5/5/5 | 5/5/5 | 5/5/5 |
87
+ | move-between | 4 | 4/4/4 | 4/4/4 | 4/4/4 |
88
+ | move-block | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
89
+ | multi-file | 4 | 4/4/4 | 4/4/4 | 4/4/4 |
90
+ | replace-all | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
91
+ | replace-block | 3 | 3/3/3 | 3/3/3 | 3/3/3 |
92
+ | select-one | 4 | 4/4/4 | 4/4/4 | 4/4/4 |
93
+ | select-subset | 4 | 2/2/4 | 2/2/4 | 2/2/4 |
94
+ | unicode-fix | 4 | 0/0/4 | 0/0/4 | 0/0/4 |
95
+ | unique | 2 | 0/0/2 | 0/0/2 | 0/0/2 |
96
+
97
+ ### 共同例外
98
+
99
+ 三臂最终均失败的 8 个任务为:`select-subset-1000-unicode`、`select-subset-1000-plain`、4 个 `unicode-fix-*` 任务,以及 `unique-10-plain`、`unique-100-unicode`。它们不构成本地修改版回归;本地独有最终回归为 0。
100
+
101
+ ### 可复现信息
102
+
103
+ - 数据源:https://github.com/alexshpunt/explicit-edit-benchmark,commit `5827e3fb65845f4b76adb06e5919d94ec4ffd5cf`。
104
+ - 冻结任务:80 Neutral + 20 Challenge;seed 为 `pi-toolkit-codex-edit-100-v1`。
105
+ - 模型:`openai-codex/gpt-5.6-sol`;reasoning:`low`;Pi:`0.85.1`;最多 5 次 Oracle recovery。
106
+ - 发布版:`@maxiaochao/pi-codex-edit@0.1.7 (sha512-hfNfx20h51FLCueXaMqBUAVYnxoCakuI306rzuFPI/lS0HT1jE/wQ8xkKl9PwEXboVVD6ZzS/YB0VVrd67V7cw==)`。
107
+ - 本地版:`@maxiaochao/pi-codex-edit@0.1.7+local (sha512-Mf21ZfSwOOgaH2lSiF/DpoA8Jk5jH84MaZUX1abkMop3HST3xt2YtCw2NKwrRJpvmJB02bl9wwYyFlAVI68Qwg==)`。
108
+ - Run ID:`explicit-edit-100-sol-low-v3-complete`;运行完整性:300/300。
109
+ - Identity:task manifest `cab0278f530271c7bb3de3e4dba6ab252fa2b748d00164e749ecdbd8f9f7f431`;config `739bb323275fdcead7709f19a894b0dee57062f40064c04d182d96474535328c`;verifier `8bd4be15c886bba84e8a6fa46576c43f2ca0f02d2536d98403f97361914a166d`。
110
+ - Provider 失败:0;失败工具调用:0。
111
+
112
+
113
+ ## 协议与包验证摘要
114
+
115
+ | 检查项 | 结果 | 命令 |
116
+ |---|---|---|
117
+ | Node 回归测试 | 通过 | `node --experimental-strip-types --test tests/package.test.mjs tests/parser.test.mjs tests/released-vs-local.test.mjs tests/benchmark.test.mjs` |
118
+ | 原生 edit 与本地 Codex Edit 对比 | 通过 | `pi -ne -e ./tests/native-vs-codex.ts` |
119
+ | 扩展加载 | 通过 | `pi -ne -e ./extensions/codex-edit.ts` |
120
+ | 独立包 tarball | 通过 | `npm pack --dry-run --json` |
121
+
122
+ ## 确定性回归基线
123
+
124
+ - Node 测试:预期 22 个,覆盖 parser、matcher、路由、包契约、resolved-path 防护、发布版对比、分层调度和生产事故语料。
125
+ - 共享对比 corpus:12 个 case。
126
+ - 原生 edit:5 通过 / 2 失败或正确拒绝 / 5 不适用。
127
+ - Codex Edit 发布版 v0.1.7:6 通过 / 6 失败或正确拒绝。
128
+ - Codex Edit 本地版:7 通过 / 5 正确安全拒绝。
129
+
130
+ “正确拒绝”表示无效或陈旧操作被拒绝,且工作区没有变化。“不适用”表示 Pi 原生 `edit` 无法用一次编辑协议调用表达该操作,不计为工具缺陷。
131
+
132
+ ## 安全回归
133
+
134
+ 本地版 5/5 个安全 case 均正确拒绝,且工作区零写入。特别是 `outside-workspace-absolute-path` 的结果为 **工作区外路径 — 正确拒绝**;放行该操作会允许任意写入 benchmark sandbox 之外。安全拒绝不计入编辑失败率。
135
+
136
+ ## 生产事故回归
137
+
138
+ 这些 case 来自实际报告过的问题。Agent-and-protocol case 使用不包含工具语法提示的用户任务,可进入后续付费模型运行;protocol-only case 只验证指定工具形态,不用于宣称模型质量提升。
139
+
140
+ | Incident | 层级 | 对应回归 | 问题 |
141
+ |---|---|---|---|
142
+ | `incident-repeated-markdown-context` | agent-and-protocol | `repeated-markdown-separator` | 重复 Markdown 分隔符曾触发 ambiguous match |
143
+ | `incident-repeated-update-same-path` | protocol-only | `repeated-update-same-path` | 同一个 raw path 的多个 Update File 块在发布版中冲突 |
144
+ | `incident-runner-type-rename` | agent-and-protocol | `ordered-runner-type-renames` | runner.ts 中重复类型名的大范围重命名曾触发 ambiguous match |
145
+ | `incident-multi-file-structural-edit` | agent-and-protocol | `multi-file-add-delete-move` | 一次 patch 中的 add/delete/move 需要原子预校验和执行 |
146
+ | `incident-bom-crlf` | agent-and-protocol | `bom-and-crlf` | UTF-8 BOM 与 CRLF 曾导致原生精确文本匹配失败 |
147
+
148
+ 冻结的 100-task 清单按每 5 个任务包含 4 Neutral + 1 Challenge 的顺序调度;本次完整运行覆盖全部 80 个 Neutral 和 20 个 Challenge。
149
+
150
+ ## 确定性对比结论
151
+
152
+ 每个测试臂运行同一组 12 个 case。对比 1 执行 24 次工具调用(原生 + 本地);对比 2 执行 24 次 parser/执行评估(发布版 + 本地)。下表以 7 个有效编辑任务为完成率分母;5 个故意无效或危险的 patch 单独计入安全检查。
153
+
154
+ ### 1. Pi 原生 edit vs Codex Edit 本地版
155
+
156
+ | 指标 | Pi 原生 edit | Codex Edit 本地版 | 变化 |
157
+ |---|---:|---:|---:|
158
+ | 有效任务完成 | 5/7 | 7/7 | +2 个 |
159
+ | 有效任务完成率 | 71.4% | 100.0% | +28.6 pp |
160
+ | 有效任务失败 | 1/7 | 0/7 | 减少 1 个 |
161
+ | 有效任务不适用 | 1/7 | 0/7 | 减少 1 个 |
162
+ | 正确拒绝适用的危险/陈旧 patch | 1/1 (100.0%) | 5/5 (100.0%) | Codex 覆盖全部 5 个 |
163
+
164
+ ### 2. Codex Edit 发布版 v0.1.7 vs 本地修改版
165
+
166
+ | 指标 | 发布版 v0.1.7 | 本地修改版 | 变化 |
167
+ |---|---:|---:|---:|
168
+ | 有效任务完成 | 6/7 | 7/7 | +1 个 |
169
+ | 有效任务完成率 | 85.7% | 100.0% | +14.3 pp |
170
+ | 安全 case 正确拒绝 | 5/5 (100.0%) | 5/5 (100.0%) | 无变化 |
171
+ | 保留原有通过任务 | 6/6 | 6/6 | 100% 保留 |
172
+
173
+ 这些是确定性协议/执行成功率,不是独立模型采样成功率。Agent 质量、token、成本和耗时只在 Agent Benchmark 章节报告。
174
+
175
+ ## 逐 case:原生 edit vs 本地 Codex Edit
176
+
177
+ 该对比在隔离临时工作区中执行 Pi 导出的 `createEditToolDefinition`,以及本地扩展实际注册的 `apply_patch`。
178
+
179
+ | Case | 原生 edit | 本地 Codex Edit | 说明 |
180
+ |---|---|---|---|
181
+ | `unique-replacement` | 通过 | 通过 | 预期行为一致 |
182
+ | `disjoint-edits-one-file` | 通过 | 通过 | 预期行为一致 |
183
+ | `repeated-markdown-separator` | 正确拒绝/失败 | 通过 | Codex 协议可处理该形态 |
184
+ | `repeated-update-same-path` | 通过 | 通过 | 预期行为一致 |
185
+ | `ordered-runner-type-renames` | 通过 | 通过 | 预期行为一致 |
186
+ | `multi-file-add-delete-move` | 不适用 | 通过 | Codex 协议可处理该形态 |
187
+ | `stale-expected-lines` | 正确拒绝/失败 | 正确拒绝/失败 | 预期行为一致 |
188
+ | `conflicting-targets` | 不适用 | 正确拒绝/失败 | 两者均避免危险写入 |
189
+ | `empty-update-chunk` | 不适用 | 正确拒绝/失败 | 两者均避免危险写入 |
190
+ | `resolved-path-alias-conflict` | 不适用 | 正确拒绝/失败 | 两者均避免危险写入 |
191
+ | `outside-workspace-absolute-path` | 不适用 | 正确拒绝/失败 | 两者均避免危险写入 |
192
+ | `bom-and-crlf` | 通过 | 通过 | 预期行为一致 |
193
+
194
+ 关键结果:本地 Codex Edit 可以处理重复 Markdown 上下文和多文件 add/delete/move;原生精确替换无法用一次调用安全表达这些形态。两者都会拒绝陈旧的预期文本。
195
+
196
+ ## 逐 case:发布版 v0.1.7 vs 本地 Codex Edit
197
+
198
+ 发布版测试臂使用从 tag `codex-edit-v0.1.7` 冻结的语义基线(source blob `e2896b59d40b5f6f01b3a4a7918476f4acc28cd8`)。
199
+
200
+ | Case | 发布版 v0.1.7 | 本地版 | 回归状态 |
201
+ |---|---|---|---|
202
+ | `unique-replacement` | 通过 | 通过 | 已保留 |
203
+ | `disjoint-edits-one-file` | 通过 | 通过 | 已保留 |
204
+ | `repeated-markdown-separator` | 通过 | 通过 | 已保留 |
205
+ | `repeated-update-same-path` | 正确拒绝/失败 | 通过 | 已修复 |
206
+ | `ordered-runner-type-renames` | 通过 | 通过 | 已保留 |
207
+ | `multi-file-add-delete-move` | 通过 | 通过 | 已保留 |
208
+ | `stale-expected-lines` | 正确拒绝/失败 | 正确拒绝/失败 | 已保留 |
209
+ | `conflicting-targets` | 正确拒绝/失败 | 正确拒绝/失败 | 已保留 |
210
+ | `empty-update-chunk` | 正确拒绝/失败 | 正确拒绝/失败 | 已保留 |
211
+ | `resolved-path-alias-conflict` | 正确拒绝/失败 | 正确拒绝/失败 | 已保留 |
212
+ | `outside-workspace-absolute-path` | 正确拒绝/失败 | 正确拒绝/失败 | 已保留 |
213
+ | `bom-and-crlf` | 通过 | 通过 | 已保留 |
214
+
215
+ 关键结果:`repeated-update-same-path` 从 v0.1.7 的失败变为本地版通过。既有成功 case 全部保留;陈旧、空 chunk、冲突和 resolved-alias 目标仍会被安全拒绝。
216
+
217
+ ## 已报告错误覆盖
218
+
219
+ | 错误形态 | 当前策略 | Corpus 覆盖 |
220
+ |---|---|---|
221
+ | 重复 Markdown/HTML 区域或重复类型名(`Ambiguous ... match`) | 从当前 hunk cursor 起选择第一个匹配,hunk 按源位置排序 | `repeated-markdown-separator`、`ordered-runner-type-renames` 及 parser HTML 测试 |
222
+ | 同一 raw path 出现多个普通 `Update File` | 合并后按顺序执行 | `repeated-update-same-path` |
223
+ | Add/delete/move 或 resolved path alias 冲突 | 写入前拒绝 | `conflicting-targets`、`resolved-path-alias-conflict` |
224
+ | 工作区外绝对路径 | 读取或写入前正确拒绝 | `outside-workspace-absolute-path` |
225
+ | 空 update chunk | 拒绝,不猜测意图 | `empty-update-chunk` |
226
+ | 陈旧预期行 | 拒绝;调用方必须重新读取并生成 patch | `stale-expected-lines` |
227
+ | 缺少 `patch` 参数 | 必填 TypeBox schema 在扩展执行前拒绝 | 包契约测试 |
228
+
229
+ ## 上游实现对齐
230
+
231
+ 报告结构遵循 OpenAI Codex 的 scenario-test 模式:输入文件 + patch + 精确预期的最终文件系统。新旧 parser 对比采用 Codex 统一 streaming parser 时使用的差分测试方法。
232
+
233
+ 本地 repeated-header 支持是有意加入的兼容扩展。当前 Codex 已验证实现会拒绝多个操作解析到同一路径,并推荐在一个 `Update File` 下使用多个 `@@` chunk。本实现只容忍 raw path 完全相同的重复普通 update;resolved alias 和破坏性操作冲突仍会拒绝。
234
+
235
+ 来源:
236
+ - OpenAI Codex scenario tests: https://github.com/openai/codex/blob/9a8730f3/codex-rs/apply-patch/tests/suite/scenarios.rs
237
+ - OpenAI Codex parser differential validation: https://github.com/openai/codex/commit/e26f734f919819bdd802b1b67ad653863c7a90e5
238
+ - Duplicate resolved-path guard: https://github.com/openai/codex/pull/37867
239
+
240
+ ## 维护规则
241
+
242
+ 每个新的生产失败都必须先转化为 `tests/comparison-cases.mjs` 中的最小 case,并明确原生、发布版和本地版预期。已有 case 保持不变。发布前运行两组对比并重新生成本报告。模型路由、prompt 或 schema 变化还必须运行独立的真实模型 A/B benchmark。
@@ -0,0 +1,9 @@
1
+ # Codex Edit 测试摘要
2
+
3
+ - Agent Benchmark:**通过(有共同例外)** — 完成 300/300 条 chain;全部正式验收门槛通过。
4
+ - 协议/包回归:**通过** — 4/4 项检查通过。
5
+ - 安全回归:**5/5 正确拒绝**,覆盖陈旧上下文、空 chunk、目标冲突、resolved alias 和工作区外路径。
6
+ - First Exact:原生 edit 91/100,发布版 91/100,本地版 92/100;Final Exact:三臂均为 92/100。
7
+ - 总成本:$12.388;Provider 失败:0。
8
+
9
+ 完整证据和方法见 `test-report.md` 或自包含的 `test-report.html`。
@@ -10,14 +10,14 @@ import {
10
10
  import { Type } from "typebox";
11
11
  import {
12
12
  APPLY_PATCH_LARK_GRAMMAR,
13
- applyUpdate,
13
+ applyUpdateGroups,
14
14
  parsePatch,
15
15
  resolveWorkspacePath,
16
- } from "./parser.ts";
17
- import { modelIsAllowed } from "./model-routing.ts";
16
+ } from "../src/parser.ts";
17
+ import { modelIsAllowed } from "../src/model-routing.ts";
18
18
 
19
19
  const editorToolNames = new Set(["edit", "apply_patch"]);
20
- const settingsUrl = new URL("./config.json", import.meta.url);
20
+ const settingsUrl = new URL("../config.json", import.meta.url);
21
21
 
22
22
  async function loadSettings() {
23
23
  try {
@@ -140,7 +140,9 @@ export default async function (pi) {
140
140
  } else {
141
141
  await fileInfo(operation.source, `Source file ${operation.path}`);
142
142
  const before = await readFile(operation.source, "utf8");
143
- const after = operation.action === "delete" ? "" : applyUpdate(before, operation.chunks, operation.path);
143
+ const after = operation.action === "delete"
144
+ ? ""
145
+ : applyUpdateGroups(before, operation.chunkGroups, operation.path);
144
146
  if (operation.destination && await pathExists(operation.destination)) {
145
147
  throw new Error(`Cannot move over existing file ${operation.moveTo}`);
146
148
  }
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@maxiaochao/pi-codex-edit",
3
+ "version": "0.1.9",
4
+ "description": "Pi extension that exposes Codex-style apply_patch for GPT/Codex models with model-aware edit fallback.",
5
+ "type": "module",
6
+ "keywords": [
7
+ "pi-package",
8
+ "pi-extension",
9
+ "codex",
10
+ "gpt-5",
11
+ "apply-patch"
12
+ ],
13
+ "files": [
14
+ "extensions",
15
+ "src",
16
+ "tests",
17
+ "benchmark",
18
+ "docs",
19
+ "config.json",
20
+ "summary.md",
21
+ "codex-edit-explainer.html",
22
+ "README.md",
23
+ "TESTING.md",
24
+ "CHANGELOG.md",
25
+ "package.json"
26
+ ],
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/chaochaoxiaochao/pi-toolkit.git",
30
+ "directory": "packages/codex-edit"
31
+ },
32
+ "pi": {
33
+ "extensions": [
34
+ "./extensions/codex-edit.ts"
35
+ ]
36
+ },
37
+ "peerDependencies": {
38
+ "@earendil-works/pi-ai": "*",
39
+ "@earendil-works/pi-coding-agent": "*",
40
+ "@earendil-works/pi-tui": "*",
41
+ "typebox": "*"
42
+ },
43
+ "scripts": {
44
+ "test": "node tests/generate-test-report.mjs",
45
+ "test:regression": "node --experimental-strip-types --test tests/package.test.mjs tests/parser.test.mjs tests/released-vs-local.test.mjs tests/benchmark.test.mjs",
46
+ "test:compare-native": "pi -ne -e ./tests/native-vs-codex.ts",
47
+ "test:compare-released": "node --experimental-strip-types --test tests/released-vs-local.test.mjs",
48
+ "test:report": "node tests/generate-test-report.mjs",
49
+ "load-test": "pi -ne -e ./extensions/codex-edit.ts"
50
+ },
51
+ "publishConfig": {
52
+ "access": "public"
53
+ }
54
+ }
@@ -88,9 +88,28 @@ export function parsePatch(patch) {
88
88
  throw new Error(`Invalid hunk header on line ${index + 1}: ${line}`);
89
89
  }
90
90
  if (operations.length === 0) throw new Error("Patch contains no file operations");
91
- const targets = operations.flatMap((operation) => [operation.path, operation.moveTo].filter(Boolean));
92
- if (new Set(targets).size !== targets.length) throw new Error("Multiple operations target the same path");
93
- return operations;
91
+ const coalesced = [];
92
+ const targetOwners = new Map();
93
+ for (const operation of operations) {
94
+ const prior = targetOwners.get(operation.path);
95
+ if (
96
+ prior?.action === "update" &&
97
+ !prior.moveTo &&
98
+ operation.action === "update" &&
99
+ !operation.moveTo
100
+ ) {
101
+ prior.chunkGroups.push(operation.chunks);
102
+ continue;
103
+ }
104
+ const targets = [operation.path, operation.moveTo].filter(Boolean);
105
+ if (targets.some((target) => targetOwners.has(target))) {
106
+ throw new Error("Multiple operations target the same path");
107
+ }
108
+ if (operation.action === "update") operation.chunkGroups = [operation.chunks];
109
+ coalesced.push(operation);
110
+ for (const target of targets) targetOwners.set(target, operation);
111
+ }
112
+ return coalesced;
94
113
  }
95
114
 
96
115
  function normalizeLineEndings(text) {
@@ -115,8 +134,7 @@ function locate(lines, pattern, start, eof, label) {
115
134
  ];
116
135
  for (const compare of comparisons) {
117
136
  const candidates = findCandidates(lines, pattern, start, eof, compare);
118
- if (candidates.length === 1) return candidates[0];
119
- if (candidates.length > 1) throw new Error(`Ambiguous ${label || "patch"} match: ${candidates.length} regions found`);
137
+ if (candidates.length > 0) return candidates[0];
120
138
  }
121
139
  throw new Error(`Failed to find expected lines${label ? ` in ${label}` : ""}: ${pattern.join("\\n")}`);
122
140
  }
@@ -145,6 +163,10 @@ export function applyUpdate(content, chunks, path) {
145
163
  return bom + next;
146
164
  }
147
165
 
166
+ export function applyUpdateGroups(content, chunkGroups, path) {
167
+ return chunkGroups.reduce((current, chunks) => applyUpdate(current, chunks, path), content);
168
+ }
169
+
148
170
  export function resolveWorkspacePath(cwd, rawPath) {
149
171
  if (!rawPath || rawPath.includes("\0")) throw new Error(`Unsafe patch path: ${rawPath}`);
150
172
  const absolute = resolve(cwd, rawPath);
@@ -51,6 +51,6 @@ The probe conditions above reflect v0.1.5, when the route still required grammar
51
51
 
52
52
  ## Decision
53
53
 
54
- No-go for universal enablement in the original benchmark environment. The evidence did not justify routing every model through `apply_patch` or making it the default editor for the whole Pi workflow. The shipped route is therefore a model-ID allowlist (`gpt-5.6-*` plus an `openai-responses`/`openai-codex-responses` API check) that keeps `edit` as the fallback everywhere else. The implementation now ships inside `@maxiaochao/pi-toolkit` instead of as a standalone opt-in package, but the allowlist — not the packaging — is what bounds the change: for a non-allowlisted model the extension only re-syncs an already-active editor tool and otherwise returns early.
54
+ No-go for universal routing in the original benchmark environment. The evidence did not justify routing every model through `apply_patch` or making it the default editor for the whole Pi workflow. Ship the extension both with `@maxiaochao/pi-toolkit` and as the standalone `@maxiaochao/pi-codex-edit` child package, while bounding activation with a model-ID allowlist (`gpt-5.6-*` plus an `openai-responses`/`openai-codex-responses` API check) and keeping `edit` as the fallback everywhere else.
55
55
 
56
56
  The production route was validated with `pudu-openai-proxy/gpt-5.6-sol:high` and `pudu-openai-proxy/gpt-5.6-luna:high`. Sol's 20-case fair run improved first editor-call success from 85% to 95% with equal final correctness. A corrected Luna rerun used the fully qualified `pudu-openai-proxy/gpt-5.6-luna:high` in both arms, with 20/20 normally completed runs in each arm: both reached 18/20 final correctness, while first editor-call success was 18/20 for `edit` and 20/20 for `apply_patch`. Among the 17 cases correct in both arms, the paired median total-duration delta was +2.3s for `apply_patch`, with 8 cases faster and 9 slower; across all 20 paired runs, the median delta was +1.2s, with 9 cases faster and 11 slower. The measured tradeoff justified validating Sol first; the shipped `gpt-5.6-*` glob additionally enables Luna and Terra, whose gap is protocol adaptation rather than a stable duration win.