tianshu-harness 3.23.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 (255) hide show
  1. package/LICENSE +189 -0
  2. package/README.en.md +1066 -0
  3. package/README.ja.md +1214 -0
  4. package/README.ko.md +1216 -0
  5. package/README.md +309 -0
  6. package/completions/rivet.bash +118 -0
  7. package/completions/rivet.fish +110 -0
  8. package/completions/rivet.ps1 +230 -0
  9. package/completions/rivet.zsh +187 -0
  10. package/dist/account-OAEKORZE.js +47 -0
  11. package/dist/account-login-V6FOLHJR.js +65 -0
  12. package/dist/account-status-KHNPPAFL.js +23 -0
  13. package/dist/agent/worker-process/child.js +407 -0
  14. package/dist/browser-cli-FLNY5ZHI.js +19 -0
  15. package/dist/browser-readiness-LWVGSBD5.js +12 -0
  16. package/dist/build-backends-5KXKR2S4.js +13 -0
  17. package/dist/build-options-66Y4ZE7C.js +9 -0
  18. package/dist/bundled-skills/brainstorming/SKILL.md +139 -0
  19. package/dist/bundled-skills/visual-acceptance/SKILL.md +64 -0
  20. package/dist/chain-7MAMFXZ3.js +10 -0
  21. package/dist/checkpoint-MND4DUIX.js +33 -0
  22. package/dist/chunk-2E6CK2RC.js +392 -0
  23. package/dist/chunk-2EBWGAXD.js +51 -0
  24. package/dist/chunk-2HDP7RMR.js +119 -0
  25. package/dist/chunk-2HKD2UFM.js +1000 -0
  26. package/dist/chunk-2VYRLULP.js +51 -0
  27. package/dist/chunk-2XN7PM4N.js +125 -0
  28. package/dist/chunk-35H7GVNR.js +66 -0
  29. package/dist/chunk-3C3R7JXY.js +77 -0
  30. package/dist/chunk-3VGD2FXB.js +118 -0
  31. package/dist/chunk-424AP7FM.js +534 -0
  32. package/dist/chunk-4J5TZGOE.js +3707 -0
  33. package/dist/chunk-4QJ3Y5JM.js +898 -0
  34. package/dist/chunk-4U2ET7ZY.js +1326 -0
  35. package/dist/chunk-4XPFLICE.js +876 -0
  36. package/dist/chunk-5B2OCU5P.js +31707 -0
  37. package/dist/chunk-5GGOGLSN.js +73 -0
  38. package/dist/chunk-5JRZPLI6.js +251 -0
  39. package/dist/chunk-5TQ3JYBU.js +397 -0
  40. package/dist/chunk-66JPZSGX.js +55 -0
  41. package/dist/chunk-6HSVXHCZ.js +764 -0
  42. package/dist/chunk-6S5FPUS7.js +21 -0
  43. package/dist/chunk-722LQO64.js +131 -0
  44. package/dist/chunk-73NJ7IT7.js +354 -0
  45. package/dist/chunk-7FW3I7WC.js +95 -0
  46. package/dist/chunk-7KUCBNLB.js +173 -0
  47. package/dist/chunk-7RDOV3DF.js +201 -0
  48. package/dist/chunk-A5MNH4ER.js +79 -0
  49. package/dist/chunk-ADIVZ2D5.js +15 -0
  50. package/dist/chunk-AE3P3F6W.js +366 -0
  51. package/dist/chunk-AERM3VGP.js +280 -0
  52. package/dist/chunk-AEWHC4WM.js +246 -0
  53. package/dist/chunk-AOVQ7VIF.js +169 -0
  54. package/dist/chunk-AOYHP3WI.js +12411 -0
  55. package/dist/chunk-B3YEBABJ.js +7160 -0
  56. package/dist/chunk-BCQXEZPJ.js +154 -0
  57. package/dist/chunk-BKTQAYO2.js +418 -0
  58. package/dist/chunk-BSECIPAQ.js +80 -0
  59. package/dist/chunk-BUIMGNDD.js +52 -0
  60. package/dist/chunk-BXHV3GSB.js +10320 -0
  61. package/dist/chunk-C6ZVZCPP.js +139 -0
  62. package/dist/chunk-CEZNQVWM.js +82 -0
  63. package/dist/chunk-CGFZGR7G.js +1492 -0
  64. package/dist/chunk-CGOADM2X.js +337 -0
  65. package/dist/chunk-CI4R6P2B.js +24 -0
  66. package/dist/chunk-D2TCGRSX.js +73 -0
  67. package/dist/chunk-D4PTYFOG.js +154 -0
  68. package/dist/chunk-D54ZWG4T.js +717 -0
  69. package/dist/chunk-DEBI37QB.js +145 -0
  70. package/dist/chunk-DWROHPXP.js +1051 -0
  71. package/dist/chunk-DZ6UOJ5D.js +92 -0
  72. package/dist/chunk-EBJTVRQN.js +81 -0
  73. package/dist/chunk-EGEI2RQA.js +1167 -0
  74. package/dist/chunk-EHHUASO5.js +1305 -0
  75. package/dist/chunk-EPKTIOED.js +41 -0
  76. package/dist/chunk-ER5JW2QZ.js +5681 -0
  77. package/dist/chunk-ETHFACAB.js +315 -0
  78. package/dist/chunk-EXUE5W2F.js +112 -0
  79. package/dist/chunk-EXXICUIW.js +73 -0
  80. package/dist/chunk-FFQSZJOO.js +169 -0
  81. package/dist/chunk-FHFKPGDO.js +85 -0
  82. package/dist/chunk-FW3QKMFX.js +158 -0
  83. package/dist/chunk-GD4ZNZ7W.js +30 -0
  84. package/dist/chunk-GHVMWF2I.js +581 -0
  85. package/dist/chunk-GMKAXSYQ.js +297 -0
  86. package/dist/chunk-HBVF6BWO.js +325 -0
  87. package/dist/chunk-HV4YGVMS.js +379 -0
  88. package/dist/chunk-ILBCKAV6.js +89 -0
  89. package/dist/chunk-J4U4AW46.js +440 -0
  90. package/dist/chunk-J6ESSM47.js +1791 -0
  91. package/dist/chunk-JBZHIC6T.js +2309 -0
  92. package/dist/chunk-JCFWOULR.js +245 -0
  93. package/dist/chunk-JRF5L6OD.js +131 -0
  94. package/dist/chunk-JSS75KCG.js +2014 -0
  95. package/dist/chunk-JSXWIWWJ.js +225 -0
  96. package/dist/chunk-JYV7I2QH.js +98 -0
  97. package/dist/chunk-KIOXTQQL.js +1596 -0
  98. package/dist/chunk-KWVTXT5B.js +89 -0
  99. package/dist/chunk-KX22RD6R.js +33 -0
  100. package/dist/chunk-LA6IPTGH.js +709 -0
  101. package/dist/chunk-LH5VCGLB.js +1796 -0
  102. package/dist/chunk-LQQ6XJ2Y.js +269 -0
  103. package/dist/chunk-MJQ55WZZ.js +65 -0
  104. package/dist/chunk-ML3UKMH3.js +40 -0
  105. package/dist/chunk-MOZPLHSU.js +5526 -0
  106. package/dist/chunk-NGEWJ4DF.js +89 -0
  107. package/dist/chunk-NH6VIOAR.js +211 -0
  108. package/dist/chunk-O5VZQKCB.js +26106 -0
  109. package/dist/chunk-OHSV42QB.js +748 -0
  110. package/dist/chunk-OO3TAHO5.js +4070 -0
  111. package/dist/chunk-OTNHSAGN.js +38282 -0
  112. package/dist/chunk-OUWAC7AQ.js +442 -0
  113. package/dist/chunk-OXMIV4WE.js +270 -0
  114. package/dist/chunk-P5KJURUR.js +229 -0
  115. package/dist/chunk-P6GJOVO6.js +307 -0
  116. package/dist/chunk-QARAGUXX.js +23 -0
  117. package/dist/chunk-R43HJCDX.js +2711 -0
  118. package/dist/chunk-RAXBAWRN.js +210 -0
  119. package/dist/chunk-RK3JHWPT.js +39 -0
  120. package/dist/chunk-RWA23GMM.js +24 -0
  121. package/dist/chunk-S4K4L6IM.js +179 -0
  122. package/dist/chunk-SAXK6LVF.js +124 -0
  123. package/dist/chunk-SEPGEJNK.js +1607 -0
  124. package/dist/chunk-SUJERP5I.js +285 -0
  125. package/dist/chunk-T3ED6BY2.js +913 -0
  126. package/dist/chunk-THKDVSEC.js +90 -0
  127. package/dist/chunk-TI4GSEL5.js +52 -0
  128. package/dist/chunk-TPI73L57.js +164 -0
  129. package/dist/chunk-TVHW77C7.js +338 -0
  130. package/dist/chunk-TWNHOSDT.js +1073 -0
  131. package/dist/chunk-UFMN76QD.js +347 -0
  132. package/dist/chunk-UHGLFRZE.js +205 -0
  133. package/dist/chunk-UTZYPKBF.js +15231 -0
  134. package/dist/chunk-UXNLELW6.js +25 -0
  135. package/dist/chunk-UXXS53EA.js +166 -0
  136. package/dist/chunk-V5DHFGI7.js +171 -0
  137. package/dist/chunk-V5YIPMHV.js +2353 -0
  138. package/dist/chunk-WMUWEO2C.js +754 -0
  139. package/dist/chunk-WOE3D7JM.js +945 -0
  140. package/dist/chunk-X2IK5LVR.js +2592 -0
  141. package/dist/chunk-XU2YXCDL.js +459 -0
  142. package/dist/chunk-YJG5HIX5.js +203 -0
  143. package/dist/chunk-Z3QE7TZM.js +72 -0
  144. package/dist/chunk-Z5Z3JHJY.js +64 -0
  145. package/dist/chunk-ZDIFBJFT.js +168 -0
  146. package/dist/chunk-ZGGCEXDC.js +176 -0
  147. package/dist/chunk-ZMZCZNKU.js +263 -0
  148. package/dist/chunk-ZNC5T65N.js +17 -0
  149. package/dist/chunk-ZO3I3A7O.js +308 -0
  150. package/dist/chunk-ZQKXBUB7.js +217 -0
  151. package/dist/chunk-ZTRINFM2.js +78 -0
  152. package/dist/chunk-ZU7JZCVP.js +193 -0
  153. package/dist/chunk-ZZAWNALS.js +350 -0
  154. package/dist/cli/entry.js +54 -0
  155. package/dist/connector-L5QQTR6Y.js +26 -0
  156. package/dist/context-62DC6EC3.js +31 -0
  157. package/dist/council-convene-NJWPNBDU.js +46 -0
  158. package/dist/create-agent-config-ODSQWNVC.js +55 -0
  159. package/dist/default-registry-2PU3BTAU.js +68 -0
  160. package/dist/deliver-task-KONV4C4M.js +57 -0
  161. package/dist/delivery-gate-v2-F2YVMVGK.js +27 -0
  162. package/dist/domain-knowledge-store-DDZ3F4J7.js +11 -0
  163. package/dist/fetch-core-ZPPYDSZH.js +14 -0
  164. package/dist/galaxy-K72BBDQN.js +91 -0
  165. package/dist/goal-criteria-SCALAU2D.js +141 -0
  166. package/dist/goal-persist-XWY2GDQX.js +19 -0
  167. package/dist/goal-tracker-FXMJ2GVK.js +11 -0
  168. package/dist/headless-LSB6I7PT.js +162 -0
  169. package/dist/headless-coordinator-VDPDMVUT.js +90 -0
  170. package/dist/inject-U55SMB6A.js +39 -0
  171. package/dist/login-flow-CD4ZGXOC.js +59 -0
  172. package/dist/logs-cli-SQVYFMMN.js +283 -0
  173. package/dist/loop-ULZYBDA3.js +76 -0
  174. package/dist/main.js +4064 -0
  175. package/dist/manager-ROZBCWPA.js +171 -0
  176. package/dist/manager-Z7XFFUOM.js +10670 -0
  177. package/dist/meridian-indexer-ZIW363L7.js +21 -0
  178. package/dist/native-resolver-PN34UIVV.js +13 -0
  179. package/dist/node_modules/better-sqlite3/lib/database.js +90 -0
  180. package/dist/node_modules/better-sqlite3/lib/index.js +3 -0
  181. package/dist/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
  182. package/dist/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
  183. package/dist/node_modules/better-sqlite3/lib/methods/function.js +31 -0
  184. package/dist/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
  185. package/dist/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
  186. package/dist/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
  187. package/dist/node_modules/better-sqlite3/lib/methods/table.js +189 -0
  188. package/dist/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
  189. package/dist/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
  190. package/dist/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
  191. package/dist/node_modules/better-sqlite3/lib/util.js +12 -0
  192. package/dist/node_modules/better-sqlite3/package.json +59 -0
  193. package/dist/ownership-ledger-E3WIEKPL.js +9 -0
  194. package/dist/package.json +3 -0
  195. package/dist/path-grants-XRRXHXG7.js +40 -0
  196. package/dist/paths-LVV5IMQN.js +49 -0
  197. package/dist/plan-fact-anchors-LMVQZCW5.js +13 -0
  198. package/dist/plan-store-ODLTTPFB.js +17 -0
  199. package/dist/plan-templates-5FPFBOL6.js +109 -0
  200. package/dist/plugin-loader-ZZKX7ZE6.js +22 -0
  201. package/dist/prefix-budget-62OV5HQC.js +13 -0
  202. package/dist/project-trust-5WS5MOMU.js +31 -0
  203. package/dist/provider-cli-ALDQXIT7.js +37 -0
  204. package/dist/providers-XQYTAVPK.js +11 -0
  205. package/dist/proxy-resolver-V2Y6534D.js +15 -0
  206. package/dist/recovery-cli-KPGY6FY3.js +106 -0
  207. package/dist/review-discipline-MNSYCST5.js +55 -0
  208. package/dist/review-router-BVQZW3MX.js +18 -0
  209. package/dist/seed-capsules/seed-capsule-attack-methodology.md +62 -0
  210. package/dist/seed-capsules/seed-capsule-changgeng.md +31 -0
  211. package/dist/seed-capsules/seed-capsule-design-aesthetics.md +38 -0
  212. package/dist/seed-capsules/seed-capsule-diagnostic-ladder.md +54 -0
  213. package/dist/seed-capsules/seed-capsule-fu.md +39 -0
  214. package/dist/seed-capsules/seed-capsule-huagai.md +73 -0
  215. package/dist/seed-capsules/seed-capsule-kaiyang.md +65 -0
  216. package/dist/seed-capsules/seed-capsule-knowledge-work.md +40 -0
  217. package/dist/seed-capsules/seed-capsule-tanlang.md +47 -0
  218. package/dist/seed-capsules/seed-capsule-tianfu.md +23 -0
  219. package/dist/seed-capsules/seed-capsule-tianliang.md +63 -0
  220. package/dist/seed-capsules/seed-capsule-tianquan.md +32 -0
  221. package/dist/seed-capsules/seed-capsule-tianxuan-ccr.md +22 -0
  222. package/dist/seed-capsules/seed-capsule-tianxuan.md +64 -0
  223. package/dist/seed-capsules/seed-capsule-yaoguang.md +87 -0
  224. package/dist/serve-X2R2UTLF.js +170 -0
  225. package/dist/serve-agent-KHSU6RVV.js +965 -0
  226. package/dist/session-persist-HUUC772H.js +37 -0
  227. package/dist/session-registry-6XUD5C4Y.js +15 -0
  228. package/dist/skill-loader-CK3RMO47.js +48 -0
  229. package/dist/skills/cli-harness-sop.md +207 -0
  230. package/dist/spawn-git-4HFU65AH.js +29 -0
  231. package/dist/sse-EYUTBWUK.js +512 -0
  232. package/dist/starflow-NOFBMPFU.js +30 -0
  233. package/dist/streamableHttp-V67CNJMO.js +447 -0
  234. package/dist/task-contract-6XHBNJQK.js +32 -0
  235. package/dist/task-ledger-3K4TJIER.js +9 -0
  236. package/dist/team-orchestrate-S6NHZP7X.js +64 -0
  237. package/dist/title-extract-QX4XGCYN.js +50 -0
  238. package/dist/todo-QDXGRZZP.js +31 -0
  239. package/dist/todo-store-V3TTO5AH.js +12 -0
  240. package/dist/tool-preset-LU5BYEDZ.js +23 -0
  241. package/dist/tool-tiers-GD742YBN.js +23 -0
  242. package/dist/turndown.es-OI55K6C3.js +17373 -0
  243. package/dist/verification-attribution-EKG4CNUV.js +13 -0
  244. package/dist/volatile-git-MBTST7RA.js +15 -0
  245. package/dist/wave-checkpoint-XLIIHXQA.js +30 -0
  246. package/dist/web-cli-DUG4I35O.js +233 -0
  247. package/dist/web-search-3IK542AA.js +34 -0
  248. package/dist/workers/cpu-worker.js +45 -0
  249. package/dist/workflow-runner-HX7UBAYG.js +297 -0
  250. package/dist/worktree-baseline-O5NCU6M2.js +9 -0
  251. package/package.json +122 -0
  252. package/scripts/check-mobile-env.mjs +76 -0
  253. package/scripts/fetch-native-sqlite.js +392 -0
  254. package/scripts/prune-installed-wasms.js +45 -0
  255. package/scripts/tree-sitter-wasm-keep.js +40 -0
@@ -0,0 +1,2309 @@
1
+ #!/usr/bin/env -S node --expose-gc --max-old-space-size=4096
2
+ import { createRequire as __rivetCreateRequire } from 'node:module'; const require = __rivetCreateRequire(import.meta.url);
3
+ import {
4
+ formatObligationReport,
5
+ verifyObligations
6
+ } from "./chunk-KWVTXT5B.js";
7
+ import {
8
+ createDelegationActivityMapper
9
+ } from "./chunk-AERM3VGP.js";
10
+ import {
11
+ getStoredPlan
12
+ } from "./chunk-RK3JHWPT.js";
13
+ import {
14
+ formatPatcherArtifacts,
15
+ reviewWorkflowBudgetMs,
16
+ routeReviewWorkflow
17
+ } from "./chunk-NH6VIOAR.js";
18
+ import {
19
+ GENERAL_DEV_DISCIPLINES,
20
+ LARGE_FILE_WARN_THRESHOLD,
21
+ isCrossModule,
22
+ isFixContext,
23
+ shouldRouteReviewWorkflow
24
+ } from "./chunk-S4K4L6IM.js";
25
+ import {
26
+ deserializeUnifiedPlan
27
+ } from "./chunk-TVHW77C7.js";
28
+ import {
29
+ appendMemoryEntry,
30
+ countSimilarMemoryEntries,
31
+ enqueuePostCommitReviewOutcome,
32
+ findApprovedPlanInventory,
33
+ findUnverifiedClaimRefs,
34
+ formatInventoryReport,
35
+ formatProbeHits,
36
+ isUiFilePath,
37
+ isVisualVerifyTool,
38
+ scanFilesForProbes,
39
+ verifyRegressionInventory
40
+ } from "./chunk-T3ED6BY2.js";
41
+ import {
42
+ acknowledgeAll,
43
+ readUnacknowledged
44
+ } from "./chunk-ZU7JZCVP.js";
45
+ import {
46
+ evaluateTestPresence,
47
+ isSourceFilePath,
48
+ isTestFilePath,
49
+ parseVerifyCommand,
50
+ spawnVerifyArgv,
51
+ testPresenceGateEnabled
52
+ } from "./chunk-LA6IPTGH.js";
53
+ import {
54
+ analyzeImpact
55
+ } from "./chunk-3C3R7JXY.js";
56
+ import {
57
+ filterExternalNoise,
58
+ summarizeOwnershipHealth
59
+ } from "./chunk-2E6CK2RC.js";
60
+ import {
61
+ debugLog
62
+ } from "./chunk-CI4R6P2B.js";
63
+ import {
64
+ spawnGitSync
65
+ } from "./chunk-THKDVSEC.js";
66
+ import {
67
+ init_esm_shims
68
+ } from "./chunk-5GGOGLSN.js";
69
+
70
+ // src/agent/deliver-task.ts
71
+ init_esm_shims();
72
+ import { existsSync as existsSync2, readFileSync as readFileSync3, statSync, unlinkSync } from "fs";
73
+ import { join as join3, isAbsolute as isAbsolute3 } from "path";
74
+
75
+ // src/agent/change-classification.ts
76
+ init_esm_shims();
77
+ var DOCS_FILE_PATTERN = /(?:^|\/)README|CHANGELOG(?:\.[^/]*)?$|\.(?:md|mdx|rst|adoc|txt|json|yaml|yml|toml|ini|cfg)$/i;
78
+ var DOCS_DIR_PATTERN = /(?:^|\/)docs\//i;
79
+ var TEST_FILE_PATTERN = /(?:^|\/)__tests__\//i;
80
+ function isDocsOrTestFile(file) {
81
+ return DOCS_FILE_PATTERN.test(file) || DOCS_DIR_PATTERN.test(file) || TEST_FILE_PATTERN.test(file);
82
+ }
83
+ function parsePatchLines(patch) {
84
+ const added = [];
85
+ const removed = [];
86
+ for (const line of patch.split("\n")) {
87
+ if (line.startsWith("+++") || line.startsWith("---")) continue;
88
+ if (line.startsWith("+")) added.push(line.slice(1));
89
+ else if (line.startsWith("-")) removed.push(line.slice(1));
90
+ }
91
+ return { added, removed };
92
+ }
93
+ function isWhitespaceOrCommentOnly(added, removed) {
94
+ const isNoise = (l) => l.trim() === "" || l.trim().startsWith("//") || l.trim().startsWith("/*") || l.trim().startsWith("*") || l.trim().startsWith("*/");
95
+ return added.every(isNoise) && removed.every(isNoise);
96
+ }
97
+ function detectSingleIdentifierReplacement(added, removed) {
98
+ if (added.length === 0 || added.length !== removed.length) return null;
99
+ if (added.length > 200) return null;
100
+ const IDENT = /[a-zA-Z_$][a-zA-Z0-9_$]*/g;
101
+ const collect = (lines) => {
102
+ const ids = [];
103
+ for (const line of lines) {
104
+ const matches2 = line.matchAll(IDENT);
105
+ for (const m of matches2) ids.push(m[0]);
106
+ }
107
+ return ids;
108
+ };
109
+ const addedIds = collect(added);
110
+ const removedIds = collect(removed);
111
+ if (addedIds.length === 0 || removedIds.length === 0) return null;
112
+ const countFreq = (ids) => {
113
+ const m = /* @__PURE__ */ new Map();
114
+ for (const id of ids) m.set(id, (m.get(id) ?? 0) + 1);
115
+ return m;
116
+ };
117
+ const addedFreq = countFreq(addedIds);
118
+ const removedFreq = countFreq(removedIds);
119
+ const oldCandidates = [];
120
+ const newCandidates = [];
121
+ for (const [id, count] of removedFreq) {
122
+ const inAdded = addedFreq.get(id) ?? 0;
123
+ if (inAdded < count) oldCandidates.push(id);
124
+ }
125
+ for (const [id, count] of addedFreq) {
126
+ const inRemoved = removedFreq.get(id) ?? 0;
127
+ if (inRemoved < count) newCandidates.push(id);
128
+ }
129
+ if (oldCandidates.length !== 1 || newCandidates.length !== 1) return null;
130
+ const oldName = oldCandidates[0];
131
+ const newName = newCandidates[0];
132
+ if (oldName.length < 2 || newName.length < 2) return null;
133
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(oldName)) return null;
134
+ if (!/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(newName)) return null;
135
+ const KEYWORDS = /* @__PURE__ */ new Set(["true", "false", "null", "undefined", "void", "this", "super"]);
136
+ if (KEYWORDS.has(oldName) || KEYWORDS.has(newName)) return null;
137
+ for (let i = 0; i < added.length; i++) {
138
+ const expected = removed[i].split(oldName).join(newName);
139
+ if (expected !== added[i]) return null;
140
+ }
141
+ return { old: oldName, new: newName };
142
+ }
143
+ function classifyChange(files, diff) {
144
+ if (files.length === 0) {
145
+ return { class: "normal", skipReview: false, skipVerification: false, reason: "no files", files };
146
+ }
147
+ if (files.every(isDocsOrTestFile)) {
148
+ return {
149
+ class: "docs-only",
150
+ skipReview: true,
151
+ skipVerification: true,
152
+ reason: `${files.length} doc/test file(s), no code logic`,
153
+ files
154
+ };
155
+ }
156
+ const nameStatus = diff.nameStatus();
157
+ const renameEndpoints = /* @__PURE__ */ new Set();
158
+ for (const line of nameStatus.split("\n")) {
159
+ if (!line.trim()) continue;
160
+ const parts = line.split(" ");
161
+ const status = parts[0] ?? "";
162
+ if (status.startsWith("R100")) {
163
+ if (parts[1]) renameEndpoints.add(parts[1]);
164
+ if (parts[2]) renameEndpoints.add(parts[2]);
165
+ }
166
+ }
167
+ if (renameEndpoints.size > 0 && files.every((f) => isDocsOrTestFile(f) || renameEndpoints.has(f))) {
168
+ return {
169
+ class: "rename-mechanical",
170
+ skipReview: true,
171
+ skipVerification: true,
172
+ reason: `pure rename(s) R100, zero content change: ${[...renameEndpoints].join(", ")}`,
173
+ files
174
+ };
175
+ }
176
+ let allWhitespaceOrComment = true;
177
+ let singleRename = null;
178
+ let hasCodeFile = false;
179
+ for (const file of files) {
180
+ if (isDocsOrTestFile(file)) continue;
181
+ hasCodeFile = true;
182
+ const patch = diff.filePatch(file);
183
+ if (!patch) {
184
+ allWhitespaceOrComment = false;
185
+ break;
186
+ }
187
+ const { added, removed } = parsePatchLines(patch);
188
+ if (added.length === 0 && removed.length === 0) continue;
189
+ if (!isWhitespaceOrCommentOnly(added, removed)) {
190
+ allWhitespaceOrComment = false;
191
+ const replacement = detectSingleIdentifierReplacement(added, removed);
192
+ if (replacement) {
193
+ if (singleRename === null) {
194
+ singleRename = replacement;
195
+ } else if (singleRename.old !== replacement.old || singleRename.new !== replacement.new) {
196
+ singleRename = null;
197
+ break;
198
+ }
199
+ } else {
200
+ singleRename = null;
201
+ break;
202
+ }
203
+ }
204
+ }
205
+ if (hasCodeFile && allWhitespaceOrComment) {
206
+ return {
207
+ class: "rename-mechanical",
208
+ skipReview: true,
209
+ skipVerification: true,
210
+ reason: "all changes are comments or whitespace only",
211
+ files
212
+ };
213
+ }
214
+ if (singleRename) {
215
+ return {
216
+ class: "heuristic-rename",
217
+ skipReview: true,
218
+ skipVerification: false,
219
+ // still needs verification!
220
+ reason: `consistent identifier rename: ${singleRename.old} \u2192 ${singleRename.new}`,
221
+ files
222
+ };
223
+ }
224
+ return {
225
+ class: "normal",
226
+ skipReview: false,
227
+ skipVerification: false,
228
+ reason: "contains logic changes",
229
+ files
230
+ };
231
+ }
232
+ function createGitDiffProvider(cwd, files, dirtyFiles) {
233
+ let partition;
234
+ const getPartition = () => {
235
+ if (partition) return partition;
236
+ const untracked = /* @__PURE__ */ new Set();
237
+ if (files.length > 0) {
238
+ const result = spawnGitSync(["-c", "core.quotePath=false", "ls-files", "--", ...files], {
239
+ cwd,
240
+ encoding: "utf-8",
241
+ timeout: 5e3
242
+ });
243
+ const listed = result.status === 0 ? new Set(String(result.stdout).split("\n").filter(Boolean)) : /* @__PURE__ */ new Set();
244
+ for (const f of files) {
245
+ if (!listed.has(f)) untracked.add(f);
246
+ }
247
+ }
248
+ partition = { untracked };
249
+ return partition;
250
+ };
251
+ const renameScope = dirtyFiles && dirtyFiles.length > 0 ? dirtyFiles : files;
252
+ let cachedNameStatus;
253
+ const nameStatus = () => {
254
+ if (cachedNameStatus !== void 0) return cachedNameStatus;
255
+ if (renameScope.length === 0) {
256
+ cachedNameStatus = "";
257
+ return cachedNameStatus;
258
+ }
259
+ const result = spawnGitSync(["-c", "core.quotePath=false", "diff", "-M", "--name-status", "HEAD", "--", ...renameScope], {
260
+ cwd,
261
+ encoding: "utf-8",
262
+ timeout: 1e4
263
+ });
264
+ cachedNameStatus = result.status === 0 ? String(result.stdout) : "";
265
+ return cachedNameStatus;
266
+ };
267
+ const filePatch = (file) => {
268
+ if (getPartition().untracked.has(file)) return "";
269
+ const result = spawnGitSync(["-c", "core.quotePath=false", "diff", "HEAD", "--", file], {
270
+ cwd,
271
+ encoding: "utf-8",
272
+ timeout: 1e4
273
+ });
274
+ return result.status === 0 ? String(result.stdout) : "";
275
+ };
276
+ return { nameStatus, filePatch };
277
+ }
278
+ function isMechanicalFastPathEnabled(reviewConfig) {
279
+ return reviewConfig?.mechanicalFastPath !== false;
280
+ }
281
+
282
+ // src/agent/scoped-git-commit.ts
283
+ init_esm_shims();
284
+ import { existsSync } from "fs";
285
+ import { isAbsolute, join, relative, resolve } from "path";
286
+ var LOCK_RETRY_DELAYS_MS = [1e3, 2e3, 4e3];
287
+ function isLockContention(output) {
288
+ const lower = output.toLowerCase();
289
+ return lower.includes("index.lock") || lower.includes("unable to create") || lower.includes("another git process");
290
+ }
291
+ function sleepSync(ms) {
292
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
293
+ }
294
+ function classifyStalePaths(cwd, files) {
295
+ const addable = [];
296
+ const missing = [];
297
+ for (const f of files) {
298
+ if (existsSync(join(cwd, f))) addable.push(f);
299
+ else missing.push(f);
300
+ }
301
+ if (missing.length === 0) return { addable, stale: [] };
302
+ const ls = runGit(cwd, ["ls-files", "-z", "--", ...missing]);
303
+ const inIndex = new Set(ls.ok ? ls.output.split("\0").filter(Boolean) : []);
304
+ const stale = [];
305
+ for (const f of missing) {
306
+ if (inIndex.has(f)) addable.push(f);
307
+ else stale.push(f);
308
+ }
309
+ return { addable, stale };
310
+ }
311
+ function commitScopedFiles(input) {
312
+ if (!input.message.trim()) return { ok: false, output: "Commit message is required." };
313
+ const files = normalizeFiles(input.cwd, input.files);
314
+ if (files.length === 0) return { ok: false, output: "No owned files to commit." };
315
+ const { addable, stale } = classifyStalePaths(input.cwd, files);
316
+ if (addable.length === 0) {
317
+ return {
318
+ ok: false,
319
+ output: `No owned files to commit: every owned path is stale \u2014 already deleted and committed by another session: ${stale.join(", ")}. Refresh the owned set before retrying.`
320
+ };
321
+ }
322
+ const staleNote = stale.length > 0 ? `\u26A0 Skipped ${stale.length} stale path(s) already committed externally: ${stale.join(", ")}
323
+ ` : "";
324
+ for (let attempt = 0; ; attempt++) {
325
+ const add = runGit(input.cwd, ["add", "--", ...addable]);
326
+ if (!add.ok) {
327
+ const delay = LOCK_RETRY_DELAYS_MS[attempt];
328
+ if (delay !== void 0 && isLockContention(add.output)) {
329
+ sleepSync(delay);
330
+ continue;
331
+ }
332
+ return add;
333
+ }
334
+ const commit = runGit(input.cwd, ["commit", "-m", input.message, "--only", "--", ...addable]);
335
+ if (!commit.ok) {
336
+ const delay = LOCK_RETRY_DELAYS_MS[attempt];
337
+ if (delay !== void 0 && isLockContention(commit.output)) {
338
+ sleepSync(delay);
339
+ continue;
340
+ }
341
+ const lower = commit.output.toLowerCase();
342
+ if (lower.includes("nothing to commit") || lower.includes("no changes")) {
343
+ return { ok: false, output: `No changes in owned files to commit (${addable.join(", ")}). Files may have been committed already or not modified.` };
344
+ }
345
+ }
346
+ if (!commit.ok) return commit;
347
+ return { ok: true, output: staleNote + commit.output };
348
+ }
349
+ }
350
+ function normalizeFiles(cwd, files) {
351
+ const normalized = files.map((file) => {
352
+ const resolved = resolve(cwd, file);
353
+ const rel = relative(cwd, resolved);
354
+ if (rel === "" || rel.startsWith("..") || isAbsolute(rel)) return null;
355
+ return rel;
356
+ }).filter((file) => file !== null);
357
+ return [...new Set(normalized)].sort((a, b) => a.localeCompare(b));
358
+ }
359
+ function runGit(cwd, args) {
360
+ const result = spawnGitSync(args, {
361
+ cwd,
362
+ encoding: "utf-8",
363
+ timeout: 1e4,
364
+ env: { ...process.env, LC_ALL: "C", LANG: "C" }
365
+ });
366
+ const output = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
367
+ if (result.status !== 0) return { ok: false, output: output || `git ${args[0] ?? "command"} failed` };
368
+ return { ok: true, output };
369
+ }
370
+
371
+ // src/agent/review-principle-checklist.ts
372
+ init_esm_shims();
373
+ function extractReviewPrinciples(markdown) {
374
+ const entries = markdown.split(/(?=^### )/m).filter((entry) => entry.trim());
375
+ const principles = [];
376
+ for (const entry of entries) {
377
+ const kind = extractField(entry, "Kind");
378
+ if (!kind || !kind.includes("review_principle")) continue;
379
+ const heading = entry.match(/^###\s+\d{4}-\d{2}-\d{2}\s+\u2014\s+(.+)$/m);
380
+ const title = heading?.[1]?.trim();
381
+ const claim = extractField(entry, "Claim");
382
+ if (!title || !claim) continue;
383
+ principles.push({
384
+ title,
385
+ claim,
386
+ appliesWhen: extractListSection(entry, "Applies when"),
387
+ reviewRule: extractField(entry, "Review rule"),
388
+ evidence: extractCodePaths(extractListSection(entry, "Evidence"))
389
+ });
390
+ }
391
+ return principles;
392
+ }
393
+ function buildReviewPrincipleChecklist(input) {
394
+ const changed = new Set(input.changedFiles.map(normalizePath));
395
+ const items = [];
396
+ for (const principle of extractReviewPrinciples(input.knowledgeMarkdown)) {
397
+ const matchedEvidence = principle.evidence.find((path) => changed.has(normalizePath(path)));
398
+ if (!matchedEvidence) continue;
399
+ const rule = principle.reviewRule ?? principle.claim;
400
+ items.push({
401
+ source: principle.title,
402
+ question: rule,
403
+ reason: `Changed file matches review-principle evidence path: ${matchedEvidence}`
404
+ });
405
+ }
406
+ return items.slice(0, input.maxItems ?? 5);
407
+ }
408
+ function extractField(entry, label) {
409
+ const re = new RegExp(`^\\*\\*${escapeRegExp(label)}\\*\\*:\\s*(.+)$`, "im");
410
+ return entry.match(re)?.[1]?.trim();
411
+ }
412
+ function extractListSection(entry, label) {
413
+ const escapedLabel = escapeRegExp(label);
414
+ const startRe = new RegExp(`^\\*\\*${escapedLabel}\\*\\*:\\s*$`, "im");
415
+ const startMatch = entry.match(startRe);
416
+ if (!startMatch?.[0]) return [];
417
+ const startIndex = entry.indexOf(startMatch[0]) + startMatch[0].length;
418
+ const remaining = entry.slice(startIndex);
419
+ const lines = [];
420
+ for (const line of remaining.split("\n")) {
421
+ const trimmed = line.trim();
422
+ if (trimmed === "") continue;
423
+ if (/^\*\*[^*]+\*\*:/.test(trimmed) || trimmed.startsWith("### ")) break;
424
+ if (trimmed.startsWith("-")) {
425
+ lines.push(trimmed.replace(/^-\s*/, "").trim());
426
+ }
427
+ }
428
+ return lines.filter(Boolean);
429
+ }
430
+ function extractCodePaths(lines) {
431
+ return lines.map((line) => line.match(/`([^`]+)`/)?.[1] ?? line).filter((path) => path.includes("/"));
432
+ }
433
+ function normalizePath(path) {
434
+ return path.replace(/^\.\//, "");
435
+ }
436
+ function escapeRegExp(text) {
437
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
438
+ }
439
+
440
+ // src/agent/commit-cohesion.ts
441
+ init_esm_shims();
442
+ function extractTopDir(filePath) {
443
+ const parts = filePath.split("/");
444
+ if (parts.length <= 1) return ".";
445
+ return parts.slice(0, 2).join("/");
446
+ }
447
+ function checkCommitCohesion(files, thresholds) {
448
+ const maxFiles = thresholds?.maxFiles ?? 5;
449
+ const maxTopDirs = thresholds?.maxTopDirs ?? 2;
450
+ const topDirSet = /* @__PURE__ */ new Set();
451
+ for (const f of files) {
452
+ topDirSet.add(extractTopDir(f));
453
+ }
454
+ const topDirs = [...topDirSet].sort();
455
+ const topDirCount = topDirs.length;
456
+ const fileCount = files.length;
457
+ const overFileLimit = fileCount > maxFiles;
458
+ const overDirLimit = topDirCount > maxTopDirs;
459
+ const needsWarning = overFileLimit || overDirLimit;
460
+ const warningLines = [];
461
+ const splitSuggestion = [];
462
+ if (needsWarning) {
463
+ const areaSummary = topDirs.join(", ");
464
+ if (overDirLimit) {
465
+ warningLines.push(
466
+ `\u274C Commit cohesion gate: ${fileCount} owned files across ${topDirCount} areas (${areaSummary}).`
467
+ );
468
+ } else {
469
+ warningLines.push(
470
+ `\u274C Commit cohesion gate: ${fileCount} owned files. Large commit \u2014 verify all changes are one logical unit.`
471
+ );
472
+ }
473
+ warningLines.push(
474
+ "If this truly is one logical unit, re-run with force=true to override."
475
+ );
476
+ const filesByArea = /* @__PURE__ */ new Map();
477
+ for (const f of files) {
478
+ const dir = extractTopDir(f);
479
+ const existing = filesByArea.get(dir);
480
+ if (existing) existing.push(f);
481
+ else filesByArea.set(dir, [f]);
482
+ }
483
+ const sortedAreas = [...filesByArea.entries()].sort(([a], [b]) => a.localeCompare(b));
484
+ for (const [area, areaFiles] of sortedAreas) {
485
+ const fileList = areaFiles.map((f) => `"${f}"`).join(", ");
486
+ splitSuggestion.push(
487
+ `deliver_task commit=true message="\u63CF\u8FF0" files=[${fileList}]`
488
+ );
489
+ }
490
+ warningLines.push(
491
+ "Suggested split by area:"
492
+ );
493
+ for (const cmd of splitSuggestion) {
494
+ warningLines.push(` ${cmd}`);
495
+ }
496
+ }
497
+ return { topDirs, topDirCount, fileCount, needsWarning, warningLines, splitSuggestion };
498
+ }
499
+
500
+ // src/config/review-discipline-config.ts
501
+ init_esm_shims();
502
+ function isReviewDisciplineEnabled() {
503
+ const raw = process.env.RIVET_REVIEW_DISCIPLINE;
504
+ if (raw === void 0) return true;
505
+ const lower = raw.trim().toLowerCase();
506
+ return !(lower === "0" || lower === "false" || lower === "off" || lower === "no");
507
+ }
508
+
509
+ // src/agent/review-health.ts
510
+ init_esm_shims();
511
+ var state = {
512
+ totalRuns: 0,
513
+ infraFailureCount: 0,
514
+ consecutiveInfraFailures: 0,
515
+ retryRecoveredCount: 0
516
+ };
517
+ function recordAutoReviewRun(run) {
518
+ state = {
519
+ ...state,
520
+ totalRuns: state.totalRuns + 1,
521
+ infraFailureCount: run.ran ? state.infraFailureCount : state.infraFailureCount + 1,
522
+ consecutiveInfraFailures: run.ran ? 0 : state.consecutiveInfraFailures + 1,
523
+ retryRecoveredCount: run.recoveredByRetry ? state.retryRecoveredCount + 1 : state.retryRecoveredCount,
524
+ ...run.ran ? {} : { lastFailureAt: Date.now(), lastFailureKinds: run.failureKinds ?? [] }
525
+ };
526
+ }
527
+ function formatReviewHealthLine() {
528
+ if (state.totalRuns === 0) return "reviewHealth: no auto reviews this session";
529
+ const parts = [
530
+ `reviewHealth: ${state.totalRuns - state.infraFailureCount}/${state.totalRuns} runs produced a verdict`
531
+ ];
532
+ if (state.infraFailureCount > 0) {
533
+ parts.push(`${state.infraFailureCount} infra failure(s)`);
534
+ if (state.consecutiveInfraFailures > 1) parts.push(`${state.consecutiveInfraFailures} consecutive`);
535
+ if (state.lastFailureKinds && state.lastFailureKinds.length > 0) parts.push(`last: ${state.lastFailureKinds.join(",")}`);
536
+ }
537
+ if (state.retryRecoveredCount > 0) parts.push(`${state.retryRecoveredCount} recovered by retry`);
538
+ return parts.join(" \u2014 ");
539
+ }
540
+
541
+ // src/agent/wiring-nudge.ts
542
+ init_esm_shims();
543
+ import { readFileSync } from "fs";
544
+ import { resolve as resolve2 } from "path";
545
+ var MAX_SYMBOLS_SCANNED = 8;
546
+ var MAX_NUDGE_FINDINGS = 5;
547
+ var EXPORT_RE = /^export\s+(?:declare\s+)?(?:abstract\s+)?(?:async\s+)?(?:function\*?|const|let|class|interface|type|enum)\s+([A-Za-z_$][\w$]*)/;
548
+ var FIELD_RE = /^\s{2,}(?:readonly\s+)?([A-Za-z_$][\w$]*)\??:\s*\S/;
549
+ var NOISE_NAMES = /* @__PURE__ */ new Set([
550
+ "id",
551
+ "name",
552
+ "type",
553
+ "kind",
554
+ "value",
555
+ "data",
556
+ "key",
557
+ "path",
558
+ "file",
559
+ "files",
560
+ "content",
561
+ "message",
562
+ "status",
563
+ "state",
564
+ "result",
565
+ "error",
566
+ "options",
567
+ "config",
568
+ "input",
569
+ "output",
570
+ "index",
571
+ "count",
572
+ "items",
573
+ "description",
574
+ "required",
575
+ "properties"
576
+ ]);
577
+ function isCandidateName(name) {
578
+ return name.length >= 4 && !NOISE_NAMES.has(name);
579
+ }
580
+ function gitAddedLines(cwd, file) {
581
+ try {
582
+ const diff = spawnGitSync(["diff", "HEAD", "--", file], { cwd, encoding: "utf-8", timeout: 1e4 });
583
+ if (diff.status === 0 && diff.stdout.trim()) {
584
+ return diff.stdout.split(/\r?\n/).filter((l) => l.startsWith("+") && !l.startsWith("+++")).map((l) => l.slice(1));
585
+ }
586
+ const tracked = spawnGitSync(["ls-files", "--error-unmatch", "--", file], { cwd, encoding: "utf-8", timeout: 5e3 });
587
+ if (tracked.status !== 0) {
588
+ try {
589
+ return readFileSync(resolve2(cwd, file), "utf-8").split(/\r?\n/);
590
+ } catch {
591
+ return [];
592
+ }
593
+ }
594
+ } catch {
595
+ }
596
+ return [];
597
+ }
598
+ function collectAddedSymbols(cwd, changedFiles) {
599
+ const seen = /* @__PURE__ */ new Set();
600
+ const exportsFound = [];
601
+ const fieldsFound = [];
602
+ for (const file of changedFiles) {
603
+ if (!/\.(ts|tsx|mts|cts)$/.test(file) || /\.test\./.test(file) || file.includes("__tests__")) continue;
604
+ for (const line of gitAddedLines(cwd, file)) {
605
+ const exp = EXPORT_RE.exec(line.trimStart());
606
+ if (exp && isCandidateName(exp[1]) && !seen.has(exp[1])) {
607
+ seen.add(exp[1]);
608
+ exportsFound.push({ symbol: exp[1], file, kind: "export" });
609
+ continue;
610
+ }
611
+ const field = FIELD_RE.exec(line);
612
+ if (field && isCandidateName(field[1]) && !seen.has(field[1])) {
613
+ seen.add(field[1]);
614
+ fieldsFound.push({ symbol: field[1], file, kind: "field" });
615
+ }
616
+ }
617
+ }
618
+ return [...fieldsFound, ...exportsFound].slice(0, MAX_SYMBOLS_SCANNED);
619
+ }
620
+ function isWriteOrDeclareOnly(line, symbol) {
621
+ const occurrences = line.split(symbol).length - 1;
622
+ if (occurrences === 0) return true;
623
+ const writeRe = new RegExp(`\\b${symbol}\\??\\s*(?::(?!:)|=(?![=>]))`, "g");
624
+ const writes = (line.match(writeRe) ?? []).length;
625
+ return writes >= occurrences;
626
+ }
627
+ function hasReadSideUsage(cwd, symbol) {
628
+ try {
629
+ const grep = spawnGitSync(
630
+ ["grep", "-n", "--untracked", "-F", symbol, "--", "*.ts", "*.tsx"],
631
+ { cwd, encoding: "utf-8", timeout: 1e4 }
632
+ );
633
+ if (grep.status !== 0 && grep.status !== 1) return true;
634
+ for (const hit of grep.stdout.split("\n")) {
635
+ if (!hit) continue;
636
+ const firstColon = hit.indexOf(":");
637
+ const secondColon = hit.indexOf(":", firstColon + 1);
638
+ if (firstColon < 0 || secondColon < 0) continue;
639
+ const file = hit.slice(0, firstColon);
640
+ if (/\.test\./.test(file) || file.includes("__tests__")) continue;
641
+ const text = hit.slice(secondColon + 1);
642
+ if (text.trimStart().startsWith("//") || text.trimStart().startsWith("*")) continue;
643
+ if (!isWriteOrDeclareOnly(text, symbol)) return true;
644
+ }
645
+ return false;
646
+ } catch {
647
+ return true;
648
+ }
649
+ }
650
+ function detectWroteButNeverRead(cwd, changedFiles) {
651
+ const findings = [];
652
+ for (const candidate of collectAddedSymbols(cwd, changedFiles)) {
653
+ if (!hasReadSideUsage(cwd, candidate.symbol)) findings.push(candidate);
654
+ if (findings.length >= MAX_NUDGE_FINDINGS) break;
655
+ }
656
+ return findings;
657
+ }
658
+ function formatWroteButNeverRead(findings) {
659
+ if (findings.length === 0) return [];
660
+ const lines = ["", "\u26A0\uFE0F wrote-but-never-read (YELLOW, non-blocking):"];
661
+ for (const f of findings) {
662
+ lines.push(` ${f.symbol} (${f.kind}, added in ${f.file}) \u2014 0 read-side consumers found. Wire a reader on the production path or remove it.`);
663
+ }
664
+ return lines;
665
+ }
666
+ var FIELD_READ_RE = /\.([A-Za-z_$][\w$]*)\b(?!\s*\()/g;
667
+ function colonRhsIsType(rhs) {
668
+ const t = rhs.trim();
669
+ return /^(?:string|number|boolean|unknown|any|void|null|undefined|symbol|bigint|object|never|true|false|readonly\b|keyof\b|typeof\b|Array<|Record<|Partial<|Readonly<|Promise<|Map<|Set<|\[|\(|[A-Z])/.test(t);
670
+ }
671
+ function isFieldDeclaration(line, field) {
672
+ if (/\b(?:interface|type)\b/.test(line)) return true;
673
+ const m = new RegExp(`^\\s*(?:readonly\\s+)?${field}\\??:\\s*(.+)$`).exec(line);
674
+ return m ? colonRhsIsType(m[1]) : false;
675
+ }
676
+ function isFieldValueWrite(line, field) {
677
+ if (isFieldDeclaration(line, field)) return false;
678
+ if (new RegExp(`\\.${field}\\s*=(?![=>])`).test(line)) return true;
679
+ const m = new RegExp(`\\b${field}\\s*:\\s*(.+)$`).exec(line);
680
+ if (m && !colonRhsIsType(m[1])) return true;
681
+ return false;
682
+ }
683
+ function collectReadFields(cwd, changedFiles) {
684
+ const seen = /* @__PURE__ */ new Set();
685
+ const found = [];
686
+ for (const file of changedFiles) {
687
+ if (!/\.(ts|tsx|mts|cts)$/.test(file) || /\.test\./.test(file) || file.includes("__tests__")) continue;
688
+ for (const line of gitAddedLines(cwd, file)) {
689
+ let m;
690
+ FIELD_READ_RE.lastIndex = 0;
691
+ while (m = FIELD_READ_RE.exec(line)) {
692
+ const name = m[1];
693
+ if (isCandidateName(name) && !seen.has(name)) {
694
+ seen.add(name);
695
+ found.push({ symbol: name, file, kind: "field" });
696
+ }
697
+ }
698
+ }
699
+ }
700
+ return found.slice(0, MAX_SYMBOLS_SCANNED);
701
+ }
702
+ function fieldWriteSites(cwd, field) {
703
+ try {
704
+ const grep = spawnGitSync(
705
+ ["grep", "-n", "--untracked", "-F", field, "--", "*.ts", "*.tsx"],
706
+ { cwd, encoding: "utf-8", timeout: 1e4 }
707
+ );
708
+ if (grep.status !== 0 && grep.status !== 1) return { prod: 1, test: 0 };
709
+ let prod = 0;
710
+ let test = 0;
711
+ for (const hit of grep.stdout.split("\n")) {
712
+ if (!hit) continue;
713
+ const firstColon = hit.indexOf(":");
714
+ const secondColon = hit.indexOf(":", firstColon + 1);
715
+ if (firstColon < 0 || secondColon < 0) continue;
716
+ const file = hit.slice(0, firstColon);
717
+ const text = hit.slice(secondColon + 1);
718
+ if (text.trimStart().startsWith("//") || text.trimStart().startsWith("*")) continue;
719
+ if (!isFieldValueWrite(text, field)) continue;
720
+ if (/\.test\./.test(file) || file.includes("__tests__")) test++;
721
+ else prod++;
722
+ }
723
+ return { prod, test };
724
+ } catch {
725
+ return { prod: 1, test: 0 };
726
+ }
727
+ }
728
+ function detectReadButNeverProduced(cwd, changedFiles) {
729
+ const findings = [];
730
+ for (const candidate of collectReadFields(cwd, changedFiles)) {
731
+ const sites = fieldWriteSites(cwd, candidate.symbol);
732
+ if (sites.prod === 0 && sites.test > 0) {
733
+ findings.push({ field: candidate.symbol, file: candidate.file });
734
+ }
735
+ if (findings.length >= MAX_NUDGE_FINDINGS) break;
736
+ }
737
+ return findings;
738
+ }
739
+ function formatReadButNeverProduced(findings) {
740
+ if (findings.length === 0) return [];
741
+ const lines = ["", "\u26A0\uFE0F read-but-never-produced (YELLOW, \u7591\u4F3C\u865A\u5047\u7EFF\u706F / non-blocking):"];
742
+ for (const f of findings) {
743
+ lines.push(` ${f.field} (read in ${f.file}) \u2014 \u4EC5\u6D4B\u8BD5 fixture \u8D4B\u503C\uFF0C\u751F\u4EA7\u4EE3\u7801\u65E0\u5199\u5165\u70B9\u3002\u786E\u8BA4\u771F\u5B9E\u6570\u636E\u6D41\u5199\u5165\u70B9\uFF0C\u5426\u5219\u6D88\u8D39\u662F\u6B7B\u4EE3\u7801\u3002`);
744
+ }
745
+ return lines;
746
+ }
747
+
748
+ // src/agent/claim-audit.ts
749
+ init_esm_shims();
750
+ var TEST_CLAIM_RE = /全绿|所有测试通过|\btests?\s+(?:pass(?:ed|ing)?|green)\b|\d+\s*\/\s*\d+\s*(?:通过|passed|pass)/i;
751
+ var TYPECHECK_CLAIM_RE = /(?:typecheck|类型检查)\s*(?:干净|clean|passed|通过)/i;
752
+ var COUNT_CLAIM_RE = /(\d+)\s*\/\s*(\d+)\s*(?:通过|passed|pass|全绿)/i;
753
+ function claimAuditEnabled() {
754
+ return process.env.RIVET_CLAIM_AUDIT !== "0";
755
+ }
756
+ var GIT_WORKTREE_MUTATION_RE = /\bgit\s+(?:stash(?:_pop)?(?!\s+(?:list|show))|reset\s+--hard|checkout(?!\s+-b\b)|switch(?!\s+-c\b)|restore|clean|merge|rebase|cherry-pick|revert|apply|pull)\b/i;
757
+ function isCodeMutation(e) {
758
+ if (e.type === "file_write") {
759
+ if (typeof e.path !== "string" || e.path.length === 0) return true;
760
+ return isSourceFilePath(e.path) || isTestFilePath(e.path);
761
+ }
762
+ if (e.type === "git_action") {
763
+ const cmd = typeof e.meta?.command === "string" ? e.meta.command : "";
764
+ return GIT_WORKTREE_MUTATION_RE.test(cmd);
765
+ }
766
+ return false;
767
+ }
768
+ var TEST_CMD_RE = /run_tests|\b(?:npm|pnpm|yarn|bun)\s+(?:run\s+)?test|node\s+--test|vitest|jest|mocha|pytest|cargo\s+test|go\s+test/i;
769
+ var TYPECHECK_CMD_RE = /\btsc\b|typecheck/i;
770
+ var LINT_BUILD_CMD_RE = /eslint|biome\s+(?:check|lint)|\blint\b|\bbuild\b/i;
771
+ function verificationKind(e) {
772
+ const declared = e.meta?.kind;
773
+ if (declared === "test" || declared === "typecheck") return declared;
774
+ if (declared === "lint" || declared === "build") return "lint-build";
775
+ const cmd = e.command ?? "";
776
+ if (TEST_CMD_RE.test(cmd)) return "test";
777
+ if (TYPECHECK_CMD_RE.test(cmd)) return "typecheck";
778
+ if (LINT_BUILD_CMD_RE.test(cmd)) return "lint-build";
779
+ return "unknown";
780
+ }
781
+ function backsTestClaim(e) {
782
+ const kind = verificationKind(e);
783
+ return kind === "test" || kind === "unknown";
784
+ }
785
+ function backsTypecheckClaim(e) {
786
+ const kind = verificationKind(e);
787
+ return kind === "typecheck" || kind === "unknown";
788
+ }
789
+ function freshPassedVerifications(events) {
790
+ let lastMutationAt = 0;
791
+ for (const e of events) {
792
+ if (isCodeMutation(e) && e.timestamp > lastMutationAt) lastMutationAt = e.timestamp;
793
+ }
794
+ return events.filter(
795
+ (e) => e.type === "verification" && e.status === "passed" && e.timestamp >= lastMutationAt
796
+ );
797
+ }
798
+ function auditDeliveryClaims(input) {
799
+ const claimsTests = TEST_CLAIM_RE.test(input.claimText);
800
+ const claimsTypecheck = TYPECHECK_CLAIM_RE.test(input.claimText);
801
+ if (!claimsTests && !claimsTypecheck) {
802
+ return { status: "ok", lines: [] };
803
+ }
804
+ const hasWrites = input.events.some(isCodeMutation);
805
+ const fresh = freshPassedVerifications(input.events);
806
+ if (hasWrites) {
807
+ if (claimsTests && !fresh.some(backsTestClaim)) {
808
+ return {
809
+ status: "block",
810
+ lines: [
811
+ "\u274C \u5BA3\u79F0\u5BF9\u8D26\u5931\u8D25\uFF1A\u4EA4\u4ED8\u6587\u672C\u5BA3\u79F0\u6D4B\u8BD5\u5DF2\u901A\u8FC7\uFF0C\u4F46\u6700\u540E\u4E00\u6B21\u4EE3\u7801\u53D8\u66F4\u4E4B\u540E\u6CA1\u6709\u4EFB\u4F55\u6D4B\u8BD5\u5F62\u72B6\u7684 passed \u9A8C\u8BC1\u8BB0\u5F55\u3002",
812
+ fresh.length > 0 ? " \u73B0\u6709\u65B0\u9C9C\u8BB0\u5F55\u53EA\u6709 typecheck/lint \u5F62\u72B6\u2014\u2014typecheck \u5E72\u51C0\u8BC1\u660E\u4E0D\u4E86\u6D4B\u8BD5\u901A\u8FC7\u3002\u5148 run_tests\uFF08\u6216\u6D4B\u8BD5\u5F62\u72B6\u7684 bash\uFF09\u590D\u73B0\u7ED3\u8BBA\uFF0C\u518D\u4EA4\u4ED8\u3002" : ' \u6539\u5B8C\u4EE3\u7801\u6CA1\u91CD\u8DD1\u7684"\u5168\u7EFF"\u662F\u65E7\u7EFF\u3002\u5148 run_tests\uFF08\u6216\u9A8C\u8BC1\u5F62\u72B6\u7684 bash\uFF09\u590D\u73B0\u7ED3\u8BBA\uFF0C\u518D\u4EA4\u4ED8\u3002'
813
+ ]
814
+ };
815
+ }
816
+ if (claimsTypecheck && !fresh.some(backsTypecheckClaim)) {
817
+ return {
818
+ status: "block",
819
+ lines: [
820
+ "\u274C \u5BA3\u79F0\u5BF9\u8D26\u5931\u8D25\uFF1A\u4EA4\u4ED8\u6587\u672C\u5BA3\u79F0 typecheck \u5E72\u51C0\uFF0C\u4F46\u6700\u540E\u4E00\u6B21\u4EE3\u7801\u53D8\u66F4\u4E4B\u540E\u6CA1\u6709\u4EFB\u4F55 typecheck \u5F62\u72B6\u7684 passed \u9A8C\u8BC1\u8BB0\u5F55\u3002",
821
+ " \u6D4B\u8BD5\u901A\u8FC7\u8BC1\u660E\u4E0D\u4E86\u7C7B\u578B\u5E72\u51C0\u3002\u5148\u8DD1 tsc --noEmit\uFF08\u6216\u58F0\u660E\u7684 typecheck \u547D\u4EE4\uFF09\u590D\u73B0\u7ED3\u8BBA\uFF0C\u518D\u4EA4\u4ED8\u3002"
822
+ ]
823
+ };
824
+ }
825
+ }
826
+ const countMatch = COUNT_CLAIM_RE.exec(input.claimText);
827
+ if (countMatch) {
828
+ const claimedPassed = Number(countMatch[1]);
829
+ const latestWithTotals = [...input.events].reverse().find(
830
+ (e) => e.type === "verification" && typeof e.meta?.passed === "number"
831
+ );
832
+ const actualPassed = latestWithTotals?.meta?.passed;
833
+ if (actualPassed !== void 0 && actualPassed !== claimedPassed) {
834
+ return {
835
+ status: "warn",
836
+ lines: [
837
+ `\u26A0\uFE0F \u5BA3\u79F0\u8BA1\u6570\u5BF9\u4E0D\u4E0A\uFF1A\u4EA4\u4ED8\u6587\u672C\u5199 ${claimedPassed} \u901A\u8FC7\uFF0Cledger \u6700\u65B0\u9A8C\u8BC1\u8BB0\u5F55\u662F ${actualPassed} \u901A\u8FC7\u3002`,
838
+ " \u62A5\u544A\u91CC\u7684\u6BCF\u4E2A\u6570\u5B57\u8981\u80FD\u6307\u5230\u4E00\u6761\u771F\u5B9E\u9A8C\u8BC1\u8BB0\u5F55\u2014\u2014\u8BF7\u6838\u5BF9\u540E\u4FEE\u6B63\u5BA3\u79F0\u3002"
839
+ ]
840
+ };
841
+ }
842
+ }
843
+ return { status: "ok", lines: [] };
844
+ }
845
+
846
+ // src/agent/structure-gate.ts
847
+ init_esm_shims();
848
+ import { readFileSync as readFileSync2 } from "fs";
849
+ import { isAbsolute as isAbsolute2, join as join2, relative as relative2, sep } from "path";
850
+
851
+ // scripts/source-budgets.manifest.json
852
+ var source_budgets_manifest_default = {
853
+ $schema: "\u884C\u6570\u68D8\u8F6E\u8D26\u672C\u2014\u2014ratchet \u8BED\u4E49\uFF1A\u8868\u5185\u70B9\u540D\u5DE8\u77F3 ceiling \u53EA\u964D\u4E0D\u5347\uFF08\u5B58\u91CF\u4E0D\u8FFD\u6EAF\uFF09\uFF0C\u8868\u5916\u4EA7\u54C1\u6E90\u6587\u4EF6\u8D85 redline \u5373\u547D\u4E2D\uFF08\u7EA2\u7EBF\u7531 architecture-guards \u786C\u95E8\u6267\u6CD5\uFF09\u3002\u589E\u957F\u5FC5\u987B\u5728\u540C\u4E00 PR \u4FEE\u6539\u672C\u6587\u4EF6\u5E76\u8BF4\u660E\u7406\u7531\u2014\u2014\u7EAF\u6570\u636E diff \u5373\u8BC4\u5BA1\u5BF9\u8C61\uFF08\u79FB\u690D\u81EA oh-my-tianshu \u7684 budgets \u7EAA\u5F8B\uFF1B\u7ED3\u6784\u5316\u6D88\u8D39\u65B9\uFF1Asrc/agent/structure-gate.ts\uFF09\u3002",
854
+ redline: 800,
855
+ budgets: {
856
+ "src/tui/engine/app.ts": {
857
+ ceiling: 7848,
858
+ note: "7500\u21927520\uFF1A\u6362\u884C\u6A21\u5F0F\u5BF9\u9F50\uFF08footer \u884C + shift \u8DEF\u7531 +23 \u884C\uFF0C2026-08-25\uFF09 7520\u21927526\uFF1A\u534F\u540C\u5EFA\u8BAE\u884C\u63A5\u7EBF\uFF08onChange \u8BC4\u4F30 + \u6E32\u67D3\u69FD + Tab \u91C7\u7EB3 + Esc \u5173\u95ED\uFF1B\u68C0\u6D4B/\u6E32\u67D3\u4E3B\u4F53\u5728 engine/orchestration-hint.ts \u65B0\u6A21\u5757\uFF09\u3002 7526\u21927553\uFF1A\u9519\u8BEF\u65F6\u523B\u4E09\u4EF6\u5957\uFF08handleError \u5206\u7C7B\u6307\u5F15\u884C + \u4E0A\u4E00\u6761\u56DE\u586B\u8F93\u5165\u6846 + lastSubmittedText \u751F\u547D\u5468\u671F\uFF0C+27\uFF09\u3002 7553\u21927558\uFF1Aonboarding \u54E8\u5175\u63A5\u7EBF\uFF08\u5411\u5BFC\u7EAF\u53D6\u6D88\u540E\u65B0\u4F1A\u8BDD\u4E0D\u518D\u81EA\u52A8\u5F39 /connect\uFF0C+5\uFF09\u3002 7558\u21927637\uFF1Achoice-panel \u8F93\u5165\u5B50\u6A21\u5F0F\u8865\u5149\u6807\u7F16\u8F91\u4E0E\u7C98\u8D34\uFF08choicePanelInputCursor/\u6309\u952E\u5149\u6807\u8BED\u4E49\u5168\u5957/onPaste \u8DEF\u7531\u5206\u652F/caret \u63A5\u7EBF +73\uFF09+ \u8BA1\u5212\u5BA1\u6279\u5361 separator \u4F20\u53C2\uFF08+6\uFF09\u2014\u2014\u8F93\u5165\u5B50\u6A21\u5F0F\u72B6\u6001\u4E0E app.ts \u73B0\u6709 choicePanel* \u5B57\u6BB5\u65CF\u5F3A\u8026\u5408\uFF0C\u62C6\u5206\u6536\u76CA\u8D1F\uFF1B\u8D26\u672C\u968F\u503A\u5BF9\u9F50\u3002 7637\u21927675\uFF1A/model \u9762\u677F\u5BF9\u6807 CC\uFF1Aeffort draft \u72B6\u6001 + </> \u6B65\u8FDB\u6309\u952E + Enter/s \u8BED\u4E49\u7FFB\u8F6C + \u6CE8\u518C\u5904 effort \u6570\u636E\u7EC4\u88C5\uFF08+38\uFF09\u3002 7675\u21927682\uFF1Arouting nudge \u63A5\u7EBF\uFF08routingConfiguredProvider \u5B57\u6BB5/setter + onChange ctx + Tab \u91C7\u7EB3 /config \u7279\u4F8B\uFF0C+7\uFF09\u3002 7682\u21927692\uFF1Akitty \u952E\u76D8\u80FD\u529B\u63A2\u6D4B\u63A5\u7EBF\uFF08\x1B[?u \u67E5\u8BE2\u5199\u5165 + onKittyFlags \u6CE8\u518C + kittyKeyboard \u65D7 + footer \u900F\u4F20 +10\uFF0CCSI-u \u5168\u91CF\u89E3\u7801\u7684\u914D\u5957\u2014\u2014baf58d3b2 P1 \u5BA1\u67E5\u4FEE\u590D\uFF09 7692\u21927754\uFF1ATermux \u624B\u673A\u7AEF\u9002\u914D\u56DE\u6D41\uFF08alpha 42f550c8c\uFF1ACtrl+S/Ctrl+Q \u63A5\u7BA1 XOFF/XON \u8F93\u51FA\u51BB\u7ED3 + \u89E3\u51BB\u6309\u5E8F\u8865\u653E\uFF1B\u77EE\u5C4F rows<14 \u9690\u85CF\u72B6\u6001\u884C / rows<24 \u9AD8\u6C34\u4F4D\u534A\u5C4F\uFF0C+62\uFF0C2026-09-12 Mobile Remote \u56DE\u6D41\uFF09 7754\u21927765\uFF1A\u51BB\u7ED3\u671F commit \u5165\u961F\u6B7B\u9501\u4FEE\u590D\uFF08requestPump \u5B88\u536B\u8865 outputFrozen\u2014\u2014settle \u65E0\u6761\u4EF6\u7EED\u8C03\u672C\u51FD\u6570\u3001\u800C drain \u5728\u51BB\u7ED3\u65F6 return \u4E14\u4E0D shift \u961F\u5217\uFF0C\u4E24\u5934\u4E0D\u5BF9\u79F0\u6784\u6210\u5FAE\u4EFB\u52A1\u81EA\u65CB\u3001\u4E8B\u4EF6\u5FAA\u73AF\u88AB\u997F\u6B7B\uFF1ACtrl+S \u51BB\u7ED3\u540E Ctrl+Q/Ctrl+C \u5168\u5931\u6548\u53EA\u80FD\u6740\u8FDB\u7A0B\uFF1B+11\uFF0C2026-09-12 P0 \u4FEE\u590D\uFF0C\u542B\u56DE\u5F52\u7528\u4F8B\u4E0E pty \u7AEF\u5230\u7AEF\u9A8C\u6536\uFF09 7765\u21927775\uFF1Azen \u56DE\u6D41 Wave 5\uFF082026-09-13\uFF09\u2014\u2014zenBadgeProvider \u5B57\u6BB5 + setZenBadgeProvider\uFF08\u4EFF setAgentRunningProbe \u5F62\u5236\uFF09+ formatGlanceLeft \u5B9E\u53C2\u4F20 zenBadge\uFF08+10\uFF09\u3002 7775\u21927793\uFF1Azen_unlock \u7ED3\u679C\u6539\u8D70\u9650\u65F6\u63D0\u793A\uFF082026-09-13 \u7528\u6237\u62A5\u544A\u300C\u7985\u89E3\u9664\u63D0\u793A\u4E00\u76F4\u6302\u5728\u63A8\u7406\u533A\u4E0B\u9762\u300D\uFF09\u2014\u2014handleToolResult \u6700\u524D\u9762\u62E6\u622A\u8BE5\u865A\u62DF\u5DE5\u5177\uFF08\u6B64\u524D\u6309\u666E\u901A\u7ED3\u679C\u6E32\u67D3\uFF1A\u5B83\u6CA1\u6709\u7B2C\u4E8C\u6B21\u56DE\u8C03\uFF0Clive \u5DE5\u5177\u5361\u6C38\u8FDC\u7B49\u4E0D\u5230\u7EC8\u6001 \u2192 \u6C38\u4E45\u60AC\u505C\uFF09\uFF0C\u6539\u4E3A glance bar \u4E0A\u7684 TTL \u63D0\u793A\uFF08ZEN_UNLOCK_NOTICE_MS=6s \u5230\u70B9\u81EA\u52A8\u6D88\u5931\uFF09\uFF0C\u4E0D\u843D scrollback \u4E5F\u4E0D\u5360 live \u5361\uFF08+18\uFF09\u3002 7793\u21927834\uFF1ATUI \u63A8\u7406\u533A/\u5DE5\u5177\u5361\u4F18\u5316\u63A5\u7EBF\uFF082026-09-19\uFF09\u2014\u2014thinking \u56DE\u770B\u4ED3\u5B57\u6BB5\u4E0E ctrl+t \u91CD\u5370\u5206\u652F\u3001commitThinkingToScrollback \u7559\u5B58 + reviewHint\u3001analyzing \u76F8\u4F4D spinner \u5982\u5B9E\u5316\uFF08activityLabel\uFF09\u3001\u65B0 run \u6E05\u56DE\u770B\u4ED3\uFF08+41\uFF0C\u5168\u4E3A\u63A5\u7EBF\uFF09\uFF1B\u673A\u5236\u4E3B\u4F53\u6CBF\u63A5\u7F1D\u62C6\u5728 engine/thinking-review.ts \u65B0\u6A21\u5757\uFF0Cspinner \u6587\u6848\u5728 format/spinner-status.ts\uFF0C\u5DE5\u5177\u6807\u9898\u515C\u5E95\u5728 tool-label.ts/tool-card.ts\u3002 7834\u21927842\uFF1Azen_unlock \u60AC\u505C\u5361\u6839\u4FEE\uFF082026-09-19\uFF09\u2014\u2014handleToolUse \u5BF9\u865A\u62DF\u5DE5\u5177\u4E0D\u5EFA pending\uFF08+4\uFF09+ \u7ED3\u679C\u62E6\u622A\u9632\u5FA1\u6027 deletePending\uFF08+4\uFF09\uFF0C\u65E0\u673A\u5236\u53EF\u62C6\uFF08\u5B88\u536B\u5FC5\u987B\u8D34\u7740 setPending/\u62E6\u622A\u70B9\uFF09\u3002 7842\u21927845\uFF1A\u524D\u5E8F\u6539\u52A8\u8865\u8BB0\uFF08\u5B9E\u6D4B\u884C\u6570\uFF0C\u8D26\u672C\u5F53\u65F6\u672A\u540C\u6B65\uFF09\u3002 7845\u21927848\uFF1A\u5BA1\u67E5\u4FEE\u590D\uFF082026-09-19\uFF09\u2014\u2014ctrl+t \u56DE\u770B\u8865 isAgentActive \u5B88\u536B\uFF08+2 \u6CE8\u91CA\uFF0C\u5951\u7EA6\u9489\u5728 engine/__tests__/thinking-review-hotkey.test.ts\uFF09+ spinner \u4F20 columns \u505A\u6807\u7B7E\u5BBD\u5EA6\u9002\u914D\uFF08+3\uFF1A\u6CE8\u91CA 2 + \u53C2\u6570 1\uFF09\uFF0C\u51C0 +3\uFF0C\u5B9E\u6D4B 7848\u3002"
859
+ },
860
+ "src/server/session-manager.ts": {
861
+ ceiling: 6243,
862
+ note: "session-manager \u589E\u957F\uFF1A\u8DE8\u76D8\u5BA1\u6279\u5E7F\u64AD/applyGlobalApprovalMode\uFF0893956f5e2+970b05196 \u56DE\u6D41 2026-09-07\uFF09\u2014\u2014ceiling 5800\u21925830\uFF08\u65F6\u70B9\u5B9E\u9645 5820\uFF0C10 \u884C\u7F13\u51B2\uFF09\u30025820\u21925942 \u7684 +122 \u4E3A 2026-09-07~09-09 \u95F4 11 \u7B14\u63D0\u4EA4\u7D2F\u8BA1\u6B20\u8D26\uFF0838b246ba6 stall-observer \u63A5\u7EBF / f512c450a+b738faffc images\xB7continuationReason \u900F\u4F20 / a93b36f13 handoff \u5BB9\u5DEE / 029421c36 \u524D\u7684 settlement \u6F14\u8FDB\u7B49\uFF0C\u5747\u672A\u540C\u6B65\u8D26\u672C\u2014\u2014f35d922ea \u66FE\u8BEF\u8BB0\u6574\u6BB5\u4E3A 029421c36 \u7684 +9\uFF0C\u5B9E\u4E3A +8/-1 \u51C0\u589E 7\uFF0C\u6B64\u6761\u66F4\u6B63\uFF09\u3002ceiling \u5BF9\u9F50\u5B9E\u9645 5949\uFF08\u96F6\u7F13\u51B2\uFF0C+1 \u5373\u89E6\u8B66\uFF09\u30025949\u21925956\uFF1Aissue #63 \u6B8B\u7559\uFF08\u7F16\u8F91\u91CD\u53D1\u5BF9\u542B hook \u6CE8\u5165\u7684\u4F1A\u8BDD\u5931\u6548\uFF09\u2014\u2014\u914D\u5BF9\u7EAF\u51FD\u6570\uFF08buildUserAnchors/stripInjectedSuffix\uFF09\u5DF2\u6CBF\u63A5\u7F1D\u62C6\u5230\u65B0\u6A21\u5757 src/server/rewind-anchors.ts\uFF0C\u6B64\u5904\u53EA\u5269 import + userEventListFor\uFF08\u73AF\u88AB\u622A\u5C3E\u65F6\u8BFB\u78C1\u76D8\u5168\u91CF\u4E8B\u4EF6\uFF0Crewind \u7684 anchorSeq \u624D\u4E0D\u4E22\uFF09\u30025956\u21925958\uFF1A\u5408\u5E76\u4E24\u4E2A\u6765\u6E90\u7684\u6280\u80FD\u52A0\u8F7D\u9519\u8BEF\uFF08import + getSkillLoadErrors \u5408\u5E76\u5171 +2\uFF09\u2014\u2014\u8BB0\u5F55\u4FA7\u5728\u65B0\u6A21\u5757 src/server/skill-load-errors.ts\uFF0C\u6B64\u5904\u53EA\u5269\u8BFB\u5408\u5E76\u3002 5958\u21925992\uFF1Aqueue lane \u6536\u5C3E flush\uFF08797b38def\uFF0C2026-09-11 \u65B9\u6848 A\uFF09\u2014\u2014run \u6536\u5C3E\u540E\u4E3B\u52A8\u5F00\u65B0\u4E00\u8F6E\u6D88\u8D39\u6392\u961F\u6D88\u606F\uFF08scheduleQueueLaneFlush + abort \u5347\u4EE3\u65E9\u9000\u5206\u652F\u8865\u8C03\u5EA6\uFF09\u3002flush \u4E0E mergeQueuedIntoPrompt/run/\u4F1A\u8BDD\u79C1\u6709\u72B6\u6001\uFF08sessions map\u3001queueLane\u3001durability \u5224\u5B9A\u5747\u4E3A\u79C1\u6709\uFF09\u5F3A\u8026\u5408\uFF0C\u62C6\u51FA\u9700\u5927\u91CF\u56DE\u8C03\u6CE8\u5165\u3001\u6536\u76CA\u8D1F\uFF1B\u8D26\u672C\u968F\u503A\u5BF9\u9F50\uFF08\u96F6\u7F13\u51B2\uFF09\u3002 5992\u21926019\uFF1AStop\u2192settle \u7A97\u53E3\uFF08waitForRunSettled\uFF0C+27\uFF0C2026-09-13\uFF09\u2014\u2014abort() \u540C\u6B65\u7FFB status \u4E3A aborted \u800C `running` \u8981\u7B49 agent loop \u6536\u5C3E\u624D\u843D\uFF0C\u684C\u9762\u7AEF\u7F16\u8F91\u91CD\u53D1 abort\u2192\u8F6E\u8BE2\u5230\u975E running\u2192/rewind \u649E running \u5B88\u536B 409\u300C\u4FDD\u5B58\u5931\u8D25\u300D\uFF1B\u65B9\u6CD5\u8BFB activeRunSettlement.promise\uFF08\u79C1\u6709\uFF09\u6545\u7559\u5728\u672C\u6587\u4EF6\uFF0C\u8DEF\u7531\u4FA7 /rewind\u3001/prompt \u8C03\u7528\u3002 6019\u21926052\uFF1A\u684C\u9762\u6027\u80FD\u9636\u6BB5 4 \u5168\u5C40\u63A8\u9001\u901A\u9053\uFF08+33\uFF0C2026-09-13\uFF09\u2014\u2014RuntimeSessionManagerOptions.onSessionsChanged \u9009\u9879 + notifySessionsChanged \u94A9\u5B50\uFF0C\u56DB\u4E2A\u89E6\u53D1\u70B9\uFF08persistRecord \u5148\u4E8E\u843D\u76D8 / touch / hardDelete / recountApprovals \u8BA1\u6570\u53D8\u5316\uFF09\u3002\u56DE\u8C03\u53EA\u8868\u8FBE\u300C\u4F1A\u8BDD\u5217\u8868\u8BE5\u91CD\u53D6\u4E86\u300D\u3001\u4E0D\u5E26\u8BB0\u5F55\u672C\u4F53\uFF0C\u5408\u5E76\u4E0E\u63A8\u9001\u5728 server-event-bus.ts / server-events-route.ts\uFF08\u65B0\u6A21\u5757\uFF09\uFF1B\u89E6\u53D1\u70B9\u8BFB\u4F1A\u8BDD\u79C1\u6709\u72B6\u6001\u6545\u7559\u5728\u672C\u6587\u4EF6\u3002\u96F6\u7F13\u51B2\u3002 6052\u21926056\uFF1A\u751F\u56FE\u69FD\u914D\u7F6E\u7684\u5B58\u6D3B agent \u5DE5\u5177\u8868\u5237\u65B0\uFF08issue #8\uFF09\u2014\u2014import + \u5355\u884C\u8F6C\u53D1\u65B9\u6CD5 refreshAgentTools\uFF1B\u904D\u5386\u903B\u8F91\u5916\u79FB\u5230 src/server/agent-tool-refresh.ts \u65B0\u6A21\u5757\uFF0C\u672C\u6587\u4EF6\u53EA\u7559\u8F6C\u53D1\u6B63\u662F\u4E3A\u5B88\u4F4F\u884C\u6570\u68D8\u8F6E\uFF08\u672C\u5730\u7EBF\u539F\u8BB0 5992\u21925996\uFF0Cmerge origin/main \u65F6\u56E0\u8FDC\u7AEF\u4E24\u6BB5\u5148\u843D\u800C\u987A\u5EF6\u81F3\u94FE\u5C3E\uFF09\u3002 6056\u21926064\uFF1A\u6253\u65AD\u540E lane flush \u5F52\u5E76\u5934\uFF08\u4EFB\u52A1 5/7\uFF0C2026-09-13\uFF09\u2014\u2014QUEUE_LANE_MERGE_HEADER_AFTER_ABORT \u5E38\u91CF\uFF08\u6CE8\u91CA 2 \u884C + \u5E38\u91CF 1 \u884C\uFF09+ mergeQueuedIntoPrompt \u5185 header \u6309 session.record.status === 'aborted' \u9009\u62E9\uFF08\u6CE8\u91CA 3 \u884C + \u5224\u5B9A + \u66FF\u6362\uFF0C\u51C0 +8\uFF09\u3002\u89C4\u5219\u5185\u805A\u5728\u5F52\u5E76\u51FD\u6570\u5185\uFF1Arun:2282 \u4E0E scheduleQueueLaneFlush \u4E24\u5904\u8C03\u7528\u70B9\u90FD\u5728 status \u7FFB running \u524D\u5F52\u5E76\uFF0C\u65E0\u9700\u6539\u52A8\u3002 6064\u21926074\uFF1Azen \u56DE\u6D41 Wave 5\uFF082026-09-13\uFF09\u2014\u2014buildCallbacks \u88C5\u914D onZenPhaseChange \u2192 append('zen_phase', { phase, reason?, armed, zenTurns })\uFF0C\u4E0E onPhaseChange \u540C\u65CF\uFF1Bworker/\u5B50\u4EE3\u7406\u4E0D\u63A5\uFF08loop \u4FA7 isTopLevel=false \u4E0D arm\uFF0C+10\uFF09\u3002 6074\u21926079\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014\u9879\u76EE\u9ED8\u8BA4\u6A21\u578B\u53D6 keys \u6C60\u6D3E\u751F\uFF08contractModels \u53D6\u4EE3\u9876\u5C42 models \u76F4\u8BFB\uFF09\uFF08+5\uFF09\u3002 6079\u21926080\uFF1Azen_phase \u534F\u8BAE\u6CE8\u91CA\u66F4\u6B63\uFF082026-09-13 \u5BA1\u67E5\u53F0\u8D26 #6\uFF09\u2014\u2014\u6D88\u8D39\u7AEF\u5F85\u5B9E\u73B0\uFF08\u684C\u9762\u7AEF\u672A\u8BA2\u9605\u8BE5\u4E8B\u4EF6\u3001TUI \u5FBD\u7AE0\u8D70 zenBadgeProvider\uFF09\uFF0C\u6CE8\u91CA\u4E0D\u518D\u7528\u73B0\u5728\u65F6\u63CF\u8FF0\uFF08+1\uFF09\u3002 6080\u21926094\uFF1APR \u5408\u5165\u6536\u53E3\uFF08\u591A key current \u5224\u5B9A\u7CBE\u786E\u5230 key\uFF09\u2014\u2014ModelOption \u8865 keyId/keyLabel \u58F0\u660E + listModels \u4E09\u6BB5\u5F0F\u7CBE\u786E\u5339\u914D\u4E0E first-wins \u515C\u5E95\uFF08\u53CC key \u6302\u540C wire id \u65F6\u4E24\u6761\u90FD\u6807 current\uFF0C\u684C\u9762\u7AEF\u5BF9 current \u65E9\u9000 \u2192 \u4E24\u6761\u90FD\u70B9\u4E0D\u52A8\uFF09\uFF08+14\uFF09 6094\u21926113\uFF1A\u684C\u9762\u7AEF\u7985\u76F8\u4F4D\u63A5\u5165\uFF082026-09-14\uFF09\u2014\u2014onZenPhaseChange \u7684\u8F7D\u8377\u6539\u4E0E SessionRecord \u955C\u50CF\u5171\u7528\u540C\u4E00\u5F62\u72B6 ZenPhaseMirror\uFF08armed/zenTurns \u6052\u5E26\uFF0C\u6D88\u8D39\u7AEF\u4E0D\u5FC5\u518D\u5206\u8FA8\u300C\u5B57\u6BB5\u7F3A\u5931\u300D\u4E0E\u300C\u503C\u4E3A false/0\u300D\u4E24\u79CD\u7F3A\u5931\u8BED\u4E49\uFF09+ \u540C\u6B65\u5199 record.zenPhaseMirror \u5E76 persistRecord\uFF08+9\uFF09\uFF1B\u65B0\u589E getZenPhaseMirror(id) \u8BFB\u53D6\u5668\uFF08+10\uFF09\u2014\u2014\u5B83\u662F /stream \u5EFA\u8FDE\u8865\u53D1\u7684\u552F\u4E00\u6765\u6E90\uFF08zen_phase \u662F\u8FB9\u6CBF\u4E8B\u4EF6\uFF0C\u6ED1\u51FA\u56DE\u653E\u7A97\u53E3\u540E\u53EA\u80FD\u9760 record\uFF09\uFF0C\u8BFB\u79C1\u6709 sessions map \u6545\u7559\u5728\u672C\u6587\u4EF6\u3002 6113\u21926144\uFF1Azen \u8DF3\u8FC7\u76F8\u4F4D\uFF08/zen skip / POST /sessions/:id/zen\uFF0C2026-09-14\uFF09\u2014\u2014ManagedAgent \u52A0\u4E24\u4E2A optional \u9762\uFF08promoteZen? / zenController? \u5404\u5E26\u6CE8\u91CA\uFF0C+15\uFF09\u4E0E skipZen(id)\uFF08+16\uFF0C\u542B\u6CE8\u91CA\uFF09\uFF1A\u8BFB\u79C1\u6709 sessions map \u5E76\u7ECF ensureAgentAsync \u53D6\u540C\u4E00\u5B9E\u4F8B\uFF0C\u6545\u7559\u5728\u672C\u6587\u4EF6\uFF1B\u8DEF\u7531\u4FA7\u5728 session-routes.ts \u53EA\u505A\u53C2\u6570\u6821\u9A8C\u4E0E\u72B6\u6001\u7801\u3002optional \u53E3\u5F84\u8BA9\u8F7B\u91CF\u6D4B\u8BD5\u66FF\u8EAB\u65E0\u9700\u5B9E\u73B0 zen \u9762\uFF08\u7F3A\u7701\u6309\u672A\u664B\u5347\u5904\u7406\uFF0C\u4E0D\u662F\u9519\u8BEF\uFF09\u3002 6144\u21926155\uFF1Aissue #147 \u5DE5\u4F5C\u533A\u7B56\u7565\uFF08+12/-1 \u5B9E\u6D4B 6155\uFF09\u2014\u2014\u5224\u5B9A/\u914D\u7F6E\u8BFB\u53D6/\u9694\u79BB\u76EE\u5F55\u521B\u5EFA\u4E0E\u5931\u8D25\u56DE\u9000\u7684\u4E3B\u4F53\u5168\u90E8\u843D\u5728 src/server/workspace.ts \u7684 resolveSessionWorkspaceForSession\uFF08\u65B0\u51FD\u6570\uFF0C\u4F9D\u8D56\u6CE8\u5165 readConfig/rivetHome\uFF09\uFF0C\u672C\u6587\u4EF6\u53EA\u7559\uFF1Aimport +1\u3001CreateSessionInput.workspaceMode \u58F0\u660E +1\u3001createSession \u5185 3 \u884C\u8C03\u7528\uFF08\u4F20 this.defaultCwd/id/loadConfig\uFF09\u3001worktree \u5206\u652F\u4FDD\u6301 source='explicit' \u7684 2 \u884C\u3001record \u7684 workspaceSource \u4E00\u884C\uFF08\u660E\u793A cwd \u6765\u6E90\uFF0C\u684C\u9762\u7AEF\u6D88\u8D39\uFF09\u3002 6155\u21926159\uFF1A\u4F1A\u8BDD\u91CA\u653E\u94FE\u6536\u5272\u4E94\u5F20\u4F1A\u8BDD\u952E\u63A7 module store\uFF08PR #156\uFF0C2026-09-15\uFF09\u2014\u2014waveResults/waveGates/plan-store/post-commit-pending/skill-gate \u4E94\u8868 clear \u5916\u79FB\u5230 src/agent/session-module-store-reaper.ts \u65B0\u6A21\u5757\uFF08\u5747\u4E3A\u6A21\u5757\u7EA7\u81EA\u7531\u51FD\u6570\uFF0C\u65E0\u79C1\u6709\u72B6\u6001\uFF09\uFF0C\u672C\u6587\u4EF6\u53EA\u5269 import + forgetStores \u5185\u4E00\u884C\u8C03\u7528\uFF08+4 \u542B 2 \u884C\u6CE8\u91CA\uFF09\uFF0C\u6B63\u662F\u4E3A\u5B88\u4F4F\u884C\u6570\u68D8\u8F6E\uFF1B\u6536\u5272\u70B9\u9009 forgetStores \u56E0 releaseAgent \u4E0E hardDelete \u53CC\u94FE\u90FD\u6C47\u4E8E\u6B64\u3002 6159\u21926160\uFF1A\u4F1A\u8BDD module store \u6536\u5272\u5206\u7EA7\uFF082026-09-16 \u5BA1\u67E5\u4FEE\u590D\uFF0C#156 \u5BA1\u67E5\u8DDF\u8FDB\uFF09\u2014\u2014forgetStores \u52A0 terminal \u53C2\u6570\uFF1A\u6302\u8D77\u7EA7\uFF08releaseAgent\uFF1A\u7A7A\u95F2\u56DE\u6536/\u5F52\u6863\uFF09\u6E05 wave \u7ED3\u679C\u6865/plan/\u5F85\u5BA1\u96C6\uFF0C\u7EC8\u7ED3\u7EA7\uFF08hardDelete\uFF09\u4E94\u5F20\u5168\u6E05\uFF1B\u95E8\u7981\u7C7B\uFF08wave-gate/skill-gate\uFF09\u6539\u4E3A\u4EC5\u7EC8\u7ED3\u7EA7\u6E05\u7406\u2014\u2014plan-executor \u7684\u8DE8\u6CE2\u95E8\u7981\u5224\u5B9A\u662F fail-open\uFF08\u8BB0\u5F55\u7F3A\u5931\u5373\u653E\u884C\uFF09\uFF0C\u6302\u8D77\u7EA7\u6E05\u6389\u4F1A\u8BA9\u95F2\u7F6E\u6216\u5F52\u6863\u6062\u590D\u7684\u4F1A\u8BDD\u7ED5\u8FC7\u300C\u4E0A\u4E00\u6CE2\u672A\u8FC7\u300D\u62E6\u622A\uFF08\u5BA1\u67E5\u63A2\u9488\u590D\u73B0\uFF09\u3002\u672C\u6587\u4EF6 +1 \u884C\uFF08forgetStores \u6CE8\u91CA 2\u21923 \u884C\uFF09\uFF0C\u5206\u6D41\u903B\u8F91\u4E3B\u4F53\u5728 src/agent/session-module-store-reaper.ts\u3002 6160\u21926165\uFF1A\u786C\u5220\u94FE\u8865\u843D\u76D8\u6587\u4EF6\u6E05\u7406\uFF082026-09-16 \u5BA1\u67E5\u4FEE\u590D\uFF09\u2014\u2014import deleteSessionFiles + hardDelete \u5185 3 \u884C\u8C03\u7528\uFF08\u542B 2 \u884C\u6CE8\u91CA\uFF09\uFF1B\u6E05\u7406\u9762\u4E0E\u5217\u8868\u7F13\u5B58\u6761\u76EE\u79FB\u9664\u4E3B\u4F53\u5728 src/agent/session-persist.ts\uFF08deleteSessionFiles/removeSessionFilesIn/removeListCacheEntry\uFF09\u3002 6165\u21926173\uFF1A\u6D41\u9519\u8BEF\u7EC8\u6001\u4FEE\u590D\uFF082026-09-16\uFF09\u2014\u2014buildCallbacks \u65B0\u589E onStreamInterrupted \u5B9E\u73B0\uFF08+8 \u542B 4 \u884C\u6CE8\u91CA\uFF09\uFF1A\u975E AbortError \u7684\u6D41\u9519\u8BEF\u7EC8\u7ED3\u672C run \u65F6\u628A status \u7F6E 'interrupted'\uFF08\u6B64\u524D run \u7167\u5E38 resolve\u3001\u7EC8\u6001\u88AB\u8BEF\u6807 completed\uFF1B\u811A\u672C/\u770B\u677F\u628A\u8D85\u65F6\u8BEF\u5224\u6210\u529F\uFF09\uFF1B\u72B6\u6001\u503C\u5B9A\u4E49\u4E0E\u4E24\u7AEF\u6E32\u67D3\u89C1 src/server/protocol.ts \u7684 SessionStatus\u30026173\u21926176\uFF1A\u4F1A\u8BDD\u6807\u9898\u843D\u76D8\u524D\u5265\u7EC8\u7AEF\u8F6C\u4E49\uFF082026-09-17 \u5BA1\u8BA1\uFF09\u2014\u2014\u6807\u9898\u6765\u81EA HTTP body\u3001\u4E0D\u7ECF\u6D88\u606F\u6D88\u6BD2\u94FE\uFF08addUserMessage \u7684 sanitizeForJsonTransport \u53EA\u62A4\u6D88\u606F\u94FE\uFF09\uFF0C\u7ECF /sessions/Chronicle/\u9000\u51FA\u6458\u8981\u4E09\u6761\u8DEF\u5F84\u56DE\u653E\u7EC8\u7AEF\u7684 OSC 52 \u53EF\u9A7B\u7559\uFF1BstripTerminalEscapes \u4E3B\u4F53\u5728 src/utils/safe-path.ts\uFF0C\u672C\u6587\u4EF6 +3\uFF08import + createSession \u4E24\u884C\uFF09\u3002 6176\u21926243\uFF1A\u5916\u90E8\u8FDB\u7A0B\u65B0\u589E\u4F1A\u8BDD\u7684\u53D1\u73B0\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #205\uFF0C2026-09-18\uFF0C+57 \u51C0\uFF09\u2014\u2014\u540C\u4E00\u4E2A home \u88AB\u591A\u4E2A\u8FDB\u7A0B\u540C\u65F6 serve \u65F6\uFF0Crehydrate() \u53EA\u5728\u6784\u9020\u65F6\u8BFB\u76D8\u4E00\u6B21\uFF0C\u5916\u90E8\u8FDB\u7A0B\u4E4B\u540E\u5EFA\u7684\u4F1A\u8BDD\u5217\u8868\u4E0D\u53EF\u89C1\u3001\u6309 id \u53D6 404\uFF0CUI \u53EA\u80FD\u9760\u91CD\u542F\u624D\u770B\u5F97\u5230\u3002\u65B0\u589E externalScanMs \u9009\u9879\uFF08\u9ED8\u8BA4 5000 / env RIVET_EXTERNAL_SCAN_MS / 0=\u5173\u95ED\uFF1Bunref \u5B9A\u65F6\u5668\uFF0C\u4E0E idleSweepTimer \u540C\u8303\u5F0F\uFF09+ adoptExternalSessions()\uFF1A\u53EA\u589E\u4E0D\u8986\u76D6\uFF08\u5F52\u6863/\u5220\u9664\u5F52\u6301\u6709\u5B83\u7684\u8FDB\u7A0B\uFF09\u3001\u5916\u90E8\u65B0\u4F1A\u8BDD\u4E0D\u53EF\u80FD running \u6545\u4E0D\u8D70\u5D29\u6E83\u6536\u5C3E\uFF1B\u901A\u77E5\u590D\u7528\u65E2\u6709 onSessionsChanged\u3002\u88C5\u586B\u4F53\u4E0E\u79C1\u6709 sessions map \u5F3A\u8026\u5408\uFF0C\u8D26\u672C\u968F\u503A\u5BF9\u9F50\uFF1B\u7559 10 \u884C\u7F13\u51B2\uFF08\u5BF9\u9F50 PR \u540C\u6B3E\u5148\u4F8B\uFF1Bdev \u57FA\u7EBF 6176 \u542B #181 \u7684 +3\uFF09\u3002"
863
+ },
864
+ "src/tui/slash-commands.ts": {
865
+ ceiling: 4654,
866
+ note: "4536\u21924620\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09 4620\u21924622\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014allProviders \u900F\u4F20\u5B8C\u6574 ProviderConfig\uFF08\u4FEE\u300Ckeys \u6C60\u5728\u6B64\u6295\u5F71\u4E22\u5931\u300D\u6839\u56E0\uFF09\uFF0C/model \u6E05\u5355\u8D70 contractModels\uFF08+4\uFF09 4622\u21924642\uFF1A/login \u52A0\u5929\u67A2\u8D26\u53F7\u901A\u9053 + \u65B0\u589E /logout\uFF082026-09-14\uFF09\u3002\u5DF2\u6309\u95E8\u7981\u8981\u6C42\u6CBF\u63A5\u7F1D\u62C6\u5206\uFF1Adevice flow \u7F16\u6392\u7684 ~60 \u884C\u5B9E\u73B0\u5916\u7F6E\u5230 src/tui/account-login.ts\uFF0C\u672C\u6587\u4EF6\u53EA\u7559\u4E24\u4E2A register \u58F3\u2014\u2014\u51C0\u589E\u7531 +66 \u964D\u5230 +20\uFF08\u58F3\u672C\u8EAB\u4E5F\u8981\u5360\u884C\uFF09\u3002 4642\u21924651\uFF1A/zen \u663E\u5F0F\u5F00\u5173\u547D\u4EE4\uFF082026-09-14\uFF09\u2014\u2014\u7985\u6A21\u5F0F\u9ED8\u8BA4\u5173\uFF0C\u672C\u6761\u662F\u7528\u6237\u663E\u5F0F\u5F00\u542F\u7684\u552F\u4E00\u547D\u4EE4\u5165\u53E3\uFF08on/off \u5199\u914D\u7F6E\u3001status \u53EA\u8BFB\uFF1B\u65B0\u4F1A\u8BDD\u751F\u6548\uFF09\u3002\u5DF2\u6309\u95E8\u7981\u8981\u6C42\u6CBF\u63A5\u7F1D\u62C6\u5206\uFF1A\u5224\u5B9A\u4E0E\u63A5\u7EBF\u5916\u7F6E\u5230 src/tui/zen-command.ts\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 +1 import\u3001+2 \u884C\u6CE8\u91CA\u4E0E +6 \u884C register \u58F3\uFF08\u58F3\u5916\u7684 ctx \u9002\u914D\u4E5F\u4E00\u5E76\u5916\u7F6E\u6210 runZenSlash\uFF09\u3002 4651\u21924654\uFF1A/status \u52A0\u661F\u7C4D\u6BB5\uFF08\u8D26\u53F7 Task 4.2\uFF0C2026-09-18\uFF09\u2014\u2014\u53D6\u6570/\u964D\u7EA7/\u683C\u5F0F\u5316/\u540E\u53F0\u5237\u65B0\u5168\u5728 src/tui/account-status.ts\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 +1 \u52A8\u6001 import\u3001+2 \u884C\u6CE8\u91CA\u4E0E /status \u7684 lines \u521D\u59CB\u5316\u6539\u5199\uFF08+3\uFF09\u3002"
867
+ },
868
+ "src/agent/coordinator.ts": {
869
+ ceiling: 3540,
870
+ note: "coordinator \u589E\u957F\uFF1A\u7EED\u8DD1\u51BB\u7ED3\u5FEB\u7167\u56DE\u4F20\uFF08e67af8a4f 1e \u56DE\u6D41 2026-09-07\uFF09\u6B20\u8D26\u6536\u53E3\u2014\u2014ceiling 3495\u21923540"
871
+ },
872
+ "src/agent/loop.ts": {
873
+ ceiling: 3126,
874
+ note: "3144\u21923126\uFF1A\u884C\u6570\u5BF9\u9F50\u5B9E\u9645\uFF08-18\uFF09\u3002 2854\u21922928\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09\uFF1B\u5176\u4E2D\u6346\u7ED1\u6280\u80FD\u6D88\u878D\u79FB\u9664 pin \u94FE\u5E76\u6302 <plan-executing> \u5378\u8F7D\u70B9\uFF08\u51C0\u589E\u7EA6 15 \u884C\uFF09\u3002 2928\u21922943\uFF1A\u514D\u5BA1\u6279\u6863\u6743\u9650\u63D0\u793A\u63A5\u7EBF\uFF08def690436 +4\uFF09+ \u6954\u6B7B\u65CF\u540E\u7EED\u4FEE\u590D setApprovalMode \u53CC\u5199\uFF080e792e26e +15/-4\uFF09\u2014\u2014\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\u3002 2943\u21922944\uFF1A\u5DE5\u5177\u7BA1\u7EBF\u7EB5\u6DF1\u4FEE\u590D\u7B2C\u4E00\u6CE2\uFF08job \u957F\u7B49\u5F85\u5FC3\u8DF3\u6CE8\u5165 touchActivity\uFF0C+1\uFF0C2026-09-10\uFF09 2944\u21922952\uFF1Aconfig \u70ED\u8F7D watcher \u7684\u5B9E\u4F8B\u7EA7 teardown `stopConfigWatcher()`\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #127\uFF0C+7 \u51C0\uFF0C\u6CE8\u91CA\u5DF2\u7531 6 \u884C\u538B\u81F3 2 \u884C\uFF09\u2014\u2014\u5355\u4E00\u4E0D\u53EF\u62C6\u7684\u91CA\u653E\u65B9\u6CD5\uFF08\u4E0D\u53EF\u5E76\u5165\u6BCF\u8F6E run() \u7684 stopFsWatcher\uFF0C\u5426\u5219\u70ED\u8F7D\u5728\u9996\u6761\u6D88\u606F\u540E\u5173\u6B7B\uFF09\uFF0C\u968F\u9644\u5B57\u6BB5\u6CE8\u91CA\u8BA2\u6B63\uFF1A\u539F\u300CAgentLoop \u4F1A\u8BDD\u7ED3\u675F\u5373\u968F\u5B9E\u4F8B\u56DE\u6536\u300D\u6709\u8BEF\uFF0CFSWatcher \u6301\u539F\u751F\u53E5\u67C4\u4E0D\u4F1A\u968F GC \u91CA\u653E 2952\u21922978\uFF1AStop\u2192settle \u6839\u6CBB\uFF082026-09-13\uFF09\u2014\u2014runPostSession \u62C6\u4E3A buildPostSessionContext + runPostSessionWith\uFF08\u5FEB\u7167\u540C\u6B65\u53D6\uFF0Chooks/\u843D\u76D8\u8FDB\u540E\u53F0\u94FE\uFF09\uFF0C\u65B0\u589E schedulePostSessionDetached / drainPostSession \u4E0E postSessionDetach \u5B57\u6BB5\uFF08DetachedRunner \u4E3B\u4F53\u5728 src/agent/post-session-detach.ts \u65B0\u6A21\u5757 38 \u884C\uFF0C\u672C\u6587\u4EF6\u53EA\u7559\u63A5\u7EBF\uFF0C+26\uFF09\u3002 2978\u21923109\uFF1Azen \u56DE\u6D41 Wave 2\uFF082026-09-13\uFF09\u2014\u2014\u76F8\u4F4D\u72B6\u6001\u673A\u6784\u9020 + armZenIfNeeded\uFF08persist \u5C31\u7EEA\u540E arm\uFF1Bresume \u6298\u53E0\u76F8\u4F4D\u4E0E\u6B65\u6570\u9884\u7B97\uFF09+ gatedToolDefinitions \u95E8\u63A7\u5148\u884C\u6539\u9020\uFF08zen \u6536\u7A84\u53E0\u52A0\u5728\u95E8\u63A7\u5B58\u6D3B\u96C6\u4E4B\u4E0A\uFF0C\u539F !gating \u65E9\u9000\u8DEF\u5F84\u5408\u5E76\uFF09+ \u65B9\u6CD5\u65CF\uFF08promoteZen / onZenEscape / getZenUnregisteredHint / zenTurnBoundary / emitZenPhaseEvent / persistZenPhase\uFF09+ run() \u76F8\u4F4D\u8865\u53D1\u4E0E turn \u8FB9\u754C\uFF08+131\uFF09\u3002 3109\u21923116\uFF1Azen \u56DE\u6D41 Wave 3\uFF082026-09-13\uFF09\u2014\u2014onPhaseChange \u63A5 setZenLean\uFF08appendixLean \u6D88\u8D39\u7AEF\uFF09+ gatedToolDefinitions \u63CF\u8FF0\u6863\u4F4D\u53CC\u91CD\u5E94\u7528\u4FEE\u6B63\uFF08gateToolDefinitions \u4E09\u5904\u8FD4\u56DE\u5DF2 applyDescriptionMode\uFF1B\u6539\u540E\u53EA\u5BF9\u65B0\u589E\u7684 zen_unlock \u518D\u5E94\u7528\u4E00\u6B21\uFF1B+7\uFF09\u3002 3116\u21923126\uFF1Azen \u5BA1\u67E5\u53F0\u8D26\u6536\u53E3\u4E24\u7B14\uFF082026-09-13\uFF0CP0+P1\uFF09\u2014\u2014emitZenPhaseEvent \u8865 best-effort \u4FDD\u62A4\uFF08run() \u7684\u76F8\u4F4D\u8865\u53D1\u5728 try \u4E4B\u5916\u4E14\u8BE5\u51FD\u6570\u539F\u5148\u65E0\u4FDD\u62A4\uFF1A\u56DE\u8C03\u629B\u4E00\u6B21\u5C31\u8BA9 run reject \u800C _running \u6052\u771F\uFF0C\u540E\u7EED\u8F93\u5165\u5168\u90E8 skipped-already-running\uFF0C\u4F1A\u8BDD\u5047\u6B7B\uFF1B+8\uFF09+ onPhaseChange \u7684 appendixLean \u6539\u8BFB zenController.resolvedConfig\uFF08\u6D88\u9664\u5185\u8054 `?? true` \u6784\u6210\u7684\u7B2C\u4E8C\u771F\u76F8\u6E90\uFF1B+2\uFF09\u3002"
875
+ },
876
+ "src/bootstrap.ts": {
877
+ ceiling: 2511,
878
+ note: "2421/2511\uFF08zen \u56DE\u6D41 Wave 1\uFF0C2026-09-13\uFF09\uFF1A+12 \u884C\u63A5\u7EBF\u2014\u2014resolveZenConfig \u7269\u5316 config.tools.zen\u3001zenStructuredRead \u4E09\u5904 preset \u8C41\u514D\uFF08repo_graph / related_tests / semantic_search\uFF09\u3001AgentLoop \u4F20\u5165 zen\u3002\u4F59\u91CF 90 \u884C\u3002"
879
+ },
880
+ "src/config/manager.ts": {
881
+ ceiling: 2871,
882
+ note: "2219\u21922606\uFF1Ahooks \u914D\u7F6E Wave 3a \u7B49\u914D\u7F6E\u9762\u589E\u957F\uFF08\u5148\u4E8E\u672C\u8C03\u6574\u5DF2\u5728 HEAD \u8D85\u9650\uFF09+ / \u9879\u76EE\u4FE1\u4EFB\u95E8\u63A5\u7EBF\uFF08\u672A\u6388\u4FE1\u5265\u79BB\uFF0C+9 \u884C\uFF09\u2014\u2014\u6309\u73B0\u503C\u5BF9\u9F50\u3002 2606\u21922648\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF0868a961c20 \u9884\u8BBE\u65B0\u589E\u6A21\u578B\u56DE\u6D41\u5B58\u91CF\u5FEB\u7167\uFF0C\u5DF2\u5408\u5165 HEAD \u540E\u8D85\u9650\uFF09\u3002 2648\u21922664\uFF1Aconfig login \u5B50\u547D\u4EE4\uFF08OAuth \u767B\u5F55\u843D\u5730\uFF0C/login \u5E7D\u7075\u6307\u5F15\u95ED\u73AF\uFF09\u2014\u2014case +12\u3001\u5E2E\u52A9\u6587\u672C +1\u3001\u5E2E\u52A9\u793A\u4F8B\u533A +3\u3002 2664\u21922682\uFF1AsetDefaultModelConfig \u6269\u5C55 defaultEffort\uFF08undefined=\u4E0D\u52A8/null\xB7auto=\u5220\u5B57\u6BB5/\u663E\u5F0F\u6863=\u843D\u76D8\uFF0C+17\uFF09\u2014\u2014/model \u9762\u677F effort \u968F\u9ED8\u8BA4\u6301\u4E45\u5316\uFF08CC \u5BF9\u6807\uFF09\u3002 2682\u21922643\uFF1AmigratePresetModelBackfill \u8FC1\u51FA\u81F3 preset-model-backfill.ts\uFF08\u7ED3\u6784\u68D8\u8F6E\u62C6\u5206\uFF1B\u968F\u8FC1\u6536\u7D27 userSaved \u5224\u636E\u2014\u2014\u7528\u6237\u5220\u6A21\u578B\u4E0D\u518D\u88AB\u9884\u8BBE\u56DE\u6D41\uFF09\u3002 2682\u21922705\uFF1AAPI key \u5220\u9664\u539F\u8BED clearApiKey\uFF08947f8b6e1\uFF0C+42 \u672A\u6536\u53E3\u6B20\u8D26\uFF0C2026-09-07 \u5BA1\u67E5\u8865\u8BB0\uFF09 2705\u21922706\uFF1AapplyAdvancedConfig \u652F\u6301 retry \u914D\u7F6E\u5757\uFF08issue #75\uFF0C+1 \u884C\u6302\u8F7D\uFF1Bretry \u7684 zod \u5B9A\u4E49\u4E3B\u4F53\u62C6\u5728 src/config/retry-schema.ts \u65B0\u6A21\u5757\uFF09 2706\u21922709\uFF1A\u8D26\u672C\u5BF9\u9F50\u5408\u5165\u524D\u7684\u5B58\u91CF\u6B20\u8D26 3 \u884C\uFF08\u672C\u6B21\u4F1A\u8BDD\u63A5\u624B HEAD \u5373\u4E3A 2709\uFF09\u2014\u2014\u751F\u56FE\u914D\u7F6E\u9762\uFF08issue #8\uFF09\u672A\u843D\u5728\u672C\u6587\u4EF6\uFF1AimageGenModelSchema \u62C6\u5728 src/config/image-gen-schema.ts\uFF0CgetImageGenModelConfig/registerImageGenModelConfig \u62C6\u5728 src/config/image-gen-model.ts\uFF0C\u672C\u6587\u4EF6\u96F6\u589E\u957F\u3002 2709\u21922722\uFF1AsaveConfig \u5265 profile \u4E34\u65F6\u5C42\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #128\uFF09\u2014\u2014\u300C\u6CBF overlay \u8DEF\u5F84\u9010\u952E\u8FD8\u539F\u300D\u7684\u4E3B\u4F53\u903B\u8F91\u62C6\u5728 src/config/profile-persist.ts\uFF08\u65B0\u6A21\u5757 70 \u884C\uFF1B\u4E0D\u62C6\u5219\u672C\u6587\u4EF6 +71\uFF09\u3002\u6B8B\u7559 +13 = loadConfig \u7684 skipProfileOverlay \u9009\u9879\uFF08+2\uFF09+ \u4E09\u6001\u5224\u65AD\uFF08+2\uFF09+ loadPersistableConfig\uFF08+5\uFF09+ import\uFF08+1\uFF09+ saveConfig \u8C03\u7528\u70B9\uFF08+2\uFF09\u2014\u2014\u9009\u9879\u672C\u4F53\u5FC5\u987B\u843D\u5728 loadConfig \u4E0A\uFF0C\u4E0D\u53EF\u518D\u62C6\u3002 2722\u21922809\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014keys \u6C60 A\u2032 \u8FC1\u79FB\uFF08loadConfig \u5E42\u7B49\u5408\u6210 keys[0]\u3001saveConfig \u5265\u660E\u6587\u843D provider-keys.json\u3001keyRef \u5F15\u7528\u8BA1\u6570\u5B88\u5220\u9664\u3001\u5893\u7891\u4FDD\u5168\uFF09\uFF08+87\uFF09 2809\u21922871\uFF1A\u6A21\u578B alias \u4F53\u7CFB\u5E9F\u5F03\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #154\uFF09\u2014\u2014migrateStripModelAlias \u843D\u672C\u6587\u4EF6\uFF08provider.models[]/keys[].models[] \u5265 alias + id \u53BB\u91CD\uFF0C\u4E0E\u65E2\u6709 migrate* \u5BB6\u65CF\u540C\u6784\uFF0C+62\uFF09\u3002"
883
+ },
884
+ "src/main.ts": {
885
+ ceiling: 2440,
886
+ note: "main \u589E\u957F\uFF1Ayolo \u8BED\u4E49\u6CE8\u91CA/\u63A5\u7EBF\uFF086ff919118 2026-09-07\uFF09\u6B20\u8D26\u6536\u53E3\u2014\u2014ceiling 2358\u21922395 2395\u21922397\uFF1A\u5CF0\u8C37\u8BA1\u4EF7\u6807\u793A\uFF0825b3022ef\uFF0C+12 \u672A\u6536\u53E3\u6B20\u8D26\uFF0C2026-09-07 \u5BA1\u67E5\u8865\u8BB0\uFF09 2397\u21922415\uFF1A\u6B22\u8FCE\u9875 P1-P2 \u63A5\u7EBF\uFF08guide \u54E8\u5175\u5224\u5B9A/\u5C55\u793A\u5373\u5199 + \u95EE\u5019\u8BED settle \u8584\u58F3\u2014\u2014\u4E3B\u4F53\u62C6\u81F3 src/tui/welcome-greeting.ts \u4E0E onboarding.ts\uFF0C2026-09-09\uFF09 2415\u21922417\uFF1A\u5BA1\u67E5 #6 settle \u5B88\u536B\u4F20\u53C2(isInputPending,\u5916\u5C42 busy \u9884\u5224\u5220\u9664\u51C0 +2,2026-09-09) 2417\u21922420\uFF1Azen \u56DE\u6D41 Wave 5\uFF082026-09-13\uFF09\u2014\u2014TUI \u6CE8\u5165 zen \u76F8\u4F4D\u5FBD\u7AE0\u63A2\u9488\uFF08setZenBadgeProvider \u2192 agent.zenController.isZen ? '\u7985' : undefined\uFF0C+3\uFF09\u3002 2420\u21922435\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014headless --model \u8BA4\u4E09\u6BB5\u5F0F provider:keyId:modelId\uFF08\u5148\u6A21\u578B\u5F52\u5C5E\u540E\u51ED\u636E\uFF09\uFF08+15\uFF09\u3002 2435\u21922440\uFF1Aone-shot \u8DEF\u5F84\u9489\u4F4F zen: undefined\uFF082026-09-13 \u5BA1\u67E5\u53F0\u8D26 #4\uFF09\u2014\u2014\u65E0\u4EBA\u503C\u5B88\u4F1A\u8BDD\u65E0 /fast \u901A\u9053\uFF0C\u4E14\u4E0D\u80FD\u4F9D\u8D56 createAgentConfig\u300C\u6070\u597D\u300D\u4E0D\u900F\u4F20 zen\uFF08\u8BE5\u51FD\u6570\u8FD4\u56DE\u767D\u540D\u5355\u4E00\u65E6\u5E76\u5165 zen \u5373\u9759\u9ED8 arm\uFF09\u3002\u523B\u610F\u672A\u540C\u65F6\u8BBE headless:true\uFF1A\u5B83\u4F1A\u8054\u52A8 tool-pipeline \u7684\u5BA1\u6279\u81EA\u52A8\u653E\u884C/\u62D2\u7EDD\u5206\u652F\uFF0C\u5C5E\u72EC\u7ACB\u6539\u52A8\uFF08+5\uFF09\u3002"
887
+ },
888
+ "src/tui/pi/latex-to-unicode.ts": {
889
+ ceiling: 2071
890
+ },
891
+ "src/agent/tool-pipeline.ts": {
892
+ ceiling: 2234,
893
+ note: "tool-pipeline \u589E\u957F\uFF1A\u51FA\u754C\u9996\u89E6\u5373\u6388\uFF08970b05196\uFF09+ approvalMode \u6CE8\u5165\uFF08f8bc60ffe \u56DE\u6D41 2026-09-07\uFF09\u2014\u2014ceiling 2099\u21922115 2115\u21922168\uFF1A\u6954\u6B7B\u65CF\u52A0\u56FA\uFF080e792e26e +38 / b76861adc \u5199\u540E\u6302\u8D77\u4E8B\u6545\u65CF +9 \u51C0\uFF09+ stall-observer \u5DE5\u5177\u6253\u70B9\uFF0838b246ba6 +10 \u51C0\uFF09+ meridian \u961F\u5217\u8BA9\u51FA\uFF08d1c06e5ac +5 \u51C0\uFF09\u2014\u2014\u591A\u7B14\u843D\u5730\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\u3002 2168\u21922197\uFF1A\u5DE5\u5177\u7BA1\u7EBF\u7EB5\u6DF1\u4FEE\u590D\u7B2C\u4E00\u6CE2\uFF08pre/post \u9636\u6BB5\u6253\u70B9 11 \u5904 + [slow-sync-stage] \u540C\u6B65\u6BB5\u8BA1\u65F6\uFF0C+29\uFF0C2026-09-10\uFF09 2197\u21922180\uFF1AR2 \u5199\u524D\u8BA4\u9886\u8DEF\u5F84\u89E3\u6790\u62C6\u51FA pre-write-claims.ts\uFF082026-09-12 \u62C6\u5206\u843D\u5730\u6536\u7D27\uFF09 2180\u21922187\uFF1Acheckpoint \u57FA\u7EBF\u5931\u8D25\u53EF\u89C1\u5316\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #133\uFF09\u2014\u2014deps.createCheckpoint \u6D4B\u8BD5\u63A5\u7F1D\uFF08+1\uFF09\u3001checkpointFailureNote \u58F0\u660E\uFF08+2\uFF09\u3001\u5931\u8D25\u5206\u652F\u4E0E\u5C3E\u90E8\u8B66\u793A\uFF08+11\uFF09\uFF1B\u539F PR \u6CE8\u91CA 12 \u884C\u5DF2\u538B\u7F29\u4E3A 5 \u884C\uFF08-7\uFF09\u3002\u65E7\u4EE3\u7801\u65E0\u6761\u4EF6\u7F6E\u4F4D checkpointCreated \u4F1A\u8BA9\u5EFA\u57FA\u7EBF\u5931\u8D25\u9759\u9ED8\u84B8\u53D1\uFF1A\u672C\u56DE\u5408\u5269\u4F59\u7834\u574F\u6027\u64CD\u4F5C\u5168\u843D\u5728\u56DE\u6EDA\u7A97\u5916\uFF0C\u4E14\u540E\u7EED\u56DE\u5408\u4EE5\u4E3A\u57FA\u7EBF\u5DF2\u5EFA\u3001\u6C38\u4E0D\u518D\u91CD\u8BD5\u3002 2187\u21922205\uFF1A\u5199\u5DE5\u5177\u8BB0\u8D26\u4E0E LSP \u901A\u77E5\u6536\u53E3\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #134\uFF09\u2014\u2014trackEdit \u95E8\u6539 WRITE_TOOL_NAMES + extractWriteFilePaths \u591A\u8DEF\u5F84\u5FAA\u73AF\uFF08+9\uFF09\u3001LSP changeFile \u540C\u95E8 + patchTargetPaths\uFF08+9\uFF09\uFF1B\u539F PR \u6CE8\u91CA 12 \u884C\u538B\u7F29\u4E3A 6 \u884C\uFF08-6\uFF09\u3002 2205\u21922221\uFF1A\u5199\u5DE5\u5177\u8D26\u672C\u4E09\u5904\u6F0F\u6536\u53E3\u2014\u2014recordAgentTouchedFile\uFF08\u56DE\u6EDA\u8303\u56F4\uFF09/ prewarm.invalidate / apply_patch\xB7ast_edit \u7684 evidence \u4E0E claim \u65B0\u9C9C\u5EA6\u3002\u56DE\u6EDA\u8303\u56F4\u662F checkpoint\u300C\u53EA\u56DE\u6EDA agent \u62E5\u6709\u6587\u4EF6\u300D\u7684\u552F\u4E00\u6587\u4EF6\u6765\u6E90\uFF0C\u6F0F\u8BB0 = hash_edit/ast_edit/apply_patch \u7684\u4FEE\u6539\u843D\u5728 YOLO \u6863 safety net\uFF08checkpoints + rollback\uFF09\u7684\u7A97\u5916\uFF1Bevidence \u6F0F\u8BB0\u5219 markClaimsStaleForFile \u4E0D\u89E6\u53D1\uFF08\u6D4B\u5B8C\u518D patch \u65E7\u9A8C\u8BC1\u4ECD\u663E\u793A\u65B0\u9C9C\uFF09\u3002\u6CE8\u91CA\u5DF2\u6309\u6700\u5C0F\u53EF\u8FA9\u62A4\u538B\u7F29 3 \u884C\u3002 2221\u21922234\uFF1Azen \u56DE\u6D41 Wave 2\uFF082026-09-13\uFF09\u2014\u2014deps.getZenUnregisteredHint \u5B57\u6BB5 + toolRegistry.execute \u7684 zen guard\uFF08zen \u76F8\u4F4D\u4E0B\u672A\u77E5\u5DE5\u5177\u62A5\u9519\u9644 zen_unlock \u51FA\u8DEF\uFF0C\u4E0D\u664B\u5347\uFF09+ \u6CE8\u91CA\uFF08+13\uFF09\u3002"
894
+ },
895
+ "src/server/session-routes.ts": {
896
+ ceiling: 2224,
897
+ note: "1995\u21921997\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09\u3002 1997\u21922019\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08HEAD 2005 \u5DF2\u8D85 8\uFF0C\u6B20\u8D26\uFF09+ \u81EA\u5B9A\u4E49\u6A21\u578B\u89E3\u6790\u4FEE\u590D\u56DE\u6D41\uFF08main 74ac20588\uFF1A409 \u9644\u56E0\u5206\u7C7B\u63A5\u7EBF classifyModelSpecMiss\uFF0C+14\uFF0C2026-09-07\uFF09 2019\u21922040\uFF1AA1 describeModelSwitchFailure \u5206\u6D41\uFF08272123ac0\uFF0C+21\uFF0C2026-09-07 \u5BA1\u67E5\u4FEE\u590D\uFF09 2040\u21922054\uFF1AB4 reloadConfig \u9644\u56E0\u5237\u65B0\uFF08682161674\uFF0C+14\uFF0C2026-09-07 \u5BA1\u67E5\u4FEE\u590D\uFF09 2054\u21922121\uFF1Aissue #63 \u4FDD\u5B58\u5E76\u91CD\u53D1\uFF0898888ab2b +8 \u51C0\uFF09+ IntelligenceMenu \u900F\u4F20\uFF08471807189 +24\uFF09+ git/branches \u6821\u9A8C\uFF08cdb93489a +11\uFF09+ images \u6821\u9A8C\u62BD\u53D6\uFF08f512c450a +15 \u51C0\uFF09+ \u6954\u6B7B\u65CF\uFF080e792e26e +9\uFF09\u2014\u2014\u591A\u7B14\u843D\u5730\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0 2121\u21922146\uFF1AHEAD 2123 \u6B20\u8D26 2 \u884C\u6536\u53E3 + POST /sessions documents \u652F\u6301\uFF08validateDocumentsPayload \u62BD\u53D6\u5171\u7528 + \u521B\u5EFA\u8DEF\u7531\u62BD\u53D6\u524D\u7F6E\uFF0C2026-09-12 \u6B22\u8FCE\u9875\u9644\u4EF6\uFF0C+23\uFF09 2146\u21922154\uFF1Adocuments \u6269\u5C55\u540D\u767D\u540D\u5355\uFF08\u5BF9\u9F50 doc-extract \u7684 EXTRACTABLE\u2014\u2014\u6B64\u524D\u4EFB\u610F\u6269\u5C55\u540D\u53EF\u8FC7\u6821\u9A8C\u843D\u76D8\u4EA4\u62BD\u53D6\u5668\uFF0C\u56FE\u7247\u8DEF\u5F84\u65E9\u6709 MIME \u6B63\u5219\uFF0C+8\uFF0C2026-09-12 \u5BA1\u67E5\u4FEE\u590D\uFF09 2154\u21922187\uFF1AHEAD 2176 \u6B20\u8D26 22 \u884C\u6536\u53E3\uFF08b967cab0c \u56DE\u653E\u51FA\u53E3\u5408\u5E76\u672A\u4E0A\u8D26\uFF09+ Stop\u2192settle \u7A97\u53E3\u7B49\u5F85\uFF08/rewind\u3001/prompt \u5404\u4E00\u5904 await manager.waitForRunSettled + \u6CE8\u91CA\uFF0C+11\uFF0C2026-09-13\u2014\u2014\u684C\u9762\u7AEF\u7F16\u8F91\u91CD\u53D1\u300C\u4FDD\u5B58\u5931\u8D25\uFF1ASession is running\u300D\u6839\u56E0\uFF09 2187\u21922196\uFF1ASSE \u4F18\u96C5\u5173\u505C\u63A5\u7EBF\uFF08agent-13 \u4FEE\u590D\uFF09\u2014\u2014import SseConnectionRegistry\uFF081\uFF09+ SessionRouteDependencies.sseRegistry \u5B57\u6BB5\u4E0E\u6CE8\u91CA\uFF085\uFF09+ /stream \u8DEF\u7531\u5EFA\u8FDE\u767B\u8BB0/\u6E05\u7406\u6CE8\u9500\uFF083\uFF09\u3002\u6CE8\u518C\u8868\u4E3B\u4F53\u5728 src/server/sse-registry.ts \u65B0\u6A21\u5757\uFF0C\u6B64\u5904\u53EA\u5269\u4F9D\u8D56\u6CE8\u5165\u4F4D\u3002 2196\u21922205\uFF1A\u684C\u9762\u7AEF\u7985\u76F8\u4F4D\u63A5\u5165\uFF082026-09-14\uFF09\u2014\u2014/stream \u5EFA\u8FDE\u8865\u53D1\u7B2C\u4E09\u6761 seq=0 \u5408\u6210\u4E8B\u4EF6 zen_phase\uFF08+9\uFF0C\u4E0E replay_window / job_snapshot \u540C\u6784\uFF0C\u53D6\u81EA record \u7684\u76F8\u4F4D\u955C\u50CF\uFF09\u3002zen_phase \u662F\u8FB9\u6CBF\u4E8B\u4EF6\uFF08\u53EA\u5728 run \u8D77\u70B9 arm \u4E0E\u6BCF\u6B21\u664B\u5347\u65F6\u53D1\uFF09\uFF0C\u957F\u4F1A\u8BDD\u91CC\u65E9\u5DF2\u6ED1\u51FA\u56DE\u653E\u7A97\u53E3\uFF0C\u800C\u91CD\u8FDE\u4ECE ?since= \u7EED\u8BFB\uFF1A\u4E0D\u8865\u53D1\u5219\u300C\u5207\u8D70\u518D\u5207\u56DE\u300D\u7684\u76F8\u4F4D\u5FBD\u7AE0\u5FC5\u4E22\uFF0C\u8981\u7B49\u4E0B\u4E00\u6B21 run \u624D\u56DE\u6765\u3002 2205\u21922220\uFF1Azen \u8DF3\u8FC7\u76F8\u4F4D\u8DEF\u7531\uFF08POST /sessions/:id/zen\uFF0Caction=skip\uFF0C2026-09-14\uFF09\u2014\u2014\u684C\u9762\u7AEF\u6CA1\u6709 TUI \u7684 /fast\uFF0C\u8FD9\u6761\u662F\u300C\u5F53\u524D\u4F1A\u8BDD\u7ACB\u523B\u9000\u51FA\u8BFB\u4E13\u6CE8\u300D\u7684\u901A\u9053\u3002\u8DEF\u7531\u53EA\u505A action \u6821\u9A8C\uFF08\u7F3A\u5931/\u975E skip \u2192 400\uFF09\u3001404 \u4E0E\u4F1A\u8BDD\u79C1\u6709\u65B9\u6CD5\u7684\u8F6C\u53D1\uFF1Bpromoted:false \u662F\u5982\u5B9E\u62A5\u544A\uFF08\u672A arm/\u5DF2\u664B\u5347\uFF09\uFF0C\u4E0D\u662F\u9519\u8BEF\u3002\u4E0E POST /sessions/:id/plan-mode \u540C\u65CF\u540C\u5F62\u3002 2220\u21922224\uFF1Aissue #147 \u5DE5\u4F5C\u533A\u6A21\u5F0F\uFF08+5/-1 \u5B9E\u6D4B 2224\uFF09\u2014\u2014POST /sessions \u900F\u4F20 workspaceMode\uFF08\u6821\u9A8C\u51FD\u6570 isSessionWorkspaceMode \u4E0E\u5224\u5B9A\u4E3B\u4F53\u90FD\u5728 src/server/workspace.ts\uFF0C\u6B64\u5904\u53EA\u7559 import +1\u3001\u4E00\u884C 400 \u5B88\u536B\u3001\u4E00\u884C\u4F20\u53C2\uFF09\uFF1B\u7F3A\u7701 undefined \u5373\u65E7\u5BA2\u6237\u7AEF\u8BED\u4E49\uFF0C\u975E\u6CD5\u503C\u663E\u5F0F 400 \u4E0D\u9759\u9ED8\u964D\u7EA7\u3002"
898
+ },
899
+ "src/prompt/engine.ts": {
900
+ ceiling: 1711,
901
+ note: "1624\u21921635\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09\uFF1B\u5176\u4E2D\u6346\u7ED1\u6280\u80FD\u6D88\u878D\u52A0 planExecutingBlock \u5B57\u6BB5/setter/getter\uFF08\u51C0\u589E\u7EA6 13 \u884C\uFF09\u3002 1635\u21921657\uFF1AupdateTools \u5E42\u7B49\u5B88\u536B\uFF084f85c6b1f +8\uFF09+ approvalMode \u5B57\u6BB5\u4E0E setter\uFF08def690436 +14\uFF09\u2014\u2014\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0 1657\u21921679\uFF1A\u591A\u6A21\u6001 parts \u4FDD\u771F\uFF08buildOaiRequest \u7684 volatile trailer \u5408\u5E76\u5BF9 parts \u6570\u7EC4\u53D6\u7A7A\u4E32\uFF0C\u7528\u6237\u9644\u56FE\u7684\u56FE\u7247\u88AB\u9759\u9ED8\u4E22\u5F03\u3001\u6A21\u578B\u4E00\u5F8B\u7B54\u300C\u770B\u4E0D\u5230\u56FE\u7247\u300D\uFF1B\u6539\u4E3A\u628A volatile \u524D\u7F00\u4F5C\u4E3A\u72EC\u7ACB text part\uFF0C\u7EAF\u6587\u672C\u8DEF\u5F84\u5B57\u8282\u4E0D\u53D8\uFF09+ trailerTextPrefix / buildTraileredUserContent \u4E24\u4E2A\u79C1\u6709 helper\uFF08\u987B\u8BBF\u95EE\u5F15\u64CE\u79C1\u6709 volatile \u72B6\u6001\uFF0C\u5916\u79FB\u4F1A\u589E\u8C03\u7528\u5904\u6837\u677F\uFF09 1679\u21921684\uFF1ArequestTimeCollapse \u5355\u904D\u7D22\u5F15\u5316\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #142\uFF0C+5 \u51C0\uFF09\u2014\u2014\u9010\u6761\u53CD\u5411\u7EBF\u6027\u56DE\u6EAF\u6539\u4E3A\u5355\u904D\u9884\u5EFA tool_call_id\u2192{name,target} \u7D22\u5F15\uFF08\u65B0\u589E buildToolCallIndex/parseCallTarget/toolNameFromIndex/targetFromIndex\uFF0C\u5220\u9664 inferToolName/inferToolTarget\uFF09\uFF0C\u6784\u5EFA\u9636\u6BB5\u6700\u574F O(n\xB2)\u2192O(n)\uFF1B\u7B49\u4EF7\u6027\u4EE5 300 \u7EC4\u5408\u6CD5\u968F\u673A\u5E8F\u5217 sha256 \u9010\u5B57\u8282\u5BF9\u62CD\u9489\u4F4F\uFF08\u6298\u53E0\u8F93\u51FA\u4E0D\u53D8\uFF0C\u957F\u4F1A\u8BDD\u524D\u7F00\u7F13\u5B58\u4E0D\u53D7\u5F71\u54CD\uFF09\u3002 1684\u21921694\uFF1Azen \u56DE\u6D41 Wave 3\uFF082026-09-13\uFF09\u2014\u2014private zenLean + setZenLean\uFF08loop \u76F8\u4F4D\u53D8\u5316\u65F6\u8C03\u7528\uFF1Aarm\u2192true / promote\u2192false\uFF09+ dynamicCtx \u4F20\u503C\uFF08zenLean: this.zenLean || undefined\uFF1B+10\uFF09\u3002 1694\u21921711\uFF1Aappendix \u6784\u6210\u5206\u89E3\uFF082026-09-13 zen \u7F13\u5B58\u8C03\u7814\u5EFA\u8BAE 1\uFF09\u2014\u2014private lastAppendixAnatomy \u5B57\u6BB5 + getAppendixAnatomy getter + buildAppendixBody \u8BB0\u5F55 + invalidateFreshCache \u6E05\u7A7A\uFF08+17\uFF09\u3002\u5206\u89E3\u4E3B\u4F53\u62C6\u5728 src/prompt/appendix-anatomy.ts \u65B0\u6A21\u5757\uFF0C\u6B64\u5904\u53EA\u7559\u5B57\u6BB5\u4E0E\u751F\u547D\u5468\u671F\u63A5\u7EBF\u3002"
902
+ },
903
+ "src/agent/compaction-controller.ts": {
904
+ ceiling: 1647,
905
+ note: "1621\u21921647\uFF1Aceiling checkpoint \u4FDD\u7559\u5C3E\u968F\u672A\u56DE\u590D user \u539F\u6587\uFF08desktop \u957F\u6D88\u606F\u622A\u65AD\u6839\u4FEE 59205b109\uFF0C+32 \u884C\u542B\u6CE8\u91CA\uFF09\u3002"
906
+ },
907
+ "src/agent/deliver-task.ts": {
908
+ ceiling: 1613,
909
+ note: "1595\u21921613\uFF1A\u672C\u6587\u4EF6\u7684 YELLOW \u9884\u8B66\u95E8\u63A5\u7EBF\uFF08ctx \u94A9\u5B50 + \u68C0\u67E5\u5757\uFF09\u843D\u5728 deliver-task\u3002"
910
+ },
911
+ "src/tui/format/overlay.ts": {
912
+ ceiling: 1678,
913
+ note: "1492\u21921678\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09"
914
+ },
915
+ "src/api/openai-client.ts": {
916
+ ceiling: 1642,
917
+ note: "1492\u21921563\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09 1563\u21921587\uFF1Awire \u63A2\u9488 tools \u7EF4\uFF084f85c6b1f +22\uFF09+ reasoning-repetition guard \u63A5\u7EBF\uFF08609e0964a +2 \u51C0\uFF09\u2014\u2014\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0 1587\u21921592\uFF1A\u53EF\u914D\u7F6E\u4F1A\u8BDD\u5934\u540D sessionHeader\uFF08OpenCode Go \u5F3A\u5236 x-opencode-session\uFF0C\u5B57\u6BB5 + header \u6CE8\u5165\uFF0C+5\uFF09 1592\u21921602\uFF1Aretry \u7B56\u7565\u63A5\u7EBF\uFF08issue #75\uFF1Aconfig.retry \u5B57\u6BB5\u58F0\u660E + maxTotalDurationMs \u53EF\u914D + policy \u900F\u4F20 + fetch \u524D\u9650\u901F\u8FC7\u6876\uFF0C+10\uFF09\u2014\u2014\u5168\u90E8\u662F\u63A5\u7EBF\u884C\uFF0C\u903B\u8F91\u4E3B\u4F53\u5728 retry-engine.ts \u4E0E rate-limiter.ts \u65B0\u6A21\u5757 1602\u21921641\uFF1Aimage_strip \u6062\u590D\u63A5\u7EBF\uFF08issue #94\uFF1A\u5206\u7C7B\u5668\u627F\u8BFA\u7684\u300C\u5265\u56FE\u91CD\u53D1\u300D\u6B64\u524D\u5168\u94FE\u8DEF\u6CA1\u6709\u6D88\u8D39\u65B9\uFF0C413/\u56FE\u7247\u88AB\u62D2\u53EA\u662F\u539F\u6837\u91CD\u53D1\u3002\u672C\u6587\u4EF6\u52A0\u5265\u56FE\u72B6\u6001\u4F4D + 413 \u9519\u8BEF\u4E0A\u7684 payloadHadImages \u6807\u8BB0 + onRetry \u6302\u94A9\uFF0C\u7EAF\u51FD\u6570\u4E3B\u4F53\u5728 oai-types.ts\uFF0C\u5206\u7C7B\u5206\u6D41\u5728 error-classifier.ts\uFF09 1641\u21921642\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014\u8BF7\u6C42\u7AEF baseUrl normalize \u515C\u5E95\uFF08+1\uFF09"
918
+ },
919
+ "src/agent/turn-orchestrator.ts": {
920
+ ceiling: 1530,
921
+ note: "1462\u21921471\uFF1AB2 \u6536\u655B\u8F68\u8FF9\u95E8\uFF08\u4F1A\u8BDD 506a5e86 \u4F18\u5316\uFF0Cdeps getter + AND \u95E8\u5224\u5B9A\uFF0C+9 \u884C\uFF09\u30021471\u21921485\uFF1Astall-observer \u4FEE\u590D\u63A5\u7EBF\u2014\u2014wrapCallbacksWithHeartbeat \u52A0\u53EF\u9009 getSessionId \u53C2 + isFinal \u56DE\u5408\u5B8C\u6210\u6253 markIdle\uFF08\u4EA4\u4ED8\u540E\u7B49\u5F85\u7528\u6237\u514D\u8BEF\u62A5\uFF0C+14 \u884C\uFF0C2026-09-09\uFF09\u3002 1485\u21921491\uFF1AmarkIdle \u65F6\u5E8F\u6536\u53E3 try/finally\uFF08b3329e590 +6\uFF0Cstall \u8BEF\u544A\u8B66 P2 \u4FEE\u590D\uFF09 1491\u21921500\uFF1Aimage_strip \u6062\u590D\u7684\u7528\u6237\u53EF\u89C1\u63D0\u793A\u63A5\u7EBF\uFF08\u5265\u56FE\u540E\u6A21\u578B\u770B\u4E0D\u5230\u56FE\uFF0C\u9759\u9ED8\u5904\u7406\u4F1A\u88AB\u8BFB\u6210\u300C\u6A21\u578B\u6CA1\u7406\u6211\u7684\u622A\u56FE\u300D\u2014\u2014onImageStripped \u2192 onPhaseChange('image-stripped')\uFF0Cissue #94\uFF09 1512 \u5185\uFF1A1500\u21921512\u2014\u2014Stop\u2192settle \u6839\u6CBB\uFF082026-09-13\uFF09\uFF1A\u56DB\u4E2A\u4E2D\u6B62\u51FA\u53E3\u7EDF\u4E00\u8D70 finishInterrupted\uFF08\u65E7\u884C\u4E3A\u56DB\u51FA\u53E3\u5404\u81EA\u4E3A\u653F\uFF1A\u51FA\u53E3 1/2 \u4E0D\u8DD1 postSession\u30013/4 \u963B\u585E\u7B49\u5B83\u3001\u53EA\u6709 1/4 \u8BB0 stop reason\uFF09\uFF0C\u65B0\u589E turnTextPersisted \u5B88\u536B\uFF08\u5DF2\u6301\u4E45\u5316\u7684 streamedText \u4E0D\u5F97\u91CD\u590D\u4F5C partial\uFF09\u4E0E deps \u7684 drainPersist / schedulePostSessionDetached\u3002\u7EAF\u63A5\u7EBF\u4E0E\u51FA\u53E3\u5408\u5E76\uFF0C\u65E0\u65B0\u673A\u5236\uFF08+12\uFF09\u3002 1512\u21921525\uFF1A\u6253\u65AD\u7559\u75D5\uFF08\u4EFB\u52A1 4/7\uFF0C2026-09-13\uFF09\u2014\u2014deps \u65B0\u589E getInterruptMarkerEnabled getter\uFF08config `agent.interruptMarker` / env \u53CC\u901A\u9053\uFF09\uFF0CfinishInterrupted \u7684 removeLastMessage \u66FF\u6362\u4E3A recordInterruption \u8C03\u7528\uFF08\u7528\u6237 Stop \u4FDD\u7559 user/partial + \u8FFD\u52A0 [interrupted] \u6807\u8BB0\uFF1Bwatchdog/\u5F00\u5173\u5173\u4ECD\u8D70\u65E7\u64A4\u56DE\uFF09\uFF1B\u6807\u8BB0\u8BED\u4E49\u4E3B\u4F53\u62C6\u5728 src/agent/interrupt-marker.ts \u65B0\u6A21\u5757\uFF0C\u6B64\u5904\u53EA\u7559 import + deps \u63A5\u7EBF + \u8C03\u7528\uFF08+13\uFF09\u3002 1525\u21921530\uFF1A\u6D41\u9519\u8BEF\u7EC8\u6001\u4FEE\u590D\uFF082026-09-16\uFF09\u2014\u2014\u4E24\u5904\u7EC8\u7ED3\u70B9\uFF08streamError \u5206\u652F + execute \u9876\u5C42 catch\uFF09\u8C03 callbacks.onStreamInterrupted\uFF08+5 \u542B 2 \u884C\u6CE8\u91CA\uFF09\uFF1BAbortError \u6392\u9664\u5728\u5916\uFF08\u7528\u6237\u4E2D\u65AD\u8D70 aborted\uFF0C\u7531 server \u7684 onAbort \u7F6E\u7EC8\u6001\uFF09\u3002"
922
+ },
923
+ "src/agent/loop-factory.ts": {
924
+ ceiling: 1544,
925
+ note: "1548\u21921544\uFF1A\u884C\u6570\u5BF9\u9F50\u5B9E\u9645\uFF08-4\uFF09\u3002 1470\u21921473\uFF1A\u6CE8\u5165 B2 \u6536\u655B\u8F68\u8FF9 getter\u30021473\u21921487\u21921505\uFF1A\u63A5\u7EBF\u8BB0\u5FC6\u5F62\u6210\u4FA7\uFF08autoCapture\uFF09+ \u4F1A\u8BDD\u7ED3\u675F\u5DE9\u56FA\uFF08sessionConsolidation\uFF09\u30021505\u21921509\uFF1Aworker \u8BB0\u5FC6\u9694\u79BB\u8FB9\u754C guard\u3002 1509\u21921517\uFF1AupdateTools \u5E42\u7B49\u5B88\u536B\u63A5\u7EBF\uFF084f85c6b1f +8\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0 1517\u21921519\uFF1AStop\u2192settle \u6839\u6CBB\u63A5\u7EBF\uFF082026-09-13\uFF09\u2014\u2014deps \u8865 drainPersist\uFF08inline \u6709\u754C drain\uFF0C\u5DE5\u5177\u7ED3\u679C\u4E0D\u56E0 Ctrl+C \u4E22\uFF09\u4E0E schedulePostSessionDetached\uFF08\u4E2D\u6B62\u8DEF\u5F84 postSession \u8FDB\u540E\u53F0\u94FE\uFF09\uFF0C\u5404 1 \u884C\u3002 1519\u21921522\uFF1A\u6253\u65AD\u7559\u75D5\u63A5\u7EBF\uFF08\u4EFB\u52A1 4/7\uFF0C2026-09-13\uFF09\u2014\u2014import isInterruptMarkerEnabled\uFF08+1\uFF09+ deps \u7684 getInterruptMarkerEnabled\uFF08config `agent.interruptMarker` / env `RIVET_INTERRUPT_MARKER` \u53CC\u901A\u9053\uFF0C\u6CE8\u91CA +1\u3001getter +1\uFF09\u3002 1522\u21921528\uFF1Azen \u56DE\u6D41 Wave 2\uFF082026-09-13\uFF09\u2014\u2014onZenEscape / onZenUnlock / getZenUnregisteredHint \u4E09\u56DE\u8C03\u63A5\u7EBF\uFF08+6\uFF09\u3002 1528\u21921544\uFF1Aappendix \u6784\u6210\u5206\u89E3\u63A5\u7EBF\uFF082026-09-13\uFF0C\u4E0E engine.ts \u540C\u7B14\uFF09\u2014\u2014recordTurnCache \u8BFB getAppendixAnatomy \u843D appendixPartsChars / appendixCvmChars / appendixCvmBySource / appendixKeepChars\uFF08+16\uFF0C\u5168\u4E3A\u89C2\u6D4B\u5B57\u6BB5\uFF0C\u4E0D\u6539\u8BF7\u6C42\u5B57\u8282\uFF09\u3002"
926
+ },
927
+ "src/agent/advisory-bus.ts": {
928
+ ceiling: 1277,
929
+ note: "1273\u21921277\uFF1A\u8D26\u672C\u5BF9\u9F50\u2014\u2014CVM \u6CE8\u5165\u9884\u7B97\u88C1\u526A\u5148\u4E8E\u9001\u8FBE\u8BB0\u8D26\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #82\uFF0Ccommit b393b4ca2\uFF0C+4 \u51C0\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-13 \u8865\u8BB0\u3002\u8BE5\u6539\u52A8\u662F\u771F\u5B9E\u7684\u8BB0\u8D26\u987A\u5E8F\u4FEE\u590D\uFF08\u5047\u9001\u8FBE\u4F1A\u8150\u8680\u4E60\u60EF\u5316/\u6548\u80FD\u53CD\u9988\u73AF\uFF09\uFF0C\u975E\u81A8\u80C0\uFF0C\u6545\u6309\u73B0\u503C\u5BF9\u9F50\u800C\u975E\u62C6\u5206\u3002"
930
+ },
931
+ "src/agent/convergence-detector.ts": {
932
+ ceiling: 1260
933
+ },
934
+ "src/tui/engine/input-line.ts": {
935
+ ceiling: 1278,
936
+ note: "1270\u21921278\uFF1A\u56FE\u7247\u9644\u4EF6\u7F16\u8F91\u95ED\u73AF\u2014\u2014undo \u5FEB\u7167\u7EB3\u5165 images\uFF08UndoUnit+undo/redo \u6062\u590D\uFF09\u3001backspace \u7A7A\u6587\u672C\u5220\u6700\u540E\u4E00\u5F20\u56FE\u3001clearAll \u539F\u5B50\u6E05\u7A7A\uFF08+8\uFF0C2026-08-26\uFF09"
937
+ },
938
+ "src/prompt/volatile.ts": {
939
+ ceiling: 1263,
940
+ note: "1197\u21921226\uFF1A\u6346\u7ED1\u6280\u80FD\u6D88\u878D\u2014\u2014<plan-mode> \u5E76\u5165\u4E94\u6761\u8C03\u7814\u7EAA\u5F8B + \u65B0\u589E renderPlanExecutingBlock \u539F\u751F\u6267\u884C\u7EAA\u5F8B\u5757\uFF08writing/executing-plans \u6280\u80FD\u6D88\u878D\u7684\u843D\u70B9\uFF0C+29\uFF09\u3002 1226\u21921253\uFF1ArenderPermissionNote \u514D\u5BA1\u6279\u6863\u63D0\u793A\uFF08def690436 +27\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0 1253\u21921263\uFF1Azen \u56DE\u6D41 Wave 3\uFF082026-09-13\uFF09\u2014\u2014VolatileContext.zenLean \u5B57\u6BB5 + push() \u9996\u884C\u88C1\u526A CVM \u52A8\u6001\u6CE8\u5165\u5757\uFF08ctx.zenLean && source !== undefined\uFF1Bkeep-list \u5757\u4E0D\u6302 source \u6545\u4FDD\u7559\uFF09+ \u6CE8\u91CA\uFF08+10\uFF09\u3002"
941
+ },
942
+ "src/agent/turn-step-producer.ts": {
943
+ ceiling: 1200,
944
+ note: "1169\u21921200\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09"
945
+ },
946
+ "src/agent/worker-session.ts": {
947
+ ceiling: 1247,
948
+ note: "worker-session \u589E\u957F\uFF1A\u7EED\u8DD1\u51BB\u7ED3\u5FEB\u7167\u6536\u53E3\uFF08e67af8a4f 1e \u56DE\u6D41 2026-09-07\uFF09\u6B20\u8D26\u6536\u53E3\u2014\u2014ceiling 1206\u21921235\u30021235\u21921241\uFF1A\u5047\u62A5\u544A\u94FE\u6CBB\u672C\uFF082026-09-13 \u4E8B\u6545\u2014\u2014\u63A2\u7D22\u8FC7\u7684 worker \u843D\u8FDB\u65E0\u5386\u53F2 JSON \u4FEE\u590D\u901A\u9053\u540E\u5199\u51FA\u300C\u6211\u6CA1\u62FF\u5230\u4E0A\u4E0B\u6587\u300D\u7684\u81EA\u8FF0\u5E76\u88AB\u4E0B\u6E38\u5F53\u6B63\u5F0F\u7ED3\u8BBA\u6E32\u67D3\uFF09\uFF1A\u5B88\u536B\u6539\u8C03 shouldUseContextFreeRepair + import + \u4E8B\u6545\u80CC\u666F\u6CE8\u91CA\uFF1B\u5224\u636E\u4E0E\u51B3\u7B56\u8868\u5DF2\u6CBF\u63A5\u7F1D\u62C6\u5230\u65B0\u6A21\u5757 worker-repair-route.ts\uFF0C\u6B64\u5904\u53EA\u7559\u8C03\u7528\u4E0E\u8BF4\u660E\uFF08+6 \u4E3A\u8C03\u7528/import/\u6CE8\u91CA\uFF0C\u65E0\u903B\u8F91\u589E\u91CF\uFF09\u30021241\u21921245\uFF1A\u6536\u5C3E\u8F6E\u622A\u65AD\u53EF\u89C2\u6D4B\u6027\uFF08\u540C\u65E5 json_parse \u6839\u56E0\u2014\u2014reviewer \u6536\u5C3E\u8F6E max_tokens \u843D 4096 \u515C\u5E95\uFF0C\u957F\u62A5\u544A\u622A\u6210\u7578\u5F62 JSON \u540E\u73B0\u573A\u53EA\u5269\u300CJSON malformed\u300D\uFF09\uFF1A\u5224\u636E/\u5E38\u91CF/\u7ED3\u679C\u5305\u88C5\uFF08isTruncationStopReason / FINALIZE_TRUNCATION_RISK / withTruncationRisk\uFF09\u5DF2\u6CBF\u540C\u4E00\u63A5\u7F1D\u62C6\u81F3 worker-repair-route.ts\uFF0C\u6B64\u5904\u53EA\u5269 finalizeWorkerReport \u8FD4\u56DE\u503C\u6539\u9020\uFF08string\u2192{text,truncated}\uFF09\u4E0E\u4E24\u5904\u6CE8\u5165\u8C03\u7528\uFF08+4\uFF0C\u5168\u4E3A\u6570\u636E\u900F\u4F20\uFF0C\u65E0\u65B0\u5206\u652F\uFF09 1245\u21921247\uFF1A\u5BA1\u67E5\u4FEE\u590D\uFF082026-09-19\uFF09\u2014\u2014summarizeToolUseLine \u4E0A\u65B9\u6CE8\u91CA\u540C\u6B65 toolArgSummary \u65B0\u589E\u7684\u901A\u7528\u53C2\u6570\u952E\u515C\u5E95\uFF08+2 \u884C\u6CE8\u91CA\uFF0C\u65E0\u903B\u8F91\u6539\u52A8\uFF09"
949
+ },
950
+ "src/tools/galaxy.ts": {
951
+ ceiling: 1170,
952
+ note: "galaxy \u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF08\u5148\u524D\u63D0\u4EA4\u7D2F\u79EF\uFF09\u2014\u2014ceiling 1158\u21921170"
953
+ },
954
+ "src/pro/computer-use/windows-driver.ts": {
955
+ ceiling: 1136
956
+ },
957
+ "src/tools/read-file.ts": {
958
+ ceiling: 1124,
959
+ note: "1123\u21921124\uFF1A\u91CD\u8BFB\u7EAA\u5F8B\u63AA\u8F9E\uFF0875f41db16 +1\uFF09"
960
+ },
961
+ "src/server/serve-agent.ts": {
962
+ ceiling: 1209,
963
+ note: "1125\u21921150\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF091150\u21921158\uFF1Amemory \u5DE5\u5177\u8865 deep_recall \u4FA7\u8DEF\u63A5\u7EBF\uFF08deepRecallAgentRef \u665A\u7ED1\u5B9A\u76D2\uFF09+ RIVET_MEMORY_BACKFILL \u56DE\u586B\u8C03\u5EA6\uFF08\u5BF9\u9F50 dsh \u8BB0\u5FC6\u4EF6\uFF0C+8 \u51C0\u589E\uFF09\u30021158\u21921161\uFF1A\u6280\u80FD\u52A0\u8F7D\u53CC\u8DEF\u5F84\u9519\u8BEF\u5F52\u96C6\u63A5\u7EBF\uFF08import + recordSkillLoadErrors \u8C03\u7528 + forgetSessionStores \u6E05\u7406\uFF0C\u5404 1 \u884C\uFF09\u2014\u2014\u4E3B\u4F53\u903B\u8F91\u5DF2\u6CBF\u63A5\u7F1D\u62C6\u5230\u65B0\u6A21\u5757 src/server/skill-load-errors.ts\uFF0C\u6B64\u5904\u53EA\u5269\u63A5\u7EBF\u3002 1161\u21921187\uFF1AHEAD 1176 \u6B20\u8D26\u6536\u53E3\uFF08+15\uFF0Cissue #103 spawn windowsHide \u7EDF\u4E00\u7B49\u4ED6\u4F1A\u8BDD\u5DF2\u843D\u672A\u4E0A\u8D26\uFF09+ sidecar \u63D2\u4EF6\u5FEB\u7167\u5408\u5165\uFF08pluginToolsSnapshot \u88C5\u914D + import\uFF0C2026-09-12\uFF0C+11\uFF09 1187\u21921196\uFF1A\u63D2\u4EF6\u5DE5\u5177\u5408\u5165\u524D\u4E8C\u6B21\u51B2\u7A81\u8FC7\u6EE4\uFF08partitionPluginTools + \u51B2\u7A81\u70B9\u540D\u8B66\u544A\u2014\u2014\u6696\u573A\u57FA\u5EA7\u4E0D\u542B createInteractiveToolRegistry \u88C5\u914D\u7684 galaxy/deliver_task/\u57DF\u5DE5\u5177\uFF0C+9\uFF0C2026-09-12 \u5BA1\u67E5\u4FEE\u590D\uFF09 1196\u21921198\uFF1AStop\u2192settle \u6839\u6CBB\uFF082026-09-13\uFF09\u2014\u2014shutdown \u5148 await agent.drainPostSession(5_000) \u6709\u754C\u6536\u53E3\u540E\u53F0 postSession \u94FE\uFF08memory/consolidation \u5199\u5165\u4E0D\u56E0\u8FDB\u7A0B\u5173\u505C\u4E22\uFF09\uFF0C+2\u3002 1198\u21921201\uFF1Aclaim-store \u6536\u53E3\u6392\u7A7A\u63A5\u7EBF\uFF08agent-16 \u4FEE\u590D\uFF09\u2014\u2014shutdown \u91CC await stores.claimStore.flushWrites(2_000)\uFF08\u4E0E drainPostSession \u540C\u94FE\uFF1Bwrite-behind \u6EDE\u7559\u884C\u5728\u4F1A\u8BDD\u672B\u649E\u77ED\u6682\u6587\u4EF6\u9501\u65F6\u4F1A\u968F\u8FDB\u7A0B\u9000\u51FA\u4E22\u5931\uFF09\uFF0C+3 \u542B\u6CE8\u91CA\u3002 1201\u21921205\uFF1APR \u5408\u5165\u6536\u53E3\uFF08\u591A key\uFF09\u2014\u2014switchModel \u7684 modelRef \u5E26 keyId \u6BB5\uFF08resume \u4E0E listModels \u7684 current \u5224\u5B9A\u7CBE\u786E\u5230\u8D26\u53F7\uFF09\uFF1B\u5355 key/\u672A\u8FC1\u79FB provider \u4FDD\u6301\u4E24\u6BB5\u5F0F\uFF08+4\uFF09 1205\u21921209\uFF1Ashutdown \u8865\u5173 config \u70ED\u8F7D watcher\uFF08PR #157\uFF0C2026-09-15\uFF09\u2014\u2014ManagedAgent.shutdown \u663E\u5F0F stopConfigWatcher\uFF08+4\uFF1A1 \u4EE3\u7801 + 3 \u6CE8\u91CA\uFF09\uFF1BTUI switch \u4E09\u8DEF\u5F84\u5DF2\u5728 bootstrap \u663E\u5F0F\u8C03 stop\uFF0C\u6B64\u5904\u8865\u9F50 serve \u91CA\u653E\u94FE\uFF08releaseAgent \u2192 shutdown\uFF09\uFF0Csidecar \u7A7A\u95F2\u56DE\u6536\u4E0D\u518D\u6F0F FSWatcher\u3002"
964
+ },
965
+ "src/tools/browser-debug/tool.ts": {
966
+ ceiling: 1084
967
+ },
968
+ "src/server/session-persistence.ts": {
969
+ ceiling: 1381,
970
+ note: "write-behind \u56DE\u6D41\uFF08995bb81a9 2026-09-07\uFF09\u6B20\u8D26\u6536\u53E3\u2014\u2014ceiling 1062\u21921280\u3002 1280\u21921282\uFF1Amerge origin/main \u5BF9\u9F50\u2014\u2014\u8FDC\u7AEF\u7EBF\u5728 loadEventsTail \u5185\u52A0 RIVET_DEBUG_RENDER \u51B7\u5F00\u9996\u5C4F\u5F52\u56E0\u63A2\u9488\uFF08read/parse \u4E24\u6BB5\u8017\u65F6 + __log \u5305\u88C5 inline/worker/chunked \u4E09\u6761 parse \u8DEF\u5F84\uFF0C+11 \u884C\uFF0C\u914D\u5957 docs/dev/render-debug-playbook.md\uFF09\uFF0C\u672C\u5730 ceiling \u7684 9 \u884C\u4F59\u91CF\u4E0D\u8DB3\u4EE5\u8986\u76D6\u53E0\u52A0\u540E\u7684\u5B9E\u6D4B\u503C\uFF0C\u6545\u6309\u5408\u5E76\u6001 1282 \u5BF9\u9F50\u3002 1282\u21921349\uFF1A\u5199\u94FE\u9519\u8BEF\u5206\u7C7B\u4E0E\u68AF\u5EA6\uFF08agent-16 \u4FEE\u590D\uFF09\u2014\u2014PERMANENT_WRITE_ERROR_CODES\uFF08ENOENT/ENOTDIR/EROFS/EISDIR \u7ACB\u5373\u505C\u94FE\uFF09+ MAX_TRANSIENT_WRITE_RETRIES\uFF08EACCES/EPERM/EBUSY \u53CA\u672A\u77E5\u7801\u68AF\u5EA6\uFF0C\u9ED8\u8BA4 8\uFF09+ eventChainFailures/recordChainFailures \u72B6\u6001\u56FE + registerWriteFailure\uFF0822\uFF09+ \u4E24\u6761\u94FE\u7684 catch \u5206\u7C7B\u4E0E finally \u505C\u94FE\u5224\u5B9A\uFF08~26\uFF09+ kick/saveRecord/\u540C\u6B65\u6392\u7A7A\u8DEF\u5F84\u7684\u300C\u6210\u529F\u6E05\u4F4D\u300D\u63A5\u7EBF\uFF08~8\uFF09+ flushSessionAsync/flushAllAsync \u505C\u94FE\u611F\u77E5\uFF08~9\uFF09\u3002\u65E0\u5206\u7C7B\u7684 250ms \u65E0\u9650\u91CD\u8BD5\u4F1A\u8BA9\u5B9A\u65F6\u5668\u94FE\u6C38\u4E0D\u7EC8\u6B62\uFF08\u4E8B\u4EF6\u5FAA\u73AF\u6C38\u4E0D\u7A7A\u3001node --test \u6302\u6B7B\u5F62\u6001\uFF0C\u4E0E claim-store 89b5ec310 \u540C\u6B3E\u6559\u8BAD\uFF09\uFF1B\u505C\u94FE\u5224\u5B9A\u7684\u62C6\u51FA\u53D7\u72B6\u6001\u56FE\u79C1\u6709\u6027\u7EA6\u675F\uFF0C\u7559\u5F85\u540E\u7EED\u63A5\u7F1D\u3002 1349\u21921378\uFF1Aflush \u7CFB\u5217\u8865\u8E22\uFF08agent-16 \u5BA1\u67E5\u8DDF\u8FDB\u9879\u300Cagain \u541E\u4E00\u62CD\u300D\uFF09\u2014\u2014flushSessionAsync / flushAllAsync \u8F6E\u8BE2\u89C2\u6D4B\u300C\u505C\u94FE + \u6EDE\u7559\u300D\u65F6\u6E05\u4F4D\u91CD\u8E22\u4E00\u6B21\uFF08\u6709\u754C\u9632\u70ED\u5FAA\u73AF\uFF1B\u8865\u8E22\u540E\u4ECD\u505C\u94FE\u5219\u786E\u8BA4\u6392\u4E0D\u6389\u3001\u63D0\u524D\u8FD4\u56DE\uFF09\uFF0C+29 \u542B\u6CE8\u91CA\u3002\u5165\u53E3 kick \u649E\u4E0A\u8FD0\u884C\u4E2D\u7684\u94FE\u88AB\u541E\u4E3A again \u540E\u505C\u94FE\u65F6\u6CA1\u6709\u4E0B\u4E00\u6B21 kick\u2014\u2014\u8865\u8E22\u662F\u6293\u4F4F\u300C\u9501\u5DF2\u91CA\u653E\u300D\u7A97\u53E3\u7684\u552F\u4E00\u901A\u9053\u3002 1378\u21921381\uFF1Arecord \u94FE\u6210\u529F\u6E05\u4F4D + stallLogged \u72EC\u7ACB\u95F8\uFF08fa5946908\uFF0C\u8DE8\u4F1A\u8BDD\u843D\u5730\u672A\u4E0A\u8D26\u2014\u2014Wave A \u9A8C\u8BC1\u65F6\u4EE3\u6536\u53E3\uFF0C\u8D26\u76EE\u4F9D\u636E\u8BE5\u63D0\u4EA4\u5B9E\u9645 diff\uFF09\u3002"
971
+ },
972
+ "src/agent/problem-attack-loop.ts": {
973
+ ceiling: 1027
974
+ },
975
+ "src/agent/hooks/cognitive-capsule-router.ts": {
976
+ ceiling: 1026
977
+ },
978
+ "src/agent/work-order.ts": {
979
+ ceiling: 1098,
980
+ note: "work-order \u589E\u957F\uFF1ArepairJsonSyntax \u63A5\u7EBF\uFF08e89d0074f \u56DE\u6D41 2026-09-07\uFF09+ \u57FA\u7EBF\u7D2F\u79EF\u2014\u2014ceiling 1019\u21921090 \u6536\u53E3\u30021090\u21921098\uFF1A\u7ED3\u679C\u6765\u6E90\u5951\u7EA6\uFF082026-09-13 \u5047\u62A5\u544A\u4E8B\u6545\uFF09\u2014\u2014workerResultSchema \u65B0\u589E reportSource \u679A\u4E3E\uFF08\u7CFB\u7EDF\u76D6\u7AE0\u901A\u9053\u6807\u8BB0\uFF0C\u523B\u610F\u4E0D\u8FDB workerResultIngestSchema\uFF1Aworker \u4E0D\u5F97\u81EA\u79F0 live\uFF09+ salvage/blocked \u4E24\u4E2A\u4EA7\u51FA\u70B9\u5404\u76D6\u4E00\u884C"
981
+ },
982
+ "src/tools/run-tests.ts": {
983
+ ceiling: 1004,
984
+ note: "995\u21921004\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09"
985
+ },
986
+ "src/server/serve.ts": {
987
+ ceiling: 1432,
988
+ note: "serve \u589E\u957F\uFF1A\u8DE8\u76D8\u5BA1\u6279\u5E7F\u64AD\u63A5\u7EBF\uFF0893956f5e2 \u56DE\u6D41 2026-09-07\uFF09\u2014\u2014ceiling 989\u21921010\u3002 1010\u21921027\uFF1A\u81EA\u5B9A\u4E49\u6A21\u578B\u89E3\u6790\u4FEE\u590D\u56DE\u6D41\uFF08main 74ac20588\uFF1A\u65E0 key provider \u65E9\u9000\u6539 continue + classifyModelSpecMiss\uFF0C2026-09-07\uFF09 1027\u21921036\uFF1AB4 reloadConfig \u6CE8\u5165 + B5 oauth isAuthenticated\uFF08682161674\uFF0C+9\uFF0C2026-09-07 \u5BA1\u67E5\u4FEE\u590D\uFF09 1036\u21921116\uFF1A\u6954\u6B7B\u65CF serve \u4FA7\uFF080e792e26e +44\uFF09+ \u4F1A\u8BDD\u5185\u6A21\u578B\u5217\u8868 key \u8FC7\u6EE4\uFF08e3fdaa146 +30\uFF09+ \u78C1\u76D8\u5BF9\u8D26\u5355\u6E90\u63A5\u7EBF\uFF08c77427d21\uFF09\u2014\u2014\u591A\u7B14\u843D\u5730\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\uFF09\u30021116\u21921120\uFF1A/project/trust \u8DEF\u7531\u6CE8\u518C\uFF08import + \u6CE8\u518C\u8C03\u7528\u5171 +3\uFF09\u2014\u2014\u8DEF\u7531\u4E3B\u4F53\u3001\u53C2\u6570\u6821\u9A8C\u4E0E 6 \u6761\u6D4B\u8BD5\u90FD\u5728\u65B0\u6A21\u5757 src/server/trust-api.ts\uFF0C\u6B64\u5904\u53EA\u5269\u63A5\u7EBF\u3002 1120\u21921184\uFF1AMobile Remote \u670D\u52A1\u7AEF\u56DE\u6D41\uFF08alpha 9899d9dca/b1c5fa100\uFF1ARunServeOptions host/allowedHosts/mobileDir + RIVET_SERVE_HOST/ALLOW/MOBILE_DIR \u89E3\u6790 + --host/--mobile-dir + /remote/info \u88C5\u914D + parseHostsAllow fail-open \u4FEE\u590D\uFF0C+64\uFF0C2026-09-12\uFF09 1184\u21921220\uFF1AHEAD 1215 \u6B20\u8D26\u6536\u53E3\uFF08+31\uFF0Cprovider \u70ED\u91CD\u8F7D\u7B49\u4ED6\u4F1A\u8BDD\u5DF2\u843D\u672A\u4E0A\u8D26\uFF09+ sidecar \u63D2\u4EF6\u6696\u573A\u63A5\u7EBF\uFF08warmPluginToolsCache \u8C03\u7528 + import\uFF0C2026-09-12\uFF0C+5\uFF09 1220\u21921268\uFF1A\u684C\u9762\u6027\u80FD\u9636\u6BB5 3 sidecar \u9996\u79D2\uFF081c9bbb0e5\uFF0C2026-09-13\uFF0C+48\uFF09\u2014\u2014[serve-timing] \u5341\u9636\u6BB5 mark + scheduleDeferredWarmup/fireNow/cancel \u63A5\u7EBF + \u5BBF\u4E3B\u63A2\u9488 prewarm \u70B9\u706B + createAgent \u6709\u754C\u7B49\u5F85\uFF1B\u63D0\u4EA4\u65F6\u6F0F\u8BB0\u8D26\u672C\uFF0C\u6B64\u6761\u8865\u8BB0\u30021268\u21921287\uFF1A\u684C\u9762\u6027\u80FD\u9636\u6BB5 4 \u5168\u5C40\u63A8\u9001\u901A\u9053\uFF08+19\uFF0C2026-09-13\uFF09\u2014\u2014ServerEventBus \u6784\u9020/close\u3001onSessionsChanged \u4E0E TaskRegistry.onEvent \u4E24\u6761 publish \u63A5\u7EBF\u3001GET /events \u8DEF\u7531\u6CE8\u518C\uFF1Bhealth \u5FEB\u7167\u6784\u9020\u70B9\u62BD\u5230 health-route.ts\uFF08createHealthSnapshot\uFF09\u4E0E SSE \u5FC3\u8DF3\u5171\u7528\uFF0CloopLag \u56DE\u8C03\u63D0\u4E3A\u5C40\u90E8\u51FD\u6570\u590D\u7528\u3002\u603B\u7EBF/\u8DEF\u7531\u4E3B\u4F53\u5728\u65B0\u6A21\u5757 server-event-bus.ts / server-events-route.ts\uFF0C\u6B64\u5904\u53EA\u5269\u63A5\u7EBF\u3002 1287\u21921292\uFF1A\u751F\u56FE\u69FD\u914D\u7F6E\u843D\u76D8 hook\uFF08issue #8\uFF09\u2014\u2014onImageGenConfigChanged \u63A5\u7EBF\uFF08\u4E0E specReload/onProviderConfigChanged \u540C\u5224\u5B9A\uFF1A\u6CE8\u5165\u5F0F ctx \u4E0D\u63A5\u7EBF\u4EE5\u4FDD\u6301\u6D4B\u8BD5\u786E\u5B9A\u6027\uFF1B\u6CA1\u6709\u5B83\uFF0C\u5B58\u6D3B agent \u7684\u5DE5\u5177\u8868\u4E0D\u5237\u65B0\uFF0C\u300C\u914D\u7F6E\u6210\u529F\u4F46\u5DE5\u5177\u4E0D\u51FA\u73B0\u300D\uFF09\u2014\u2014\u672C\u5730\u7EBF\u539F\u8BB0 ceiling 1225 \u672A\u9644\u8BF4\u660E\u6BB5\uFF0Cmerge \u65F6\u6309\u5408\u5E76\u6001\u5B9E\u9645\u884C\u6570\u5BF9\u9F50\u3002 1292\u21921319\uFF1ASSE \u4F18\u96C5\u5173\u505C\uFF08agent-13 \u4FEE\u590D\uFF09\u2014\u2014sseRegistry \u6784\u9020\u4E0E\u6CE8\u91CA\uFF086\uFF09+ \u4E09\u5904\u8DEF\u7531\u63A5\u7EBF\uFF08prompt deps / session routes deps / events route \u7B2C 4 \u53C2\uFF0C3\uFF09+ finish \u94FE closeAll \u4E0E closeIdleConnections \u53CA\u6CE8\u91CA\uFF0810\uFF09+ shutdownServer \u5E42\u7B49\u4E0E 3s \u4FDD\u9669\u4E1D\u53CA\u6CE8\u91CA\uFF088\uFF0C\u8986\u76D6 SIGINT/SIGTERM/parent-watchdog \u4E09\u6761\u89E6\u53D1\u8DEF\u5F84\uFF09\u3002\u673A\u5236\u4E3B\u4F53\u5728\u65B0\u6A21\u5757 src/server/sse-registry.ts \u4E0E\u5404 route builder \u7684\u6CE8\u518C/\u6CE8\u9500\uFF1B\u672C\u6587\u4EF6\u4E3A\u88C5\u914D\u5C42\u4E0E\u5173\u505C\u7F16\u6392\u3002 1319\u21921323\uFF1A\u4E8B\u4EF6\u5199\u94FE\u6536\u53E3\u6392\u7A7A\uFF08agent-16 \u4FEE\u590D\uFF09\u2014\u2014finish \u8F6C async \u5E76\u5728\u5173\u505C\u524D await persistence.flushAllAsync(3_000)\uFF08flushAllAsync \u6B64\u524D\u65E0\u751F\u4EA7\u8C03\u7528\u70B9\uFF09\uFF0C+4 \u542B\u6CE8\u91CA\u3002 1323\u21921331\uFF1A\u5173\u505C\u4FDD\u9669\u4E1D\u5BF9\u9F50\u8BBE\u8BA1\u5185\u9884\u7B97 + \u4E8C\u6B21\u4FE1\u53F7\u7ACB\u5373\u5F3A\u9000\uFF08387b17178\uFF0C\u8DE8\u4F1A\u8BDD\u843D\u5730\u672A\u4E0A\u8D26\u2014\u2014Wave A \u9A8C\u8BC1\u65F6\u4EE3\u6536\u53E3\uFF0C\u8D26\u76EE\u4F9D\u636E\u8BE5\u63D0\u4EA4\u5B9E\u9645 diff\uFF09\u3002 1331\u21921410\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014resolveModelSpec \u6309\u6A21\u578B\u5F52\u5C5E key \u89E3\u6790\u51ED\u636E\uFF08provider\xD7keyId\xD7model \u4E09\u6BB5\u5F0F\uFF09\uFF08+79\uFF09 1410\u21921414\uFF1A\u8D26\u53F7 device flow \u8DEF\u7531\u63A5\u7EBF\uFF08import + \u6CE8\u91CA + \u6CE8\u518C\u8C03\u7528\u5171 +4\uFF0C2026-09-14\uFF09\u2014\u2014\u8DEF\u7531\u4E3B\u4F53\u3001\u5355\u6B21\u8F6E\u8BE2\u8BED\u4E49\u3001\u51ED\u636E\u4E0D\u51FA sidecar \u4E0E 11 \u6761\u6D4B\u8BD5\u90FD\u5728\u65B0\u6A21\u5757 src/server/account-routes.ts\uFF0C\u6B64\u5904\u53EA\u5269\u63A5\u7EBF\u3002 1414\u21921419\uFF1AMCP stdio \u81EA\u52A8\u91CD\u8FDE\u6062\u590D\u540E\u7684\u5DE5\u5177\u9762\u63A8\u9001\u63A5\u7EBF\uFF08\u63D0\u4EA4 995812ef6\uFF0C+6/-1 \u51C0 +5\uFF0C\u8DE8\u4F1A\u8BDD\u843D\u5730\u672A\u4E0A\u8D26\uFF0C\u672C\u6B21\u4EE3\u6536\u53E3\uFF09\u2014\u2014McpManager \u6784\u9020\u4F20\u5165 onToolsChanged \u56DE\u8C03\uFF08\u81EA\u52A8\u91CD\u8FDE\u6210\u529F \u2192 injectMcpTools \u628A\u6062\u590D\u7684\u5DE5\u5177\u9762\u63A8\u56DE\u5DF2\u6709 live agent \u7684\u4F1A\u8BDD\uFF09\uFF0C\u5BBF\u4E3B\u4FA7\u662F\u4E3B\u52A8\u63A8\u9001\u8BED\u4E49 1419\u21921432\uFF1Aissue #193\u2014\u2014open-file \u6253\u5F00\u300C\u65E0\u5173\u8054\u5904\u7406\u7A0B\u5E8F\u300D\u7684\u6587\u4EF6\u65F6\uFF0CWindows \u4F1A\u5F39\u300C\u9009\u53D6\u5E94\u7528\u300D\u5BF9\u8BDD\u6846\uFF08\u5BBF\u4E3B\u662F shell\uFF0Cdetached \u4E4B\u540E\u8C03\u7528\u65B9\u65E2\u56DE\u6536\u4E0D\u4E86\u4E5F\u5BDF\u89C9\u4E0D\u5230\uFF09\uFF0C\u4E14\u65E7\u4EE3\u7801\u65E0\u8BBA\u6210\u8D25\u90FD\u56DE `opened`\u3002\u8865\u4E00\u6B21\u5224\u5B9A\uFF1A\u65E0\u5904\u7406\u7A0B\u5E8F \u2192 \u9000\u5316\u4E3A explorer /select, \u5B9A\u4F4D + \u5982\u5B9E\u56DE\u62A5\uFF08+12\uFF0C2026-09-18\uFF1B\u672C\u4ED3\u57FA\u7EBF 1420\u2014\u2014harness \u7EBF\u5C11 1 \u884C Pro \u6CE8\u91CA\u5DEE\u5F02\uFF0C\u6536\u7F16 PR #195 \u65F6\u5BF9\u9F50\uFF09\u3002\u5224\u5B9A/\u63A2\u6D4B/\u547D\u4EE4\u9009\u62E9\u7684\u4E3B\u4F53\u5168\u90E8\u843D\u5728 src/tools/open-path.ts\uFF08decideOpenAction / windowsFileHasHandler / isDirectoryPath\uFF1B\u8BE5\u6587\u4EF6\u672A\u70B9\u540D\uFF0C210 \u884C\u8FDC\u4F4E\u4E8E redline 800\uFF0C\u4E0D\u53E6\u7ACB\u6761\u76EE\uFF09\uFF1B\u672C\u6587\u4EF6\u53EA\u7559 `const isDir/action` 6 \u884C\u3001effectiveCommand \u4E09\u6001 4 \u884C\u3001\u8FD4\u56DE\u4F53\u4E09\u6001 2 \u884C\u2014\u2014\u8DEF\u7531\u8868\u4E0E apiToken \u5747\u4E3A\u672C\u6587\u4EF6\u79C1\u6709\uFF0C\u65E0\u63A5\u7F1D\u53EF\u62C6\u3002"
989
+ },
990
+ "src/agent/starflow-orchestrator.ts": {
991
+ ceiling: 1075,
992
+ note: "988\u21921075\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09"
993
+ },
994
+ "src/pro/computer-use/macos-driver.ts": {
995
+ ceiling: 987,
996
+ note: "976\u2192987\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09"
997
+ },
998
+ "src/agent/create-runtime-hooks.ts": {
999
+ ceiling: 991,
1000
+ note: "BASE_PROMPT \u53CD\u8FC7\u5EA6\u63D0\u95EE\u7B49\u63D0\u4EA4\uFF0862e0fb3e9 2026-09-07\uFF09\u6B20\u8D26\u6536\u53E3\u2014\u2014ceiling 976\u2192985 985\u2192991\uFF1Ameridian \u961F\u5217 follow-up \u63A5\u7EBF\uFF08b078f617b +11 / 641c5c9d7 +2\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0"
1001
+ },
1002
+ "src/tools/bash.ts": {
1003
+ ceiling: 1061,
1004
+ note: "943\u2192950\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09 950\u2192995\uFF1Askip \u6863\u6C99\u7BB1\u62D2\u7EDD\u9996\u89E6\u5373\u6388\u5224\u5B9A\uFF08f8bc60ffe \u56DE\u6D41\uFF0C2026-09-07\uFF09 995\u21921000\uFF1Aad-hoc tsc \u7ECF\u8DE8\u8FDB\u7A0B\u9501\u4E32\u884C\u6536\u53E3\uFF08isTypecheckCommand + runTypecheckSerialized \u63A5\u7EBF\uFF0C\u4E3B\u4F53\u5728 src/lsp/typecheck-cache.ts\uFF0C2026-09-12 galaxy \u5E76\u53D1 tsc \u9965\u997F\u4E8B\u6545\u4FEE\u590D\uFF09 1000\u21921037\uFF1Aad-hoc \u56DB\u9879\u6536\u53E3\u5347\u7EA7\uFF08\u5171\u4EAB\u7F13\u5B58\u56DE\u653E + git-toplevel \u9501\u6839 + \u540E\u53F0 job \u6301\u9501 + \u78C1\u76D8\u6C34\u4F4D\u8B66\u544A\u524D\u7F00\uFF0C\u4E3B\u4F53\u5728 typecheck-cache.ts \u4E0E src/utils/disk-space.ts\uFF0C2026-09-12\uFF09 1037\u21921042\uFF1A\u654F\u611F\u6587\u4EF6\u95E8\u8865\u9F50\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #141\uFF0C+5 \u51C0\uFF09\u2014\u2014SAFE_ENV_PREFIXES \u6458\u9664 NODE_OPTIONS/JAVA_TOOL_OPTIONS\uFF08--require/-javaagent \u5B50\u8FDB\u7A0B\u6CE8\u5165\u9762\uFF0C+3 \u884C\u6CE8\u91CA\u8BF4\u660E\uFF09\uFF0CisSensitiveGitAdd \u6539\u7528\u805A\u5408\u54E8\u5175\u8FC7\u6EE4\uFF08.some(h => h !== AGGREGATE_ADD_MARKER)\uFF0Cgit add ./-A/--all \u4E0D\u518D\u8D70\u5BA1\u6279\u786C\u95E8\u3001\u6539\u7531 assessToolRisk \u8BB0 medium \u7406\u7531\uFF09\u3002 1042\u21921051\uFF1Abash-reread key \u9000\u5316\u4FEE\u590D\uFF08\u53F0\u8D26 F1\uFF09\u2014\u2014\u65B0\u589E looksLikeFilePath \u5F62\u6001\u6821\u9A8C\uFF08\u62D2\u7EDD\u7BA1\u9053\u8FC7\u6EE4\u5668\u53C2\u6570\u5165 key\uFF1A'\u2714'\u3001-N \u7B49\u7EAF\u7B26\u53F7/\u9009\u9879\u5F62\u6001\uFF1B\u771F\u91CD\u590D\u6355\u83B7\u4FDD\u771F\uFF0C12/12 \u6D4B\u8BD5\uFF09\uFF0C+9 \u542B\u6CE8\u91CA\u3002 1051\u21921055\uFF1Abash \u5DE5\u5177\u5BF9 typecheck \u5F62\u6001\u58F0\u660E\u5339\u914D\u95F8\u95E8\u7684\u5DE5\u5177\u7EA7\u8D85\u65F6\uFF08+4\uFF0C2026-09-14\uFF09\u2014\u2014BASH_TOOL.timeoutMs \u590D\u7528 isTypecheckCommand \u5224\u5B9A\uFF1Atypecheck \u5F62\u6001\u7ED9 TYPECHECK_CALLER_BUDGET_MS\uFF08\u95F8\u95E8 10 \u5206\u949F\u7B49\u5F85\u4E0A\u9650 + 3 \u5206\u949F\u7ED9 tsc\uFF09\uFF0C\u5176\u4F59\u7EF4\u6301 Tool \u9ED8\u8BA4 120s\u3002\u6B64\u524D\u7528\u9ED8\u8BA4 120s \u53BB\u5957\u4E00\u6761\u7B49\u5F85\u4E0A\u9650 10 \u5206\u949F\u7684\u95F8\u95E8\u8DEF\u5F84\uFF0C\u5E76\u53D1 4~5 \u8DEF\u65F6\u6700\u540E\u4E00\u4E2A\u7B49\u5F85\u8005\u5FC5\u7136 [tool-timeout]\uFF08\u7F13\u5B58\u8BB0\u5F55\u5B9E\u6D4B\uFF1A\u5E76\u53D1\u671F\u5355\u6B21\u5B9E\u8DD1\u7531 15.5s \u52A3\u5316\u5230 41.0s\uFF09\u30021055\u21921061\uFF1Amanual \u6863\u5BA1\u6279\u95E8\u6FC0\u6D3B INJECTION \u6E05\u5355 + \u63A5 matchesDangerousBash \u53CC\u89C6\u56FE\uFF082026-09-17 \u5BA1\u8BA1\u2014\u2014INJECTION \u6B64\u524D\u53EA\u5728 auto-safe \u6863\u88AB\u6D88\u8D39\u3001manual \u6863\u6B7B\u4EE3\u7801\uFF1B\u5F52\u4E00\u5316\u89C6\u56FE\u6CBB ${IFS}/\u7EED\u884C/\u5B57\u7B26\u7EA7\u8F6C\u4E49/\u5F15\u53F7\u62FC\u63A5\u7684\u5224\u5B9A\u5931\u6548\uFF0C\u4E3B\u4F53\u5728 approval-risk.ts \u7684 normalizeBashCommand/testBoth\uFF0C\u672C\u6587\u4EF6 +6 \u542B\u6CE8\u91CA\uFF09\u3002"
1005
+ },
1006
+ "src/skills/skill-loader.ts": {
1007
+ ceiling: 1040,
1008
+ note: "941\u21921022\uFF1A\u6346\u7ED1\u6280\u80FD\u9000\u5F79\u6E05\u7406\u2014\u2014RETIRED_BUNDLED_SKILLS \u540D\u5355\uFF08\u540D\u5B57+SHA-256\uFF09+ retireMatchingSkillCopies \u4E00\u6B21\u6027\u6E05\u7406\u903B\u8F91\uFF08\u64A4\u51FA\u4E09\u9879 + \u6D88\u878D\u4E24\u9879\u7684\u5B58\u91CF\u526F\u672C\u6E05\u7406\uFF0C+81\uFF09\u3002 1022\u21921040\uFF1A.agents/skills \u53CC\u69FD\u4F4D\u81EA\u52A8\u626B\u63CF + \u5185\u5EFA\u673A\u5236\u6587\u6863\u540C\u6B65\uFF08issue #100\uFF0C2026-09-12\uFF09"
1009
+ },
1010
+ "src/config/env-registry.ts": {
1011
+ ceiling: 1262,
1012
+ note: "1244\u21921232\uFF1A\u884C\u6570\u5BF9\u9F50\u5B9E\u9645\uFF08-12\uFF09\u3002 944\u2192956\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09956\u2192962\uFF1ARIVET_ORCHESTRATION_HINT \u767B\u8BB0\uFF08+6\uFF09962\u2192968\uFF1ARIVET_ADAPTIVE_MEMORY \u7684 shadow/on/off \u53D1\u5E03\u5F00\u5173\u767B\u8BB0\uFF08+6\uFF09968\u2192974\uFF1ARIVET_MEMORY_AUTO_CAPTURE \u767B\u8BB0\uFF08+6\uFF09974\u2192980\uFF1ARIVET_MEMORY_CONSOLIDATION \u767B\u8BB0\uFF08+6\uFF09980\u2192986\uFF1ARIVET_MEMORY_BACKFILL \u767B\u8BB0\uFF08+6\uFF0C\u5386\u53F2\u4F1A\u8BDD\u56DE\u586B opt-in \u5F00\u5173\uFF09\u3002 986\u21921040\uFF1Agen-env-registry \u91CD\u751F\u6210\u6536\u53E3\uFF08153\u2192169 \u6761\uFF0C\u542B 8 \u4E2A\u5B58\u91CF\u6F0F\u6CE8\u518C RIVET_APPROVAL_TIMEOUT_MS/RIVET_CPU_POOL_IDLE_MS/RIVET_FETCH_SKIP_COMPILE/RIVET_PROFILE/RIVET_TEST_ENV_KEY_PROV/RIVET_TRUST_PROJECT/RIVET_WELCOME_ANIM/RIVET_WORKER_ISOLATION \u4E0E\u65B0\u589E RIVET_WELCOME_GREETING\uFF1B\u81EA\u52A8\u751F\u6210\u6587\u4EF6\u968F\u6CE8\u518C\u6570\u673A\u68B0\u589E\u957F\uFF0C2026-09-09\uFF09 1040\u21921064\uFF1Agen-env-registry \u91CD\u751F\u6210\uFF08169\u2192173 \u6761\uFF1ARIVET_MOBILE_DIR/RIVET_SERVE_HOST/RIVET_SERVE_HOSTS_ALLOW \u767B\u8BB0 + \u5B58\u91CF\u6F0F\u6CE8\u518C 1 \u6761\uFF0C2026-09-12 Mobile Remote \u56DE\u6D41\uFF09 1064\u21921070\uFF1Agen-env-registry \u91CD\u751F\u6210\uFF08173\u2192174 \u6761\uFF1ARIVET_DISK_WARN_MB \u767B\u8BB0\u2014\u2014src/utils/disk-space.ts \u7684\u78C1\u76D8\u4F4E\u6C34\u4F4D\u9608\u503C\u8986\u76D6\uFF0C0=\u5173\u95ED\u8B66\u544A\uFF1B\u53E6\u5237\u65B0 files \u7D22\u5F15\uFF1ARIVET_SESSION_DIR \u8865 agent/__tests__/session-cd-rebuild-stores.test.ts\u3001RIVET_TYPECHECK_SHARE \u8865 tools/bash.ts\uFF09\u3002 1070\u21921076\uFF1A\u684C\u9762\u6027\u80FD\u9636\u6BB5 3 RIVET_SERVE_TIMING + RIVET_SERVE_WARM_DELAY_MS \u767B\u8BB0\uFF081c9bbb0e5\uFF0C+6\uFF0C2026-09-13\uFF1B\u63D0\u4EA4\u65F6\u6F0F\u8BB0\u8D26\u672C\uFF0C\u6B64\u6761\u8865\u8BB0\uFF09\u3002 1076\u21921082\uFF1Amerge origin/main \u5BF9\u9F50\u2014\u2014\u8FDC\u7AEF\u7EBF\u53E6\u8BB0\u4E00\u6BB5 1064\u21921070\u300C\u6B20\u8D26\u6536\u53E3\uFF08+6\uFF0C2026-09-12~13 \u95F4\u4ED6\u4F1A\u8BDD\u767B\u8BB0\u672A\u4E0A\u8D26\uFF09\u300D\uFF0C\u4E0E\u672C\u5730\u7EBF 1064\u21921070 \u7684 RIVET_DISK_WARN_MB \u767B\u8BB0\u4E3A\u4E24\u7B14\u72EC\u7ACB\u51C0\u589E\uFF08\u81EA\u52A8\u751F\u6210\u6587\u4EF6\u968F\u6CE8\u518C\u6761\u76EE\u673A\u68B0\u53E0\u52A0\uFF0C\u5408\u8BA1 1064\u21921082\uFF09\u3002 1082\u21921094\uFF1Agen-env-registry \u91CD\u751F\u6210\uFF08178 \u6761\uFF0C\u4EFB\u52A1 4/7 \u6253\u65AD\u7559\u75D5\uFF09\u2014\u2014RIVET_INTERRUPT_MARKER \u767B\u8BB0\uFF08+6\uFF09+ \u6536\u53E3\u5B58\u91CF\u6B20\u8D26 RIVET_ISSUE_\uFF08+6\uFF0C7e897bae0 \u7684 windows-smoke \u73AF\u5883\u95E8\u63A7\u5F15\u7528\u672A\u4E0A\u8D26\uFF1B\u8BE5\u6761\u76EE\u540D\u56E0\u751F\u6210\u5668\u6B63\u5219 `[A-Z_]+` \u4E0D\u542B\u6570\u5B57\u800C\u4ECE RIVET_ISSUE_144 \u622A\u65AD\u2014\u2014\u751F\u6210\u5668\u4E0E assembly-audit \u540C\u6B3E\u6B63\u5219\u3001\u540C\u6B3E\u622A\u65AD\uFF0C\u884C\u4E3A\u4E00\u81F4\uFF09\u3002\u53E6\u5237\u65B0 RIVET_DEBUG \u7B49\u6761\u76EE files \u7D22\u5F15\uFF08\u65E0\u884C\u6570\u589E\u91CF\uFF09\u3002 1094\u21921100\uFF1Aenv-registry \u8865\u767B\u8BB0 RIVET_MCP_LIVE\uFF08MCP \u4F1A\u8BDD\u843D\u7801\u672A\u8DD1\u751F\u6210\u5668\uFF09\u2014\u2014\u751F\u6210\u5668\u91CD\u8DD1\u4EA7\u51FA\u7684\u6761\u76EE\u884C\uFF08+6\uFF09\uFF0C1e5f4aa2f \u4EE3\u6536\u53E3\u3002 1100\u21921112\uFF1ACLI \u8D26\u53F7 device flow\uFF08src/auth/account.ts\uFF0C\u65B0\u589E\uFF09\u767B\u8BB0 RIVET_ACCOUNT_API / RIVET_ACCOUNT_ANON_KEY \u4E24\u4E2A\u8986\u76D6\u70B9\uFF08+12\uFF0C2026-09-14\uFF09\u3002\u672C\u6587\u4EF6\u662F\u81EA\u52A8\u751F\u6210\u7269\uFF08scripts/gen-env-registry.ts \u6309\u6E90\u7801 RIVET_* \u5F15\u7528\u4EA7\u51FA\uFF09\uFF0C\u884C\u6570 = \u6761\u76EE\u6570 \xD7 6\uFF0C\u65E0\u63A5\u7F1D\u53EF\u62C6\u2014\u2014\u589E\u957F\u4E0E\u6CE8\u518C\u6570\u673A\u68B0\u540C\u6B65\uFF0C\u6545 ceiling \u6309\u672C\u4ED3\u60EF\u4F8B\u5BF9\u9F50\u5B9E\u9645\u503C\u3002 1118\u21921244\uFF1A\u62EC\u53F7\u5F62\u5F0F\u8BC6\u522B\u8865\u9F50\uFF08+126\uFF0C\u6761\u76EE 186\u2192203\uFF09\u2014\u2014\u751F\u6210\u5668\u4E0E assembly-audit \u7684\u626B\u63CF\u6B63\u5219\u6B64\u524D\u53EA\u8BA4 `process.env.RIVET_X` \u70B9\u53F7\u5F62\u5F0F\uFF0C\u7528 `process.env['RIVET_X']` \u5199\u6CD5\u7684\u53D8\u91CF\u65E2\u6F0F\u767B\u8BB0\u3001\u53C8\u53CD\u5411\u89E6\u53D1\u300Cregistry \u6709\u6761\u76EE\u4F46\u6E90\u7801\u65E0\u5F15\u7528\u300D\u5047\u7EA2\u3002\u4E24\u5904\u8865\u9F50 bracket pattern \u540E\u91CD\u8DD1\u751F\u6210\u5668\uFF0C\u5B58\u91CF\u6F0F\u767B\u8BB0\u4E00\u6B21\u6027\u5165\u8D26\uFF08RIVET_GIT_BASH_PATH / RIVET_PLAYBOOK / RIVET_TERSE / RIVET_MAX_WORKERS \u7B49\uFF0C\u4ED3\u5E93\u5185\u62EC\u53F7\u5199\u6CD5\u672C\u8EAB\u6709\u6570\u5341\u5904\uFF09\uFF1B\u51C0 +17 \u6761 = \u65B0\u589E\u771F\u5B9E\u6761\u76EE - \u53CD\u8BC1\u7528\u4F8B\u81EA\u9020\u6837\u672C\u540D\u56DE\u6536\u3002\u81EA\u52A8\u751F\u6210\u7269\uFF0C\u65E0\u63A5\u7F1D\u53EF\u62C6\u3002 1232\u21921262\uFF1Agen-env-registry \u91CD\u751F\u6210\uFF08204\u2192206 \u6761\uFF09\u2014\u2014\u767B\u8BB0\u5916\u90E8\u4F1A\u8BDD\u53D1\u73B0\u5F00\u5173 RIVET_EXTERNAL_SCAN_MS\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #205\uFF09\u4E0E\u5B58\u91CF\u6F0F\u6CE8\u518C\u7684 RIVET_JOB_LAUNCHER\uFF08tools/process-kill.ts\uFF09\uFF0C\u5E76\u968F\u6E90\u7801\u5237\u65B0 RIVET_HOME / RIVET_TOKEN_STORE \u7684\u5F15\u7528\u7D22\u5F15\u3002\u672C\u6587\u4EF6\u4E3A\u81EA\u52A8\u751F\u6210\u7269\uFF0C\u884C\u6570\u968F\u6761\u76EE\u6570\u4E0E\u5F15\u7528\u9762\u673A\u68B0\u53D8\u5316\uFF0Cceiling \u6309\u672C\u4ED3\u60EF\u4F8B\u5BF9\u9F50\u5B9E\u9645\u503C\u3002"
1013
+ },
1014
+ "src/server/config-routes.ts": {
1015
+ ceiling: 1360,
1016
+ note: "config-routes \u589E\u957F\uFF1AonApprovalConfigChanged hook \u9002\u914D\uFF0893956f5e2 \u56DE\u6D41 2026-09-07\uFF09\u2014\u2014ceiling 1055\u21921065 1065\u21921074\uFF1ADELETE key \u7AEF\u70B9\uFF08947f8b6e1\uFF0C+15 \u672A\u6536\u53E3\u6B20\u8D26\uFF0C2026-09-07 \u5BA1\u67E5\u8865\u8BB0\uFF09 1074\u21921118\uFF1APOST /config/providers/test completion \u7EA7\u771F\u6D4B\u7AEF\u70B9\uFF087f0cc7c1c\uFF0C+44 \u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\uFF09 1118\u21921117\uFF1Atest-key/test \u89E3\u6790\u94FE\u62BD\u5171\u4EAB resolveProviderProbeTarget\uFF084cdff0bd9\uFF0C-1 \u56DE\u6536\uFF09\u3002ceiling \u5BF9\u9F50\u5B9E\u9645 1117\u3002 1117\u21921136\uFF1A\u5B58\u91CF\u6F02\u79FB\u6536\u53E3\uFF082026-09-09 \u7ED3\u6784\u95E8\u7981\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF0C\u975E\u672C PR \u5F15\u5165\uFF09 1136\u21921143\uFF1Aprovider retry/maxRetries \u56DE\u663E\u63A5\u7EBF\uFF08issue #75 \u684C\u9762\u7AEF\u914D\u7F6E\u9762\u677F\uFF1AProviderListItem +2 \u5B57\u6BB5\u3001GET \u7EC4\u88C5 +2 \u884C\u3001\u7C7B\u578B import +1\uFF0C\u5171 +7\uFF09 1143\u21921296\uFF1A\u751F\u56FE\u6A21\u578B\u914D\u7F6E\u9762\uFF08issue #8 Wave 4\uFF09\u2014\u2014\u5176\u4E2D 1143\u21921166 \u4E3A\u5408\u5165\u524D\u5DF2\u5B58\u5728\u7684 23 \u884C\u672A\u4E0A\u8D26\u6F02\u79FB\uFF08\u975E\u672C\u6B21\u5F15\u5165\uFF09\uFF0C1166\u21921296 \u4E3A\u672C\u6B21\u65B0\u589E +130\uFF1A4 \u6761\u8DEF\u7531\uFF08GET/PUT /config/image-gen-model + POST /test \u4E0E /onboard\uFF09+ parseImageGenRequest \u51ED\u636E\u89E3\u6790\uFF08apiKey/apiKeyEnv \u4E92\u65A5\u5224\u636E + sizeField \u900F\u4F20\uFF09\u3002\u4E1A\u52A1\u903B\u8F91\u4E0D\u5728\u672C\u6587\u4EF6\u2014\u2014\u6587\u751F\u56FE\u5BA2\u6237\u7AEF\u5728 src/api/image-gen-client.ts\u3001\u69FD\u8BFB\u5199\u4E0E\u6CE8\u518C\u5668\u5728 src/config/image-gen-model.ts\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 withAuth \u8584\u58F3\u4E0E\u53C2\u6570\u89E3\u6790\uFF0C\u4E0E\u8BC6\u56FE\u94FE\u8DEF\u7684 parseVisionCredentials/validateVisionModel \u5206\u5DE5\u540C\u6784\u3002 1296\u21921303\uFF1A\u751F\u56FE\u914D\u7F6E\u843D\u76D8\u540E\u7684\u5E7F\u64AD hook\uFF08issue #8 \u95ED\u73AF\uFF09\u2014\u2014ConfigRouteHooks \u52A0 onImageGenConfigChanged \u5B9A\u4E49 + GET/PUT/onboard \u4E09\u6761\u8DEF\u7531\u4E2D\u4E24\u6761\u5199\u8DEF\u5F84\u7684\u89E6\u53D1\uFF08\u4E0E\u65E2\u6709 onApprovalConfigChanged \u540C\u5C5E\u300C\u843D\u76D8\u540E\u5BF9\u5B58\u6D3B agent \u5E7F\u64AD\u300D\u6A21\u5F0F\uFF1B\u6CA1\u6709\u5B83\uFF0C\u6CE8\u518C\u5B8C\u751F\u56FE provider \u7684\u5F53\u524D\u4F1A\u8BDD\u770B\u4E0D\u5230 generate_image\uFF09\u3002 1303\u21921340\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014test-key/test \u5171\u7528 resolveProviderProbeTarget\uFF08allowKeyless\uFF09+ match-models \u7AEF\u70B9 + ProviderListItem.keys \u900F\u4F20\uFF08+37\uFF09 1340\u21921346\uFF1Azen \u663E\u5F0F\u5F00\u5173\u8DEF\u7531\uFF082026-09-14\uFF09\u2014\u2014GET/PUT /config/zen \u7684\u8DEF\u7531\u672C\u4F53\u5DF2\u6309\u63A5\u7F1D\u5916\u63D0\u5230\u65B0\u6A21\u5757 src/server/config-routes-zen.ts\uFF08\u540C config-routes-keys.ts \u7684\u591A key \u6C60\u5148\u4F8B\uFF0C\u4EE5 spread \u63A5\u5165\uFF09\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 +1 import\u3001+1 spread \u8C03\u7528\u4E0E +4 \u884C\u8DEF\u7531\u6E05\u5355\u6CE8\u91CA\uFF08\u542B\u6BB5\u6CE8\u91CA\uFF09\uFF1BBearer \u5B88\u536B\u3001enabled \u7C7B\u578B\u6821\u9A8C\u4E0E 400 \u8BED\u4E49\u90FD\u5728\u65B0\u6A21\u5757\u4E0E config/zen-config.ts\u3002 1346\u21921348\uFF1Aissue #147 \u5DE5\u4F5C\u533A\u7B56\u7565\u8DEF\u7531\u6309\u540C\u4E00\u63A5\u7F1D\u5916\u63D0\u5230 src/server/workspace-route.ts\uFF08buildWorkspaceRoutes\uFF09\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 +1 import \u4E0E +1 spread \u63A5\u7EBF\uFF1B\u8DEF\u7531\u672C\u4F53\u3001Bearer \u5B88\u536B\u3001workspace \u8BFB\u5199\u90FD\u5728\u65B0\u6A21\u5757\u3002 1348\u21921360\uFF08merge \u5408\u8D26 PR #13\uFF09\uFF1A\u300C\u6D4B\u8BD5\u6A21\u578B\u8C03\u7528\u300D\u5BF9 A\u2032 keys \u6C60 provider 400 \u4FEE\u590D\uFF08d6529e8d2\uFF09\u2014\u2014resolveProviderProbeTarget \u8865 keys[0] \u51ED\u636E\u56DE\u9000\uFF08+11 \u51C0\u589E\uFF09\uFF0C\u63A2\u6D4B\u89E3\u6790\u5C5E test/test-key \u5171\u7528\u94FE\uFF0C\u968F\u672C\u6587\u4EF6\u7EF4\u62A4\uFF1Bkeys \u6C60\u6570\u636E\u64CD\u4F5C\u5728 config-routes-keys.ts\u3002\u4E0E\u4E0A\u6E38 1340\u21921348\uFF08zen + #147 \u4E24\u5904\u5916\u63D0\uFF09\u5408\u5E76\u540E\u5BF9\u9F50\u5B9E\u9645 1360\u3002"
1017
+ },
1018
+ "src/pro/computer-use/cdp/driver.ts": {
1019
+ ceiling: 899
1020
+ },
1021
+ "src/pro/computer-use/tool.ts": {
1022
+ ceiling: 891
1023
+ },
1024
+ "src/pro/computer-use/windows-uia-com.ts": {
1025
+ ceiling: 888
1026
+ },
1027
+ "src/config/schema.ts": {
1028
+ ceiling: 1109,
1029
+ note: "1108\u21921103\uFF1A\u884C\u6570\u5BF9\u9F50\u5B9E\u9645\uFF08-5\uFF09\u3002 1004\u21921025\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09\uFF1Bissue #75 \u7684 retry \u5B50 schema \u62C6\u81F3 src/config/retry-schema.ts \u65B0\u6A21\u5757\uFF08-37 \u884C\uFF0C\u5B9E\u6D4B 1022\uFF0Cceiling \u672A\u52A8\u2014\u2014\u4F59\u91CF\u7559\u7ED9\u5E76\u53D1\u7684\u914D\u7F6E\u9762\u53D8\u66F4\uFF09\u30021025\u21921027\uFF1A\u6536\u7F16\u516C\u5F00\u4ED3 PR #108 \u7684\u673A\u5236\u90E8\u5206\u2014\u2014modelConfigSchema \u589E deprecated/deprecationNote \u4E24\u4E2A\u53EF\u9009\u5B57\u6BB5\uFF08issue #105 \u5E2D\u4F4D\u6A21\u578B\u900F\u660E\u5316 + \u5F03\u7528\u544A\u8B66\uFF09\uFF0C\u5B9E\u6D4B 1027\uFF0Cceiling \u5BF9\u9F50\u5B9E\u9645\uFF08\u96F6\u7F13\u51B2\uFF09\u30021027\u21921041\uFF1A\u751F\u56FE\u6A21\u578B\u914D\u7F6E\u69FD\u6302\u8F7D\uFF08issue #8\uFF09\u2014\u2014\u69FD\u5B9A\u4E49\u4E3B\u4F53\u62C6\u5728 src/config/image-gen-schema.ts \u65B0\u6A21\u5757\uFF08\u540C retry-schema \u5148\u4F8B\uFF09\uFF0C\u672C\u6587\u4EF6\u4EC5 +1 \u884C import \u4E0E +5 \u884C\u6302\u8F7D\uFF08\u6CE8\u91CA + \u5B57\u6BB5\uFF09\u3002 1041\u21921044\uFF1A\u6253\u65AD\u7559\u75D5\u5F00\u5173\uFF08\u4EFB\u52A1 4/7\uFF0C2026-09-13\uFF09\u2014\u2014agent.interruptMarker z.boolean().default(true)\uFF08\u6CE8\u91CA 2 \u884C + \u5B57\u6BB5 1 \u884C\uFF09\uFF1B\u5F00\u5173\u89E3\u6790\u8BED\u4E49\u5728 src/config/interrupt-marker-config.ts \u65B0\u6A21\u5757\u3002 1044\u21921077\uFF1Azen \u56DE\u6D41 Wave 1\uFF082026-09-13\uFF09\u2014\u2014tools \u6BB5\u65B0\u589E zen \u5B50 schema\uFF08enabled / face / faceMode / timeoutSteps / triage / appendixLean\uFF09\u3002strict() \u8BA9\u672A\u77E5\u952E\u4E0E\u7C7B\u578B\u9519\u8BEF\u5728\u52A0\u8F7D\u671F\u629B\u9519\uFF0C\u5426\u5219 resolveZenConfig \u7684\u672A\u77E5\u952E\u68C0\u67E5\u662F\u6B7B\u4EE3\u7801\uFF1B\u672A\u914D\u7F6E\u65F6 tools.zen \u4E3A undefined\uFF0C\u96F6\u884C\u4E3A\u9762\uFF08\u9ED8\u8BA4\u5173\uFF09\u3002+33 \u884C\u3002 1077\u21921099\uFF1APR #9-#12 \u5408\u5165\uFF08provider \u591A key \u5168\u6808\uFF0C2026-09-13\uFF09\u2014\u2014providerKeySchema + provider.keys[] \u5B57\u6BB5\uFF08\u9876\u5C42\u4E09\u69FD\u4FDD\u7559\u4E3A keys[0] \u517C\u5BB9\u955C\u50CF\uFF09\uFF08+22\uFF09 1102\u21921104\uFF1Aissue #147 \u5DE5\u4F5C\u533A\u7B56\u7565\uFF08\u9ED8\u8BA4\u5DE5\u4F5C\u533A / \u4E34\u65F6\u4F1A\u8BDD\u9694\u79BB\u76EE\u5F55\uFF09\u2014\u2014schema \u4E3B\u4F53\u5DF2\u6309\u63A5\u7F1D\u5916\u63D0\u5230 src/config/workspace-schema.ts \u65B0\u6A21\u5757\uFF08\u540C retry-schema / image-gen-schema \u5148\u4F8B\uFF09\uFF0C\u672C\u6587\u4EF6\u53EA\u7559 +1 import\u3001configSchema \u6302\u8F7D\u5B57\u6BB5 +1\u3001Config \u7C7B\u578B\u5B57\u6BB5 +1\uFF08+3/-1 \u5B9E\u6D4B 1104\uFF09\u3002 1104\u21921106\uFF1AmodelConfigSchema \u5254\u9664 alias \u5B57\u6BB5\u58F0\u660E\uFF08\u6536\u7F16\u516C\u5F00\u4ED3 PR #154\uFF0C+2\uFF09\u3002 1106\u21921109\uFF1Alicense Pro \u95E8\u52A0\u56FA\uFF08f0861df12\uFF09\u2014\u2014schema \u589E Pro \u51ED\u8BC1\u76F8\u5173\u5B57\u6BB5\uFF0C+6 \u884C\u672A\u968F\u8BE5\u63D0\u4EA4\u540C\u6B65\u8D26\u672C\uFF0C\u672C\u6B21\u8865\u8BB0\u5E76\u5BF9\u9F50\u5B9E\u9645\uFF08\u5B9E\u6D4B 1109\uFF1B4fa0e87d5 \u5728\u672C\u6587\u4EF6\u53EA\u662F +6/-6 \u7B49\u91CF\u66FF\u6362\uFF0C\u975E\u6B20\u8D26\u6765\u6E90\uFF09\u3002"
1030
+ },
1031
+ "src/agent/session-persist.ts": {
1032
+ ceiling: 928,
1033
+ note: "870\u2192876\uFF1A2026-09-08 \u6954\u6B7B\u65CF\u540E\u7EED\u4FEE\u590D\uFF080e792e26e\uFF0C+8/-2\uFF1A\u5DE5\u5177\u603B\u95F8/\u7EED\u8DD1\u964D\u7EA7/\u78C1\u76D8\u5BF9\u8D26/loop-lag \u5F52\u56E0\u4E2D\u7684\u6301\u4E45\u5316\u4FA7\u6539\u52A8\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\uFF0Cceiling \u5BF9\u9F50\u5B9E\u9645\u3002 876\u2192907\uFF1A\u4F1A\u8BDD\u5217\u8868\u7F13\u5B58\u6539\u589E\u91CF upsert\uFF08PR #155\uFF0C2026-09-15\uFF09\u2014\u2014\u6B64\u524D writeMetadata/updateMetadata \u6BCF\u6B21 append \u90FD invalidateListCache\uFF0C\u800C loadPrevHandoff \u6BCF\u4E2A LLM \u8F6E\u8BFB\u5217\u8868\uFF0C\u6D3B\u8DC3\u4F1A\u8BDD\u7F13\u5B58\u547D\u4E2D\u6052 0\uFF08\u5B9E\u6D4B 50 \u8F6E\u5DE5\u5177\u6D41 71ms\u21921.86ms\uFF0C38\xD7\uFF09\u3002\u65B0\u589E refreshListCacheEntry\uFF08\u5B9E\u4F8B\u65B9\u6CD5\uFF0C\u8BFB\u79C1\u6709 metaStore\uFF09+ buildListEntry\uFF08\u5168\u91CF/\u589E\u91CF\u4E24\u8DEF\u7EDF\u4E00\u6761\u76EE\u5F62\u72B6\uFF0C\u9632\u6F02\u79FB\uFF09+ upsertListCacheEntry\uFF08\u539F\u4F4D\u5E76\u5165\u4E0D\u6362\u6570\u7EC4\u4E0D\u5237 ts\uFF0C\u6052\u7B49\u5951\u7EA6\u4E0E TTL \u8BED\u4E49\u4E0D\u53D8\uFF09\u3002\u7F13\u5B58 Map \u662F private static \u4E14\u4E0E TTL/\u6052\u7B49\u5951\u7EA6\u5F3A\u8026\u5408\uFF0C\u62C6\u51FA\u9700\u8FDE\u5E26\u642C\u79FB\u8BFB\u8DEF\u5F84\uFF0C\u672C\u6279\u4ECE\u7B80\u968F\u503A\u5BF9\u9F50\uFF1B\u540E\u7EED\u53EF\u6574\u4F53\u5916\u79FB session-list-cache \u72EC\u7ACB\u6A21\u5757\u5C4A\u65F6\u5220\u6761\u76EE\u3002 907\u2192928\uFF1A\u786C\u5220\u94FE\u843D\u76D8\u6E05\u7406\uFF082026-09-16 \u5BA1\u67E5\u4FEE\u590D\uFF09\u2014\u2014\u65B0\u589E\u5BFC\u51FA deleteSessionFiles(cwd,id) + \u79C1\u6709 removeSessionFilesIn(dir,id)\uFF1A\u5220 transcript/meta/memory/claims/frozen + \u540C\u540D\u5B50\u76EE\u5F55\u5E76\u79FB\u9664\u5217\u8868\u7F13\u5B58\u6761\u76EE\uFF08removeListCacheEntry\uFF09\uFF1BevictOldSessionsInternal \u539F\u5185\u8054\u6E05\u7406\u91CD\u6784\u4E3A\u5171\u7528 helper\uFF08\u51C0 -6\uFF0C\u4E24\u5904\u6E05\u7406\u9762\u4E0D\u518D\u6F02\u79FB\uFF09\u3002\u6B64\u524D\u786C\u5220\u53EA\u6E05 events \u5B50\u76EE\u5F55\u4E0E\u5185\u5B58 record\uFF0C\u843D\u76D8\u6B8B\u7559\u8BA9 listSessionsWithMetadata \u5168\u91CF\u91CD\u5EFA\u540E\u4ECD\u5217\u51FA\u5DF2\u5220\u4F1A\u8BDD\uFF08\u63A2\u9488\u5B9E\u6D4B\uFF09\u3002"
1034
+ },
1035
+ "src/repo/meridian-db.ts": {
1036
+ ceiling: 952,
1037
+ note: "797\u2192884\uFF1Aschema v2 \u9065\u6D4B\u4FDD\u7559\u673A\u5236\uFF08migrateToV2/cleanupExpiredRows/WAL checkpoint/LIKE \u524D\u7F00\u7D22\u5F15\uFF0C2026-08-17 \u5B58\u50A8\u81A8\u80C0\u4FEE\u590D\uFF09\u3002 884\u2192903\uFF1A\u8D26\u672C\u5BF9\u9F50\u5B9E\u9645\u884C\u6570\uFF08\u591A\u4F1A\u8BDD\u529F\u80FD\u589E\u957F\u6B20\u8D26\u6536\u53E3\uFF0C2026-08-25\uFF09 903\u2192912\uFF1AP1-2 hasFiles \u51B7\u5E93\u63A2\u6D4B\uFF08LIMIT 1\uFF0C2026-09-07 \u5BA1\u67E5\u4FEE\u590D\uFF09 912\u2192946\uFF1A\u540C\u6B65\u7D22\u5F15\u963B\u585E\u4E8B\u4EF6\u5FAA\u73AF\u56DB\u9879\u4FEE\u590D\uFF08641c5c9d7\uFF0C+34\uFF1Aread \u5EC9\u4EF7\u63A2\u9488\u5165\u961F/write \u53BB\u6296/\u6279\u91CF\u7B26\u53F7\u67E5\u8BE2/\u6279\u95F4\u8BA9\u51FA\uFF09\u843D\u5730\u65F6\u672A\u4E0A\u8D26\uFF0C2026-09-09 \u5BA1\u67E5\u8865\u8BB0\u3002 946\u2192952\uFF1AGLOB \u524D\u7F00 JS \u6574\u4F53\u7ED1\u5B9A\u4FEE\u590D\u5168\u8868\u626B\u63CF\uFF08slow-sync-stage meridian-impact 4s \u4E8B\u6545\u5B9E\u6D4B\u6839\u4FEE\uFF0C2026-09-12\uFF0C+6 \u6CE8\u91CA\uFF09"
1038
+ },
1039
+ "src/tui/settings-model.ts": {
1040
+ ceiling: 945,
1041
+ note: "865\u2192945\uFF1A\u5B50\u4EE3\u7406\u8DEF\u7531\u5F15\u5BFC\u4E09\u4EF6\u5957\u2014\u2014findRecommendedRoutingTarget/applyRoutingRecommendation \u7EAF\u51FD\u6570 + workers \u7C7B\u76EE\u4E00\u952E\u63A8\u8350 action \u5B57\u6BB5 + workers/review \u6761\u76EE\u5B58\u5728\u6027\u8B66\u544A hint\uFF08routingTargetResolves\uFF09\uFF0C\u7C7B\u76EE\u7B7E\u540D\u52A0 env\uFF08+76\uFF09\u3002"
1042
+ },
1043
+ "src/tui/connect-flow.ts": {
1044
+ ceiling: 2166,
1045
+ note: "PR#38 /connect \u5411\u5BFC\uFF082160 \u884C\uFF0C2026-08-22 \u5408\u5165\uFF09\u2014\u2014\u5165\u8D26\u9489\u4F4F\uFF0C\u62C6\u5206\u5F85\u540E\u7EED 2160\u21922166\uFF1AConnectCommit.needsLogin \u6807\u8BB0 + oauth \u5206\u652F\uFF08+6\uFF09\u3002"
1046
+ },
1047
+ "src/config/provider-presets.ts": {
1048
+ ceiling: 1070,
1049
+ note: "PR#38 provider onboarding \u7EDF\u4E00\u6808\uFF08861 \u884C\uFF09\u2014\u2014\u5165\u8D26\u9489\u4F4F\u3002861\u2192875\uFF1Adeepseek \u9884\u8BBE\u65B0\u589E v4-flash-vision-exp \u89C6\u89C9\u5B9E\u9A8C\u6863\uFF081M \u4E0A\u4E0B\u6587 + supportsVision + \u5B9A\u4EF7\u540C flash\uFF0C+14\uFF09\u3002875\u2192900\uFF1Aglm \u9884\u8BBE\u65B0\u589E glm-5.3 \u6587\u672C\u65D7\u8230 + glm-5.3-flash \u539F\u751F\u591A\u6A21\u6001\uFF08Coding Plan provider\uFF0C\u5747 1M/128K + pricing \u5168 0\uFF0C+21\uFF09\u3002 900\u2192925\uFF1AP0 \u83B7\u53D6 Key \u76F4\u94FE\u2014\u2014ProviderPreset \u52A0 keyUrl \u5B57\u6BB5 + 13 \u4E2A\u9884\u8BBE\u5B98\u65B9\u63A7\u5236\u53F0 URL\uFF08\u7EAF\u6570\u636E +16\uFF0C\u5148\u4F8B ffc6d2f66 \u7EAF\u6570\u636E\u8C03\u5347\uFF09\u3002 925\u2192926\uFF1AisKeylessProviderEntry \u5355\u4E00\u4E8B\u5B9E\u6E90\uFF08keyless \u5224\u5B9A\u6536\u53E3\uFF0C+14\uFF1B\u6CE8\u91CA\u884C\u5360\u5927\u5934\uFF09\u3002 926\u2192926\uFF1AisLoopbackBaseUrl \u8FC1\u51FA\u81F3 local-endpoint.ts\uFF08\u7ED3\u6784\u68D8\u8F6E\u62C6\u5206\uFF1B\u968F\u8FC1\u6536\u7D27 keyless \u5224\u5B9A\u2014\u2014\u4E91\u7AEF baseUrl \u65E0 key \u4E0D\u518D\u8C41\u514D\uFF09\u3002 926\u2192936\uFF1Akimi \u9884\u8BBE\u8865\u5B98\u65B9 k3-256k\uFF08256K \u7701\u989D\u5EA6\u7248\uFF1A262144 \u4E0A\u4E0B\u6587 / 131072 \u8F93\u51FA / max / pricing \u5168 0\uFF0C+10 \u7EAF\u6570\u636E\uFF1B\u540C\u6279 kimi \u6BB5\u6539\u8D70 Kimi Code \u8BA2\u9605\u7AEF\u70B9 api.kimi.com/coding/v1\uFF0C\u884C\u6570\u51C0 0\uFF09\u3002 936\u21921054\uFF1AOpenCode Go \u4E24\u4E2A\u9884\u8BBE\uFF08opencode-go / opencode-go-anthropic\uFF1A\u6A21\u578B\u6E05\u5355 + \u534F\u8BAE + \u80FD\u529B\u58F0\u660E\uFF0C\u7EAF\u6570\u636E +118\uFF09\u2014\u2014\u4E0A\u6E38 2026-09 \u8D77\u5F3A\u5236 x-opencode-session\uFF0C\u4E24\u7AEF\u9884\u8BBE\u987B\u5F00\u7BB1\u53EF\u7528\u3002 1054\u21921070\uFF1Adeepseek \u9884\u8BBE\u63A5\u5165 deepseek-flash\uFF08V4.1 \u7EBF\uFF1A1M \u4E0A\u4E0B\u6587 + \u539F\u751F\u591A\u6A21\u6001 supportsVision\uFF0C\u5B9A\u4EF7\u540C flash\uFF0C+14 \u6761\u76EE\u542B\u6CE8\u91CA\uFF09\u5E76\u91CD\u6392\u4E3A [v4-flash, v4-pro, deepseek-flash, vision-exp]\u3001\u9ED8\u8BA4\u6863\u4E0E\u9996\u6A21\u578B\u5207 v4-flash\uFF08V4-Pro 14 \u65E5\u4E0B\u7EBF\u524D\u53D1\u7248\u51C6\u5907\uFF0C\u51C0 +2\uFF09\u2014\u2014\u7EAF\u6570\u636E\u589E\u957F"
1050
+ }
1051
+ }
1052
+ };
1053
+
1054
+ // src/agent/structure-gate.ts
1055
+ var MAX_LINES_REDLINE = source_budgets_manifest_default.redline;
1056
+ var MAX_LINES_BASELINE = Object.entries(
1057
+ source_budgets_manifest_default.budgets
1058
+ ).map(([file, spec]) => [file, spec.ceiling]);
1059
+ function countPhysicalLines(content) {
1060
+ if (content.length === 0) return 0;
1061
+ const segments = content.split("\n").length;
1062
+ return content.endsWith("\n") ? segments - 1 : segments;
1063
+ }
1064
+ function isGuardedSourcePath(relPosix) {
1065
+ if (!relPosix.startsWith("src/")) return false;
1066
+ if (!relPosix.endsWith(".ts") || relPosix.endsWith(".d.ts")) return false;
1067
+ if (relPosix.includes("/__tests__/")) return false;
1068
+ return true;
1069
+ }
1070
+ function checkStructureGate(files, cwd, readFile = (p) => {
1071
+ try {
1072
+ return readFileSync2(p, "utf-8");
1073
+ } catch {
1074
+ return null;
1075
+ }
1076
+ }) {
1077
+ const baseline = new Map(MAX_LINES_BASELINE);
1078
+ const violations = [];
1079
+ for (const filePath of files) {
1080
+ const abs = isAbsolute2(filePath) ? filePath : join2(cwd, filePath);
1081
+ const relPosix = relative2(cwd, abs).split(sep).join("/");
1082
+ if (!isGuardedSourcePath(relPosix)) continue;
1083
+ const content = readFile(abs);
1084
+ if (content === null) continue;
1085
+ const lines = countPhysicalLines(content);
1086
+ const ceiling = baseline.get(relPosix);
1087
+ if (ceiling !== void 0) {
1088
+ if (lines > ceiling) violations.push({ file: relPosix, lines, limit: ceiling, kind: "ceiling" });
1089
+ } else if (lines > MAX_LINES_REDLINE) {
1090
+ violations.push({ file: relPosix, lines, limit: MAX_LINES_REDLINE, kind: "redline" });
1091
+ }
1092
+ }
1093
+ const needsWarning = violations.length > 0;
1094
+ const warningLines = [];
1095
+ if (needsWarning) {
1096
+ warningLines.push("\u26A0\uFE0F Structure gate\uFF08\u9884\u8B66\uFF0C\u4E0D\u963B\u65AD\uFF09\uFF1A\u672C\u6B21\u63D0\u4EA4\u547D\u4E2D\u884C\u6570\u68D8\u8F6E\uFF0Cnpm test \u7684 architecture-guards \u4F1A\u7EA2\uFF1A");
1097
+ for (const v of violations) {
1098
+ const hint = v.kind === "ceiling" ? "\u70B9\u540D\u5DE8\u77F3\u53EA\u964D\u4E0D\u5347" : `\u8868\u5916\u6587\u4EF6\u7EA2\u7EBF ${MAX_LINES_REDLINE}`;
1099
+ warningLines.push(` ${v.file}: ${v.lines} \u884C > ${v.kind} ${v.limit}\uFF08${hint}\uFF09`);
1100
+ }
1101
+ warningLines.push("\u6CBF\u63A5\u7F1D\u62C6\u5206\uFF1A\u63D0\u53D6\u5B50\u6A21\u5757\u540E\u6309\u533A\u57DF\u5206\u6279 deliver_task\uFF0C\u800C\u4E0D\u662F\u7EE7\u7EED\u81A8\u80C0\u3002");
1102
+ warningLines.push("\u786E\u9700\u589E\u957F/\u786E\u5C5E\u5355\u4E00\u804C\u8D23\uFF1A\u5728\u540C\u4E00 PR \u4FEE\u6539 scripts/source-budgets.manifest.json \u5E76\u8BF4\u660E\u7406\u7531\uFF08\u7EAF\u6570\u636E diff\uFF09\u3002");
1103
+ warningLines.push("\u9A8C\u8BC1\uFF1Anpm run structure:check");
1104
+ }
1105
+ return { violations, needsWarning, warningLines };
1106
+ }
1107
+
1108
+ // src/agent/verification-reconcile.ts
1109
+ init_esm_shims();
1110
+ function declaredVerificationCommands(plan) {
1111
+ const seen = /* @__PURE__ */ new Set();
1112
+ const out = [];
1113
+ for (const task of plan.tasks) {
1114
+ for (const cmd of task.verification ?? []) {
1115
+ const key = normalize(cmd);
1116
+ if (!key || seen.has(key)) continue;
1117
+ seen.add(key);
1118
+ out.push(cmd.trim());
1119
+ }
1120
+ }
1121
+ return out;
1122
+ }
1123
+ function normalize(cmd) {
1124
+ return cmd.trim().replace(/\s+/g, " ");
1125
+ }
1126
+ function matches(declared, event) {
1127
+ const d = normalize(declared);
1128
+ const candidates = [event.command, event.meta?.resolvedCommand].filter((c) => typeof c === "string" && c.length > 0).map(normalize);
1129
+ return candidates.some((c) => c.includes(d) || d.includes(c));
1130
+ }
1131
+ function reconcileVerificationCommands(declared, ran) {
1132
+ return declared.map((command) => {
1133
+ let last;
1134
+ for (const ev of ran) {
1135
+ if (matches(command, ev)) last = ev;
1136
+ }
1137
+ if (!last || !last.status) return { command, status: "not_run" };
1138
+ const meta = last.meta ?? {};
1139
+ let detail;
1140
+ if (last.status === "blocked") {
1141
+ detail = typeof meta.blockedReason === "string" ? meta.blockedReason : void 0;
1142
+ } else {
1143
+ const passed = typeof meta.passed === "number" ? meta.passed : void 0;
1144
+ const failed = typeof meta.failed === "number" ? meta.failed : void 0;
1145
+ if (passed !== void 0 && failed !== void 0) detail = `${passed} pass ${failed} fail`;
1146
+ }
1147
+ return { command, status: last.status, ...detail ? { detail } : {} };
1148
+ });
1149
+ }
1150
+ function formatVerificationReconcileReport(items) {
1151
+ if (items.length === 0) return [];
1152
+ const unsettled = items.filter((i) => i.status !== "passed");
1153
+ if (unsettled.length === 0) {
1154
+ return ["", `\u2713 \u9A8C\u8BC1\u547D\u4EE4\u5BF9\u8D26\uFF1A\u8BA1\u5212\u58F0\u660E ${items.length} \u6761\uFF0C\u5168\u90E8\u6709\u901A\u8FC7\u8BB0\u5F55\u3002`];
1155
+ }
1156
+ const lines = ["", `\u2500\u2500 \u9A8C\u8BC1\u547D\u4EE4\u5BF9\u8D26\uFF08\u8BA1\u5212\u58F0\u660E ${items.length} \u6761\uFF0C${unsettled.length} \u6761\u672A\u6838\u9500\uFF09\u2500\u2500`];
1157
+ for (const item of items) {
1158
+ const mark = item.status === "passed" ? "\u2713" : item.status === "failed" ? "\u2717" : item.status === "blocked" ? "\u23F1" : "\u2205";
1159
+ const label = item.status === "not_run" ? "\u65E0\u8FD0\u884C\u8BB0\u5F55" : item.status === "blocked" ? `blocked${item.detail ? `\uFF08${item.detail}\uFF09` : ""}\u2014\u2014\u8DD1\u4E86\u4F46\u6CA1\u8DD1\u5B8C = \u672A\u9A8C\u8BC1` : `${item.status}${item.detail ? `\uFF08${item.detail}\uFF09` : ""}`;
1160
+ lines.push(` ${mark} ${item.command} \u2014 ${label}`);
1161
+ }
1162
+ lines.push(" \u2192 \u5BF9\u8D26\u4E0D\u5E73\u4E0D\u963B\u65AD\u4EA4\u4ED8\uFF0C\u4F46\u4EA4\u4ED8\u62A5\u544A\u5FC5\u987B\u9010\u6761\u62AB\u9732\u4E0A\u8FF0\u72B6\u6001\u2014\u2014\u53EA\u62A5\u8DD1\u901A\u7684\u5957\u4EF6\u3001\u5BF9\u6CA1\u8DD1\u5B8C\u7684\u6C89\u9ED8\uFF0C\u4E0E\u865A\u62A5\u540C\u7F6A\u3002");
1163
+ lines.push(" \u2192 blocked(timeout) \u7684\u5957\u4EF6\u82E5\u8986\u76D6\u672C\u8F6E\u65B0\u5EFA/\u4FEE\u6539\u7684\u4EE3\u7801\uFF0C\u4F18\u5148\u6000\u7591\u4EE3\u7801\u6B7B\u5FAA\u73AF\u800C\u975E\u673A\u5668\u6162\u3002");
1164
+ return lines;
1165
+ }
1166
+
1167
+ // src/agent/post-commit-review-pending.ts
1168
+ init_esm_shims();
1169
+ var pendingBySession = /* @__PURE__ */ new Map();
1170
+ var FALLBACK_SESSION_KEY = "(no-session)";
1171
+ function keyOf(sessionId) {
1172
+ return sessionId && sessionId.length > 0 ? sessionId : FALLBACK_SESSION_KEY;
1173
+ }
1174
+ function addPendingReviewFiles(sessionId, files, opts) {
1175
+ const key = keyOf(sessionId);
1176
+ let scope = pendingBySession.get(key);
1177
+ if (!scope) {
1178
+ scope = { files: /* @__PURE__ */ new Set(), commits: 0, escalate: false };
1179
+ pendingBySession.set(key, scope);
1180
+ }
1181
+ for (const file of files) scope.files.add(file);
1182
+ scope.commits++;
1183
+ if (opts?.escalate) scope.escalate = true;
1184
+ return { files: new Set(scope.files), commits: scope.commits, escalate: scope.escalate };
1185
+ }
1186
+ function peekPendingReview(sessionId) {
1187
+ const scope = pendingBySession.get(keyOf(sessionId));
1188
+ if (!scope || scope.commits === 0) return null;
1189
+ return { files: new Set(scope.files), commits: scope.commits, escalate: scope.escalate };
1190
+ }
1191
+ function consumePendingReview(sessionId) {
1192
+ const scope = peekPendingReview(sessionId);
1193
+ pendingBySession.delete(keyOf(sessionId));
1194
+ return scope;
1195
+ }
1196
+ function clearPendingReview(sessionId) {
1197
+ pendingBySession.delete(keyOf(sessionId));
1198
+ }
1199
+ function __resetPostCommitReviewPending() {
1200
+ pendingBySession.clear();
1201
+ }
1202
+ function formatDeferredReviewNotice(commits, files) {
1203
+ return [
1204
+ `\u23ED \u672C\u6B21\u63D0\u4EA4\u7684\u5BA1\u67E5\u5DF2\u5E76\u5165\u6279\u91CF\u7EC8\u5BA1\uFF1A\u5DF2\u7D2F\u79EF ${commits} \u4E2A\u63D0\u4EA4\u3001${files} \u4E2A\u6587\u4EF6\uFF08\u957F\u4EFB\u52A1\u7701 token \u7684\u7B56\u7565\uFF09\u3002`,
1205
+ " \u73B0\u5728\u5C31\u60F3\u770B\u5BA1\u67E5\u610F\u89C1 \u2192 \u6572 /review\uFF08\u5355\u9A8C\u8BC1\u5458\uFF09\u6216 /review max\uFF08\u4E94\u4EBA\u7F16\u961F\uFF09",
1206
+ " \u4E0D\u64CD\u4F5C\u4E5F\u53EF\u4EE5 \u2192 \u6536\u5C3E\u65F6\u4F1A\u81EA\u52A8\u5408\u5E76\u6210\u4E00\u6B21\u7EC8\u5BA1\uFF0C\u65E0\u9700\u989D\u5916\u52A8\u4F5C"
1207
+ ];
1208
+ }
1209
+
1210
+ // src/config/scout-firewall-config.ts
1211
+ init_esm_shims();
1212
+ function isScoutFirewallEnabled(configValue) {
1213
+ const env = process.env.RIVET_SCOUT_FIREWALL;
1214
+ if (env !== void 0) {
1215
+ if (env === "1" || env === "true" || env === "on" || env === "yes") return true;
1216
+ if (env === "0" || env === "false" || env === "off" || env === "no") return false;
1217
+ }
1218
+ return configValue ?? false;
1219
+ }
1220
+
1221
+ // src/agent/deliver-task.ts
1222
+ function createStageMarker(prefix) {
1223
+ const t0 = Date.now();
1224
+ return (stage) => {
1225
+ debugLog(`[${prefix}] ${stage} +${Date.now() - t0}ms`);
1226
+ };
1227
+ }
1228
+ function runGateAsync(command, cwd) {
1229
+ const argv = parseVerifyCommand(command);
1230
+ if (!argv) return Promise.resolve({ ok: false, detail: "\u975E\u767D\u540D\u5355 gate \u547D\u4EE4\u5F62\u72B6\u2014\u2014\u62D2\u7EDD shell \u6267\u884C" });
1231
+ return new Promise((resolvePromise) => {
1232
+ let stdout = "";
1233
+ let stderr = "";
1234
+ let settled = false;
1235
+ const tail = () => `${stdout}
1236
+ ${stderr}`.trim().split("\n").slice(-3).join("\n").slice(0, 300);
1237
+ const settle = (r) => {
1238
+ if (!settled) {
1239
+ settled = true;
1240
+ resolvePromise(r);
1241
+ }
1242
+ };
1243
+ try {
1244
+ const child = spawnVerifyArgv(cwd, argv, { stdio: ["ignore", "pipe", "pipe"] });
1245
+ child.stdout?.on("data", (d) => {
1246
+ stdout += d.toString();
1247
+ });
1248
+ child.stderr?.on("data", (d) => {
1249
+ stderr += d.toString();
1250
+ });
1251
+ const timer = setTimeout(() => {
1252
+ try {
1253
+ child.kill("SIGKILL");
1254
+ } catch {
1255
+ }
1256
+ settle({ ok: false, detail: tail() });
1257
+ }, 6e4);
1258
+ child.on("close", (code) => {
1259
+ clearTimeout(timer);
1260
+ if (code === 0) settle({ ok: true });
1261
+ else settle({ ok: false, detail: tail() });
1262
+ });
1263
+ child.on("error", (err) => {
1264
+ clearTimeout(timer);
1265
+ settle({ ok: false, detail: err.message });
1266
+ });
1267
+ } catch (err) {
1268
+ settle({ ok: false, detail: err instanceof Error ? err.message : String(err) });
1269
+ }
1270
+ });
1271
+ }
1272
+ var POST_COMMIT_REVIEW_COOLDOWN_MS = 3e4;
1273
+ var lastPostCommitReviewAt = 0;
1274
+ var postCommitReviewInFlight = false;
1275
+ function resetPostCommitReviewCooldown() {
1276
+ lastPostCommitReviewAt = 0;
1277
+ postCommitReviewInFlight = false;
1278
+ __resetPostCommitReviewPending();
1279
+ }
1280
+ function __expirePostCommitReviewCooldown() {
1281
+ lastPostCommitReviewAt = 0;
1282
+ }
1283
+ function formatReviewOutcomeLines(outcome) {
1284
+ const lines = [];
1285
+ if (outcome.verdict === "rejected" || outcome.escalated) {
1286
+ lines.push(`\u26A0\uFE0F \u5BA1\u67E5\u95E8\u53D1\u73B0\u95EE\u9898 (${outcome.tier})\uFF1A${outcome.evidence ?? "\u5BF9\u6297\u6027\u5BA1\u67E5\u672A\u9A8C\u8BC1\u6B64\u4EA4\u4ED8"}`);
1287
+ if (typeof outcome.rounds === "number") lines.push(` \u8F6E\u6B21\uFF1A${outcome.rounds}`);
1288
+ lines.push(" \u2192 \u63D0\u4EA4\u5DF2\u843D\u5730\u3002\u8BF7\u5728\u540E\u7EED\u63D0\u4EA4\u4E2D\u5904\u7406\u5BA1\u67E5\u53D1\u73B0\u3002");
1289
+ lines.push(" \u26A0 \u4EE5\u4E0A\u5BA1\u67E5\u610F\u89C1\u6765\u81EA worker\uFF0C\u672A\u7ECF\u4E3B\u63A7\u72EC\u7ACB\u6838\u9A8C\u3002\u6C47\u62A5\u7528\u6237\u524D\u8BF7\u7528 grep/read \u786E\u8BA4\u6BCF\u6761\u58F0\u79F0\u7684\u6587\u4EF6:\u884C\u53F7\u771F\u5B9E\u5B58\u5728\u3002");
1290
+ } else if (outcome.verdict === "verified") {
1291
+ if (outcome.infraFailures && outcome.infraFailures.length > 0) {
1292
+ lines.push(`\u26A0\uFE0F \u5BA1\u67E5\u95E8 YELLOW (${outcome.tier})\uFF1A\u5BA1\u67E5\u57FA\u7840\u8BBE\u65BD\u6709\u6CE8\u610F\u4E8B\u9879\uFF0C\u4EA4\u4ED8\u5DF2\u901A\u8FC7\u53EF\u7528\u8BC1\u636E\u9A8C\u8BC1\u3002`);
1293
+ lines.push(` ${outcome.evidence ?? "\u5DF2\u901A\u8FC7\u5BA1\u67E5\u57FA\u7840\u8BBE\u65BD\u6CE8\u610F\u4E8B\u9879\u9A8C\u8BC1"}`);
1294
+ } else {
1295
+ lines.push(`\u2705 \u5BA1\u67E5\u901A\u8FC7 (${outcome.tier})\uFF1A${outcome.evidence ?? "\u5DF2\u9A8C\u8BC1"}`);
1296
+ }
1297
+ } else if (outcome.verdict === "inconclusive") {
1298
+ lines.push(`\u26A0\uFE0F \u5BA1\u67E5\u672A\u51B3 (${outcome.tier})\uFF1A${outcome.evidence ?? "\u63D0\u4EA4\u540E\u5BA1\u67E5\u672A\u8FD0\u884C\uFF08\u57FA\u7840\u8BBE\u65BD\u6545\u969C\uFF09"}`);
1299
+ lines.push(" \u2192 \u6B64\u53D8\u66F4\u672A\u7ECF\u5BA1\u67E5\u3002\u8FD0\u884C /review max \u8FDB\u884C\u5B8C\u6574\u7F16\u961F\u5BA1\u67E5\u3002");
1300
+ } else if (outcome.verdict === "nudge") {
1301
+ lines.push(`\u26A0\uFE0F \u5BA1\u67E5\u63D0\u9192 (${outcome.tier})\uFF1A\u8BF7\u5728\u540E\u7EED\u5DE5\u4F5C\u4E2D\u5E94\u7528\u5BA1\u67E5\u7EAA\u5F8B\u3002`);
1302
+ lines.push("\u901A\u7528\u5F00\u53D1\u65B9\u6CD5\u8BBA\uFF1A");
1303
+ for (const directive of GENERAL_DEV_DISCIPLINES) lines.push(` - ${directive}`);
1304
+ }
1305
+ lines.push(...formatPatcherArtifacts(outcome.patcherArtifacts));
1306
+ return lines;
1307
+ }
1308
+ function parseNulFileList(output) {
1309
+ return output.split("\0").filter(Boolean);
1310
+ }
1311
+ function readProjectMemory(cwd) {
1312
+ try {
1313
+ return readFileSync3(join3(cwd, ".rivet", "knowledge", "project-memory.md"), "utf-8");
1314
+ } catch {
1315
+ return void 0;
1316
+ }
1317
+ }
1318
+ function collectLargeFiles(cwd, filePaths) {
1319
+ const large = [];
1320
+ for (const p of filePaths) {
1321
+ try {
1322
+ const st = statSync(join3(cwd, p));
1323
+ if (st.size >= LARGE_FILE_WARN_THRESHOLD) {
1324
+ large.push({ path: p, sizeBytes: st.size });
1325
+ }
1326
+ } catch {
1327
+ }
1328
+ }
1329
+ return large.length > 0 ? large : void 0;
1330
+ }
1331
+ function gitNameList(cwd, args) {
1332
+ const result = spawnGitSync(["-c", "core.quotePath=false", ...args], { cwd, encoding: "utf-8", timeout: 5e3 });
1333
+ if (result.status !== 0) return null;
1334
+ return parseNulFileList(result.stdout);
1335
+ }
1336
+ function detectSymptomPatch(cwd) {
1337
+ const res = spawnGitSync(["-c", "core.quotePath=false", "diff", "--numstat", "HEAD"], { cwd, encoding: "utf-8", timeout: 5e3 });
1338
+ if (res.status !== 0) return null;
1339
+ const rows = res.stdout.split(/\r?\n/).filter(Boolean).map((l) => l.split(" ")).filter((c) => c.length === 3 && !(c[2] ?? "").includes("test"));
1340
+ if (rows.length !== 1) return null;
1341
+ const row = rows[0];
1342
+ const added = Number(row[0]) || 0;
1343
+ if (added > 2) return null;
1344
+ const patch = spawnGitSync(["-c", "core.quotePath=false", "diff", "HEAD", "--", row[2]], { cwd, encoding: "utf-8", timeout: 5e3 });
1345
+ if (patch.status !== 0) return null;
1346
+ const addedLines = patch.stdout.split(/\r?\n/).filter((l) => l.startsWith("+") && !l.startsWith("+++"));
1347
+ const fallbackOnly = addedLines.length > 0 && addedLines.every((l) => /\?\?|\|\||=\s*['"`]?\w*['"`]?\s*$|fallback|default/.test(l));
1348
+ if (!fallbackOnly) return null;
1349
+ return "\u2696\uFE0F \u8FD9\u662F\u75C7\u72B6\u5904\u7684 fallback \u8865\u4E01(\u5355\u884C\u3001\u6539\u9ED8\u8BA4\u503C)\u3002\u662F\u6E90\u5934\u4FEE\u590D\u8FD8\u662F\u5C31\u8FD1\u6253\u8865\u4E01\uFF1F\u6570\u636E\u6D41\u8FFD\u5230\u6E90\u5934\u4E86\u5417\uFF1F(\u6E05\u9192\u951A\u70B9\uFF0C\u4E0D\u963B\u585E)";
1350
+ }
1351
+ function collectCurrentDirtyFiles(cwd) {
1352
+ const unstaged = gitNameList(cwd, ["diff", "--name-only", "-z"]);
1353
+ const staged = gitNameList(cwd, ["diff", "--cached", "--name-only", "-z"]);
1354
+ const untracked = gitNameList(cwd, ["ls-files", "--others", "--exclude-standard", "-z"]);
1355
+ if (!unstaged || !staged || !untracked) return void 0;
1356
+ const files = /* @__PURE__ */ new Set();
1357
+ for (const file of [...unstaged, ...staged, ...untracked]) {
1358
+ files.add(file);
1359
+ }
1360
+ return [...files].sort();
1361
+ }
1362
+ function detectMissingVisualVerify(ownedFiles, getEvents) {
1363
+ const uiFiles = ownedFiles.filter((f) => isUiFilePath(f));
1364
+ if (uiFiles.length === 0) return null;
1365
+ const events = getEvents?.() ?? [];
1366
+ const hasVisualVerify = events.some(
1367
+ (e) => e.type === "tool_exec" && e.tool && isVisualVerifyTool(e.tool)
1368
+ );
1369
+ if (hasVisualVerify) return null;
1370
+ return `\u26A0\uFE0F \u6E32\u67D3\u672A\u9A8C\u8BC1\uFF1AUI \u6587\u4EF6\u5DF2\u4FEE\u6539\uFF08${uiFiles.slice(0, 2).join(", ")}${uiFiles.length > 2 ? " \u7B49" : ""}\uFF09\uFF0C\u4F46\u672A\u89C1 browser_debug/computer_use \u89C6\u89C9\u9A8C\u8BC1\u3002\u4EA4\u4ED8\u524D\u7528 browser_debug open \u2192 navigate \u2192 screenshot \u68C0\u67E5\u6E32\u67D3\u7ED3\u679C\u3002`;
1371
+ }
1372
+ function parseLearnedEntries(raw) {
1373
+ if (!Array.isArray(raw)) return [];
1374
+ const out = [];
1375
+ for (const item of raw) {
1376
+ if (typeof item !== "string") continue;
1377
+ const trimmed = item.trim();
1378
+ if (!trimmed) continue;
1379
+ const sep2 = trimmed.match(/[—–-]{1,2}\s*证据\s*[::]/);
1380
+ let text = trimmed;
1381
+ let evidence = "";
1382
+ if (sep2 && sep2.index !== void 0) {
1383
+ text = trimmed.slice(0, sep2.index).trim();
1384
+ evidence = trimmed.slice(sep2.index + sep2[0].length).trim();
1385
+ }
1386
+ if (!text) continue;
1387
+ const pathLike = evidence.match(/[\w@./-]+\.[A-Za-z]{1,8}\b/);
1388
+ out.push({
1389
+ text,
1390
+ evidence,
1391
+ tags: ["agent-learned"],
1392
+ topic: pathLike?.[0]
1393
+ });
1394
+ }
1395
+ return out;
1396
+ }
1397
+ function createDeliverTaskTool(getB1Context) {
1398
+ return {
1399
+ definition: {
1400
+ name: "deliver_task",
1401
+ description: `\u57FA\u4E8E B1 \u5F52\u5C5E\u4E0E\u9A8C\u8BC1\u53F0\u8D26\u68C0\u67E5\u4EFB\u52A1\u4EA4\u4ED8\u5C31\u7EEA\u5EA6\u3002
1402
+
1403
+ ### \u7528\u6CD5
1404
+ - \u7528 deliver_task \u68C0\u67E5\u5F53\u524D\u4EFB\u52A1\u662F\u5426\u53EF\u4EA4\u4ED8/\u63D0\u4EA4
1405
+ - \u62A5\u544A GREEN\uFF08\u5C31\u7EEA\uFF09\u3001YELLOW\uFF08\u5C31\u7EEA\u4F46\u9644\u5916\u90E8\u6CE8\u610F\u4E8B\u9879\uFF09\u6216 RED\uFF08\u963B\u585E\uFF09
1406
+ - \u5305\u542B\u5DF2\u5F52\u5C5E\u6587\u4EF6\u3001\u5916\u90E8\u6587\u4EF6\u4E0E\u9A8C\u8BC1\u72B6\u6001
1407
+ - \u9ED8\u8BA4\u53EA\u62A5\u544A\u5C31\u7EEA\u5EA6\uFF0C\u4E0D\u6267\u884C\u63D0\u4EA4
1408
+ - commit=true \u65F6\uFF0C\u5728\u5BA1\u6279\u901A\u8FC7\u540E\u6267\u884C\u4E00\u6B21\u5F52\u5C5E\u8303\u56F4\u9650\u5B9A\u7684\u63D0\u4EA4
1409
+
1410
+ ### \u53C2\u6570
1411
+ - commit: \u8BBE\u4E3A true \u4EE5\u8BF7\u6C42\u9650\u5B9A\u8303\u56F4\u63D0\u4EA4\u7684\u5BA1\u6279\uFF08\u9ED8\u8BA4\uFF1Afalse\uFF09
1412
+ - message: \u63D0\u4EA4\u4FE1\u606F\uFF08commit=true \u65F6\u5FC5\u586B\uFF09
1413
+ - files: \u53EF\u9009\uFF0C\u8981\u63D0\u4EA4\u7684\u5DF2\u5F52\u5C5E\u6587\u4EF6\u8DEF\u5F84\u6570\u7EC4\uFF08\u5B50\u96C6\uFF09\u3002\u7701\u7565\u65F6\u63D0\u4EA4\u5168\u90E8\u5DF2\u5F52\u5C5E\u6587\u4EF6\u3002\u7528\u6B64\u53C2\u6570\u628A\u903B\u8F91\u5355\u5143\u5206\u5F00\u63D0\u4EA4\u3002
1414
+ - adopt: \u63D0\u4EA4\u524D\u8981\u8BA4\u9886\u5F52\u5C5E\u7684\u5916\u90E8\u6216\u5171\u540C\u5F52\u5C5E\u6587\u4EF6\u8DEF\u5F84\u6570\u7EC4\u3002\u7528\u4E8E\u63A5\u7BA1\u5D29\u6E83/\u51BB\u7ED3\u4F1A\u8BDD\u7684\u5DE5\u4F5C\u3002\u8981\u6C42 commit=true\u3002\u88AB\u8BA4\u9886\u7684\u6587\u4EF6\u4F1A\u5F3A\u5236\u52A0\u5165\u5DF2\u5F52\u5C5E\u96C6\u5408\uFF0C\u5E76\u7EB3\u5165\u63D0\u4EA4\u8303\u56F4\u3002
1415
+ - force: \u8BBE\u4E3A true \u53EF\u5728\u8DE8\u591A\u4E2A\u533A\u57DF\u63D0\u4EA4\u5927\u91CF\u6587\u4EF6\u65F6\u8986\u76D6\u5185\u805A\u6027\u95E8\u7981\u3002\u8C28\u614E\u4F7F\u7528\u3002
1416
+ - learned: \u672C\u6B21\u4F1A\u8BDD\u786E\u8BA4\u7684\u53EF\u590D\u7528\u6A21\u5F0F\u6570\u7EC4\uFF08\u6BCF\u6761\u683C\u5F0F\uFF1A"\u6A21\u5F0F\u63CF\u8FF0\u2014\u2014\u8BC1\u636E\uFF1A\u8DEF\u5F84\u6216\u590D\u73B0\u6B65\u9AA4"\uFF09\u3002\u6301\u4E45\u5316\u5230\u9879\u76EE\u77E5\u8BC6\u5E93\uFF0C\u672A\u6765\u4F1A\u8BDD\u81EA\u52A8\u6CE8\u5165\u3002\u53EA\u63D0\u4EA4\u5DF2\u9A8C\u8BC1\u7684\u6A21\u5F0F\u3002
1417
+ - review_policy: \u957F\u4EFB\u52A1\u7684\u63D0\u4EA4\u540E\u5BA1\u67E5\u5206\u6279\u3002each\uFF08\u9ED8\u8BA4\uFF09\u6BCF\u6B21\u63D0\u4EA4\u90FD\u5BA1\u67E5\uFF1Bdefer \u4E0D\u5BA1\u67E5\uFF0C\u628A\u63D0\u4EA4\u7D2F\u79EF\u8FDB\u4F1A\u8BDD\u5F85\u5BA1\u8303\u56F4\uFF1Bfinal \u5BF9\u6240\u6709\u7D2F\u79EF\u63D0\u4EA4\u505A\u4E00\u6B21\u5BA1\u67E5\u3002\u76F8\u6BD4\u5927\u91CF\u5C0F\u63D0\u4EA4\uFF0C\u4F18\u5148 defer+final\uFF0C\u907F\u514D\u6BCF\u6B21\u63D0\u4EA4\u90FD\u6D88\u8017\u4E00\u4E2A review worker\u3002
1418
+
1419
+ ### \u590D\u6742 spec \u4EA4\u4ED8\u6E05\u5355
1420
+ \u5BF9\u590D\u6742 spec \u6216\u8DE8\u6A21\u5757\u96C6\u6210\uFF0C\u6E05\u5355\u6761\u76EE\u8981\u5305\u542B\uFF1A\u4E8B\u5B9E\u6D41\u56FE\u5DF2\u9A8C\u8BC1\u3001\u6761\u4EF6\u77E9\u9635\u5DF2\u9A8C\u8BC1\u3001\u53CD\u4F8B\u6D4B\u8BD5\u5DF2\u9A8C\u8BC1\u3002`,
1421
+ input_schema: {
1422
+ type: "object",
1423
+ properties: {
1424
+ commit: { type: "boolean", description: "\u8BF7\u6C42\u5BF9\u5DF2\u5F52\u5C5E\u6587\u4EF6\u505A\u9650\u5B9A\u8303\u56F4\u7684\u63D0\u4EA4" },
1425
+ message: { type: "string", description: "\u63D0\u4EA4\u4FE1\u606F\uFF08commit=true \u65F6\u5FC5\u586B\uFF09" },
1426
+ files: {
1427
+ type: "array",
1428
+ items: { type: "string" },
1429
+ description: "\u53EF\u9009\uFF0C\u8981\u63D0\u4EA4\u7684\u5DF2\u5F52\u5C5E\u6587\u4EF6\u5B50\u96C6\u3002\u7701\u7565\u65F6\u63D0\u4EA4\u5168\u90E8\u5DF2\u5F52\u5C5E\u6587\u4EF6\u3002\u7528\u4E8E\u628A\u5DE5\u4F5C\u62C6\u6210\u72EC\u7ACB\u7684\u903B\u8F91\u63D0\u4EA4\u3002"
1430
+ },
1431
+ adopt: {
1432
+ type: "array",
1433
+ items: { type: "string" },
1434
+ description: "\u63D0\u4EA4\u524D\u8BA4\u9886\u8FDB\u5DF2\u5F52\u5C5E\u96C6\u5408\u7684\u5916\u90E8\u6587\u4EF6\u8DEF\u5F84\u3002\u7528\u4E8E\u53E6\u4E00\u4F1A\u8BDD\u5D29\u6E83\u540E\u7684\u8DE8\u4F1A\u8BDD\u63A5\u7BA1\u3002\u8981\u6C42 commit=true\u3002"
1435
+ },
1436
+ force: {
1437
+ type: "boolean",
1438
+ description: "\u8986\u76D6\u5185\u805A\u6027\u95E8\u7981\u3002\u4EC5\u5728\u63D0\u4EA4\u5C3D\u7BA1\u8DE8\u591A\u4E2A\u533A\u57DF\u3001\u4F46\u786E\u5B9E\u662F\u4E00\u4E2A\u903B\u8F91\u5355\u5143\u65F6\u4F7F\u7528\u3002"
1439
+ },
1440
+ checklist: {
1441
+ type: "array",
1442
+ items: {
1443
+ type: "object",
1444
+ properties: {
1445
+ item: { type: "string" },
1446
+ done: { type: "boolean" },
1447
+ files: { type: "array", items: { type: "string" } }
1448
+ },
1449
+ required: ["item", "done"]
1450
+ },
1451
+ description: "\u4EFB\u52A1\u5B8C\u6210\u5EA6\u5BA1\u8BA1\u6761\u76EE\u3002\u5BF9\u590D\u6742 spec\uFF0C\u8981\u5305\u542B\u4E8B\u5B9E\u6D41\u56FE\u5DF2\u9A8C\u8BC1\u3001\u6761\u4EF6\u77E9\u9635\u5DF2\u9A8C\u8BC1\u3001\u53CD\u4F8B\u6D4B\u8BD5\u5DF2\u9A8C\u8BC1/\u5DF2\u5EF6\u671F\u3002"
1452
+ },
1453
+ review_level: {
1454
+ type: "string",
1455
+ enum: ["L2", "L3"],
1456
+ description: "\u663E\u5F0F\u8BBE\u7F6E\u5BA1\u67E5\u5DE5\u4F5C\u6D41\u6DF1\u5EA6\u3002L2 = \u5355\u4E2A\u5BF9\u6297\u5F0F\u9A8C\u8BC1\u5668\u3002L3 = Review Squadron\uFF085 \u4E2A\u68C0\u67E5\u5458\uFF09\u3002\u7701\u7565\u65F6\u6309\u53D8\u66F4\u7ED3\u6784\u81EA\u52A8\u5206\u7EA7\uFF08\u9ED8\u8BA4\uFF1AL1 \u4EC5\u63D0\u9192\uFF09\u3002\u7528\u4E8E\u5BF9\u9AD8\u98CE\u9669\u6216\u5173\u952E\u8DEF\u5F84\u53D8\u66F4\u624B\u52A8\u89E6\u53D1\u66F4\u6DF1\u7684\u5BA1\u67E5\u3002"
1457
+ },
1458
+ skipAutoReview: {
1459
+ type: "boolean",
1460
+ description: "\u6291\u5236\u63D0\u4EA4\u540E\u7684\u81EA\u52A8\u5BA1\u67E5\u3002goal tracker \u6FC0\u6D3B\u65F6\uFF08\u76EE\u6807\u9A71\u52A8\u7684\u81EA\u52A8\u7EED\u8DD1\uFF09\u81EA\u52A8\u8BBE\u7F6E\u3002\u5BF9\u7410\u788E\u6216\u7D27\u6025\u53D8\u66F4\u53EF\u624B\u52A8\u8BBE\u7F6E\u4EE5\u7ED5\u8FC7\u5BA1\u67E5\u3002"
1461
+ },
1462
+ review_policy: {
1463
+ type: "string",
1464
+ enum: ["each", "defer", "final"],
1465
+ description: "\u957F\u4EFB\u52A1\u7684\u63D0\u4EA4\u540E\u5BA1\u67E5\u5206\u6279\u3002each\uFF08\u9ED8\u8BA4\uFF09\uFF1A\u6BCF\u6B21\u63D0\u4EA4\u90FD\u5BA1\u67E5\u3002defer\uFF1A\u8DF3\u8FC7\u5373\u65F6\u5BA1\u67E5\uFF0C\u628A\u672C\u6B21\u63D0\u4EA4\u7D2F\u79EF\u8FDB\u4F1A\u8BDD\u5F85\u5BA1\u8303\u56F4\u3002final\uFF1A\u5BF9\u6240\u6709\u7D2F\u79EF\u7684\u5EF6\u8FDF\u63D0\u4EA4\u8FDE\u540C\u672C\u6B21\u505A\u4E00\u6B21\u5BA1\u67E5\u3002\u8BBE\u7F6E review_level \u65F6\u5FFD\u7565\u6B64\u53C2\u6570\u3002"
1466
+ },
1467
+ learned: {
1468
+ type: "array",
1469
+ items: { type: "string" },
1470
+ description: '\u672C\u6B21 session \u786E\u8BA4\u7684\u53EF\u590D\u7528\u77E5\u8BC6\uFF08\u6BCF\u6761\u683C\u5F0F\uFF1A"\u6A21\u5F0F\u63CF\u8FF0\u2014\u2014\u8BC1\u636E\uFF1A\u6587\u4EF6\u8DEF\u5F84\u6216\u590D\u73B0\u6B65\u9AA4"\uFF09\u3002\u53EA\u5728\u4EA4\u4ED8\u65F6\u63D0\u4EA4\u7ECF\u8FC7\u9A8C\u8BC1\u7684\u6A21\u5F0F\uFF1A\u8BCA\u65AD\u65B9\u6CD5/\u67B6\u6784\u8FB9\u754C/\u5DE5\u5177\u94FE\u4E8B\u5B9E/\u5DF2\u9A8C\u8BC1\u4FEE\u590D\u3002\u4E0D\u786E\u5B9A\u7684\u4E0D\u8981\u5199\u3002'
1471
+ }
1472
+ }
1473
+ }
1474
+ },
1475
+ async execute(params) {
1476
+ const mark = createStageMarker("deliver-task");
1477
+ mark("execute:start");
1478
+ const ctx = getB1Context(params);
1479
+ const reviewDepth = params.reviewDepth ?? ctx.reviewDepth ?? 0;
1480
+ ctx.ownership.autoOwnFromLedger();
1481
+ const currentDirtyFiles = ctx.getCurrentDirtyFiles?.(params.cwd) ?? collectCurrentDirtyFiles(params.cwd);
1482
+ if (currentDirtyFiles) ctx.ownership.autoOwnFromBaseline(currentDirtyFiles);
1483
+ const impactedTests = ctx.getImpactedTests?.() ?? [];
1484
+ const moduleCoverage = impactedTests.length > 0 ? {
1485
+ impactedTests,
1486
+ testExists: (p) => existsSync2(isAbsolute3(p) ? p : join3(params.cwd, p))
1487
+ } : void 0;
1488
+ const report = ctx.gate.getReport([], currentDirtyFiles, ctx.getCurrentSnapshotRef?.(), moduleCoverage);
1489
+ mark("report:done");
1490
+ const FILE_LIST_CAP = 5;
1491
+ const renderFileList = (files, extraHiddenCount = 0) => {
1492
+ if (files.length === 0 && extraHiddenCount === 0) return [" (none)"];
1493
+ const shown = files.slice(0, FILE_LIST_CAP).map((f) => ` ${f}`);
1494
+ const hidden = files.length - Math.min(files.length, FILE_LIST_CAP) + extraHiddenCount;
1495
+ if (hidden > 0) shown.push(` (+${hidden} more${extraHiddenCount > 0 ? `, ${extraHiddenCount} junk/gitignored` : ""})`);
1496
+ return shown;
1497
+ };
1498
+ const externalSplit = filterExternalNoise(report.externalFiles, params.cwd);
1499
+ const lines = [
1500
+ `Delivery Gate: ${report.state}`,
1501
+ `Task: ${report.taskId}`,
1502
+ "",
1503
+ `Owned files (${report.ownedFileCount}):`,
1504
+ ...renderFileList(report.ownedFiles),
1505
+ "",
1506
+ `Co-owned files (${report.coOwnedFileCount}):`,
1507
+ ...renderFileList(report.coOwnedFiles),
1508
+ "",
1509
+ `Historical owned files (${report.historicalOwnedFileCount}):`,
1510
+ ...renderFileList(report.historicalOwnedFiles),
1511
+ "",
1512
+ `External files (${report.externalFileCount}):`,
1513
+ ...renderFileList(externalSplit.files, externalSplit.noiseCount),
1514
+ "",
1515
+ report.verificationCount > 0 ? `Verifications: ${report.verificationCount}` : "Verifications: none (no tests were run for this task)"
1516
+ ];
1517
+ if (report.latestVerificationTotals) {
1518
+ const v = report.latestVerificationTotals;
1519
+ lines.push(` Latest: ${v.passed} pass ${v.failed} fail ${v.skipped} skip \u2014 ${v.command}`);
1520
+ } else if (report.verificationCount === 0) {
1521
+ lines.push(' (Typecheck passed, but no test suite was executed. Run tests before claiming "verified".)');
1522
+ }
1523
+ if (testPresenceGateEnabled()) {
1524
+ const presence = evaluateTestPresence(report.ownedFiles);
1525
+ if (!presence.ok) {
1526
+ lines.push("", `\u26A0\uFE0F \u96F6\u6D4B\u8BD5\u4EA4\u4ED8\uFF1A${presence.detail}`);
1527
+ lines.push(' \u4EA4\u4ED8\u62A5\u544A\u4E2D\u4E0D\u8981\u5BA3\u79F0"\u5DF2\u9A8C\u8BC1"\u2014\u2014\u8FD9\u6279\u6E90\u6587\u4EF6\u6CA1\u6709\u4EFB\u4F55\u6D4B\u8BD5\u80CC\u4E66\u3002');
1528
+ }
1529
+ }
1530
+ if (ctx.taskLedger && process.env.RIVET_RENDER_VERIFY !== "0") {
1531
+ const visualWarn = detectMissingVisualVerify(
1532
+ report.ownedFiles,
1533
+ () => ctx.taskLedger.getEvents()
1534
+ );
1535
+ if (visualWarn) lines.push("", visualWarn);
1536
+ }
1537
+ const hasVerificationDiagnostics = report.currentBlockingFailure || report.supersededFailures > 0;
1538
+ if (hasVerificationDiagnostics) {
1539
+ lines.push("");
1540
+ if (report.currentBlockingFailure) {
1541
+ lines.push(` \u963B\u65AD\u9879\uFF1A${report.currentBlockingFailure}`);
1542
+ }
1543
+ if (report.staleFailureCandidates > 0) {
1544
+ lines.push(` \u9884\u5B58\u91CF\u5931\u8D25\uFF1A${report.staleFailureCandidates} \u6761\uFF08\u6539\u52A8\u524D\u5DF2\u5B58\u5728\uFF0C\u4E0D\u5F52\u672C\u6B21\u6539\u52A8\uFF0C\u53EF force \u4EA4\u4ED8\uFF09`);
1545
+ }
1546
+ if (report.attributionClass === "no_test_infra") {
1547
+ lines.push("", " \u26A0\uFE0F \u6D4B\u8BD5\u57FA\u7840\u8BBE\u65BD\u7F3A\u5931 \u2014 \u9879\u76EE\u6CA1\u6709\u53EF\u81EA\u52A8\u68C0\u6D4B\u7684\u6D4B\u8BD5\u6846\u67B6\u6216\u6D4B\u8BD5\u6587\u4EF6\u3002");
1548
+ lines.push(" run_tests \u6BCF\u6B21\u90FD\u4F1A\u4EE5\u540C\u6837\u539F\u56E0\u53D7\u963B\uFF0C\u7EE7\u7EED\u91CD\u8BD5\u4E0D\u4F1A\u6539\u53D8\u7ED3\u679C\u3002");
1549
+ lines.push(" \u5EFA\u8BAE\u5411\u7528\u6237\u62A5\u544A\uFF1A");
1550
+ lines.push(" 1. \u5F53\u524D\u9879\u76EE\u7F3A\u5C11\u4EC0\u4E48\uFF08package.json \u4E2D\u7684 test \u811A\u672C / pytest / vitest \u7B49\uFF09");
1551
+ lines.push(" 2. \u7528\u6237\u662F\u5426\u9700\u8981\u534F\u52A9\u642D\u5EFA\u6D4B\u8BD5\u6846\u67B6");
1552
+ lines.push(" 3. \u6216\u8005\u7528 bash \u8FD0\u884C\u66FF\u4EE3\u9A8C\u8BC1\uFF08\u7F16\u8BD1\u68C0\u67E5/\u624B\u52A8\u6D4B\u8BD5/\u811A\u672C\u8F93\u51FA\u68C0\u67E5\uFF09\u540E\u4EA4\u4ED8");
1553
+ lines.push(' \u26A0 \u4E0D\u8981\u53EA\u8BF4"\u8BF7\u8FD0\u884C\u6D4B\u8BD5"\u2014\u2014\u9879\u76EE\u6839\u672C\u6CA1\u6709\u6D4B\u8BD5\u53EF\u4EE5\u8FD0\u884C\u3002');
1554
+ }
1555
+ }
1556
+ const projectMemory = ctx.getProjectMemoryContent?.(params.cwd) ?? readProjectMemory(params.cwd);
1557
+ const reviewChecklist = projectMemory ? buildReviewPrincipleChecklist({ knowledgeMarkdown: projectMemory, changedFiles: report.ownedFiles }) : [];
1558
+ const health = summarizeOwnershipHealth({
1559
+ ownedFiles: report.ownedFiles,
1560
+ coOwnedFiles: report.coOwnedFiles,
1561
+ externalFiles: report.externalFiles,
1562
+ dirtyFiles: currentDirtyFiles ?? [...report.ownedFiles, ...report.coOwnedFiles, ...report.externalFiles]
1563
+ });
1564
+ if (health.warningLines.length > 0) {
1565
+ lines.push("", "Ownership health warnings:");
1566
+ lines.push(...health.warningLines.map((line) => ` ${line}`));
1567
+ }
1568
+ if (health.infoLines.length > 0) {
1569
+ lines.push("", "Ownership caveats:");
1570
+ lines.push(...health.infoLines.map((line) => ` ${line}`));
1571
+ }
1572
+ if (report.blockingReason) {
1573
+ lines.push("", `\u26A0\uFE0F Blocking: ${report.blockingReason}`);
1574
+ }
1575
+ const claimConflicts = [];
1576
+ if (ctx.sessionRegistry && ctx.sessionId && report.ownedFiles.length > 0) {
1577
+ for (const f of report.ownedFiles) {
1578
+ const claim = ctx.sessionRegistry.checkClaim(f);
1579
+ if (claim && claim.sessionId !== ctx.sessionId) {
1580
+ claimConflicts.push({ file: f, holder: claim.sessionId, claimType: claim.claimType });
1581
+ }
1582
+ }
1583
+ if (claimConflicts.length > 0) {
1584
+ lines.push("", "\u26A0\uFE0F Cross-session claim conflicts:");
1585
+ for (const c of claimConflicts) {
1586
+ const claimKind = c.claimType === "exclusive" ? "exclusive lock" : "shared read";
1587
+ lines.push(` ${c.file} \u2014 ${claimKind} held by session ${c.holder}`);
1588
+ }
1589
+ lines.push("", " (Continue only if you have verified this conflict is safe to override.)");
1590
+ }
1591
+ }
1592
+ lines.push("", `Attribution: ${report.attributionSummary}`);
1593
+ {
1594
+ const parts = [];
1595
+ if (report.currentBlockingFailure) parts.push(`1 \u6761\u963B\u65AD\u5931\u8D25\uFF08\u4F60\u9700\u5904\u7406\uFF09`);
1596
+ if (report.supersededFailures > 0) parts.push(`${report.supersededFailures} \u6761\u9884\u5B58\u91CF\uFF08\u6539\u52A8\u524D\u5DF2\u5B58\u5728\uFF0C\u4E0D\u5F52\u4F60\uFF09`);
1597
+ if (parts.length > 0) {
1598
+ lines.push(`\u5931\u8D25\u5F52\u56E0\uFF1A${parts.join(" | ")}`);
1599
+ lines.push('\u2192 \u53EA\u5904\u7406"\u963B\u65AD\u5931\u8D25"\u2014\u2014\u90A3\u662F\u4F60\u7684\u6539\u52A8\u5F15\u5165\u7684\u3002\u9884\u5B58\u91CF\u5931\u8D25\u4E0D\u5F52\u4F60\uFF0C\u53EF force \u4EA4\u4ED8\u3002');
1600
+ }
1601
+ }
1602
+ const recoverySessionId = ctx.sessionId ?? params.sessionId;
1603
+ const recoveries = readUnacknowledged(params.cwd, recoverySessionId);
1604
+ if (recoveries.length > 0) {
1605
+ lines.push("", "--- Recovery Journal ---");
1606
+ lines.push(" Files restored during this session (edit failure \u2192 restore):");
1607
+ for (const r of recoveries) {
1608
+ lines.push(` \u26A0\uFE0F ${r.file} (${r.action}, ~${r.linesLost} lines lost at ${r.ts.slice(11, 19)})`);
1609
+ }
1610
+ lines.push("", " \u26A0\uFE0F Verify no intended changes were lost in these recoveries.");
1611
+ lines.push(" If all recovered changes have been re-applied, this warning will clear on next deliver_task.");
1612
+ }
1613
+ const commit = params.input.commit === true;
1614
+ const message = params.input.message;
1615
+ const auditList = params.input.checklist;
1616
+ if (auditList && Array.isArray(auditList) && auditList.length > 0) {
1617
+ const incomplete = auditList.filter((entry) => !entry.done);
1618
+ const complete = auditList.filter((entry) => entry.done);
1619
+ lines.push("", "--- Task Completion Audit ---");
1620
+ for (const entry of complete) {
1621
+ lines.push(` \u2705 ${entry.item}` + (entry.files?.length ? ` (${entry.files.join(", ")})` : ""));
1622
+ }
1623
+ for (const entry of incomplete) {
1624
+ lines.push(` \u26A0\uFE0F NOT DONE: ${entry.item}`);
1625
+ }
1626
+ if (incomplete.length > 0) {
1627
+ const totalCount = auditList.length;
1628
+ const doneCount = complete.length;
1629
+ if (totalCount >= 4) {
1630
+ const remainingRatio = incomplete.length / totalCount;
1631
+ if (remainingRatio > 0.4) {
1632
+ lines.push("", ` \u{1F4A1} ${incomplete.length}/${totalCount} tasks remaining (${Math.round(remainingRatio * 100)}%). Consider pausing after this wave \u2014 typecheck+test the completed batch, then continue with the next ${Math.min(incomplete.length, 3)} tasks.`);
1633
+ } else {
1634
+ lines.push("", ` \u26A0\uFE0F ${incomplete.length} of ${totalCount} tasks incomplete. Verify these are intentionally deferred to the next wave, not forgotten.`);
1635
+ }
1636
+ } else {
1637
+ lines.push("", " \u26A0\uFE0F Incomplete tasks detected. Verify these are intentionally deferred, not forgotten.");
1638
+ }
1639
+ }
1640
+ }
1641
+ try {
1642
+ const { isRefactorObjective } = await import("./task-contract-6XHBNJQK.js");
1643
+ const contract = ctx.getTaskContract?.();
1644
+ const inventory = contract?.regressionInventory && contract.regressionInventory.length > 0 ? contract.regressionInventory : findApprovedPlanInventory(params.cwd);
1645
+ const refactorish = isRefactorObjective(contract?.objective ?? "") || isRefactorObjective(typeof params.input.message === "string" ? params.input.message : "");
1646
+ if (inventory && inventory.length > 0) {
1647
+ const results = verifyRegressionInventory(params.cwd, inventory, ctx.inventorySearcher);
1648
+ lines.push(...formatInventoryReport(results));
1649
+ } else if (refactorish) {
1650
+ lines.push("", "\u26A0\uFE0F \u91CD\u6784\u7C7B\u4EA4\u4ED8\u7F3A\u5C11\u56DE\u5F52\u6E05\u5355\uFF08task contract regressionInventory / \u8BA1\u5212\u300C\u56DE\u5F52\u6E05\u5355\u300D\u7AE0\u8282\u5747\u4E3A\u7A7A\uFF09\u3002");
1651
+ lines.push(" \u884C\u4E3A\u7B49\u4EF7\u672A\u6838\u9A8C \u2192 \u6309 YELLOW \u5904\u7406\uFF1A\u4EA4\u4ED8\u62A5\u544A\u5FC5\u987B\u8BF4\u660E\u54EA\u4E9B\u65E2\u6709\u529F\u80FD\uFF08\u8DEF\u7531/\u5BFC\u822A/\u5BFC\u51FA/\u547D\u4EE4\u5165\u53E3\uFF09\u5DF2\u4EBA\u5DE5\u786E\u8BA4\u4ECD\u5728\u3002");
1652
+ }
1653
+ } catch {
1654
+ }
1655
+ try {
1656
+ const storedJson = getStoredPlan(params.sessionId);
1657
+ const storedPlan = storedJson ? deserializeUnifiedPlan(storedJson) : null;
1658
+ if (storedPlan && ctx.taskLedger) {
1659
+ const declared = declaredVerificationCommands(storedPlan);
1660
+ if (declared.length > 0) {
1661
+ const reconciled = reconcileVerificationCommands(declared, ctx.taskLedger.getVerifications());
1662
+ lines.push(...formatVerificationReconcileReport(reconciled));
1663
+ }
1664
+ }
1665
+ if (storedPlan?.obligations && storedPlan.obligations.length > 0) {
1666
+ const runGate = ctx.obligationGateRunner ?? ((command) => runGateAsync(command, params.cwd));
1667
+ const results = await verifyObligations(storedPlan.obligations, runGate);
1668
+ lines.push(...formatObligationReport(results));
1669
+ }
1670
+ } catch {
1671
+ }
1672
+ try {
1673
+ const converged = ctx.getPalConvergedCases?.() ?? [];
1674
+ if (converged.length > 0) {
1675
+ const deliveredFiles = [
1676
+ ...report.ownedFiles,
1677
+ ...report.coOwnedFiles,
1678
+ ...currentDirtyFiles ?? []
1679
+ ];
1680
+ for (const c of converged) {
1681
+ const fileTargets = c.targets.filter((t) => t.includes("/") || t.includes("."));
1682
+ if (fileTargets.length === 0) continue;
1683
+ const touched = fileTargets.some((t) => deliveredFiles.some((f) => f.includes(t) || t.includes(f)));
1684
+ if (!touched) {
1685
+ lines.push("", `\u26A0\uFE0F \u653B\u575A\u6848\u4EF6 ${c.caseId} \u5DF2\u6536\u655B\uFF08\u5047\u8BBE ${c.selectedHypothesisId}\uFF0Ctargets: ${fileTargets.slice(0, 3).join(", ")}${fileTargets.length > 3 ? " \u2026" : ""}\uFF09\uFF0C\u4F46\u8FD9\u4E9B targets \u4E0D\u5728\u672C\u6B21\u4EA4\u4ED8\u6587\u4EF6\u4E2D\u2014\u2014\u786E\u8BA4\u4FEE\u590D\u771F\u7684\u843D\u5728\u4E86\u6536\u655B\u5047\u8BBE\u6307\u5411\u7684\u4F4D\u7F6E\u3002`);
1686
+ }
1687
+ }
1688
+ }
1689
+ } catch {
1690
+ }
1691
+ try {
1692
+ const needsUser = ctx.getPalNeedsUserCases?.() ?? [];
1693
+ for (const c of needsUser) {
1694
+ lines.push("", `\u26A0\uFE0F \u653B\u575A\u6848\u4EF6 ${c.caseId} \u5361\u5728\u7B49\u7528\u6237\u88C1\u51B3\uFF08needs_user\uFF09\uFF1A${c.problem} \u2014\u2014 \u4EA4\u4ED8\u62A5\u544A\u5FC5\u987B\u628A\u5B83\u5217\u4E3A\u9057\u7559\u9879\uFF0C\u5E76\u9644\u6700\u5C0F\u51B3\u7B56\u95EE\u9898\uFF1A${c.minimalQuestion}`);
1695
+ }
1696
+ } catch {
1697
+ }
1698
+ try {
1699
+ const learnedProvided = Array.isArray(params.input.learned) && params.input.learned.length > 0;
1700
+ const palActivity = (ctx.getPalConvergedCases?.() ?? []).length > 0 || (ctx.getPalNeedsUserCases?.() ?? []).length > 0;
1701
+ if (!learnedProvided && (palActivity || report.ownedFileCount >= 3)) {
1702
+ lines.push(
1703
+ "",
1704
+ "### \u77E5\u8BC6\u6536\u5272\uFF08\u865A\u7A7A\u4ED3\u5E93\uFF09",
1705
+ "\u672C\u6B21 session \u82E5\u786E\u8BA4\u4E86\u53EF\u590D\u7528\u6A21\u5F0F\uFF08\u8BCA\u65AD\u65B9\u6CD5/\u67B6\u6784\u8FB9\u754C/\u5DE5\u5177\u94FE\u4E8B\u5B9E/\u5DF2\u9A8C\u8BC1\u4FEE\u590D\uFF09\uFF0C",
1706
+ '\u5728 commit \u8C03\u7528\u65F6\u901A\u8FC7 learned \u53C2\u6570\u63D0\u4EA4\uFF08\u6BCF\u6761\uFF1A"\u6A21\u5F0F\u63CF\u8FF0\u2014\u2014\u8BC1\u636E\uFF1A\u8DEF\u5F84\u6216\u590D\u73B0\u6B65\u9AA4"\uFF09\u3002\u4E0D\u786E\u5B9A\u7684\u4E0D\u8981\u5199\u3002'
1707
+ );
1708
+ }
1709
+ } catch {
1710
+ }
1711
+ let redundancyBlocked = false;
1712
+ const obligationStore = ctx.getObligationStore?.();
1713
+ if (obligationStore) {
1714
+ const unsatisfied = obligationStore.obligations.filter(
1715
+ (o) => o.redundancy && o.state !== "satisfied" && o.state !== "superseded"
1716
+ );
1717
+ if (unsatisfied.length > 0) {
1718
+ redundancyBlocked = true;
1719
+ lines.push("", "\u274C \u5197\u4F59\u9A8C\u8BC1\u672A\u8FBE quorum\uFF08blocked\uFF0C\u4E0D\u9759\u9ED8\u964D\u7EA7\uFF09\uFF1A");
1720
+ for (const ob of unsatisfied) {
1721
+ lines.push(` - \u4E49\u52A1\u300C${ob.claim}\u300D\u9700\u8981 ${ob.redundancy.k} \u4E2A\u72EC\u7ACB\u8BC1\u636E\uFF0C\u5F53\u524D ${ob.satisfyCount ?? 0} \u4E2A\uFF08\u72B6\u6001 ${ob.state}\uFF09`);
1722
+ }
1723
+ lines.push(" \u63D0\u4F9B\u989D\u5916\u7684\u72EC\u7ACB\u8BC1\u636E\uFF08DP \u526F\u672C\u9A8C\u8BC1\u3001\u72EC\u7ACB\u5DE5\u5177\u590D\u73B0\uFF09\u540E\u518D\u6B21 deliver\u3002");
1724
+ }
1725
+ }
1726
+ const firewallTracker = ctx.getClaimTracker?.();
1727
+ const deliveryText = [
1728
+ typeof params.input.message === "string" ? params.input.message : "",
1729
+ ...(auditList ?? []).map((e) => e.item)
1730
+ ].join("\n");
1731
+ const firewallRefs = firewallTracker ? findUnverifiedClaimRefs(firewallTracker, deliveryText) : [];
1732
+ if (firewallRefs.length > 0) {
1733
+ lines.push("", `\u26A0 \u8BC1\u636E\u9632\u706B\u5899\uFF1A\u4EA4\u4ED8\u6587\u672C\u5F15\u7528\u4E86 delegate \u62A5\u544A\u7684\u8DEF\u5F84\uFF0C\u4F46\u672C\u4F1A\u8BDD\u672A\u72EC\u7ACB\u6838\u9A8C\uFF1A${firewallRefs.join("\u3001")}`);
1734
+ lines.push(" worker \u7684 file:line \u662F\u5F85\u6838\u9A8C\u5047\u8BBE\u2014\u2014read_file/grep \u786E\u8BA4\u540E\u518D\u4F5C\u4E3A\u4EA4\u4ED8\u7ED3\u8BBA\uFF0C\u6216\u6807\u6CE8 [\u5F85\u6838] \u964D\u7EA7\u4E3A\u7EBF\u7D22\u3002");
1735
+ }
1736
+ if (commit) {
1737
+ if (ctx.autoCommit === false) {
1738
+ lines.push(
1739
+ "",
1740
+ "\u{1F6D1} \u81EA\u52A8\u63D0\u4EA4\u5DF2\u5173\u95ED\uFF08agent.delivery.autoCommit = false\uFF09\u3002",
1741
+ "\u4EE5\u4E0A\u4EA4\u4ED8\u62A5\u544A\u4E0E\u95E8\u7981\u72B6\u6001\u4EC5\u4F9B\u53C2\u8003\u2014\u2014\u53D8\u66F4\u672A\u63D0\u4EA4\u3002",
1742
+ "\u5BA1\u67E5\u5B8C\u6210\u540E\u7531\u7528\u6237\u624B\u52A8\u6267\u884C git commit\u3002"
1743
+ );
1744
+ return { content: lines.join("\n") };
1745
+ }
1746
+ lines.push(
1747
+ "",
1748
+ "<atomic-commit-reminder>",
1749
+ "\u63D0\u4EA4\u524D\u53EA\u786E\u8BA4\u672C\u6B21\u4E00\u4E2A\u903B\u8F91\u5355\u5143\uFF1A",
1750
+ "1. \u662F\u5426\u53EA\u5305\u542B\u5F53\u524D\u4EFB\u52A1\u6587\u4EF6\uFF1F",
1751
+ "2. \u662F\u5426\u6DF7\u5165\u5916\u90E8/\u4ED6\u4EBA\u6539\u52A8\uFF1F",
1752
+ "3. \u6D4B\u8BD5\u4E0E typecheck \u662F\u5426\u5BF9\u5E94\u672C\u903B\u8F91\u5355\u5143\uFF1F",
1753
+ "4. commit message \u662F\u5426\u63CF\u8FF0\u4E00\u4E2A\u539F\u5B50\u53D8\u66F4\uFF1F",
1754
+ "</atomic-commit-reminder>"
1755
+ );
1756
+ const forceGate = params.input.force === true;
1757
+ if (commit && firewallRefs.length > 0 && isScoutFirewallEnabled(ctx.scoutFirewallConfig)) {
1758
+ lines.push("", "\u274C Cannot commit: evidence firewall \u2014 \u4EA4\u4ED8\u5F15\u7528\u4E86\u672A\u7ECF\u72EC\u7ACB\u6838\u9A8C\u7684 scout/delegate \u65AD\u8A00\u3002");
1759
+ lines.push(" \uFF08force \u4E0D\u8C41\u514D\uFF1A\u8FD9\u662F\u5F53\u524D\u4EA4\u4ED8\u7684\u8BC1\u636E\u4E49\u52A1\uFF0C\u4E0D\u662F\u9884\u5B58\u91CF\u5931\u8D25\u3002\uFF09");
1760
+ lines.push("", "Recovery:");
1761
+ lines.push(` \u2192 \u7528 read_file / grep \u9010\u4E00\u6838\u9A8C\uFF1A${firewallRefs.join("\u3001")}\uFF0C\u7136\u540E\u91CD\u8DD1 deliver_task`);
1762
+ lines.push(" \u2192 \u6216\u4ECE\u63D0\u4EA4\u4FE1\u606F/\u6E05\u5355\u4E2D\u79FB\u9664\u8BE5\u5F15\u7528\uFF0C\u6216\u4E3A\u8BE5\u884C\u6DFB\u52A0 [\u5F85\u6838] \u6807\u6CE8\uFF08\u8BDA\u5B9E\u964D\u7EA7\u4E3A\u7EBF\u7D22\uFF09\u3002");
1763
+ return { content: lines.join("\n"), isError: true };
1764
+ }
1765
+ let mechanicalClass;
1766
+ if (isMechanicalFastPathEnabled(ctx.reviewConfig)) {
1767
+ mechanicalClass = classifyChange(
1768
+ report.ownedFiles,
1769
+ createGitDiffProvider(params.cwd, report.ownedFiles, currentDirtyFiles ?? void 0)
1770
+ );
1771
+ }
1772
+ if (report.state === "RED" || redundancyBlocked) {
1773
+ if (redundancyBlocked) {
1774
+ lines.push("", "\u274C Cannot commit: redundancy obligations not met \u2014 quorum evidence insufficient.");
1775
+ lines.push("", "Recovery:");
1776
+ lines.push(" \u2192 \u63D0\u4F9B\u989D\u5916\u7684\u72EC\u7ACB\u8BC1\u636E\u6EE1\u8DB3\u5197\u4F59\u4E49\u52A1\uFF08DP \u526F\u672C\u9A8C\u8BC1\u3001\u72EC\u7ACB\u5DE5\u5177\u590D\u73B0\u3001");
1777
+ lines.push(" \u7B2C\u4E8C\u6761 file:line \u8BC1\u636E\u94FE\uFF09\uFF0C\u6216\u663E\u5F0F supersede \u8BE5\u4E49\u52A1\u540E\u91CD\u8BD5\u3002");
1778
+ return { content: lines.join("\n"), isError: true };
1779
+ } else if (forceGate && report.supersededFailures > 0) {
1780
+ lines.push("", "\u26A0\uFE0F RED overridden (force=true): superseded failures detected (these were later fixed).");
1781
+ lines.push(" Verify these pre-existing failures are unrelated to your changes before proceeding.");
1782
+ } else if (report.attributionClass === "unverified" && mechanicalClass?.skipVerification) {
1783
+ lines.push("", `\u2705 \u673A\u68B0\u5F0F\u53D8\u66F4 (${mechanicalClass.class})\uFF0C\u514D\u9A8C\u8BC1\u4EA4\u4ED8\uFF1A${mechanicalClass.reason}`);
1784
+ } else {
1785
+ lines.push("", "\u274C Cannot commit: delivery gate is RED.");
1786
+ if (report.supersededFailures > 0) {
1787
+ lines.push(" (Superseded failures found \u2014 these were later fixed, use force=true if pre-existing.)");
1788
+ }
1789
+ lines.push("", "Recovery:");
1790
+ if (report.blockingReason) {
1791
+ lines.push(` Reason: ${report.blockingReason}`);
1792
+ }
1793
+ if (report.currentBlockingFailure) {
1794
+ lines.push(` Detail: ${report.currentBlockingFailure}`);
1795
+ }
1796
+ lines.push("");
1797
+ if (report.ownedFiles.length > 0 && report.verificationCount === 0) {
1798
+ lines.push(" \u2192 Files are unverified. Run TARGETED tests first:");
1799
+ lines.push(' Use run_tests with filter="test-file-name" for each changed file.');
1800
+ lines.push(" Use related_tests(sourceFile) to find the right test file.");
1801
+ lines.push("");
1802
+ lines.push(" Do NOT run the full test suite as first step \u2014 it may timeout.");
1803
+ } else {
1804
+ lines.push(" \u2192 Fix the blocking issue above, then re-run deliver_task.");
1805
+ lines.push(" If tests keep timing out, run them in smaller batches by directory.");
1806
+ }
1807
+ return { content: lines.join("\n"), isError: true };
1808
+ }
1809
+ }
1810
+ if (report.state === "YELLOW" && report.attributionClass === "module_unverified") {
1811
+ if (forceGate) {
1812
+ lines.push("", "\u26A0\uFE0F module_unverified overridden (force=true): impacted tests remain unverified.");
1813
+ lines.push(" Confirm the uncovered tests are static-analysis false positives before proceeding.");
1814
+ } else {
1815
+ lines.push("", "\u274C Cannot commit: impacted tests were never covered by a passed verification.");
1816
+ lines.push(` Reason: ${report.attributionSummary}`);
1817
+ const uncovered = report.uncoveredImpactedTests ?? [];
1818
+ if (uncovered.length > 0) {
1819
+ lines.push("", " Uncovered impacted tests:");
1820
+ for (const t of uncovered.slice(0, 10)) lines.push(` ${t}`);
1821
+ if (uncovered.length > 10) lines.push(` (+${uncovered.length - 10} more)`);
1822
+ }
1823
+ lines.push("", " \u2192 Run these tests (run_tests with filter) or a full-scope verification, then re-run deliver_task.");
1824
+ lines.push(" If they are unrelated to your change (static-analysis false positive), use force=true.");
1825
+ return { content: lines.join("\n"), isError: true };
1826
+ }
1827
+ }
1828
+ if (report.state === "YELLOW") {
1829
+ const stanceHint = detectSymptomPatch(params.cwd);
1830
+ if (stanceHint) lines.push("", stanceHint);
1831
+ }
1832
+ if (!message) {
1833
+ lines.push("", '\u274C Commit requires a "message" parameter.');
1834
+ return { content: lines.join("\n"), isError: true };
1835
+ }
1836
+ if (claimAuditEnabled()) {
1837
+ const checklistText = (auditList ?? []).map((e) => e.item).join("\n");
1838
+ const audit = auditDeliveryClaims({
1839
+ claimText: `${message}
1840
+ ${checklistText}`,
1841
+ events: ctx.taskLedger.getEvents()
1842
+ });
1843
+ if (audit.status === "block") {
1844
+ lines.push("", ...audit.lines);
1845
+ return { content: lines.join("\n"), isError: true };
1846
+ }
1847
+ if (audit.status === "warn") {
1848
+ lines.push("", ...audit.lines);
1849
+ }
1850
+ }
1851
+ const adoptFiles = params.input.adopt;
1852
+ if (adoptFiles && Array.isArray(adoptFiles) && adoptFiles.length > 0) {
1853
+ const externalSet = new Set(report.externalFiles);
1854
+ const coOwnedSet = new Set(report.coOwnedFiles);
1855
+ const adoptableSet = /* @__PURE__ */ new Set([...externalSet, ...coOwnedSet]);
1856
+ const notAdoptable = adoptFiles.filter((f) => !adoptableSet.has(f));
1857
+ if (notAdoptable.length > 0) {
1858
+ lines.push("", `\u274C Adopt: file(s) not in external or co-owned files: ${notAdoptable.join(", ")}. Only external and co-owned files can be adopted.`);
1859
+ return { content: lines.join("\n"), isError: true };
1860
+ }
1861
+ const adopted = ctx.ownership.adoptFiles(adoptFiles);
1862
+ if (adopted.length > 0) {
1863
+ const wasCoOwned = adopted.filter((f) => coOwnedSet.has(f));
1864
+ const wasExternal = adopted.filter((f) => externalSet.has(f));
1865
+ if (wasCoOwned.length > 0) lines.push("", `\u{1F513} Adopted ${wasCoOwned.length} co-owned file(s) into exclusive ownership:`);
1866
+ if (wasExternal.length > 0) lines.push("", `\u{1F513} Adopted ${wasExternal.length} external file(s) into owned set:`);
1867
+ for (const f of adopted) lines.push(` ${f}`);
1868
+ if (wasCoOwned.length > 0) lines.push("", " \u26A0\uFE0F These files were shared with another session. Verify the other session has finished before committing.");
1869
+ if (wasExternal.length > 0) lines.push("", " \u26A0\uFE0F These files were modified by another session. Verify the changes are correct before committing.");
1870
+ }
1871
+ } else if (adoptFiles && Array.isArray(adoptFiles) && adoptFiles.length === 0) {
1872
+ }
1873
+ const postAdoptionReport = adoptFiles && Array.isArray(adoptFiles) && adoptFiles.length > 0 ? ctx.gate.getReport([], currentDirtyFiles, ctx.getCurrentSnapshotRef?.(), moduleCoverage) : report;
1874
+ if (postAdoptionReport.state === "RED") {
1875
+ const postAdoptionBypass = postAdoptionReport.attributionClass === "unverified" && mechanicalClass?.skipVerification;
1876
+ if (!postAdoptionBypass) {
1877
+ lines.push("", "\u274C Cannot commit: delivery gate is RED after adoption.");
1878
+ if (postAdoptionReport.blockingReason) {
1879
+ lines.push(` Reason: ${postAdoptionReport.blockingReason}`);
1880
+ }
1881
+ if (postAdoptionReport.currentBlockingFailure) {
1882
+ lines.push(` Detail: ${postAdoptionReport.currentBlockingFailure}`);
1883
+ }
1884
+ lines.push(" \u2192 Run verification for the adopted files, then re-run deliver_task.");
1885
+ return { content: lines.join("\n"), isError: true };
1886
+ }
1887
+ }
1888
+ const requestedFiles = params.input.files;
1889
+ const currentOwnedFiles = ctx.ownership.getOwnedFiles();
1890
+ let filesToCommit = currentOwnedFiles;
1891
+ if (requestedFiles && Array.isArray(requestedFiles) && requestedFiles.length > 0) {
1892
+ const ownedSet = new Set(currentOwnedFiles);
1893
+ const notOwned = requestedFiles.filter((f) => !ownedSet.has(f));
1894
+ if (notOwned.length > 0) {
1895
+ lines.push("", `\u274C File(s) not in owned files: ${notOwned.join(", ")}. Cannot commit non-owned files.`);
1896
+ return { content: lines.join("\n"), isError: true };
1897
+ }
1898
+ filesToCommit = requestedFiles;
1899
+ } else if (requestedFiles && Array.isArray(requestedFiles) && requestedFiles.length === 0) {
1900
+ lines.push("", "\u274C No files specified for commit. Provide non-empty files array or omit to commit all owned files.");
1901
+ return { content: lines.join("\n"), isError: true };
1902
+ }
1903
+ const commitConflictFiles = new Set(filesToCommit);
1904
+ const blockingClaimConflicts = claimConflicts.filter((conflict) => commitConflictFiles.has(conflict.file));
1905
+ if (blockingClaimConflicts.length > 0 && !forceGate) {
1906
+ lines.push("", "\u274C Cannot commit: cross-session claim conflicts are present.");
1907
+ lines.push(" \u2192 Resolve the other session claim or use force=true only after independently verifying the override is safe.");
1908
+ return { content: lines.join("\n"), isError: true };
1909
+ }
1910
+ if (blockingClaimConflicts.length > 0 && forceGate) {
1911
+ lines.push("", "\u26A0\uFE0F Cross-session claim conflicts overridden with force=true. Verify the other session has finished or approved the takeover.");
1912
+ }
1913
+ try {
1914
+ const deadSymbols = (ctx.detectWroteButNeverRead ?? detectWroteButNeverRead)(params.cwd, filesToCommit);
1915
+ lines.push(...formatWroteButNeverRead(deadSymbols));
1916
+ } catch {
1917
+ }
1918
+ try {
1919
+ const falseGreen = (ctx.detectReadButNeverProduced ?? detectReadButNeverProduced)(params.cwd, filesToCommit);
1920
+ lines.push(...formatReadButNeverProduced(falseGreen));
1921
+ } catch {
1922
+ }
1923
+ try {
1924
+ const scanner = ctx.scanProbes ?? ((files, cwd) => {
1925
+ return scanFilesForProbes(files, cwd, (p) => {
1926
+ try {
1927
+ return readFileSync3(p, "utf-8");
1928
+ } catch {
1929
+ return null;
1930
+ }
1931
+ });
1932
+ });
1933
+ const probeHits = scanner(filesToCommit, params.cwd);
1934
+ if (probeHits.length > 0) {
1935
+ lines.push(...formatProbeHits(probeHits));
1936
+ }
1937
+ } catch {
1938
+ }
1939
+ try {
1940
+ const structure = (ctx.checkStructure ?? checkStructureGate)(filesToCommit, params.cwd);
1941
+ if (structure.needsWarning) {
1942
+ lines.push("", ...structure.warningLines.map((l) => ` ${l}`));
1943
+ }
1944
+ } catch {
1945
+ }
1946
+ const cohesionOverride = forceGate || adoptFiles && Array.isArray(adoptFiles) && adoptFiles.length > 0;
1947
+ const cohesion = checkCommitCohesion(filesToCommit);
1948
+ mark("cohesion:done");
1949
+ if (cohesion.needsWarning && !cohesionOverride) {
1950
+ lines.push("", ...cohesion.warningLines.map((l) => ` ${l}`));
1951
+ mark("cohesion:denied");
1952
+ return { content: lines.join("\n"), isError: true };
1953
+ }
1954
+ if (cohesion.needsWarning && cohesionOverride) {
1955
+ lines.push("", " \u26A0\uFE0F Cohesion gate overridden with force=true. Verify this is truly one logical unit.");
1956
+ }
1957
+ const lockPath = join3(params.cwd, ".git", "index.lock");
1958
+ if (existsSync2(lockPath)) {
1959
+ try {
1960
+ const lockAge = Date.now() - statSync(lockPath).mtimeMs;
1961
+ if (lockAge > 3e4) {
1962
+ unlinkSync(lockPath);
1963
+ lines.push("", " \u267B\uFE0F Cleared a stale git lock file before committing.");
1964
+ } else {
1965
+ lines.push("", ` \u26A0\uFE0F Git index is locked (another session mid-commit, lock age ${Math.round(lockAge / 1e3)}s).`);
1966
+ lines.push(" Wait a few seconds and retry deliver_task. Do NOT fall back to raw git commit \u2014 that bypasses ownership scoping.");
1967
+ return { content: lines.join("\n"), isError: true };
1968
+ }
1969
+ } catch {
1970
+ }
1971
+ }
1972
+ const headBefore = spawnGitSync(["rev-parse", "--short", "HEAD"], { cwd: params.cwd, encoding: "utf-8", timeout: 5e3 });
1973
+ mark("commit:before");
1974
+ const headBeforeHash = headBefore.status === 0 ? headBefore.stdout.trim() : null;
1975
+ const executor = ctx.commitOwnedFiles ?? ((cwd, files, msg) => commitScopedFiles({ cwd, files, message: msg }));
1976
+ const commitResult = executor(params.cwd, filesToCommit, message);
1977
+ if (!commitResult.ok) {
1978
+ lines.push("", `\u274C Scoped commit failed: ${commitResult.output}`);
1979
+ return { content: lines.join("\n"), isError: true };
1980
+ }
1981
+ lines.push("", `\u2705 Scoped commit created with message: "${message}"`);
1982
+ lines.push(` Files: ${filesToCommit.join(", ") || "(none)"}`);
1983
+ if (commitResult.output) lines.push(` ${commitResult.output}`);
1984
+ const headAfter = spawnGitSync(["rev-parse", "--short", "HEAD"], { cwd: params.cwd, encoding: "utf-8", timeout: 5e3 });
1985
+ mark("commit:after");
1986
+ const headAfterHash = headAfter.status === 0 ? headAfter.stdout.trim() : null;
1987
+ if (headBeforeHash && headAfterHash) {
1988
+ if (headBeforeHash !== headAfterHash) {
1989
+ lines.push(` VERIFIED: HEAD moved ${headBeforeHash} \u2192 ${headAfterHash}. The commit is real \u2014 do NOT re-attempt it.`);
1990
+ } else {
1991
+ lines.push(` \u26A0\uFE0F WARNING: HEAD did not move (still ${headAfterHash}). The commit may not have landed \u2014 verify with git log before retrying.`);
1992
+ }
1993
+ }
1994
+ const readback = spawnGitSync(["-c", "core.quotePath=false", "show", "--stat", "--format=%h%d", "HEAD"], { cwd: params.cwd, encoding: "utf-8", timeout: 1e4 });
1995
+ if (readback.status === 0 && readback.stdout.trim()) {
1996
+ lines.push("", "--- actual changes (git show --stat) ---");
1997
+ lines.push(readback.stdout.trim());
1998
+ }
1999
+ if (recoveries.length > 0) acknowledgeAll(params.cwd, recoverySessionId);
2000
+ const explicitReviewLevel = params.input.review_level;
2001
+ const skipAutoReview = params.input.skipAutoReview === true || ctx.isGoalActive?.() === true || ctx.reviewConfig?.skipAuto === true || ctx.isAutoReviewOff?.() === true;
2002
+ const goalAchieved = ctx.isGoalAchieved?.() === true;
2003
+ const goalVerdict = ctx.getLastVerdict?.() ?? null;
2004
+ const reviewPolicyRaw = params.input.review_policy;
2005
+ const reviewPolicy = reviewPolicyRaw === "defer" || reviewPolicyRaw === "final" ? reviewPolicyRaw : "each";
2006
+ const effectiveReviewLevel = goalAchieved && !explicitReviewLevel ? "L3" : explicitReviewLevel;
2007
+ const change = {
2008
+ files: filesToCommit,
2009
+ crossModule: isCrossModule(filesToCommit),
2010
+ isFix: isFixContext(message),
2011
+ goalActive: ctx.isGoalActive?.() === true,
2012
+ largeFiles: collectLargeFiles(params.cwd, filesToCommit),
2013
+ commitMessage: typeof message === "string" ? message : void 0,
2014
+ ...effectiveReviewLevel ? { forceLevel: effectiveReviewLevel } : {},
2015
+ ...mechanicalClass ? { changeClass: mechanicalClass } : {}
2016
+ };
2017
+ const meridianDb = ctx.meridianIndexer?.getDb();
2018
+ const relChangeFiles = change.files.filter((f) => !isAbsolute3(f));
2019
+ if (meridianDb && relChangeFiles.length > 0) {
2020
+ const impact = analyzeImpact(meridianDb, relChangeFiles);
2021
+ const parts = [];
2022
+ if (impact.direct.length > 0)
2023
+ parts.push(`downstream consumers: ${impact.direct.slice(0, 8).join(", ")}${impact.direct.length > 8 ? ` (+${impact.direct.length - 8} more)` : ""}`);
2024
+ if (impact.tests.length > 0)
2025
+ parts.push(`related tests: ${impact.tests.slice(0, 8).join(", ")}${impact.tests.length > 8 ? ` (+${impact.tests.length - 8} more)` : ""}`);
2026
+ if (parts.length > 0) {
2027
+ const blast = `Blast radius \u2014 ${parts.join("; ")}`;
2028
+ change.focusHint = change.focusHint ? `${change.focusHint} | ${blast}` : blast;
2029
+ }
2030
+ }
2031
+ if (goalVerdict) {
2032
+ const v = goalVerdict;
2033
+ if (v.overall === "verified") {
2034
+ lines.push("", `\u2705 Goal judge: verified (${v.criteriaMet}/${v.criteriaTotal} criteria met). L3 review can focus on code quality.`);
2035
+ } else if (v.overall === "rejected") {
2036
+ lines.push("", `\u26A0\uFE0F Goal judge: rejected (${v.criteriaUnmet} unmet of ${v.criteriaTotal}). Accepted at judge cap \u2014 residual: ${v.summary}`);
2037
+ } else {
2038
+ lines.push("", `\u26A0\uFE0F Goal judge: inconclusive. Accepted unverified \u2014 ${v.summary}`);
2039
+ }
2040
+ }
2041
+ if (skipAutoReview && !explicitReviewLevel) {
2042
+ lines.push("", "\u23ED \u81EA\u52A8\u5BA1\u67E5\u5DF2\u8DF3\u8FC7\uFF08goal \u6A21\u5F0F / skipAuto / /review off\uFF09\u3002\u624B\u52A8 /review [max] \u5BA1\u67E5\u4E0D\u53D7\u5F71\u54CD\u3002");
2043
+ } else if (reviewDepth === 0 && shouldRouteReviewWorkflow(change) && isReviewDisciplineEnabled()) {
2044
+ if (reviewPolicy === "defer" && !explicitReviewLevel) {
2045
+ const scope = addPendingReviewFiles(ctx.sessionId, filesToCommit, { escalate: change.forceLevel === "L3" });
2046
+ lines.push("", ...formatDeferredReviewNotice(scope.commits, scope.files.size));
2047
+ } else {
2048
+ const bypassCooldown = explicitReviewLevel !== void 0 || reviewPolicy === "final" || goalAchieved;
2049
+ const bypassInFlight = explicitReviewLevel !== void 0 || goalAchieved;
2050
+ const now = Date.now();
2051
+ if (!bypassCooldown && now - lastPostCommitReviewAt < POST_COMMIT_REVIEW_COOLDOWN_MS) {
2052
+ const sinceSec = Math.round((now - lastPostCommitReviewAt) / 1e3);
2053
+ const windowSec = Math.round(POST_COMMIT_REVIEW_COOLDOWN_MS / 1e3);
2054
+ addPendingReviewFiles(ctx.sessionId, filesToCommit, { escalate: change.forceLevel === "L3" });
2055
+ lines.push("", `\u23ED \u63D0\u4EA4\u540E\u5BA1\u67E5\u8DF3\u8FC7\uFF1A\u8DDD\u4E0A\u8F6E\u5BA1\u67E5\u4EC5 ${sinceSec}s\uFF08<${windowSec}s \u51B7\u5374\u7A97\u53E3\uFF09\u3002\u672C\u8F6E\u53D8\u66F4\u5DF2\u8BB0\u5165\u5F85\u5BA1\u8303\u56F4\uFF0C\u4E0B\u6B21\u5BA1\u67E5\u81EA\u52A8\u8986\u76D6\uFF0C\u4E5F\u53EF /review \u624B\u52A8\u7ACB\u5373\u5BA1\u67E5\u3002`);
2056
+ } else if (!bypassInFlight && postCommitReviewInFlight) {
2057
+ const scope = addPendingReviewFiles(ctx.sessionId, filesToCommit, { escalate: change.forceLevel === "L3" });
2058
+ lines.push("", `\u23ED \u5DF2\u6709\u5728\u98DE\u5BA1\u67E5\uFF1A\u672C\u8F6E commit \u5E76\u5165\u5F85\u5BA1\u8303\u56F4\uFF08\u4F1A\u8BDD\u7D2F\u8BA1 ${scope.commits} \u4E2A commit\uFF09\uFF0C\u5728\u98DE\u5BA1\u67E5\u5B8C\u6210\u540E\u7EDF\u4E00\u8865\u5BA1\uFF0C\u4E0D\u91CD\u590D\u8D77 worker\u3002`);
2059
+ } else {
2060
+ const route = ctx.routeReviewWorkflow ?? (ctx.reviewDeps ? routeReviewWorkflow : void 0);
2061
+ if (!route || !ctx.reviewDeps) {
2062
+ lines.push("", "\u26A0\uFE0F \u63D0\u4EA4\u540E\u5BA1\u67E5\u8DF3\u8FC7\uFF1A\u5BA1\u67E5\u4F9D\u8D56\u4E0D\u53EF\u7528\uFF08ReviewRouter \u672A\u63A5\u5165\uFF09\u3002");
2063
+ } else {
2064
+ const accumulated = consumePendingReview(ctx.sessionId);
2065
+ let reviewChange = change;
2066
+ if (accumulated && accumulated.files.size > 0) {
2067
+ const mergedFiles = [.../* @__PURE__ */ new Set([...accumulated.files, ...change.files])];
2068
+ reviewChange = {
2069
+ ...change,
2070
+ files: mergedFiles,
2071
+ crossModule: isCrossModule(mergedFiles),
2072
+ largeFiles: collectLargeFiles(params.cwd, mergedFiles),
2073
+ ...accumulated.escalate && !change.forceLevel ? {
2074
+ forceLevel: "L3",
2075
+ focusHint: change.focusHint ? `Deferred commits escalated by typecheck/declared-check failure | ${change.focusHint}` : "Deferred commits escalated by typecheck/declared-check failure"
2076
+ } : {}
2077
+ };
2078
+ lines.push("", reviewPolicy === "final" ? `\u{1F50D} \u7EC8\u5BA1\uFF08review_policy=final\uFF09\uFF1A\u8986\u76D6 ${accumulated.commits} \u4E2A\u5EF6\u8FDF commit + \u672C\u6B21\u63D0\u4EA4\uFF0C\u5171 ${mergedFiles.length} \u4E2A\u6587\u4EF6\u3002` : `\u{1F4CE} \u672C\u6B21\u5BA1\u67E5\u4E00\u5E76\u8986\u76D6\u6B64\u524D\u7D2F\u79EF\u7684 ${accumulated.commits} \u4E2A\u672A\u5BA1 commit\uFF08\u5171 ${mergedFiles.length} \u4E2A\u6587\u4EF6\uFF09\u3002`);
2079
+ } else if (reviewPolicy === "final") {
2080
+ lines.push("", "\u{1F50D} \u7EC8\u5BA1\uFF08review_policy=final\uFF09\uFF1A\u65E0\u7D2F\u79EF\u7684\u5EF6\u8FDF commit\uFF0C\u4EC5\u5BA1\u67E5\u672C\u6B21\u63D0\u4EA4\u3002");
2081
+ }
2082
+ const reviewMode = reviewChange.forceLevel ? "manual" : "auto";
2083
+ const REVIEW_TIMEOUT_MS = reviewWorkflowBudgetMs(reviewMode, reviewChange.forceLevel);
2084
+ const reviewAbort = new AbortController();
2085
+ if (params.abortSignal) {
2086
+ if (params.abortSignal.aborted) {
2087
+ lines.push("", "\u26A0\uFE0F \u63D0\u4EA4\u540E\u5BA1\u67E5\u8DF3\u8FC7\uFF1A\u5DE5\u5177\u5DF2\u53D6\u6D88\u3002");
2088
+ } else {
2089
+ params.abortSignal.addEventListener("abort", () => reviewAbort.abort(), { once: true });
2090
+ }
2091
+ }
2092
+ if (!params.abortSignal?.aborted) {
2093
+ const budgetSec = Math.round(REVIEW_TIMEOUT_MS / 1e3);
2094
+ const reviewDeps = ctx.reviewDeps;
2095
+ const reviewTerminalOf = (outcome) => {
2096
+ const terminalStatus = outcome.verdict === "verified" || outcome.verdict === "nudge" ? "completed" : outcome.verdict === "rejected" ? "completed" : "failed";
2097
+ const failureReason = outcome.verdict === "rejected" ? "review-findings" : outcome.verdict === "inconclusive" ? "review-infra" : void 0;
2098
+ const lines0 = formatReviewOutcomeLines(outcome);
2099
+ const progressLine = outcome.verdict === "nudge" ? "\u5BA1\u67E5\u95E8\u5B8C\u6210 (nudge)\uFF1A\u53D8\u66F4\u7410\u788E\uFF0C\u514D\u6DF1\u5BA1" : lines0[0] ?? `\u5BA1\u67E5\u95E8\u5B8C\u6210 (${outcome.verdict})`;
2100
+ const summary = outcome.verdict === "rejected" || outcome.verdict === "inconclusive" ? lines0.join("\n") : void 0;
2101
+ return { terminalStatus, failureReason, progressLine, summary };
2102
+ };
2103
+ const settleReviewWorkers = (outcome, activityMapper, latestByWorker) => {
2104
+ if (!activityMapper || latestByWorker.size === 0) return;
2105
+ const { terminalStatus, failureReason, progressLine, summary } = reviewTerminalOf(outcome);
2106
+ for (const [wid, latest] of latestByWorker) {
2107
+ activityMapper.finish({
2108
+ ...latest,
2109
+ workOrderId: wid,
2110
+ parentToolId: params.toolUseId,
2111
+ profile: "reviewer",
2112
+ status: terminalStatus,
2113
+ progressLine,
2114
+ ...failureReason ? { failureReason } : {},
2115
+ ...summary ? { summary } : {}
2116
+ });
2117
+ }
2118
+ latestByWorker.clear();
2119
+ };
2120
+ const runReviewOnce = async (targetChange, runMode, timeoutMs, controller, activityMapper) => {
2121
+ const fallbackTier = runMode === "auto" ? "auto" : targetChange.forceLevel ?? effectiveReviewLevel ?? "L2";
2122
+ let reviewTimer;
2123
+ let outcome;
2124
+ try {
2125
+ const timeoutPromise = new Promise((_, reject) => {
2126
+ reviewTimer = setTimeout(() => {
2127
+ controller.abort();
2128
+ reject(new Error("Review workflow timed out"));
2129
+ }, timeoutMs);
2130
+ });
2131
+ outcome = await Promise.race([
2132
+ route(targetChange, reviewDeps, { abortSignal: controller.signal, mode: runMode, depthLayer: ctx.getDepthLayer?.(), onActivity: activityMapper }),
2133
+ timeoutPromise
2134
+ ]);
2135
+ } catch (err) {
2136
+ const reason = err instanceof Error ? err.message : String(err);
2137
+ outcome = {
2138
+ tier: fallbackTier,
2139
+ verdict: "inconclusive",
2140
+ rounds: 0,
2141
+ evidence: `post-commit review DID NOT run (${reason.includes("timed out") ? "timed out" : "infra failure"}: ${reason})`,
2142
+ infraFailures: [{ kind: reason.includes("timed out") ? "timeout" : "crash", claim: reason }]
2143
+ };
2144
+ } finally {
2145
+ if (reviewTimer) clearTimeout(reviewTimer);
2146
+ }
2147
+ if (runMode === "auto" && outcome.rounds !== void 0 && outcome.verdict !== "nudge") {
2148
+ if (outcome.verdict === "inconclusive") {
2149
+ recordAutoReviewRun({ ran: false, failureKinds: (outcome.infraFailures ?? []).map((f) => f.kind) });
2150
+ } else {
2151
+ recordAutoReviewRun({ ran: true, ...outcome.recoveredByRetry ? { recoveredByRetry: true } : {} });
2152
+ }
2153
+ }
2154
+ return outcome;
2155
+ };
2156
+ const launchDetached = (targetChange, runMode, commitRef2) => {
2157
+ lastPostCommitReviewAt = Date.now();
2158
+ postCommitReviewInFlight = true;
2159
+ const controller = new AbortController();
2160
+ if (params.abortSignal) {
2161
+ if (params.abortSignal.aborted) {
2162
+ postCommitReviewInFlight = false;
2163
+ return;
2164
+ }
2165
+ params.abortSignal.addEventListener("abort", () => controller.abort(), { once: true });
2166
+ }
2167
+ const timeoutMs = reviewWorkflowBudgetMs(runMode, targetChange.forceLevel);
2168
+ const runBudgetSec = Math.round(timeoutMs / 1e3);
2169
+ const latestByWorker = /* @__PURE__ */ new Map();
2170
+ const trackedUpstream = params.onWorkerActivity ? (activity) => {
2171
+ latestByWorker.set(activity.workOrderId, activity);
2172
+ params.onWorkerActivity(activity);
2173
+ } : void 0;
2174
+ const activityMapper = trackedUpstream ? createDelegationActivityMapper(params.toolUseId, trackedUpstream) : void 0;
2175
+ params.onOutput?.(`
2176
+ \u23F3 \u63D0\u4EA4\u540E\u5BA1\u67E5\u542F\u52A8\u4E2D (${runMode}${targetChange.forceLevel ? " " + targetChange.forceLevel : ""}, \u2264${runBudgetSec}s)\u2014\u2014\u5BA1\u67E5 worker \u8FDB\u5EA6\u89C1\u5B50\u4EE3\u7406\u9762\u677F...
2177
+ `);
2178
+ const reviewRunId = `review-gate-${String(commitRef2).slice(0, 7)}-${Math.random().toString(36).slice(2, 7)}`;
2179
+ params.onWorkerActivity?.({ workOrderId: reviewRunId, parentToolId: params.toolUseId, profile: "reviewer", status: "running", progressLine: `\u5BA1\u67E5\u95E8\u542F\u52A8 (${runMode}\u2026)` });
2180
+ void runReviewOnce(targetChange, runMode, timeoutMs, controller, activityMapper).then((outcome) => {
2181
+ enqueuePostCommitReviewOutcome({
2182
+ lines: [`\u63D0\u4EA4 ${commitRef2} \u7684\u63D0\u4EA4\u540E\u5BA1\u67E5\u5B8C\u6210\uFF1A`, ...formatReviewOutcomeLines(outcome)],
2183
+ verdict: outcome.verdict,
2184
+ tier: String(outcome.tier)
2185
+ });
2186
+ const { terminalStatus, failureReason, progressLine, summary } = reviewTerminalOf(outcome);
2187
+ params.onWorkerActivity?.({
2188
+ workOrderId: reviewRunId,
2189
+ parentToolId: params.toolUseId,
2190
+ profile: "reviewer",
2191
+ status: terminalStatus,
2192
+ progressLine,
2193
+ ...failureReason ? { failureReason } : {},
2194
+ ...summary ? { summary } : {}
2195
+ });
2196
+ settleReviewWorkers(outcome, activityMapper, latestByWorker);
2197
+ }).catch(() => {
2198
+ }).finally(() => {
2199
+ activityMapper?.dispose();
2200
+ postCommitReviewInFlight = false;
2201
+ if (params.abortSignal?.aborted) return;
2202
+ const followUp = consumePendingReview(ctx.sessionId);
2203
+ if (!followUp || followUp.files.size === 0) return;
2204
+ const mergedFiles = [...followUp.files];
2205
+ const followChange = {
2206
+ files: mergedFiles,
2207
+ crossModule: isCrossModule(mergedFiles),
2208
+ isFix: false,
2209
+ goalActive: false,
2210
+ largeFiles: collectLargeFiles(params.cwd, mergedFiles),
2211
+ ...followUp.escalate ? { forceLevel: "L3" } : {}
2212
+ };
2213
+ const followMode = followChange.forceLevel ? "manual" : "auto";
2214
+ launchDetached(followChange, followMode, "HEAD\uFF08\u5408\u5E76\u8865\u5BA1\uFF09");
2215
+ });
2216
+ };
2217
+ const commitRef = headAfterHash ?? "HEAD";
2218
+ mark("review:dispatch-start");
2219
+ if (explicitReviewLevel) {
2220
+ lastPostCommitReviewAt = Date.now();
2221
+ launchDetached(reviewChange, reviewMode, String(commitRef));
2222
+ lines.push("", `\u23F3 \u63D0\u4EA4\u540E\u5BA1\u67E5\u5DF2\u8F6C\u540E\u53F0 (${reviewMode} ${explicitReviewLevel}, \u2264${budgetSec}s)\u2014\u2014\u5BA1\u67E5\u7ED3\u8BBA\u5C06\u901A\u8FC7 system \u901A\u9053\u6CE8\u5165\u540E\u7EED\u5BF9\u8BDD\uFF0C\u672C\u6B21\u4EA4\u4ED8\u4E0D\u7B49\u5F85\u3002`);
2223
+ } else {
2224
+ lastPostCommitReviewAt = Date.now();
2225
+ launchDetached(reviewChange, reviewMode, String(commitRef));
2226
+ lines.push("", `\u23F3 \u63D0\u4EA4\u540E\u5BA1\u67E5\u5DF2\u8F6C\u540E\u53F0 (${reviewMode}${reviewChange.forceLevel ? " " + reviewChange.forceLevel : ""}, \u2264${budgetSec}s)\u2014\u2014\u8FDB\u5EA6\u89C1\u5B50\u4EE3\u7406\u9762\u677F\uFF0C\u7ED3\u8BBA\u5C06\u4EE5 system \u901A\u9053\u6CE8\u5165\u540E\u7EED\u5BF9\u8BDD\u5E76\u6C47\u603B\u5230\u9762\u677F\uFF0C\u672C\u6B21\u4EA4\u4ED8\u4E0D\u7B49\u5F85\u3002`);
2227
+ }
2228
+ }
2229
+ }
2230
+ }
2231
+ }
2232
+ }
2233
+ }
2234
+ if (reviewChecklist.length > 0) {
2235
+ lines.push("", "\u5BA1\u67E5\u539F\u5219\u6E05\u5355\uFF1A");
2236
+ for (const item of reviewChecklist.slice(0, 2)) {
2237
+ lines.push(` - ${item.question}`);
2238
+ }
2239
+ if (reviewChecklist.length > 2) {
2240
+ lines.push(` ... \u8FD8\u6709 ${reviewChecklist.length - 2} \u6761\u539F\u5219`);
2241
+ }
2242
+ }
2243
+ lines.push(
2244
+ "",
2245
+ "\u4EA4\u4ED8\u540E\u81EA\u68C0\uFF08\u63D0\u4EA4\u540E 10 \u79D2\u5185\u770B\u4E00\u773C\uFF09\uFF1A",
2246
+ " 1. \u521A\u624D\u7684\u63D0\u4EA4\u6587\u4EF6\u5217\u8868\u662F\u5426\u7B26\u5408\u300C\u672C\u6B21\u6539\u52A8\u7684\u6587\u4EF6\u300D\u2014\u2014\u662F\u7684\u8BDD\u7EE7\u7EED\uFF0C\u6709\u964C\u751F\u7684\u7ACB\u523B revert",
2247
+ " 2. \u9879\u76EE\u76EE\u5F55\u4E0B\u6709\u6CA1\u6709\u4E0D\u8BE5\u51FA\u73B0\u7684\u65B0\u6587\u4EF6\uFF08git status \u91CC\u7684 ?? \u884C\uFF09\u2014\u2014\u6709\u7684\u8BDD\u52A0 gitignore \u6216\u6E05\u7406"
2248
+ );
2249
+ setImmediate(() => {
2250
+ try {
2251
+ const entries = parseLearnedEntries(params.input.learned);
2252
+ let written = 0;
2253
+ for (const lp of entries) {
2254
+ if (countSimilarMemoryEntries(params.cwd, lp.text) > 0) continue;
2255
+ appendMemoryEntry(params.cwd, {
2256
+ text: lp.text,
2257
+ kind: "verified_pattern",
2258
+ confidence: 0.95,
2259
+ source: "agent-crafted",
2260
+ status: "verified",
2261
+ evidence: lp.evidence || void 0,
2262
+ sessionId: ctx.sessionId,
2263
+ tags: lp.tags,
2264
+ transferableTo: ["all"],
2265
+ topic: lp.topic
2266
+ });
2267
+ written++;
2268
+ }
2269
+ } catch {
2270
+ }
2271
+ });
2272
+ return { content: lines.join("\n") };
2273
+ },
2274
+ requiresApproval(params) {
2275
+ return params.input.commit === true;
2276
+ },
2277
+ isConcurrencySafe: () => true,
2278
+ isEnabled: () => true,
2279
+ // Budget: typecheck and review are now agent-owned steps (run before
2280
+ // calling deliver_task), not internal stages. commit=false is a fast
2281
+ // ownership query; commit=true does file I/O + git.
2282
+ //
2283
+ // commit 预算 60s→180s(2026-07-21 事故):多会话共享工作区(几十个外部
2284
+ // 改动文件)+ 并发 tsc/test 满载 CPU 时,所有权扫描 + claim 审计 + git
2285
+ // 操作 60s 跑不完 → 超时重试全灭 → 模型退回裸 git 提交 → 把并发会话的
2286
+ // 半成品 tracked 改动卷进提交、漏掉 untracked 依赖文件(损毁提交
2287
+ // 7b0c2936)。提交是终态动作,宁可慢不可断。
2288
+ timeoutMs: (params) => {
2289
+ if (params?.input?.commit !== true) return 6e4;
2290
+ return 18e4;
2291
+ }
2292
+ };
2293
+ }
2294
+
2295
+ export {
2296
+ formatReviewHealthLine,
2297
+ peekPendingReview,
2298
+ consumePendingReview,
2299
+ clearPendingReview,
2300
+ createStageMarker,
2301
+ resetPostCommitReviewCooldown,
2302
+ __expirePostCommitReviewCooldown,
2303
+ formatReviewOutcomeLines,
2304
+ detectSymptomPatch,
2305
+ collectCurrentDirtyFiles,
2306
+ detectMissingVisualVerify,
2307
+ parseLearnedEntries,
2308
+ createDeliverTaskTool
2309
+ };