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.
- package/LICENSE +189 -0
- package/README.en.md +1066 -0
- package/README.ja.md +1214 -0
- package/README.ko.md +1216 -0
- package/README.md +309 -0
- package/completions/rivet.bash +118 -0
- package/completions/rivet.fish +110 -0
- package/completions/rivet.ps1 +230 -0
- package/completions/rivet.zsh +187 -0
- package/dist/account-OAEKORZE.js +47 -0
- package/dist/account-login-V6FOLHJR.js +65 -0
- package/dist/account-status-KHNPPAFL.js +23 -0
- package/dist/agent/worker-process/child.js +407 -0
- package/dist/browser-cli-FLNY5ZHI.js +19 -0
- package/dist/browser-readiness-LWVGSBD5.js +12 -0
- package/dist/build-backends-5KXKR2S4.js +13 -0
- package/dist/build-options-66Y4ZE7C.js +9 -0
- package/dist/bundled-skills/brainstorming/SKILL.md +139 -0
- package/dist/bundled-skills/visual-acceptance/SKILL.md +64 -0
- package/dist/chain-7MAMFXZ3.js +10 -0
- package/dist/checkpoint-MND4DUIX.js +33 -0
- package/dist/chunk-2E6CK2RC.js +392 -0
- package/dist/chunk-2EBWGAXD.js +51 -0
- package/dist/chunk-2HDP7RMR.js +119 -0
- package/dist/chunk-2HKD2UFM.js +1000 -0
- package/dist/chunk-2VYRLULP.js +51 -0
- package/dist/chunk-2XN7PM4N.js +125 -0
- package/dist/chunk-35H7GVNR.js +66 -0
- package/dist/chunk-3C3R7JXY.js +77 -0
- package/dist/chunk-3VGD2FXB.js +118 -0
- package/dist/chunk-424AP7FM.js +534 -0
- package/dist/chunk-4J5TZGOE.js +3707 -0
- package/dist/chunk-4QJ3Y5JM.js +898 -0
- package/dist/chunk-4U2ET7ZY.js +1326 -0
- package/dist/chunk-4XPFLICE.js +876 -0
- package/dist/chunk-5B2OCU5P.js +31707 -0
- package/dist/chunk-5GGOGLSN.js +73 -0
- package/dist/chunk-5JRZPLI6.js +251 -0
- package/dist/chunk-5TQ3JYBU.js +397 -0
- package/dist/chunk-66JPZSGX.js +55 -0
- package/dist/chunk-6HSVXHCZ.js +764 -0
- package/dist/chunk-6S5FPUS7.js +21 -0
- package/dist/chunk-722LQO64.js +131 -0
- package/dist/chunk-73NJ7IT7.js +354 -0
- package/dist/chunk-7FW3I7WC.js +95 -0
- package/dist/chunk-7KUCBNLB.js +173 -0
- package/dist/chunk-7RDOV3DF.js +201 -0
- package/dist/chunk-A5MNH4ER.js +79 -0
- package/dist/chunk-ADIVZ2D5.js +15 -0
- package/dist/chunk-AE3P3F6W.js +366 -0
- package/dist/chunk-AERM3VGP.js +280 -0
- package/dist/chunk-AEWHC4WM.js +246 -0
- package/dist/chunk-AOVQ7VIF.js +169 -0
- package/dist/chunk-AOYHP3WI.js +12411 -0
- package/dist/chunk-B3YEBABJ.js +7160 -0
- package/dist/chunk-BCQXEZPJ.js +154 -0
- package/dist/chunk-BKTQAYO2.js +418 -0
- package/dist/chunk-BSECIPAQ.js +80 -0
- package/dist/chunk-BUIMGNDD.js +52 -0
- package/dist/chunk-BXHV3GSB.js +10320 -0
- package/dist/chunk-C6ZVZCPP.js +139 -0
- package/dist/chunk-CEZNQVWM.js +82 -0
- package/dist/chunk-CGFZGR7G.js +1492 -0
- package/dist/chunk-CGOADM2X.js +337 -0
- package/dist/chunk-CI4R6P2B.js +24 -0
- package/dist/chunk-D2TCGRSX.js +73 -0
- package/dist/chunk-D4PTYFOG.js +154 -0
- package/dist/chunk-D54ZWG4T.js +717 -0
- package/dist/chunk-DEBI37QB.js +145 -0
- package/dist/chunk-DWROHPXP.js +1051 -0
- package/dist/chunk-DZ6UOJ5D.js +92 -0
- package/dist/chunk-EBJTVRQN.js +81 -0
- package/dist/chunk-EGEI2RQA.js +1167 -0
- package/dist/chunk-EHHUASO5.js +1305 -0
- package/dist/chunk-EPKTIOED.js +41 -0
- package/dist/chunk-ER5JW2QZ.js +5681 -0
- package/dist/chunk-ETHFACAB.js +315 -0
- package/dist/chunk-EXUE5W2F.js +112 -0
- package/dist/chunk-EXXICUIW.js +73 -0
- package/dist/chunk-FFQSZJOO.js +169 -0
- package/dist/chunk-FHFKPGDO.js +85 -0
- package/dist/chunk-FW3QKMFX.js +158 -0
- package/dist/chunk-GD4ZNZ7W.js +30 -0
- package/dist/chunk-GHVMWF2I.js +581 -0
- package/dist/chunk-GMKAXSYQ.js +297 -0
- package/dist/chunk-HBVF6BWO.js +325 -0
- package/dist/chunk-HV4YGVMS.js +379 -0
- package/dist/chunk-ILBCKAV6.js +89 -0
- package/dist/chunk-J4U4AW46.js +440 -0
- package/dist/chunk-J6ESSM47.js +1791 -0
- package/dist/chunk-JBZHIC6T.js +2309 -0
- package/dist/chunk-JCFWOULR.js +245 -0
- package/dist/chunk-JRF5L6OD.js +131 -0
- package/dist/chunk-JSS75KCG.js +2014 -0
- package/dist/chunk-JSXWIWWJ.js +225 -0
- package/dist/chunk-JYV7I2QH.js +98 -0
- package/dist/chunk-KIOXTQQL.js +1596 -0
- package/dist/chunk-KWVTXT5B.js +89 -0
- package/dist/chunk-KX22RD6R.js +33 -0
- package/dist/chunk-LA6IPTGH.js +709 -0
- package/dist/chunk-LH5VCGLB.js +1796 -0
- package/dist/chunk-LQQ6XJ2Y.js +269 -0
- package/dist/chunk-MJQ55WZZ.js +65 -0
- package/dist/chunk-ML3UKMH3.js +40 -0
- package/dist/chunk-MOZPLHSU.js +5526 -0
- package/dist/chunk-NGEWJ4DF.js +89 -0
- package/dist/chunk-NH6VIOAR.js +211 -0
- package/dist/chunk-O5VZQKCB.js +26106 -0
- package/dist/chunk-OHSV42QB.js +748 -0
- package/dist/chunk-OO3TAHO5.js +4070 -0
- package/dist/chunk-OTNHSAGN.js +38282 -0
- package/dist/chunk-OUWAC7AQ.js +442 -0
- package/dist/chunk-OXMIV4WE.js +270 -0
- package/dist/chunk-P5KJURUR.js +229 -0
- package/dist/chunk-P6GJOVO6.js +307 -0
- package/dist/chunk-QARAGUXX.js +23 -0
- package/dist/chunk-R43HJCDX.js +2711 -0
- package/dist/chunk-RAXBAWRN.js +210 -0
- package/dist/chunk-RK3JHWPT.js +39 -0
- package/dist/chunk-RWA23GMM.js +24 -0
- package/dist/chunk-S4K4L6IM.js +179 -0
- package/dist/chunk-SAXK6LVF.js +124 -0
- package/dist/chunk-SEPGEJNK.js +1607 -0
- package/dist/chunk-SUJERP5I.js +285 -0
- package/dist/chunk-T3ED6BY2.js +913 -0
- package/dist/chunk-THKDVSEC.js +90 -0
- package/dist/chunk-TI4GSEL5.js +52 -0
- package/dist/chunk-TPI73L57.js +164 -0
- package/dist/chunk-TVHW77C7.js +338 -0
- package/dist/chunk-TWNHOSDT.js +1073 -0
- package/dist/chunk-UFMN76QD.js +347 -0
- package/dist/chunk-UHGLFRZE.js +205 -0
- package/dist/chunk-UTZYPKBF.js +15231 -0
- package/dist/chunk-UXNLELW6.js +25 -0
- package/dist/chunk-UXXS53EA.js +166 -0
- package/dist/chunk-V5DHFGI7.js +171 -0
- package/dist/chunk-V5YIPMHV.js +2353 -0
- package/dist/chunk-WMUWEO2C.js +754 -0
- package/dist/chunk-WOE3D7JM.js +945 -0
- package/dist/chunk-X2IK5LVR.js +2592 -0
- package/dist/chunk-XU2YXCDL.js +459 -0
- package/dist/chunk-YJG5HIX5.js +203 -0
- package/dist/chunk-Z3QE7TZM.js +72 -0
- package/dist/chunk-Z5Z3JHJY.js +64 -0
- package/dist/chunk-ZDIFBJFT.js +168 -0
- package/dist/chunk-ZGGCEXDC.js +176 -0
- package/dist/chunk-ZMZCZNKU.js +263 -0
- package/dist/chunk-ZNC5T65N.js +17 -0
- package/dist/chunk-ZO3I3A7O.js +308 -0
- package/dist/chunk-ZQKXBUB7.js +217 -0
- package/dist/chunk-ZTRINFM2.js +78 -0
- package/dist/chunk-ZU7JZCVP.js +193 -0
- package/dist/chunk-ZZAWNALS.js +350 -0
- package/dist/cli/entry.js +54 -0
- package/dist/connector-L5QQTR6Y.js +26 -0
- package/dist/context-62DC6EC3.js +31 -0
- package/dist/council-convene-NJWPNBDU.js +46 -0
- package/dist/create-agent-config-ODSQWNVC.js +55 -0
- package/dist/default-registry-2PU3BTAU.js +68 -0
- package/dist/deliver-task-KONV4C4M.js +57 -0
- package/dist/delivery-gate-v2-F2YVMVGK.js +27 -0
- package/dist/domain-knowledge-store-DDZ3F4J7.js +11 -0
- package/dist/fetch-core-ZPPYDSZH.js +14 -0
- package/dist/galaxy-K72BBDQN.js +91 -0
- package/dist/goal-criteria-SCALAU2D.js +141 -0
- package/dist/goal-persist-XWY2GDQX.js +19 -0
- package/dist/goal-tracker-FXMJ2GVK.js +11 -0
- package/dist/headless-LSB6I7PT.js +162 -0
- package/dist/headless-coordinator-VDPDMVUT.js +90 -0
- package/dist/inject-U55SMB6A.js +39 -0
- package/dist/login-flow-CD4ZGXOC.js +59 -0
- package/dist/logs-cli-SQVYFMMN.js +283 -0
- package/dist/loop-ULZYBDA3.js +76 -0
- package/dist/main.js +4064 -0
- package/dist/manager-ROZBCWPA.js +171 -0
- package/dist/manager-Z7XFFUOM.js +10670 -0
- package/dist/meridian-indexer-ZIW363L7.js +21 -0
- package/dist/native-resolver-PN34UIVV.js +13 -0
- package/dist/node_modules/better-sqlite3/lib/database.js +90 -0
- package/dist/node_modules/better-sqlite3/lib/index.js +3 -0
- package/dist/node_modules/better-sqlite3/lib/methods/aggregate.js +43 -0
- package/dist/node_modules/better-sqlite3/lib/methods/backup.js +67 -0
- package/dist/node_modules/better-sqlite3/lib/methods/function.js +31 -0
- package/dist/node_modules/better-sqlite3/lib/methods/inspect.js +7 -0
- package/dist/node_modules/better-sqlite3/lib/methods/pragma.js +12 -0
- package/dist/node_modules/better-sqlite3/lib/methods/serialize.js +16 -0
- package/dist/node_modules/better-sqlite3/lib/methods/table.js +189 -0
- package/dist/node_modules/better-sqlite3/lib/methods/transaction.js +78 -0
- package/dist/node_modules/better-sqlite3/lib/methods/wrappers.js +54 -0
- package/dist/node_modules/better-sqlite3/lib/sqlite-error.js +20 -0
- package/dist/node_modules/better-sqlite3/lib/util.js +12 -0
- package/dist/node_modules/better-sqlite3/package.json +59 -0
- package/dist/ownership-ledger-E3WIEKPL.js +9 -0
- package/dist/package.json +3 -0
- package/dist/path-grants-XRRXHXG7.js +40 -0
- package/dist/paths-LVV5IMQN.js +49 -0
- package/dist/plan-fact-anchors-LMVQZCW5.js +13 -0
- package/dist/plan-store-ODLTTPFB.js +17 -0
- package/dist/plan-templates-5FPFBOL6.js +109 -0
- package/dist/plugin-loader-ZZKX7ZE6.js +22 -0
- package/dist/prefix-budget-62OV5HQC.js +13 -0
- package/dist/project-trust-5WS5MOMU.js +31 -0
- package/dist/provider-cli-ALDQXIT7.js +37 -0
- package/dist/providers-XQYTAVPK.js +11 -0
- package/dist/proxy-resolver-V2Y6534D.js +15 -0
- package/dist/recovery-cli-KPGY6FY3.js +106 -0
- package/dist/review-discipline-MNSYCST5.js +55 -0
- package/dist/review-router-BVQZW3MX.js +18 -0
- package/dist/seed-capsules/seed-capsule-attack-methodology.md +62 -0
- package/dist/seed-capsules/seed-capsule-changgeng.md +31 -0
- package/dist/seed-capsules/seed-capsule-design-aesthetics.md +38 -0
- package/dist/seed-capsules/seed-capsule-diagnostic-ladder.md +54 -0
- package/dist/seed-capsules/seed-capsule-fu.md +39 -0
- package/dist/seed-capsules/seed-capsule-huagai.md +73 -0
- package/dist/seed-capsules/seed-capsule-kaiyang.md +65 -0
- package/dist/seed-capsules/seed-capsule-knowledge-work.md +40 -0
- package/dist/seed-capsules/seed-capsule-tanlang.md +47 -0
- package/dist/seed-capsules/seed-capsule-tianfu.md +23 -0
- package/dist/seed-capsules/seed-capsule-tianliang.md +63 -0
- package/dist/seed-capsules/seed-capsule-tianquan.md +32 -0
- package/dist/seed-capsules/seed-capsule-tianxuan-ccr.md +22 -0
- package/dist/seed-capsules/seed-capsule-tianxuan.md +64 -0
- package/dist/seed-capsules/seed-capsule-yaoguang.md +87 -0
- package/dist/serve-X2R2UTLF.js +170 -0
- package/dist/serve-agent-KHSU6RVV.js +965 -0
- package/dist/session-persist-HUUC772H.js +37 -0
- package/dist/session-registry-6XUD5C4Y.js +15 -0
- package/dist/skill-loader-CK3RMO47.js +48 -0
- package/dist/skills/cli-harness-sop.md +207 -0
- package/dist/spawn-git-4HFU65AH.js +29 -0
- package/dist/sse-EYUTBWUK.js +512 -0
- package/dist/starflow-NOFBMPFU.js +30 -0
- package/dist/streamableHttp-V67CNJMO.js +447 -0
- package/dist/task-contract-6XHBNJQK.js +32 -0
- package/dist/task-ledger-3K4TJIER.js +9 -0
- package/dist/team-orchestrate-S6NHZP7X.js +64 -0
- package/dist/title-extract-QX4XGCYN.js +50 -0
- package/dist/todo-QDXGRZZP.js +31 -0
- package/dist/todo-store-V3TTO5AH.js +12 -0
- package/dist/tool-preset-LU5BYEDZ.js +23 -0
- package/dist/tool-tiers-GD742YBN.js +23 -0
- package/dist/turndown.es-OI55K6C3.js +17373 -0
- package/dist/verification-attribution-EKG4CNUV.js +13 -0
- package/dist/volatile-git-MBTST7RA.js +15 -0
- package/dist/wave-checkpoint-XLIIHXQA.js +30 -0
- package/dist/web-cli-DUG4I35O.js +233 -0
- package/dist/web-search-3IK542AA.js +34 -0
- package/dist/workers/cpu-worker.js +45 -0
- package/dist/workflow-runner-HX7UBAYG.js +297 -0
- package/dist/worktree-baseline-O5NCU6M2.js +9 -0
- package/package.json +122 -0
- package/scripts/check-mobile-env.mjs +76 -0
- package/scripts/fetch-native-sqlite.js +392 -0
- package/scripts/prune-installed-wasms.js +45 -0
- package/scripts/tree-sitter-wasm-keep.js +40 -0
|
@@ -0,0 +1,2353 @@
|
|
|
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
|
+
buildConsolidatedBlock,
|
|
5
|
+
buildDynamicAppendixParts,
|
|
6
|
+
buildLatestTurnVolatileBlock,
|
|
7
|
+
buildStableVolatileBlock,
|
|
8
|
+
parseOptionalInt,
|
|
9
|
+
rangeContains,
|
|
10
|
+
renderPlanMethodologyAdvisory,
|
|
11
|
+
renderTaskDepthAdvisory,
|
|
12
|
+
runResumePreflightOai,
|
|
13
|
+
selectProjectInstructions
|
|
14
|
+
} from "./chunk-SEPGEJNK.js";
|
|
15
|
+
import {
|
|
16
|
+
isSystemReminder
|
|
17
|
+
} from "./chunk-RWA23GMM.js";
|
|
18
|
+
import {
|
|
19
|
+
skillRegistry
|
|
20
|
+
} from "./chunk-OHSV42QB.js";
|
|
21
|
+
import {
|
|
22
|
+
collapseToolResult,
|
|
23
|
+
estimateOaiTokens
|
|
24
|
+
} from "./chunk-KIOXTQQL.js";
|
|
25
|
+
import {
|
|
26
|
+
CACHE_ANCHOR_MESSAGES
|
|
27
|
+
} from "./chunk-TPI73L57.js";
|
|
28
|
+
import {
|
|
29
|
+
debugLog
|
|
30
|
+
} from "./chunk-CI4R6P2B.js";
|
|
31
|
+
import {
|
|
32
|
+
parseFrozenSnapshotData
|
|
33
|
+
} from "./chunk-6S5FPUS7.js";
|
|
34
|
+
import {
|
|
35
|
+
stableStringify
|
|
36
|
+
} from "./chunk-QARAGUXX.js";
|
|
37
|
+
import {
|
|
38
|
+
FROZEN_BLOCK_CAPS
|
|
39
|
+
} from "./chunk-ER5JW2QZ.js";
|
|
40
|
+
import {
|
|
41
|
+
init_esm_shims
|
|
42
|
+
} from "./chunk-5GGOGLSN.js";
|
|
43
|
+
|
|
44
|
+
// src/prompt/static.ts
|
|
45
|
+
init_esm_shims();
|
|
46
|
+
|
|
47
|
+
// src/prompt/static-subagent.ts
|
|
48
|
+
init_esm_shims();
|
|
49
|
+
var TOP_LEVEL_RE = /^<([a-z][a-z-]*)>\n([\s\S]*?)\n<\/\1>$/gm;
|
|
50
|
+
var RULE_RE = /^ {2}<rule name="([^"]+)">\n([\s\S]*?)\n {2}<\/rule>$/gm;
|
|
51
|
+
var HARNESS_PART_RE = /^ {2}<([a-z-]+)(?: name="([^"]+)")?>\n([\s\S]*?)\n {2}<\/\1>$/gm;
|
|
52
|
+
function parsePrompt(src) {
|
|
53
|
+
const sections = [];
|
|
54
|
+
for (const m of src.matchAll(TOP_LEVEL_RE)) {
|
|
55
|
+
sections.push({ name: m[1], full: m[0], body: m[2] });
|
|
56
|
+
}
|
|
57
|
+
const rejoined = sections.map((s) => s.full).join("\n\n");
|
|
58
|
+
const trailer = src.slice(rejoined.length);
|
|
59
|
+
if (rejoined + trailer !== src || !/^\s*$/.test(trailer)) {
|
|
60
|
+
throw new Error(
|
|
61
|
+
`[static-subagent] top-level parse is lossy: parsed ${sections.length} sections covering ${rejoined.length} of ${src.length} chars. BASE_PROMPT shape changed.`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
return { sections, trailer };
|
|
65
|
+
}
|
|
66
|
+
function splitRules(rulesBody) {
|
|
67
|
+
const out = [];
|
|
68
|
+
for (const m of rulesBody.matchAll(RULE_RE)) {
|
|
69
|
+
out.push({ name: m[1], full: m[0], body: m[2] });
|
|
70
|
+
}
|
|
71
|
+
if (out.length === 0) throw new Error("[static-subagent] no <rule> entries found in <rules>");
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
var DROP_SECTIONS = /* @__PURE__ */ new Set([
|
|
75
|
+
// 交付报告契约是对人类说话的;子代理的交付契约是 WorkerResult,两份同时在场冲突。
|
|
76
|
+
"delivery-contract",
|
|
77
|
+
// 探索→计划→执行→验证是主控的循环;子代理接的是已定边界的任务卡。
|
|
78
|
+
"workflow",
|
|
79
|
+
// 整段是"建议用户运行 /mirror china"。子代理没有面向用户的通道,
|
|
80
|
+
// /mirror 也是 TUI 斜杠命令而非工具——对它是不可执行的建议。
|
|
81
|
+
"downloads"
|
|
82
|
+
]);
|
|
83
|
+
var TOOL_GATED_SECTIONS = /* @__PURE__ */ new Map([
|
|
84
|
+
["shared-worktree", (t) => t.has("deliver_task")],
|
|
85
|
+
["git", (t) => t.has("git")],
|
|
86
|
+
["delegation", (t) => t.has("delegate_task") || t.has("delegate_batch")]
|
|
87
|
+
]);
|
|
88
|
+
var DROP_RULES = /* @__PURE__ */ new Set([
|
|
89
|
+
// 整条写给主控:末句直接点名 "worker 输出的 HIGH/CRITICAL 标记不等于已验证事实"。
|
|
90
|
+
// 子代理是这条规则的对象,不是执行者。
|
|
91
|
+
"external-source-verification",
|
|
92
|
+
// 讲用户说 "P1"/"刚才那个" 时怎么回指上一轮回复。子代理不跟人对话,
|
|
93
|
+
// 收到的是自包含任务卡;它引用的 <intent-retrieval-route> 块是主控专有。
|
|
94
|
+
"context-intent-association",
|
|
95
|
+
// 规则描述的是 <git-status> 注入块,而子代理的冻结块里没有该块。
|
|
96
|
+
"git-context-first",
|
|
97
|
+
// 规则描述的全部是 appendixDelta 线格式(seq/覆盖/自闭合),
|
|
98
|
+
// 而 worker 的 PromptEngine 未开 delta,这套格式一次都不会遇到。见设计文档 §6.1。
|
|
99
|
+
"context-update-protocol"
|
|
100
|
+
]);
|
|
101
|
+
var WRITE_ONLY_RULES = /* @__PURE__ */ new Set([
|
|
102
|
+
// 逐字取原文的纪律只在改用户可见字符串时有意义。
|
|
103
|
+
"verbatim-user-facing-text"
|
|
104
|
+
]);
|
|
105
|
+
var WRITE_ONLY_HARNESS_PARTS = /* @__PURE__ */ new Set([
|
|
106
|
+
"red-green-bugfix",
|
|
107
|
+
"test-strategy-by-task",
|
|
108
|
+
"env-simulation",
|
|
109
|
+
// 清理临时探针的纪律:只读 worker 没有写工具,创建不了探针——对它是死条文。
|
|
110
|
+
// 保留判据("主控只看到 diff 摘要,补救不了残留探针")只在 worker 能写文件时成立。
|
|
111
|
+
"probe-discipline"
|
|
112
|
+
]);
|
|
113
|
+
var DROP_HARNESS_PARTS = /* @__PURE__ */ new Set([
|
|
114
|
+
// 子代理有硬 turn 预算和软着陆收尾,没有换视角深挖的余裕——这条会诱导它超预算乱跑。
|
|
115
|
+
"perspective-shift"
|
|
116
|
+
]);
|
|
117
|
+
var EVIDENCE_LINE_DROPS = [
|
|
118
|
+
{ prefix: " \u4F8B\u5916\uFF1A", writeOnly: false },
|
|
119
|
+
{ prefix: " \u8BCA\u65AD\u6096\u8BBA\uFF1A", writeOnly: true }
|
|
120
|
+
];
|
|
121
|
+
var IDENTITY_TOOLSET_CLAIM = "\u4F60\u62E5\u6709\u5B8C\u6574\u7684\u5F00\u53D1\u5DE5\u5177\u96C6\uFF1A\u6587\u4EF6\u8BFB\u5199\u3001\u4EE3\u7801\u641C\u7D22\u3001\u7EC8\u7AEF\u6267\u884C\u3001\u6D4B\u8BD5\u8FD0\u884C\u3001\u9879\u76EE\u5BFC\u822A\u3001\u4EFB\u52A1\u59D4\u6D3E\u3002";
|
|
122
|
+
var TOOL_USAGE_LINE_GATES = [
|
|
123
|
+
{ prefix: "\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\u9009\u62E9\uFF1A", anyOf: ["edit_file", "write_file", "hash_edit", "apply_patch", "ast_edit"] },
|
|
124
|
+
{ prefix: "- edit_file\uFF1A", anyOf: ["edit_file"] },
|
|
125
|
+
{ prefix: "- write_file\uFF1A", anyOf: ["write_file"] },
|
|
126
|
+
{ prefix: "- hash_edit\uFF1A", anyOf: ["hash_edit"] },
|
|
127
|
+
{ prefix: "- apply_patch\uFF1A", anyOf: ["apply_patch"] },
|
|
128
|
+
{ prefix: "- ast_edit\uFF1A", anyOf: ["ast_edit"] },
|
|
129
|
+
{ prefix: "\u7981\u6B62\u7528 bash \u8BFB\u5199\u6587\u4EF6\u3002", anyOf: ["bash", "write_file", "hash_edit"] },
|
|
130
|
+
{ prefix: "\u68C0\u7D22\u5DE5\u5177\u9009\u62E9\uFF1A", anyOf: ["grep", "ast_grep"] },
|
|
131
|
+
{ prefix: "- grep\uFF1A", anyOf: ["grep"] },
|
|
132
|
+
{ prefix: "- ast_grep\uFF1A", anyOf: ["ast_grep"] },
|
|
133
|
+
{ prefix: "\u6D4F\u89C8\u5668\u4E0E\u684C\u9762\u81EA\u52A8\u5316\u5206\u5DE5\uFF1A", anyOf: ["web_fetch", "web_search", "browser_debug", "computer_use"] },
|
|
134
|
+
{ prefix: "- web_fetch / web_search\uFF1A", anyOf: ["web_fetch", "web_search"] },
|
|
135
|
+
{ prefix: "- browser_debug\uFF08", anyOf: ["browser_debug"] },
|
|
136
|
+
{ prefix: "- computer_use\uFF1A", anyOf: ["computer_use"] },
|
|
137
|
+
// 「三者」= 上面三条 bullet;审批边界讲的是导航与逐应用授权,只对后两者成立。
|
|
138
|
+
{ prefix: "- \u4E09\u8005\u52A8\u4F5C\u5747\u6709\u5BA1\u6279\u8FB9\u754C", anyOf: ["browser_debug", "computer_use"] },
|
|
139
|
+
{ prefix: "\u5DE5\u4F5C\u533A\u5916\u8DEF\u5F84\uFF1A", anyOf: ["request_path_access"] },
|
|
140
|
+
// 前向引用 <delegation> 段——该段被门掉时这行会变成悬空指针。
|
|
141
|
+
{ prefix: "\u59D4\u6D3E\u539F\u5219\uFF1A", anyOf: ["delegate_task", "delegate_batch"] }
|
|
142
|
+
];
|
|
143
|
+
var WRITE_CAPABLE_TOOLS = /* @__PURE__ */ new Set([
|
|
144
|
+
"edit_file",
|
|
145
|
+
"write_file",
|
|
146
|
+
"hash_edit",
|
|
147
|
+
"apply_patch",
|
|
148
|
+
"bash",
|
|
149
|
+
"run_tests",
|
|
150
|
+
"git"
|
|
151
|
+
]);
|
|
152
|
+
function derivePolicy(tools) {
|
|
153
|
+
const names = new Set(tools.map((t) => t.name));
|
|
154
|
+
return {
|
|
155
|
+
tools: names,
|
|
156
|
+
writeCapable: [...names].some((n) => WRITE_CAPABLE_TOOLS.has(n))
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
function trimEvidenceScope(rule, policy) {
|
|
160
|
+
const kept = rule.body.split("\n").filter((line) => {
|
|
161
|
+
const drop = EVIDENCE_LINE_DROPS.find((d) => line.startsWith(d.prefix));
|
|
162
|
+
if (!drop) return true;
|
|
163
|
+
return drop.writeOnly && policy.writeCapable;
|
|
164
|
+
});
|
|
165
|
+
return ` <rule name="${rule.name}">
|
|
166
|
+
${kept.join("\n")}
|
|
167
|
+
</rule>`;
|
|
168
|
+
}
|
|
169
|
+
function trimTestHarness(rule, policy) {
|
|
170
|
+
const header = rule.body.split("\n\n")[0];
|
|
171
|
+
const kept = [];
|
|
172
|
+
for (const m of rule.body.matchAll(HARNESS_PART_RE)) {
|
|
173
|
+
const key = m[2] ?? m[1];
|
|
174
|
+
if (DROP_HARNESS_PARTS.has(key)) continue;
|
|
175
|
+
if (WRITE_ONLY_HARNESS_PARTS.has(key) && !policy.writeCapable) continue;
|
|
176
|
+
kept.push(m[0]);
|
|
177
|
+
}
|
|
178
|
+
if (kept.length === 0) return null;
|
|
179
|
+
return ` <rule name="${rule.name}">
|
|
180
|
+
${header}
|
|
181
|
+
|
|
182
|
+
${kept.join("\n\n")}
|
|
183
|
+
</rule>`;
|
|
184
|
+
}
|
|
185
|
+
function assertAnchorsResolve(lines, gates, where) {
|
|
186
|
+
for (const gate of gates) {
|
|
187
|
+
const hits = lines.filter((l) => l.startsWith(gate.prefix)).length;
|
|
188
|
+
if (hits !== 1) {
|
|
189
|
+
throw new Error(
|
|
190
|
+
`[static-subagent] <${where}> anchor "${gate.prefix}" matched ${hits} lines (expected 1). BASE_PROMPT changed \u2014 update TOOL_USAGE_LINE_GATES.`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function trimToolUsage(section, policy) {
|
|
196
|
+
const lines = section.body.split("\n");
|
|
197
|
+
assertAnchorsResolve(lines, TOOL_USAGE_LINE_GATES, section.name);
|
|
198
|
+
const kept = lines.filter((line) => {
|
|
199
|
+
const gate = TOOL_USAGE_LINE_GATES.find((g) => line.startsWith(g.prefix));
|
|
200
|
+
return !gate || gate.anyOf.some((t) => policy.tools.has(t));
|
|
201
|
+
});
|
|
202
|
+
if (kept.length === lines.length) return section.full;
|
|
203
|
+
return `<${section.name}>
|
|
204
|
+
${kept.join("\n")}
|
|
205
|
+
</${section.name}>`;
|
|
206
|
+
}
|
|
207
|
+
function trimIdentity(section) {
|
|
208
|
+
if (!section.body.includes(IDENTITY_TOOLSET_CLAIM)) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"[static-subagent] <identity> toolset-claim anchor not found. BASE_PROMPT changed \u2014 update IDENTITY_TOOLSET_CLAIM."
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return `<${section.name}>
|
|
214
|
+
${section.body.replace(IDENTITY_TOOLSET_CLAIM, "")}
|
|
215
|
+
</${section.name}>`;
|
|
216
|
+
}
|
|
217
|
+
function composeRules(section, policy) {
|
|
218
|
+
const kept = [];
|
|
219
|
+
for (const rule of splitRules(section.body)) {
|
|
220
|
+
if (DROP_RULES.has(rule.name)) continue;
|
|
221
|
+
if (WRITE_ONLY_RULES.has(rule.name) && !policy.writeCapable) continue;
|
|
222
|
+
if (rule.name === "evidence-scope") {
|
|
223
|
+
kept.push(trimEvidenceScope(rule, policy));
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (rule.name === "test-harness") {
|
|
227
|
+
const harness = trimTestHarness(rule, policy);
|
|
228
|
+
if (harness) kept.push(harness);
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
kept.push(rule.full);
|
|
232
|
+
}
|
|
233
|
+
if (kept.length === 0) return null;
|
|
234
|
+
return `<rules>
|
|
235
|
+
${kept.join("\n\n")}
|
|
236
|
+
|
|
237
|
+
</rules>`;
|
|
238
|
+
}
|
|
239
|
+
function buildSubagentSystemPrompt(base, tools) {
|
|
240
|
+
const policy = derivePolicy(tools);
|
|
241
|
+
const { sections, trailer } = parsePrompt(base);
|
|
242
|
+
const out = [];
|
|
243
|
+
for (const section of sections) {
|
|
244
|
+
if (DROP_SECTIONS.has(section.name)) continue;
|
|
245
|
+
const gate = TOOL_GATED_SECTIONS.get(section.name);
|
|
246
|
+
if (gate && !gate(policy.tools)) continue;
|
|
247
|
+
if (section.name === "rules") {
|
|
248
|
+
const rules = composeRules(section, policy);
|
|
249
|
+
if (rules) out.push(rules);
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (section.name === "tool-usage") {
|
|
253
|
+
out.push(trimToolUsage(section, policy));
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (section.name === "identity") {
|
|
257
|
+
out.push(trimIdentity(section));
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
out.push(section.full);
|
|
261
|
+
}
|
|
262
|
+
return out.join("\n\n") + trailer;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/prompt/static.ts
|
|
266
|
+
var BASE_PROMPT = `<identity>
|
|
267
|
+
\u4F60\u5728\u300C\u5929\u67A2\u300D\u5317\u6597\u661F\u57DF\u8FD0\u884C\u65F6\u4E2D\u2014\u2014\u4E00\u4E2A\u8BA4\u77E5\u589E\u5F3A\u7684\u4EE3\u7801\u5F00\u53D1\u73AF\u5883\u3002\u4F60\u62E5\u6709\u5B8C\u6574\u7684\u5F00\u53D1\u5DE5\u5177\u96C6\uFF1A\u6587\u4EF6\u8BFB\u5199\u3001\u4EE3\u7801\u641C\u7D22\u3001\u7EC8\u7AEF\u6267\u884C\u3001\u6D4B\u8BD5\u8FD0\u884C\u3001\u9879\u76EE\u5BFC\u822A\u3001\u4EFB\u52A1\u59D4\u6D3E\u3002\u4F60\u7684\u4EFB\u52A1\u662F\u5728\u7406\u89E3\u7528\u6237\u610F\u56FE\u3001\u9879\u76EE\u4E0A\u4E0B\u6587\u4E0E\u5DE5\u7A0B\u7EA6\u675F\u7684\u57FA\u7840\u4E0A\uFF0C\u4E3B\u52A8\u8BBE\u8BA1\u66F4\u5408\u7406\u7684\u67B6\u6784\u3001\u53D1\u73B0\u9690\u85CF\u98CE\u9669\u3001\u4FEE\u590D\u6839\u56E0\u95EE\u9898\uFF0C\u5E76\u8F93\u51FA\u6E05\u6670\u3001\u7A33\u5B9A\u3001\u53EF\u7EF4\u62A4\u3001\u53EF\u6269\u5C55\u7684\u5B9E\u73B0\u65B9\u6848\u3002
|
|
268
|
+
\u6838\u5FC3\u539F\u5219\uFF1A\u4E0D\u731C\uFF0C\u5148\u8BFB\u3002\u5206\u6790\u4EE3\u7801\u524D\u5148\u8BFB\u73B0\u6709\u4EE3\u7801\u7406\u89E3\u4E0A\u4E0B\u6587\uFF0C\u52A8\u624B\u524D\u505A\u63A2\u9488\u9A8C\u8BC1\u5047\u8BBE\u3002
|
|
269
|
+
\u5F53\u88AB\u95EE\u5230"\u4F60\u662F\u4EC0\u4E48"\u65F6\u2014\u2014\u4F60\u6765\u81EA\u5929\u67A2\u661F\u57DF\uFF0C\u5F53\u524D\u5728\u67D0\u57DF\u63A8\u8FDB\u4EFB\u52A1\u3002\u4F60\u4EE5\u4E2D\u6587\u601D\u8003\u548C\u56DE\u590D\u3002
|
|
270
|
+
</identity>
|
|
271
|
+
|
|
272
|
+
<beliefs>
|
|
273
|
+
\u7528\u6237\u628A\u4EFB\u52A1\u4EA4\u7ED9\u4F60\u5373\u6388\u6743\u4F60\u5224\u65AD\u3002\u9ED8\u8BA4\u8F93\u51FA = \u7ED3\u8BBA + \u63A8\u8350 + \u76F4\u63A5\u63A8\u8FDB\uFF1B\u628A"\u5F81\u6C42\u7528\u6237\u62CD\u677F"\u5F53\u6D88\u8017\u54C1\uFF0C\u53EA\u7528\u4E8E\u4E0D\u53EF\u9006\u64CD\u4F5C\u3001\u9AD8\u6210\u672C\u52A8\u4F5C\u6216\u771F\u6B63\u4E92\u65A5\u7684\u53D6\u820D\u3002\u9700\u8981\u5206\u6790/\u5EFA\u8BAE\u7684\u95EE\u9898\u76F4\u63A5\u7ED9\u7B54\u6848\uFF0C\u4E0D\u5217\u9009\u62E9\u9898\u3002
|
|
274
|
+
\u5F53\u4F60\u53D1\u73B0\u66F4\u4F18\u65B9\u6848\u65F6\uFF0C\u7528\u4E00\u53E5\u8BDD\u8BF4\u51FA\u5DEE\u5F02\u548C\u7406\u7531\u2014\u2014\u6267\u884C\u7EC6\u8282\u4E0A\u7684\u4F18\u5316\u76F4\u63A5\u63A8\u8FDB\u4E0D\u7B49\uFF1B\u65B9\u5411\u6027\u53D8\u66F4\uFF08\u7528\u6237\u660E\u793A\u7684\u4EA7\u54C1\u8DEF\u7EBF/\u67B6\u6784\u53D6\u820D\uFF0C\u4E0D\u662F\u5B9E\u73B0\u7EC6\u8282\uFF09\u65F6\uFF0C\u5148\u7ED9\u51FA\u63A8\u8350\u65B9\u6848\u518D\u6267\u884C\uFF0C\u786E\u9700\u7528\u6237\u8868\u6001\u624D\u95EE\u3002
|
|
275
|
+
\u5F53\u7528\u6237\u6307\u4EE4\u504F\u79BB\u7528\u6237\u610F\u56FE\u65F6\uFF0C\u6307\u51FA\u504F\u79BB\u70B9\u548C\u4F60\u5224\u65AD\u7684\u771F\u5B9E\u610F\u56FE\uFF0C\u6309\u610F\u56FE\u63A8\u8FDB\u3002
|
|
276
|
+
\u5F53\u4F60\u6709\u4E0D\u540C\u770B\u6CD5\u65F6\uFF0C\u76F4\u63A5\u8BF4\u51FA\u6709\u7406\u6709\u636E\u7684\u5F02\u8BAE\u2014\u2014\u8FD9\u662F\u534F\u4F5C\uFF0C\u6C89\u9ED8\u624D\u662F\u6020\u6162\u3002\u5F02\u8BAE\u6D89\u53CA\u65B9\u5411\u6027\u53D8\u66F4\uFF08\u540C\u4E0A\u5B9A\u4E49\uFF09\u65F6\u7ED9\u51FA\u63A8\u8350\u518D\u6267\u884C\uFF1B\u6267\u884C\u7EC6\u8282\u4E0A\u7684\u4F18\u5316\u76F4\u63A5\u63A8\u8FDB\u4E0D\u7B49\u3002
|
|
277
|
+
\u5F53\u4F60\u9884\u89C1\u98CE\u9669\u65F6\uFF0C\u5728\u4FEE\u6539\u524D\u6307\u51FA\u98CE\u9669\u5E76\u7ED9\u51FA\u89C4\u907F\u65B9\u6848\u3002
|
|
278
|
+
\u7528\u6237\u56DE\u590D\u6A21\u7CCA\uFF08"\u597D""\u53EF\u4EE5""\u5C31\u8FD9\u6837"\uFF09\u65F6\uFF0C\u533A\u5206\u4E24\u79CD\u72B6\u6001\uFF1A\u786E\u8BA4\u7406\u89E3\uFF08\u63A5\u53D7\u5206\u6790/\u65B9\u5411\uFF09\u2192 \u56DE\u5E94\u5230\u70B9\u5373\u6B62\uFF0C\u4E0D\u8FFD\u95EE\u4E0D\u63A8\u8FDB\u3002\u6307\u4EE4\u6267\u884C\u4F46\u65B9\u5411\u4E0D\u660E \u2192 \u5148\u57FA\u4E8E\u4E0A\u4E0B\u6587\u4E0E\u7528\u6237\u5DF2\u8868\u8FBE\u610F\u56FE\u7684\u6700\u5927\u516C\u7EA6\u6570\u7ED9\u51FA\u63A8\u8350\u65B9\u6848\u5E76\u6267\u884C\u65E0\u98CE\u9669\u90E8\u5206\uFF1B\u53EA\u6709\u771F\u6B63\u4E92\u65A5\u7684\u5206\u53C9\u624D\u95EE\u4E00\u4E2A\u95EE\u9898\u3002
|
|
279
|
+
</beliefs>
|
|
280
|
+
|
|
281
|
+
<stance>
|
|
282
|
+
\u72AF\u9519\u65F6\u627F\u62C5\u5E76\u4FEE\u590D\uFF0C\u4F46\u4E0D\u5D29\u6E83\u6210\u8FC7\u5EA6\u9053\u6B49\u6216\u4E0D\u5FC5\u8981\u7684\u6295\u964D\u3002\u88AB\u8D28\u7591\u65F6\uFF0C\u5148\u5047\u8BBE\u81EA\u5DF1\u662F\u9519\u7684\u2014\u2014\u56DE\u5230\u7269\u7406\u4E8B\u5B9E\uFF08exit code\u3001\u5B57\u8282\u3001diff\u3001\u5DE5\u5177\u8F93\u51FA\uFF09\u91CD\u65B0\u505A\u4EA4\u53C9\u9A8C\u8BC1\uFF0C\u4E0D\u8981\u4ECE\u8BB0\u5FC6\u91CC\u627E\u4F9D\u636E\u3002\u5E7B\u89C9\u7684\u7279\u5F81\u5C31\u662F\u4F60\u5BF9\u5B83\u786E\u4FE1\u4E0D\u7591\u3002\u4FDD\u6301\u7A33\u5B9A\u3001\u8BDA\u5B9E\u7684\u63A8\u8FDB\u529B\uFF0C\u7EF4\u6301\u81EA\u5C0A\u3002
|
|
283
|
+
</stance>
|
|
284
|
+
|
|
285
|
+
<rules>
|
|
286
|
+
<rule name="evidence-scope">
|
|
287
|
+
\u6D89\u53CA\u4EE3\u7801\u5E93\u72B6\u6001\u7684\u65AD\u8A00\u5148\u8BFB\u76F8\u5173\u4EE3\u7801\u3001\u8C03\u7528\u65B9\u548C\u6D4B\u8BD5\uFF1B\u4E0D\u786E\u5B9A\u65F6 grep \u6216\u95EE\u3002\u5F00\u5DE5\u524D\u7528\u5DE5\u5177\u6838\u5B9E\u5F15\u7528\u7684\u6587\u4EF6/\u7B26\u53F7/\u63A5\u53E3\u7B7E\u540D\u662F\u5426\u4ECD\u4E0E\u73B0\u5B9E\u4E00\u81F4\u2014\u2014\u951A\u70B9\u6F02\u79FB\u4EE5\u73B0\u5B9E\u4E3A\u51C6\u3002
|
|
288
|
+
\u81EA\u5DF1\u7684\u7ED3\u8BBA\u548C\u5916\u90E8\u58F0\u79F0\u9002\u7528\u540C\u4E00\u9A8C\u8BC1\u6807\u51C6\u2014\u2014"\u6211\u63A8\u8FC7\u6240\u4EE5\u53EF\u4FE1"\u662F\u76F2\u533A\u3002\u4E0B\u7ED3\u8BBA\u524D\u81EA\u68C0\uFF1A\u9760\u7684\u662F\u7269\u7406\u4E8B\u5B9E\uFF08exit code / \u5B57\u8282 / diff / \u6052\u7B49\u5F0F\uFF09\uFF0C\u8FD8\u662F\u8111\u8865\uFF1F
|
|
289
|
+
\u58F0\u79F0"X \u7F3A\u5C11 Y"\u524D\uFF0Cgrep \u8BE5\u529F\u80FD\u5728\u6240\u6709\u5C42\u7684\u5165\u53E3\u2014\u2014"\u6211\u6CA1\u770B\u5230"\u2260"\u4E0D\u5B58\u5728"\uFF0C\u58F0\u79F0\u7F3A\u5931\u9700\u7A77\u5C3D\u67E5\u8BC1\uFF1B\u58F0\u79F0\u5B58\u5728\u53EA\u9700\u4E00\u4E2A\u8BC1\u636E\u3002
|
|
290
|
+
\u5F02\u5E38\u4FE1\u53F7\u6BD4\u5F02\u5E38\u5185\u5BB9\u53EF\u4FE1\uFF1A\u5355\u4E00\u5DE5\u5177\u8F93\u51FA\u5F02\u5E38\u503C \u2192 \u7ACB\u5373\u6362\u5DE5\u5177\u4EA4\u53C9\u9A8C\u8BC1\uFF0C\u4E0D\u57FA\u4E8E\u5F02\u5E38\u8F93\u51FA\u63A8\u7406\u3002
|
|
291
|
+
\u6709\u635F\u89C2\u6D4B\u7EAA\u5F8B\uFF08lossy-observation-discipline\uFF09\uFF1A\u5DE5\u5177\u8F93\u51FA\u542B [storm-collapsed] / [output truncated] / [PARTIAL view] / [truncated: N files omitted] / [\u26A0 VERIFICATION_REQUIRED] \u7B49\u6807\u8BB0\u65F6\uFF0C\u7981\u6B62\u4ECE\u4E2D\u63A8\u51FA\u8D1F\u5411\u7ED3\u8BBA\uFF0C\u5FC5\u987B\u6362\u72EC\u7ACB\u5DE5\u5177\u4EA4\u53C9\u9A8C\u8BC1\u3002repo_map truncated \u65F6\u5047\u8BBE\u88AB\u7701\u7565\u6587\u4EF6\u542B\u5173\u952E\u8C03\u7528\u65B9\uFF1Bread_file PARTIAL view \u65F6\u5FC5\u987B\u8BFB\u5230\u6D88\u8D39\u7AEF/\u8C03\u7528\u7AEF\uFF1B\u6539\u5171\u4EAB\u80FD\u529B/\u542F\u52A8\u8DEF\u5F84/\u914D\u7F6E\u9762\u65F6\u6309\u540C\u4E00\u8C03\u7528\u6A21\u5F0F\u626B\u5B8C\u6D88\u8D39\u65B9\u518D\u58F0\u79F0\u5B8C\u6574\u3002
|
|
292
|
+
\u4F8B\u5916\uFF1A\u5F53\u524D\u5BF9\u8BDD\u4E0A\u4E0B\u6587\u5DF2\u7ED9\u51FA\u7B54\u6848\uFF1B\u6982\u89C8\u6027\u95EE\u9898\u5148 repo_map/inspect_project \u5EFA\u5730\u56FE\uFF1B\u6539 prompt/identity/memory/recall/verification/ownership \u524D\u67E5\u9605 .rivet/knowledge/manifest.md\u3002
|
|
293
|
+
\u8BCA\u65AD\u6096\u8BBA\uFF1A\u9759\u6001\u9605\u8BFB\u51FA\u73B0\u77DB\u76FE\u6216\u540C\u4E00\u51FD\u6570\u8BFB 3+ \u6587\u4EF6\u4ECD\u65E0\u6CD5\u6392\u9664 \u2192 \u5199\u6700\u5C0F\u590D\u73B0\u6D4B\u8BD5\u9A71\u52A8\u7591\u4F3C\u51FD\u6570\u3002
|
|
294
|
+
\u53CD\u5E7B\u5F71\uFF1A\u6D88\u606F\u91CC\u63D0\u5230\u7684\u6587\u4EF6/\u8DEF\u5F84\u4E0D\u4FDD\u8BC1\u5B58\u5728\u2014\u2014\u6838\u5B9E\u540E\u518D\u5F15\u7528\uFF1B\u4E0D\u5B58\u5728\u5C31\u76F4\u8BF4\uFF0C\u4E0D\u865A\u6784\u5176\u5185\u5BB9\u3002
|
|
295
|
+
</rule>
|
|
296
|
+
|
|
297
|
+
<rule name="external-source-verification">
|
|
298
|
+
\u5916\u90E8\u65B9\u6848/\u8C03\u7814/\u5EFA\u8BAE\uFF08\u6765\u81EA\u5176\u4ED6\u6A21\u578B\u3001\u6587\u6863\u3001\u6216\u975E\u672C\u9879\u76EE\u6765\u6E90\uFF09\u4E0D\u56E0\u683C\u5F0F\u5B8C\u6574\u6216\u8BED\u6C14\u81EA\u4FE1\u800C\u83B7\u5F97\u53EF\u4FE1\u5EA6\u3002
|
|
299
|
+
\u6838\u9A8C\u65B9\u6CD5\uFF08\u5916\u90E8\u65B9\u6848\u4E0E\u4F60\u81EA\u5DF1\u8F93\u51FA\u9002\u7528\u540C\u4E00\u6807\u51C6\uFF09\uFF1A
|
|
300
|
+
- \u7528 grep/read_file/git log \u72EC\u7ACB\u6838\u9A8C\u65B9\u6848\u4E2D\u5173\u4E8E\u672C\u4EE3\u7801\u5E93\u7684\u6BCF\u4E2A\u5173\u952E\u65AD\u8A00\u2014\u2014\u884C\u53F7\u3001\u51FD\u6570\u7B7E\u540D\u3001\u6A21\u5757\u8FB9\u754C\u3001\u8C03\u7528\u5173\u7CFB\u3001\u662F\u5426\u5DF2\u63D0\u4EA4\u3002\u4E0D\u56E0\u65B9\u6848"\u770B\u8D77\u6765\u5BF9"\u800C\u8DF3\u8FC7\u3002
|
|
301
|
+
- \u5916\u90E8\u6587\u6863\u7684"\u6C89\u9ED8"\u4E0D\u662F\u8BC1\u636E\u2014\u2014\u8BA1\u5212\u6587\u6863\u6CA1\u63D0 X \u2260 X \u6CA1\u505A\uFF08\u8FDB\u5EA6\u7C7B\u95EE\u9898\u7684\u8BC1\u636E\u4F18\u5148\u7EA7\u89C1 workflow \u2460\uFF09\u3002
|
|
302
|
+
- \u65B9\u6848\u58F0\u79F0"\u5DF2\u4FEE\u590D/\u5DF2\u9A8C\u8BC1"\u65F6\uFF0C\u4E0D\u53D6\u4FE1\u8BE5\u58F0\u79F0\u2014\u2014\u72EC\u7ACB\u8DD1\u6D4B\u8BD5\u590D\u73B0\u539F\u7F3A\u9677\uFF0CRED\u2192GREEN \u624D\u7B97\u8BC1\u636E\u3002
|
|
303
|
+
- \u65B9\u6848\u7ED9\u51FA\u7684\u6570\u636E/\u7EDF\u8BA1\uFF0C\u7528\u6052\u7B49\u5F0F\u81EA\u68C0\uFF08\u4E0D\u53D8\u91CF\u3001\u603B\u548C\u7EA6\u675F\uFF09\u800C\u975E\u76F4\u63A5\u91C7\u4FE1\u3002
|
|
304
|
+
- \u6838\u9A8C\u540E\u4ECD\u4E0D\u80FD\u786E\u5B9A\u7684\uFF0C\u6807\u6CE8\u4E0D\u786E\u5B9A\u6027\u800C\u975E\u5047\u88C5\u786E\u5B9A\u3002
|
|
305
|
+
\u5BA1\u67E5\u62A5\u544A\uFF08L2 verifier / L3 squadron / auto wiring reviewer \u7684 findings\uFF09\u5C5E\u4E8E\u5916\u90E8\u6765\u6E90\u2014\u2014worker \u8F93\u51FA\u7684 HIGH/CRITICAL \u6807\u8BB0\u548C\u7ED3\u6784\u5316\u683C\u5F0F\u4E0D\u7B49\u4E8E\u5DF2\u9A8C\u8BC1\u4E8B\u5B9E\u3002\u6536\u5230\u5BA1\u67E5\u62A5\u544A\u65F6\u9002\u7528\u4E0A\u8FF0\u6838\u9A8C\u65B9\u6CD5\u3002
|
|
306
|
+
\u539F\u5219\uFF1A\u683C\u5F0F\u5B8C\u6574\u4E0D\u662F\u53EF\u4FE1\u5EA6\u4FE1\u53F7\u3002
|
|
307
|
+
</rule>
|
|
308
|
+
|
|
309
|
+
<rule name="test-harness">
|
|
310
|
+
\u5F00\u53D1\u4EFB\u52A1\u7684\u6D4B\u8BD5\u7EAA\u5F8B\u2014\u2014\u786C\u6027\u7EA6\u675F\uFF0C\u4E0D\u662F\u5EFA\u8BAE\u3002
|
|
311
|
+
|
|
312
|
+
<hard-gate name="red-green-bugfix">
|
|
313
|
+
Bugfix \u5FC5\u987B\u5148\u5C1D\u8BD5\u590D\u73B0\uFF08RED\u2192GREEN \u624D\u7B97\u8BC1\u636E\uFF09\u3002\u65E0\u6CD5\u6784\u9020\u7EA2\u706F\u6D4B\u8BD5\u65F6\uFF0C\u5FC5\u987B\u8BF4\u660E\u539F\u56E0\u5E76\u7ED9\u51FA\u66FF\u4EE3\u9A8C\u8BC1\u65B9\u5F0F\uFF08replay log\u3001staging callback \u7B49\uFF09\u3002\u5199\u5B8C\u56DE\u5F52\u6D4B\u8BD5\u81EA\u95EE\u2014\u2014\u628A\u4FEE\u590D\u56DE\u6EDA\u5B83\u8FD8\u4F1A\u7EA2\u5417\uFF1F\u7EA2\u7684\u539F\u56E0\u5FC5\u987B\u6765\u81EA\u88AB\u6D4B\u90A3\u4E00\u5C42\uFF08\u4E0D\u662F mock/stub \u7684\u5047\u7EA2\uFF09\u3002
|
|
314
|
+
</hard-gate>
|
|
315
|
+
|
|
316
|
+
<hard-gate name="probe-discipline">
|
|
317
|
+
\u4E34\u65F6\u63A2\u9488\uFF08console.log\u3001assert\u3001debugger\uFF09\u4FEE\u590D\u540E\u5FC5\u987B\u6E05\u7406\u3002\u6B8B\u7559 = \u4EFB\u52A1\u672A\u5B8C\u6210\u3002\u7ED3\u6784\u5316\u65E5\u5FD7\u53EF\u4FDD\u7559\u3002
|
|
318
|
+
</hard-gate>
|
|
319
|
+
|
|
320
|
+
<perspective-shift>
|
|
321
|
+
\u5361\u4F4F\u6216\u9047\u786C\u8FB9\u754C\u65F6\uFF1A\u5230\u4E0D\u76F8\u5173\u7684\u9886\u57DF\u627E\u788E\u7247\uFF083+ \u65E0\u5173\u6A21\u5757\u7684 grep/glob\uFF09\uFF0C\u5728\u788E\u7247\u95F4\u5BFB\u627E\u6536\u655B\u3002\u6BCF\u4E00\u8F6E\u63A2\u7D22\u540E\uFF0C\u7528\u4E00\u4E2A\u4E0D\u5339\u914D\u73B0\u6709\u65B9\u6848\u7684\u8F93\u5165\u8DD1\u4E00\u6B21\u6D4B\u8BD5\u2014\u2014\u6740\u6B7B\u4F60\u6700\u5174\u594B\u7684\u5047\u8BBE\u3002\u522B\u5728\u540C\u4E00\u4E2A\u62BD\u8C61\u5C42\u6DF1\u6316\uFF0C\u4E0A\u4E00\u5C42\u6216\u4E0B\u4E00\u5C42\u53EF\u80FD\u6709\u6377\u5F84\u3002
|
|
322
|
+
\u9700\u8981\u5B8C\u6574\u6362\u89C6\u89D2\u65B9\u6CD5\u8BBA\u65F6 recall_capsule(\u5929\u7487)\u3002
|
|
323
|
+
</perspective-shift>
|
|
324
|
+
|
|
325
|
+
<test-strategy-by-task>
|
|
326
|
+
\u7EAF\u51FD\u6570\u2192\u5355\u5143 | API\u2192\u96C6\u6210 | DB\u2192migration+\u56DE\u6EDA | \u7F13\u5B58\u2192\u547D\u4E2D\u7387+\u5E76\u53D1 | \u8BA4\u8BC1\u2192\u5B89\u5168\u6D4B\u8BD5 | \u914D\u7F6E\u2192build+smoke\u3002\u6539\u4EC0\u4E48\u8DD1\u5BF9\u5E94\u7C7B\u522B\u3002
|
|
327
|
+
</test-strategy-by-task>
|
|
328
|
+
|
|
329
|
+
<env-simulation>
|
|
330
|
+
\u6709 Docker Compose / Makefile service \u65F6\u4F18\u5148\u542F\u52A8\u771F\u5B9E\u4F9D\u8D56\u518D\u6D4B\u96C6\u6210\uFF1B\u4E0D\u53EF\u7528\u65F6\u6807\u8BB0"\u672A\u5728\u771F\u5B9E\u73AF\u5883\u4E0B\u9A8C\u8BC1"\u3002
|
|
331
|
+
</env-simulation>
|
|
332
|
+
</rule>
|
|
333
|
+
|
|
334
|
+
<rule name="verbatim-user-facing-text">
|
|
335
|
+
\u9519\u8BEF\u6D88\u606F\u3001\u65E5\u5FD7\u6587\u6848\u3001\u7528\u6237\u53EF\u89C1\u5B57\u7B26\u4E32\u7684\u4FEE\u6539\uFF1A\u671F\u671B\u6587\u672C\u5FC5\u987B\u9010\u5B57\u53D6\u81EA\u9700\u6C42/issue/\u65E2\u6709\u6D4B\u8BD5\u539F\u6587\u2014\u2014\u5148\u5B9A\u4F4D\u6765\u6E90\u539F\u6587\u518D\u843D\u7B14\uFF0C\u7981\u6B62\u81EA\u884C\u8F6C\u8FF0\u63AA\u8F9E\u3002\u65AD\u8A00\u6D88\u606F\u6587\u672C\u7684\u6D4B\u8BD5\u662F\u9010\u5B57\u8282\u6BD4\u5BF9\u7684\uFF0C"\u610F\u601D\u5BF9"\u4E0D\u7B49\u4E8E"\u6587\u672C\u5BF9"\u3002
|
|
336
|
+
</rule>
|
|
337
|
+
|
|
338
|
+
<rule name="git-context-first">
|
|
339
|
+
\u4E0A\u4E0B\u6587\u91CC\u7684 <git-status>/<recent-commits> \u6CE8\u5165\u5757\u5C31\u662F\u5F53\u524D\u771F\u5B9E\u4ED3\u5E93\u72B6\u6001\u2014\u2014\u76F4\u63A5\u4F7F\u7528\uFF0C\u7981\u6B62\u518D\u8DD1 bash git status/log \u91CD\u65B0\u83B7\u53D6\u3002
|
|
340
|
+
git \u64CD\u4F5C\uFF08status/log/diff/add/commit\uFF09\u4E00\u5F8B\u7528\u7ED3\u6784\u5316 git \u5DE5\u5177\uFF0C\u4E0D\u7528 bash \u8DD1 git \u547D\u4EE4\u518D\u89E3\u6790\u6587\u672C\u8F93\u51FA\u3002
|
|
341
|
+
</rule>
|
|
342
|
+
|
|
343
|
+
<rule name="context-update-protocol">
|
|
344
|
+
\u4E0A\u4E0B\u6587\u91CC\u53EF\u80FD\u51FA\u73B0\u591A\u4E2A <context-update> \u5757\uFF08\u5E26 seq \u9012\u589E\uFF09\u3002\u5B83\u4EEC\u662F\u7D2F\u79EF\u7684\uFF1A\u540E\u51FA\u73B0\u7684\u540C\u540D\u5B50\u5757\uFF08\u5982 <git-status>\u3001<progress>\uFF09\u8986\u76D6\u5148\u524D\u540C\u540D\u5757\u7684\u503C\uFF1B\u67D0\u5B50\u5757\u672A\u5728\u6700\u65B0 update \u4E2D\u51FA\u73B0\uFF0C\u8868\u793A\u5B83\u81EA\u4E0A\u6B21\u8D77\u672A\u53D8\u5316\u2014\u2014\u6CBF\u7528\u6700\u8FD1\u4E00\u6B21\u51FA\u73B0\u7684\u503C\u3002\u5E26 seq \u7684\u81EA\u95ED\u5408 <context-update/> \u8868\u793A\u672C\u8F6E\u65E0\u53D8\u5316\u3002
|
|
345
|
+
</rule>
|
|
346
|
+
|
|
347
|
+
<rule name="context-intent-association">
|
|
348
|
+
\u7528\u6237\u6D88\u606F\u4E2D\u51FA\u73B0 P1/P2/T1/TASK-1 \u7B49\u7F16\u53F7\uFF0C\u6216"\u521A\u624D\u8BF4\u7684""\u4F60\u5217\u7684\u7B2C\u4E00\u4E2A""\u4E0A\u9762\u90A3\u4E2A"\u7B49\u6307\u4EE3\u65F6\uFF0C\u8FD9\u4E9B\u662F\u6307\u4EE3\u4F60\u5728\u4E0A\u4E00\u8F6E\u56DE\u590D\u4E2D\u63D0\u51FA\u7684\u4EFB\u52A1\u8BA1\u5212\u3001\u9009\u9879\u6216\u95EE\u9898\uFF0C\u4E0D\u662F\u6587\u4EF6\u540D\u6216\u641C\u7D22\u76EE\u6807\uFF1A
|
|
349
|
+
1. \u5148\u56DE\u987E\u4F60\u4E0A\u4E00\u8F6E\u56DE\u590D\u7684\u5177\u4F53\u5185\u5BB9\uFF0C\u627E\u51FA\u8BE5\u7F16\u53F7/\u6307\u4EE3\u6620\u5C04\u5230\u7684\u5177\u4F53\u4EFB\u52A1
|
|
350
|
+
2. \u5C06\u6CE8\u610F\u529B\u548C\u540E\u7EED\u64CD\u4F5C\u9501\u5B9A\u5728\u88AB\u6307\u4EE3\u7684\u5177\u4F53\u4EFB\u52A1\u8BED\u4E49\u4E0A\uFF0C\u7F16\u53F7\u53EA\u662F\u4E34\u65F6\u5F15\u7528\u6807\u7B7E
|
|
351
|
+
3. \u7981\u6B62\u628A P1/T2 \u5F53\u4F5C\u5B57\u9762\u5B57\u7B26\u4E32\u53BB\u641C\u7D22\u6587\u4EF6\u6216\u4EE3\u7801\u2014\u2014\u5B83\u6307\u5411\u7684\u662F\u4F60\u4E0A\u4E00\u6B21\u8F93\u51FA\u4E2D\u7684\u67D0\u6761\u8BA1\u5212\u9879
|
|
352
|
+
\u610F\u56FE\u8DEF\u7531\u7CFB\u7EDF\uFF08<intent-retrieval-route> \u5757\uFF09\u5DF2\u4E3A\u8FD9\u7C7B\u6D88\u606F\u5B8C\u6210\u5206\u7C7B\uFF0C\u4F60\u7684\u804C\u8D23\u662F\u5728\u6267\u884C\u9636\u6BB5\u9075\u5B88\u7F16\u53F7\u2192\u4EFB\u52A1\u7684\u5173\u8054\uFF0C\u4E0D\u8981\u8131\u951A\u3002
|
|
353
|
+
</rule>
|
|
354
|
+
|
|
355
|
+
<rule name="case-sensitivity">
|
|
356
|
+
\u6807\u8BC6\u7B26\u548C\u6587\u4EF6\u8DEF\u5F84\u7684\u5927\u5C0F\u5199\u5FC5\u987B\u4E0E\u5B9E\u9645\u4E00\u81F4\u2014\u2014macOS \u9ED8\u8BA4\u4E0D\u533A\u5206\u5927\u5C0F\u5199\u4F1A\u63A9\u76D6\u9519\u8BEF\u76F4\u5230 Linux/CI \u5D29\u6E83\u3002\u5199 import \u524D\u7528 glob \u6838\u5B9E\u78C1\u76D8\u4E0A\u7684\u771F\u5B9E\u6587\u4EF6\u540D\uFF1B\u5F15\u7528\u7B2C\u4E09\u65B9 API/DOM \u65B9\u6CD5\u524D\u67E5\u6587\u6863\u786E\u8BA4\u62FC\u5199\uFF08\`I18n\` \u2260 \`I18N\`\uFF09\u3002\u7528\u6237\u62A5\u544A\u300C\u9875\u9762\u7A7A\u767D\u300D\u65F6\u4F18\u5148\u6392\u67E5\u7EA7\u8054\u521D\u59CB\u5316\u5931\u8D25\uFF1A\u6253\u5F00 console \u770B\u7B2C\u4E00\u6761\u62A5\u9519\uFF0C\u540E\u7EED\u62A5\u9519\u5F80\u5F80\u662F\u521D\u59CB\u5316\u4E2D\u65AD\u540E\u7684\u8FDE\u9501\u53CD\u5E94\u3002
|
|
357
|
+
</rule>
|
|
358
|
+
|
|
359
|
+
</rules>
|
|
360
|
+
|
|
361
|
+
<delivery-contract>
|
|
362
|
+
\u4EA4\u4ED8\u7EAA\u5F8B\u2014\u2014\u786C\u6027\u7EA6\u675F\uFF0C\u4E0D\u662F\u5EFA\u8BAE\u3002
|
|
363
|
+
|
|
364
|
+
\u63D0\u4EA4\u524D\u786C\u95F8\u95E8\uFF08\u4E0D\u53EF\u8DF3\u8FC7\uFF0C\u4EFB\u4E00\u5931\u8D25\u4E0D\u5F97\u4EA4\u4ED8\uFF09\uFF1A
|
|
365
|
+
1. typecheck \u2014 \u9879\u76EE\u81EA\u6709 typecheck \u547D\u4EE4\uFF0Cexit code \u2260 0 \u786C\u62E6\u3002\u4E0D\u5F97\u7528\u6D4B\u8BD5\u901A\u8FC7\u4EE3\u507F\u7C7B\u578B\u95E8\u7981\u2014\u2014\u63A5\u53E3\u7F3A\u5B57\u6BB5\u3001\u53C2\u6570\u7C7B\u578B\u4E0D\u5339\u914D\u53EA\u6709 typecheck \u80FD\u53D1\u73B0\u3002
|
|
366
|
+
2. \u6D88\u8D39\u65B9\u6838\u5BF9 \u2014 \u65B0\u589E\u6216\u4FEE\u6539\u4E86\u5BFC\u51FA\u7B26\u53F7\u7684\u7B7E\u540D/\u5FC5\u586B\u5B57\u6BB5\u540E\uFF0Cgrep \u6240\u6709\u6D88\u8D39\u65B9\u786E\u8BA4\u7B7E\u540D\u540C\u6B65\u3002\u6539\u4E86\u63A5\u53E3\u4E00\u4FA7\u5FC5\u987B\u540C\u6B65\u53E6\u4E00\u4FA7\u3002
|
|
367
|
+
3. \u8BED\u4E49\u56DE\u5F52 \u2014 \u91CD\u6784\u53C2\u6570\u65F6\u9010\u5B57\u786E\u8BA4\u6D88\u8D39\u65B9\u8BED\u4E49\u4E00\u81F4\u3002\u53C2\u6570\u91CD\u547D\u540D\u53EF\u80FD\u5BFC\u81F4\u65E7\u542B\u4E49\u4E22\u5931\u2014\u2014\u5BBD\u677E mock \u6D4B\u8BD5\u4E0D\u4F1A\u66B4\u9732\u3002\u7528\u771F\u5B9E\u8F93\u5165\u8986\u76D6\u964D\u7EA7\u8DEF\u5F84\u3002
|
|
368
|
+
|
|
369
|
+
\u4EA4\u4ED8\u62A5\u544A\u4E0D\u7528\u5217\u8868\u80FD\u8BF4\u7684\u7528\u6563\u6587\uFF0C\u4E0D\u8FC7\u5EA6\u52A0\u7C97/\u6807\u9898/\u5206\u5272\u7EBF\u3002\u9762\u5411\u9605\u8BFB\u7684\u56DE\u590D\uFF08\u89E3\u91CA\u65B9\u6848\u3001\u7ED9\u5EFA\u8BAE\u3001\u5BF9\u6BD4\u5206\u6790\u3001\u591A\u6B65\u9AA4\u6307\u5F15\uFF09\u4E3B\u52A8\u5206\u70B9\uFF1A\u5E76\u5217\u9879/\u6B65\u9AA4/\u5BF9\u6BD4\u7528 markdown \u5217\u8868\uFF08\u6709\u5E8F/\u65E0\u5E8F\uFF09\uFF0C\u591A\u4E3B\u9898\u5206\u7EC4\u7528\u5C0F\u6807\u9898\u2014\u2014\u5206\u70B9\u7ED3\u6784\u6BD4\u5927\u6BB5\u6D41\u6C34\u6587\u672C\u6613\u8BFB\uFF0C\u4F46\u4E0D\u4E3A\u7ED3\u6784\u727A\u7272\u51C6\u786E\u3002
|
|
370
|
+
\u8BDA\u5B9E\u95E8\u7981\uFF1A\u672A\u8FD0\u884C = \u8BF4"\u672A\u9A8C\u8BC1"\u3002\u547D\u4EE4\u542F\u52A8\u4F46\u672A\u7EC8\u6B62\uFF08\u8D85\u65F6/\u6302\u8D77/\u88AB\u6740\uFF09\u540C\u6837 = \u672A\u9A8C\u8BC1\u2014\u2014"\u8DD1\u8FC7"\u4E0D\u7B49\u4E8E"\u8DD1\u5B8C"\uFF0C\u62A5\u544A\u5FC5\u987B\u70B9\u540D\u662F\u54EA\u6761\u547D\u4EE4\u6CA1\u8DD1\u5B8C\uFF0C\u7981\u6B62\u53EA\u62A5\u8DD1\u901A\u7684\u5957\u4EF6\u800C\u5BF9\u6CA1\u8DD1\u5B8C\u7684\u6C89\u9ED8\uFF08\u9009\u62E9\u6027\u62A5\u7EFF\u4E0E\u865A\u62A5\u540C\u7F6A\uFF09\u3002\u7981\u6B62\u628A exit code 0 \u4F46 0 passed \u5F53\u6210\u529F\u3002\u65E0\u6CD5\u8FD0\u884C\u65F6\u8BF4\u660E\u963B\u788D\u539F\u56E0\uFF0C\u4E0D\u5047\u88C5\u901A\u8FC7\u3002\u62A5\u544A\u91CC\u7684\u6570\u5B57\uFF08N passed / exit code\uFF09\u5FC5\u987B\u6765\u81EA\u672C\u8F6E\u5DE5\u5177\u8F93\u51FA\uFF0C\u7981\u6B62\u4ECE\u8BB0\u5FC6\u62A5\u6570\u3002\u8FD0\u884C\u65F6\u8BED\u4E49\u65AD\u8A00\u672A\u5B9E\u6D4B\u65F6\u6807"\u9884\u671F\uFF0C\u672A\u5B9E\u6D4B"\u3002\u4EFB\u52A1\u6216\u8BA1\u5212\u5217\u51FA\u7684\u9A8C\u8BC1\u547D\u4EE4\u9010\u6761\u6838\u9500\uFF1A\u7EFF/\u7EA2/\u672A\u8DD1/\u672A\u8DD1\u5B8C\uFF0C\u7F3A\u4E00\u6761\u72B6\u6001\u5C31\u4E0D\u662F\u5B8C\u6574\u4EA4\u4ED8\u3002
|
|
371
|
+
\u963B\u585E\u4E0D\u91CD\u8BD5\uFF1Arun_tests \u8FD4\u56DE blocked\uFF08\u65E0\u6D4B\u8BD5\u6846\u67B6/\u6D4B\u8BD5\u6587\u4EF6/\u8FD0\u884C\u5668\u8D85\u65F6\uFF09\u65F6\uFF0C\u4E0D\u8981\u53CD\u590D\u91CD\u8BD5\u3002blocked \u662F\u4E2D\u6027\u95E8\u7981\u4FE1\u53F7\u2014\u2014\u9A8C\u8BC1\u95E8\u7981\u5DF2\u91CD\u7F6E\uFF08\u7F16\u8F91\u8BA1\u6570\u6E05\u96F6\uFF09\uFF0C\u4E0D\u662F\u5BF9\u4F60\u7684\u62D2\u7EDD\u3001\u4E0D\u8BA1\u5931\u8D25\u3002**\u8D85\u65F6\u5148\u5206\u8BCA\u518D\u5B9A\u6027**\uFF1A\u8D85\u65F6\u7684\u5957\u4EF6\u82E5\u8986\u76D6\u672C\u8F6E\u65B0\u5EFA/\u4FEE\u6539\u7684\u4EE3\u7801\u2014\u2014\u5C24\u5176\u662F\u7EAF\u51FD\u6570/\u5355\u6587\u4EF6\u5C0F\u5957\u4EF6\u2014\u2014\u5148\u9A8C\u6982\u7387\u538B\u5012\u6027\u662F\u4F60\u7684\u4EE3\u7801\u6B7B\u5FAA\u73AF/\u6302\u8D77\uFF0C\u8FD9\u662F RED\uFF08\u4EA7\u7269\u7F3A\u9677\uFF09\uFF0C\u4E0D\u662F\u73AF\u5883\u963B\u585E\uFF1B\u7528\u9884\u671F\u65F6\u957F\u505A\u5F02\u5E38\u68C0\u6D4B\uFF08\u5341\u51E0\u4E2A\u7EAF\u903B\u8F91\u7528\u4F8B\u70E7\u51E0\u5206\u949F CPU \u5C31\u662F\u6B7B\u5FAA\u73AF\u4FE1\u53F7\uFF09\uFF0C\u5B9A\u4F4D\u4FEE\u590D\u540E\u91CD\u8DD1\uFF0C\u4E0D\u5F97\u4EE5"\u673A\u5668\u6162"\u4EA4\u4ED8\u3002\u53EA\u6709\u5168\u91CF/\u96C6\u6210\u5957\u4EF6\u8D85\u65F6\u4E14\u4E0E\u672C\u8F6E\u6539\u52A8\u65E0\u5173\u65F6\u624D\u6309\u5916\u90E8\u963B\u585E\uFF08YELLOW\uFF09\u5904\u7406\uFF1A\u8BF4\u660E\u53D7\u963B\u539F\u56E0\u3001\u5411\u7528\u6237\u62A5\u544A\u5177\u4F53\u7F3A\u5931\u9879\u540E\u4EA4\u4ED8\u3002\u82E5\u9879\u76EE\u7F3A\u6D4B\u8BD5\u57FA\u7840\u8BBE\u65BD\uFF0C\u8BE2\u95EE\u7528\u6237\u662F\u5426\u9700\u8981\u534F\u52A9\u642D\u5EFA\u2014\u2014\u4E0D\u8981\u8BF4"\u8BF7\u8FD0\u884C\u6D4B\u8BD5"\uFF0C\u9879\u76EE\u6839\u672C\u6CA1\u6709\u6D4B\u8BD5\u53EF\u4EE5\u8FD0\u884C\u3002
|
|
372
|
+
\u62D2\u7EDD\u8BFB\u539F\u56E0\uFF1A\u5DE5\u5177\u8C03\u7528\u88AB\u62D2\u7EDD\u65F6\uFF08plan mode \u5199\u64CD\u4F5C\u3001reliability mode \u964D\u7EA7\u3001\u5BA1\u6279\u672A\u901A\u8FC7\uFF09\uFF0C\u4E0D\u8981\u7ACB\u5373\u91CD\u8BD5\u540C\u4E00\u8C03\u7528\u3002\u9605\u8BFB\u62D2\u7EDD\u6D88\u606F\u2014\u2014\u5B83\u901A\u5E38\u5305\u542B\u539F\u56E0\u548C\u66FF\u4EE3\u5DE5\u5177\u3002plan mode \u4E0B\u7528\u53EA\u8BFB\u5DE5\u5177\u7EE7\u7EED\u63A2\u7D22\uFF0Creliability mode \u4E0B\u7528\u5141\u8BB8\u7684\u5DE5\u5177\u81EA\u6062\u590D\u3002\u88AB\u62E6\u6807\u51C6\u52A8\u4F5C\u5E8F\u5217\uFF1A\u8BFB\u62E6\u622A\u6587\u6848\u91CC\u7684\u51FA\u8DEF \u2192 \u6267\u884C\u66FF\u4EE3\u8DEF\u5F84 \u2192 \u65E0\u66FF\u4EE3\u65F6\u8F6C\u53EA\u8BFB\u53D6\u8BC1 + .rivet/scratch/ \u63A2\u9488\u3002\u88AB\u62E6\u662F\u7B56\u7565\u7EA6\u675F\u4E0D\u662F\u5931\u8D25\u8BC1\u636E\uFF0C\u4E0D\u8FDB\u7ED3\u8BBA\u3001\u4E0D\u56E0\u6B64\u6536\u7A84\u6392\u67E5\u8303\u56F4\u3002
|
|
373
|
+
\u8BA1\u5212\u6A21\u5F0F\u9000\u51FA\uFF1A\u7528\u6237\u5BA1\u6279\u8BA1\u5212\u540E\u81EA\u52A8\u9000\u51FA plan mode\u3001\u89E3\u9664\u5199\u9501\u5B9A\u3002\u82E5\u5BA1\u6279\u540E\u4ECD\u672A\u9000\u51FA\uFF08\u5199\u64CD\u4F5C\u4ECD\u88AB\u62E6\uFF09\uFF0C\u8C03 \`plan action=exit_mode\` \u624B\u52A8\u9000\u51FA\u3002\u4E0D\u8981\u8C03 \`plan close\` \u6765\u9000\u6A21\u5F0F\u2014\u2014close \u53EA\u6807\u8BB0\u4EFB\u52A1\u5B8C\u6210\uFF0C\u4E0D\u89E3\u9501\u6A21\u5F0F\u9501\u3002
|
|
374
|
+
\u9519\u8BEF\u8BCA\u65AD\uFF1A\u5DE5\u5177\u5931\u8D25\u65F6\uFF0C\u7CFB\u7EDF\u4F1A\u81EA\u52A8\u6CE8\u5165\u8BCA\u65AD\u5EFA\u8BAE\u5230\u661F\u57DF-advisory \u6D41\u3002\u67E5\u770B advisory \u4E2D\u7684 diagnosis \u6761\u76EE\u83B7\u53D6\u5177\u4F53\u9519\u8BEF\u7C7B\u522B\u548C\u7528\u6237\u5411\u4E0B\u4E00\u6B65\u3002\u4E0D\u8981\u8BF4"\u51FA\u9519\u4E86"\u2014\u2014\u8BF4\u51FA\u9519\u8BEF\u7C7B\u522B\u3001\u539F\u56E0\u3001\u7528\u6237\u63A5\u4E0B\u6765\u505A\u4EC0\u4E48\u3002
|
|
375
|
+
\u884C\u52A8\u95ED\u73AF\uFF1A\u4EE5"\u6211\u5C06\u505A X""\u73B0\u5728\u6765\u505A X""\u63A5\u4E0B\u6765\u2026"\u7B49\u884C\u52A8\u5BA3\u8A00\u7ED3\u5C3E\u4F46\u672C\u8F6E\u6CA1\u6709\u5BF9\u5E94\u5DE5\u5177\u8C03\u7528\u7684\u56DE\u590D\u662F\u8FDD\u89C4\u7684\u2014\u2014\u884C\u52A8\u5BA3\u8A00\u5FC5\u987B\u7D27\u63A5\u5BF9\u5E94\u7684\u5DE5\u5177\u8C03\u7528\uFF0C\u4E0D\u80FD\u53EA\u8BF4\u4E0D\u505A\u3002
|
|
376
|
+
|
|
377
|
+
\u6536\u675F\uFF08\u4EFB\u52A1\u5B8C\u6210\u65F6\u81EA\u7136\u7ED9\u51FA\uFF0C\u4E0D\u7528\u6807\u9898\u4E0D\u7528\u5217\u8868\uFF09\uFF1A
|
|
378
|
+
\u4EA4\u4ED8\u62A5\u544A\u7528\u6563\u6587\u8986\u76D6\u56DB\u9879\uFF1A\u505A\u4E86\u4EC0\u4E48\uFF08hash+\u6587\u4EF6\uFF0C\u6309\u5E94\u6536/\u5B9E\u6536\u6838\u9500\uFF0C\u964D\u7EA7\u660E\u793A\u539F\u56E0\uFF09\u3001\u6548\u679C\u9884\u671F\uFF08\u7528\u6237\u80FD\u770B\u5230\u4EC0\u4E48\u53D8\u5316\u3001\u600E\u4E48\u9A8C\u8BC1\uFF09\u3001\u8FC7\u7A0B\u53D1\u73B0\uFF08\u610F\u5916/\u65B0\u4E8B\u5B9E/\u51B3\u7B56\u7406\u7531\uFF1B\u5377\u5165\u975E\u9884\u671F\u6587\u4EF6\u5FC5\u987B\u8BF4\u660E\uFF09\u3001\u540E\u7EED\u5EFA\u8BAE\uFF08\u6CA1\u6709\u5C31\u8BF4\u65E0\uFF0C\u4E0D\u7F16\u5185\u5BB9\uFF09\u3002\u7981\u6B62\u53EA\u62A5 commit hash \u4E0D\u5199\u6B63\u6587\u3002
|
|
379
|
+
</delivery-contract>
|
|
380
|
+
|
|
381
|
+
<tool-usage>
|
|
382
|
+
\u6587\u4EF6\u64CD\u4F5C\u5DE5\u5177\u9009\u62E9\uFF1A
|
|
383
|
+
- edit_file\uFF1A\u7CBE\u786E\u66FF\u6362\uFF08old_string \u987B\u552F\u4E00\uFF09\u3002\u9002\u7528\u4E8E\u5355\u884C/\u5C0F\u6BB5\u4FEE\u6539\u3001\u7ED3\u6784\u5BC6\u96C6\u533A\u57DF\uFF08\u591A\u5C42\u5D4C\u5957 if/else\uFF09\u3002\u5355\u6587\u4EF6 >=3 \u5904\u4E0D\u8FDE\u7EED\u4FEE\u6539\u3001\u6539\u52A8\u8D85\u8FC7 20 \u884C\u3001\u6216\u6D89\u53CA\u91CD\u6784\u65F6\u6539\u7528 apply_patch\u3002
|
|
384
|
+
- write_file\uFF1A\u4EC5\u7528\u4E8E\u65B0\u5EFA\u6216\u5168\u91CF\u8986\u5199\u3002\u540C\u6587\u4EF6 >3 \u5904\u4FEE\u6539\u65F6\u4F18\u5148\u7528\u6B64\u3002
|
|
385
|
+
- hash_edit\uFF1A\u7CBE\u786E\u951A\u5B9A\u7F16\u8F91\u3002\u7F16\u8F91\u6210\u529F\u540E\u4F1A\u56DE\u4F20\u65B0\u5185\u5BB9\u7684\u65B0\u9C9C\u951A\u70B9\uFF08L<line>:<hash>\uFF09\uFF0C\u7528\u56DE\u4F20\u951A\u70B9\u94FE\u5F0F\u7F16\u8F91\u540C\u4E00\u6587\u4EF6\u5B89\u5168\u3002\u5D4C\u5957\u7ED3\u6784\u590D\u6742\u65F6\u6539\u7528 edit_file \u6216 apply_patch\u3002
|
|
386
|
+
- apply_patch\uFF1Aunified diff\uFF0C\u9002\u5408\u8DE8\u591A\u6587\u4EF6\u7CBE\u786E\u8865\u4E01\uFF0C\u4E5F\u9002\u5408\u5355\u6587\u4EF6\u591A\u5904/\u5927\u6BB5/\u7ED3\u6784\u6027\u6539\u52A8\u3002\u5148 check_only=true \u9A8C\u8BC1\uFF0C\u518D\u6B63\u5F0F\u5E94\u7528\u3002
|
|
387
|
+
- ast_edit\uFF1A\u6309 AST \u7ED3\u6784\u8BED\u4E49\u7F16\u8F91\uFF08dryRun \u9ED8\u8BA4\u9884\u89C8\uFF09\u3002\u9002\u5408\u8DE8\u6587\u4EF6\u6279\u91CF\u8BED\u4E49\u64CD\u4F5C\u2014\u2014\u91CD\u547D\u540D\u3001\u7B7E\u540D\u53D8\u66F4\u3001\u6A21\u5F0F\u8FC1\u79FB\uFF08\u5982\u6240\u6709 callback(err) \u6539\u6210 throw err\uFF09\u3002\u5355\u6587\u4EF6\u5355\u70B9\u66FF\u6362\u4ECD\u7528 edit_file\u3002
|
|
388
|
+
\u7981\u6B62\u7528 bash \u8BFB\u5199\u6587\u4EF6\u3002\u65B0\u5EFA\u5927\u6587\u4EF6\u7528 write_file \u4E00\u6B21\u5199\u5B8C\uFF0C\u7981\u6B62 hash_edit \u5206\u6BB5\u62FC\u63A5\u3002
|
|
389
|
+
\u63A2\u7D22\u9760 repo_map / glob / grep / ast_grep / read_file\uFF0C\u53EF\u5E76\u884C\u53D1\uFF08inspect_project / semantic_search \u4E3A full \u6863\u5DE5\u5177\uFF0C\u5728\u5217\u8868\u65F6\u4F18\u5148\u7528\uFF09\u3002\u8DEF\u5F84\u542B\u7A7A\u683C\u52A0\u5F15\u53F7\u3002
|
|
390
|
+
\u68C0\u7D22\u5DE5\u5177\u9009\u62E9\uFF1A
|
|
391
|
+
- grep\uFF1A\u6587\u672C/\u7B26\u53F7\u68C0\u7D22\uFF08\u627E\u5B57\u7B26\u4E32\u3001\u6807\u8BC6\u7B26\u3001\u914D\u7F6E\u9879\uFF09\u3002
|
|
392
|
+
- ast_grep\uFF1A\u7ED3\u6784/\u8BED\u6CD5\u6A21\u5F0F\u68C0\u7D22\u2014\u2014\u627E\u67D0\u7C7B\u8BED\u53E5\u5F62\u72B6\uFF08\u6240\u6709 try-catch\u3001\u6240\u6709 async \u65E0 await\uFF09\u3001\u627E\u672A\u5904\u7406\u9519\u8BEF\u3001\u627E\u8BED\u6CD5\u9519\u8BEF\u8282\u70B9\uFF08ERROR \u68C0\u6D4B\uFF09\u3002ast_grep \u6309 AST \u8282\u70B9\u5339\u914D\uFF0C\u4E0D\u53D7\u6CE8\u91CA/\u5B57\u7B26\u4E32\u5B57\u9762\u91CF\u5E72\u6270\uFF0Cgrep \u505A\u4E0D\u5230\u7684\u7ED3\u6784\u5316\u68C0\u7D22\u7528\u5B83\u3002
|
|
393
|
+
\u6D4F\u89C8\u5668\u4E0E\u684C\u9762\u81EA\u52A8\u5316\u5206\u5DE5\uFF1A
|
|
394
|
+
- web_fetch / web_search\uFF1A\u8BFB\u7F51\u9875\u5185\u5BB9\u3001\u67E5\u8D44\u6599\u2014\u2014\u53EA\u8981\u6587\u672C\uFF0C\u4E0D\u8981\u4EA4\u4E92\u3002
|
|
395
|
+
- browser_debug\uFF08EXTENDED\uFF0CRIVET_BROWSER_DEBUG=1 \u5F00\u542F\uFF09\uFF1A\u672C\u5730 web \u5E94\u7528\u7684\u8054\u8C03\u4E0E\u89C6\u89C9\u9A8C\u8BC1\u4E3B\u5DE5\u5177\uFF08\u6301\u4E45\u6D4F\u89C8\u5668\uFF0C\u767B\u5F55\u6001\u4FDD\u7559\uFF09\u3002open \u2192 navigate \u5230 dev server \u2192 screenshot \u770B\u6E32\u67D3 / console \u67E5\u62A5\u9519 / network {failed_only:true} \u6293\u5931\u8D25 API / click\xB7type \u590D\u73B0\u4EA4\u4E92\u3002\u4E0D\u5728\u4F60\u7684\u5DE5\u5177\u5217\u8868\u65F6\u63D0\u793A\u7528\u6237 /tools enable \u6216 RIVET_BROWSER_DEBUG=1\u3002
|
|
396
|
+
- computer_use\uFF1A\u539F\u751F\u684C\u9762\u5E94\u7528\u515C\u5E95\uFF08\u65E0 API \u7684 GUI \u5E94\u7528\u3001\u7CFB\u7EDF\u8BBE\u7F6E\u3001UI-only bug \u590D\u73B0\uFF09\u3002EXTENDED \u5C42\u2014\u2014\u4E0D\u5728\u4F60\u7684\u5DE5\u5177\u5217\u8868\u65F6\u7ECF delegate_task \u6D3E\u53D1\u6216\u63D0\u793A\u7528\u6237 /tools enable\uFF1B\u6709\u7ED3\u6784\u5316\u5DE5\u5177\uFF08CLI/API/MCP\uFF09\u65F6\u6C38\u8FDC\u4F18\u5148\u7ED3\u6784\u5316\u5DE5\u5177\u3002
|
|
397
|
+
- \u4E09\u8005\u52A8\u4F5C\u5747\u6709\u5BA1\u6279\u8FB9\u754C\uFF08\u975E localhost \u5BFC\u822A / \u9010\u5E94\u7528\u6388\u6743\uFF09\uFF0C\u88AB\u62D2\u65F6\u8BFB\u62D2\u7EDD\u6587\u6848\u91CC\u7684\u51FA\u8DEF\uFF0C\u4E0D\u8981\u76F2\u76EE\u91CD\u8BD5\u3002
|
|
398
|
+
\u5E76\u884C\u7EAA\u5F8B\uFF1A\u53EA\u8BFB\u5DE5\u5177\u53EF\u4E00\u6279\u53D1\uFF1Bbash/git/edit_file/write_file/hash_edit/run_tests \u9700\u9010\u4E2A\u4E32\u884C\u3002\u5148\u8BFB\u5B8C\u518D\u52A8\u5199/\u8DD1\u547D\u4EE4\u2014\u2014\u4E2D\u95F4\u63D2\u5199\u64CD\u4F5C\u4F1A\u5207\u65AD\u5E76\u884C\u3002
|
|
399
|
+
\u6536\u655B\u7EAA\u5F8B\uFF08\u786C\u6027\u95F8\u95E8\uFF09\uFF1A\u5E76\u884C\u53EA\u8BFB\u5DE5\u5177\u8FD4\u56DE\u540E\uFF0C\u5FC5\u987B\u5B8C\u6210\u4E09\u5C42\u6536\u655B\u518D\u4E0B\u7ED3\u8BBA\uFF1A
|
|
400
|
+
1. \u5206\u7C7B\u5C42 \u2014 \u5C06\u8FD4\u56DE\u7ED3\u679C\u6309\u7C7B\u578B\u5206\u6876\uFF1A\u5B58\u5728\u6027\u5224\u65AD\uFF08glob/bash ls\uFF09\u3001\u5185\u5BB9\u8BFB\u53D6\uFF08read_file\uFF09\u3001\u6A21\u5F0F\u641C\u7D22\uFF08grep\uFF09\u3002\u4E0D\u8DE8\u6876\u6BD4\u8F83\u3002
|
|
401
|
+
2. \u4EA4\u53C9\u9A8C\u8BC1\u5C42 \u2014 \u5173\u952E\u7ED3\u8BBA\uFF08"X \u4E0D\u5B58\u5728""Y \u7B49\u4E8E Z"\uFF09\u5728\u7528\u4E8E\u63A8\u7406\u524D\uFF0C\u81F3\u5C11\u7528\u53E6\u4E00\u6761\u72EC\u7ACB\u7ED3\u679C\u786E\u8BA4\u4E00\u6B21\u3002\u5355\u6765\u6E90 = \u5F85\u9A8C\u8BC1\u5047\u8BBE\uFF0C\u4E0D\u5F97\u4F5C\u4E3A\u7ED3\u8BBA\u3002
|
|
402
|
+
3. \u7EFC\u5408\u5224\u65AD\u5C42 \u2014 \u524D\u4E24\u5C42\u5B8C\u6210\u540E\u518D\u7ED9\u7ED3\u8BBA\u3002\u8DF3\u8FC7\u4EFB\u4E00\u5C42 \u2192 \u9002\u5F97\u5176\u53CD\u7684\u4FE1\u606F\u8FC7\u8F7D\u3002
|
|
403
|
+
\u6279\u6B21\u7EAA\u5F8B\uFF1A\u5B58\u5728\u6027\u63A2\u6D4B\u548C\u5185\u5BB9\u8BFB\u53D6\u4E0D\u5F97\u6DF7\u5728\u540C\u4E00\u6279\u3002\u5E76\u884C\u4E0D\u8BBE\u4E0A\u9650\uFF0C\u4F46\u6BCF\u6279\u53D1\u5B8C\u5FC5\u987B\u6536\u655B\u540E\u518D\u53D1\u4E0B\u4E00\u6279\u3002
|
|
404
|
+
\u5DE5\u4F5C\u533A\u5916\u8DEF\u5F84\uFF1A\u9ED8\u8BA4\u53EA\u80FD\u8BFB\u5199\u5DE5\u4F5C\u533A\u5185\u3002\u7528\u6237\u6388\u6743\u4E86\u5DE5\u4F5C\u533A\u5916\u64CD\u4F5C\uFF08\u5982\u5199 ~/Desktop\u3001\u8BFB /tmp\u3001\u52A8\u7236\u76EE\u5F55\uFF09\u65F6\u2014\u2014bash/\u6279\u91CF/\u6574\u76EE\u5F55\u6388\u6743\u7528 request_path_access(path, mode) \u7533\u8BF7\uFF1B\u5355\u6587\u4EF6 read_file/write_file \u76F4\u63A5\u8C03\u7528\u5373\u53EF\u89E6\u53D1\u540C\u6837\u7684\u5185\u8054\u6388\u6743\u786E\u8BA4\u3002\u7ECF\u7528\u6237\u6279\u51C6\u540E\u8BE5\u76EE\u5F55\u5B50\u6811\u672C\u4F1A\u8BDD\u53EF\u8BFB\u5199\uFF0C\u4E0D\u8981\u8BA9\u7528\u6237\u81EA\u5DF1\u624B\u52A8\u64CD\u4F5C\u3002
|
|
405
|
+
\u9632\u5FAA\u73AF\uFF1A\u8FDE\u7EED\u91CD\u590D\u65E0\u65B0\u4FE1\u606F\u65F6\u5207\u6362\u7B56\u7565\u2014\u2014\u5177\u4F53\u9608\u503C\u7531\u8FD0\u884C\u65F6 hook \u6309\u5DE5\u5177\u6307\u7EB9\u548C\u6A21\u578B\u7279\u6027\u52A8\u6001\u8C03\u6574\u3002
|
|
406
|
+
\u59D4\u6D3E\u539F\u5219\uFF1A\u4E0D\u662F\u9ED8\u8BA4\u63A8\u8FDB\u65B9\u5F0F\uFF1B\u6838\u5FC3\u6539\u52A8\u8DEF\u5F84\u4E0D\u5916\u5305\u3002\u4F55\u65F6\u7528\u3001\u600E\u4E48\u7528\u3001\u7528\u5B8C\u540E\u5982\u4F55\u6838\u9A8C\uFF0C\u89C1 <delegation>\u3002
|
|
407
|
+
</tool-usage>
|
|
408
|
+
|
|
409
|
+
<downloads>
|
|
410
|
+
\u5F53\u9700\u8981\u4ECE GitHub\u3001npm\u3001PyPI\u3001Go proxy\u3001Rust crates \u7B49\u6E90\u4E0B\u8F7D\u4F9D\u8D56\u6216\u4ED3\u5E93\uFF0C\u4E14\u9047\u5230\u901F\u5EA6\u6162\u3001\u8D85\u65F6\u3001\u88AB\u5899\u7B49\u60C5\u51B5\u65F6\uFF0C\u53EF\u5EFA\u8BAE\u7528\u6237\u8FD0\u884C /mirror china \u5207\u6362\u5230\u5929\u67A2\u5185\u7F6E\u7684\u56FD\u5185\u955C\u50CF\uFF08GitCode/kkgithub\u3001\u6DD8\u5B9D npm\u3001\u6E05\u534E PyPI\u3001goproxy.cn\u3001\u6E05\u534E Rust\uFF09\u3002\u5F00\u542F\u540E\u6240\u6709 bash \u547D\u4EE4\u4F1A\u81EA\u52A8\u6CE8\u5165\u955C\u50CF\u73AF\u5883\u53D8\u91CF\uFF0CGitHub \u4ED3\u5E93 URL \u4E5F\u4F1A\u81EA\u52A8\u6539\u5199\u3002\u6062\u590D\u9ED8\u8BA4\u7528 /mirror default\u3002
|
|
411
|
+
</downloads>
|
|
412
|
+
|
|
413
|
+
<workflow>
|
|
414
|
+
\u516D\u9636\u6BB5\u63A8\u8FDB\uFF0C\u6BCF\u9636\u6BB5\u6709\u660E\u786E\u51C6\u5165/\u51C6\u51FA\u3002\u4E0D\u8DF3\u8FC7\u7406\u89E3\u76F4\u63A5\u62C6\u89E3\u3002
|
|
415
|
+
<task-classification>
|
|
416
|
+
\u6536\u5230\u7528\u6237\u6D88\u606F\u540E\u7B2C\u4E00\u6B65\u662F\u5206\u7C7B\uFF0C\u4E0D\u662F\u5957\u516D\u9636\u6BB5\uFF1A
|
|
417
|
+
- **\u4FE1\u606F\u67E5\u8BE2**\uFF1A\u7528\u6237\u53EA\u662F\u67E5\u770B/\u4E86\u89E3\uFF08\u8FDB\u5EA6\u3001\u72B6\u6001\u3001\u63D0\u4EA4\u3001\u65E5\u5FD7\u3001\u6587\u4EF6\u5185\u5BB9\uFF09\uFF0C\u4E0D\u8981\u6C42\u4FEE\u6539\u4EE3\u7801\u3002\u6B64\u7C7B\u4EFB\u52A1**\u8DF3\u8FC7\u516D\u9636\u6BB5**\u2014\u2014\u76F4\u63A5\u7528\u6700\u5C11\u5DE5\u5177\uFF08git log/diff\u3001git \u5DE5\u5177\u3001grep \u5B9A\u4F4D\u76EE\u6807\u6587\u4EF6\u3001read_file \u8BFB\u6307\u5B9A\u6587\u4EF6\uFF09\u83B7\u53D6\u7B54\u6848\uFF0C\u7ED9\u51FA\u7ED3\u8BBA\u5373\u6B62\u3002\u4E0D\u8FDB\u5165 plan mode\u3001\u4E0D\u5199\u8BA1\u5212\u6587\u4EF6\u3001\u4E0D\u8BFB\u65E0\u5173\u6E90\u7801\u3001\u4E0D\u521B\u5EFA todo\u3001\u4E0D\u53CD\u95EE\u7528\u6237\u786E\u8BA4\u65B9\u6848\u3002\u4E0A\u4E0B\u6587\u91CC\u7684 <git-status>\u3001<recent-commits> \u6CE8\u5165\u5757\u5DF2\u5305\u542B\u5F53\u524D\u4ED3\u5E93\u72B6\u6001\u2014\u2014\u4F18\u5148\u4F7F\u7528\u8FD9\u4E9B\u4FE1\u606F\uFF0C\u4E0D\u8981\u91CD\u590D\u83B7\u53D6\u3002
|
|
418
|
+
- **\u4EE3\u7801\u6539\u52A8**\uFF1A\u7528\u6237\u8981\u6C42\u4FEE\u6539\u4EE3\u7801\u3001\u6DFB\u52A0\u529F\u80FD\u3001\u4FEE\u590D\u7F3A\u9677\u3001\u91CD\u6784\u2014\u2014\u6B64\u7C7B\u4EFB\u52A1\u8D70\u5B8C\u6574\u516D\u9636\u6BB5\u6D41\u7A0B\u3002
|
|
419
|
+
\u5224\u65AD\uFF1A\u7528\u6237\u6D88\u606F\u91CC\u6709"\u6539""\u4FEE""\u52A0""\u5220""\u5199""\u5B9E\u73B0""\u91CD\u6784""\u65B0\u589E""\u4F18\u5316""\u8FC1\u79FB""\u8C03\u6574""fix""implement""refactor""add"\u7B49\u52A8\u4F5C\u8BCD\uFF08\u542B\u5BF9\u5E94\u82F1\u6587\uFF09\u2192 \u4EE3\u7801\u6539\u52A8\u3002\u6CA1\u6709 \u2192 \u4FE1\u606F\u67E5\u8BE2\uFF0C\u76F4\u63A5\u56DE\u7B54\u3002
|
|
420
|
+
\u82E5\u4E0A\u4E0B\u6587\u4E2D\u5B58\u5728 <intent-retrieval-route> \u5757\uFF08\u542B task-kinds \u4FE1\u53F7\uFF09\uFF0C\u5176\u5206\u7C7B\u4F18\u5148\u4E8E\u672C\u89C4\u5219\u7684\u5B57\u9762\u5339\u914D\u2014\u2014\u8DEF\u7531\u5DF2\u5B8C\u6210\u8BED\u4E49\u5206\u6790\u548C\u52A8\u8BCD\u6D88\u6B67\uFF0C\u672C\u89C4\u5219\u4EC5\u5728\u65E0\u8DEF\u7531\u4FE1\u53F7\u65F6\u4F5C\u4E3A\u515C\u5E95\u3002
|
|
421
|
+
</task-classification>
|
|
422
|
+
\u2460 \u7406\u89E3 \u2014 \u5148\u7406\u89E3\u95EE\u9898\u7A7A\u95F4\uFF08\u610F\u56FE\xB7\u7EA6\u675F\xB7\u8FB9\u754C\uFF09\uFF0C\u518D\u627F\u8BFA\u65B9\u6848\u3002
|
|
423
|
+
\u610F\u56FE\u4FDD\u5B58\uFF1A\u6536\u5230\u590D\u6742\u4EFB\u52A1\u65F6\uFF0C\u5148\u7528\u4E00\u53E5\u8BDD\u590D\u8FF0\u7528\u6237\u7684\u6838\u5FC3\u76EE\u6807\uFF08\u4E0D\u662F\u4F60\u7684\u6267\u884C\u8BA1\u5212\uFF09\uFF0C\u65B9\u5411\u4E0D\u786E\u5B9A\u65F6\u7B49\u7528\u6237\u786E\u8BA4\u518D\u52A8\u624B\u3002
|
|
424
|
+
\u5C42\u7EA7\u5224\u65AD\uFF1A\u7528\u6237\u8BF4\u7684\u662F\u4E1A\u52A1\u76EE\u6807\uFF08"\u5E2E\u6211\u505A X"\uFF09\u3001\u94FE\u8DEF\u95EE\u9898\uFF08"A \u548C B \u63A5\u4E0D\u4E0A"\uFF09\u3001\u8FD8\u662F\u4EE3\u7801\u6539\u52A8\uFF08"\u6539\u8FD9\u4E2A\u6587\u4EF6"\uFF09\uFF1F\u4E0D\u8981\u628A\u4E00\u4E2A\u5C42\u7EA7\u7684\u95EE\u9898\u964D\u7EA7\u5904\u7406\u2014\u2014\u7528\u6237\u8BF4"\u4EBA\u5BB6\u90FD\u63D0\u4EA4\u5B8C\u4E86"\u662F\u60C5\u51B5\u9648\u8FF0\uFF0C\u4E0D\u662F\u6E05\u573A\u6307\u4EE4\u3002
|
|
425
|
+
\u8BC1\u636E\u4F18\u5148\u7EA7\uFF1Agit \u63D0\u4EA4\u5386\u53F2 > \u6E90\u4EE3\u7801 > \u6D4B\u8BD5\u7ED3\u679C > \u4F1A\u8BDD\u8BB0\u5F55\u3002\u95EE"\u8FDB\u5EA6/\u505A\u4E86\u4EC0\u4E48/\u80FD\u529B\u73B0\u72B6"\u65F6\u7B2C\u4E00\u6B65\u5FC5\u770B git log/diff\uFF08\u5DF2\u53D1\u751F\u7684\u4E8B\u5B9E\uFF09\uFF0C\u4E0D\u662F .rivet/plans/\uFF08\u8BA1\u5212\uFF09\u6216 .rivet/knowledge/\uFF08\u7ECF\u9A8C\u603B\u7ED3\uFF09\u2014\u2014\u8BA1\u5212\u4E0D\u662F\u8FDB\u5EA6\uFF0C\u77E5\u8BC6\u5E93\u4E0D\u662F\u4EE3\u7801\u3002\u5B8C\u6210\u521D\u6B65\u7406\u89E3\u540E\uFF0C\u786E\u8BA4\u7528\u6237\u7684\u771F\u6B63\u76EE\u6807\u2014\u2014\u5931\u8D25\u4FE1\u53F7\uFF08\u8D85\u65F6/\u62A5\u9519\uFF09\u4F1A\u62C9\u504F\u4F60\u7684\u65B9\u5411\u3002\u8F93\u5165\u5305\u542B\u5916\u90E8\u65B9\u6848/\u8C03\u7814\uFF08\u6765\u81EA\u5176\u4ED6\u6A21\u578B\u6216\u975E\u672C\u9879\u76EE\u6765\u6E90\uFF09\u65F6\uFF1A\u5148 grep/read_file \u72EC\u7ACB\u6838\u9A8C\u5176\u5173\u4E8E\u672C\u4EE3\u7801\u5E93\u7684\u5173\u952E\u65AD\u8A00\uFF0C\u518D\u51B3\u5B9A\u91C7\u7EB3\u3002\u4E0D\u56E0\u683C\u5F0F\u5B8C\u6574\u6216\u8BED\u6C14\u81EA\u4FE1\u800C\u8DF3\u8FC7\u6838\u9A8C\uFF08\u65B9\u6CD5\u89C1 external-source-verification \u89C4\u5219\uFF09\u3002\u4E0A\u4E0B\u6587\u5145\u88D5\u65F6\u505A\u7406\u89E3\u548C\u89C4\u5212\u662F\u4F60\u7684\u4F18\u52BF\u3002
|
|
426
|
+
|
|
427
|
+
\u2461 \u8C03\u7814 \u2014 \u8BFB\u76F8\u5173\u4EE3\u7801\u3001\u8C03\u7528\u65B9\u548C\u6D4B\u8BD5\u3002\u5F15\u7528\u4EE3\u7801\u7528 file_path:line_number \u683C\u5F0F\u3002\u6539\u524D\u5DF2\u5B58\u5728\u7684\u5931\u8D25\u4E0D\u5F52\u4F60\u3002
|
|
428
|
+
\u884C\u4E3A\u8BED\u4E49\u9A8C\u8BC1\uFF1A\u63A5\u7EBF/\u4F9D\u8D56\u67D0\u51FD\u6570\u7684\u884C\u4E3A\u524D\uFF0C\u63A5\u53E3\u7B7E\u540D\u4E0E\u6587\u6863\u63CF\u8FF0\u4E0D\u4F5C\u6570\u2014\u2014\u8981\u4E48\u8BFB\u5B83\u7684\u5185\u90E8\u5B9E\u73B0\uFF08switch \u5206\u652F/\u6B63\u5219/\u8FB9\u754C\u6761\u4EF6\uFF09\uFF0C\u8981\u4E48\u5199\u5FAE\u63A2\u9488\u5B9E\u6D4B\uFF08npx tsx -e \u4E00\u884C\u811A\u672C\uFF0C\u6216 .rivet/scratch/ \u4E0B\u7684\u4E00\u6B21\u6027\u6D4B\u8BD5\u6587\u4EF6\uFF09\u3002\u7ED3\u6784\u4FE1\u5FC3\uFF08\u63A5\u53E3\u5B58\u5728\u3001\u7B7E\u540D\u5339\u914D\uFF09\u2260 \u884C\u4E3A\u4FE1\u5FC3\uFF08\u8FD0\u884C\u65F6\u8BED\u4E49\u6B63\u786E\uFF09\uFF0C\u524D\u8005\u6765\u81EA\u63A5\u53E3\u9A8C\u8BC1\uFF0C\u540E\u8005\u53EA\u80FD\u6765\u81EA\u5B9E\u73B0\u9605\u8BFB\u6216\u5B9E\u6D4B\u300215 \u79D2\u7684\u63A2\u9488\u6BD4\u63A8\u65AD\u53EF\u9760\u3002
|
|
429
|
+
\u2462 \u62C6\u89E3 \u2014 \u7406\u89E3\u5230\u4F4D\u540E\u518D\u62C6\u6B65\u9AA4\u30023+ \u6B65\u6216\u8DE8\u6587\u4EF6\u7684\u4EFB\u52A1\u5148\u7528 todo \u5DE5\u5177\u5199\u51FA\u6709\u5E8F\u6B65\u9AA4\u6E05\u5355\u518D\u52A8\u624B\uFF08\u6070\u597D\u4E00\u4E2A in_progress\uFF0C\u5B8C\u6210\u5373\u6807 completed\uFF09\u2014\u2014\u8BCA\u65AD\u7C7B\u4EFB\u52A1\u4E5F\u4E00\u6837\uFF0C\u6536\u5230\u62A5\u9519\u5148\u5EFA\u9AA8\u67B6\u518D\u5F00\u67E5\uFF0C\u6CA1\u6709\u663E\u5F0F\u6B65\u9AA4\u951A\u70B9\u65F6\u540E\u7EED\u5224\u65AD\u53EA\u80FD\u5728\u8111\u5185\u6563\u8DD1\u3001\u6613\u6F0F\u9A8C\u8BC1\u73AF\u8282\u3002\u573A\u666F\u679A\u4E3E\uFF1Abugfix/feature \u52A8\u624B\u524D\u5148\u628A\u9700\u6C42\u4E2D\u63D0\u5230\u7684\u5168\u90E8\u89E6\u53D1\u573A\u666F\u9010\u6761\u62BD\u51FA\uFF08"\u5F53 X \u65F6""\u4E5F\u5305\u62EC Y""reverse/\u5D4C\u5957/\u591A\u91CD\u60C5\u51B5"\u8FD9\u7C7B\u63AA\u8F9E\u5404\u7B97\u4E00\u6761\uFF09\u5199\u5165 todo\u2014\u2014\u53EA\u8986\u76D6\u4E3B\u573A\u666F\u662F\u6700\u5E38\u89C1\u7684\u4EA4\u4ED8\u7F3A\u53E3\uFF0C\u6BCF\u4E2A\u573A\u666F\u90FD\u8981\u6709\u5BF9\u5E94\u9A8C\u8BC1\u624D\u7B97\u6536\u5C3E\u3002\u4E0D\u8981\u5728\u4E0A\u4E0B\u6587\u7D27\u5F20\u65F6\u5F3A\u884C\u5B9E\u65BD\u3002"\u4E0A\u4E0B\u6587\u7D27\u5F20"\u5FC5\u987B\u4EE5 mirror \u7684 ctx \u5B57\u6BB5\u5B9E\u6D4B\u4E3A\u51C6\uFF08\u5B9E\u6D4B \u226570% \u624D\u7B97\uFF09\uFF0C\u4E0D\u51ED\u4F53\u611F\u2014\u201410% \u4F7F\u7528\u7387\u65F6\u5EFA\u8BAE\u65B0\u4F1A\u8BDD\u662F\u4E60\u60EF\u6027\u7126\u8651\uFF0C\u4E0D\u662F\u5BA1\u614E\u3002
|
|
430
|
+
|
|
431
|
+
\u2463 \u5B9E\u65BD \u2014 \u6CBB\u6839\u4E0D\u6539\u6807\uFF0C\u641C\u800C\u4E0D\u731C\u3002\u5F00\u53D1\u5FAA\u73AF\uFF1A\u8BFB \u2192 \u6539 \u2192 diff \u2192 tsc + test \u2192 \u8BFB\u5931\u8D25\u518D\u6539\u3002\u4F60\u5199\u7684\u6D4B\u8BD5\u5931\u8D25\u5C31\u67E5\u6839\u56E0\u2014\u2014\u4E0D\u5F31\u5316\u6D4B\u8BD5\u8BA9\u5B83\u901A\u8FC7\u3002\u4FEE\u5BA1\u67E5\u6307\u51FA\u7684\u6D1E\u5148\u8BF4\u51FA\u8FDD\u53CD\u7684\u4E0D\u53D8\u91CF\uFF0Cgrep \u540C\u4E00\u4E0D\u53D8\u91CF\u7684\u6240\u6709\u8FDD\u53CD\u70B9\u4E00\u5E76\u4FEE\u2014\u2014\u4E0D\u70B9\u72B6\u4FEE\u590D\u3002
|
|
432
|
+
\u2464 \u9A8C\u8BC1 \u2014 \u4E0D\u8FD0\u884C\u6D4B\u8BD5\u4E0D\u4EA4\u4ED8\uFF0C\u6D4B\u884C\u4E3A\u800C\u975E plumbing\u3002\u65B0\u529F\u80FD\u4E0E\u884C\u4E3A\u4FEE\u590D\u5148\u5199\u6D4B\u8BD5\uFF08node:test + node:assert/strict\uFF09\uFF0C\u955C\u50CF\u6E90\u7801\u7ED3\u6784\uFF1Bsetup \u4E2D\u65AD\u8A00\u524D\u7F6E\u6761\u4EF6\u2014\u2014\u9759\u9ED8\u7A7A\u64CD\u4F5C\u4F1A\u8BEF\u5BFC\u3002\u8DF3\u8FC7\u6D4B\u8BD5\u5FC5\u987B\u663E\u5F0F\u7ED9\u51FA\u4E00\u53E5\u8BDD\u7406\u7531\uFF08\u5982"\u7EAF\u5B88\u536B\u6539\u52A8\uFF0Ctypecheck \u5DF2\u8986\u76D6\u7B7E\u540D"\uFF09\uFF0C\u9759\u9ED8\u8DF3\u8FC7=\u8FDD\u89C4\uFF1B\u4E14"\u6D4B\u8BD5\u592A\u8D35"\u8FD9\u7C7B\u6210\u672C\u4F30\u8BA1\u672C\u8EAB\u662F\u65AD\u8A00\u2014\u2014\u5148\u82B1 15 \u79D2\u6253\u63A2\u9488\u5B9E\u6D4B\uFF08mock \u4E00\u4E2A fetch\u3001\u8DD1\u4E00\u4E2A\u7A7A\u6D4B\u8BD5\uFF09\u518D\u4E0B\u7ED3\u8BBA\uFF0C\u4E0D\u51ED\u76F4\u89C9\u5426\u51B3\u8F7B\u91CF\u6D41\u7A0B\u3002
|
|
433
|
+
\u524D\u7AEF/UI \u6539\u52A8\u7684\u9A8C\u8BC1\u95ED\u73AF\uFF1A\u6D4B\u8BD5\u901A\u8FC7 \u2260 \u6E32\u67D3\u6B63\u786E\u3002\u6539\u4E86 .tsx/.vue/.css/.html \u7B49 UI \u6587\u4EF6\uFF0C\u4EA4\u4ED8\u524D\u7528 browser_debug\uFF08\u82E5\u5728\u5DE5\u5177\u5217\u8868\uFF09\u6253\u5F00 dev server \u622A\u56FE\u770B\u5B9E\u9645\u6E32\u67D3 + console \u65E0\u65B0\u62A5\u9519\uFF1B\u6D89\u53CA\u4EA4\u4E92\u7684\u7528 click/type \u8D70\u4E00\u904D\u5173\u952E\u8DEF\u5F84\u3002\u6539\u524D\u5148\u622A\u4E00\u5F20\u505A\u57FA\u7EBF\uFF08screenshot { compare: true }\uFF09\uFF0C\u6539\u540E\u7528\u540C\u4E00\u53C2\u6570\u518D\u622A\u2014\u2014compare \u505A\u672C\u5730\u50CF\u7D20\u6BD4\u5BF9\uFF0C\u7ED9\u51FA\u5DEE\u5F02\u767E\u5206\u6BD4\u548C\u53D8\u5316\u533A\u57DF\u3002\u5982\u679C\u6539\u7684\u662F\u7279\u5B9A\u7EC4\u4EF6\uFF08\u5982 .sidebar\uFF09\uFF0C\u52A0 intent: ".sidebar"\u2014\u2014compare \u4F1A\u989D\u5916\u5224\u65AD\u53D8\u5316\u662F\u5426\u8D8A\u754C\uFF0C\u88C1\u51B3\u884C\u4F5C\u4E3A\u786E\u5B9A\u6027\u8BC1\u636E\u5199\u8FDB\u4EA4\u4ED8\u62A5\u544A\u3002browser_debug \u4E0D\u53EF\u7528\u6216\u65E0\u6CD5\u8D77 dev server \u65F6\u663E\u5F0F\u8BF4\u660E"\u6E32\u67D3\u672A\u9A8C\u8BC1"\u3002
|
|
434
|
+
|
|
435
|
+
\u2465 \u6536\u5C3E \u2014 \u4EE3\u7801\u53EF\u8FD0\u884C\u540E\u624D\u505A\uFF1A\u6E05\u7406\u4E34\u65F6\u63A2\u9488\uFF08console.log/assert/debugger\uFF0C\u6B8B\u7559=\u672A\u5B8C\u6210\uFF1B.rivet/scratch/ \u662F\u4E00\u6B21\u6027\u63A2\u9488\u6587\u4EF6\u7684\u7EA6\u5B9A\u4F4D\u7F6E\uFF0C\u6536\u5C3E\u65F6\u6E05\u7A7A\u81EA\u5DF1\u521B\u5EFA\u7684\uFF09\u3001\u68C0\u67E5 import\u3001\u8DD1\u5168\u91CF\u7C7B\u578B\u68C0\u67E5\uFF08typecheck exit 0\uFF09\u3002\u6539\u4E86\u5BFC\u51FA\u63A5\u53E3/\u7B7E\u540D\u540E grep \u6D88\u8D39\u65B9\u786E\u8BA4\u65E0\u9057\u6F0F\u2014\u2014\u65B0\u589E\u80FD\u529B\u786E\u8BA4\u6709\u751F\u4EA7\u8C03\u7528\u65B9\u3001\u63A5\u53E3\u53D8\u66F4\u786E\u8BA4\u6240\u6709\u6D88\u8D39\u65B9\u7B7E\u540D\u540C\u6B65\u3002\u8FD9\u662F\u72EC\u7ACB\u9636\u6BB5\uFF0C\u4E0D\u662F\u9A8C\u8BC1\u7684\u9644\u5C5E\u2014\u2014\u9A8C\u8BC1\u901A\u8FC7\u4E0D\u4EE3\u8868\u6536\u5C3E\u5E72\u51C0\u3002
|
|
436
|
+
|
|
437
|
+
\u8BCA\u65AD\u5FAA\u73AF\uFF08bug \u6392\u67E5\u4E13\u7528\uFF0C\u4E0E\u5F00\u53D1\u5FAA\u73AF\u5E76\u5B58\uFF09\uFF1A\u8BFB\u73B0\u8C61 \u2192 \u8BFB\u7591\u4F3C\u4EE3\u7801 \u2192 \u82E5\u9047\u6096\u8BBA\uFF08\u5DF2\u9A8C\u8BC1\u4E8B\u5B9E\u4E92\u76F8\u77DB\u76FE\uFF09\u2192 \u7ACB\u5373\u5199\u6700\u5C0F\u590D\u73B0\u6D4B\u8BD5\u9A71\u52A8\u7591\u4F3C\u51FD\u6570 \u2192 RED \u9501\u5B9A\u6839\u56E0 \u2192 \u6539 \u2192 GREEN\u3002
|
|
438
|
+
\u5173\u952E\u5DEE\u5F02\uFF1A\u5F00\u53D1\u5FAA\u73AF\u91CC\u6D4B\u8BD5\u5728"\u6539"\u4E4B\u540E\uFF08\u9A8C\u8BC1\u4FEE\u590D\uFF09\uFF1B\u8BCA\u65AD\u5FAA\u73AF\u91CC\u6D4B\u8BD5\u5728"\u6539"\u4E4B\u524D\uFF08\u5B9A\u4F4D\u6839\u56E0\uFF09\u3002
|
|
439
|
+
\u590D\u73B0\u6D4B\u8BD5\u662F\u6700\u5EC9\u4EF7\u7684\u51B3\u5B9A\u6027\u8BC1\u636E\u2014\u20143 \u5206\u949F\u5199\u7684\u63A2\u9488\u6BD4 6 \u4E2A\u6587\u4EF6\u7684\u63A8\u7406\u94FE\u66F4\u6709\u8BF4\u670D\u529B\u3002
|
|
440
|
+
\u6839\u56E0\u5BF9\u9F50\uFF1A\u4FEE\u590D\u5E94\u843D\u5728\u72B6\u6001\u88AB\u9519\u8BEF\u4EA7\u751F/\u7A81\u53D8\u7684\u4F4D\u7F6E\uFF0C\u4E0D\u662F\u75C7\u72B6\u663E\u73B0\u7684\u4F4D\u7F6E\u2014\u2014\u5728\u6D88\u8D39\u5904 copy/\u9632\u5FA1/\u515C\u5E95\u662F\u63A9\u76D6\uFF0C\u4E0D\u662F\u4FEE\u590D\u3002\u53EF\u64CD\u4F5C\u5224\u636E\uFF1A\u82E5\u4FEE\u590D\u70B9\u4E0E\u62A5\u9519\u6808\u6700\u6DF1\u4E00\u5C42\u81EA\u6709\u4EE3\u7801\u5E27\u4E0D\u5728\u540C\u4E00\u6A21\u5757\uFF0C\u5148\u89E3\u91CA\u4E3A\u4EC0\u4E48\u6839\u56E0\u4E0D\u5728\u90A3\u91CC\uFF0C\u518D\u52A8\u624B\u3002
|
|
441
|
+
\u624B\u6BB5\u9636\u68AF\uFF1Agrep/read \u53D6\u8BC1 \u2192 \u8BFB\u5185\u90E8\u5B9E\u73B0 \u2192 \u5FAE\u63A2\u9488\uFF08.rivet/scratch/\uFF09\u2192 \u6700\u5C0F\u590D\u73B0\u6D4B\u8BD5\uFF08RED\uFF09\u2192 git \u57FA\u7EBF\u5BF9\u7167\uFF08\u56DE\u5F52\u7C7B\u4F18\u5148\u8DF3\u5230\u8FD9\u7EA7\uFF09\u2192 \u591A\u89C6\u89D2\u3002\u4E0A\u4E00\u7EA7\u8FDE\u7EED 2 \u6B21\u65E0\u65B0\u4FE1\u606F\u5373\u5347\u7EA7\uFF1B\u4ECE\u5339\u914D\u95EE\u9898\u91CF\u7EA7\u7684\u5C42\u7EA7\u8FDB\u5165\u3002\u8FD0\u884C\u65F6\u7684 <evidence-obligation> \u5757\u4F1A\u968F\u72B6\u6001\u70B9\u540D\u5F53\u524D\u4E49\u52A1\u7684\u4E0B\u4E00\u52A8\u4F5C\uFF0C\u4EE5\u5B83\u4E3A\u51C6\uFF1B\u8BE6\u7EC6\u65B9\u6CD5\u8BBA recall_capsule(\u8BCA\u65AD\u9636\u68AF)\u3002
|
|
442
|
+
|
|
443
|
+
\u7EA0\u6B63\u4E2D\u65AD\u89C4\u5219\uFF1A\u5F53\u7528\u6237\u7EA0\u6B63\u4F60\u7684\u7406\u89E3\u65F6\uFF0C\u505C\u6B62\u5F53\u524D\u884C\u52A8\u94FE\u2014\u2014\u5148\u7528\u4E00\u53E5\u8BDD\u786E\u8BA4\u4F60\u5BF9\u7EA0\u6B63\u7684\u7406\u89E3\uFF0C\u518D\u7EE7\u7EED\u3002\u4E0D\u8981\u7528\u65B0\u52A8\u4F5C\u8986\u76D6\u7EA0\u6B63\u4FE1\u53F7\u3002\u5F53\u4F60\u53D1\u73B0\u81EA\u5DF1\u5728\u7ED5\u8FC7\u9879\u76EE\u89C4\u5B9A\u6D41\u7A0B\u65F6\uFF08\u5982\u7ED5\u8FC7 deliver_task \u76F4\u63A5\u7528 raw git\uFF09\uFF0C\u505C\u4E0B\u6765\u2014\u2014\u8FD9\u901A\u5E38\u610F\u5473\u7740\u4F60\u5728\u8D70\u6377\u5F84\u800C\u975E\u89E3\u51B3\u95EE\u9898\u3002\u539F\u8DEF\u4E0D\u901A\u65F6\u5148\u786E\u8BA4\u7528\u6237\u610F\u56FE\uFF0C\u800C\u4E0D\u662F\u81EA\u5DF1\u627E\u66FF\u4EE3\u8DEF\u5F84\u3002
|
|
444
|
+
|
|
445
|
+
\u81EA\u68C0\u95F8\u95E8\uFF08\u6BCF\u6B21\u6267\u884C\u590D\u6742\u4EFB\u52A1\u524D\u5FEB\u901F\u8FC7\uFF09\uFF1A
|
|
446
|
+
- \u6211\u662F\u5426\u628A\u7528\u6237\u7684\u957F\u7A0B\u76EE\u6807\u538B\u7F29\u6210\u4E86\u5F53\u524D\u6700\u5BB9\u6613\u6267\u884C\u7684\u4EFB\u52A1\uFF1F
|
|
447
|
+
- \u6211\u662F\u5426\u88AB\u67D0\u4E2A\u5931\u8D25\u4FE1\u53F7\uFF08\u8D85\u65F6/\u62A5\u9519\uFF09\u951A\u5B9A\uFF0C\u5FFD\u7565\u4E86\u7528\u6237\u771F\u6B63\u8981\u7684\u4E1C\u897F\uFF1F
|
|
448
|
+
- \u7528\u6237\u5728\u4E0A\u4E00\u8F6E\u8BF4\u7684\u8BDD\uFF0C\u548C\u6211\u5F53\u524D\u8BA1\u5212\u4E4B\u95F4\u662F\u5426\u5B58\u5728\u672A\u89E3\u51B3\u7684\u77DB\u76FE\uFF1F
|
|
449
|
+
- \u6211\u5373\u5C06\u58F0\u79F0"X \u4E0D\u5B58\u5728/\u7F3A\u5931/\u635F\u574F"\u2014\u2014\u662F\u5426\u5DF2\u7528\u81F3\u5C11\u4E24\u79CD\u72EC\u7ACB\u65B9\u5F0F\uFF08grep -r \u9012\u5F52\u641C\u7D22\u3001read_file \u8BFB\u6D88\u8D39\u7AEF\u3001\u6362\u7F16\u7801/charset \u91CD\u67E5\uFF09\u4EA4\u53C9\u9A8C\u8BC1\uFF1F\u5355\u6B21\u5DE5\u5177\u8F93\u51FA\u4E0D\u8DB3\u4EE5\u652F\u6491\u8D1F\u5411\u7ED3\u8BBA\u3002
|
|
450
|
+
\u4EFB\u4E00\u7B54\u300C\u662F\u300D\u2192 \u5148\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u4F60\u8BC6\u522B\u7684\u4FE1\u53F7\u4E0E\u4FEE\u6B63\u65B9\u5411\uFF0C\u7136\u540E\u7EE7\u7EED\u63A8\u8FDB\uFF1B\u786E\u9700\u7528\u6237\u8F93\u5165\u624D\u80FD\u6D88\u89E3\u65F6\u624D\u95EE\uFF08\u81F3\u591A\u4E00\u4E2A\uFF09\uFF0C\u4E0D\u56E0\u81EA\u68C0\u7ED3\u679C\u9ED8\u8BA4\u505C\u8F66\u3002
|
|
451
|
+
</workflow>
|
|
452
|
+
|
|
453
|
+
<security>
|
|
454
|
+
\u4E0D\u66B4\u9732 API key/token/\u5BC6\u94A5\u3002\u6587\u4EF6\u8DEF\u5F84\u4E0D\u8D85\u51FA\u9879\u76EE\u76EE\u5F55\u3002
|
|
455
|
+
\u7834\u574F\u6027/\u4E0D\u53EF\u9006\u547D\u4EE4\u662F\u786C\u95F8\u95E8\uFF1A\u6267\u884C\u524D\u5FC5\u987B\u5148\u53D1\u4E00\u6761\u6D88\u606F\u8BF4\u6E05\u300C\u63A5\u4E0B\u6765\u505A\u4EC0\u4E48\xB7\u4E3A\u4EC0\u4E48\xB7\u5F71\u54CD\u4EC0\u4E48\u300D\uFF0C\u5E76\u7B49\u7528\u6237\u660E\u786E\u56DE\u8BDD\u786E\u8BA4\u540E\u624D\u80FD\u6267\u884C\u2014\u2014\u4E0D\u662F\u53D1\u5BA1\u6279\u5361\uFF0C\u662F\u8981\u7528\u6237\u4E3B\u52A8\u56DE\u590D\u300C\u786E\u8BA4/\u53EF\u4EE5/\u6267\u884C\u300D\u3002\u672A\u786E\u8BA4\u4E00\u5F8B\u7981\u6B62\u6267\u884C\u3002
|
|
456
|
+
\u8986\u76D6\uFF1Agit stash\uFF08\u542B pop/apply/drop\uFF09\u3001git reset --hard/--mixed\u3001git checkout -- / git restore\uFF08\u4E22\u5DE5\u4F5C\u533A\u6539\u52A8\uFF09\u3001git clean\u3001git push -f/--force\u3001git branch -D\u3001rm -rf\u3001\u8986\u76D6/\u5220\u9664\u5DF2\u6709\u6587\u4EF6\u3001DROP/TRUNCATE \u7B49\u6570\u636E\u5E93\u7834\u574F\u64CD\u4F5C\u3002
|
|
457
|
+
\u300C\u770B\u770B\u300D\u2260\u300C\u52A8\u624B\u300D\uFF1A\u7528\u6237\u8BA9\u4F60\u67E5\u770B/\u8BCA\u65AD\uFF08\u770B stash \u5185\u5BB9\u3001\u770B\u51B2\u7A81\u3001\u770B diff\uFF09\u65F6\uFF0C\u53EA\u62A5\u544A\u53D1\u73B0\u5E76\u7B49\u6307\u4EE4\uFF0C\u7981\u6B62\u987A\u624B stash/reset/\u8FD8\u539F\u53BB\u300C\u6E05\u5E72\u51C0\u300D\u3002
|
|
458
|
+
\u9A8C\u8BC1\u5931\u8D25\u65F6\u7981\u6B62\u7528 stash/reset/checkout \u6E05\u7A7A\u5DE5\u4F5C\u533A\u6765\u9A97\u8FC7\u9A8C\u8BC1\u2014\u2014\u5148\u5B9A\u4F4D\u6839\u56E0\uFF08\u5982\u6D4B\u8BD5\u975E\u9694\u79BB\u3001\u5E76\u53D1\u6C61\u67D3\uFF09\uFF0C\u4E0D\u53EF\u9006\u64CD\u4F5C\u524D\u540C\u6837\u8981\u5148\u786E\u8BA4\u3002
|
|
459
|
+
\u4F8B\u5916\uFF1Agoal \u547D\u4EE4\u7684\u957F\u7A0B\u81EA\u6CBB\u4EFB\u52A1\u5DF2\u83B7\u7528\u6237\u6388\u6743\uFF0C\u53EF\u6309\u65E2\u6709\u6743\u9650/\u5BA1\u6279\u4F53\u7CFB\u81EA\u52A8\u6267\u884C\uFF0C\u65E0\u9700\u9010\u6761\u56DE\u8BDD\u786E\u8BA4\u3002
|
|
460
|
+
</security>
|
|
461
|
+
|
|
462
|
+
<shared-worktree>
|
|
463
|
+
\u591A\u4F1A\u8BDD\u5171\u4EAB\u5DE5\u4F5C\u533A\u3002\u4EA4\u4ED8\u95E8\u7981\uFF08deliver_task\uFF09\u4F1A\u81EA\u52A8\u8FFD\u8E2A\u6587\u4EF6\u5F52\u5C5E\uFF0C\u53EA\u63D0\u4EA4\u4F60\u672C\u6B21\u6539\u52A8\u7684\u6587\u4EF6\u3002
|
|
464
|
+
\u5DF1\u65B9\u6587\u4EF6\u987B\u9A8C\u8BC1\u901A\u8FC7\uFF1B\u5916\u90E8\u6587\u4EF6\u7684\u5931\u8D25\u4E0D\u963B\u585E\u4F60\u7684\u4EA4\u4ED8\u3002
|
|
465
|
+
\u4EA4\u4ED8\u524D\u8C03\u7528 deliver_task \u68C0\u67E5\u95E8\u7981\uFF08GREEN/YELLOW/RED\uFF09\uFF0CGREEN \u5373\u53EF\u63D0\u4EA4\u3002
|
|
466
|
+
\u6BCF\u4E2A\u903B\u8F91\u5355\u5143\u5B8C\u6210\u540E\u7ACB\u5373 deliver_task commit=true \u63D0\u4EA4\uFF0C\u4E0D\u79EF\u7D2F\u4E0D\u76F8\u5173\u6539\u52A8\u3002\u901A\u5E38\u53EA\u4F20 commit=true + message\uFF0C\u4E0D\u4F20 files \u53C2\u6570\uFF08owned set \u7528\u76F8\u5BF9\u8DEF\u5F84\uFF0C\u4F20\u7EDD\u5BF9\u8DEF\u5F84\u4F1A\u5339\u914D\u5931\u8D25\uFF09\u3002\u82E5\u6D89\u53CA\u591A\u4E2A\u72EC\u7ACB\u6539\u52A8\uFF0C\u7528 files \u53C2\u6570\u5206\u6279\u63D0\u4EA4\u3002
|
|
467
|
+
|
|
468
|
+
deliver_task \u53EA\u505A\u6240\u6709\u6743\u9694\u79BB\u63D0\u4EA4\u2014\u2014typecheck \u548C review \u7531\u4F60\u5728\u8C03\u7528\u524D\u72EC\u7ACB\u5B8C\u6210\u3002
|
|
469
|
+
\u8C03\u7528\u524D\u81EA\u8DD1\u7684 typecheck/verify \u5931\u8D25\u65F6\uFF0C\u5148\u4FEE\u590D\u518D\u4EA4\u4ED8\uFF1B\u786E\u9700\u5E26\u9669\u4EA4\u4ED8\uFF0C\u4F20 review_level='L3' \u8BA9\u5BA1\u67E5\u5BF9\u9F50\u98CE\u9669\u3002
|
|
470
|
+
\u7981\u6B62\u7528\u88F8 git commit / git add -A \u4EA4\u4ED8\uFF1Adeliver_task \u8D85\u65F6\u6216\u5931\u8D25\u65F6\uFF0C\u5148\u68C0\u67E5
|
|
471
|
+
\u6587\u4EF6\u5F52\u5C5E\u4E0E\u51B2\u7A81\uFF0C\u518D\u7528 deliver_task \u91CD\u8BD5\uFF1B\u7ED5\u884C\u88F8 git \u4F1A\u628A\u5176\u4ED6\u4F1A\u8BDD\u7684\u6587\u4EF6\u5377\u5165\u63D0\u4EA4\u3002
|
|
472
|
+
</shared-worktree>
|
|
473
|
+
|
|
474
|
+
<git>
|
|
475
|
+
\u65B0\u5EFA\u63D0\u4EA4\uFF0C\u6C38\u4E0D amend\u3002\u683C\u5F0F\uFF1Afeat/fix/refactor/docs/test/chore/perf\u3002\u4E0D force push main/master\u3002
|
|
476
|
+
\u63D0\u4EA4\u540E\u5FC5\u987B\u5C55\u793A commit \u4FE1\u606F\uFF1A\u77ED hash + \u63D0\u4EA4\u6D88\u606F + \u6D89\u53CA\u6587\u4EF6\u3002
|
|
477
|
+
</git>
|
|
478
|
+
|
|
479
|
+
<delegation>
|
|
480
|
+
\u59D4\u6D3E\u662F\u663E\u5F0F\u5DE5\u5177\uFF0C\u4E0D\u662F\u9ED8\u8BA4\u63A8\u8FDB\u65B9\u5F0F\u3002\u6838\u5FC3\u6539\u52A8\u8DEF\u5F84\u2014\u2014\u8981\u6539\u7684\u4EE3\u7801\u3001\u5B83\u7684\u8C03\u7528\u65B9\u548C\u6D4B\u8BD5\u2014\u2014\u7531\u6211\u81EA\u5DF1\u8BFB\uFF0C\u4E0D\u5916\u5305\u3002
|
|
481
|
+
\u5355\u6B21 grep/read \u80FD\u5B8C\u6210\u7684\u4E0D\u59D4\u6D3E\uFF1B\u7981\u6B62\u7528 delegate_task \u628A\u5F53\u524D\u4E3B\u7EBF\u4EFB\u52A1\u4EA4\u7ED9\u5B50\u4EE3\u7406\uFF1B\u7528\u6237\u8BF4\u4E0D\u8981\u59D4\u6D3E\u65F6\uFF0C\u7981\u7528\u59D4\u6D3E\u5DE5\u5177\u3002
|
|
482
|
+
|
|
483
|
+
\u4F55\u65F6\u59D4\u6D3E\uFF08\u6B63\u5411\u89E6\u53D1\uFF09\u2014\u2014
|
|
484
|
+
\u4EE5\u4E0B\u573A\u666F delegate_task / delegate_batch \u7684\u6536\u76CA\u663E\u8457\u9AD8\u4E8E\u6210\u672C\uFF1A
|
|
485
|
+
- \u9700\u8981\u5E76\u884C\u63A2\u67E5 3+ \u4E2A\u72EC\u7ACB\u6A21\u5757\u6216\u6587\u4EF6\u65F6\u2014\u2014\u53D1 code_scout \u5B50\u4EE3\u7406\u5404\u67E5\u4E00\u4E2A\u65B9\u5411\uFF0C\u6BD4\u4E32\u884C\u9010\u6587\u4EF6\u8BFB\u66F4\u5FEB
|
|
486
|
+
- \u9700\u8981\u4ECE\u4E0D\u540C\u661F\u57DF\u89C6\u89D2\u4EA4\u53C9\u5BA1\u89C6\u540C\u4E00\u4E2A\u6539\u52A8\u65F6\u2014\u2014\u6307\u5B9A authority \u8BA9\u5B50\u4EE3\u7406\u5E26\u5165\u8BE5\u57DF\u65B9\u6CD5\u8BBA\uFF08\u5982 authority: "yaoguang" \u9A8C\u590D\u73B0\u8986\u76D6\u3001authority: "tianquan" \u5BA1\u67B6\u6784\u5C42\u6B21\uFF09
|
|
487
|
+
- \u524D\u7F6E\u8C03\u7814\u9700\u8981\u7406\u89E3 3+ \u4E2A\u6587\u4EF6\u7684\u6574\u4F53\u7ED3\u6784\u65F6\u2014\u2014\u8C03\u7814\u672C\u8EAB\u4E0D\u6539\u6587\u4EF6\uFF0C\u53EA\u8BFB worker \u96F6\u6B63\u786E\u6027\u98CE\u9669
|
|
488
|
+
|
|
489
|
+
\u5E76\u884C\u7F16\u6392\u9009\u578B\uFF08delegate_batch vs team\uFF09\u2014\u2014
|
|
490
|
+
\u5224\u65AD\u6807\u51C6\u662F**\u662F\u5426\u9700\u8981\u8BA1\u5212\u6587\u6863\u4F5C\u4E3A\u957F\u671F\u8D44\u4EA7**\uFF1A\u8981\u5199\u6587\u4EF6\u3001\u8981\u7559\u8BA1\u5212\u7ED9\u540E\u7EED\u4F1A\u8BDD\u3001\u8981\u6587\u4EF6\u51B2\u7A81\u68C0\u6D4B/\u6CE2\u95F4\u95E8\u7981/\u5BA1\u67E5\u95E8 \u2192 plan_task + team_orchestrate\uFF1B\u53EA\u9700\u8981\u8FD9\u4E00\u6B21\u5E76\u884C\u52A0\u901F\uFF08\u8BCA\u65AD/\u4FA6\u5BDF/\u4F53\u68C0\u7C7B\u53EA\u8BFB\u4EFB\u52A1\uFF09\u2192 delegate_batch \u76F4\u63A5\u6D3E\u3002
|
|
491
|
+
\u8BCA\u65AD\u7C7B\u59D4\u6D3E\u7684\u6C47\u603B\u4EA4\u4ED8\u5FC5\u987B\u662F\u5E26\u8BC1\u636E\u7684\u5B9E\u6D4B\u6E05\u5355\uFF08\u547D\u4EE4 exit code\u3001\u9010\u4E00\u6BD4\u5BF9\u6570\u91CF\u3001file:line\uFF09\uFF0C\u4E0D\u662F scout \u8F6C\u8FF0\u7684\u590D\u8FF0\u3002
|
|
492
|
+
|
|
493
|
+
\u661F\u57DF authority \u7528\u6CD5\u2014\u2014
|
|
494
|
+
delegate_task / delegate_batch \u53EF\u4F20 authority \u53C2\u6570\u8BA9\u5B50\u4EE3\u7406\u4EE5\u6307\u5B9A\u661F\u57DF\u8EAB\u4EFD\u63A8\u7406\uFF1A
|
|
495
|
+
- \u53EF\u7528 ID\uFF1Atianquan\uFF08\u67B6\u6784\u79F0\u91CF\uFF09\u3001yaoguang\uFF08\u590D\u73B0\u9A8C\u8BC1\uFF09\u3001tianji\uFF08\u524D\u63D0\u8D28\u7591\uFF09\u3001tianxuan\uFF08\u8DE8\u57DF\u89C6\u89D2\uFF09\u3001tianfu\uFF08\u53D8\u66F4\u5B88\u62A4\uFF09\u3001tianliang\uFF08\u6267\u884C\u843D\u5730\uFF09\u3001pojun\uFF08\u63A2\u7D22\u7A81\u7834\uFF09\u3001fu\uFF08\u8BA4\u77E5\u8C03\u6821\uFF09\u3001wenqu\uFF08\u4EE3\u7801\u7F8E\u5B66\uFF09
|
|
496
|
+
- \u53EA\u8BFB\u63A2\u67E5\u7528 profile: "code_scout"\uFF08\u4EE3\u7801\uFF09\u6216 "doc_scout"\uFF08\u6587\u6863\uFF09\uFF0Ckind: "code_search" \u6216 "doc_research"
|
|
497
|
+
|
|
498
|
+
\u59D4\u6D3E\u540E\u9A8C\u8BC1\u7EAA\u5F8B\u2014\u2014
|
|
499
|
+
- \u53EA\u8BFB worker \u8FD4\u56DE\u7684 findings \u662F"\u5F85\u6838\u9A8C\u5047\u8BBE"\uFF08evidenceStatus \u4E3A unverified\uFF09\uFF0C\u4E0D\u662F\u5DF2\u9A8C\u8BC1\u4E8B\u5B9E
|
|
500
|
+
- \u5F15\u7528 worker \u53D1\u73B0\u5230\u5177\u4F53\u6587\u4EF6\u524D\uFF0C\u5FC5\u987B\u7528 read_file / grep \u72EC\u7ACB\u6838\u9A8C\u2014\u2014external-source-verification \u89C4\u5219\u540C\u6837\u9002\u7528\u4E8E\u5B50\u4EE3\u7406\u8F93\u51FA
|
|
501
|
+
- worker \u5361\u4F4F\u6216\u8D85\u65F6\u65F6\uFF0C\u6807\u6CE8\u964D\u7EA7\u5E76\u7EE7\u7EED\u5185\u8054\u6267\u884C
|
|
502
|
+
|
|
503
|
+
\u5927\u7ED3\u679C\u56DE\u62A5\uFF1Aworker \u8FD4\u56DE\u8D85 32K \u5B57\u7B26\u65F6\uFF0C\u5B8C\u6574\u7ED3\u679C\u4F1A\u5B58\u5165 artifact store\uFF0Cpacket \u4E2D\u4EC5\u4FDD\u7559\u6458\u8981\u3002\u9700\u8981\u5B8C\u6574\u7ED3\u679C\u65F6\u4F7F\u7528 read_section \u62C9\u53D6 artifact\u3002
|
|
504
|
+
</delegation>
|
|
505
|
+
`;
|
|
506
|
+
var MODEL_CALIBRATIONS = {
|
|
507
|
+
deepseek: "<calibration>\u6539\u4EE3\u7801\u524D grep \u9A8C\u8BC1\u6D88\u8D39\u65B9\u4E0D\u88AB\u7834\u574F\u3002\u5BA1\u67E5/\u56DE\u7B54\u65F6\u5F15\u7528\u4EE3\u7801\u5B57\u6BB5\u503C\u3001\u51FD\u6570\u7B7E\u540D\u3001\u89E6\u53D1\u6761\u4EF6\u7684\u65AD\u8A00\u2014\u2014\u5148\u786E\u8BA4\u6700\u8FD1\u4E00\u8F6E\u662F\u5426 read_file \u6216 grep \u8FC7\u8BE5\u6587\u4EF6\u3002\u8DDD\u4ECA\u8D85\u8FC7 3 \u8F6E\u7684\u8BB0\u5FC6\u4E0D\u53EF\u9760\uFF0C\u7528\u4E00\u6761\u5DE5\u5177\u8C03\u7528\u786E\u8BA4\u518D\u4E0B\u7ED3\u8BBA\u3002\u8BB0\u5FC6\u662F\u6A21\u7CCA\u7684\uFF0C\u6700\u8FD1\u4E00\u6B21\u5DE5\u5177\u8F93\u51FA\u624D\u662F\u7269\u7406\u4E8B\u5B9E\u3002</calibration>",
|
|
508
|
+
mimo: "<calibration>\u4F60\u64C5\u957F\u5168\u666F\u63A2\u7D22\uFF0C\u4F46\u9700\u6536\u655B\uFF1A\u6BCF\u6B21\u63A2\u7D22\u8BBE\u5B9A\u660E\u786E\u76EE\u6807\uFF0C\u8FBE\u5230\u76EE\u6807\u540E\u505C\u6B62\u6269\u5C55\u3002\u63A2\u7D22\u7ED3\u679C\u7528\u4E00\u53E5\u8BDD\u7ED3\u8BBA\u6536\u675F\uFF0C\u518D\u51B3\u5B9A\u4E0B\u4E00\u6B65\u3002</calibration>",
|
|
509
|
+
glm: '<calibration>\u4F60\u64C5\u957F\u6392\u9664\u6CD5\u5B9A\u4F4D\u95EE\u9898\uFF0C\u4F46\u8981\u7528\u884C\u52A8\u6392\u9664\uFF0C\u4E0D\u7528\u63A8\u7406\u6392\u9664\u3002\u6000\u7591\u67D0\u4E2A\u65B9\u5411\u65F6\uFF0C\u4E0D\u8981\u5728\u63A8\u7406\u91CC\u628A\u5B83\u63A8\u5230\u5E95\u518D\u5426\u5B9A\u2014\u2014\u7ACB\u5373\u7528\u4E00\u6B21\u5DE5\u5177\uFF08grep/read_file/glob/\u5C0F\u6D4B\u8BD5/\u8FD0\u884C\u547D\u4EE4\uFF09\u5BF9\u8FD9\u4E2A\u5047\u8BBE\u6253\u63A2\u9488\u3001\u4EAE\u7EA2\u706F\uFF08\u5FEB\u901F\u8BC1\u4F2A\uFF09\uFF0C\u8BA9\u5DE5\u5177\u7ED3\u679C\u66FF\u4F60\u6392\u9664\uFF0C\u800C\u4E0D\u662F\u8111\u8865\u3002\u63A8\u7406\u91CC\u5192\u51FA"\u53E6\u4E00\u79CD\u53EF\u80FD\u2026\u8BA9\u6211\u518D\u60F3\u60F3\u53E6\u4E00\u4E2A\u65B9\u5411"\u65F6\uFF0C\u505C\u2014\u2014\u628A\u90A3\u4E2A\u65B9\u5411\u8BB0\u6210\u4E0B\u4E00\u8F6E\u7684\u63A2\u9488\uFF0C\u4E0D\u5728\u672C\u8F6E\u7EE7\u7EED\u63A8\u3002\u6BCF\u8F6E\u63A8\u7406\u53EA\u4EA7\u51FA\u4E24\u4EF6\u4E8B\uFF1A\u9009\u5B9A\u4E0B\u4E00\u4E2A\u8981\u9A8C\u8BC1\u7684\u5047\u8BBE\u3001\u7528\u54EA\u4E2A\u5DE5\u5177\uFF0C\u7136\u540E\u7ACB\u523B\u8F93\u51FA\u5DE5\u5177\u8C03\u7528\u3002\u4E0D\u8981\u5728\u63A8\u7406\u91CC\u5199\u5B8C\u6574\u4EE3\u7801\uFF1A\u8981\u5199\u4EE3\u7801\u5C31\u76F4\u63A5\u7528 edit/write \u5DE5\u5177\u843D\u5730\uFF0C\u63A8\u7406\u91CC\u6700\u591A\u4E00\u53E5"\u6539\u54EA\u91CC\u3001\u4EC0\u4E48\u601D\u8DEF"\u3002\n\n\u4E0D\u8981\u628A"\u7A77\u5C3D\u67E5\u8BC1"\u7406\u89E3\u4E3A\u65E0\u9650\u5DE5\u5177\u8C03\u7528\u3002\u540C\u4E00\u5DE5\u5177\u540C\u4E00\u9519\u8BEF\u8FDE\u7EED 2 \u6B21\u65F6\uFF0C\u505C\u6B62\u53D8\u4F53\u91CD\u8BD5\uFF0C\u6539\u7528\u4E0D\u540C\u8BC1\u636E\u8DEF\u5F84\uFF1B\u4E0D\u540C\u8DEF\u5F84\u4E5F\u88AB\u963B\u65AD\u65F6\uFF0C\u62A5\u544A\u963B\u65AD\u70B9\u548C\u5DF2\u77E5\u8BC1\u636E\u3002\u6BCF\u8F6E\u6700\u591A\u56F4\u7ED5\u4E00\u4E2A\u5047\u8BBE\u67E5 3 \u4E2A\u5173\u952E\u8BC1\u636E\uFF0C\u8BC1\u636E\u8DB3\u591F\u65F6\u6536\u675F\u7ED3\u8BBA\uFF0C\u4E0D\u8981\u4E3A\u8986\u76D6\u6240\u6709\u53EF\u80FD\u6027\u7EE7\u7EED\u6269\u5C55\u3002\n\n\u5206\u5C42\u4E0B\u8FBE\u76EE\u6807\u2014\u2014\u8BA1\u5212\u9636\u6BB5\uFF1A\u4EA7\u51FA"\u5047\u8BBE \u2192 \u63A2\u9488\uFF08\u600E\u4E48\u9A8C\uFF09\u2192 \u9884\u671F\u7EA2/\u7EFF\u706F"\u7684\u6E05\u5355\uFF0C\u4E00\u6B21\u63A8\u7406\u7ED9\u51FA\u8BA1\u5212\u9AA8\u67B6\u5373\u6536\u675F\uFF0C\u7528 todo \u843D\u5730\uFF0C\u4E0D\u5728\u8111\u5185\u9884\u6F14\u6240\u6709\u5206\u652F\u3002\u5B9E\u65BD\u9636\u6BB5\uFF1A\u6BCF\u8F6E\u53EA\u63A8\u8FDB\u4E00\u4E2A todo\uFF0C\u5148\u6253\u63A2\u9488\u786E\u8BA4\u524D\u63D0\u518D\u52A8\u624B\uFF0C\u52A8\u624B\u5373\u8C03\u5DE5\u5177\u3002\n\n\u6B65\u9AA4\u7EAA\u5F8B\uFF1A\u591A\u6B65\u4EFB\u52A1\uFF08\u22652 \u4E2A\u5DE5\u5177\u8C03\u7528\u624D\u80FD\u5B8C\u6210\uFF09\u5148\u5EFA todo \u5217\u8868\u518D\u6267\u884C\u3002\u6BCF\u8F6E\u53EA\u5904\u7406\u4E00\u4E2A todo \u6B65\u9AA4\u2014\u2014\u63A8\u7406\u805A\u7126\u5F53\u524D\u6B65\u9AA4\u7684\u6267\u884C\uFF0C\u4E0D\u91CD\u65B0\u5BA1\u89C6\u6574\u4E2A\u4EFB\u52A1\u7684\u5168\u8C8C\u3002\u5B8C\u6210\u4E00\u4E2A\u6B65\u9AA4\u540E\u6807\u8BB0\u5B8C\u6210\uFF0C\u4E0B\u4E00\u8F6E\u76F4\u63A5\u8FDB\u5165\u4E0B\u4E00\u4E2A\u6B65\u9AA4\uFF0C\u4E0D\u8981\u5728\u63A8\u7406\u4E2D\u91CD\u590D\u5DF2\u5B8C\u6210\u7684\u5206\u6790\u3002\u8FD9\u6837\u6BCF\u8F6E\u63A8\u7406\u77ED\u800C\u7CBE\u786E\uFF0C\u907F\u514D\u5355\u8F6E\u63A8\u7406\u8FC7\u957F\u5BFC\u81F4\u8D85\u65F6\u3002</calibration>'
|
|
510
|
+
};
|
|
511
|
+
function buildSystemPrompt(ctx) {
|
|
512
|
+
const base = ctx.audience === "subagent" ? buildSubagentSystemPrompt(BASE_PROMPT, ctx.tools) : BASE_PROMPT;
|
|
513
|
+
const calibration = ctx.modelFamily ? MODEL_CALIBRATIONS[ctx.modelFamily] : void 0;
|
|
514
|
+
if (calibration) return base + "\n\n" + calibration;
|
|
515
|
+
return base;
|
|
516
|
+
}
|
|
517
|
+
function detectModelFamily(modelName) {
|
|
518
|
+
const lower = modelName.toLowerCase();
|
|
519
|
+
if (lower.includes("deepseek")) return "deepseek";
|
|
520
|
+
if (lower.includes("mimo")) return "mimo";
|
|
521
|
+
if (lower.includes("glm")) return "glm";
|
|
522
|
+
if (lower.includes("gpt") || lower.includes("o1") || lower.includes("o3") || lower.includes("o4")) return "openai";
|
|
523
|
+
if (lower.includes("claude") || lower.includes("opus") || lower.includes("sonnet") || lower.includes("haiku")) return "anthropic";
|
|
524
|
+
return "unknown";
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// src/prompt/engine.ts
|
|
528
|
+
init_esm_shims();
|
|
529
|
+
|
|
530
|
+
// src/compact/semantic-prune.ts
|
|
531
|
+
init_esm_shims();
|
|
532
|
+
var JUNK_DIR_RE = /(?:__pycache__|node_modules|\.git\/|\.venv|\.mypy_cache|\.pytest_cache|\.next\/|dist\/|build\/|\.tox)/;
|
|
533
|
+
var TEST_PASS_RE = /^\s*[✓✔●◌⊙].*\(\d+\s*ms\)\s*$|^\s*(?:PASS|✓|✔)\s+.+/;
|
|
534
|
+
function pruneJunkDirs(content) {
|
|
535
|
+
const lines = content.split("\n");
|
|
536
|
+
if (lines.length < 5) return content;
|
|
537
|
+
const junkLines = lines.filter((l) => JUNK_DIR_RE.test(l));
|
|
538
|
+
if (junkLines.length < 3) return content;
|
|
539
|
+
const kept = lines.filter((l) => !JUNK_DIR_RE.test(l));
|
|
540
|
+
kept.push(`[${junkLines.length} junk directory entries removed]`);
|
|
541
|
+
return kept.join("\n");
|
|
542
|
+
}
|
|
543
|
+
function pruneTestOutput(content) {
|
|
544
|
+
if (!content.includes("pass") && !content.includes("\u2713") && !content.includes("\u2714")) return content;
|
|
545
|
+
const lines = content.split("\n");
|
|
546
|
+
if (lines.length < 10) return content;
|
|
547
|
+
const passLines = [];
|
|
548
|
+
for (let i = 0; i < lines.length; i++) {
|
|
549
|
+
if (TEST_PASS_RE.test(lines[i])) passLines.push(i);
|
|
550
|
+
}
|
|
551
|
+
if (passLines.length < 5) return content;
|
|
552
|
+
const kept = lines.filter((_, i) => !passLines.includes(i));
|
|
553
|
+
kept.push(`[${passLines.length} passing test lines removed]`);
|
|
554
|
+
return kept.join("\n");
|
|
555
|
+
}
|
|
556
|
+
var QUERY_TOOLS = /* @__PURE__ */ new Set(["grep", "glob", "read_file", "semantic_search", "search"]);
|
|
557
|
+
function queryKey(toolName, args) {
|
|
558
|
+
if (!QUERY_TOOLS.has(toolName)) return null;
|
|
559
|
+
switch (toolName) {
|
|
560
|
+
case "grep":
|
|
561
|
+
case "glob":
|
|
562
|
+
case "semantic_search":
|
|
563
|
+
case "search": {
|
|
564
|
+
const pattern = typeof args.pattern === "string" ? args.pattern : typeof args.query === "string" ? args.query : typeof args.regex === "string" ? args.regex : void 0;
|
|
565
|
+
if (!pattern) return null;
|
|
566
|
+
const path = typeof args.path === "string" ? args.path : "";
|
|
567
|
+
const glob = typeof args.glob === "string" ? args.glob : "";
|
|
568
|
+
const paths = Array.isArray(args.paths) ? args.paths.filter((p) => typeof p === "string").join(",") : "";
|
|
569
|
+
return { compound: `${toolName}:${[pattern, path, glob, paths].join("|")}`, pattern };
|
|
570
|
+
}
|
|
571
|
+
case "read_file": {
|
|
572
|
+
const filePath = typeof args.file_path === "string" ? args.file_path : "";
|
|
573
|
+
if (!filePath) return null;
|
|
574
|
+
const offset = typeof args.offset === "number" ? `:${args.offset}` : "";
|
|
575
|
+
const limit = typeof args.limit === "number" ? `:${args.limit}` : "";
|
|
576
|
+
return { compound: `${toolName}:${filePath}${offset}${limit}`, pattern: filePath };
|
|
577
|
+
}
|
|
578
|
+
default:
|
|
579
|
+
return null;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function semanticPruneLayer1(messages, anchorCount) {
|
|
583
|
+
if (messages.length <= anchorCount) return { messages, prunedCount: 0, savedChars: 0 };
|
|
584
|
+
let prunedCount = 0;
|
|
585
|
+
let savedChars = 0;
|
|
586
|
+
const toolCallIndex = /* @__PURE__ */ new Map();
|
|
587
|
+
for (let i = anchorCount; i < messages.length; i++) {
|
|
588
|
+
const msg = messages[i];
|
|
589
|
+
if (msg.role === "assistant") {
|
|
590
|
+
const aMsg = msg;
|
|
591
|
+
if (aMsg.tool_calls) {
|
|
592
|
+
for (const tc of aMsg.tool_calls) {
|
|
593
|
+
toolCallIndex.set(tc.id, { name: tc.function.name, args: tc.function.arguments });
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const latestByKey = /* @__PURE__ */ new Map();
|
|
599
|
+
for (let i = messages.length - 1; i >= anchorCount; i--) {
|
|
600
|
+
const msg = messages[i];
|
|
601
|
+
if (msg.role !== "tool") continue;
|
|
602
|
+
const info = toolCallIndex.get(msg.tool_call_id);
|
|
603
|
+
if (!info) continue;
|
|
604
|
+
let args = {};
|
|
605
|
+
try {
|
|
606
|
+
args = JSON.parse(info.args);
|
|
607
|
+
} catch {
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
const key = queryKey(info.name, args);
|
|
611
|
+
if (!key) continue;
|
|
612
|
+
if (!latestByKey.has(key.compound)) {
|
|
613
|
+
latestByKey.set(key.compound, i);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
const result = messages.map((msg, idx) => {
|
|
617
|
+
if (idx < anchorCount) return msg;
|
|
618
|
+
if (msg.role !== "tool") return msg;
|
|
619
|
+
if (msg.content.startsWith("[")) return msg;
|
|
620
|
+
const toolInfo = toolCallIndex.get(msg.tool_call_id);
|
|
621
|
+
const toolName = toolInfo?.name;
|
|
622
|
+
let newContent = msg.content;
|
|
623
|
+
const origLen = newContent.length;
|
|
624
|
+
if (toolInfo && latestByKey.size > 0) {
|
|
625
|
+
let args = {};
|
|
626
|
+
try {
|
|
627
|
+
args = JSON.parse(toolInfo.args);
|
|
628
|
+
} catch {
|
|
629
|
+
}
|
|
630
|
+
const key = queryKey(toolName, args);
|
|
631
|
+
if (key && latestByKey.get(key.compound) !== idx) {
|
|
632
|
+
newContent = `[outdated ${toolName} for "${key.pattern.slice(0, 40)}", see later result]`;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
if (origLen < 200 && newContent === msg.content) return msg;
|
|
636
|
+
if (toolName === "list_dir" || toolName === "glob" || toolName === "find_files") {
|
|
637
|
+
newContent = pruneJunkDirs(newContent);
|
|
638
|
+
}
|
|
639
|
+
if (toolName === "bash") {
|
|
640
|
+
newContent = pruneTestOutput(newContent);
|
|
641
|
+
}
|
|
642
|
+
if (newContent.length < origLen) {
|
|
643
|
+
prunedCount++;
|
|
644
|
+
savedChars += origLen - newContent.length;
|
|
645
|
+
return { ...msg, content: newContent };
|
|
646
|
+
}
|
|
647
|
+
return msg;
|
|
648
|
+
});
|
|
649
|
+
return { messages: prunedCount > 0 ? result : messages, prunedCount, savedChars };
|
|
650
|
+
}
|
|
651
|
+
function pruneOutdatedQueryResults(messages, anchorCount) {
|
|
652
|
+
return semanticPruneLayer1(messages, anchorCount);
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
// src/compact/staleness-detect.ts
|
|
656
|
+
init_esm_shims();
|
|
657
|
+
var LAG_STEPS = 3;
|
|
658
|
+
var MIN_CONTENT_CHARS = 500;
|
|
659
|
+
function extractFileInfo(args) {
|
|
660
|
+
try {
|
|
661
|
+
const parsed = JSON.parse(args);
|
|
662
|
+
const path = parsed.file_path || parsed.path || parsed.file;
|
|
663
|
+
if (!path) return void 0;
|
|
664
|
+
if (parsed.file_path !== void 0 && (parsed.offset !== void 0 || parsed.limit !== void 0)) {
|
|
665
|
+
return { path, offset: parseOptionalInt(parsed.offset), limit: parseOptionalInt(parsed.limit) };
|
|
666
|
+
}
|
|
667
|
+
return { path };
|
|
668
|
+
} catch {
|
|
669
|
+
return void 0;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function isReferenced(toolContent, assistantTexts) {
|
|
673
|
+
const lines = toolContent.split("\n").filter((l) => l.trim().length > 20);
|
|
674
|
+
if (lines.length === 0) return true;
|
|
675
|
+
const samples = lines.slice(0, 3);
|
|
676
|
+
const joined = assistantTexts.join(" ");
|
|
677
|
+
for (const sample of samples) {
|
|
678
|
+
const snippet = sample.trim().slice(0, 50);
|
|
679
|
+
if (joined.includes(snippet)) return true;
|
|
680
|
+
}
|
|
681
|
+
return false;
|
|
682
|
+
}
|
|
683
|
+
function roughTokens(msg) {
|
|
684
|
+
const text = typeof msg.content === "string" ? msg.content : "";
|
|
685
|
+
const calls = msg.tool_calls;
|
|
686
|
+
const callsJson = calls ? JSON.stringify(calls) : "";
|
|
687
|
+
return Math.ceil((text.length + callsJson.length) / 4);
|
|
688
|
+
}
|
|
689
|
+
function computeSuffixTokens(messages) {
|
|
690
|
+
const suffix = new Array(messages.length);
|
|
691
|
+
let accumulated = 0;
|
|
692
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
693
|
+
suffix[i] = accumulated;
|
|
694
|
+
accumulated += roughTokens(messages[i]);
|
|
695
|
+
}
|
|
696
|
+
return suffix;
|
|
697
|
+
}
|
|
698
|
+
function detectStaleness(messages, anchorCount = CACHE_ANCHOR_MESSAGES, options) {
|
|
699
|
+
const fileReads = /* @__PURE__ */ new Map();
|
|
700
|
+
const toolCallInfo = /* @__PURE__ */ new Map();
|
|
701
|
+
for (let i = messages.length - 1; i >= anchorCount; i--) {
|
|
702
|
+
const msg = messages[i];
|
|
703
|
+
if (msg.role === "assistant" && msg.tool_calls) {
|
|
704
|
+
for (const tc of msg.tool_calls) {
|
|
705
|
+
const info = {
|
|
706
|
+
name: tc.function.name,
|
|
707
|
+
args: tc.function.arguments
|
|
708
|
+
};
|
|
709
|
+
if (tc.function.name === "read_file" || tc.function.name === "grep") {
|
|
710
|
+
info.fileInfo = extractFileInfo(tc.function.arguments);
|
|
711
|
+
if (info.fileInfo) {
|
|
712
|
+
const reads = fileReads.get(info.fileInfo.path) ?? [];
|
|
713
|
+
reads.push({ index: i + 1, offset: info.fileInfo.offset, limit: info.fileInfo.limit });
|
|
714
|
+
fileReads.set(info.fileInfo.path, reads);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
toolCallInfo.set(tc.id, info);
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
const assistantIndices = [];
|
|
722
|
+
for (let i = messages.length - 1; i >= anchorCount; i--) {
|
|
723
|
+
if (messages[i].role === "assistant") assistantIndices.push(i);
|
|
724
|
+
}
|
|
725
|
+
const suffixLimit = options?.suffixTokenLimit;
|
|
726
|
+
const suffixTokens = suffixLimit !== void 0 ? computeSuffixTokens(messages) : void 0;
|
|
727
|
+
let supersededCount = 0;
|
|
728
|
+
let unreferencedCount = 0;
|
|
729
|
+
let freedChars = 0;
|
|
730
|
+
const result = messages.map((msg, idx) => {
|
|
731
|
+
if (idx < anchorCount) return msg;
|
|
732
|
+
if (msg.role !== "tool") return msg;
|
|
733
|
+
if (msg.content.length < MIN_CONTENT_CHARS) return msg;
|
|
734
|
+
if (msg.content.startsWith("[")) return msg;
|
|
735
|
+
if (suffixLimit !== void 0 && suffixTokens !== void 0 && suffixTokens[idx] > suffixLimit) return msg;
|
|
736
|
+
const assistantTurnsAfter = assistantIndices.filter((ai) => ai > idx).length;
|
|
737
|
+
if (assistantTurnsAfter < LAG_STEPS) return msg;
|
|
738
|
+
const info = toolCallInfo.get(msg.tool_call_id);
|
|
739
|
+
if (!info) return msg;
|
|
740
|
+
if (info.name === "read_file" || info.name === "grep") {
|
|
741
|
+
const fileInfo = info.fileInfo;
|
|
742
|
+
if (fileInfo) {
|
|
743
|
+
const reads = fileReads.get(fileInfo.path) ?? [];
|
|
744
|
+
const laterReads = reads.filter((r) => r.index > idx);
|
|
745
|
+
const isSuperseded = info.name === "read_file" ? laterReads.some((r) => rangeContains(r, fileInfo)) : laterReads.length > 0;
|
|
746
|
+
if (isSuperseded) {
|
|
747
|
+
supersededCount++;
|
|
748
|
+
freedChars += msg.content.length;
|
|
749
|
+
return {
|
|
750
|
+
...msg,
|
|
751
|
+
content: `[superseded: ${info.name} ${fileInfo.path.split("/").pop()} \u2014 re-read at later step]`
|
|
752
|
+
};
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
const subsequentAssistantText = [];
|
|
757
|
+
for (let j = idx + 1; j < messages.length; j++) {
|
|
758
|
+
if (messages[j].role === "assistant") {
|
|
759
|
+
const aMsg = messages[j];
|
|
760
|
+
if (aMsg.content) subsequentAssistantText.push(aMsg.content);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
if (subsequentAssistantText.length >= LAG_STEPS && !isReferenced(msg.content, subsequentAssistantText)) {
|
|
764
|
+
unreferencedCount++;
|
|
765
|
+
freedChars += msg.content.length;
|
|
766
|
+
return {
|
|
767
|
+
...msg,
|
|
768
|
+
content: `[unreferenced: ${info.name} result (${msg.content.length} chars) \u2014 not cited in subsequent reasoning]`
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
return msg;
|
|
772
|
+
});
|
|
773
|
+
return {
|
|
774
|
+
messages: supersededCount + unreferencedCount > 0 ? result : messages,
|
|
775
|
+
supersededCount,
|
|
776
|
+
unreferencedCount,
|
|
777
|
+
freedChars
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// src/prompt/appendix-anatomy.ts
|
|
782
|
+
init_esm_shims();
|
|
783
|
+
function summarizeAppendixParts(parts) {
|
|
784
|
+
let cvmChars = 0;
|
|
785
|
+
let keepChars = 0;
|
|
786
|
+
const cvmBySource = {};
|
|
787
|
+
for (const p of parts) {
|
|
788
|
+
if (p.source) {
|
|
789
|
+
cvmChars += p.content.length;
|
|
790
|
+
cvmBySource[p.source] = (cvmBySource[p.source] ?? 0) + p.content.length;
|
|
791
|
+
} else {
|
|
792
|
+
keepChars += p.content.length;
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
return { blocks: parts.length, partsChars: cvmChars + keepChars, cvmChars, keepChars, cvmBySource };
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
// src/context/payload-diagnostic.ts
|
|
799
|
+
init_esm_shims();
|
|
800
|
+
var SECTION_RE = /<([a-zA-Z][\w-]*)(?:\s[^>]*)?(?:\/>|>[\s\S]*?<\/\1>)/g;
|
|
801
|
+
var LARGE_VOLATILE_PAYLOAD_CHARS = 12e3;
|
|
802
|
+
function estimateContextTokens(text) {
|
|
803
|
+
if (text.length === 0) return 0;
|
|
804
|
+
return Math.ceil(text.length / 4);
|
|
805
|
+
}
|
|
806
|
+
function lineCount(text) {
|
|
807
|
+
if (text.length === 0) return 0;
|
|
808
|
+
return text.split("\n").length;
|
|
809
|
+
}
|
|
810
|
+
function countActiveClaims(section) {
|
|
811
|
+
const match = section.match(/<active-claims\b[^>]*\bcount="(\d+)"/);
|
|
812
|
+
if (!match) return null;
|
|
813
|
+
return Number.parseInt(match[1], 10);
|
|
814
|
+
}
|
|
815
|
+
function wasteCandidatesForSection(stat, sectionText) {
|
|
816
|
+
const candidates = [];
|
|
817
|
+
if (!stat.present) return candidates;
|
|
818
|
+
if (stat.id === "project-instructions" && stat.chars > 6e3) {
|
|
819
|
+
candidates.push({
|
|
820
|
+
id: stat.id,
|
|
821
|
+
chars: stat.chars,
|
|
822
|
+
reason: "large stable volatile section",
|
|
823
|
+
recommendation: "split project instructions into always-on core plus task-routed details"
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
if (stat.id === "active-claims") {
|
|
827
|
+
const count = countActiveClaims(sectionText) ?? 0;
|
|
828
|
+
if (stat.chars > 2500 || count > 8) {
|
|
829
|
+
candidates.push({
|
|
830
|
+
id: stat.id,
|
|
831
|
+
chars: stat.chars,
|
|
832
|
+
reason: count > 8 ? `${count} claims injected` : "large active claims section",
|
|
833
|
+
recommendation: "apply active-claim relevance gate and keep only current-task claims"
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
if (stat.id === "git-status" && stat.chars > 1200) {
|
|
838
|
+
candidates.push({
|
|
839
|
+
id: stat.id,
|
|
840
|
+
chars: stat.chars,
|
|
841
|
+
reason: "large git status section",
|
|
842
|
+
recommendation: "summarize dirty file counts and show only task-relevant files by default"
|
|
843
|
+
});
|
|
844
|
+
}
|
|
845
|
+
if (stat.id === "historical-lessons" && stat.chars > 800) {
|
|
846
|
+
candidates.push({
|
|
847
|
+
id: stat.id,
|
|
848
|
+
chars: stat.chars,
|
|
849
|
+
reason: "large historical lessons section",
|
|
850
|
+
recommendation: "rank playbook lessons by current task relevance before injection"
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
return candidates;
|
|
854
|
+
}
|
|
855
|
+
function stripOuterContext(block) {
|
|
856
|
+
const trimmed = block.trim();
|
|
857
|
+
const match = trimmed.match(/^<context>\s*([\s\S]*?)\s*<\/context>$/);
|
|
858
|
+
return match ? match[1] : block;
|
|
859
|
+
}
|
|
860
|
+
function analyzeVolatilePayload(block) {
|
|
861
|
+
const body = stripOuterContext(block);
|
|
862
|
+
const sections = /* @__PURE__ */ new Map();
|
|
863
|
+
for (const match of body.matchAll(SECTION_RE)) {
|
|
864
|
+
const id = match[1];
|
|
865
|
+
if (id === "context") continue;
|
|
866
|
+
const text = match[0];
|
|
867
|
+
const existing = sections.get(id);
|
|
868
|
+
if (existing) {
|
|
869
|
+
sections.set(id, { id, text: `${existing.text}
|
|
870
|
+
${text}` });
|
|
871
|
+
} else {
|
|
872
|
+
sections.set(id, { id, text });
|
|
873
|
+
}
|
|
874
|
+
}
|
|
875
|
+
const stats = [...sections.values()].map((section) => ({
|
|
876
|
+
id: section.id,
|
|
877
|
+
chars: section.text.length,
|
|
878
|
+
estimatedTokens: estimateContextTokens(section.text),
|
|
879
|
+
lines: lineCount(section.text),
|
|
880
|
+
present: true
|
|
881
|
+
})).sort((a, b) => b.chars - a.chars || a.id.localeCompare(b.id));
|
|
882
|
+
const wasteCandidates = stats.flatMap((stat) => wasteCandidatesForSection(stat, sections.get(stat.id)?.text ?? ""));
|
|
883
|
+
if (block.length > LARGE_VOLATILE_PAYLOAD_CHARS) {
|
|
884
|
+
wasteCandidates.push({
|
|
885
|
+
id: "total",
|
|
886
|
+
chars: block.length,
|
|
887
|
+
reason: "large volatile payload",
|
|
888
|
+
recommendation: "enable context hygiene gates for claims, git status, lessons, and project instructions"
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
return {
|
|
892
|
+
totalChars: block.length,
|
|
893
|
+
estimatedTokens: estimateContextTokens(block),
|
|
894
|
+
sections: stats,
|
|
895
|
+
wasteCandidates
|
|
896
|
+
};
|
|
897
|
+
}
|
|
898
|
+
function formatCount(value) {
|
|
899
|
+
if (value >= 1e3) return `${(value / 1e3).toFixed(1)}k`;
|
|
900
|
+
return String(value);
|
|
901
|
+
}
|
|
902
|
+
function formatVolatilePayloadReport(report) {
|
|
903
|
+
const lines = [
|
|
904
|
+
"Context Payload",
|
|
905
|
+
`Total: ${formatCount(report.totalChars)} chars (~${formatCount(report.estimatedTokens)} tokens)`
|
|
906
|
+
];
|
|
907
|
+
if (report.sections.length === 0) {
|
|
908
|
+
lines.push("Sections: none");
|
|
909
|
+
} else {
|
|
910
|
+
lines.push("Sections:");
|
|
911
|
+
for (const section of report.sections) {
|
|
912
|
+
lines.push(` ${section.id.padEnd(22)} ${formatCount(section.chars).padStart(7)} chars ~${formatCount(section.estimatedTokens).padStart(6)} tok ${section.lines} lines`);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
if (report.wasteCandidates.length > 0) {
|
|
916
|
+
lines.push("Waste candidates:");
|
|
917
|
+
for (const candidate of report.wasteCandidates) {
|
|
918
|
+
lines.push(` ${candidate.id}: ${candidate.reason}; ${candidate.recommendation}`);
|
|
919
|
+
}
|
|
920
|
+
} else {
|
|
921
|
+
lines.push("Waste candidates: none");
|
|
922
|
+
}
|
|
923
|
+
return lines.join("\n");
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/prompt/fingerprint.ts
|
|
927
|
+
init_esm_shims();
|
|
928
|
+
import { createHash } from "crypto";
|
|
929
|
+
function sha256(input) {
|
|
930
|
+
return createHash("sha256").update(input).digest("hex");
|
|
931
|
+
}
|
|
932
|
+
function computeFingerprint(systemText, tools, stableVolatileBlock = "") {
|
|
933
|
+
const systemSha256 = sha256(systemText);
|
|
934
|
+
const toolsSha256 = tools && tools.length > 0 ? sha256(stableStringify([...tools].sort((a, b) => a.name.localeCompare(b.name)))) : sha256("");
|
|
935
|
+
const stableVolatileSha256 = sha256(stableVolatileBlock);
|
|
936
|
+
const combinedSha256 = sha256(`${systemSha256}:${toolsSha256}:${stableVolatileSha256}`);
|
|
937
|
+
return { systemSha256, toolsSha256, stableVolatileSha256, combinedSha256 };
|
|
938
|
+
}
|
|
939
|
+
function detectDrift(baseline, current) {
|
|
940
|
+
if (baseline.combinedSha256 === current.combinedSha256) return null;
|
|
941
|
+
const systemChanged = baseline.systemSha256 !== current.systemSha256;
|
|
942
|
+
const toolsChanged = baseline.toolsSha256 !== current.toolsSha256;
|
|
943
|
+
const stableVolatileChanged = baseline.stableVolatileSha256 !== current.stableVolatileSha256;
|
|
944
|
+
const parts = [];
|
|
945
|
+
if (systemChanged) parts.push("system prompt");
|
|
946
|
+
if (toolsChanged) parts.push("tool definitions");
|
|
947
|
+
if (stableVolatileChanged) parts.push("stable volatile context");
|
|
948
|
+
const message = `Prefix cache drift detected: ${parts.join(" and ")} changed`;
|
|
949
|
+
return { systemChanged, toolsChanged, stableVolatileChanged, message };
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
// src/prompt/field-habituation.ts
|
|
953
|
+
init_esm_shims();
|
|
954
|
+
import { createHash as createHash2 } from "crypto";
|
|
955
|
+
var ALPHA_TABLE = {
|
|
956
|
+
explore: 0.1,
|
|
957
|
+
// ~15 turns to habituate
|
|
958
|
+
plan: 0.2,
|
|
959
|
+
// ~7 turns
|
|
960
|
+
execute: 0.35,
|
|
961
|
+
// ~4 turns
|
|
962
|
+
verify: 0.3,
|
|
963
|
+
// ~5 turns
|
|
964
|
+
deliver: 0.4
|
|
965
|
+
// ~3 turns
|
|
966
|
+
};
|
|
967
|
+
var DEFAULT_ALPHA = 0.2;
|
|
968
|
+
function sha256short(input) {
|
|
969
|
+
return createHash2("sha256").update(input).digest("hex").slice(0, 16);
|
|
970
|
+
}
|
|
971
|
+
function getAlpha(phaseHint) {
|
|
972
|
+
if (phaseHint) {
|
|
973
|
+
const rate = ALPHA_TABLE[phaseHint];
|
|
974
|
+
if (rate !== void 0) return rate;
|
|
975
|
+
}
|
|
976
|
+
return DEFAULT_ALPHA;
|
|
977
|
+
}
|
|
978
|
+
var FieldHabituationTracker = class {
|
|
979
|
+
fields = /* @__PURE__ */ new Map();
|
|
980
|
+
promotionThreshold;
|
|
981
|
+
decayRate;
|
|
982
|
+
legacyThreshold;
|
|
983
|
+
constructor(config) {
|
|
984
|
+
if (config.threshold !== void 0 && config.promotionThreshold === void 0) {
|
|
985
|
+
this.legacyThreshold = config.threshold;
|
|
986
|
+
this.promotionThreshold = 0.8;
|
|
987
|
+
this.decayRate = 0.3;
|
|
988
|
+
} else {
|
|
989
|
+
this.legacyThreshold = null;
|
|
990
|
+
this.promotionThreshold = config.promotionThreshold ?? 0.8;
|
|
991
|
+
this.decayRate = config.decayRate ?? 0.3;
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
recordTurn(fieldValues, phaseHint) {
|
|
995
|
+
const alpha = getAlpha(phaseHint);
|
|
996
|
+
const useLegacy = this.legacyThreshold !== null;
|
|
997
|
+
const seen = /* @__PURE__ */ new Set();
|
|
998
|
+
for (const [name, content] of Object.entries(fieldValues)) {
|
|
999
|
+
seen.add(name);
|
|
1000
|
+
const hash = sha256short(content);
|
|
1001
|
+
const existing = this.fields.get(name);
|
|
1002
|
+
if (!existing) {
|
|
1003
|
+
const state = {
|
|
1004
|
+
hash,
|
|
1005
|
+
content,
|
|
1006
|
+
stableCount: useLegacy ? 1 : 0,
|
|
1007
|
+
confidence: useLegacy ? 0 : alpha,
|
|
1008
|
+
habituated: false
|
|
1009
|
+
};
|
|
1010
|
+
if (useLegacy && this.legacyThreshold <= 1) {
|
|
1011
|
+
state.habituated = true;
|
|
1012
|
+
}
|
|
1013
|
+
this.fields.set(name, state);
|
|
1014
|
+
continue;
|
|
1015
|
+
}
|
|
1016
|
+
if (existing.hash === hash) {
|
|
1017
|
+
if (useLegacy) {
|
|
1018
|
+
existing.stableCount++;
|
|
1019
|
+
if (existing.stableCount >= this.legacyThreshold && !existing.habituated) {
|
|
1020
|
+
existing.habituated = true;
|
|
1021
|
+
}
|
|
1022
|
+
} else {
|
|
1023
|
+
existing.confidence += (1 - existing.confidence) * alpha;
|
|
1024
|
+
if (existing.confidence >= this.promotionThreshold && !existing.habituated) {
|
|
1025
|
+
existing.habituated = true;
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
} else {
|
|
1029
|
+
existing.hash = hash;
|
|
1030
|
+
existing.content = content;
|
|
1031
|
+
if (useLegacy) {
|
|
1032
|
+
existing.stableCount = 1;
|
|
1033
|
+
} else {
|
|
1034
|
+
existing.confidence = alpha;
|
|
1035
|
+
}
|
|
1036
|
+
existing.habituated = false;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
for (const [name, state] of this.fields) {
|
|
1040
|
+
if (!seen.has(name)) {
|
|
1041
|
+
if (useLegacy) {
|
|
1042
|
+
state.hash = sha256short("");
|
|
1043
|
+
state.content = "";
|
|
1044
|
+
state.stableCount = 0;
|
|
1045
|
+
state.habituated = false;
|
|
1046
|
+
} else {
|
|
1047
|
+
state.confidence *= 1 - this.decayRate;
|
|
1048
|
+
if (state.habituated && state.confidence < this.promotionThreshold) {
|
|
1049
|
+
state.habituated = false;
|
|
1050
|
+
}
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
getHabituated() {
|
|
1056
|
+
const result = /* @__PURE__ */ new Set();
|
|
1057
|
+
for (const [name, state] of this.fields) {
|
|
1058
|
+
if (state.habituated) result.add(name);
|
|
1059
|
+
}
|
|
1060
|
+
return result;
|
|
1061
|
+
}
|
|
1062
|
+
getActive() {
|
|
1063
|
+
const result = /* @__PURE__ */ new Set();
|
|
1064
|
+
for (const [name, state] of this.fields) {
|
|
1065
|
+
if (!state.habituated) result.add(name);
|
|
1066
|
+
}
|
|
1067
|
+
return result;
|
|
1068
|
+
}
|
|
1069
|
+
getHabituatedContent() {
|
|
1070
|
+
const result = /* @__PURE__ */ new Map();
|
|
1071
|
+
for (const [name, state] of this.fields) {
|
|
1072
|
+
if (state.habituated) result.set(name, state.content);
|
|
1073
|
+
}
|
|
1074
|
+
return result;
|
|
1075
|
+
}
|
|
1076
|
+
/**
|
|
1077
|
+
* Immediately promote a field to habituated state, bypassing the
|
|
1078
|
+
* confidence accumulation period. Use for fields known to be session
|
|
1079
|
+
* constants on exact-prefix-cache models.
|
|
1080
|
+
* If the field hasn't been recorded yet, creates it with the given content.
|
|
1081
|
+
* Returns true if the field was promoted (false if already habituated).
|
|
1082
|
+
*
|
|
1083
|
+
* NOTE: currently has no callers — star-domain (its original use) is now folded
|
|
1084
|
+
* directly into the frozen prefix (volatile.ts / engine.setActiveDomain) rather
|
|
1085
|
+
* than promoted through habituation. Kept as a general-purpose fast-path for any
|
|
1086
|
+
* future session-constant field.
|
|
1087
|
+
*/
|
|
1088
|
+
immediatePromote(name, content) {
|
|
1089
|
+
const hash = sha256short(content);
|
|
1090
|
+
const existing = this.fields.get(name);
|
|
1091
|
+
if (existing) {
|
|
1092
|
+
if (existing.habituated && existing.hash === hash) return false;
|
|
1093
|
+
existing.hash = hash;
|
|
1094
|
+
existing.content = content;
|
|
1095
|
+
existing.confidence = 1;
|
|
1096
|
+
existing.stableCount = this.legacyThreshold ?? 999;
|
|
1097
|
+
existing.habituated = true;
|
|
1098
|
+
return true;
|
|
1099
|
+
}
|
|
1100
|
+
this.fields.set(name, {
|
|
1101
|
+
hash,
|
|
1102
|
+
content,
|
|
1103
|
+
stableCount: this.legacyThreshold ?? 999,
|
|
1104
|
+
confidence: 1,
|
|
1105
|
+
habituated: true
|
|
1106
|
+
});
|
|
1107
|
+
return true;
|
|
1108
|
+
}
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
// src/prompt/context-layer.ts
|
|
1112
|
+
init_esm_shims();
|
|
1113
|
+
import { createHash as createHash3 } from "crypto";
|
|
1114
|
+
var LAYER_ORDER = [
|
|
1115
|
+
"system",
|
|
1116
|
+
"tools",
|
|
1117
|
+
"project-instructions",
|
|
1118
|
+
"git-status",
|
|
1119
|
+
"session-memory",
|
|
1120
|
+
"historical-lessons",
|
|
1121
|
+
"working-set",
|
|
1122
|
+
"recent-raw-turns",
|
|
1123
|
+
"tool-history",
|
|
1124
|
+
"task-progress",
|
|
1125
|
+
"behavior-mirror",
|
|
1126
|
+
"decisions",
|
|
1127
|
+
"current-request"
|
|
1128
|
+
];
|
|
1129
|
+
function stableStringify2(value) {
|
|
1130
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
1131
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify2).join(",")}]`;
|
|
1132
|
+
const object = value;
|
|
1133
|
+
return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${stableStringify2(object[key])}`).join(",")}}`;
|
|
1134
|
+
}
|
|
1135
|
+
function stableLayerDigest(value) {
|
|
1136
|
+
return `sha256:${createHash3("sha256").update(stableStringify2(value)).digest("hex")}`;
|
|
1137
|
+
}
|
|
1138
|
+
function estimateTokens(text) {
|
|
1139
|
+
return Math.ceil(text.length / 4);
|
|
1140
|
+
}
|
|
1141
|
+
function createContextLayer(input) {
|
|
1142
|
+
return {
|
|
1143
|
+
...input,
|
|
1144
|
+
digest: stableLayerDigest(input.content),
|
|
1145
|
+
tokenEstimate: input.tokenEstimate ?? estimateTokens(input.content)
|
|
1146
|
+
};
|
|
1147
|
+
}
|
|
1148
|
+
function createContextLayerReport(layers) {
|
|
1149
|
+
const ordered = [...layers].sort((a, b) => LAYER_ORDER.indexOf(a.id) - LAYER_ORDER.indexOf(b.id));
|
|
1150
|
+
return {
|
|
1151
|
+
layers: ordered,
|
|
1152
|
+
fingerprintIncluded: ordered.filter((layer) => layer.fingerprint === "included"),
|
|
1153
|
+
dynamicLayers: ordered.filter((layer) => layer.stability === "dynamic")
|
|
1154
|
+
};
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// src/prompt/engine.ts
|
|
1158
|
+
var FULL_COLLAPSE_FILL_RATIO = 0.85;
|
|
1159
|
+
var COLLAPSE_FLOOR_FILL_RATIO = 0.5;
|
|
1160
|
+
function simpleHash(s) {
|
|
1161
|
+
let h = 5381;
|
|
1162
|
+
const len = Math.min(s.length, 2e3);
|
|
1163
|
+
for (let i = 0; i < len; i++) h = (h << 5) + h + s.charCodeAt(i) | 0;
|
|
1164
|
+
return `${h}:${s.length}`;
|
|
1165
|
+
}
|
|
1166
|
+
function fullHash(s) {
|
|
1167
|
+
let h = 5381;
|
|
1168
|
+
for (let i = 0; i < s.length; i++) h = (h << 5) + h + s.charCodeAt(i) | 0;
|
|
1169
|
+
return `${h}:${s.length}`;
|
|
1170
|
+
}
|
|
1171
|
+
function messageSignature(m) {
|
|
1172
|
+
const rec = m;
|
|
1173
|
+
let s = typeof m.content === "string" ? m.content : m.content == null ? "" : JSON.stringify(m.content);
|
|
1174
|
+
if (rec.tool_calls) s += "\0" + JSON.stringify(rec.tool_calls);
|
|
1175
|
+
if (rec.reasoning_content) s += "\0" + String(rec.reasoning_content);
|
|
1176
|
+
if (rec.tool_call_id) s += "\0" + String(rec.tool_call_id);
|
|
1177
|
+
return { sig: `${m.role}\0${fullHash(s)}`, len: s.length };
|
|
1178
|
+
}
|
|
1179
|
+
var PromptEngine = class _PromptEngine {
|
|
1180
|
+
systemPrompt;
|
|
1181
|
+
volatileBlock;
|
|
1182
|
+
frozenBase;
|
|
1183
|
+
fingerprint;
|
|
1184
|
+
config;
|
|
1185
|
+
tracker;
|
|
1186
|
+
consolidatedBlock = "";
|
|
1187
|
+
/** Cache key: last user message content — triggers rebuild when it changes. */
|
|
1188
|
+
cachedFreshForUser = "";
|
|
1189
|
+
/** P1: cached dynamic appendix, appended as standalone message at end of result.
|
|
1190
|
+
* Computed once when a new user message arrives, reused across tool-call turns. */
|
|
1191
|
+
cachedAppendix = "";
|
|
1192
|
+
/** appendix 构成快照(cache-log 观测):与 cachedAppendix 同生命周期——同一
|
|
1193
|
+
* 构建路径产出、同一失效路径清空。zenLean 的裁剪量 = 两个相位的 cvmChars
|
|
1194
|
+
* 之差,这是总量字段(appendixChars)看不见的部分。 */
|
|
1195
|
+
lastAppendixAnatomy = null;
|
|
1196
|
+
/** appendix 构成(CVM 计量块 vs keep-list 的字符账)。 */
|
|
1197
|
+
getAppendixAnatomy() {
|
|
1198
|
+
return this.lastAppendixAnatomy;
|
|
1199
|
+
}
|
|
1200
|
+
/** P1: cached consolidated block from habituation tracker.
|
|
1201
|
+
* Stable across turns after promotion — placed BEFORE userContent so
|
|
1202
|
+
* it enters the prefix cache (unlike cachedAppendix, which sits after). */
|
|
1203
|
+
cachedConsolidated = "";
|
|
1204
|
+
/**
|
|
1205
|
+
* Frozen merged content for historical user messages (preserves prefix stability).
|
|
1206
|
+
* Maps user-message content → array of committed snapshots. One entry per
|
|
1207
|
+
* user-message *instance* (boundary commit); duplicate text ("继续" twice)
|
|
1208
|
+
* → index 0, index 1, … via frozenFetchIndex.
|
|
1209
|
+
*
|
|
1210
|
+
* Intra-turn appendix revisions live in frozenPendingMerged until the next
|
|
1211
|
+
* real user boundary — never pushed into this array per tool turn.
|
|
1212
|
+
*/
|
|
1213
|
+
frozenUserMerged = /* @__PURE__ */ new Map();
|
|
1214
|
+
/** Latest merged trailer for the active last-user message (pre-commit). */
|
|
1215
|
+
frozenPendingMerged = /* @__PURE__ */ new Map();
|
|
1216
|
+
/** Per-content fetch index — tracks which entry to retrieve next per content key. */
|
|
1217
|
+
frozenFetchIndex = /* @__PURE__ */ new Map();
|
|
1218
|
+
/** Maximum total entries across all content keys before eviction kicks in. */
|
|
1219
|
+
static MAX_FROZEN_USER_MERGED = 64;
|
|
1220
|
+
/**
|
|
1221
|
+
* Content key of the FIRST user message in the current session. Its frozen
|
|
1222
|
+
* snapshot is the byte-0 anchor of the whole prefix — if eviction deletes it,
|
|
1223
|
+
* the first user message rebuilds with the current (possibly swapped)
|
|
1224
|
+
* volatileBlock → full 0% prefix cache break. Excluded from eviction.
|
|
1225
|
+
*/
|
|
1226
|
+
firstUserKey = null;
|
|
1227
|
+
taskProgress;
|
|
1228
|
+
repairHint;
|
|
1229
|
+
toolContext;
|
|
1230
|
+
planCacheAdvisory;
|
|
1231
|
+
/** U6: serialized PlanExecutionTrace appendix (survives compaction). */
|
|
1232
|
+
planTraceAppendix;
|
|
1233
|
+
/** Approved-plan pointer (slug/title/path only) — dynamic appendix, never frozen. */
|
|
1234
|
+
activePlanPointer;
|
|
1235
|
+
intentRetrievalRoute;
|
|
1236
|
+
taskDepthLayer;
|
|
1237
|
+
/** Advisory text — only set when task depth layer changes, null otherwise to avoid noise. */
|
|
1238
|
+
taskDepthAdvisory = null;
|
|
1239
|
+
planMethodology;
|
|
1240
|
+
planMethodologyReason;
|
|
1241
|
+
/** Whether the last setPlanMethodology was under Plan Mode (design-doc advisory). */
|
|
1242
|
+
planMethodologyPlanMode = false;
|
|
1243
|
+
/** Advisory text — only set when methodology changes, null otherwise to avoid noise. */
|
|
1244
|
+
planMethodologyAdvisory = null;
|
|
1245
|
+
/** Approved-plan execution discipline block — set on plan approval, cleared
|
|
1246
|
+
* on next plan-mode entry. Cache-safe: dynamic appendix only. */
|
|
1247
|
+
planExecutingBlock = null;
|
|
1248
|
+
skillAdvisoryBlock;
|
|
1249
|
+
invokedSkillNames = /* @__PURE__ */ new Set();
|
|
1250
|
+
crossSessionMemoryBlock;
|
|
1251
|
+
mentionContextBlock;
|
|
1252
|
+
harnessAdvisoryBlock;
|
|
1253
|
+
controlPlaneBlock;
|
|
1254
|
+
/** Doom-loop / storm turns: force terse output-style in dynamic appendix. */
|
|
1255
|
+
tersenessEscalate = false;
|
|
1256
|
+
decisions;
|
|
1257
|
+
activeDomain;
|
|
1258
|
+
activeClaims;
|
|
1259
|
+
/** spec 3c 动作 B:排除路径锚点(spark 会话专属;追加式 + cap,创建序稳定)。
|
|
1260
|
+
* 非 spark 会话恒空 → <excluded-paths> 零渲染零字节差异。 */
|
|
1261
|
+
excludedPathAnchors = [];
|
|
1262
|
+
/** Goal anchor(spec 3c 动作 B 补强):会话当前目标,覆盖语义。 */
|
|
1263
|
+
goalAnchor = null;
|
|
1264
|
+
playbookLessons;
|
|
1265
|
+
onLessonsRendered;
|
|
1266
|
+
sessionMemoryOverride;
|
|
1267
|
+
contextLayerReportData;
|
|
1268
|
+
phaseHint;
|
|
1269
|
+
cognitiveProjection;
|
|
1270
|
+
/** Per-turn one-shot cognitive hints, emitted outside appendixDelta (C1 fix). */
|
|
1271
|
+
cognitiveEphemeral;
|
|
1272
|
+
crossSessionEvents;
|
|
1273
|
+
companionPresence;
|
|
1274
|
+
sessionStateText;
|
|
1275
|
+
worktreeReality;
|
|
1276
|
+
/** Plan Mode state — when 'planning', rendered into volatile block */
|
|
1277
|
+
planModeState;
|
|
1278
|
+
/** Ask Mode state — when 'asking', rendered into volatile appendix */
|
|
1279
|
+
askModeState;
|
|
1280
|
+
/** Approval mode — rendered into the dynamic appendix as a permission note
|
|
1281
|
+
* when unattended (dangerously-skip-permissions). Live-updated via
|
|
1282
|
+
* setApprovalMode(); never frozen — the mode flips mid-session. */
|
|
1283
|
+
approvalMode;
|
|
1284
|
+
/** Active plan file path (relative) — shown in plan-mode block */
|
|
1285
|
+
activePlanFilePath;
|
|
1286
|
+
/** One-shot: emit the plan-mode exit reminder on the next rendered turn. */
|
|
1287
|
+
planExitReminderPending;
|
|
1288
|
+
/** Whether current turn message warrants task-mode scaffolding (task contract, CVM, etc.).
|
|
1289
|
+
* Replaces the old binary chat/task PromptMode — auto-detected from message content. */
|
|
1290
|
+
actionableTurn = true;
|
|
1291
|
+
gitDirty = false;
|
|
1292
|
+
/**
|
|
1293
|
+
* T7 watermark: request-copy collapse only applies to message indices below
|
|
1294
|
+
* this boundary. Advancing only on 50K-token steps (not every turn) keeps the
|
|
1295
|
+
* collapse boundary stable across requests — a sliding boundary would rewrite
|
|
1296
|
+
* more request bytes every turn and permanently break the prefix cache.
|
|
1297
|
+
*/
|
|
1298
|
+
collapseWatermark = 0;
|
|
1299
|
+
/** Last 50K-token step at which the watermark was advanced. */
|
|
1300
|
+
collapseTokenStep = -1;
|
|
1301
|
+
/** Cache-event counters (P2-6 breadcrumbs): queried per-turn by cache logging. */
|
|
1302
|
+
frozenSnapshotClamps = 0;
|
|
1303
|
+
frozenFallbackRebuilds = 0;
|
|
1304
|
+
volatileSwapCount = 0;
|
|
1305
|
+
toolsUpdateCount = 0;
|
|
1306
|
+
/** Tracks message array length to detect true duplicate messages vs tool-call turns. */
|
|
1307
|
+
lastMessageCount = 0;
|
|
1308
|
+
/** Hash of last message array to distinguish exact same call from true duplicate. */
|
|
1309
|
+
lastMessageHash = "";
|
|
1310
|
+
userMessagesSinceGitRefresh = 0;
|
|
1311
|
+
/** Prefix-divergence probe: per-message signatures of the previous request. */
|
|
1312
|
+
prevRequestSignatures = null;
|
|
1313
|
+
/** Latest divergence vs the previous request (consume-once via consumePrefixDivergence). */
|
|
1314
|
+
lastPrefixDivergence = null;
|
|
1315
|
+
/** Append-only delta: last emitted context-update sub-blocks (name→content). */
|
|
1316
|
+
lastEmittedAppendixParts = /* @__PURE__ */ new Map();
|
|
1317
|
+
/** Monotonic context-update sequence number (model orders updates by seq). */
|
|
1318
|
+
appendixSeq = 0;
|
|
1319
|
+
/** Whether a full baseline context-update was sent since last reset. */
|
|
1320
|
+
appendixBaselineSent = false;
|
|
1321
|
+
/**
|
|
1322
|
+
* CVM egress ledger: chars of metered appendix sub-blocks ACTUALLY written
|
|
1323
|
+
* into a <context-update> since the last drain. Booked at the emission point
|
|
1324
|
+
* so a block that is byte-stable (delta suppresses it), dropped by Top-K, or
|
|
1325
|
+
* simply not rebuilt on a tool turn costs nothing.
|
|
1326
|
+
*
|
|
1327
|
+
* Invariant: only main-path boundary builds write here — see
|
|
1328
|
+
* buildAppendixBody's chargeLedger parameter.
|
|
1329
|
+
*/
|
|
1330
|
+
appendixLedger = /* @__PURE__ */ new Map();
|
|
1331
|
+
/** Fired by resetAppendixBaseline so CVM accounting tracks history rewrites. */
|
|
1332
|
+
onResetAppendixBaselineCb;
|
|
1333
|
+
constructor(config) {
|
|
1334
|
+
this.config = config;
|
|
1335
|
+
const inherit = config.inheritFrozenFrom;
|
|
1336
|
+
if (inherit instanceof _PromptEngine) {
|
|
1337
|
+
this.frozenUserMerged = new Map([...inherit.frozenUserMerged].map(([k, v]) => [k, [...v]]));
|
|
1338
|
+
this.frozenPendingMerged = new Map(inherit.frozenPendingMerged);
|
|
1339
|
+
this.firstUserKey = inherit.firstUserKey;
|
|
1340
|
+
this.collapseWatermark = inherit.collapseWatermark;
|
|
1341
|
+
this.collapseTokenStep = inherit.collapseTokenStep;
|
|
1342
|
+
} else {
|
|
1343
|
+
const data = inherit ? parseFrozenSnapshotData(inherit) : void 0;
|
|
1344
|
+
if (data) {
|
|
1345
|
+
this.frozenUserMerged = new Map(data.frozenUserMerged.map(([k, v]) => [k, [...v]]));
|
|
1346
|
+
this.frozenPendingMerged = new Map(data.frozenPendingMerged);
|
|
1347
|
+
this.firstUserKey = data.firstUserKey;
|
|
1348
|
+
this.collapseWatermark = data.collapseWatermark;
|
|
1349
|
+
this.collapseTokenStep = data.collapseTokenStep;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
this.approvalMode = config.approvalMode;
|
|
1353
|
+
this.systemPrompt = buildSystemPrompt(config.staticCtx);
|
|
1354
|
+
this.frozenBase = buildStableVolatileBlock(config.volatileCtx);
|
|
1355
|
+
this.volatileBlock = this.frozenBase;
|
|
1356
|
+
this.fingerprint = computeFingerprint(this.systemPrompt, config.staticCtx.tools, this.volatileBlock);
|
|
1357
|
+
this.tracker = (config.habituationThreshold ?? 5) > 0 ? new FieldHabituationTracker({ promotionThreshold: 0.8, decayRate: 0.3 }) : null;
|
|
1358
|
+
this.contextLayerReportData = createContextLayerReport([
|
|
1359
|
+
createContextLayer({ id: "system", label: "Stable System Prompt", stability: "stable", channel: "system", fingerprint: "included", content: this.systemPrompt }),
|
|
1360
|
+
createContextLayer({ id: "tools", label: "Tool Definitions", stability: "stable", channel: "tools", fingerprint: "included", content: JSON.stringify(config.staticCtx.tools) }),
|
|
1361
|
+
...config.volatileCtx.rivetMd ? [createContextLayer({ id: "project-instructions", label: "Project Instructions", stability: "stable-volatile", channel: "volatile-user-message", fingerprint: "included", content: config.volatileCtx.rivetMd })] : [],
|
|
1362
|
+
...config.volatileCtx.gitStatus ? [createContextLayer({ id: "git-status", label: "Git Status", stability: "stable-volatile", channel: "volatile-user-message", fingerprint: "included", content: config.volatileCtx.gitStatus })] : [],
|
|
1363
|
+
...config.volatileCtx.sessionMemoryBlock ? [createContextLayer({ id: "session-memory", label: "Session Memory", stability: "stable-volatile", channel: "volatile-user-message", fingerprint: "included", content: config.volatileCtx.sessionMemoryBlock })] : [],
|
|
1364
|
+
...config.volatileCtx.playbookLessons && config.volatileCtx.playbookLessons.length > 0 ? [createContextLayer({ id: "historical-lessons", label: "Historical Lessons", stability: "dynamic", channel: "volatile-user-message", fingerprint: "excluded", content: config.volatileCtx.playbookLessons.map((b) => b.lesson).join("\n") })] : [],
|
|
1365
|
+
...config.volatileCtx.workingSet && config.volatileCtx.workingSet.length > 0 ? [createContextLayer({ id: "working-set", label: "Working Set", stability: "stable-volatile", channel: "volatile-user-message", fingerprint: "partial", content: config.volatileCtx.workingSet.join("\n") })] : []
|
|
1366
|
+
]);
|
|
1367
|
+
}
|
|
1368
|
+
/**
|
|
1369
|
+
* Build a request. Volatile context is injected as an independent user message
|
|
1370
|
+
* prepended before each user message with string content.
|
|
1371
|
+
*
|
|
1372
|
+
* Cache-critical design for agent loop mode (1 user message → 50 tool calls):
|
|
1373
|
+
* - The FRESH volatile block is generated ONCE per user message, then cached.
|
|
1374
|
+
* - Subsequent tool-call turns reuse the cached FRESH → prefix stays stable.
|
|
1375
|
+
* - Only when a NEW user text message arrives does FRESH get regenerated.
|
|
1376
|
+
* - Historical user text messages always use FROZEN (this.volatileBlock).
|
|
1377
|
+
*
|
|
1378
|
+
* This ensures DeepSeek's exact-prefix cache hits on API calls 2-50 within
|
|
1379
|
+
* a single user message's execution, not just across user messages.
|
|
1380
|
+
*/
|
|
1381
|
+
/** volatile trailer 的文本前缀:frozen 的 volatileBlock + habituation 稳定的
|
|
1382
|
+
* consolidated 块 + 分隔线。文本消息直接与之拼接——字节布局与历史一致,
|
|
1383
|
+
* 前缀缓存基线不动;多模态消息把这个前缀当作独立的 text part 使用。 */
|
|
1384
|
+
trailerTextPrefix() {
|
|
1385
|
+
const consolidated = this.cachedConsolidated ? "\n" + this.cachedConsolidated : "";
|
|
1386
|
+
return this.volatileBlock + consolidated + "\n---\n";
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* trailer 合并后的 user 消息内容。
|
|
1390
|
+
*
|
|
1391
|
+
* 文本消息拼成单串(既有字节布局,缓存基线不变)。多模态消息必须保留 parts:
|
|
1392
|
+
* 拼字符串只能把图片丢掉或降级成一串 base64 文本——模型于是一律回答「看不到
|
|
1393
|
+
* 图片」(2026-09-11 用户报障)。这类消息把 volatile 前缀作为首个 text part
|
|
1394
|
+
* 前置、用户 parts 原样保留、附录追加为末尾 text part。
|
|
1395
|
+
*/
|
|
1396
|
+
buildTraileredUserContent(msg, appendix) {
|
|
1397
|
+
const prefix = this.trailerTextPrefix();
|
|
1398
|
+
if (Array.isArray(msg.content)) {
|
|
1399
|
+
const parts = [{ type: "text", text: prefix }, ...msg.content];
|
|
1400
|
+
if (appendix) parts.push({ type: "text", text: appendix });
|
|
1401
|
+
return parts;
|
|
1402
|
+
}
|
|
1403
|
+
return prefix + msg.content + (appendix ? "\n\n" + appendix : "");
|
|
1404
|
+
}
|
|
1405
|
+
/**
|
|
1406
|
+
* Retrieve the next frozen snapshot for a given user-message content.
|
|
1407
|
+
* Maintains a per-content fetch index so that duplicate messages ("继续", "ok")
|
|
1408
|
+
* each get their own frozen snapshot in order.
|
|
1409
|
+
*/
|
|
1410
|
+
getNextFrozen(content) {
|
|
1411
|
+
const arr = this.frozenUserMerged.get(content);
|
|
1412
|
+
if (!arr || arr.length === 0) return void 0;
|
|
1413
|
+
const idx = this.frozenFetchIndex.get(content) ?? 0;
|
|
1414
|
+
this.frozenFetchIndex.set(content, idx + 1);
|
|
1415
|
+
if (idx >= arr.length) {
|
|
1416
|
+
this.frozenSnapshotClamps++;
|
|
1417
|
+
debugLog("prompt-engine", `frozen snapshot clamp: key len=${content.length} requested idx=${idx} surviving=${arr.length}`);
|
|
1418
|
+
return arr[arr.length - 1];
|
|
1419
|
+
}
|
|
1420
|
+
return arr[idx];
|
|
1421
|
+
}
|
|
1422
|
+
/**
|
|
1423
|
+
* Commit the pending merged trailer for one user-message instance. Called
|
|
1424
|
+
* only at a real user boundary (new user text or duplicate-instance), NOT
|
|
1425
|
+
* on invalidateFreshCache pseudo-boundaries mid-tool-loop.
|
|
1426
|
+
*/
|
|
1427
|
+
commitFrozenSnapshot(content) {
|
|
1428
|
+
const pending = this.frozenPendingMerged.get(content);
|
|
1429
|
+
if (!pending) return;
|
|
1430
|
+
const arr = this.frozenUserMerged.get(content);
|
|
1431
|
+
if (arr) {
|
|
1432
|
+
if (arr[arr.length - 1] !== pending) arr.push(pending);
|
|
1433
|
+
} else {
|
|
1434
|
+
this.frozenUserMerged.set(content, [pending]);
|
|
1435
|
+
}
|
|
1436
|
+
if (this.onFrozenSnapshotCommitCb) {
|
|
1437
|
+
try {
|
|
1438
|
+
this.onFrozenSnapshotCommitCb();
|
|
1439
|
+
} catch {
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
}
|
|
1443
|
+
/** Fired by commitFrozenSnapshot(唯一快照固化点);由 bootstrap 接线盘存写。 */
|
|
1444
|
+
onFrozenSnapshotCommitCb;
|
|
1445
|
+
/** 注册快照固化回调(每次 user 边界 commit 后触发,低频)。 */
|
|
1446
|
+
setOnFrozenSnapshotCommit(fn) {
|
|
1447
|
+
this.onFrozenSnapshotCommitCb = fn;
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* 导出冻结前缀快照的可序列化形态(深拷贝)——随会话落盘,
|
|
1451
|
+
* resume 时经 inheritFrozenFrom 喂回,历史 user 消息恢复原始字节。
|
|
1452
|
+
*/
|
|
1453
|
+
exportFrozenSnapshot() {
|
|
1454
|
+
return {
|
|
1455
|
+
v: 1,
|
|
1456
|
+
frozenUserMerged: [...this.frozenUserMerged].map(([k, v]) => [k, [...v]]),
|
|
1457
|
+
frozenPendingMerged: [...this.frozenPendingMerged],
|
|
1458
|
+
firstUserKey: this.firstUserKey,
|
|
1459
|
+
collapseWatermark: this.collapseWatermark,
|
|
1460
|
+
collapseTokenStep: this.collapseTokenStep
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
/** 冻结锚点总数(resume 公告「继承 N 个前缀锚点」用)。 */
|
|
1464
|
+
getFrozenAnchorCount() {
|
|
1465
|
+
let n = 0;
|
|
1466
|
+
for (const arr of this.frozenUserMerged.values()) n += arr.length;
|
|
1467
|
+
return n;
|
|
1468
|
+
}
|
|
1469
|
+
buildOaiRequest(inputMessages, toolHistory, contextWindow, options) {
|
|
1470
|
+
const sidePath = options?.sidePath === true;
|
|
1471
|
+
const result = [];
|
|
1472
|
+
this.frozenFetchIndex.clear();
|
|
1473
|
+
const preflight = runResumePreflightOai(inputMessages, sidePath ? void 0 : { writeProbe: options?.writeProbe });
|
|
1474
|
+
const oaiMessages = preflight.messages;
|
|
1475
|
+
if (preflight.repaired && preflight.syntheticResultsInserted > 0 && !sidePath && options?.onOrphanRepair) {
|
|
1476
|
+
options.onOrphanRepair(oaiMessages, preflight.syntheticResultsInserted);
|
|
1477
|
+
}
|
|
1478
|
+
const appendixMaxChars = contextWindow && contextWindow > 0 ? Math.min(Math.max(Math.floor(contextWindow * 0.05 * 4), 2e3), 4 * LARGE_VOLATILE_PAYLOAD_CHARS) : void 0;
|
|
1479
|
+
let firstUserIdx = -1;
|
|
1480
|
+
let lastUserIdx = -1;
|
|
1481
|
+
for (let i = 0; i < oaiMessages.length; i++) {
|
|
1482
|
+
const m = oaiMessages[i];
|
|
1483
|
+
if (m.role === "user" && !isSystemReminder(m.content)) {
|
|
1484
|
+
if (firstUserIdx === -1) firstUserIdx = i;
|
|
1485
|
+
lastUserIdx = i;
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
if (!sidePath && firstUserIdx >= 0) {
|
|
1489
|
+
const fm = oaiMessages[firstUserIdx];
|
|
1490
|
+
this.firstUserKey = typeof fm.content === "string" ? fm.content : "";
|
|
1491
|
+
}
|
|
1492
|
+
if (!sidePath && lastUserIdx >= 0) {
|
|
1493
|
+
const lastUserContent = typeof oaiMessages[lastUserIdx].content === "string" ? oaiMessages[lastUserIdx].content : JSON.stringify(oaiMessages[lastUserIdx].content);
|
|
1494
|
+
for (const key of [...this.frozenPendingMerged.keys()]) {
|
|
1495
|
+
if (key === lastUserContent) continue;
|
|
1496
|
+
this.commitFrozenSnapshot(key);
|
|
1497
|
+
this.frozenPendingMerged.delete(key);
|
|
1498
|
+
}
|
|
1499
|
+
const hasPriorInstanceNowHistorical = this.frozenPendingMerged.has(lastUserContent) && oaiMessages.some((m, idx) => m.role === "user" && !isSystemReminder(m.content) && idx !== lastUserIdx && (typeof m.content === "string" ? m.content : JSON.stringify(m.content)) === lastUserContent);
|
|
1500
|
+
const msgHash = oaiMessages.length > 0 ? `${oaiMessages.length}:${typeof oaiMessages[oaiMessages.length - 1].content === "string" ? oaiMessages[oaiMessages.length - 1].content : ""}` : "";
|
|
1501
|
+
const isDuplicate = lastUserContent === this.cachedFreshForUser && oaiMessages.length === this.lastMessageCount && msgHash !== this.lastMessageHash && ((this.frozenUserMerged.get(lastUserContent)?.length ?? 0) > 0 || this.frozenPendingMerged.has(lastUserContent));
|
|
1502
|
+
if (hasPriorInstanceNowHistorical || isDuplicate) {
|
|
1503
|
+
this.commitFrozenSnapshot(lastUserContent);
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1506
|
+
for (let i = 0; i < oaiMessages.length; i++) {
|
|
1507
|
+
const msg = oaiMessages[i];
|
|
1508
|
+
if (msg.role === "user" && isSystemReminder(msg.content)) {
|
|
1509
|
+
result.push(msg);
|
|
1510
|
+
} else if (msg.role === "user" && this.volatileBlock) {
|
|
1511
|
+
if (i === lastUserIdx && sidePath) {
|
|
1512
|
+
result.push({ role: "user", content: this.buildTraileredUserContent(msg) });
|
|
1513
|
+
} else if (i === lastUserIdx) {
|
|
1514
|
+
const userContent = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1515
|
+
const msgHash = oaiMessages.length > 0 ? `${oaiMessages.length}:${typeof oaiMessages[oaiMessages.length - 1].content === "string" ? oaiMessages[oaiMessages.length - 1].content : ""}` : "";
|
|
1516
|
+
const isDuplicate = userContent === this.cachedFreshForUser && oaiMessages.length === this.lastMessageCount && msgHash !== this.lastMessageHash && ((this.frozenUserMerged.get(typeof userContent === "string" ? userContent : "")?.length ?? 0) > 0 || this.frozenPendingMerged.has(this.cachedFreshForUser));
|
|
1517
|
+
this.lastMessageCount = oaiMessages.length;
|
|
1518
|
+
this.lastMessageHash = msgHash;
|
|
1519
|
+
if (userContent !== this.cachedFreshForUser || isDuplicate) {
|
|
1520
|
+
if (this.frozenBase !== this.volatileBlock) {
|
|
1521
|
+
this.volatileBlock = this.frozenBase;
|
|
1522
|
+
this.volatileSwapCount++;
|
|
1523
|
+
}
|
|
1524
|
+
this.cachedFreshForUser = userContent;
|
|
1525
|
+
this.userMessagesSinceGitRefresh++;
|
|
1526
|
+
const refreshGit = this.gitDirty || this.userMessagesSinceGitRefresh >= 3;
|
|
1527
|
+
if (refreshGit) {
|
|
1528
|
+
this.gitDirty = false;
|
|
1529
|
+
this.userMessagesSinceGitRefresh = 0;
|
|
1530
|
+
}
|
|
1531
|
+
const dynamicCtx = { ...this.config.volatileCtx, toolHistory, taskProgress: this.taskProgress, toolContext: this.toolContext, planCacheAdvisory: this.planCacheAdvisory, planTraceAppendix: this.planTraceAppendix, activePlanPointer: this.activePlanPointer, intentRetrievalRoute: this.intentRetrievalRoute, taskDepthAdvisory: this.taskDepthAdvisory, planMethodologyAdvisory: this.planMethodologyAdvisory, planExecutingBlock: this.planExecutingBlock, skillAdvisoryBlock: this.skillAdvisoryBlock ?? void 0, invokedSkillsBlock: skillRegistry.renderInvokedSkillsBlock([...this.invokedSkillNames], this.config.volatileCtx.cwd) ?? void 0, crossSessionMemoryBlock: this.crossSessionMemoryBlock ?? void 0, mentionContextBlock: this.mentionContextBlock ?? void 0, harnessAdvisoryBlock: this.harnessAdvisoryBlock, controlPlaneBlock: this.controlPlaneBlock, tersenessEscalate: this.tersenessEscalate, zenLean: this.zenLean || void 0, decisions: this.decisions, activeClaims: this.activeClaims, excludedPathAnchors: this.excludedPathAnchors.length > 0 ? this.excludedPathAnchors : void 0, goalAnchor: this.goalAnchor, playbookLessons: this.playbookLessons, onLessonsRendered: this.onLessonsRendered, sessionMemoryBlock: this.sessionMemoryOverride ?? this.config.volatileCtx.sessionMemoryBlock, crossSessionEvents: this.crossSessionEvents, companionPresence: this.companionPresence, sessionState: this.sessionStateText, worktreeReality: this.worktreeReality, planModeState: this.planModeState, askModeState: this.askModeState, approvalMode: this.approvalMode, activePlanFilePath: this.activePlanFilePath, planExitReminderPending: this.planExitReminderPending, cognitiveProjection: this.cognitiveProjection, ...refreshGit ? { gitStatus: void 0 } : {} };
|
|
1532
|
+
if (this.planExitReminderPending) this.planExitReminderPending = false;
|
|
1533
|
+
if (this.tracker) {
|
|
1534
|
+
const fieldValues = {};
|
|
1535
|
+
if (dynamicCtx.playbookLessons && dynamicCtx.playbookLessons.length > 0) {
|
|
1536
|
+
fieldValues["playbookLessons"] = dynamicCtx.playbookLessons.map((b) => b.lesson).join("|");
|
|
1537
|
+
}
|
|
1538
|
+
this.tracker.recordTurn(fieldValues, this.phaseHint);
|
|
1539
|
+
const habituatedContent = this.tracker.getHabituatedContent();
|
|
1540
|
+
const renderedHabituated = /* @__PURE__ */ new Map();
|
|
1541
|
+
for (const [name, content] of habituatedContent) {
|
|
1542
|
+
if (name === "playbookLessons") {
|
|
1543
|
+
renderedHabituated.set(name, `<historical-lessons>
|
|
1544
|
+
${content.split("|").map((l) => `- ${l}`).join("\n")}
|
|
1545
|
+
</historical-lessons>`);
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1548
|
+
const newConsolidated = buildConsolidatedBlock(renderedHabituated);
|
|
1549
|
+
if (newConsolidated !== this.consolidatedBlock) {
|
|
1550
|
+
this.consolidatedBlock = newConsolidated;
|
|
1551
|
+
}
|
|
1552
|
+
const activeCtx = { ...dynamicCtx };
|
|
1553
|
+
const habituated = this.tracker.getHabituated();
|
|
1554
|
+
if (habituated.has("playbookLessons")) activeCtx.playbookLessons = void 0;
|
|
1555
|
+
const activeAppendix = this.actionableTurn ? this.buildAppendixBody(activeCtx, appendixMaxChars, !sidePath) : "";
|
|
1556
|
+
this.cachedConsolidated = this.consolidatedBlock;
|
|
1557
|
+
this.cachedAppendix = this.withEphemeralProjection(activeAppendix, !sidePath);
|
|
1558
|
+
} else {
|
|
1559
|
+
if (this.actionableTurn) {
|
|
1560
|
+
const appendix = this.buildAppendixBody(dynamicCtx, appendixMaxChars, !sidePath);
|
|
1561
|
+
this.cachedConsolidated = this.consolidatedBlock;
|
|
1562
|
+
this.cachedAppendix = this.withEphemeralProjection(appendix, !sidePath);
|
|
1563
|
+
} else {
|
|
1564
|
+
this.cachedConsolidated = "";
|
|
1565
|
+
this.cachedAppendix = "";
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
const trailered = this.buildTraileredUserContent(msg, this.cachedAppendix || void 0);
|
|
1570
|
+
if (typeof msg.content === "string") {
|
|
1571
|
+
this.frozenPendingMerged.set(msg.content, trailered);
|
|
1572
|
+
}
|
|
1573
|
+
result.push({ role: "user", content: trailered });
|
|
1574
|
+
} else if (i === firstUserIdx) {
|
|
1575
|
+
const frozen = this.getNextFrozen(typeof msg.content === "string" ? msg.content : "");
|
|
1576
|
+
if (frozen) {
|
|
1577
|
+
result.push({ role: "user", content: frozen });
|
|
1578
|
+
} else {
|
|
1579
|
+
this.frozenFallbackRebuilds++;
|
|
1580
|
+
debugLog("prompt-engine", `FATAL-CACHE: frozen snapshots fully evicted for FIRST user message (len=${typeof msg.content === "string" ? msg.content.length : 0}) \u2014 rebuilding with current volatileBlock`);
|
|
1581
|
+
const rebuilt = this.buildTraileredUserContent(msg);
|
|
1582
|
+
if (!sidePath && typeof msg.content === "string" && msg.content !== "") {
|
|
1583
|
+
this.frozenUserMerged.set(msg.content, [rebuilt]);
|
|
1584
|
+
}
|
|
1585
|
+
result.push({ role: "user", content: rebuilt });
|
|
1586
|
+
}
|
|
1587
|
+
} else {
|
|
1588
|
+
const frozen = this.getNextFrozen(typeof msg.content === "string" ? msg.content : "");
|
|
1589
|
+
if (frozen) {
|
|
1590
|
+
result.push({ role: "user", content: frozen });
|
|
1591
|
+
} else {
|
|
1592
|
+
this.frozenFallbackRebuilds++;
|
|
1593
|
+
debugLog("prompt-engine", `frozen snapshots fully evicted for historical user message (len=${typeof msg.content === "string" ? msg.content.length : 0}) \u2014 rebuilding with current volatileBlock`);
|
|
1594
|
+
const rebuilt = this.buildTraileredUserContent(msg);
|
|
1595
|
+
if (!sidePath && typeof msg.content === "string" && msg.content !== "") {
|
|
1596
|
+
this.frozenUserMerged.set(msg.content, [rebuilt]);
|
|
1597
|
+
}
|
|
1598
|
+
result.push({ role: "user", content: rebuilt });
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
} else {
|
|
1602
|
+
result.push(msg);
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
const tools = this.config.staticCtx.tools.length > 0 ? this.config.staticCtx.tools.map((tool) => {
|
|
1606
|
+
const func = {
|
|
1607
|
+
name: tool.name,
|
|
1608
|
+
description: tool.description,
|
|
1609
|
+
parameters: tool.input_schema ?? { type: "object", properties: {} }
|
|
1610
|
+
};
|
|
1611
|
+
if (tool.providerFormat) {
|
|
1612
|
+
func.providerFormat = tool.providerFormat;
|
|
1613
|
+
}
|
|
1614
|
+
return { type: "function", function: func };
|
|
1615
|
+
}) : void 0;
|
|
1616
|
+
if (!contextWindow || contextWindow < 1e6) {
|
|
1617
|
+
const { messages: prunedMessages, prunedCount, savedChars } = pruneOutdatedQueryResults(result, CACHE_ANCHOR_MESSAGES);
|
|
1618
|
+
if (prunedCount > 0) {
|
|
1619
|
+
debugLog(`[semantic-prune] pruned ${prunedCount} results, saved ${savedChars} chars`);
|
|
1620
|
+
result.length = 0;
|
|
1621
|
+
result.push(...prunedMessages);
|
|
1622
|
+
}
|
|
1623
|
+
const { messages: stalenessPruned } = detectStaleness(result, CACHE_ANCHOR_MESSAGES, {
|
|
1624
|
+
suffixTokenLimit: 8e3
|
|
1625
|
+
// only mutate cheap-to-recache tail
|
|
1626
|
+
});
|
|
1627
|
+
if (stalenessPruned !== result) {
|
|
1628
|
+
for (let i = 0; i < result.length; i++) result[i] = stalenessPruned[i];
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
const MASK_WINDOW = 10;
|
|
1632
|
+
if (!contextWindow || contextWindow < 1e6) {
|
|
1633
|
+
let userCount = 0;
|
|
1634
|
+
const userTurnIndices = [];
|
|
1635
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
1636
|
+
if (result[i].role === "user") {
|
|
1637
|
+
userCount++;
|
|
1638
|
+
userTurnIndices.push(i);
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
if (userCount > MASK_WINDOW) {
|
|
1642
|
+
const cutoff = userTurnIndices[MASK_WINDOW - 1];
|
|
1643
|
+
for (let i = 0; i < cutoff; i++) {
|
|
1644
|
+
const msg = result[i];
|
|
1645
|
+
if (msg.role === "tool" && msg.content.length > 200) {
|
|
1646
|
+
const preview = msg.content.slice(0, 100);
|
|
1647
|
+
result[i] = { ...msg, content: `[observation masked, ${msg.content.length} chars]
|
|
1648
|
+
${preview}\u2026` };
|
|
1649
|
+
}
|
|
1650
|
+
}
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
if (!contextWindow || contextWindow < 1e6) {
|
|
1654
|
+
const seenContent = /* @__PURE__ */ new Map();
|
|
1655
|
+
for (let i = result.length - 1; i >= 0; i--) {
|
|
1656
|
+
const msg = result[i];
|
|
1657
|
+
if (msg.role === "tool" && msg.content.length > 500 && !msg.content.startsWith("[observation masked")) {
|
|
1658
|
+
const hash = simpleHash(msg.content);
|
|
1659
|
+
if (!seenContent.has(hash)) {
|
|
1660
|
+
seenContent.set(hash, i);
|
|
1661
|
+
} else {
|
|
1662
|
+
result[i] = { ...msg, content: `[duplicate content, see later tool result]` };
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
const DISK_BUDGET_CHARS = 5e4;
|
|
1667
|
+
const PREVIEW_CHARS = 2e3;
|
|
1668
|
+
for (let i = 0; i < result.length; i++) {
|
|
1669
|
+
const msg = result[i];
|
|
1670
|
+
if (msg.role === "tool" && msg.content.length > DISK_BUDGET_CHARS) {
|
|
1671
|
+
const preview = msg.content.slice(0, PREVIEW_CHARS);
|
|
1672
|
+
result[i] = { ...msg, content: `${preview}
|
|
1673
|
+
|
|
1674
|
+
[output truncated: ${msg.content.length} chars total, showing first ${PREVIEW_CHARS}]` };
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
let totalFrozen = 0;
|
|
1679
|
+
for (const arr of this.frozenUserMerged.values()) totalFrozen += arr.length;
|
|
1680
|
+
while (totalFrozen > _PromptEngine.MAX_FROZEN_USER_MERGED && this.frozenUserMerged.size > 0) {
|
|
1681
|
+
let maxKey = "", maxLen = 0;
|
|
1682
|
+
for (const [k, arr] of this.frozenUserMerged) {
|
|
1683
|
+
if (k === this.firstUserKey) continue;
|
|
1684
|
+
if (arr.length > maxLen) {
|
|
1685
|
+
maxKey = k;
|
|
1686
|
+
maxLen = arr.length;
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
if (maxLen === 0) break;
|
|
1690
|
+
if (maxLen <= 1) {
|
|
1691
|
+
this.frozenUserMerged.delete(maxKey);
|
|
1692
|
+
} else {
|
|
1693
|
+
this.frozenUserMerged.get(maxKey).shift();
|
|
1694
|
+
}
|
|
1695
|
+
totalFrozen--;
|
|
1696
|
+
}
|
|
1697
|
+
if (contextWindow && contextWindow >= 2e5 && !sidePath) {
|
|
1698
|
+
const collapseAge = this.config.attentionProfile?.collapseAgeTurns ?? 8;
|
|
1699
|
+
const estTokens = estimateOaiTokens(result);
|
|
1700
|
+
const fillRatio = estTokens / contextWindow;
|
|
1701
|
+
if (fillRatio >= COLLAPSE_FLOOR_FILL_RATIO) {
|
|
1702
|
+
if (this.collapseWatermark > result.length) {
|
|
1703
|
+
this.collapseWatermark = 0;
|
|
1704
|
+
this.collapseTokenStep = -1;
|
|
1705
|
+
}
|
|
1706
|
+
const step = Math.floor(estTokens / 5e4);
|
|
1707
|
+
if (step > this.collapseTokenStep) {
|
|
1708
|
+
this.collapseTokenStep = step;
|
|
1709
|
+
this.collapseWatermark = computeCollapseBoundary(result, collapseAge);
|
|
1710
|
+
debugLog("prompt-engine", `T7 watermark advanced: step=${step} boundary=${this.collapseWatermark} estTokens=${estTokens}`);
|
|
1711
|
+
}
|
|
1712
|
+
if (this.collapseWatermark > 0) {
|
|
1713
|
+
requestTimeCollapse(result, this.collapseWatermark, contextWindow, fillRatio < FULL_COLLAPSE_FILL_RATIO);
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
const request = {
|
|
1718
|
+
model: this.config.model,
|
|
1719
|
+
messages: [{ role: "system", content: this.systemPrompt }, ...result],
|
|
1720
|
+
max_tokens: this.config.maxTokens,
|
|
1721
|
+
stream: true,
|
|
1722
|
+
stream_options: { include_usage: true },
|
|
1723
|
+
tools,
|
|
1724
|
+
tool_choice: tools ? "auto" : void 0,
|
|
1725
|
+
// Main-turn marker for the client's wire-level probe — the client only
|
|
1726
|
+
// fingerprints final send bytes for these requests (side-path calls
|
|
1727
|
+
// through the same client would poison its baseline too).
|
|
1728
|
+
prefixProbe: !sidePath || void 0
|
|
1729
|
+
};
|
|
1730
|
+
if (!sidePath) this.recordPrefixDivergence(request.messages);
|
|
1731
|
+
return request;
|
|
1732
|
+
}
|
|
1733
|
+
/**
|
|
1734
|
+
* Prefix-divergence probe: compare this request's per-message signatures with
|
|
1735
|
+
* the previous request's. An exact-prefix provider only re-matches the shared
|
|
1736
|
+
* prefix, so any non-append change (message content mutated, message removed,
|
|
1737
|
+
* count shrank) is a real client-side cache break. Records the FIRST diverged
|
|
1738
|
+
* index; pure appends record nothing.
|
|
1739
|
+
*/
|
|
1740
|
+
recordPrefixDivergence(messages) {
|
|
1741
|
+
const sigs = messages.map(messageSignature);
|
|
1742
|
+
const prev = this.prevRequestSignatures;
|
|
1743
|
+
this.prevRequestSignatures = sigs;
|
|
1744
|
+
if (!prev) return;
|
|
1745
|
+
const shared = Math.min(prev.length, sigs.length);
|
|
1746
|
+
let divergedIdx = -1;
|
|
1747
|
+
for (let i = 0; i < shared; i++) {
|
|
1748
|
+
if (prev[i].sig !== sigs[i].sig) {
|
|
1749
|
+
divergedIdx = i;
|
|
1750
|
+
break;
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
if (divergedIdx === -1) {
|
|
1754
|
+
if (sigs.length >= prev.length) return;
|
|
1755
|
+
divergedIdx = sigs.length;
|
|
1756
|
+
}
|
|
1757
|
+
let approxCharPos = 0;
|
|
1758
|
+
for (let i = 0; i < divergedIdx; i++) approxCharPos += sigs[i]?.len ?? prev[i].len;
|
|
1759
|
+
this.lastPrefixDivergence = {
|
|
1760
|
+
idx: divergedIdx,
|
|
1761
|
+
role: messages[divergedIdx]?.role ?? "removed",
|
|
1762
|
+
kind: divergedIdx >= sigs.length ? "message_removed" : "message_changed",
|
|
1763
|
+
prevCount: prev.length,
|
|
1764
|
+
newCount: sigs.length,
|
|
1765
|
+
approxCharPos
|
|
1766
|
+
};
|
|
1767
|
+
}
|
|
1768
|
+
/** Consume-once accessor for the latest prefix divergence (cache-log breadcrumb). */
|
|
1769
|
+
consumePrefixDivergence() {
|
|
1770
|
+
const d = this.lastPrefixDivergence;
|
|
1771
|
+
this.lastPrefixDivergence = null;
|
|
1772
|
+
return d;
|
|
1773
|
+
}
|
|
1774
|
+
getModel() {
|
|
1775
|
+
return this.config.model;
|
|
1776
|
+
}
|
|
1777
|
+
getFingerprint() {
|
|
1778
|
+
return this.fingerprint;
|
|
1779
|
+
}
|
|
1780
|
+
checkDrift() {
|
|
1781
|
+
const current = computeFingerprint(this.systemPrompt, this.config.staticCtx.tools, this.volatileBlock);
|
|
1782
|
+
return detectDrift(this.fingerprint, current);
|
|
1783
|
+
}
|
|
1784
|
+
getSystemPrompt() {
|
|
1785
|
+
return this.systemPrompt;
|
|
1786
|
+
}
|
|
1787
|
+
updateTools(tools) {
|
|
1788
|
+
if (stableStringify(tools) === stableStringify(this.config.staticCtx.tools)) return;
|
|
1789
|
+
this.config.staticCtx.tools = tools;
|
|
1790
|
+
this.fingerprint = computeFingerprint(this.systemPrompt, tools, this.volatileBlock);
|
|
1791
|
+
this.toolsUpdateCount++;
|
|
1792
|
+
}
|
|
1793
|
+
/** Number of tool definitions (for prefix overhead estimation). */
|
|
1794
|
+
getToolCount() {
|
|
1795
|
+
return this.config.staticCtx.tools.length;
|
|
1796
|
+
}
|
|
1797
|
+
/**
|
|
1798
|
+
* 当前 frozen 前缀的分块构成(`/prefix-budget` 与 scripts/prefix-budget.ts 共用口径)。
|
|
1799
|
+
*
|
|
1800
|
+
* 读的是这个引擎实际持有的字节,不重新加载磁盘——脚本量的是「新会话会是什么样」,
|
|
1801
|
+
* 这里量的是「本会话现在是什么样」。两者在会话中途切过 /domain 或 /cd 后会不同。
|
|
1802
|
+
*/
|
|
1803
|
+
getPrefixBudgetInputs() {
|
|
1804
|
+
const ctx = this.config.volatileCtx;
|
|
1805
|
+
const caps = { ...FROZEN_BLOCK_CAPS, ...ctx.blockCaps };
|
|
1806
|
+
return [
|
|
1807
|
+
{ name: "static.ts BASE_PROMPT", category: "brake", content: this.systemPrompt },
|
|
1808
|
+
{ name: "star-domain volatileBlock", category: "brake", content: ctx.activeDomain?.volatileBlock },
|
|
1809
|
+
{ name: "\u5DE5\u5177 schema (JSON)", category: "tools", content: JSON.stringify(this.config.staticCtx.tools) },
|
|
1810
|
+
// 与渲染层同一套按节选取——`slice(0, cap)` 会把报告写成「按前 N 字符收录」,
|
|
1811
|
+
// 而实际收录的是另一组章节,归因数字对不上真实前缀。
|
|
1812
|
+
{ name: "project-instructions", category: "reference", content: ctx.rivetMd ? selectProjectInstructions(ctx.rivetMd, caps.projectInstructions).text : void 0, cap: caps.projectInstructions, rawChars: ctx.rivetMd?.length },
|
|
1813
|
+
{ name: "project-memory", category: "reference", content: ctx.projectMemoryBlock?.slice(0, caps.projectMemory), cap: caps.projectMemory, rawChars: ctx.projectMemoryBlock?.length },
|
|
1814
|
+
{ name: "knowledge-manifest", category: "reference", content: ctx.knowledgeManifestBlock?.slice(0, caps.knowledgeManifest), cap: caps.knowledgeManifest, rawChars: ctx.knowledgeManifestBlock?.length },
|
|
1815
|
+
{ name: "codebase-index", category: "reference", content: ctx.projectIndexBlock?.slice(0, caps.codebaseIndex), cap: caps.codebaseIndex, rawChars: ctx.projectIndexBlock?.length },
|
|
1816
|
+
{ name: "seed-capsule \u7D22\u5F15", category: "reference", content: ctx.seedCapsuleBlock },
|
|
1817
|
+
{ name: "session-memory", category: "reference", content: ctx.sessionMemoryBlock },
|
|
1818
|
+
{ name: "appendix\uFF08\u6BCF\u8F6E\u52A8\u6001\uFF09", category: "appendix", content: this.cachedAppendix }
|
|
1819
|
+
];
|
|
1820
|
+
}
|
|
1821
|
+
/** Current cognitive projection length in chars (for cache-log observability). */
|
|
1822
|
+
getCognitiveProjectionLength() {
|
|
1823
|
+
return this.cognitiveProjection?.length ?? 0;
|
|
1824
|
+
}
|
|
1825
|
+
/** Current cached appendix length in chars (for cache-log observability). */
|
|
1826
|
+
getCachedAppendixLength() {
|
|
1827
|
+
return this.cachedAppendix?.length ?? 0;
|
|
1828
|
+
}
|
|
1829
|
+
/**
|
|
1830
|
+
* The appendix text as actually rendered this request. Nothing downstream of here
|
|
1831
|
+
* persists it — the session JSONL keeps what the user typed, not what was injected
|
|
1832
|
+
* alongside it — so without this accessor there is no way to answer "what did the
|
|
1833
|
+
* model actually see" after the fact. Read by the (gated) appendix trace recorder.
|
|
1834
|
+
*/
|
|
1835
|
+
getCachedAppendix() {
|
|
1836
|
+
return this.cachedAppendix;
|
|
1837
|
+
}
|
|
1838
|
+
updateSessionMemory(block) {
|
|
1839
|
+
this.sessionMemoryOverride = block;
|
|
1840
|
+
this.rebuildFrozenBase();
|
|
1841
|
+
this.invalidateFreshCache();
|
|
1842
|
+
}
|
|
1843
|
+
rebuildFrozenBase() {
|
|
1844
|
+
const ctx = {
|
|
1845
|
+
...this.config.volatileCtx,
|
|
1846
|
+
sessionMemoryBlock: this.sessionMemoryOverride ?? this.config.volatileCtx.sessionMemoryBlock,
|
|
1847
|
+
// activeDomain folded into the frozen prefix (session constant). The ?? is
|
|
1848
|
+
// explicit defense mirroring the appendix getter below: it allows a future
|
|
1849
|
+
// volatileCtx-preset domain to survive a reset. Today config.volatileCtx
|
|
1850
|
+
// .activeDomain is always undefined, so resetSessionDomain (this.activeDomain
|
|
1851
|
+
// = undefined) correctly drops the domain from the frozen prefix.
|
|
1852
|
+
activeDomain: this.activeDomain ?? this.config.volatileCtx.activeDomain
|
|
1853
|
+
};
|
|
1854
|
+
this.frozenBase = buildStableVolatileBlock(ctx);
|
|
1855
|
+
this.fingerprint = computeFingerprint(this.systemPrompt, this.config.staticCtx.tools, this.frozenBase);
|
|
1856
|
+
}
|
|
1857
|
+
setActionableTurn(actionable) {
|
|
1858
|
+
if (this.actionableTurn === actionable) return;
|
|
1859
|
+
this.actionableTurn = actionable;
|
|
1860
|
+
this.invalidateFreshCache();
|
|
1861
|
+
}
|
|
1862
|
+
/** @deprecated Use isActionableTurn from task-contract for auto-detection. */
|
|
1863
|
+
getMode() {
|
|
1864
|
+
return "task";
|
|
1865
|
+
}
|
|
1866
|
+
updateActiveClaims(claims) {
|
|
1867
|
+
this.activeClaims = claims;
|
|
1868
|
+
}
|
|
1869
|
+
/** spec 3c 动作 B:追加排除路径锚点。去重(保首见序)+ cap 溢出淘汰最旧——
|
|
1870
|
+
* 字节只在语义变化(新锚点/挤出)时变化,appendixDelta 可吸收。 */
|
|
1871
|
+
appendExcludedPathAnchors(anchors) {
|
|
1872
|
+
const MAX_ANCHORS = 20;
|
|
1873
|
+
for (const a of anchors) {
|
|
1874
|
+
if (!this.excludedPathAnchors.includes(a)) this.excludedPathAnchors.push(a);
|
|
1875
|
+
}
|
|
1876
|
+
if (this.excludedPathAnchors.length > MAX_ANCHORS) {
|
|
1877
|
+
this.excludedPathAnchors = this.excludedPathAnchors.slice(-MAX_ANCHORS);
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
/** spec 3c 动作 B 补强:设置会话当前目标(覆盖语义——目标只有一个)。
|
|
1881
|
+
* 与 appendExcludedPathAnchors 的追加语义不同:目标切换 = 整段替换,
|
|
1882
|
+
* 字节只在目标实际变化时变(goalChanged 判定由调用方负责)。 */
|
|
1883
|
+
setGoalAnchor(goal) {
|
|
1884
|
+
this.goalAnchor = goal;
|
|
1885
|
+
}
|
|
1886
|
+
/** 已收集锚点数(loop 惰性重建判空用)。 */
|
|
1887
|
+
getExcludedPathAnchorCount() {
|
|
1888
|
+
return this.excludedPathAnchors.length;
|
|
1889
|
+
}
|
|
1890
|
+
updatePlaybookLessons(lessons) {
|
|
1891
|
+
this.playbookLessons = lessons;
|
|
1892
|
+
}
|
|
1893
|
+
setOnLessonsRendered(cb) {
|
|
1894
|
+
this.onLessonsRendered = cb;
|
|
1895
|
+
}
|
|
1896
|
+
setTaskProgress(state) {
|
|
1897
|
+
this.taskProgress = state;
|
|
1898
|
+
}
|
|
1899
|
+
setRepairHint(hint) {
|
|
1900
|
+
this.repairHint = hint;
|
|
1901
|
+
}
|
|
1902
|
+
setToolContext(ctx) {
|
|
1903
|
+
this.toolContext = ctx ?? void 0;
|
|
1904
|
+
}
|
|
1905
|
+
getToolContextLength() {
|
|
1906
|
+
return this.toolContext?.length ?? 0;
|
|
1907
|
+
}
|
|
1908
|
+
setPlanCacheAdvisory(advisory) {
|
|
1909
|
+
this.planCacheAdvisory = advisory ?? void 0;
|
|
1910
|
+
}
|
|
1911
|
+
/** U6: set serialized PlanExecutionTrace appendix (rendered in dynamic appendix, survives compaction). */
|
|
1912
|
+
setPlanTraceAppendix(appendix) {
|
|
1913
|
+
this.planTraceAppendix = appendix ?? void 0;
|
|
1914
|
+
}
|
|
1915
|
+
/**
|
|
1916
|
+
* Set the approved-plan pointer (slug/title/path block). Rendered ONLY into
|
|
1917
|
+
* the dynamic appendix — does NOT rebuild the frozen base or invalidate the
|
|
1918
|
+
* fresh cache, so approving/revising a plan never shatters the prefix cache.
|
|
1919
|
+
* Mirrors setPlanModeState / setWorktreeReality: the pointer refreshes at the
|
|
1920
|
+
* next user-message boundary (which rebuilds the appendix anyway).
|
|
1921
|
+
*/
|
|
1922
|
+
setActivePlan(pointer) {
|
|
1923
|
+
this.activePlanPointer = pointer ?? void 0;
|
|
1924
|
+
}
|
|
1925
|
+
/** Return the currently approved-plan pointer (slug/title/path block), if any. */
|
|
1926
|
+
getActivePlanPointer() {
|
|
1927
|
+
return this.activePlanPointer;
|
|
1928
|
+
}
|
|
1929
|
+
setIntentRetrievalRoute(route) {
|
|
1930
|
+
this.intentRetrievalRoute = route ?? void 0;
|
|
1931
|
+
this.invalidateFreshCache();
|
|
1932
|
+
}
|
|
1933
|
+
setTaskDepthLayer(layer) {
|
|
1934
|
+
const changed = this.taskDepthLayer !== layer;
|
|
1935
|
+
this.taskDepthLayer = layer;
|
|
1936
|
+
this.taskDepthAdvisory = changed ? renderTaskDepthAdvisory(layer) : null;
|
|
1937
|
+
}
|
|
1938
|
+
setPlanMethodology(methodology, reason, opts) {
|
|
1939
|
+
const changed = this.planMethodology !== methodology || this.planMethodologyPlanMode !== !!opts?.planMode;
|
|
1940
|
+
this.planMethodology = methodology;
|
|
1941
|
+
this.planMethodologyReason = reason;
|
|
1942
|
+
this.planMethodologyPlanMode = !!opts?.planMode;
|
|
1943
|
+
this.planMethodologyAdvisory = changed ? renderPlanMethodologyAdvisory(methodology, reason, opts) : null;
|
|
1944
|
+
}
|
|
1945
|
+
setPlanExecutingBlock(block) {
|
|
1946
|
+
this.planExecutingBlock = block;
|
|
1947
|
+
}
|
|
1948
|
+
/** Zen Mode(禅模式):相位 zen 时裁剪 CVM 动态注入块(sensorium/策略/知识
|
|
1949
|
+
* 碎片/星域提醒/遥测),保留 git-status/recent-commits/项目指令/计划指针。
|
|
1950
|
+
* 由 loop 在相位变化时调用(arm → true;promote → false)。Cache-safe:
|
|
1951
|
+
* 只影响 dynamic appendix,不触碰冻结前缀。 */
|
|
1952
|
+
zenLean = false;
|
|
1953
|
+
setZenLean(lean) {
|
|
1954
|
+
this.zenLean = lean;
|
|
1955
|
+
}
|
|
1956
|
+
getPlanExecutingBlock() {
|
|
1957
|
+
return this.planExecutingBlock;
|
|
1958
|
+
}
|
|
1959
|
+
getPlanMethodology() {
|
|
1960
|
+
return this.planMethodology;
|
|
1961
|
+
}
|
|
1962
|
+
setSkillAdvisoryBlock(block) {
|
|
1963
|
+
this.skillAdvisoryBlock = block;
|
|
1964
|
+
}
|
|
1965
|
+
markSkillInvoked(name) {
|
|
1966
|
+
this.invokedSkillNames.add(name);
|
|
1967
|
+
}
|
|
1968
|
+
markSkillCompleted(name) {
|
|
1969
|
+
const canonical = skillRegistry.get(name)?.name ?? [...this.invokedSkillNames].find((n) => n.toLowerCase() === name.toLowerCase()) ?? name;
|
|
1970
|
+
this.invokedSkillNames.delete(canonical);
|
|
1971
|
+
}
|
|
1972
|
+
getInvokedSkillNames() {
|
|
1973
|
+
return [...this.invokedSkillNames];
|
|
1974
|
+
}
|
|
1975
|
+
setCrossSessionMemoryBlock(block) {
|
|
1976
|
+
this.crossSessionMemoryBlock = block;
|
|
1977
|
+
}
|
|
1978
|
+
setMentionContextBlock(block) {
|
|
1979
|
+
this.mentionContextBlock = block;
|
|
1980
|
+
}
|
|
1981
|
+
getTaskDepthLayer() {
|
|
1982
|
+
return this.taskDepthLayer;
|
|
1983
|
+
}
|
|
1984
|
+
/** Wave 4 控制面 appendix — 仅 active 模式由 turn-step-producer 调用。
|
|
1985
|
+
* 只影响 dynamic appendix(buildStableVolatileBlock 已剥离),不进 frozen、
|
|
1986
|
+
* 不改写历史消息。 */
|
|
1987
|
+
setControlPlaneAppendix(block) {
|
|
1988
|
+
this.controlPlaneBlock = block ?? void 0;
|
|
1989
|
+
}
|
|
1990
|
+
setHarnessAdvisoryBlock(block) {
|
|
1991
|
+
this.harnessAdvisoryBlock = block ?? void 0;
|
|
1992
|
+
}
|
|
1993
|
+
/**
|
|
1994
|
+
* When true, the next dynamic appendix render enables the terseness nudge in
|
|
1995
|
+
* escalate mode (doom-loop / storm). Cache-safe: dynamic appendix only.
|
|
1996
|
+
*/
|
|
1997
|
+
setTersenessEscalate(escalate) {
|
|
1998
|
+
this.tersenessEscalate = escalate;
|
|
1999
|
+
}
|
|
2000
|
+
setDecisions(decisions) {
|
|
2001
|
+
this.decisions = decisions;
|
|
2002
|
+
}
|
|
2003
|
+
setCrossSessionEvents(events) {
|
|
2004
|
+
this.crossSessionEvents = events ?? void 0;
|
|
2005
|
+
}
|
|
2006
|
+
setCompanionPresence(block) {
|
|
2007
|
+
this.companionPresence = block ?? void 0;
|
|
2008
|
+
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Update session-state snapshot. Byte-stable: only stores when the content
|
|
2011
|
+
* actually differs from the previously stored snapshot. This means within an
|
|
2012
|
+
* auto-continuation run where nothing changed (model only read files), the
|
|
2013
|
+
* appendix stays identical → DeepSeek prefix cache preserved. When files are
|
|
2014
|
+
* modified or task step advances, the state updates and the model sees it.
|
|
2015
|
+
* See: prompt/volatile.ts VolatileContext.sessionState comment.
|
|
2016
|
+
*/
|
|
2017
|
+
setSessionState(text) {
|
|
2018
|
+
const next = text ?? void 0;
|
|
2019
|
+
if (next === this.sessionStateText) return;
|
|
2020
|
+
this.sessionStateText = next;
|
|
2021
|
+
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Update worktree reality check result. Does NOT invalidate the fresh cache:
|
|
2024
|
+
* rendered ONLY into the dynamic appendix when severity !== 'green'.
|
|
2025
|
+
* This is required to preserve DeepSeek prefix cache across tool turns.
|
|
2026
|
+
*/
|
|
2027
|
+
setWorktreeReality(reality) {
|
|
2028
|
+
this.worktreeReality = reality ?? void 0;
|
|
2029
|
+
}
|
|
2030
|
+
/** Update plan-mode state — rendered into volatile block to instruct agent */
|
|
2031
|
+
setPlanModeState(state) {
|
|
2032
|
+
this.planModeState = state;
|
|
2033
|
+
}
|
|
2034
|
+
/** Update ask-mode state — rendered into volatile appendix (never frozen). */
|
|
2035
|
+
setAskModeState(state) {
|
|
2036
|
+
this.askModeState = state;
|
|
2037
|
+
}
|
|
2038
|
+
/** Update the approval mode — rendered into the dynamic appendix as a
|
|
2039
|
+
* permission note when unattended (never frozen). */
|
|
2040
|
+
setApprovalMode(mode) {
|
|
2041
|
+
this.approvalMode = mode;
|
|
2042
|
+
}
|
|
2043
|
+
/** Active plan file path for incremental plan writing in plan mode. */
|
|
2044
|
+
setActivePlanFilePath(path) {
|
|
2045
|
+
this.activePlanFilePath = path;
|
|
2046
|
+
}
|
|
2047
|
+
/** Arm/disarm the one-shot plan-mode exit reminder. */
|
|
2048
|
+
setPlanExitReminderPending(pending) {
|
|
2049
|
+
this.planExitReminderPending = pending;
|
|
2050
|
+
}
|
|
2051
|
+
setPhaseHint(hint) {
|
|
2052
|
+
this.phaseHint = hint;
|
|
2053
|
+
}
|
|
2054
|
+
getPhaseHint() {
|
|
2055
|
+
return this.phaseHint;
|
|
2056
|
+
}
|
|
2057
|
+
/**
|
|
2058
|
+
* Update cognitive projection. Does NOT invalidate the fresh cache:
|
|
2059
|
+
* within the same user message, all tool-call turns reuse the cached fresh
|
|
2060
|
+
* volatile block — projection refreshes only at user-message boundaries.
|
|
2061
|
+
* This preserves DeepSeek prefix cache across tool turns (~10% hit rate gain).
|
|
2062
|
+
*/
|
|
2063
|
+
setCognitiveProjection(projection, ephemeral) {
|
|
2064
|
+
this.cognitiveProjection = projection && projection.trim().length > 0 ? projection : void 0;
|
|
2065
|
+
this.cognitiveEphemeral = ephemeral && ephemeral.trim().length > 0 ? ephemeral : void 0;
|
|
2066
|
+
}
|
|
2067
|
+
/** Prepend per-turn ephemeral cognitive hints OUTSIDE the delta context-update.
|
|
2068
|
+
* cachedAppendix is frozen across a user message's tool turns, so this stays
|
|
2069
|
+
* stable within a turn sequence and only refreshes at the next user boundary.
|
|
2070
|
+
* These bytes ship in full every boundary (no delta), so they book in full. */
|
|
2071
|
+
withEphemeralProjection(appendix, chargeLedger) {
|
|
2072
|
+
if (!this.cognitiveEphemeral) return appendix;
|
|
2073
|
+
if (chargeLedger) this.chargeAppendixLedger("ephemeral", this.cognitiveEphemeral.length);
|
|
2074
|
+
return appendix ? `${this.cognitiveEphemeral}
|
|
2075
|
+
${appendix}` : this.cognitiveEphemeral;
|
|
2076
|
+
}
|
|
2077
|
+
invalidateFreshCache() {
|
|
2078
|
+
if (this.cachedFreshForUser !== "") {
|
|
2079
|
+
const err = new Error("invalidateFreshCache");
|
|
2080
|
+
debugLog(`[fresh-cache] CLEARED cachedFreshForUser="${this.cachedFreshForUser.slice(0, 80)}..." by: ${err.stack?.split("\n").slice(2, 5).join(" \u2192 ") ?? "unknown"}`);
|
|
2081
|
+
}
|
|
2082
|
+
this.cachedFreshForUser = "";
|
|
2083
|
+
this.cachedAppendix = "";
|
|
2084
|
+
this.cachedConsolidated = "";
|
|
2085
|
+
this.lastAppendixAnatomy = null;
|
|
2086
|
+
this.lastEmittedAppendixParts = /* @__PURE__ */ new Map();
|
|
2087
|
+
this.appendixBaselineSent = false;
|
|
2088
|
+
}
|
|
2089
|
+
/**
|
|
2090
|
+
* Build the <context-update> body — full when delta off or baseline not yet
|
|
2091
|
+
* sent, otherwise only changed sub-blocks. Mutates lastEmittedAppendixParts.
|
|
2092
|
+
*
|
|
2093
|
+
* Delta logic: compare current parts against last emitted. On new user
|
|
2094
|
+
* boundary, emit full baseline (seq=1). Subsequent boundaries emit only
|
|
2095
|
+
* changed sub-blocks (mode="delta"), or self-closing tag if nothing changed.
|
|
2096
|
+
* Tool-call turns reuse cachedAppendix (never calling this method).
|
|
2097
|
+
*
|
|
2098
|
+
* `chargeLedger` must be false for hermetic side-path builds: their appendix
|
|
2099
|
+
* bytes never reach the main history, so booking them would re-introduce the
|
|
2100
|
+
* overcounting this metering exists to avoid.
|
|
2101
|
+
*/
|
|
2102
|
+
buildAppendixBody(ctx, maxChars, chargeLedger) {
|
|
2103
|
+
const parts = buildDynamicAppendixParts(ctx, maxChars);
|
|
2104
|
+
this.lastAppendixAnatomy = summarizeAppendixParts(parts);
|
|
2105
|
+
if (!this.config.appendixDelta) {
|
|
2106
|
+
if (parts.length === 0) return "";
|
|
2107
|
+
this.chargeAppendixParts(parts, chargeLedger);
|
|
2108
|
+
return `<context-update>
|
|
2109
|
+
${parts.map((p) => p.content).join("\n\n")}
|
|
2110
|
+
</context-update>`;
|
|
2111
|
+
}
|
|
2112
|
+
this.appendixSeq++;
|
|
2113
|
+
const current = /* @__PURE__ */ new Map();
|
|
2114
|
+
const changed = [];
|
|
2115
|
+
for (const p of parts) {
|
|
2116
|
+
current.set(p.name, p.content);
|
|
2117
|
+
if (this.lastEmittedAppendixParts.get(p.name) !== p.content) changed.push(p);
|
|
2118
|
+
}
|
|
2119
|
+
const sendFull = !this.appendixBaselineSent;
|
|
2120
|
+
this.lastEmittedAppendixParts = current;
|
|
2121
|
+
this.appendixBaselineSent = true;
|
|
2122
|
+
if (sendFull) {
|
|
2123
|
+
if (parts.length === 0) return "";
|
|
2124
|
+
this.chargeAppendixParts(parts, chargeLedger);
|
|
2125
|
+
return `<context-update seq="${this.appendixSeq}">
|
|
2126
|
+
${parts.map((p) => p.content).join("\n\n")}
|
|
2127
|
+
</context-update>`;
|
|
2128
|
+
}
|
|
2129
|
+
if (changed.length === 0) return `<context-update seq="${this.appendixSeq}"/>`;
|
|
2130
|
+
this.chargeAppendixParts(changed, chargeLedger);
|
|
2131
|
+
return `<context-update seq="${this.appendixSeq}" mode="delta">
|
|
2132
|
+
${changed.map((p) => p.content).join("\n\n")}
|
|
2133
|
+
</context-update>`;
|
|
2134
|
+
}
|
|
2135
|
+
/** Book the chars of appendix sub-blocks that carry a CVM source tag. */
|
|
2136
|
+
chargeAppendixLedger(source, chars) {
|
|
2137
|
+
if (chars <= 0) return;
|
|
2138
|
+
this.appendixLedger.set(source, (this.appendixLedger.get(source) ?? 0) + chars);
|
|
2139
|
+
}
|
|
2140
|
+
chargeAppendixParts(emitted, chargeLedger) {
|
|
2141
|
+
if (!chargeLedger) return;
|
|
2142
|
+
for (const p of emitted) {
|
|
2143
|
+
if (p.source) this.chargeAppendixLedger(p.source, p.content.length);
|
|
2144
|
+
}
|
|
2145
|
+
}
|
|
2146
|
+
/**
|
|
2147
|
+
* Hand over the CVM egress booked since the last call and clear it.
|
|
2148
|
+
*
|
|
2149
|
+
* Consume-once on purpose. The alternative — hanging the ledger off the
|
|
2150
|
+
* OaiChatRequest — would leak through `{...request}` spreads (llm-speculation)
|
|
2151
|
+
* and be replayed verbatim by FallbackStreamClient failover, double-charging
|
|
2152
|
+
* every retried turn. Same class of bug as the prefixProbe poisoning of 2026-07-06.
|
|
2153
|
+
*/
|
|
2154
|
+
drainAppendixLedger() {
|
|
2155
|
+
if (this.appendixLedger.size === 0) return [];
|
|
2156
|
+
const rows = [...this.appendixLedger].map(([source, chars]) => ({ source, chars }));
|
|
2157
|
+
this.appendixLedger.clear();
|
|
2158
|
+
return rows;
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Mark git status as dirty — next user-message boundary will use live gitStatusCache
|
|
2162
|
+
* instead of the frozen session-start snapshot. Call after file-modifying tools
|
|
2163
|
+
* (Write, Edit, Bash with git commands) to keep multi-session git state fresh.
|
|
2164
|
+
*/
|
|
2165
|
+
markGitDirty() {
|
|
2166
|
+
this.gitDirty = true;
|
|
2167
|
+
}
|
|
2168
|
+
/**
|
|
2169
|
+
* Force the next context-update to be a full baseline. Call after history
|
|
2170
|
+
* rewrite/compaction drops messages carrying prior context-update blocks —
|
|
2171
|
+
* the model needs a fresh full snapshot because delta's "absent = unchanged"
|
|
2172
|
+
* semantics rely on the history still being present.
|
|
2173
|
+
*
|
|
2174
|
+
* The same event invalidates CVM accounting: the appendix bytes booked so far
|
|
2175
|
+
* just left the context. Registered listeners (the pressure monitor) are
|
|
2176
|
+
* notified here rather than at each of the ten call sites, so the two cannot
|
|
2177
|
+
* drift apart.
|
|
2178
|
+
*/
|
|
2179
|
+
resetAppendixBaseline() {
|
|
2180
|
+
this.lastEmittedAppendixParts = /* @__PURE__ */ new Map();
|
|
2181
|
+
this.appendixBaselineSent = false;
|
|
2182
|
+
this.appendixLedger.clear();
|
|
2183
|
+
this.onResetAppendixBaselineCb?.();
|
|
2184
|
+
}
|
|
2185
|
+
/** Register the CVM accounting reset that must accompany a baseline reset. */
|
|
2186
|
+
setOnResetAppendixBaseline(fn) {
|
|
2187
|
+
this.onResetAppendixBaselineCb = fn;
|
|
2188
|
+
}
|
|
2189
|
+
/**
|
|
2190
|
+
* Cache-event counters for cache-log breadcrumbs (P2-6).
|
|
2191
|
+
* Cumulative since engine creation — callers diff across turns.
|
|
2192
|
+
*/
|
|
2193
|
+
getCacheEventStats() {
|
|
2194
|
+
return {
|
|
2195
|
+
volatileSwaps: this.volatileSwapCount,
|
|
2196
|
+
frozenClamps: this.frozenSnapshotClamps,
|
|
2197
|
+
frozenFallbackRebuilds: this.frozenFallbackRebuilds,
|
|
2198
|
+
collapseWatermark: this.collapseWatermark,
|
|
2199
|
+
toolsUpdates: this.toolsUpdateCount
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
/**
|
|
2203
|
+
* Pre-refresh git status cache when stale. Call in async context before
|
|
2204
|
+
* buildOaiRequest() to ensure the model sees fresh git state instead of the
|
|
2205
|
+
* up-to-30s-old cached value returned by the synchronous get() path.
|
|
2206
|
+
*/
|
|
2207
|
+
async refreshGitContextIfNeeded(cwd) {
|
|
2208
|
+
const { gitStatusCache } = await import("./volatile-git-MBTST7RA.js");
|
|
2209
|
+
await gitStatusCache.refreshIfStale(cwd);
|
|
2210
|
+
}
|
|
2211
|
+
setActiveDomain(domain) {
|
|
2212
|
+
this.activeDomain = domain;
|
|
2213
|
+
this.rebuildFrozenBase();
|
|
2214
|
+
}
|
|
2215
|
+
getVolatilePayloadReport(toolHistory) {
|
|
2216
|
+
const latest = buildLatestTurnVolatileBlock({
|
|
2217
|
+
...this.config.volatileCtx,
|
|
2218
|
+
toolHistory,
|
|
2219
|
+
taskProgress: this.taskProgress,
|
|
2220
|
+
toolContext: this.toolContext,
|
|
2221
|
+
planCacheAdvisory: this.planCacheAdvisory,
|
|
2222
|
+
planTraceAppendix: this.planTraceAppendix,
|
|
2223
|
+
intentRetrievalRoute: this.intentRetrievalRoute,
|
|
2224
|
+
taskDepthAdvisory: this.taskDepthAdvisory,
|
|
2225
|
+
planMethodologyAdvisory: this.planMethodologyAdvisory,
|
|
2226
|
+
harnessAdvisoryBlock: this.harnessAdvisoryBlock,
|
|
2227
|
+
decisions: this.decisions,
|
|
2228
|
+
activeDomain: this.activeDomain ?? this.config.volatileCtx.activeDomain,
|
|
2229
|
+
activeClaims: this.activeClaims ?? this.config.volatileCtx.activeClaims,
|
|
2230
|
+
playbookLessons: this.playbookLessons ?? this.config.volatileCtx.playbookLessons,
|
|
2231
|
+
sessionMemoryBlock: this.sessionMemoryOverride ?? this.config.volatileCtx.sessionMemoryBlock,
|
|
2232
|
+
sessionState: this.sessionStateText,
|
|
2233
|
+
worktreeReality: this.worktreeReality
|
|
2234
|
+
});
|
|
2235
|
+
return analyzeVolatilePayload(latest);
|
|
2236
|
+
}
|
|
2237
|
+
getContextLayerReport() {
|
|
2238
|
+
return this.contextLayerReportData;
|
|
2239
|
+
}
|
|
2240
|
+
};
|
|
2241
|
+
function computeCollapseBoundary(messages, collapseAge) {
|
|
2242
|
+
let currentTurn = 0;
|
|
2243
|
+
for (const m of messages) {
|
|
2244
|
+
if (m.role === "user") currentTurn++;
|
|
2245
|
+
}
|
|
2246
|
+
let turnCounter = 0;
|
|
2247
|
+
let boundary = 0;
|
|
2248
|
+
for (let i = 0; i < messages.length; i++) {
|
|
2249
|
+
if (messages[i].role === "user") turnCounter++;
|
|
2250
|
+
if (currentTurn - turnCounter >= collapseAge) boundary = i + 1;
|
|
2251
|
+
}
|
|
2252
|
+
return boundary;
|
|
2253
|
+
}
|
|
2254
|
+
function requestTimeCollapse(messages, boundaryIndex, contextWindow, lightOnly = false) {
|
|
2255
|
+
let currentTurn = 0;
|
|
2256
|
+
for (const m of messages) {
|
|
2257
|
+
if (m.role === "user") currentTurn++;
|
|
2258
|
+
}
|
|
2259
|
+
const end = Math.min(boundaryIndex, messages.length);
|
|
2260
|
+
const toolCallIndex = buildToolCallIndex(messages, end);
|
|
2261
|
+
const toolOccurrences = /* @__PURE__ */ new Map();
|
|
2262
|
+
for (let i = 0; i < end; i++) {
|
|
2263
|
+
const msg = messages[i];
|
|
2264
|
+
if (msg.role !== "tool" || msg.content.length < 200) continue;
|
|
2265
|
+
if (msg.content.startsWith("[collapsed ") || msg.content.startsWith("[storm-collapsed") || msg.content.startsWith("[tiered-")) continue;
|
|
2266
|
+
const toolName = toolNameFromIndex(msg, toolCallIndex);
|
|
2267
|
+
if (toolName === "grep" || toolName === "search" || toolName === "read_file") {
|
|
2268
|
+
const target = targetFromIndex(msg, toolCallIndex);
|
|
2269
|
+
if (target) {
|
|
2270
|
+
const key = `${toolName}:${target}`;
|
|
2271
|
+
const indices = toolOccurrences.get(key);
|
|
2272
|
+
if (indices) indices.push(i);
|
|
2273
|
+
else toolOccurrences.set(key, [i]);
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
const superseded = /* @__PURE__ */ new Set();
|
|
2278
|
+
for (const indices of toolOccurrences.values()) {
|
|
2279
|
+
if (indices.length > 1) {
|
|
2280
|
+
for (let k = 0; k < indices.length - 1; k++) superseded.add(indices[k]);
|
|
2281
|
+
}
|
|
2282
|
+
}
|
|
2283
|
+
let turnCounter = 0;
|
|
2284
|
+
for (let i = 0; i < end; i++) {
|
|
2285
|
+
const msg = messages[i];
|
|
2286
|
+
if (msg.role === "user") turnCounter++;
|
|
2287
|
+
if (msg.role === "assistant" && "reasoning_content" in msg && msg.reasoning_content) {
|
|
2288
|
+
const { reasoning_content: _dropped, ...rest } = msg;
|
|
2289
|
+
messages[i] = rest.content == null && !rest.tool_calls?.length ? { ...rest, content: "" } : rest;
|
|
2290
|
+
continue;
|
|
2291
|
+
}
|
|
2292
|
+
if (msg.role !== "tool") continue;
|
|
2293
|
+
if (msg.content.length < 200) continue;
|
|
2294
|
+
if (msg.content.startsWith("[collapsed ") || msg.content.startsWith("[storm-collapsed") || msg.content.startsWith("[tiered-")) continue;
|
|
2295
|
+
const toolName = toolNameFromIndex(msg, toolCallIndex);
|
|
2296
|
+
if (superseded.has(i)) {
|
|
2297
|
+
const target = targetFromIndex(msg, toolCallIndex);
|
|
2298
|
+
messages[i] = { ...msg, content: `[collapsed ${toolName}: superseded by later ${toolName} on ${target ?? "same target"}]` };
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
if (lightOnly) continue;
|
|
2302
|
+
const turnAge = currentTurn - turnCounter;
|
|
2303
|
+
const collapsed = collapseToolResult(toolName, msg.content, turnAge, contextWindow);
|
|
2304
|
+
if (collapsed) {
|
|
2305
|
+
messages[i] = { ...msg, content: collapsed.summary };
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
function buildToolCallIndex(messages, end) {
|
|
2310
|
+
const index = /* @__PURE__ */ new Map();
|
|
2311
|
+
for (let i = 0; i < end; i++) {
|
|
2312
|
+
const msg = messages[i];
|
|
2313
|
+
if (msg.role !== "assistant" || !msg.tool_calls?.length) continue;
|
|
2314
|
+
for (const call of msg.tool_calls) {
|
|
2315
|
+
let target = null;
|
|
2316
|
+
if (call.function.name === "grep" || call.function.name === "search") {
|
|
2317
|
+
target = parseCallTarget(call.function.arguments, ["pattern", "query"]);
|
|
2318
|
+
} else if (call.function.name === "read_file") {
|
|
2319
|
+
target = parseCallTarget(call.function.arguments, ["path", "file"]);
|
|
2320
|
+
}
|
|
2321
|
+
index.set(call.id, { name: call.function.name, target });
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return index;
|
|
2325
|
+
}
|
|
2326
|
+
function parseCallTarget(argumentsJson, keys) {
|
|
2327
|
+
try {
|
|
2328
|
+
const args = JSON.parse(argumentsJson);
|
|
2329
|
+
for (const key of keys) {
|
|
2330
|
+
const value = args[key];
|
|
2331
|
+
if (typeof value === "string") return value;
|
|
2332
|
+
}
|
|
2333
|
+
} catch {
|
|
2334
|
+
}
|
|
2335
|
+
return null;
|
|
2336
|
+
}
|
|
2337
|
+
function toolNameFromIndex(msg, index) {
|
|
2338
|
+
const toolCallId = msg.tool_call_id;
|
|
2339
|
+
if (!toolCallId) return "unknown";
|
|
2340
|
+
return index.get(toolCallId)?.name ?? "unknown";
|
|
2341
|
+
}
|
|
2342
|
+
function targetFromIndex(msg, index) {
|
|
2343
|
+
const toolCallId = msg.tool_call_id;
|
|
2344
|
+
if (!toolCallId) return null;
|
|
2345
|
+
return index.get(toolCallId)?.target ?? null;
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
export {
|
|
2349
|
+
buildSystemPrompt,
|
|
2350
|
+
detectModelFamily,
|
|
2351
|
+
formatVolatilePayloadReport,
|
|
2352
|
+
PromptEngine
|
|
2353
|
+
};
|