@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/ledger-tools.mjs
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* 接线:族出口 = `ledger.mjs`(re-export——消费面 agent/family-tools.mjs;旧读二装配面已清零);
|
|
11
11
|
* 消费侧一律动态 import(`ledger-db.mjs` 静态 import node:sqlite ⇒ W8 契约②)。
|
|
12
12
|
*/
|
|
13
|
-
import { ledgerAdd, ledgerClose, ledgerCount, ledgerQuery, ledgerUpdate } from "./ledger-cmd.mjs"
|
|
13
|
+
import { ledgerAdd, ledgerClose, ledgerCloseBatch, ledgerCount, ledgerQuery, ledgerUpdate, ledgerUpdateBatch } from "./ledger-cmd.mjs"
|
|
14
14
|
import { resolveLedgerRoot } from "./ledger-db.mjs"
|
|
15
15
|
import { DESC } from "./tools/shared.mjs" // #15 描述外置:文本单点 = tool-docs/ledger.md(DESC 单解析面)
|
|
16
16
|
|
|
@@ -37,37 +37,47 @@ const argPreview = (v) => {
|
|
|
37
37
|
/** 枚举取值域文本(P3 / P5 文案同源)。 */
|
|
38
38
|
const enumText = (values) => `{${values.join(", ")}}`
|
|
39
39
|
|
|
40
|
-
/** P3 后缀(声明派生):枚举字段带取值域 / 非枚举必填串带「非空字符串」/
|
|
41
|
-
|
|
40
|
+
/** P3 后缀(声明派生):枚举字段带取值域 / 非枚举必填串带「非空字符串」/ 数字字段仅「必填」
|
|
41
|
+
* (`domain` = 按形条件判据给出的取值域——`whenPresent.enum` 优先于基本形)。 */
|
|
42
|
+
const requiredSuffix = (spec, domain) => (domain ? `;取值 ∈ ${enumText(domain)}` : spec.type === "string" ? ";非空字符串" : "")
|
|
43
|
+
|
|
44
|
+
/** 值非空字段(P6 字段级:空串 / 全空白 ⇒ 拒——声明面不表达值级判据,与 `title` 同源一处)。 */
|
|
45
|
+
const NON_EMPTY_FIELDS = ["title", "resume_when"]
|
|
42
46
|
|
|
43
47
|
/**
|
|
44
|
-
* 工具层参数守卫(设计档 §3.2 · Gitee #IKIQGK / 台账 #472):判序 P1–
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* ⇒ `{}`)。非法 ⇒ throw——判于核函数之前(零写、零库动作)。
|
|
48
|
+
* 工具层参数守卫(设计档 §3.2 · Gitee #IKIQGK / 台账 #472):判序 P1–P7,文案逐字 = §3.2 模板;
|
|
49
|
+
* 消费工具自身声明派生(`required` / `type` / `enum` / `oneOfRequired` / `whenPresent`——枚举 / 必填 /
|
|
50
|
+
* 类型 / 组判据不在守卫内复写);只判不改(零缺省填充 / 零类型转换 / 零文本改写),返回入参对象
|
|
51
|
+
* (唯一归一 = 判序① 的缺省 / `null` ⇒ `{}`)。非法 ⇒ throw——判于核函数之前(零写、零库动作)。
|
|
52
|
+
* 按形条件判据(`whenPresent`):`when` 字段在本笔入参**在场**(非 `null`)⇒ 该条生效(`required` 按
|
|
53
|
+
* P3 判、`enum` 按 P5 判并取代基本形取值域);不在场 ⇒ 本条不生效、按基本形判。
|
|
48
54
|
*/
|
|
49
55
|
function assertToolArgs(tool, args) {
|
|
50
56
|
const name = tool.name
|
|
51
57
|
const decl = tool.parameters ?? {}
|
|
52
58
|
const props = decl.properties ?? {}
|
|
53
59
|
const fields = Object.keys(props)
|
|
60
|
+
// 组判据两键居声明条目顶层(不入 `parameters`——自定义键不随声明交模型 / provider)
|
|
61
|
+
const conditional = tool.whenPresent ?? []
|
|
54
62
|
// P1:入参缺省 / `null` ⇒ 视作 `{}`;非对象(数组 / 标量)⇒ 拒
|
|
55
63
|
if (args == null) args = {}
|
|
56
64
|
else if (typeof args !== "object" || Array.isArray(args)) throw new Error(`${name}:参数须为对象(收到 ${argPreview(args)})`)
|
|
57
65
|
// P2:未知键 ⇒ 拒(判序先于缺参——typo 键先给可用参数集)
|
|
58
66
|
const unknown = Object.keys(args).filter((k) => !Object.hasOwn(props, k))
|
|
59
67
|
if (unknown.length > 0) throw new Error(`${name}:未知参数:${unknown.join(" / ")}(可用参数 = ${fields.join(" / ")})`)
|
|
60
|
-
// P3–P6:逐字段(声明序)——缺失(必填;显式 `null` 同判)→ 类型 / 枚举 →
|
|
68
|
+
// P3–P6:逐字段(声明序)——缺失(必填;显式 `null` 同判)→ 类型 / 枚举 → 值非空
|
|
61
69
|
for (const field of fields) {
|
|
62
70
|
const spec = props[field]
|
|
63
71
|
const value = args[field]
|
|
64
|
-
const
|
|
72
|
+
const rule = conditional.find((c) => c.field === field && args[c.when] != null)
|
|
73
|
+
const required = rule?.required ?? (decl.required ?? []).includes(field)
|
|
74
|
+
const domain = rule?.enum ?? spec.enum
|
|
65
75
|
if (value === undefined || value === null) {
|
|
66
|
-
if (required) throw new Error(`${name}:${field} 缺失(必填${requiredSuffix(spec)})`)
|
|
76
|
+
if (required) throw new Error(`${name}:${field} 缺失(必填${requiredSuffix(spec, domain)})`)
|
|
67
77
|
continue // 可空字段 `null` ⇒ 放行(≡ 略去——与 `patch.x ?? 行值` 既有语义同源)
|
|
68
78
|
}
|
|
69
|
-
if (
|
|
70
|
-
if (!
|
|
79
|
+
if (domain) {
|
|
80
|
+
if (!domain.includes(value)) throw new Error(`${name}:${field} 非法:${argPreview(value)}(取值 ∈ ${enumText(domain)})`)
|
|
71
81
|
} else if (spec.type === "string" && typeof value !== "string") {
|
|
72
82
|
throw new Error(`${name}:${field} 非法:${argPreview(value)}(应为字符串)`)
|
|
73
83
|
} else if (spec.type === "number" && typeof value !== "number") {
|
|
@@ -75,10 +85,16 @@ function assertToolArgs(tool, args) {
|
|
|
75
85
|
} else if (spec.type === "boolean" && typeof value !== "boolean") {
|
|
76
86
|
throw new Error(`${name}:${field} 非法:${argPreview(value)}(应为布尔)`)
|
|
77
87
|
}
|
|
78
|
-
if (field
|
|
88
|
+
if (NON_EMPTY_FIELDS.includes(field) && typeof value === "string" && value.trim() === "") {
|
|
79
89
|
throw new Error(`${name}:${field} 为空(${required ? "必填;" : ""}非空字符串)`)
|
|
80
90
|
}
|
|
81
91
|
}
|
|
92
|
+
// P7:组判据(声明派生 `oneOfRequired`)——组内恰携其一 ⇒ 过;同携 / 双缺 ⇒ 拒
|
|
93
|
+
for (const group of tool.oneOfRequired ?? []) {
|
|
94
|
+
if (group.filter((f) => args[f] != null).length === 1) continue
|
|
95
|
+
const received = Object.fromEntries(group.map((f) => [f, args[f] ?? null]))
|
|
96
|
+
throw new Error(`${name}:${group.join(" ∥ ")} 须恰携其一(收到 ${argPreview(received)})`)
|
|
97
|
+
}
|
|
82
98
|
return args
|
|
83
99
|
}
|
|
84
100
|
|
|
@@ -91,68 +107,90 @@ const READ_ACTIONS = ["query", "count"]
|
|
|
91
107
|
/** cwd 参数描述(五前身声明同文——文本单点)。 */
|
|
92
108
|
const CWD_DESC = "本次动作的目标路径——项目根由它推导:沿祖先链最近的项目目录(带 PROJECT-MANIFEST.json 或已有台账库);容器目录下收敛到子项目。台账按项目根关联存储于用户数据目录(工作树外、不进 git)。相对路径按当前工作目录解析;查/写别的项目请给绝对路径。缺省 = 当前会话锚"
|
|
93
109
|
|
|
94
|
-
/** 各 action 声明条目(= 前身旧工具 `parameters` 逐字;伪工具对象守卫 + 弃用壳共用单源)。
|
|
110
|
+
/** 各 action 声明条目(= 前身旧工具 `parameters` 逐字;伪工具对象守卫 + 弃用壳共用单源)。
|
|
111
|
+
* `parameters` 内的键 = 交模型 / provider 的那一份声明;**组判据两键(`oneOfRequired` / `whenPresent`)
|
|
112
|
+
* 居本条条目顶层、不入 `parameters`**——自定义键不随声明外泄(provider 面零风险),守卫仍只读声明。 */
|
|
95
113
|
const ACTION_SPECS = {
|
|
96
114
|
add: {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
115
|
+
parameters: {
|
|
116
|
+
type: "object",
|
|
117
|
+
required: ["kind", "title"],
|
|
118
|
+
properties: {
|
|
119
|
+
cwd: { type: "string", description: CWD_DESC },
|
|
120
|
+
kind: { type: "string", enum: ["requirement", "tech_todo"], description: "类别" },
|
|
121
|
+
title: { type: "string", description: "条目标题" },
|
|
122
|
+
board: { type: "string", description: "归属板块(需求档文档名,可空)" },
|
|
123
|
+
req_doc: { type: "string", description: "需求档指针(可空)" },
|
|
124
|
+
task_book: { type: "string", description: "任务书指针(可空;在途 / 待核销必填——咬合 CHECK)" },
|
|
125
|
+
evidence: { type: "string", description: "最小证据行(可空)" },
|
|
126
|
+
trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "技术待办触发(可空)" },
|
|
127
|
+
resume_when: { type: "string", description: "复启条件句(可空;`trigger=条件` 行的一句话条件 + 可选锚 `@YYYY-MM-DD` ∥ `@事件件名`)——值非空:空串 / 全空白 ⇒ 拒" },
|
|
128
|
+
},
|
|
129
|
+
additionalProperties: false,
|
|
108
130
|
},
|
|
109
|
-
additionalProperties: false,
|
|
110
131
|
},
|
|
111
132
|
update: {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
133
|
+
parameters: {
|
|
134
|
+
type: "object",
|
|
135
|
+
properties: {
|
|
136
|
+
cwd: { type: "string", description: CWD_DESC },
|
|
137
|
+
id: { type: "number", description: "条目 id(与 batch 恰携其一)" },
|
|
138
|
+
batch: { type: "string", description: "批档指针(与 id 恰携其一)——批级批量:一次结算该批的行;携 status ⇒ 逐行按迁移表重判,不携 status ⇒ 纯字段补丁(status 零写)" },
|
|
139
|
+
status: { type: "string", enum: ["待讨论", "待设计", "在途", "待核销", "已核销", "已废弃"], description: "目标状态(六态之一,缺省 = 不变)" },
|
|
140
|
+
title: { type: "string", description: "标题(缺省 = 不变)" },
|
|
141
|
+
board: { type: "string", description: "归属板块(缺省 = 不变)" },
|
|
142
|
+
req_doc: { type: "string", description: "需求档指针(缺省 = 不变)" },
|
|
143
|
+
task_book: { type: "string", description: "任务书指针(缺省 = 不变)" },
|
|
144
|
+
evidence: { type: "string", description: "证据(缺省 = 不变;传入 = 追加——旧值非空 ⇒ 拼 `旧|新`(「|」零空格)、旧空直写;新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传)" },
|
|
145
|
+
evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换(旧文零保留);缺省 / false = 追加;旗独传(无新文)⇒ 拒" },
|
|
146
|
+
trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "触发(缺省 = 不变)" },
|
|
147
|
+
resume_when: { type: "string", description: "复启条件句(缺省 = 不变;值非空——空串 / 全空白 ⇒ 拒)" },
|
|
148
|
+
executor: { type: "string", description: "执行者 sessionId(可选——接手改写归属用;缺省 = 按状态迁移语义:进入待设计 / 在途自动写本会话;出在途 / 撤回清空)" },
|
|
149
|
+
},
|
|
150
|
+
additionalProperties: false,
|
|
126
151
|
},
|
|
127
|
-
|
|
152
|
+
oneOfRequired: [["id", "batch"]],
|
|
128
153
|
},
|
|
129
154
|
close: {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
155
|
+
parameters: {
|
|
156
|
+
type: "object",
|
|
157
|
+
required: ["status"],
|
|
158
|
+
properties: {
|
|
159
|
+
cwd: { type: "string", description: CWD_DESC },
|
|
160
|
+
id: { type: "number", description: "条目 id(与 batch 恰携其一)" },
|
|
161
|
+
batch: { type: "string", description: "批档指针(与 id 恰携其一)——批级批量结算:只销已到 待核销 的行;待讨论 / 待设计 点名跳过(追认核销不入批量面)" },
|
|
162
|
+
status: { type: "string", enum: ["已核销", "已废弃"], description: "目标态(勾销 / 追认核销 / 撤回)——携 batch 时可不给(缺省 = 勾销单值),给了只能是 已核销" },
|
|
163
|
+
evidence: { type: "string", description: "最小证据行(可选;传入 = 追加缺省——旧值非空 ⇒ 拼 `旧|新`(「|」零空格)、旧空直写;新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传。追认核销面判结果值须非空:缺 / 全空白 ⇒ 拒)" },
|
|
164
|
+
evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换(旧文零保留);缺省 / false = 追加;旗独传(无新文)⇒ 拒" },
|
|
165
|
+
},
|
|
166
|
+
additionalProperties: false,
|
|
138
167
|
},
|
|
139
|
-
|
|
168
|
+
oneOfRequired: [["id", "batch"]],
|
|
169
|
+
whenPresent: [{ when: "batch", field: "status", required: false, enum: ["已核销"] }],
|
|
140
170
|
},
|
|
141
171
|
query: {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
172
|
+
parameters: {
|
|
173
|
+
type: "object",
|
|
174
|
+
properties: {
|
|
175
|
+
cwd: { type: "string", description: CWD_DESC },
|
|
176
|
+
status: { type: "string", enum: ["待讨论", "待设计", "在途", "待核销", "已核销", "已废弃"], description: "按状态过滤(六态之一,缺省 = 不过滤)" },
|
|
177
|
+
kind: { type: "string", enum: ["requirement", "tech_todo"], description: "按类别过滤" },
|
|
178
|
+
board: { type: "string", description: "按归属板块过滤(需求档文档名)" },
|
|
179
|
+
trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "按技术待办触发过滤(三枚举,缺省 = 不过滤——无触发(NULL)不过滤)" },
|
|
180
|
+
task_book: { type: "string", description: "按任务书指针过滤(原值等值——批→行反查;缺省 = 不过滤)" },
|
|
181
|
+
},
|
|
182
|
+
additionalProperties: false,
|
|
149
183
|
},
|
|
150
|
-
additionalProperties: false,
|
|
151
184
|
},
|
|
152
185
|
count: {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
186
|
+
parameters: {
|
|
187
|
+
type: "object",
|
|
188
|
+
properties: {
|
|
189
|
+
cwd: { type: "string", description: CWD_DESC },
|
|
190
|
+
task_book: { type: "string", description: "按任务书指针过滤(原值等值)" },
|
|
191
|
+
},
|
|
192
|
+
additionalProperties: false,
|
|
193
|
+
},
|
|
156
194
|
},
|
|
157
195
|
}
|
|
158
196
|
|
|
@@ -171,8 +209,10 @@ const UNION_PARAMETERS = {
|
|
|
171
209
|
evidence: { type: "string", description: "最小证据行(add 可空;update / close 传入 = 追加缺省——旧空直写、旧非空拼 `旧|新`(「|」零空格)、新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传;update 缺省 = 不变 / close 缺省 = 判行值;追认核销须非空)" },
|
|
172
210
|
evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换;缺省 / false = 追加;旗独传(无新文)⇒ 拒(update / close)" },
|
|
173
211
|
trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "技术待办触发(add / update 可空 / query 过滤)" },
|
|
174
|
-
|
|
175
|
-
|
|
212
|
+
resume_when: { type: "string", description: "复启条件句(add / update 可空;值非空——空串 / 全空白 ⇒ 拒;`trigger=条件` 行的一句话条件 + 可选锚 `@YYYY-MM-DD` ∥ `@事件件名`)" },
|
|
213
|
+
id: { type: "number", description: "条目 id(update / close:与 batch 恰携其一)" },
|
|
214
|
+
batch: { type: "string", description: "批档指针(update / close:与 id 恰携其一)——批级批量:一次结算该批的行(update 携 status ⇒ 逐行迁移表重判;不携 status ⇒ 纯字段补丁;close 只销 待核销 行、status 可空)" },
|
|
215
|
+
status: { type: "string", enum: ["待讨论", "待设计", "在途", "待核销", "已核销", "已废弃"], description: "状态(单行 close 必填 ∈ {已核销, 已废弃};close 携 batch 时可空、给了只能是 已核销;update 迁移 / query 过滤——六态之一)" },
|
|
176
216
|
executor: { type: "string", description: "执行者 sessionId(update 可选——接手改写归属;缺省 = 按状态迁移语义:进入待设计 / 在途自动写本会话;出在途 / 撤回清空)" },
|
|
177
217
|
},
|
|
178
218
|
additionalProperties: false,
|
|
@@ -210,8 +250,10 @@ function makeLedgerTool(actions, { readonly = false, writeFace = false } = {}) {
|
|
|
210
250
|
// ② 入口级 action 判(P3 缺失 / P5 非法——消费 action;取值域 = 本装配面枚举)
|
|
211
251
|
const { action, ...rest0 } = args
|
|
212
252
|
assertToolArgs(entryTool, { action })
|
|
213
|
-
// ③ 余键喂该 action 伪工具对象(声明源 = 前身工具 parameters 逐字——`action` 键不入 P2
|
|
214
|
-
|
|
253
|
+
// ③ 余键喂该 action 伪工具对象(声明源 = 前身工具 parameters 逐字——`action` 键不入 P2 域;
|
|
254
|
+
// 组判据两键在条目顶层,与 `parameters` 同送守卫)
|
|
255
|
+
const spec = ACTION_SPECS[action]
|
|
256
|
+
const rest = assertToolArgs({ name: `ledger(${action})`, ...spec }, rest0)
|
|
215
257
|
// ④ 路由既有核函数(包装细节携自原工具 execute 逐字保留:cwd 缺省 / JSON 缩进 / getSessionId 动态 import)
|
|
216
258
|
switch (action) {
|
|
217
259
|
case "add": {
|
|
@@ -221,7 +263,7 @@ function makeLedgerTool(actions, { readonly = false, writeFace = false } = {}) {
|
|
|
221
263
|
return JSON.stringify({ id, status: "待讨论", ...reportedRoot(target) })
|
|
222
264
|
}
|
|
223
265
|
case "update": {
|
|
224
|
-
const { cwd, id, ...patch } = rest
|
|
266
|
+
const { cwd, id, batch, ...patch } = rest
|
|
225
267
|
const target = cwd ?? cwdOf(ctx)
|
|
226
268
|
let executorSessionId
|
|
227
269
|
if (patch.executor == null) {
|
|
@@ -229,17 +271,21 @@ function makeLedgerTool(actions, { readonly = false, writeFace = false } = {}) {
|
|
|
229
271
|
const { getSessionId } = await import("./session-slots.mjs")
|
|
230
272
|
executorSessionId = getSessionId()
|
|
231
273
|
}
|
|
274
|
+
// 批级批量(§3.3)——`batch` 与 `id` 恰携其一(守卫 P7 已先裁)
|
|
275
|
+
if (batch != null) return JSON.stringify({ ...ledgerUpdateBatch({ cwd: target, batch, patch, executorSessionId }), ...reportedRoot(target) })
|
|
232
276
|
return JSON.stringify({ ...ledgerUpdate({ cwd: target, id, patch, executorSessionId }), ...reportedRoot(target) })
|
|
233
277
|
}
|
|
234
278
|
case "close": {
|
|
235
279
|
const target = rest.cwd ?? cwdOf(ctx)
|
|
280
|
+
// 批级批量结算(§3.3——勾销径)
|
|
281
|
+
if (rest.batch != null) return JSON.stringify({ ...ledgerCloseBatch({ cwd: target, batch: rest.batch, evidence: rest.evidence, evidenceReplace: rest.evidenceReplace }), ...reportedRoot(target) })
|
|
236
282
|
return JSON.stringify({ ...ledgerClose({ cwd: target, id: rest.id, status: rest.status, evidence: rest.evidence, evidenceReplace: rest.evidenceReplace }), ...reportedRoot(target) })
|
|
237
283
|
}
|
|
238
284
|
case "query":
|
|
239
|
-
return JSON.stringify(ledgerQuery({ cwd: rest.cwd ?? cwdOf(ctx), status: rest.status, kind: rest.kind, board: rest.board, trigger: rest.trigger }), null, 2)
|
|
285
|
+
return JSON.stringify(ledgerQuery({ cwd: rest.cwd ?? cwdOf(ctx), status: rest.status, kind: rest.kind, board: rest.board, trigger: rest.trigger, task_book: rest.task_book }), null, 2)
|
|
240
286
|
case "count": {
|
|
241
287
|
const target = rest.cwd ?? cwdOf(ctx)
|
|
242
|
-
return JSON.stringify({
|
|
288
|
+
return JSON.stringify({ ...ledgerCount({ cwd: target, task_book: rest.task_book }), ...reportedRoot(target) })
|
|
243
289
|
}
|
|
244
290
|
default:
|
|
245
291
|
// 不可达(② 枚举守门已先拒)——fail-loud 兜底(防未来变体增量 drift)
|
|
@@ -262,7 +308,7 @@ const deprecatedLine = (old, action) => `${old} 已退役(本名不再装配
|
|
|
262
308
|
const deprecatedShell = (old, action, readonly) => ({
|
|
263
309
|
name: old,
|
|
264
310
|
description: deprecatedLine(old, action),
|
|
265
|
-
parameters: ACTION_SPECS[action],
|
|
311
|
+
parameters: ACTION_SPECS[action].parameters,
|
|
266
312
|
readonly,
|
|
267
313
|
async execute() { throw new Error(deprecatedLine(old, action)) },
|
|
268
314
|
})
|
package/ledger.mjs
CHANGED
|
@@ -65,9 +65,11 @@ export function discoverFamily(anchor) {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
/** scan 组装(SQLite 行集 + 计数单源 → scan 对象)——形状契约(设计档 §2.2):pool/tech/aged/
|
|
68
|
-
* agedKeys/agedTitles/thresholdReached/boards/actionable/root/name/ledger。行龄 = 时间戳
|
|
68
|
+
* agedKeys/agedTitles/overdue/thresholdReached/boards/actionable/root/name/ledger。行龄 = 时间戳
|
|
69
69
|
* (updated_at ?? created_at 距今 > days;未知不计——零假阳降级,同 v1 行龄未知口径)。
|
|
70
70
|
* `now`/`days` = 注入面(确定性用例——替代 v1 ageOf git 注入面)。
|
|
71
|
+
* **`days` 只作用于展示面 `aged`**(本函数局部阈值);`overdue`(出口态)恒按核常量 `AGING_DAYS`
|
|
72
|
+
* (`overdueOf` 单源)——故 `days` 调小时 `aged` 会先于 `overdue` 涨(两数口径不同,非矛盾)。
|
|
71
73
|
* **根推导**(设计档 §3.8):`root` / `name` / `ledger` = `resolveLedgerRoot(anchor)` 推导所得项目根(非锚
|
|
72
74
|
* ——容器 ∥ 工作区锚取收敛序之根)——与行集读取同一单源;无根 ⇒ `ledger` = `null`(不落兜底根——
|
|
73
75
|
* 该态下 `root` / `name` 回落锚、行集为空,且该 scan 不进族枚举(`discoverFamily` 容器支只列已注册子目录))。
|
|
@@ -77,7 +79,7 @@ export function buildScan({ cwd, days = AGING_DAYS, now = Date.now() } = {}) {
|
|
|
77
79
|
const anchor = resolve(cwd)
|
|
78
80
|
const derived = resolveLedgerRoot(anchor)
|
|
79
81
|
const root = derived ? derived.root : anchor
|
|
80
|
-
const rows = ledgerQuery({ cwd: anchor })
|
|
82
|
+
const rows = ledgerQuery({ cwd: anchor, now }) // `now` 透传(两读数同时钟——`overdue` 的行键由核同一 `now` 算)
|
|
81
83
|
const pending = rows.filter((r) => PENDING_STATUSES.includes(r.status))
|
|
82
84
|
const poolEntries = pending.filter((r) => r.kind === "requirement")
|
|
83
85
|
const techEntries = pending.filter((r) => r.kind === "tech_todo")
|
|
@@ -98,6 +100,7 @@ export function buildScan({ cwd, days = AGING_DAYS, now = Date.now() } = {}) {
|
|
|
98
100
|
return {
|
|
99
101
|
name: basename(root), root, ledger: derived ? ledgerDbPath(root) : null,
|
|
100
102
|
pool: poolEntries.length, tech: techEntries.length, aged: aged.length,
|
|
103
|
+
overdue: pending.filter((r) => r.overdue != null).length, // 待处置(§3.7——机判单源 = 行集 `overdue` 键,行值与 `now` 均同源)
|
|
101
104
|
agedKeys: aged.map((r) => normalizeEntry(r.title)), agedTitles: aged.map((r) => entryTitle(r.title)),
|
|
102
105
|
thresholdReached, boards, actionable: aged.length > 0 || thresholdReached,
|
|
103
106
|
inflightExecutors,
|
|
@@ -113,8 +116,8 @@ export function formatMarker(scan) {
|
|
|
113
116
|
* `root` = `family.current.root` 的 scan(命中但不在 `scans`——不可读 ⇒ 空范围,不掺兄弟);
|
|
114
117
|
* `current` 缺席(容器根锚)⇒ 范围 = `scans` 全体(族内已读项目——构建期不可读已跳过)。
|
|
115
118
|
* 空范围 ⇒ `{ marker: null, warn: false }`(不落 `0·0`);范围非空 ⇒ `marker` = `formatMarker`
|
|
116
|
-
* 逐字(两池分列求和——F1);`warn` = 范围内任一项 `
|
|
117
|
-
*
|
|
119
|
+
* 逐字(两池分列求和——F1);`warn` = 范围内任一项 `overdue>0 ∨ deadExecutors>0`(§3.7——`aged ⊂
|
|
120
|
+
* overdue` ⇒ 警示色不缩;端零重算)。消费面 = 核拍面(`ledger-surface.mjs`)∥ VSC item。 */
|
|
118
121
|
export function scopeMarkerOf(scans, family) {
|
|
119
122
|
const list = Array.isArray(scans) ? scans : []
|
|
120
123
|
const currentRoot = family?.current?.root ?? null
|
|
@@ -126,15 +129,15 @@ export function scopeMarkerOf(scans, family) {
|
|
|
126
129
|
for (const s of range) {
|
|
127
130
|
pool += s.pool
|
|
128
131
|
tech += s.tech
|
|
129
|
-
if (s.
|
|
132
|
+
if (s.overdue > 0 || s.deadExecutors > 0) warn = true
|
|
130
133
|
}
|
|
131
134
|
return { marker: formatMarker({ pool, tech }), warn }
|
|
132
135
|
}
|
|
133
136
|
|
|
134
|
-
/** L2 明细行(逐字——`(老化 <n
|
|
135
|
-
*
|
|
137
|
+
/** L2 明细行(逐字——`(老化 <n> · 待处置 <m>)` 两数恒显(含 0——§3.7 出口面恒显位扩);
|
|
138
|
+
* 阈值达成加 ` — 可开批`;判活尾段 = executorTail 三态——在途 executor 缺席时尾段空串)。 */
|
|
136
139
|
export function formatDetailLine(scan) {
|
|
137
|
-
return `台账 ${scan.name}:需求池 ${scan.pool} · 技术待办 ${scan.tech}(老化 ${scan.aged})${scan.thresholdReached ? " — 可开批" : ""}${executorTail(scan)}`
|
|
140
|
+
return `台账 ${scan.name}:需求池 ${scan.pool} · 技术待办 ${scan.tech}(老化 ${scan.aged} · 待处置 ${scan.overdue})${scan.thresholdReached ? " — 可开批" : ""}${executorTail(scan)}`
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
/** 明细行集 = {current} ∪ {可动作项目}(发现序——current 在前,其余按目录名升序;同项去重)。 */
|
|
@@ -148,8 +151,8 @@ export function detailScans(scans, current) {
|
|
|
148
151
|
// ── re-export(拆分件接口——命令面接线 = 动态 import 本档,KD-M2-3;`AGING_DAYS` 常量单源 = `ledger-db.mjs`
|
|
149
152
|
// ——2026-10-07 批 ledger-tool 自本档迁入,公共面零变;`ledgerChildren` / `MAX_SIBLING_SCAN` / `resolveLedgerRoot`
|
|
150
153
|
// ——2026-10-16 批 ledger-root-derivation 随迁 / 新增,公共面形零改) ──
|
|
151
|
-
export { AGING_DAYS, ALLOWED_MIGRATIONS, MAX_SIBLING_SCAN, ledgerChildren, ledgerDbPath, ledgerKey, ledgerDirPath, openLedger, PENDING_STATUSES, resolveLedgerRoot, _setLedgerDirForTest, _resetLedgerDirForTest, ensureExecutorColumn } from "./ledger-db.mjs"
|
|
152
|
-
export { ledgerAdd, ledgerClose, ledgerCount, ledgerQuery, ledgerUpdate } from "./ledger-cmd.mjs"
|
|
154
|
+
export { AGING_DAYS, ALLOWED_MIGRATIONS, EVIDENCE_SOFT_LIMIT, MAX_SIBLING_SCAN, ledgerChildren, ledgerDbPath, ledgerKey, ledgerDirPath, openLedger, PENDING_STATUSES, resolveLedgerRoot, _setLedgerDirForTest, _resetLedgerDirForTest, ensureColumn, ensureExecutorColumn } from "./ledger-db.mjs"
|
|
155
|
+
export { ledgerAdd, ledgerBatchRows, ledgerClose, ledgerCloseBatch, ledgerCount, ledgerEvidenceOversize, ledgerQuery, ledgerReanchorBatch, ledgerUpdate, ledgerUpdateBatch, overdueOf } from "./ledger-cmd.mjs"
|
|
153
156
|
export { ledgerTool, ledgerReadTool, ledgerAddTool, ledgerCloseTool, ledgerCountTool, ledgerQueryTool, ledgerUpdateTool } from "./ledger-tools.mjs"
|
|
154
157
|
export { runLedgerAudit, runLedgerMigrate } from "./ledger-migrate.mjs"
|
|
155
158
|
export { resolveExecutorStates, executorTail, _setExecutorProbeTtlForTest } from "./ledger-executors.mjs"
|
package/log.mjs
CHANGED
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
* → 整个字段丢弃;
|
|
19
19
|
* ② 内容只扫密钥形态(sk-xxx / Bearer xxx / key=… / token 值形等)→ 截断到形态之前
|
|
20
20
|
* ——宁可丢信息不漏密钥。
|
|
21
|
-
* token 值形(`uuid:epoch`——2026-10-14
|
|
22
|
-
*
|
|
21
|
+
* token 值形(`uuid:epoch`——2026-10-14 批并入):工具结果经 `tool:done` head 落盘
|
|
22
|
+
* 时在形态前截断、不落值(与 batch.mjs 凭据第三形同判据)。
|
|
23
23
|
* 工具事件不记 args;URL 不入事件(llm/tool 事件从不携带 URL;err 文本经形态扫描)。
|
|
24
24
|
* - 摘要截断(B 方案,2026-09-03 用户裁定):截断处带 "…" 标记(截后仍 ≤上限)。
|
|
25
25
|
* - 测试隔离:node --test 进程(NODE_TEST_CONTEXT)默认不写盘——防测试事件污染真实
|
package/manifest-schema.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* manifest-schema.mjs — **校验 ∕ 默认值族**出档(2026-09-29 structure-split-2 · 台账 #620):自 `manifest.mjs`
|
|
3
3
|
* 全量迁出——`isValidDocRootValue`(docRoot 子键值形态判据)·
|
|
4
|
-
* `DEFAULT_MANIFEST
|
|
4
|
+
* `DEFAULT_MANIFEST`(默认五键)· `MANIFEST_SCHEMA`(判据单源)· 元素层谓词一件 · `validateManifest` ·
|
|
5
5
|
* `fillDefaults`;**结构拆分零语义**(面不变 ∕ 判据不变,只换宿主档;切点 ∕ 缝单源 =
|
|
6
6
|
* 批档 `docs/batches/2026-09-29-structure-split-2.md` §2.2-C)。
|
|
7
7
|
*
|
|
@@ -25,7 +25,8 @@ export function isValidDocRootValue(value) {
|
|
|
25
25
|
return value.every((p) => typeof p === "string" && p.trim() !== "")
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
/**
|
|
28
|
+
/** 默认五键——整档初始化的写源与缺键 fallback 的补源;每一键描述的都是这个用户项目自身
|
|
29
|
+
* (阶段 / 文档层 / 代码段 / 索引),产品内部布局与工具参数不住这里。 */
|
|
29
30
|
export const DEFAULT_MANIFEST = Object.freeze({
|
|
30
31
|
version: 1,
|
|
31
32
|
phase: "initial-dev",
|
|
@@ -36,18 +37,9 @@ export const DEFAULT_MANIFEST = Object.freeze({
|
|
|
36
37
|
modules: "docs/design/modules",
|
|
37
38
|
batches: "docs/batches",
|
|
38
39
|
}),
|
|
39
|
-
promptsLanding: "thincoder-core/prompts",
|
|
40
|
-
checkConfig: Object.freeze({
|
|
41
|
-
scanDirs: Object.freeze(["docs"]),
|
|
42
|
-
lineWidth: 300,
|
|
43
|
-
widthExemptZones: Object.freeze([]),
|
|
44
|
-
anchors: Object.freeze({ domain: "docs", exclude: Object.freeze(["_archive", "batches"]) }),
|
|
45
|
-
exemptions: Object.freeze([]),
|
|
46
|
-
}),
|
|
47
40
|
// 项目声明三族(2026-09-27 声明载体唯一化批并入——键名 / 形态逐字承前;来源档已退役)。
|
|
48
41
|
codePaths: Object.freeze(["src"]),
|
|
49
42
|
index: Object.freeze({ codeExtensions: Object.freeze([]), docExtensions: Object.freeze([]), excludePaths: Object.freeze([]), publicRepos: Object.freeze([]) }),
|
|
50
|
-
advisor: Object.freeze({ docMap: "", standardsDoc: "" }),
|
|
51
43
|
})
|
|
52
44
|
|
|
53
45
|
/** 校验判据(模块设计 §2.2)——枚举 / 键存在(fallback 用);判据单源(KD-M1-4)。 */
|
|
@@ -59,9 +51,7 @@ export const MANIFEST_SCHEMA = Object.freeze({
|
|
|
59
51
|
keys: Object.freeze(Object.keys(DEFAULT_MANIFEST)),
|
|
60
52
|
nestedKeys: Object.freeze({
|
|
61
53
|
docRoot: Object.freeze(Object.keys(DEFAULT_MANIFEST.docRoot)),
|
|
62
|
-
checkConfig: Object.freeze(Object.keys(DEFAULT_MANIFEST.checkConfig)),
|
|
63
54
|
index: Object.freeze(Object.keys(DEFAULT_MANIFEST.index)),
|
|
64
|
-
advisor: Object.freeze(Object.keys(DEFAULT_MANIFEST.advisor)),
|
|
65
55
|
}),
|
|
66
56
|
})
|
|
67
57
|
|
|
@@ -75,7 +65,7 @@ function isNonEmptyStringArray(v) {
|
|
|
75
65
|
* 形状校验(模块设计 §2.2)——枚举 / version 数值恒做;**纯函数、零 fs**(原 `{ cwd }`
|
|
76
66
|
* 指针分支已随字段整链裁撤——KD-M1-5 墓志);不落盘。键存在性入 missingKeys(非拒):
|
|
77
67
|
* 缺键 = 便利 fallback(补默认值),不是错(与整档缺失两分——KD-M1-2)。
|
|
78
|
-
* 未知键(#802 / KD-M1-36):顶层 +
|
|
68
|
+
* 未知键(#802 / KD-M1-36):顶层 + 两嵌套层产 `unknownKeys`(非拒——ok 定性零变;产线恰 =
|
|
79
69
|
* fillDefaults 丢弃面;嵌套键用点形路径——同 missingKeys 口径)。
|
|
80
70
|
* @param {object} obj 待校验 manifest 对象
|
|
81
71
|
* @returns {{ok:boolean, errors:string[], missingKeys:string[], unknownKeys:string[]}}
|
|
@@ -86,7 +76,7 @@ export function validateManifest(obj) {
|
|
|
86
76
|
if (obj === null || typeof obj !== "object" || Array.isArray(obj)) {
|
|
87
77
|
return { ok: false, errors: ["manifest 顶层必须是 JSON 对象"], missingKeys, unknownKeys: [] }
|
|
88
78
|
}
|
|
89
|
-
// #802(KD-M1-36——未知键可见化):顶层 +
|
|
79
|
+
// #802(KD-M1-36——未知键可见化):顶层 + 两嵌套层逐键对照(非拒——ok 定性零变;嵌套键点形)。
|
|
90
80
|
const unknownKeys = []
|
|
91
81
|
for (const key of Object.keys(obj)) if (!MANIFEST_SCHEMA.keys.includes(key)) unknownKeys.push(key)
|
|
92
82
|
for (const [nested, subkeys] of Object.entries(MANIFEST_SCHEMA.nestedKeys)) {
|
|
@@ -115,7 +105,6 @@ export function validateManifest(obj) {
|
|
|
115
105
|
// `index.excludePaths`(§6.14 面①):**数组层** fail-closed(非数组 ⇒ 拒);元素层宽容——空 ∕ 非串
|
|
116
106
|
// 元素由归一剔除(L-①-1 夹具 `["./openclaw/","refs",""]` ⇒ `["openclaw","refs"]`);`index.publicRepos`(§6.15)同款。
|
|
117
107
|
const strArrErr = (key, v) => { if (!isNonEmptyStringArray(v)) errors.push(`${key} 值形态非法:${JSON.stringify(v)}(须为非空字符串数组——KD-M1-32)`) }
|
|
118
|
-
const strErr = (key, v) => { if (typeof v !== "string") errors.push(`${key} 值形态非法:${JSON.stringify(v)}(须为字符串——KD-M1-32)`) }
|
|
119
108
|
const objErr = (key, v) => (v === null || typeof v !== "object" || Array.isArray(v)
|
|
120
109
|
? `${key} 值形态非法:${JSON.stringify(v)}(须为对象——KD-M1-32)` : null)
|
|
121
110
|
if (obj.codePaths !== undefined) strArrErr("codePaths", obj.codePaths)
|
|
@@ -130,9 +119,6 @@ export function validateManifest(obj) {
|
|
|
130
119
|
errors.push(`index.publicRepos 值形态非法:${JSON.stringify(obj.index.publicRepos)}(须为数组——非数组 fail-closed;空 ∕ 非串元素由归一剔除)`)
|
|
131
120
|
}
|
|
132
121
|
}
|
|
133
|
-
const advBad = obj.advisor === undefined ? null : objErr("advisor", obj.advisor)
|
|
134
|
-
if (advBad) errors.push(advBad)
|
|
135
|
-
else if (obj.advisor !== undefined) for (const k of ["docMap", "standardsDoc"]) if (obj.advisor[k] !== undefined) strErr(`advisor.${k}`, obj.advisor[k])
|
|
136
122
|
if (obj.phase !== undefined && !MANIFEST_SCHEMA.enum.phase.includes(obj.phase)) {
|
|
137
123
|
errors.push(`phase 取值非法:"${obj.phase}"(允许:${MANIFEST_SCHEMA.enum.phase.join(" | ")})`)
|
|
138
124
|
}
|
|
@@ -142,7 +128,7 @@ export function validateManifest(obj) {
|
|
|
142
128
|
return { ok: errors.length === 0, errors, missingKeys, unknownKeys }
|
|
143
129
|
}
|
|
144
130
|
|
|
145
|
-
/** 缺键补默认值(module 设计 §2.2 管线):顶层缺键补默认、嵌套键(docRoot/
|
|
131
|
+
/** 缺键补默认值(module 设计 §2.2 管线):顶层缺键补默认、嵌套键(docRoot/index)
|
|
146
132
|
* 子键补默认(与整键缺同语义);不覆写既有值。深拷贝默认源(structuredClone)——冻结常量永不外泄引用。
|
|
147
133
|
* 三族键的非对象值**原样透传**(不洗白——再校验拒,KD-M1-32)。 */
|
|
148
134
|
export function fillDefaults(obj) {
|
|
@@ -153,7 +139,7 @@ export function fillDefaults(obj) {
|
|
|
153
139
|
if (def !== null && typeof def === "object" && !Array.isArray(def)) {
|
|
154
140
|
const src = obj[key]
|
|
155
141
|
if (src === null || typeof src !== "object" || Array.isArray(src)) {
|
|
156
|
-
if (key === "index"
|
|
142
|
+
if (key === "index") out[key] = src // 形态错 → 透传(再校验拒);docRoot 保持默认
|
|
157
143
|
continue
|
|
158
144
|
}
|
|
159
145
|
for (const sub of Object.keys(def)) {
|
package/manifest.mjs
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
* 同档同键集去重、读到零未知键清该档 memo)——非拒定性零变。
|
|
12
12
|
* - validateManifest(obj) → { ok, errors, missingKeys, unknownKeys }:枚举 / version 数值 /
|
|
13
13
|
* docRoot 子键值形态(串 | 数组 | null(本面无——KD-M1-37),F7)——**纯函数、零 fs**;不落盘。
|
|
14
|
-
* -
|
|
15
|
-
*
|
|
14
|
+
* - 两族声明键(`codePaths` / `index.{codeExtensions,docExtensions,publicRepos}`——KD-M1-31 / M1-32):
|
|
15
|
+
* 缺键补默认;形态错 = 档非法(fail-closed,与 docRoot 子键同款);
|
|
16
16
|
* 读向 = `declaration.mjs`(2026-10-01 自 `conventions.mjs` 迁出——经其转口可达)经 `readManifest` /
|
|
17
17
|
* `manifestFilePath` 投影(单向——KD-M1-33);`index.publicRepos` 绝对根集 = `declaredPublicRoots`(§6.15)。
|
|
18
18
|
* - isValidDocRootValue(value) / docRootPaths(value, cwd)(F7 判据单源 KD-M1-8):值形态谓词
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
* 落盘);仅 writer === 'main' 落盘(fail-closed 缺省拒)。
|
|
36
36
|
*
|
|
37
37
|
* N1 零依赖:仅 node:fs / node:path + 标准库 JSON.parse——无第三方解析器、无 node:sqlite。
|
|
38
|
-
* N3 可迁移:本模块不硬编码任何本仓路径(docRoot
|
|
38
|
+
* N3 可迁移:本模块不硬编码任何本仓路径(docRoot 由被开发项目声明;机检参数不在 manifest 面——住引擎内置常量)。
|
|
39
39
|
*/
|
|
40
40
|
import { readFileSync, writeFileSync } from "node:fs"
|
|
41
41
|
import { join, resolve } from "node:path"
|
|
@@ -277,7 +277,7 @@ export function writeManifest(cwd, manifest, { writer = "subagent" } = {}) {
|
|
|
277
277
|
}
|
|
278
278
|
|
|
279
279
|
/**
|
|
280
|
-
* 初始化 =
|
|
280
|
+
* 初始化 = 写默认五键档(模块设计 §2.2):只提供机制,不包交互问答(问答归壳面)。
|
|
281
281
|
* 落盘走同一写门(内部 writeManifest(cwd, DEFAULT_MANIFEST, { writer }))——与 AC-M1-5
|
|
282
282
|
* 同一闸,无第二条写路径(KD-M1-4 / 评审 #12)。
|
|
283
283
|
* @returns {object} 写入的默认 manifest
|
package/memory/code-search.mjs
CHANGED
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
import { embed, embedTolerant, cosine, toBlob, fromBlob } from "../embedding.mjs"
|
|
9
9
|
import { scanVectors, createTopK } from "./scan.mjs"
|
|
10
10
|
import { normalizeOrigin } from "./origin.mjs"
|
|
11
|
-
import { declaredPublicRoots
|
|
11
|
+
import { declaredPublicRoots } from "../conventions.mjs"
|
|
12
|
+
import { createReadGate } from "./read-gate.mjs" // 面⑥∪面⑦ 读面准入单源(#1372 抽件——两档原各持一份逐字相同的 rowHidden)
|
|
12
13
|
import { buildFtsQuery, ensureEmbeddings, EMBED_TEXT_MAX_LEN } from "./core.mjs"
|
|
13
14
|
import { safeSliceUTF16 } from "../text-budget.mjs"
|
|
14
15
|
import { DESC } from "../tools/shared.mjs" // #15 描述外置:文本单点 = tool-docs/code_search.md
|
|
@@ -35,19 +36,9 @@ export async function codeSearch(memory, query, { limit = 5 } = {}) {
|
|
|
35
36
|
const origins = searchOrigins(memory)
|
|
36
37
|
const ftsOriginFilter = origins.length === 1 ? `AND c.origin = ?` : origins.length > 1 ? `AND c.origin IN (${origins.map(() => "?").join(", ")})` : ""
|
|
37
38
|
const originParams = origins
|
|
38
|
-
// §6.14 读面准入过滤(FTS 列表建成后 ∥ 向量 top-K push
|
|
39
|
-
//
|
|
40
|
-
|
|
41
|
-
const declCache = new Map()
|
|
42
|
-
const scopeCache = new Map()
|
|
43
|
-
const rowHidden = (origin, path) => {
|
|
44
|
-
const d = declCache.get(origin) ?? loadProjectDeclaration(origin)
|
|
45
|
-
declCache.set(origin, d)
|
|
46
|
-
if (isExcludedRelPath(path, d, origin)) return true
|
|
47
|
-
let scope = scopeCache.get(origin)
|
|
48
|
-
if (scope === undefined) { scope = declaredIndexScope(origin); scopeCache.set(origin, scope) }
|
|
49
|
-
return !isInDeclaredScope(path, scope, origin)
|
|
50
|
-
}
|
|
39
|
+
// §6.14 读面准入过滤(FTS 列表建成后 ∥ 向量 top-K push 前)——判据与逐 origin 预载单源 = `./read-gate.mjs`;
|
|
40
|
+
// 存量行不删(收尾保护位);窗口欠额接受。
|
|
41
|
+
const { rowHidden } = createReadGate()
|
|
51
42
|
const ftsNeedOrigin = origins.length !== 1 // 单 origin 形 SQL 零改;多 origin ∥ 无过滤形补 `origin` 列(行 origin 随行可得)
|
|
52
43
|
|
|
53
44
|
const ftsList = ftsQuery ? memory.db.prepare(`
|
package/memory/docs.mjs
CHANGED
|
@@ -12,7 +12,8 @@ import { _upsertDocFile, yieldTick } from "./code-index.mjs"
|
|
|
12
12
|
import { markIndexedCommit, listProjectFiles, indexExtensions, declaredRootEntries } from "./code-sync.mjs"
|
|
13
13
|
import { searchOrigins } from "./code-search.mjs"
|
|
14
14
|
import { createRowBudget, rowCountOfPath, sweepStaleRows } from "./sync-tail.mjs"
|
|
15
|
-
import { loadProjectDeclaration,
|
|
15
|
+
import { loadProjectDeclaration, declaredIndexScope } from "../conventions.mjs" // 面① 谓词经 `decl` 传 `sweepStaleRows`
|
|
16
|
+
import { createReadGate } from "./read-gate.mjs" // 面⑥∪面⑦ 读面准入单源(#1372 抽件——两档原各持一份逐字相同的 rowHidden)
|
|
16
17
|
import { logEvent } from "../log.mjs"
|
|
17
18
|
import { safeSliceUTF16 } from "../text-budget.mjs"
|
|
18
19
|
import { DESC } from "../tools/shared.mjs" // #15 描述外置:文本单点 = tool-docs/*.md(DESC 单解析面,缺档抛错语义不变)
|
|
@@ -117,19 +118,9 @@ export async function docSearch(memory, query, { limit = 5, origins: originsOver
|
|
|
117
118
|
const origins = originsOverride ?? searchOrigins(memory)
|
|
118
119
|
const ftsOriginFilter = origins.length === 1 ? `AND d.origin = ?` : origins.length > 1 ? `AND d.origin IN (${origins.map(() => "?").join(", ")})` : ""
|
|
119
120
|
const originParams = origins
|
|
120
|
-
// §6.14 读面准入过滤(FTS 列表建成后 ∥ 向量 top-K push
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
const declCache = new Map()
|
|
124
|
-
const scopeCache = new Map()
|
|
125
|
-
const rowHidden = (origin, path) => {
|
|
126
|
-
const d = declCache.get(origin) ?? loadProjectDeclaration(origin)
|
|
127
|
-
declCache.set(origin, d)
|
|
128
|
-
if (isExcludedRelPath(path, d, origin)) return true
|
|
129
|
-
let scope = scopeCache.get(origin)
|
|
130
|
-
if (scope === undefined) { scope = declaredIndexScope(origin); scopeCache.set(origin, scope) }
|
|
131
|
-
return !isInDeclaredScope(path, scope, origin)
|
|
132
|
-
}
|
|
121
|
+
// §6.14 读面准入过滤(FTS 列表建成后 ∥ 向量 top-K push 前)——判据与逐 origin 预载单源 = `./read-gate.mjs`;
|
|
122
|
+
// 存量行不删(收尾保护位);窗口欠额接受。
|
|
123
|
+
const { rowHidden } = createReadGate()
|
|
133
124
|
const ftsNeedOrigin = origins.length !== 1 // 单 origin 形 SQL 零改;多 origin ∥ 无过滤形补 `origin` 列(行 origin 随行可得)
|
|
134
125
|
|
|
135
126
|
const ftsList = ftsQuery ? memory.db.prepare(`
|