@thincoder/core 0.10.10 → 0.10.12
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 +36 -2
- package/advisor/messages.mjs +1 -1
- package/advisor/project-context.mjs +2 -2
- package/agent/completion.mjs +4 -1
- package/agent/family-tools.mjs +3 -3
- package/agent/helpers.mjs +13 -11
- package/agent/light-round.mjs +1 -4
- package/agent/record-results.mjs +6 -1
- package/agent/setup-reminders.mjs +1 -1
- package/agent/unbooked-pen.mjs +62 -0
- package/agent-tools/advisor.mjs +15 -31
- package/agent-tools/audit-block.mjs +16 -62
- package/agent-tools/batch-gate.mjs +74 -66
- package/agent-tools/batch-lifecycle.mjs +66 -9
- package/agent-tools/batch-paths.mjs +10 -8
- package/agent-tools/context.mjs +1 -1
- package/agent-tools/design-slots.mjs +72 -12
- package/agent-tools/design-token.mjs +27 -4
- package/agent-tools/digest-budget.mjs +2 -0
- package/agent-tools/doc-check.mjs +3 -14
- package/agent-tools/manifest.mjs +4 -5
- package/agent-tools/review-facts.mjs +35 -81
- package/agent-tools/settings.mjs +65 -5
- package/agent-tools/spawn-gates.mjs +68 -31
- package/agent-tools/subagent-async.mjs +6 -3
- package/agent-tools/subagent-spawn.mjs +42 -61
- package/agent-tools/subagent.mjs +15 -15
- package/browser/launch.mjs +14 -3
- package/browser/queue.mjs +31 -3
- package/browser/session.mjs +4 -0
- package/config-defaults.mjs +8 -0
- package/config-presets.mjs +1 -1
- package/config.mjs +60 -11
- package/doc-check/anchors.mjs +10 -9
- package/doc-check/index.mjs +27 -23
- package/doc-check/paragraph.mjs +119 -0
- package/fts-text.mjs +4 -1
- package/ledger-cmd.mjs +56 -41
- package/ledger-db.mjs +65 -58
- package/ledger-tools.mjs +60 -23
- package/ledger.mjs +13 -11
- package/manifest-discovery.mjs +58 -34
- package/manifest.mjs +34 -21
- package/model-context.mjs +98 -0
- package/model-specs.mjs +121 -50
- package/package.json +2 -2
- package/permission.mjs +3 -1
- package/prompts/advisor-design.md +16 -0
- package/prompts/common.md +12 -1
- package/prompts/discipline-engineering.md +42 -12
- package/prompts/discipline-normal.md +8 -7
- package/prompts/persona-coder.md +8 -1
- package/prompts/persona-eng-coder.md +8 -0
- package/prompts/persona-eng-designer.md +26 -4
- package/prompts/persona-engineering.md +37 -38
- package/prompts/persona-explore.md +6 -0
- package/prompts/persona-normal.md +6 -0
- package/prompts/persona-plan.md +6 -0
- package/provider/anthropic.mjs +127 -4
- package/provider/core.mjs +16 -6
- package/provider/google.mjs +8 -8
- package/provider/list-models.mjs +37 -5
- package/provider/normalize.mjs +2 -2
- package/provider/responses.mjs +7 -7
- package/provider/sse.mjs +6 -5
- package/proxy-transport.mjs +5 -3
- package/proxy.mjs +2 -1
- package/session-guard.mjs +7 -1
- package/session-lifecycle.mjs +23 -8
- package/session-segments.mjs +1 -0
- package/session-slot-claims.mjs +4 -0
- package/session-slot-write.mjs +27 -1
- package/session-slots-manifest.mjs +25 -4
- package/token-ttl.mjs +78 -4
- package/tool-docs/advisor.md +24 -5
- package/tool-docs/doc_check.md +23 -16
- package/tool-docs/ledger.md +2 -2
- package/tool-docs/subagent.md +41 -16
- package/tool-docs/wait_for.md +15 -6
- package/tools/web-deepseek.mjs +26 -11
- package/doc-check/version.mjs +0 -84
- package/doc-check/width.mjs +0 -87
package/CHANGELOG.md
CHANGED
|
@@ -3,7 +3,39 @@
|
|
|
3
3
|
All notable changes to the core package are documented here.
|
|
4
4
|
Format: Keep a Changelog · 中文 · 号在发布时定(CalVer——见 `docs/RELEASE.md` §4)。
|
|
5
5
|
|
|
6
|
-
## [
|
|
6
|
+
## [0.10.12] — 2026-10-26
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **台账在无根目录从「静默空集」改为「抛明」**:项目根推导不出来时,`query` / `count` / `add` / `update` / `close` 一律抛错并说明「自哪个路径、向上找到哪里为止」,不再返回空集。空集与「记到别的项目」在回执上分不开,静默空集等于让人以为没有台账。
|
|
11
|
+
- **多根工作区下建批档:报明项目并给路径,不再落锚目录**:工作目录本身没有项目、直接子目录里有项目(1 个同判、多个同判),此时建批档会列全下辖项目并给显式路径指引;目标属于某个项目时照常落该项目的声明基底。
|
|
12
|
+
- **项目根解析:多根工作区一律「≥1 同判」**:取消此前「恰一个子项目」与「多个」的区别——工作目录下直接子目录带项目清单,不论 1 个还是多个,都是多根工作区,逻辑一致。
|
|
13
|
+
|
|
14
|
+
## [0.10.11] — 2026-10-25
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`providers[].modelContext`(按模型的人工上下文声明,单位 K)**:键 = 模型 ID(末段归一等价——`qwen/ZHIPU/GLM-5.3` ≡ `glm-5.3`),值 = 正整数 K(`128` = 128K)。
|
|
19
|
+
- **模型上下文五级解析链**:`modelContext` > 旧键 `providers[].context` > 渠道 `/models` 自报(`context_window` / `context_length` / `max_model_len`,取第一个合法值)> `MODEL_SPECS` 静态表 > 默认 128K;列表自报值是 token(直接采用),人工声明是 K(核内唯一乘点 ×1024)。
|
|
20
|
+
- **核导出 `providerSpecResolution(provider)` / `formatContextK(tokens)`**:来源五值(`modelContext` / `provider-override` / `list` / `spec` / `default`)与三端同一展示格式器。
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **旧键 `providers[].context` 退役为只读过渡层(A7 兼容)**:仍可读(L2,K 单位,仅未被 `modelContext` 点名的模型),但不再写入新配置——K 单位旧编辑命令退役,三端写面改用 `modelContext`(三端同批,随各端包发布);实际用到 L2 时告警一次并给出迁移形 `modelContext: { "<模型>": <K> }`(单位同为 K)。
|
|
25
|
+
- **`requestBudget().windowSource` 扩为五值**:读 `providerSpecResolution().source`(原二值 `provider-override` / `spec`),五个枚举值原样入 `Request budget:` 行(不并类)。
|
|
26
|
+
- **模型名匹配改「末段唯一」**:按 `/` 拆分后只取最后一段查表,不再先扫完整名——`glm-5.3/zzz` 由误命中 `glm-5.3`(1M 上下文)改为未命中(`DEFAULT_SPEC` + 告警);`kimi/kimi-k3` 表行退役(末段 `kimi-k3` 已有同值行,含 `/` 的键不可达)。
|
|
27
|
+
|
|
28
|
+
## [0.10.10] — 2026-10-22
|
|
29
|
+
|
|
30
|
+
### Added
|
|
31
|
+
|
|
32
|
+
- **子代理 `manifest` 只读面**:同名工具、`action` 枚举只剩 `read`(schema 级不可达 + execute 入口再拒一次),挂全部 depth>0 角色段;写面仍归主 agent——`init` / `write` 不在其装配,只读位也不落在全量变体上,审批门不被绕过。`readAction` 增 `canInit`:子代理侧缺档 / 无项目两态的引导行不再指向它调不到的 `init`。
|
|
33
|
+
- **`thincoder-core/prompts/common.md` 新增「动手前先查文档」一节**:代码只记形状,理由与后果住在文档里;一次会话只完成大任务的一步、上下文只是项目已知内容的一小部分,不先查就是拿局部当整体。
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- **提示词面 16 档重写(一条规则一行)**:`>300` 字符行 290 → **0**(最长 1,486 → 300,为未改动原行);整句加粗 / 回查指代 / 出处括注 / 写死配置值 / 本仓路径 / 中文评审面六类残留清零。判据 = 「换个仓还成立吗」——规则只写行为,不写归属、不写配置值、不写本仓路径(配置值改教模型用 `settings` 读,路径改教用 `manifest` 取 `docRoot`)。
|
|
38
|
+
- **`tool-docs` 计数随动**:53 → 54 档(枚举工具 52 → 53)。
|
|
7
39
|
|
|
8
40
|
## [0.10.9] — 2026-10-19
|
|
9
41
|
|
|
@@ -191,7 +223,9 @@ Format: Keep a Changelog · 中文 · 号在发布时定(CalVer——见 `docs
|
|
|
191
223
|
|
|
192
224
|
### Added
|
|
193
225
|
|
|
194
|
-
- **规格表覆盖扩面(新名专行)**:`qwen-flash` / `qwen-vl-max`(实测输出上限 32_768——服务端直报 `[1, 32768]
|
|
226
|
+
- **规格表覆盖扩面(新名专行)**:`qwen-flash` / `qwen-vl-max`(实测输出上限 32_768——服务端直报 `[1, 32768]`;前者思考默认可开、后者不支持)、
|
|
227
|
+
`claude-fable-5.1`(1_000_000 / 128_000)、`gemini-3.1-pro`(1_048_576 / 65_536)、`stepfun/step-3.7-flash`(262_144);
|
|
228
|
+
kimi-code 平台三行(`kimi-for-coding` / `kimi-for-coding-highspeed` / `k3-256k`——含 effort 枚举与默认档)——掉兜底名不再退默认。
|
|
195
229
|
- **bench 受测 10 档规格行**:此前经泛前缀/兜底解析的受测模型补独立行 + 视觉声明分态登记。
|
|
196
230
|
- **跨实例意图认领层(P1)**:`peers/{sessionId}.json` 增 claims 意图域(TTL 30 分钟租约 · 进程死亡即失效);写前命中他实例活认领 ⇒ 软提示(不阻写;与写后足迹登记分开存)。
|
|
197
231
|
|
package/advisor/messages.mjs
CHANGED
|
@@ -12,7 +12,7 @@ import { loadAdvisorMd, extractConversationBackground, extractAgentResponseTable
|
|
|
12
12
|
import { injectProjectGuide, injectDocumentMap, injectProjectStandards, NO_GIT_NOTICE } from "./project-context.mjs"
|
|
13
13
|
|
|
14
14
|
// Structural split (not an authority migration): the moved helpers stay reachable here.
|
|
15
|
-
export {
|
|
15
|
+
export { findGuideRoot, injectProjectGuide } from "./project-context.mjs"
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* Build the mechanical review-object declaration block (AGENT-LOOP-ASYNC-POOL.md §6.18
|
|
@@ -51,7 +51,7 @@ export const NO_GIT_NOTICE = "(No git repository detected — change-set context
|
|
|
51
51
|
* @param {string[]} scopeFiles — cwd-relative review-scope paths (may be empty)
|
|
52
52
|
* @returns {string|null} absolute project root with an AGENTS.md, or null
|
|
53
53
|
*/
|
|
54
|
-
export function
|
|
54
|
+
export function findGuideRoot(cwd, scopeFiles) {
|
|
55
55
|
// Normalize separators before comparing: input paths may use either
|
|
56
56
|
// convention (join() → "\\" on Windows; tool args / tests → "/"). Mixed
|
|
57
57
|
// styles made isInside(cwd + sep) miss legitimately nested paths.
|
|
@@ -88,7 +88,7 @@ export function findProjectRoot(cwd, scopeFiles) {
|
|
|
88
88
|
*/
|
|
89
89
|
export function injectProjectGuide(agent, parts, scopeFiles = []) {
|
|
90
90
|
parts.push("## Project Guide (AGENTS.md)")
|
|
91
|
-
const root =
|
|
91
|
+
const root = findGuideRoot(agent.cwd, scopeFiles)
|
|
92
92
|
const path = root ? join(root, "AGENTS.md") : null
|
|
93
93
|
let text
|
|
94
94
|
if (!path) {
|
package/agent/completion.mjs
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import { hasCodeMutations } from "../advisor/repos.mjs"
|
|
9
9
|
import { pushReal } from "../context.mjs"
|
|
10
10
|
import { advisorReviewPending, effectiveAdvisorRound } from "../agent-tools/advisor-async.mjs"
|
|
11
|
+
import { poolLive } from "./suspension.mjs"
|
|
11
12
|
|
|
12
13
|
const MAX_VERIFY_PUSHBACKS = 2
|
|
13
14
|
const MAX_VERIFY_RETRIES = 3
|
|
@@ -59,7 +60,9 @@ export function handleCompletion(agent, response, depth, turn, guardPushbacks, h
|
|
|
59
60
|
// list state earns one fresh reminder.
|
|
60
61
|
// F10(催更门连带):工程模式下 task 工具机械停用(装配摘除 + execute 拒)⇒ 不再发指向被拒
|
|
61
62
|
// 工具的提醒(死胡同提醒防线);列表本体与 `_taskPushbacks` 预算语义零改。
|
|
62
|
-
|
|
63
|
+
// TIMEOUT-DIGEST.md §2.3(#29):后台池 live 时不推回——池在飞时回合本就还会被 settle 唤醒,
|
|
64
|
+
// 此时催 task 只会拖回合;判据复用 `poolLive`(与挂起尾巴同一口径,不立第二处)。
|
|
65
|
+
if (depth === 0 && !agent.config?.agent?.engineering && !poolLive(agent) && agent.tasks.some((t) => t.status === "pending") && (agent._taskPushbacks ?? 0) < 1) {
|
|
63
66
|
agent._taskPushbacks = (agent._taskPushbacks ?? 0) + 1
|
|
64
67
|
const pending = agent.tasks.filter((t) => t.status === "pending").map((t) => t.title).join(", ")
|
|
65
68
|
pushReal(agent, { role: "assistant", content: response.content })
|
package/agent/family-tools.mjs
CHANGED
|
@@ -95,8 +95,8 @@ export async function assembleFamilyTools({
|
|
|
95
95
|
// async, the check/status params (id/n), the eng-coder token params
|
|
96
96
|
// (designToken/designId are meaningless for a read-only spawn; the parent
|
|
97
97
|
// spawn already carried the token) and batchDoc (an audit child derives no
|
|
98
|
-
// batch parameter — its task book rides the
|
|
99
|
-
//
|
|
98
|
+
// batch parameter — its task book rides the parent's batch record §2 instead
|
|
99
|
+
// (the audit block carries the batchDoc path)). Schema noise would invite the
|
|
100
100
|
// model to pass irrelevant args.
|
|
101
101
|
delete props.async // sync only — the parent blocks on the child's report
|
|
102
102
|
delete props.id
|
|
@@ -126,7 +126,7 @@ export async function assembleFamilyTools({
|
|
|
126
126
|
name: "subagent",
|
|
127
127
|
description: designer
|
|
128
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
|
|
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 context is appended MECHANICALLY — the batch record path (its §2 is the delivery's task book, read it before judging) 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
|
})()
|
package/agent/helpers.mjs
CHANGED
|
@@ -150,24 +150,26 @@ export async function cleanupOldToolResults(dir) {
|
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
/** Offload
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
/** Offload a tool result / report to disk (§2.2): `force` = write regardless of size (async
|
|
154
|
+
* subagent reports must survive the parent turn). Rendering splits on the SAME constant as the old
|
|
155
|
+
* gate: ≤ TOOL_RESULT_OFFLOAD_LIMIT ⇒ body once + path (never dual-end — a short body would come
|
|
156
|
+
* out twice with `middle omitted: 0 chars`); > ⇒ dual-end preview + path. Writes self-clean first. */
|
|
157
|
+
export async function offloadToolResult(text, callId, dir = join(configDir, "tool-results"), { force = false } = {}) {
|
|
158
|
+
const overLimit = text.length > TOOL_RESULT_OFFLOAD_LIMIT
|
|
159
|
+
if (!force && !overLimit) return text
|
|
158
160
|
try {
|
|
159
161
|
await cleanupOldToolResults(dir)
|
|
160
162
|
await mkdir(dir, { recursive: true })
|
|
161
163
|
const file = join(dir, `${Date.now()}-${String(callId).replace(/[^a-zA-Z0-9_-]/g, "_")}.log`)
|
|
162
164
|
await writeFile(file, text, "utf8")
|
|
163
|
-
return (
|
|
164
|
-
|
|
165
|
-
`\n\n[... output too large (${text.length} chars total), full content saved to: ${file}\n` +
|
|
165
|
+
if (!overLimit) return `${text}\n\n[... full content saved to: ${file}\nPage through it with the read tool (offset/limit) if you need the full text later.]`
|
|
166
|
+
return buildDualEndPreview(text) + `\n\n[... output too large (${text.length} chars total), full content saved to: ${file}\n` +
|
|
166
167
|
`Page through it with the read tool (offset/limit) or sed -n 'START,ENDp' — do NOT re-run the tool blindly.]`
|
|
167
|
-
)
|
|
168
168
|
} catch {
|
|
169
|
-
//
|
|
170
|
-
|
|
169
|
+
// 写盘失败不吞报告:>LIMIT 走双端预览 + 原文案;≤LIMIT 正文整段在前(不谎称 truncated)
|
|
170
|
+
const body = overLimit ? buildDualEndPreview(text) : text
|
|
171
|
+
return body + (overLimit ? `\n\n[... truncated: ${text.length} chars total, offload to disk failed]`
|
|
172
|
+
: `\n\n[... full text kept inline above (${text.length} chars), offload to disk failed]`)
|
|
171
173
|
}
|
|
172
174
|
}
|
|
173
175
|
|
package/agent/light-round.mjs
CHANGED
|
@@ -21,9 +21,6 @@ import { projectRootView, resolveProjectRoot } from "../manifest.mjs"
|
|
|
21
21
|
import { resolveDeclaredRef } from "../declaration.mjs"
|
|
22
22
|
import { readBatchStatusLine } from "../agent-tools/batch-skeleton.mjs"
|
|
23
23
|
|
|
24
|
-
/** 轮型标记(§2.3 开轮形升格为闸凭据;全链批行无此标记 ⇒ 不构成信号)。 */
|
|
25
|
-
const ROUND_MARKER = "收尾链待跑"
|
|
26
|
-
|
|
27
24
|
/** 轻通道轮在盘谓词(异步 · 只读 · fail-closed)。契约见头注。
|
|
28
25
|
* @param {object} agent — 会话 agent(读 `cwd`)
|
|
29
26
|
* @returns {Promise<boolean>} true = 放行(在盘轮次);读错 ∥ 不可判 ⇒ false(照拦)
|
|
@@ -33,7 +30,7 @@ export async function lightRoundOpen(agent) {
|
|
|
33
30
|
const cwd = agent?.cwd
|
|
34
31
|
if (typeof cwd !== "string" || cwd.trim() === "") return false
|
|
35
32
|
// W8 契约②:node:sqlite 链只经动态 import 进入(勿升静态)——候选枚举与逐候选查行同取本档公共面。
|
|
36
|
-
const { ledgerChildren, ledgerQuery } = await import("../ledger.mjs")
|
|
33
|
+
const { ledgerChildren, ledgerQuery, ROUND_MARKER } = await import("../ledger.mjs")
|
|
37
34
|
const anchor = resolve(cwd)
|
|
38
35
|
// 候选面 = 锚自身 + 直接子目录一层(纯向下;子项枚举单源 = `ledgerChildren`)。
|
|
39
36
|
const candidates = [anchor, ...ledgerChildren(anchor).map((p) => p.root)]
|
package/agent/record-results.mjs
CHANGED
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
import { pushReal } from "../context.mjs"
|
|
27
27
|
import { specForModel } from "../config.mjs"
|
|
28
28
|
import { FILE_MUTATORS, toolTouchPaths } from "./helpers.mjs"
|
|
29
|
+
// F-LC6(LIGHT-CHANNEL §2.9 · 台账 #1753):无账之笔提醒叶(谓词 + 文案 + 闩单源)。
|
|
30
|
+
import { unbookedPenNotice } from "./unbooked-pen.mjs"
|
|
29
31
|
import { capHistoryImages } from "../provider/normalize.mjs"
|
|
30
32
|
import { resolve } from "node:path"
|
|
31
33
|
import { advisorRuns, stripApprovedSuffix } from "../agent-tools/advisor-async.mjs"
|
|
@@ -82,7 +84,10 @@ export async function recordToolResults(agent, toolByName, results) {
|
|
|
82
84
|
}
|
|
83
85
|
} catch { /* Parse failure doesn't affect normal tool messages */ }
|
|
84
86
|
}
|
|
85
|
-
|
|
87
|
+
// F-LC6(LIGHT-CHANNEL §2.9):主 agent 会话首笔产品源文件笔 ⇒ 回执末尾追加一行通道提醒
|
|
88
|
+
// (零拦截——只拼回执文本、入史时机不变;命中后本会话不再提示)。
|
|
89
|
+
const penNotice = unbookedPenNotice(agent, toolCall, ok, tool)
|
|
90
|
+
pushReal(agent, { role: "tool", tool_call_id: toolCall.id, name: toolCall.name, content: penNotice ? result + penNotice : result })
|
|
86
91
|
if (tool && ok) {
|
|
87
92
|
if (FILE_MUTATORS.has(toolCall.name)) {
|
|
88
93
|
// Direct file edit — code was changed. The prior advisor review and
|
|
@@ -88,7 +88,7 @@ export function manifestStateLine({ phase }) {
|
|
|
88
88
|
*/
|
|
89
89
|
export function manifestReportLine({ state, cwd, root, path, candidates, errors, matched } = {}) {
|
|
90
90
|
if (state === "no-project") {
|
|
91
|
-
return `${MANIFEST_LINE_PREFIX}none — no project at ${cwd} (no manifest
|
|
91
|
+
return `${MANIFEST_LINE_PREFIX}none — no project at ${cwd} (no manifest at this directory, none in the direct children). ` +
|
|
92
92
|
`Parameters fall back to defaults. Create PROJECT-MANIFEST.json here to land a project (git optional).]`
|
|
93
93
|
}
|
|
94
94
|
if (state === "workspace") {
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* unbooked-pen.mjs — 无账之笔提醒叶(批 unbooked-pen · 台账 #1753)。
|
|
3
|
+
* 设计权威 = `docs/core/design/LIGHT-CHANNEL.md` §2.9(F-LC6);需求 = `docs/core/requirements/LIGHT-CHANNEL.md` §2.6。
|
|
4
|
+
*
|
|
5
|
+
* 机制:每会话**首次**由主 agent 落下**产品源文件**笔时,在工具回执末尾追加一行通道确认提醒。
|
|
6
|
+
* 只提示——不拦、不放行、不改任何判据;文案为条件句(轻通道笔须先挂轮,全链笔交 eng-coder)。
|
|
7
|
+
* 承载形对齐先例 `agent-tools/zero-write-watch.mjs`(谓词叶 + 文案函数 + 闩 + 非阻塞)。
|
|
8
|
+
* 零环:只 import 既有单源(`FILE_MUTATORS` ∥ `toolTouchPaths` ∥ `isCodePath` ∥ `declarationForTarget`),
|
|
9
|
+
* 只被 `agent/record-results.mjs` import(不反向引)。
|
|
10
|
+
*/
|
|
11
|
+
import { resolve } from "node:path"
|
|
12
|
+
import { FILE_MUTATORS, toolTouchPaths } from "./helpers.mjs"
|
|
13
|
+
import { declarationForTarget } from "../declaration.mjs"
|
|
14
|
+
import { isCodePath } from "../conventions.mjs"
|
|
15
|
+
|
|
16
|
+
/** 笔集(§2.9 ②):`FILE_MUTATORS` **减** `delete`——减式派生,不另抄第二份工具名清单
|
|
17
|
+
* (`FILE_MUTATORS` 增删成员时本集自动跟上)。`delete` 不入集:删产品源文件须 `force`、
|
|
18
|
+
* 是显式动作,本批无场景(K20)。 */
|
|
19
|
+
export const PEN_TOOLS = new Set([...FILE_MUTATORS].filter((name) => name !== "delete"))
|
|
20
|
+
|
|
21
|
+
/** 文案单源(§2.9 文案块逐字——条件句 + 后果句;改文案只改这一处)。 */
|
|
22
|
+
export function unbookedPenText() {
|
|
23
|
+
return "[pen check] first product-source write this session — confirm the channel before it lands: a light-channel pen needs its round booked first (batch record + an in-flight ledger row pointing at the record); a full-chain pen goes to eng-coder. A pen with no account is invisible at closeout, so the ledger settlement misses it."
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 四合取 + 一闩判定(§2.9 触发面)——命中:置闩并返回回执后缀(`\n` + 文案行);未中:空串。
|
|
28
|
+
* ① 身份 = 主 agent(`_role == null`——主会话装配不传 role,子代理 spawn 面统一传角色)
|
|
29
|
+
* ② 工具 ∈ `PEN_TOOLS` ③ `ok === true` ④ 任一目标分类为产品源文件
|
|
30
|
+
* (逐目标 `resolve(cwd, p)` → `declarationForTarget(abs)` → `isCodePath(abs, conv)`——与父侧门
|
|
31
|
+
* `agent/dispatch.mjs:111-118` 同式;出辖目标不判,任一命中即触发)
|
|
32
|
+
* 闩 = `agent._unbookedPenNotified`(每会话一次——不按轮、不按文件)。
|
|
33
|
+
* @param {object} agent 主 agent(读 `cwd` ∥ `_role` ∥ 闩位)
|
|
34
|
+
* @param {{name: string, arguments?: string}} toolCall
|
|
35
|
+
* @param {boolean} ok 工具结果成功位(被拒的笔没有落盘,提醒无对象)
|
|
36
|
+
* @param {object} [tool] 工具对象——只用于 `toolTouchPaths` 的可选钩子 `touchedPaths`
|
|
37
|
+
* (`apply_patch` ∥ 多目标 `edit` 的目标解析靠它;缺省按单参 `path`)
|
|
38
|
+
* @returns {string} 命中 = 回执后缀;未中 = ""
|
|
39
|
+
*/
|
|
40
|
+
export function unbookedPenNotice(agent, toolCall, ok, tool) {
|
|
41
|
+
if (!agent || agent._role != null) return ""
|
|
42
|
+
if (!tool) return ""
|
|
43
|
+
if (ok !== true) return ""
|
|
44
|
+
if (agent._unbookedPenNotified) return ""
|
|
45
|
+
if (!PEN_TOOLS.has(toolCall?.name)) return ""
|
|
46
|
+
try {
|
|
47
|
+
const args = JSON.parse(toolCall.arguments || "{}")
|
|
48
|
+
const hit = toolTouchPaths(tool, args).some((p) => {
|
|
49
|
+
if (typeof p !== "string" || !p.trim()) return false
|
|
50
|
+
const abs = resolve(agent.cwd, p)
|
|
51
|
+
const conv = declarationForTarget(abs)
|
|
52
|
+
return !!conv && isCodePath(abs, conv)
|
|
53
|
+
})
|
|
54
|
+
if (!hit) return ""
|
|
55
|
+
} catch {
|
|
56
|
+
// 恒零抛(先例 zero-write-watch.mjs 同款):提醒是兜底面——判定失败不得中断回执提交
|
|
57
|
+
// (畸形 args ∥ 非标准装配的 cwd)。退回空串 = 本笔不提示,判据面零变。
|
|
58
|
+
return ""
|
|
59
|
+
}
|
|
60
|
+
agent._unbookedPenNotified = true
|
|
61
|
+
return `\n${unbookedPenText()}`
|
|
62
|
+
}
|
package/agent-tools/advisor.mjs
CHANGED
|
@@ -9,10 +9,9 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { runAdvisorReview, advisorIncompleteMarker, ADVISOR_LAUNCH_REFUSAL_PREFIX } from "../advisor/run.mjs"
|
|
11
11
|
import { resolveBatchDocPath } from "./batch.mjs"
|
|
12
|
-
//
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
import { normAbs } from "../agent/write-gate.mjs"
|
|
12
|
+
// 设计评审的文档受理面单源 = review-facts.mjs 的 resolveReviewDocPaths(判据正文 =
|
|
13
|
+
// docs/core/design/ADVISOR-GUARDS.md §12:相对形按会话项目根 / 绝对形照用 / 受理 = 项目内现存 `.md`)。
|
|
14
|
+
import { projectRootView } from "../manifest-discovery.mjs"
|
|
16
15
|
import { resolveReviewDocPaths } from "./review-facts.mjs"
|
|
17
16
|
import {
|
|
18
17
|
generateDesignToken,
|
|
@@ -62,7 +61,7 @@ export const advisorTool = {
|
|
|
62
61
|
documents: {
|
|
63
62
|
type: "array",
|
|
64
63
|
items: { type: "string" },
|
|
65
|
-
description: "Doc paths to review (design / requirements / referenced docs).
|
|
64
|
+
description: "Doc paths to review (design / requirements / referenced docs). One rule: a relative path resolves against the session project root (e.g. docs/core/design/ADVISOR-GUARDS.md); an absolute path is used as-is. Accepted = an existing .md file anywhere inside a project (a repo-root doc such as METHODOLOGY.md counts); anything else is refused with the resolved path, the reason, and the two expected forms. The advisor reviews ONLY these — no git diff scan. Use for both review types to pass the task's docs list.",
|
|
66
65
|
},
|
|
67
66
|
batchDoc: {
|
|
68
67
|
type: "string",
|
|
@@ -111,36 +110,21 @@ export const advisorTool = {
|
|
|
111
110
|
)
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
//
|
|
115
|
-
// resolveReviewDocPaths (2026-10-04 · #921 · the four-leg single authority in
|
|
116
|
-
// review-facts.mjs): legs ① session-root set / ② owning-project root set keep the
|
|
117
|
-
// legacy behavior verbatim; leg ③ probes candidate project roots for bare
|
|
118
|
-
// project-root-relative forms; leg ④ disambiguates by readable-file uniqueness
|
|
119
|
-
// (fail-closed — criterion=scope-doc-ambiguous when ≥2 candidates are readable).
|
|
113
|
+
// 设计评审:受理面 = 项目内现存 `.md`——相对形按会话项目根解析(越根 ⇒ 拒)· 绝对形照用 · 解析不出 ⇒ 拒并说清期望形;零试探、零歧义归一。
|
|
120
114
|
if (reviewType === "design" && documents) {
|
|
121
|
-
const {
|
|
122
|
-
if (invalid.length > 0
|
|
115
|
+
const { invalid } = resolveReviewDocPaths(documents, agent.cwd)
|
|
116
|
+
if (invalid.length > 0) {
|
|
123
117
|
if (ctx._toolCallId !== undefined) (agent._advisorRefusals ??= new Set()).add(ctx._toolCallId)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
)
|
|
118
|
+
// 拒发文案:既有稳定前缀逐字(行首)+ 逐文档「输入 → 解析后绝对路径 ∥ 拒因」+ 锚态(仅无项目根)+ 期望形两句。
|
|
119
|
+
const lines = invalid.map((v) => `- ${v.doc} → ${v.resolved ?? "(no base)"} ∥ reason=${v.reason}`)
|
|
120
|
+
if (invalid.some((v) => v.reason === "no-project-root")) {
|
|
121
|
+
const view = projectRootView(agent.cwd)
|
|
122
|
+
const where = view.state === "workspace" ? `workspace (${view.candidates.join(", ")})` : "none (no PROJECT-MANIFEST.json here or above)"
|
|
123
|
+
lines.push(`Session anchor is ${where} — no project root, so a relative path has no base to resolve against.`)
|
|
130
124
|
}
|
|
131
|
-
|
|
132
|
-
// 路径 ← 基面 = 会话 cwd」+ 候选项目根提示 cap 5 + 重试指引——纠正「误报因」缺陷)。
|
|
133
|
-
const invalidList = invalid.map((v) => v.doc).join(", ")
|
|
134
|
-
const attemptedList = invalid
|
|
135
|
-
.map((v) => `${v.doc} → ${v.attempted.length > 0 ? v.attempted.join(" | ") : normAbs(v.doc, agent.cwd)}`)
|
|
136
|
-
.join("; ")
|
|
137
|
-
const shown = candidates.slice(0, 5)
|
|
138
|
-
const more = candidates.length - shown.length
|
|
139
|
-
const candidateNote = shown.length > 0
|
|
140
|
-
? `(candidates: ${shown.join(", ")}${more > 0 ? ` +${more} more` : ""}) — pass project-root-relative (e.g. ${shown[0]}/docs/…) or absolute paths`
|
|
141
|
-
: "— pass project-root-relative or absolute paths"
|
|
125
|
+
lines.push("Expected form 1 — project-root-relative: docs/core/design/ADVISOR-GUARDS.md (resolved against the session project root).", "Expected form 2 — absolute: D:\\repo\\docs\\core\\design\\ADVISOR-GUARDS.md (used as-is).")
|
|
142
126
|
return withIdentityLine(
|
|
143
|
-
`Advisor: design review documents must be documentation files (per the project's conventions). Invalid
|
|
127
|
+
`Advisor: design review documents must be documentation files (per the project's conventions). Invalid:\n${lines.join("\n")}`,
|
|
144
128
|
{ type: reviewType, scope: scopeSummary(documents), round: "—", criterion: "scope-not-doc" },
|
|
145
129
|
)
|
|
146
130
|
}
|
|
@@ -3,74 +3,26 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 自 `subagent-spawn.mjs` **逐字搬移**(D23-3 外提:spawn 档越 300 软线 + 块构造成纯函数
|
|
5
5
|
* ⇒ 判据可直测;D23-4:块内文本零改——含块内自指词,整块搬移不改真值)。本档承载
|
|
6
|
-
* spawn 级固定机制性指令的第二块(审计模板 +
|
|
6
|
+
* spawn 级固定机制性指令的第二块(审计模板 + 批档 §2 指向 + touched 快照):
|
|
7
7
|
* 装配侧只收集(`thincoder-core/agent-tools/subagent-spawn.mjs`),下发面 = system 固块
|
|
8
8
|
* (`child._spawnSystemBlock` → 核 `prepareRun` 拼接——`thincoder-core/agent/setup.mjs`)。
|
|
9
|
+
*
|
|
10
|
+
* 2026-10-24(§6.22 KD-SC6):任务书 = 批次档 §2(子代理自读);A2 拼装摘要面(按 marker 抽取
|
|
11
|
+
* 设计档 / 文件清单 / 验收三段)已随 KD-SC6 删除——审计块只注入批档路径行 + §2 指向句(路径取
|
|
12
|
+
* `ctx.agent._batchDoc`)。理由:工程角色 `_engTaskInput` 只剩一行机械文本,摘要失去来源。
|
|
9
13
|
*/
|
|
10
14
|
|
|
11
|
-
/**
|
|
12
|
-
* AGENT-LOOP-SUBAGENT.md §6.7.6 D-TS5 (A2): mechanically summarize the parent spawn task book for the
|
|
13
|
-
* audit spawn — the three audit-relevant elements VERBATIM (design doc paths /
|
|
14
|
-
* affected-file list / acceptance criteria); verbose context/background is
|
|
15
|
-
* dropped (the auditor can read the design docs themselves — they stay
|
|
16
|
-
* available outside this input). Independence preserved: the input is
|
|
17
|
-
* _engTaskInput (mechanically kept by the parent spawn) — never the
|
|
18
|
-
* eng-coder's self-report. Sections are located by header marker, prioritizing
|
|
19
|
-
* header lines (structured task books: "## 文件清单 …") and falling back to
|
|
20
|
-
* inline markers (flat one-line task books); a section runs to the next header
|
|
21
|
-
* of the SAME OR HIGHER level ("## 文件清单" survives a "### 修改" sub-header).
|
|
22
|
-
* Marker not found → the section is reported as missing (never fabricate).
|
|
23
|
-
*/
|
|
24
|
-
export function summarizeEngTaskBook(taskInput) {
|
|
25
|
-
if (!taskInput) return "(unavailable)"
|
|
26
|
-
// 五项拼装形下,`designPoints` 一段同时承载「设计档落点」与「受影响文件清单」——
|
|
27
|
-
// 前两段同取该块是有意的(语义都在其中);代价 = 摘要里该块出现两次,不是抽取失败。
|
|
28
|
-
const SECTIONS = [
|
|
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] },
|
|
31
|
-
{ name: "Acceptance criteria", markers: [/Acceptance(?: criteria)?/i, /验收标准/] },
|
|
32
|
-
]
|
|
33
|
-
const lines = taskInput.split("\n")
|
|
34
|
-
const headerLevel = (l) => {
|
|
35
|
-
const m = l.match(/^\s*(#{1,6})\s/)
|
|
36
|
-
return m ? m[1].length : 0
|
|
37
|
-
}
|
|
38
|
-
const headerIdx = lines.map((l, i) => (headerLevel(l) > 0 ? i : -1)).filter((i) => i >= 0)
|
|
39
|
-
const boundsFor = (from, level) => {
|
|
40
|
-
for (const j of headerIdx) {
|
|
41
|
-
if (j > from && (level === 0 || headerLevel(lines[j]) <= level)) return j
|
|
42
|
-
}
|
|
43
|
-
return lines.length
|
|
44
|
-
}
|
|
45
|
-
const out = []
|
|
46
|
-
for (const { name, markers } of SECTIONS) {
|
|
47
|
-
let from = -1
|
|
48
|
-
let level = 0
|
|
49
|
-
for (const i of headerIdx) {
|
|
50
|
-
if (markers.some((m) => m.test(lines[i]))) { from = i; level = headerLevel(lines[i]); break }
|
|
51
|
-
}
|
|
52
|
-
if (from === -1) {
|
|
53
|
-
for (let i = 0; i < lines.length; i++) {
|
|
54
|
-
if (markers.some((m) => m.test(lines[i]))) { from = i; level = 0; break }
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (from === -1) { out.push(`${name}: (not found in the parent task book)`); continue }
|
|
58
|
-
const body = lines.slice(from, boundsFor(from, level)).join("\n").trim()
|
|
59
|
-
out.push(body || `${name}: (empty section)`)
|
|
60
|
-
}
|
|
61
|
-
return out.join("\n\n")
|
|
62
|
-
}
|
|
63
|
-
|
|
64
15
|
/**
|
|
65
16
|
* 审计块(spawn 级固定 ⇒ system 固块——§6.26 分类裁定表「审计块」行):纯函数——touched 快照
|
|
66
|
-
* (`ctx.agent._touchedFiles`——机制面证据,非 eng-coder 自述)+
|
|
67
|
-
* (`
|
|
68
|
-
* 报告格式五段模板**逐字**。零副作用(只读 ctx
|
|
69
|
-
* @param {{ agent?: { _touchedFiles?: string[],
|
|
17
|
+
* (`ctx.agent._touchedFiles`——机制面证据,非 eng-coder 自述)+ 批档路径行与 §2 指向句
|
|
18
|
+
* (`ctx.agent._batchDoc`——任务书由审计子代理自读 §2)+ 指令 / 范围 / 零 git 权威 / 预算 /
|
|
19
|
+
* 报告格式五段模板**逐字**。零副作用(只读 ctx)。
|
|
20
|
+
* @param {{ agent?: { _touchedFiles?: string[], _batchDoc?: string } }} ctx
|
|
70
21
|
* @returns {string} 块文本(不含与其它块之间的分隔符——分隔由装配侧 `join("\n\n")` 施加)
|
|
71
22
|
*/
|
|
72
23
|
export function buildAuditBlock(ctx) {
|
|
73
24
|
const touched = (ctx.agent._touchedFiles ?? []).map((f) => `- ${f}`).join("\n") || "- (none yet)"
|
|
25
|
+
const batchDoc = typeof ctx.agent?._batchDoc === "string" ? ctx.agent._batchDoc : "(unavailable)"
|
|
74
26
|
return `[Audit scope — mechanical context, independent of the eng-coder's self-report:]\n` +
|
|
75
27
|
// §6.7.6 D-TS4 A1:审计指令模板(四类偏差 + 范围限制 + 校验清单格式)——审计语义
|
|
76
28
|
// 不再靠模型自悟;范围限制是 §6.7.4 D-AG3 声明(下方 Zero-git scope authority)
|
|
@@ -85,9 +37,11 @@ export function buildAuditBlock(ctx) {
|
|
|
85
37
|
`workspace changes not listed there are unrelated to this delivery and are NOT grounds for an out-of-list finding.\n` +
|
|
86
38
|
`Scope discipline (F-TS6 A1): read ONLY the audited files and the design-doc sections relevant to this delivery — do NOT re-read whole documents.\n` +
|
|
87
39
|
`Every deviation item MUST be fieldized: file:line + design reference (doc path + section/AC id) + severity + evidence (quoted code or doc text).\n` +
|
|
88
|
-
// §6.
|
|
89
|
-
|
|
90
|
-
|
|
40
|
+
// §6.22 KD-SC6:任务书 = 批次档 §2(审计子代理自读)——审计块注入批档路径 + §2 指向句
|
|
41
|
+
// (A2 拼装摘要面已删:工程角色 _engTaskInput = 一行机械文本,摘要无来源)。审计子代理本身是
|
|
42
|
+
// explore(任务书 = 父侧五项)——故指向句限定为「**交付**的任务书 = §2」,不冒称本 child 的唯一通道。
|
|
43
|
+
`Batch record (batchDoc): ${batchDoc}\n` +
|
|
44
|
+
`Delivery task book: §2 of the batch record at ${batchDoc} — read it before judging the delivery; it is the criterion source for this audit.\n` +
|
|
91
45
|
`Files actually touched by the eng-coder (mechanical union — audit these against the file list):\n${touched}\n` +
|
|
92
46
|
// §18.5 D-AG3(2026-09-04):审计零 git 范围权威声明——本审计任务零 git(不注入
|
|
93
47
|
// git 上下文——§6.7.4 全角色零 git);_touchedFiles 为审计范围;工作区未列于
|
|
@@ -95,7 +49,7 @@ export function buildAuditBlock(ctx) {
|
|
|
95
49
|
"Zero-git scope authority: this audit task receives NO git context — nothing is injected. " +
|
|
96
50
|
"The evidence base is the design documents, the current disk state (read/glob/grep), and the _touchedFiles list above. " +
|
|
97
51
|
"Workspace changes NOT listed in _touchedFiles are unrelated to this delivery — they are NOT grounds for an out-of-file-list finding." +
|
|
98
|
-
// §6.7.6 D-A1.2:审计预算句——A1 指令模板 +
|
|
52
|
+
// §6.7.6 D-A1.2:审计预算句——A1 指令模板 + 批档指向块之后、A3 报告模板之前(定序——评审 #7)。
|
|
99
53
|
// 逐字设计锚(D-A1.2 代码块):只读该读的——10 轮机械预算——超时报 PROBLEM 下结论。
|
|
100
54
|
// 前导 \n 与 A3 同款块分隔约定(上一句 Zero-git 句末无换行——不触碰既有句)。
|
|
101
55
|
`\n[Audit budget — mechanical]: read ONLY the touched files listed above and the design-doc sections the parent task book names (affected-files table, acceptance criteria, status line). Do NOT read whole documents. Budget = 10 tool rounds max — if you cannot conclude within it, report PROBLEM (inconclusive) rather than continuing to explore.\n` +
|