@thincoder/core 0.10.8 → 0.10.10
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/CHANGELOG.md +6 -0
- package/advisor/messages.mjs +4 -5
- package/advisor/project-context.mjs +69 -44
- package/agent/family-tools.mjs +11 -9
- package/agent/helpers.mjs +12 -6
- package/agent/post-turn.mjs +34 -0
- package/agent/run-stages.mjs +43 -9
- package/agent/run-start.mjs +1 -0
- package/agent/setup.mjs +10 -2
- package/agent/turn-loop.mjs +23 -13
- package/agent-tools/audit-block.mjs +4 -2
- package/agent-tools/batch-gate.mjs +197 -0
- package/agent-tools/batch-lifecycle.mjs +216 -36
- package/agent-tools/batch-skeleton.mjs +87 -0
- package/agent-tools/batch.mjs +11 -3
- package/agent-tools/checkpoint.mjs +11 -6
- package/agent-tools/context.mjs +28 -5
- package/agent-tools/design-slots.mjs +7 -33
- package/agent-tools/doc-check.mjs +50 -7
- package/agent-tools/manifest.mjs +44 -12
- package/agent-tools/spawn-gates.mjs +100 -41
- package/agent-tools/ssh-session.mjs +108 -61
- package/agent-tools/ssh-transport.mjs +17 -4
- package/agent-tools/ssh-vault.mjs +291 -0
- package/agent-tools/subagent-spawn.mjs +52 -30
- package/agent-tools/subagent.mjs +14 -8
- package/agent-tools/timer.mjs +2 -2
- package/agent-tools.mjs +1 -1
- package/config-defaults.mjs +92 -0
- package/config-normalize.mjs +109 -0
- package/config.mjs +88 -197
- package/declaration.mjs +5 -12
- package/doc-check/anchors.mjs +68 -34
- package/doc-check/index.mjs +143 -32
- package/doc-check/version.mjs +84 -0
- package/doc-check/width.mjs +10 -54
- package/i18n.mjs +7 -0
- package/ledger-cmd.mjs +314 -61
- package/ledger-db.mjs +22 -10
- package/ledger-migrate.mjs +15 -9
- package/ledger-tools.mjs +115 -69
- package/ledger.mjs +13 -10
- package/log.mjs +2 -2
- package/manifest-schema.mjs +7 -21
- package/manifest.mjs +4 -4
- package/memory/code-search.mjs +5 -14
- package/memory/docs.mjs +5 -14
- package/memory/read-gate.mjs +26 -0
- package/package.json +1 -1
- package/permission.mjs +7 -1
- package/prompts/advisor-design.md +95 -44
- package/prompts/advisor-round1.md +75 -27
- package/prompts/advisor-round2.md +68 -31
- package/prompts/advisor-round3.md +77 -33
- package/prompts/common.md +283 -113
- package/prompts/consult-base.md +16 -9
- package/prompts/discipline-engineering.md +213 -83
- package/prompts/discipline-normal.md +137 -85
- package/prompts/persona-coder.md +8 -6
- package/prompts/persona-eng-coder.md +42 -21
- package/prompts/persona-eng-designer.md +101 -43
- package/prompts/persona-engineering.md +376 -127
- package/prompts/persona-explore.md +3 -3
- package/prompts/persona-normal.md +29 -21
- package/prompts/persona-plan.md +7 -7
- package/prompts/subagent-base.md +4 -4
- package/provider/anthropic.mjs +4 -2
- package/provider/core.mjs +4 -2
- package/provider/google.mjs +5 -1
- package/provider/responses-request.mjs +3 -2
- package/session-lifecycle.mjs +6 -6
- package/team.mjs +2 -1
- package/token-window.mjs +1 -1
- package/tool-docs/batch.md +1 -1
- package/tool-docs/doc_check.md +47 -12
- package/tool-docs/fetch.md +3 -2
- package/tool-docs/ledger.md +7 -5
- package/tool-docs/manifest-read.md +8 -0
- package/tool-docs/manifest.md +9 -8
- package/tool-docs/ssh.md +23 -6
- package/tool-docs/subagent.md +7 -12
- package/tool-docs/websearch.md +3 -1
- package/tools/ssh.mjs +24 -11
- package/tools/web-deepseek.mjs +172 -0
- package/tools/web.mjs +29 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,12 @@ Format: Keep a Changelog · 中文 · 号在发布时定(CalVer——见 `docs
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.10.9] — 2026-10-19
|
|
9
|
+
|
|
10
|
+
### Removed
|
|
11
|
+
|
|
12
|
+
- **`design-slots` 的 `reveal` 开关与掩蔽(token-visibility-simplify 批 2026-10-17)**:清点面改**直读**——逐槽行恒携 `designToken`(过期照列、`status` 自标态;记录畸形 ⇒ `—`);删净 `reveal` 开关 / 掩蔽 / 一次一槽限制(用户裁定「没必要把 token 藏起来,agent 要就告诉他」);签发链(mint / settle / TTL / persist)零改。
|
|
13
|
+
|
|
8
14
|
## [0.10.8] — 2026-10-16
|
|
9
15
|
|
|
10
16
|
> 0.10.7 → 0.10.8(月内 +1——发布时定号)
|
package/advisor/messages.mjs
CHANGED
|
@@ -155,15 +155,14 @@ function buildAdvisorUserMessageInner(agent, prior, reviewType, designToken = nu
|
|
|
155
155
|
}
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
// Engineering mode: the project
|
|
159
|
-
//
|
|
158
|
+
// Engineering mode: the project standards section — the declaration face is gone with the
|
|
159
|
+
// manifest `advisor` key, so this is always an explicit degradation (PO-2).
|
|
160
160
|
if (agent.config?.agent?.engineering) {
|
|
161
161
|
injectProjectStandards(agent, parts, guideRoot)
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
-
// Document map:
|
|
165
|
-
//
|
|
166
|
-
// (never the old silent skip).
|
|
164
|
+
// Document map: derived from the manifest `docRoot` (declared roots' README, then their common
|
|
165
|
+
// ancestor); neither → explicit degradation sentence (never a silent skip).
|
|
167
166
|
injectDocumentMap(agent, parts, guideRoot ?? agent.cwd)
|
|
168
167
|
|
|
169
168
|
parts.push("## Instructions")
|
|
@@ -12,13 +12,14 @@
|
|
|
12
12
|
* absent — in a project with a different layout the reviewer still scored the
|
|
13
13
|
* dimension it could no longer check (silent failure, the worst class: invisible
|
|
14
14
|
* and unfixable by the user). Missing pieces now say so, and the reviewer is told
|
|
15
|
-
* to state the limitation in its findings.
|
|
16
|
-
*
|
|
15
|
+
* to state the limitation in its findings. The document map is derived from the
|
|
16
|
+
* manifest's `docRoot` (the one place that says where the docs live); the standards
|
|
17
|
+
* face has no derivable convention name and is therefore an explicit degradation.
|
|
17
18
|
*/
|
|
18
19
|
import { readFileSync, existsSync } from "node:fs"
|
|
19
20
|
import { resolve, join, relative, dirname, sep } from "node:path"
|
|
20
21
|
import { providerSpec } from "../config.mjs"
|
|
21
|
-
import {
|
|
22
|
+
import { DEFAULT_MANIFEST, docRootPaths, readManifest } from "../manifest.mjs"
|
|
22
23
|
|
|
23
24
|
/** Project guide (AGENTS.md) injection budget — decision 2026-08-08:
|
|
24
25
|
* NO fixed truncation; long-context models (1M+) get up to 5% of their context
|
|
@@ -32,8 +33,8 @@ const PROJECT_GUIDE_FRACTION = 0.05 // 5% of the reviewer model's context window
|
|
|
32
33
|
export const NO_GUIDE_NOTICE = "(No AGENTS.md found — neither at the working directory root nor in any review-scope subdirectory. Judge the user's requirements from the conversation background, and say so explicitly if the requirements are unclear.)"
|
|
33
34
|
|
|
34
35
|
/** Explicit degradation sentences (PORTABILITY design §4.4 — verbatim). */
|
|
35
|
-
export const NO_DOC_MAP_NOTICE = "(No document map found under the
|
|
36
|
-
export const NO_STANDARDS_NOTICE = "(No project standards document
|
|
36
|
+
export const NO_DOC_MAP_NOTICE = "(No document map found — no README.md under the declared document roots and none at their common ancestor — the Document ownership criterion is degraded: check placement against the Project Guide where present, and state the limitation in your findings.)"
|
|
37
|
+
export const NO_STANDARDS_NOTICE = "(No project standards document — nothing in the project declares one — judge methodology compliance from the Project Guide (when present) and the review criteria above; state the limitation in your findings.)"
|
|
37
38
|
export const NO_GIT_NOTICE = "(No git repository detected — change-set context is unavailable; read the review-scope files directly.)"
|
|
38
39
|
|
|
39
40
|
/**
|
|
@@ -126,25 +127,66 @@ export function injectProjectGuide(agent, parts, scopeFiles = []) {
|
|
|
126
127
|
return root // guide injected — requirement-fit criteria apply (truthy root)
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
/** `docRoot` probe order: the face a reviewer needs first (design) → down to the batch records. */
|
|
131
|
+
const DOC_ROOT_ORDER = ["design", "requirements", "specs", "modules", "batches"]
|
|
132
|
+
|
|
133
|
+
/** Declared document roots as ABSOLUTE paths, in declaration order (`null` / missing face ⇒ no root). */
|
|
134
|
+
function declaredDocRoots(base) {
|
|
135
|
+
const read = readManifest(base)
|
|
136
|
+
const docRoot = (read.ok && read.manifest ? read.manifest : DEFAULT_MANIFEST).docRoot ?? {}
|
|
137
|
+
const out = []
|
|
138
|
+
for (const key of DOC_ROOT_ORDER) {
|
|
139
|
+
for (const p of docRootPaths(docRoot[key], base)) if (!out.includes(p)) out.push(p)
|
|
140
|
+
}
|
|
141
|
+
return out
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/** Common ancestor of every root (segment-wise — `docs/design` and `docs/designX` are not siblings).
|
|
145
|
+
* A lone drive root is no map home ⇒ null. */
|
|
146
|
+
function commonAncestor(paths) {
|
|
147
|
+
if (paths.length === 0) return null
|
|
148
|
+
const split = (p) => p.split(/[\\/]+/)
|
|
149
|
+
let acc = split(paths[0])
|
|
150
|
+
for (const p of paths.slice(1)) {
|
|
151
|
+
const segs = split(p)
|
|
152
|
+
let i = 0
|
|
153
|
+
while (i < acc.length && i < segs.length && acc[i] === segs[i]) i++
|
|
154
|
+
acc = acc.slice(0, i)
|
|
155
|
+
}
|
|
156
|
+
if (acc.length === 0) return null
|
|
157
|
+
const joined = acc.join(sep)
|
|
158
|
+
return /^[A-Za-z]:$/.test(joined) ? null : joined
|
|
159
|
+
}
|
|
160
|
+
|
|
129
161
|
/**
|
|
130
|
-
*
|
|
131
|
-
* (
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
162
|
+
* Discover the document map from the manifest's `docRoot` — a pure fs probe, no index generation
|
|
163
|
+
* (the map is a hand-maintained register; a generated one would be a second index, and a second
|
|
164
|
+
* index is exactly the duplicate pointer the retired declaration face used to create).
|
|
165
|
+
* Ladder: ① `README.md` under each declared doc root (declaration order) ② `README.md` at the common
|
|
166
|
+
* ancestor of every declared root. Neither ⇒ null (caller injects `NO_DOC_MAP_NOTICE` — no silent skip).
|
|
167
|
+
* @param {string} base project root (or any dir inside it — `docRoot` resolves against the project root)
|
|
168
|
+
* @returns {string|null} absolute path of the map file, or null when degraded
|
|
169
|
+
*/
|
|
170
|
+
export function findDocumentMap(base) {
|
|
171
|
+
const roots = declaredDocRoots(base)
|
|
172
|
+
for (const r of roots) {
|
|
173
|
+
const p = join(r, "README.md")
|
|
174
|
+
if (existsSync(p)) return p
|
|
175
|
+
}
|
|
176
|
+
const ancestor = commonAncestor(roots)
|
|
177
|
+
if (!ancestor) return null
|
|
178
|
+
const p = join(ancestor, "README.md")
|
|
179
|
+
return existsSync(p) ? p : null
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Inject the project document map (PO-1), derived from `docRoot`. Degrades visibly: the reviewer
|
|
184
|
+
* must know the Document-ownership criterion is unchecked — never a silent skip.
|
|
135
185
|
* @returns {string|null} the injected map path, or null when degraded
|
|
136
186
|
*/
|
|
137
187
|
export function injectDocumentMap(agent, parts, root) {
|
|
138
188
|
const base = root ?? agent.cwd
|
|
139
|
-
const
|
|
140
|
-
let mapPath = null
|
|
141
|
-
if (conv.advisor.docMap) {
|
|
142
|
-
const declared = resolve(base, conv.advisor.docMap)
|
|
143
|
-
if (existsSync(declared)) mapPath = declared
|
|
144
|
-
} else {
|
|
145
|
-
mapPath = [resolve(base, "docs", "README.md"), resolve(base, "docs", "design", "README.md")]
|
|
146
|
-
.find((p) => existsSync(p)) ?? null
|
|
147
|
-
}
|
|
189
|
+
const mapPath = findDocumentMap(base)
|
|
148
190
|
parts.push("## Document Map")
|
|
149
191
|
let text = null
|
|
150
192
|
if (mapPath) {
|
|
@@ -166,32 +208,15 @@ export function injectDocumentMap(agent, parts, root) {
|
|
|
166
208
|
}
|
|
167
209
|
|
|
168
210
|
/**
|
|
169
|
-
* Inject the project standards
|
|
170
|
-
*
|
|
171
|
-
* `METHODOLOGY.md` probe
|
|
172
|
-
*
|
|
173
|
-
* @returns {
|
|
211
|
+
* Inject the project standards section (PO-2). The declaration face is gone with the manifest
|
|
212
|
+
* `advisor` key, and no standards file name is derivable from what the project does declare (the old
|
|
213
|
+
* `METHODOLOGY.md` probe was deleted exactly because most projects have none). The section stays so the
|
|
214
|
+
* reviewer is TOLD the dimension is unchecked — visible degradation, not a silent skip.
|
|
215
|
+
* @returns {null} always degraded
|
|
174
216
|
*/
|
|
175
|
-
export function injectProjectStandards(
|
|
176
|
-
const base = root ?? agent.cwd
|
|
177
|
-
const conv = loadProjectDeclaration(agent.cwd)
|
|
178
|
-
const declared = conv.advisor.standardsDoc ? resolve(base, conv.advisor.standardsDoc) : null
|
|
217
|
+
export function injectProjectStandards(_agent, parts, _root) {
|
|
179
218
|
parts.push("## Project Standards")
|
|
180
|
-
|
|
181
|
-
if (declared && existsSync(declared)) {
|
|
182
|
-
try {
|
|
183
|
-
text = readFileSync(declared, "utf8")
|
|
184
|
-
} catch (e) {
|
|
185
|
-
console.warn(`[advisor] project standards unreadable at ${declared}: ${e.message}`)
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
if (text === null) {
|
|
189
|
-
parts.push(NO_STANDARDS_NOTICE) // degraded — visible by contract
|
|
190
|
-
parts.push("")
|
|
191
|
-
return null
|
|
192
|
-
}
|
|
193
|
-
parts.push("The project declares these standards for this review. Judge the design/changes against them:")
|
|
194
|
-
parts.push(text)
|
|
219
|
+
parts.push(NO_STANDARDS_NOTICE) // degraded — visible by contract
|
|
195
220
|
parts.push("")
|
|
196
|
-
return
|
|
221
|
+
return null
|
|
197
222
|
}
|
package/agent/family-tools.mjs
CHANGED
|
@@ -26,7 +26,7 @@ export async function assembleFamilyTools({
|
|
|
26
26
|
decorate = null, // object 端差面:{ subagent?, consultStart?, consultStop?, settings? }
|
|
27
27
|
} = {}) {
|
|
28
28
|
// CORE-UNIFICATION TOOLS #83:consult 家族随统一登记册自 `../agent-tools.mjs` 取用(单一来源)
|
|
29
|
-
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, contextTool, batchTool, manifestTool, consultStartTool, consultStopTool, parentChannelTool, docCheckTool } = await import("../agent-tools.mjs")
|
|
29
|
+
const { planTool, subagentTool, taskTool, skillTool, goalTool, verifyTool, recentChangesTool, timerTool, advisorTool, engTool, readHistoryTool, contextTool, batchTool, manifestTool, manifestReadTool, consultStartTool, consultStopTool, parentChannelTool, docCheckTool } = await import("../agent-tools.mjs")
|
|
30
30
|
// 台账统一入口(2026-10-05 统一入口批——台账 #923):全量变体 = depth-0 段 ∥ 只读变体 = 全部
|
|
31
31
|
// depth>0 段。动态 import——ledger 链静态达 node:sqlite(W8 契约②);两变体同名单对象、schema 窄化。
|
|
32
32
|
const { ledgerTool, ledgerReadTool } = await import("../ledger.mjs")
|
|
@@ -125,8 +125,8 @@ export async function assembleFamilyTools({
|
|
|
125
125
|
...subagentTool,
|
|
126
126
|
name: "subagent",
|
|
127
127
|
description: designer
|
|
128
|
-
? "Spawn a read-only `explore` sub-agent to SURVEY the current state for the design: it reads code / docs / existing designs and reports evidence with file:line. BLOCKING ONLY (no async) and action:'spawn' ONLY — the survey channel is read-only; escalate/status/cancel/panel/consume-design/observe/send are not available. Survey budget: ≤6 explore spawns per batch — the main agent's survey result is reference only; do your own."
|
|
129
|
-
: "Spawn a read-only `explore` sub-agent to AUDIT your delivery against the design: it compares the delivered code with the design for divergence — partially implemented acceptance criteria, silent simplifications, doc drift, changes outside the approved file list. BLOCKING ONLY (no async — the audit report decides your next protocol step). action:'spawn' ONLY — the audit channel is a read-only spawn; escalate/status/cancel/panel/consume-design/observe/send are not available. The audit task book is appended MECHANICALLY — your own spawn task (docs involved / acceptance criteria / file list) plus the files you actually touched; never hand the audit a self-written file list (a self-report could omit exactly the out-of-scope file it must catch).",
|
|
128
|
+
? "Spawn a read-only `explore` sub-agent to SURVEY the current state for the design: it reads code / docs / existing designs and reports evidence with file:line. BLOCKING ONLY (no async) and action:'spawn' ONLY — the survey channel is read-only; escalate/status/cancel/panel/consume-design/observe/send are not available. Survey budget: ≤6 explore spawns per batch — the main agent's survey result is reference only; do your own. The spawn carries the five task-book parameters (four core: goal / knownFacts / acceptance / reportFormat + `scope`); a task-only spawn is refused."
|
|
129
|
+
: "Spawn a read-only `explore` sub-agent to AUDIT your delivery against the design: it compares the delivered code with the design for divergence — partially implemented acceptance criteria, silent simplifications, doc drift, changes outside the approved file list. BLOCKING ONLY (no async — the audit report decides your next protocol step). action:'spawn' ONLY — the audit channel is a read-only spawn; escalate/status/cancel/panel/consume-design/observe/send are not available. The audit task book is appended MECHANICALLY — your own spawn task (docs involved / acceptance criteria / file list) plus the files you actually touched; never hand the audit a self-written file list (a self-report could omit exactly the out-of-scope file it must catch). The spawn carries the five task-book parameters (four core: goal / knownFacts / acceptance / reportFormat + `scope`); a task-only spawn is refused.",
|
|
130
130
|
parameters: { ...subagentTool.parameters, properties: props },
|
|
131
131
|
}
|
|
132
132
|
})()
|
|
@@ -149,7 +149,7 @@ export async function assembleFamilyTools({
|
|
|
149
149
|
// 团队台账全量变体(五 action——同律:子代理段只挂读变体;登录态门在工具内)
|
|
150
150
|
teamLedgerTool,
|
|
151
151
|
// manifest 工具(`MANIFEST.md` §2.10 · 2026-10-08 · 台账 #1098):主 agent 读写出口——
|
|
152
|
-
//
|
|
152
|
+
// **两模式同挂**(需求未设模式门);全量变体仅 depth-0——子代理各段挂只读变体(见下 `:187-193`)。
|
|
153
153
|
manifestTool,
|
|
154
154
|
// 文档纪律机检(`DOC-DISCIPLINE.md` §3.20 条目 S):只读报告态——depth-0 主 agent 段;
|
|
155
155
|
// 写文档 / 设计档的角色段(eng-coder / eng-designer)另挂(见下两分支)。
|
|
@@ -182,13 +182,15 @@ export async function assembleFamilyTools({
|
|
|
182
182
|
// consult 段不入(其角色语义 = 父发起的一次性会诊,父在其 settle 前不期望中途对话)。
|
|
183
183
|
// 台账只读变体(query / count)——全部 depth>0 角色段(含 consult ∕ 兜底;写三 schema 级不可达);
|
|
184
184
|
// 团队台账只读变体(team-ledger 批)同律。
|
|
185
|
-
|
|
185
|
+
// manifest 只读变体(MANIFEST.md §2.10.1 · 2026-10-22 · 台账 #1738):同名 `manifest`、action 枚举只剩 `read`;
|
|
186
|
+
// 与上两条只读先例同律挂在全部 depth>0 角色段(全量变体仅 depth-0——两变体互斥装配,同名不塌缩)。
|
|
187
|
+
: engChildRole === "eng-coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, manifestReadTool, advisorTool, verifyTool, batchTool(batchDoc), docCheckTool,
|
|
186
188
|
...(engChildSubagent ? [engChildSubagent] : [])]
|
|
187
|
-
: engChildRole === "eng-designer" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, batchTool(batchDoc), docCheckTool,
|
|
189
|
+
: engChildRole === "eng-designer" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, manifestReadTool, batchTool(batchDoc), docCheckTool,
|
|
188
190
|
...(engChildSubagent ? [engChildSubagent] : [])]
|
|
189
|
-
: role === "coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, verifyTool, advisorTool, ...(batchDoc ? [batchTool(batchDoc)] : [])]
|
|
190
|
-
: role === "consult" ? [recentChangesTool, ledgerReadTool, teamLedgerReadTool]
|
|
191
|
-
: [parentChannelTool, ledgerReadTool, teamLedgerReadTool]
|
|
191
|
+
: role === "coder" ? [parentChannelTool, ledgerReadTool, teamLedgerReadTool, manifestReadTool, verifyTool, advisorTool, ...(batchDoc ? [batchTool(batchDoc)] : [])]
|
|
192
|
+
: role === "consult" ? [recentChangesTool, ledgerReadTool, teamLedgerReadTool, manifestReadTool]
|
|
193
|
+
: [parentChannelTool, ledgerReadTool, teamLedgerReadTool, manifestReadTool]
|
|
192
194
|
|
|
193
195
|
// 固定段(装配序 = agent.tools → 固定段 → 家族段 → extraTools):timer 全模式全深度;
|
|
194
196
|
// plan 随模式位——ENG-PLAN-EXCLUSION(FR31 ① · KD8 卸载而非「注册 + 报错」· KD11 全深度两端);
|
package/agent/helpers.mjs
CHANGED
|
@@ -275,6 +275,12 @@ export function turnFrame(seq, turn, maxTurns) {
|
|
|
275
275
|
return { turn: seq, maxTurns: seq - turn - 1 + maxTurns }
|
|
276
276
|
}
|
|
277
277
|
|
|
278
|
+
/** #934(零落笔看门狗——AGENT-LOOP-UPSTREAM.md §6.32.2 ① 判据式):上轮结账至今写序是否推进;非整数(未跟踪面 / NaN)⇒ true 记「有落笔」不判(不用 `?? 0` 兜底——未跟踪面当零落笔正是本收正要消除的误报方向)。 */
|
|
279
|
+
export function wroteSince(agent, mark) {
|
|
280
|
+
const seq = agent._mutationSeq
|
|
281
|
+
return Number.isInteger(seq) ? seq !== mark : true
|
|
282
|
+
}
|
|
283
|
+
|
|
278
284
|
/** Repair malformed conversation history: remove orphan tool messages and fill missing tool results */
|
|
279
285
|
export function repairHistory(history) {
|
|
280
286
|
const out = []
|
|
@@ -451,14 +457,14 @@ export const AUTO_TURN_DIGEST_DOMAIN =
|
|
|
451
457
|
/** Engineering-mode auto-turn digest domain variant (mode = `agent.config.agent.engineering`;
|
|
452
458
|
* clause 2 drops the task-tool pointer — F10: the task tool is disabled in engineering mode,
|
|
453
459
|
* the batch record + ledger are the tracking authority). Single line, verbatim from the
|
|
454
|
-
* design doc (TOOLS.md §6.15.3); mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8. Content
|
|
460
|
+
* design doc (TOOLS.md §6.15.3); mechanism = AGENT-LOOP-ASYNC-POOL.md §6.8. Content basis = the mechanism doc; landing = eng-coder. */
|
|
455
461
|
export const AUTO_TURN_DIGEST_DOMAIN_ENG =
|
|
456
462
|
"[System reminder: auto-turn — background async subagents finished while there was no user message, and this turn runs automatically to digest their reports (the finished-report reminders above). No one is waiting for this reply, so organize only: 1) summarize each finished report's key points into this conversation for the user to read later; 2) (engineering mode: the task tool is disabled — no task-list update is expected this turn; the batch record + ledger are the tracking authority and are updated in real user turns); 3) write decision points with a suggested next step as text — do not execute it. FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the summaries are written.]"
|
|
457
463
|
|
|
458
464
|
/** Up-stream wake-turn domain (AGENT-LOOP-UPSTREAM.md §6.27.12.8): a running subagent sent
|
|
459
465
|
* an in-flight message and is waiting for the reply — the digest domain's "No one is waiting
|
|
460
466
|
* for this reply" is the opposite of the truth, so the wake turn gets its own text.
|
|
461
|
-
* Verbatim from the design doc (single line — no newlines). Content
|
|
467
|
+
* Verbatim from the design doc (single line — no newlines). Content basis = the mechanism doc; landing = eng-coder. */
|
|
462
468
|
export const UPSTREAM_TURN_DOMAIN =
|
|
463
469
|
"[System reminder: auto-turn — a running subagent sent you an in-flight message (shown below). No user message is waiting. Decide it now and reply with subagent action:'send' (id + message) — the child consumes the reply at its next turn boundary and keeps working on the unaffected parts; if the message needs no answer, say so in one line and move on. If finished subagent reports are also present above, summarize them as usual in the same turn. Do not start new work: FORBIDDEN this turn (mechanically enforced): modifying files, bash/execute/verify, spawning subagents, asking questions — those need a real user message. End the turn once the reply is sent.]"
|
|
464
470
|
|
|
@@ -466,16 +472,16 @@ export const UPSTREAM_TURN_DOMAIN =
|
|
|
466
472
|
* expired while the session was idle and the end's latch opened this turn automatically —
|
|
467
473
|
* neither the digest domain ("no one is waiting", organize-only) nor the up-stream domain
|
|
468
474
|
* (a subagent waiting for a reply) fits: the timer's own semantics is "act on what the
|
|
469
|
-
* timer was for". Verbatim from the
|
|
470
|
-
* Content
|
|
475
|
+
* timer was for". Verbatim from the mechanism doc (single line — no newlines).
|
|
476
|
+
* Content basis = the mechanism doc; landing = eng-coder. */
|
|
471
477
|
export const TIMER_TURN_DOMAIN =
|
|
472
478
|
"[System reminder: auto-turn — a timer you set earlier has expired (reminder above). No user message is waiting. Act on what the timer was for now: pull the data or run the check rather than continuing to reason in the abstract. Stay within the work that was in flight when the timer was set; do not open unrelated new work. Wrap up as soon as the action is done or blocked.]"
|
|
473
479
|
|
|
474
480
|
/** Digest-accounting requirement line (AGENT-LOOP-ASYNC-POOL.md §6.31.3 · batch digest-accounting ·
|
|
475
481
|
* 2026-10-05):消化轮账目要求——两档同发(手动 / AUTO);manual 域文本只覆盖手动档 ⇒ 要求行
|
|
476
482
|
* 独立常量承载,避免 AUTO 轮无要求。`<ids>` = 未销账 id 清单占位(动态拼 = digest-account.mjs
|
|
477
|
-
* `digestAccountRequirement`);注入面 = transient(机器线独有)。Verbatim from the
|
|
478
|
-
*
|
|
483
|
+
* `digestAccountRequirement`);注入面 = transient(机器线独有)。Verbatim from the mechanism doc
|
|
484
|
+
* (single line — no newlines). Content basis = the mechanism doc; landing = eng-coder. */
|
|
479
485
|
export const DIGEST_ACCOUNT_DOMAIN =
|
|
480
486
|
"[System reminder: digest accounting — for every report listed below as not yet accounted, emit exactly one line: \"[digest-ack #<id>] digested — <key points>\" (handled) or \"[digest-ack #<id>] deferred — <reason>\" (not handled). Not yet accounted: <ids>. Do not omit any.]"
|
|
481
487
|
|
package/agent/post-turn.mjs
CHANGED
|
@@ -10,6 +10,31 @@ export const STALL_WINDOW_SIZE = 5
|
|
|
10
10
|
export const STALL_THRESHOLD = 3
|
|
11
11
|
export const GOAL_BUDGET_WARN_RATIO = 0.75
|
|
12
12
|
|
|
13
|
+
// 设计轮轮数注入阈值(AGENT-LOOP.md §6.6「设计轮轮数注入」· 台账 #1661):
|
|
14
|
+
// 5 = 在 persona 判定点(过 7 轮未落盘 ⇒ 降级)之前 3 轮给出计数;8 = 与 persona 同一判定点;
|
|
15
|
+
// 15 = persona 的首版预算(注入串里的 15 一律由它插值——改常量即改注入文本,不另留字面)。
|
|
16
|
+
export const DESIGN_TURN_NOTICE_AT = 5
|
|
17
|
+
export const DESIGN_TURN_SKELETON_AT = 8
|
|
18
|
+
export const DESIGN_FIRST_VERSION_TURNS = 15
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 设计轮轮数提醒(纯函数——注入串 | `null`;判据源 = 链内累计轮号 + 本设计轮是否落过盘)。
|
|
22
|
+
* 逐字注入形 = `AGENT-LOOP.md` §6.6「注入形逐字」;角色表当前一行(`eng-designer`),
|
|
23
|
+
* `eng-coder` 零注入(其零落笔面另由零落笔看门狗兜)。
|
|
24
|
+
* @param {{role?:string|null, turn?:number, wrote?:boolean}} o `turn` = 链内累计轮号(1 起)
|
|
25
|
+
* @returns {string|null}
|
|
26
|
+
*/
|
|
27
|
+
export function designTurnReminder({ role, turn, wrote } = {}) {
|
|
28
|
+
if (role !== "eng-designer") return null
|
|
29
|
+
const used = Number(turn ?? 0)
|
|
30
|
+
if (!Number.isFinite(used) || used < DESIGN_TURN_NOTICE_AT) return null
|
|
31
|
+
const budget = DESIGN_FIRST_VERSION_TURNS
|
|
32
|
+
if (used >= DESIGN_TURN_SKELETON_AT && !wrote) {
|
|
33
|
+
return `[System reminder: design round turn ${used}/${budget} — nothing has been written to disk yet. The 7-turn mark has passed with nothing landed: degrade the delivery NOW — write the skeleton (design doc + batch record §2) plus an unresolved list, then refine in place.]`
|
|
34
|
+
}
|
|
35
|
+
return `[System reminder: design round turn ${used}/${budget} — ${wrote ? "something has been written to disk" : "nothing has been written to disk yet"}. First version budget is ${budget} turns; past 7 turns with nothing landed the rule is a skeleton + unresolved list.]`
|
|
36
|
+
}
|
|
37
|
+
|
|
13
38
|
/**
|
|
14
39
|
* Inject all post-turn events into agent history.
|
|
15
40
|
* Must be called after every tool-execution turn within runAgent's loop.
|
|
@@ -66,5 +91,14 @@ export function injectPostTurn(agent, results, recentCallSigs, callbacks, turn,
|
|
|
66
91
|
})
|
|
67
92
|
}
|
|
68
93
|
|
|
94
|
+
// 设计轮轮数注入(AGENT-LOOP.md §6.6 · 台账 #1661):轮号取 `_currentTurn`(链内累计、续跑不重置),
|
|
95
|
+
// 写入痕迹取 `_touchedFiles`(在 resume 保留集内 ⇒ 续跑段沿用前段痕迹);段内 `turn` 形参不参与判据。
|
|
96
|
+
const designReminder = designTurnReminder({
|
|
97
|
+
role: agent._role,
|
|
98
|
+
turn: agent._currentTurn ?? 0,
|
|
99
|
+
wrote: (agent._touchedFiles ?? []).length > 0,
|
|
100
|
+
})
|
|
101
|
+
if (designReminder) agent.history.push({ role: "user", content: designReminder })
|
|
102
|
+
|
|
69
103
|
callbacks.onTurnEnd?.(agent, turn)
|
|
70
104
|
}
|
package/agent/run-stages.mjs
CHANGED
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { compressIfNeeded, compressFallback, COMPRESS_FAILURE_LIMIT } from "../context.mjs"
|
|
11
|
+
// §6.17/D-CC32 溢出守卫(兜底,非主修):预算面单源 requestBudget ∥ 再估面 contextUsage(与压缩判定同一函数)
|
|
12
|
+
import { requestBudget } from "../config.mjs"
|
|
13
|
+
import { contextUsage } from "../token-window.mjs"
|
|
11
14
|
import { ensureAutoReminder, injectEngineeringReminder, ContinueError, snapshotGuard } from "./helpers.mjs"
|
|
12
15
|
import { pushManifestStateReminder } from "./setup-reminders.mjs"
|
|
13
16
|
import { cleanupConsultSessions } from "../agent-tools/consult.mjs"
|
|
@@ -84,6 +87,16 @@ export async function runCompactionCheck(agent, ctx) {
|
|
|
84
87
|
// 其余全同(同 splitHistory / 同尾部预算 / 同摘要链 / 同回注 / 同基线失效)。
|
|
85
88
|
const pending = agent._pendingCompact
|
|
86
89
|
agent._pendingCompact = null
|
|
90
|
+
// 成功记账(§6.17:成功记账段抽局部——阈值面与守卫面共用同一段,零复制)
|
|
91
|
+
const bookkeepCompaction = () => {
|
|
92
|
+
agent._compressFailures = 0
|
|
93
|
+
agent._planReminderAtLen = 0 // After compression history shrinks, reset cadence so reminders resume
|
|
94
|
+
recentCallSigs.length = 0 // After compression history is rebuilt, reset stall detection counter
|
|
95
|
+
// Completion info (CONTEXT-COMPACTION §7 D-C2): { mode, tokensFreed, elapsedMs } —
|
|
96
|
+
// the TUI panel renders it; callers that ignore the arg keep prior onCompress semantics.
|
|
97
|
+
callbacks.onCompress?.(agent._lastCompressInfo ?? {})
|
|
98
|
+
ensureAutoReminder(agent)
|
|
99
|
+
}
|
|
87
100
|
try {
|
|
88
101
|
let compacted = await compressIfNeeded(
|
|
89
102
|
agent, threshold, callbacks,
|
|
@@ -96,15 +109,7 @@ export async function runCompactionCheck(agent, ctx) {
|
|
|
96
109
|
agent.history.push({ role: "user", content: compactNoopNote(), transient: true })
|
|
97
110
|
compacted = await compressIfNeeded(agent, threshold, callbacks, compactionOverhead, signal)
|
|
98
111
|
}
|
|
99
|
-
if (compacted)
|
|
100
|
-
agent._compressFailures = 0
|
|
101
|
-
agent._planReminderAtLen = 0 // After compression history shrinks, reset cadence so reminders resume
|
|
102
|
-
recentCallSigs.length = 0 // After compression history is rebuilt, reset stall detection counter
|
|
103
|
-
// Completion info (CONTEXT-COMPACTION §7 D-C2): { mode, tokensFreed, elapsedMs } —
|
|
104
|
-
// the TUI panel renders it; callers that ignore the arg keep prior onCompress semantics.
|
|
105
|
-
callbacks.onCompress?.(agent._lastCompressInfo ?? {})
|
|
106
|
-
ensureAutoReminder(agent)
|
|
107
|
-
}
|
|
112
|
+
if (compacted) bookkeepCompaction()
|
|
108
113
|
} catch (compressError) {
|
|
109
114
|
// AbortError must not be swallowed: user cancellation must propagate
|
|
110
115
|
if (compressError?.name === "AbortError" || signal?.aborted) throw compressError
|
|
@@ -122,6 +127,35 @@ export async function runCompactionCheck(agent, ctx) {
|
|
|
122
127
|
if (compressFallback(agent)) callbacks.onCompress?.(agent._lastCompressInfo ?? {})
|
|
123
128
|
}
|
|
124
129
|
}
|
|
130
|
+
|
|
131
|
+
// 溢出守卫(CONTEXT-COMPACTION.md §6.17 / D-CC32——兜底,非主修;本缝 = catch 闭合后、返回前)。
|
|
132
|
+
// 判据 = maxTokensSource !== "absent" ∧ prompt 估算 > limit(limit = window − reserve,恒为数)。
|
|
133
|
+
// **判据不用 maxTokens**:未发时为 undefined ⇒ NaN ⇒ 守卫静默失效(§6.17);absent 档客户端未预留
|
|
134
|
+
// ⇒ 守卫不覆盖(服务端 400 兜——把规格 maxOutput 当实发判会让 doubao 三行永久发不出去)。
|
|
135
|
+
// 真 ⇒ 以 force 强制压缩一次(先压再判);再估仍超 ⇒ 抛三实数、回合请求零发。不截断、不静默削输出。
|
|
136
|
+
const budget = requestBudget(agent.provider)
|
|
137
|
+
if (budget.maxTokensSource !== "absent") {
|
|
138
|
+
let usage = contextUsage(agent, compactionOverhead)
|
|
139
|
+
if (usage.total > budget.limit) {
|
|
140
|
+
const compacted = await compressIfNeeded(
|
|
141
|
+
agent, threshold, callbacks, { ...compactionOverhead, force: true }, signal,
|
|
142
|
+
)
|
|
143
|
+
if (compacted) bookkeepCompaction()
|
|
144
|
+
usage = contextUsage(agent, compactionOverhead)
|
|
145
|
+
if (usage.total > budget.limit) {
|
|
146
|
+
const remedy = budget.limit < 0
|
|
147
|
+
? `max_tokens ${budget.maxTokens} alone exceeds the window — lower provider.maxTokens or switch to a larger-window model`
|
|
148
|
+
: "reduce the conversation (/compact or prune) before retrying"
|
|
149
|
+
const overflow = new Error(
|
|
150
|
+
`context overflow: prompt ~${usage.total} tokens + max_tokens ${budget.maxTokens} > window ${budget.window}` +
|
|
151
|
+
` (limit ${budget.limit}) — forced compaction ran but the prompt still exceeds the window; ${remedy}`,
|
|
152
|
+
)
|
|
153
|
+
overflow.kind = "context_overflow"
|
|
154
|
+
overflow.retryable = false
|
|
155
|
+
throw overflow
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
125
159
|
}
|
|
126
160
|
|
|
127
161
|
/**
|
package/agent/run-start.mjs
CHANGED
|
@@ -105,6 +105,7 @@ export async function beginRun(agent, input, callbacks, {
|
|
|
105
105
|
// #417(撞帽载荷「本段零落盘轮数」——只报数:零阈值常量 / 零自动动作):段起点复位(首段 /
|
|
106
106
|
// 续跑新段——与 `_continueSegments` 同点);段内累加,采集点在回合环(与 `_turnSeq` 同源面)。
|
|
107
107
|
agent._zeroWriteTurns = 0
|
|
108
|
+
agent._segmentTurns = 0 // #934 B 面读数分母(§6.32.4):同点复位——同段同源(分子 ≤ 分母)
|
|
108
109
|
if (!resume) {
|
|
109
110
|
// 第 19 批(TURN-ACROSS-SEGMENTS——设计 TURN-CAP-CONTINUE.md §4):链内累计编号
|
|
110
111
|
// 只在链起点复位——续跑(resume:true)不重置、不回退(编号帧公式见 helpers.mjs
|
package/agent/setup.mjs
CHANGED
|
@@ -22,8 +22,8 @@ import { assembleFamilyTools } from "./family-tools.mjs"
|
|
|
22
22
|
import { normalizeOrigin } from "../memory/origin.mjs"
|
|
23
23
|
import { searchOrigins } from "../memory/code-search.mjs"
|
|
24
24
|
import { INDEX_ORIGIN_ROW_WARN } from "../memory/schema.mjs"
|
|
25
|
+
import { resolveCompactThreshold } from "../config.mjs"
|
|
25
26
|
|
|
26
|
-
const DEFAULT_COMPACT_THRESHOLD = 100_000
|
|
27
27
|
const DOC_SEARCH_LIMIT = 5
|
|
28
28
|
const DOC_CHUNK_PREVIEW_LEN = 300
|
|
29
29
|
/** UTF-16 安全截断(2026-09-02 deepseek 400 根因):slice(0, N) 按码元切会把 emoji 代理对切成孤立
|
|
@@ -51,7 +51,9 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
51
51
|
injections = null, toolDecorate = undefined,
|
|
52
52
|
} = {}) {
|
|
53
53
|
const maxTurns = overrideTurns ?? agent.config?.agent?.maxTurns ?? DEFAULT_MAX_TURNS
|
|
54
|
-
|
|
54
|
+
// §6.17 每 run 现算:数值在场 = 显式原样;不在场 = auto 现算(按在用 provider——切模型后下一 run 跟手)。
|
|
55
|
+
// 派生值不落 agent.compactThreshold(显式键永不被改写)。
|
|
56
|
+
const threshold = resolveCompactThreshold(agent.config?.agent?.compactThreshold, agent.provider).value
|
|
55
57
|
// 注入开关(PROMPT-SYSTEM.md §6.24 · 台账 #1420):判据 `!== false`(缺省 = 现行为零变)——
|
|
56
58
|
// 逐 run 现读 `agent.config` ⇒ `settings` 工具 ∕ `/config` 写入后本会话下一次组装即生效。
|
|
57
59
|
const injectDocs = agent.config?.agent?.injectDocs !== false
|
|
@@ -62,6 +64,12 @@ export async function prepareRun(agent, input, callbacks, {
|
|
|
62
64
|
|
|
63
65
|
agent._lastPromptTokens = null
|
|
64
66
|
agent._usageAtLen = null
|
|
67
|
+
// Cumulative provider cache accounting (batch 2026-10-18-context-cache-visibility):
|
|
68
|
+
// the service reports prompt_cache_hit/miss per response; the terminal faces each kept
|
|
69
|
+
// their own tally, so the CORE had none and `context` could not report cache efficiency.
|
|
70
|
+
agent._cacheHitTokens = 0
|
|
71
|
+
agent._cacheMissTokens = 0
|
|
72
|
+
agent._usageTurns = 0
|
|
65
73
|
agent.history = repairHistory(agent.history)
|
|
66
74
|
|
|
67
75
|
if (!resume) {
|
package/agent/turn-loop.mjs
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
turnFrame, // 第 19 批(TURN-ACROSS-SEGMENTS):跨段累计编号帧(设计 TURN-CAP-CONTINUE.md §4)
|
|
19
19
|
ContinueError,
|
|
20
20
|
FILE_MUTATORS, toolTouchPaths,
|
|
21
|
+
wroteSince, // #934(§6.32.2 ① 判据式):写序是否推进——轮顶 ∥ 撞帽收尾两处判据点共用
|
|
21
22
|
} from "./helpers.mjs"
|
|
22
23
|
|
|
23
24
|
export async function runTurnLoop(agent, {
|
|
@@ -57,9 +58,10 @@ export async function runTurnLoop(agent, {
|
|
|
57
58
|
// §6.31.5 落痕写点门(#939):载体吸收经 `carrierField` 单点(§6.31.4 同式);动态 import = 零新增静态边(沿上方两条同款)。
|
|
58
59
|
const { carrierField } = await import("../agent-tools/async-settle.mjs")
|
|
59
60
|
|
|
60
|
-
// #417(撞帽载荷「本段零落盘轮数」——只报数:零阈值 /
|
|
61
|
-
//
|
|
62
|
-
|
|
61
|
+
// #417(撞帽载荷「本段零落盘轮数」——只报数:零阈值 / 零自动动作)∥ #934(零落笔看门狗):计数基线——
|
|
62
|
+
// 判据物 = 写序(每次写事件 +1,不去重);`_touchedFiles` 是去重路径集,同档重写长度不变 ⇒ 旧判据在
|
|
63
|
+
// 此读成零落笔(本收正要消除的误报方向)。环内逐轮结账、撞帽收尾轮在环外单结(同一谓词)。
|
|
64
|
+
agent._mutSeqMark = agent._mutationSeq
|
|
63
65
|
|
|
64
66
|
for (let turn = 0; turn < maxTurns; turn++) {
|
|
65
67
|
// #793(核面小修批——AGENT-LOOP.md §6.2 环边界中止前置 ∥ 需求 F5):环头检查点——中止恒以
|
|
@@ -73,21 +75,23 @@ export async function runTurnLoop(agent, {
|
|
|
73
75
|
const frame = turnFrame(++agent._turnSeq, turn, maxTurns)
|
|
74
76
|
agent._currentTurn = frame.turn
|
|
75
77
|
agent._maxTurns = frame.maxTurns
|
|
76
|
-
// #
|
|
77
|
-
|
|
78
|
-
// #
|
|
79
|
-
//
|
|
78
|
+
// #934 B 面读数分母(§6.32.4):本段轮数逐轮 +1——段起点复位见 run-start.mjs(与零落笔计数同点)。
|
|
79
|
+
agent._segmentTurns = (agent._segmentTurns ?? 0) + 1
|
|
80
|
+
// #417(撞帽载荷「本段零落盘轮数」——采集点与 `_turnSeq` 同源面):上一轮至今写序未推进
|
|
81
|
+
// ⇒ 上一轮零落笔,计数 +1;写序标每轮前移(撞帽收尾轮在环外结账——见下方 ContinueError 前)。
|
|
82
|
+
// #934(零落笔看门狗——§6.32.2 ① 单点采集):streak 与越阈判定同点——上轮零落笔 ⇒ +1 并判越阈;
|
|
83
|
+
// 有落笔 ⇒ streak 归零 + 清闩(写后重臂——每连续零落笔段恰一条)。#417 计数与 streak 同源同谓词。
|
|
80
84
|
if (turn > 0) {
|
|
81
|
-
if (agent
|
|
85
|
+
if (!wroteSince(agent, agent._mutSeqMark)) {
|
|
82
86
|
agent._zeroWriteTurns += 1
|
|
83
87
|
agent._zeroWriteStreak += 1
|
|
84
|
-
maybeZeroWriteAlert(agent) // #934
|
|
88
|
+
maybeZeroWriteAlert(agent) // #934:连续零落笔越阈 ⇒ 自动上行提醒(只推——§6.32)
|
|
85
89
|
} else {
|
|
86
90
|
agent._zeroWriteStreak = 0
|
|
87
91
|
agent._zeroWriteAlerted = false // 写后重臂
|
|
88
92
|
}
|
|
89
93
|
}
|
|
90
|
-
agent.
|
|
94
|
+
agent._mutSeqMark = agent._mutationSeq
|
|
91
95
|
// D2 (AGENT-LOOP-SUBAGENT.md §6.7.2): depth>0 children emit a ⟦ev⟧turn progress token each turn —
|
|
92
96
|
// single emit point covering all three spawn tools; phase=llm (tool/done progress rides
|
|
93
97
|
// the onToolCall/onToolResult relay — no token for those). 第 19 批:载荷取上方帧值
|
|
@@ -180,6 +184,12 @@ export async function runTurnLoop(agent, {
|
|
|
180
184
|
|
|
181
185
|
if (response.usage) {
|
|
182
186
|
callbacks.onUsage?.(response.usage)
|
|
187
|
+
// Core-side cumulative cache tally (batch 2026-10-18-context-cache-visibility):
|
|
188
|
+
// every agent (parent or child) keeps its own hit/miss so `context` can report
|
|
189
|
+
// cache efficiency instead of the terminal faces being the only tally holders.
|
|
190
|
+
agent._cacheHitTokens = (agent._cacheHitTokens ?? 0) + (response.usage.prompt_cache_hit_tokens ?? 0)
|
|
191
|
+
agent._cacheMissTokens = (agent._cacheMissTokens ?? 0) + (response.usage.prompt_cache_miss_tokens ?? 0)
|
|
192
|
+
agent._usageTurns = (agent._usageTurns ?? 0) + 1
|
|
183
193
|
if (response.usage.prompt_tokens != null) {
|
|
184
194
|
agent._lastPromptTokens = response.usage.prompt_tokens
|
|
185
195
|
agent._usageAtLen = agent.history.length
|
|
@@ -265,9 +275,9 @@ export async function runTurnLoop(agent, {
|
|
|
265
275
|
injectPostTurn(agent, results, recentCallSigs, callbacks, turn, { suspDriven })
|
|
266
276
|
}
|
|
267
277
|
|
|
268
|
-
// #417
|
|
269
|
-
//
|
|
270
|
-
if (maxTurns > 0 && agent
|
|
278
|
+
// #417(撞帽载荷「本段零落盘轮数」):收尾轮(撞帽轮)单结——与环内同一谓词(不另作 alert ∥ streak
|
|
279
|
+
// 判定,链尾无下一轮可推报);轮顶计数只覆盖到倒数第二轮(maxTurns ≤ 0 时环未跑,不虚计)。
|
|
280
|
+
if (maxTurns > 0 && !wroteSince(agent, agent._mutSeqMark)) agent._zeroWriteTurns += 1
|
|
271
281
|
// #793(核面小修批——AGENT-LOOP.md §6.2 环边界中止前置 ∥ 需求 F5):环尾检查点——中止恒以
|
|
272
282
|
// AbortError 收束(不落 ContinueError——用户 Stop 优先于继续提示;消费契约 = 外层按 reason 判定)。
|
|
273
283
|
if (signal?.aborted) throw abortError(signal, "agent", "turn-tail")
|
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
*/
|
|
24
24
|
export function summarizeEngTaskBook(taskInput) {
|
|
25
25
|
if (!taskInput) return "(unavailable)"
|
|
26
|
+
// 五项拼装形下,`designPoints` 一段同时承载「设计档落点」与「受影响文件清单」——
|
|
27
|
+
// 前两段同取该块是有意的(语义都在其中);代价 = 摘要里该块出现两次,不是抽取失败。
|
|
26
28
|
const SECTIONS = [
|
|
27
|
-
{ name: "Design docs involved", markers: [/Docs? involved/i,
|
|
28
|
-
{ name: "Affected-file list", markers: [/Files? (?:list|to (?:modify|change)|modified)/i, /受影响文件/, /文件清单/,
|
|
29
|
+
{ name: "Design docs involved", markers: [/Docs? involved/i, /涉及文档/, /^Design points/i] },
|
|
30
|
+
{ name: "Affected-file list", markers: [/Files? (?:list|to (?:modify|change)|modified)/i, /受影响文件/, /文件清单/, /涉及文件/, /^Design points/i] },
|
|
29
31
|
{ name: "Acceptance criteria", markers: [/Acceptance(?: criteria)?/i, /验收标准/] },
|
|
30
32
|
]
|
|
31
33
|
const lines = taskInput.split("\n")
|