@thincoder/core 0.10.4 → 0.10.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/CHANGELOG.md +39 -1
  2. package/README.md +1 -1
  3. package/advisor/compaction.mjs +11 -6
  4. package/advisor/run.mjs +4 -4
  5. package/agent/assemble.mjs +1 -1
  6. package/agent/chat-call.mjs +1 -1
  7. package/agent/child-marks.mjs +2 -2
  8. package/agent/digest-account.mjs +5 -4
  9. package/agent/dispatch-gates.mjs +8 -0
  10. package/agent/dispatch.mjs +39 -18
  11. package/agent/family-tools.mjs +22 -11
  12. package/agent/helpers.mjs +4 -3
  13. package/agent/light-round.mjs +66 -0
  14. package/agent/post-turn.mjs +8 -4
  15. package/agent/run-stages.mjs +5 -3
  16. package/agent/run-start.mjs +3 -2
  17. package/agent/setup-reminders.mjs +1 -1
  18. package/agent/setup.mjs +18 -3
  19. package/agent/subagent-event.mjs +1 -1
  20. package/agent/suspension.mjs +8 -5
  21. package/agent/timers.mjs +56 -5
  22. package/agent/turn-loop.mjs +5 -3
  23. package/agent/write-gate.mjs +7 -7
  24. package/agent-tools/advisor-async.mjs +2 -2
  25. package/agent-tools/advisor-settle.mjs +2 -2
  26. package/agent-tools/advisor.mjs +1 -1
  27. package/agent-tools/async-discard.mjs +34 -0
  28. package/agent-tools/async-settle.mjs +3 -2
  29. package/agent-tools/audit-block.mjs +1 -1
  30. package/agent-tools/bash-async.mjs +1 -1
  31. package/agent-tools/batch-lifecycle.mjs +35 -14
  32. package/agent-tools/batch-paths.mjs +4 -4
  33. package/agent-tools/batch-skeleton.mjs +29 -11
  34. package/agent-tools/batch.mjs +28 -15
  35. package/agent-tools/browser-async.mjs +198 -0
  36. package/agent-tools/checkpoint.mjs +1 -1
  37. package/agent-tools/design-slots.mjs +232 -0
  38. package/agent-tools/digest-budget.mjs +1 -1
  39. package/agent-tools/manifest.mjs +193 -0
  40. package/agent-tools/parent-channel.mjs +7 -4
  41. package/agent-tools/review-facts.mjs +13 -13
  42. package/agent-tools/settings.mjs +1 -1
  43. package/agent-tools/skill.mjs +1 -1
  44. package/agent-tools/subagent-async.mjs +21 -12
  45. package/agent-tools/subagent-scheduler.mjs +8 -5
  46. package/agent-tools/subagent-spawn.mjs +29 -55
  47. package/agent-tools/subagent.mjs +21 -8
  48. package/agent-tools/timer.mjs +186 -12
  49. package/agent-tools/zero-write-watch.mjs +2 -2
  50. package/agent-tools.mjs +2 -1
  51. package/agent.mjs +1 -1
  52. package/attachments.mjs +1 -1
  53. package/browser/actions.mjs +163 -0
  54. package/browser/cdp.mjs +161 -0
  55. package/browser/clipboard.mjs +142 -0
  56. package/browser/input-actions.mjs +197 -0
  57. package/browser/input.mjs +256 -0
  58. package/browser/launch.mjs +143 -0
  59. package/browser/queue.mjs +171 -0
  60. package/browser/session.mjs +494 -0
  61. package/browser/snapshot.mjs +270 -0
  62. package/config-io.mjs +18 -13
  63. package/config-migrate.mjs +13 -29
  64. package/config-presets.mjs +33 -29
  65. package/config.mjs +68 -25
  66. package/context-degrade.mjs +1 -1
  67. package/context-echo.mjs +1 -1
  68. package/context-push.mjs +2 -2
  69. package/context.mjs +1 -1
  70. package/conventions.mjs +2 -2
  71. package/declaration.mjs +18 -2
  72. package/file-links.mjs +1 -1
  73. package/file-refs.mjs +1 -1
  74. package/history-window.mjs +1 -1
  75. package/i18n.mjs +2 -0
  76. package/ledger-cmd.mjs +62 -16
  77. package/ledger-db.mjs +6 -0
  78. package/ledger-surface.mjs +49 -5
  79. package/ledger-tools.mjs +16 -8
  80. package/ledger.mjs +9 -16
  81. package/log.mjs +1 -1
  82. package/manifest-discovery.mjs +1 -1
  83. package/manifest-schema.mjs +1 -1
  84. package/mcp/transport-stdio.mjs +1 -1
  85. package/mcp.mjs +1 -1
  86. package/memory/code-index.mjs +1 -1
  87. package/memory/code-search.mjs +1 -1
  88. package/memory/code-sync.mjs +1 -1
  89. package/memory/docs.mjs +6 -2
  90. package/memory/file-list.mjs +1 -1
  91. package/memory/file-walk.mjs +17 -5
  92. package/memory/maintain.mjs +410 -0
  93. package/memory/memory-tool.mjs +21 -6
  94. package/memory/schema.mjs +19 -7
  95. package/memory/sweep.mjs +8 -5
  96. package/memory/sync-tail.mjs +16 -10
  97. package/model-ref.mjs +16 -13
  98. package/model-specs.mjs +21 -9
  99. package/package.json +2 -1
  100. package/peer-instances.mjs +1 -1
  101. package/permission.mjs +33 -0
  102. package/process-probe.mjs +2 -2
  103. package/prompts/advisor-design.md +4 -2
  104. package/prompts/advisor-round1.md +1 -1
  105. package/prompts/advisor-round2.md +1 -1
  106. package/prompts/advisor-round3.md +1 -1
  107. package/prompts/common.md +36 -2
  108. package/prompts/consult-base.md +1 -1
  109. package/prompts/discipline-engineering.md +32 -15
  110. package/prompts/discipline-normal.md +46 -2
  111. package/prompts/persona-eng-designer.md +6 -6
  112. package/prompts/persona-engineering.md +14 -6
  113. package/prompts/persona-normal.md +2 -0
  114. package/prompts/subagent-base.md +3 -0
  115. package/provider/core.mjs +6 -0
  116. package/provider/errors.mjs +5 -3
  117. package/provider/google.mjs +9 -8
  118. package/provider/list-models.mjs +3 -2
  119. package/provider/sse.mjs +3 -0
  120. package/provider-flows.mjs +14 -15
  121. package/proxy-chunked.mjs +102 -0
  122. package/proxy-target.mjs +54 -0
  123. package/proxy-transport.mjs +298 -0
  124. package/proxy.mjs +17 -248
  125. package/session-gc.mjs +2 -2
  126. package/session-lifecycle.mjs +18 -14
  127. package/session-slot-claims.mjs +2 -2
  128. package/session-slots-manifest.mjs +1 -1
  129. package/session-slots.mjs +3 -3
  130. package/team-ledger.mjs +282 -0
  131. package/team.mjs +223 -0
  132. package/tool-docs/bash.md +1 -0
  133. package/tool-docs/batch.md +1 -1
  134. package/tool-docs/browser.md +48 -0
  135. package/tool-docs/ledger.md +3 -3
  136. package/tool-docs/manifest.md +16 -0
  137. package/tool-docs/memory.md +1 -0
  138. package/tool-docs/notify_parent.md +5 -0
  139. package/tool-docs/process.md +4 -4
  140. package/tool-docs/question.md +1 -0
  141. package/tool-docs/subagent.md +4 -3
  142. package/tool-docs/team_ledger.md +9 -0
  143. package/tool-docs/timer.md +6 -0
  144. package/tool-docs/wait_for.md +1 -0
  145. package/tools/browser.mjs +161 -0
  146. package/tools/git.mjs +1 -1
  147. package/tools/index.mjs +3 -0
  148. package/tools/ops.mjs +26 -10
  149. package/tools/repomap.mjs +1 -1
  150. package/tools/write-path.mjs +2 -2
  151. package/traces/trace-cleanup.mjs +1 -1
  152. package/traces/trace-store.mjs +1 -1
  153. package/vision-reader.mjs +10 -16
package/ledger-tools.mjs CHANGED
@@ -5,7 +5,8 @@
5
5
  * 全量 = depth-0 五 action ∥ 只读 = depth>0 二 action);旧五名 = code 面弃用壳(不入任何装配面)。
6
6
  * 守卫 = **声明派生**(设计档 §3.2 / §11.3:判序① 入参归一 → ② 入口级 action 判(消费 action)→
7
7
  * ③ 余键喂该 action 伪工具对象走 P2–P6——声明源 = 前身旧工具 `parameters` 逐字;helper `assertToolArgs`
8
- * 零改);判于核函数之前 ⇒ 非法入参零写、零库动作、零 SQLite 原文外泄。
8
+ * 机制沿用,「零改」面破例 = #1006 批增布尔分支(§13.9));判于核函数之前 ⇒ 非法入参零写、零库动作、
9
+ * 零 SQLite 原文外泄。
9
10
  * 接线:族出口 = `ledger.mjs`(re-export——消费面 agent/family-tools.mjs;旧读二装配面已清零);
10
11
  * 消费侧一律动态 import(`ledger-db.mjs` 静态 import node:sqlite ⇒ W8 契约②)。
11
12
  */
@@ -62,6 +63,8 @@ function assertToolArgs(tool, args) {
62
63
  throw new Error(`${name}:${field} 非法:${argPreview(value)}(应为字符串)`)
63
64
  } else if (spec.type === "number" && typeof value !== "number") {
64
65
  throw new Error(`${name}:${field} 非法:${argPreview(value)}(应为数字)`)
66
+ } else if (spec.type === "boolean" && typeof value !== "boolean") {
67
+ throw new Error(`${name}:${field} 非法:${argPreview(value)}(应为布尔)`)
65
68
  }
66
69
  if (field === "title" && typeof value === "string" && value.trim() === "") {
67
70
  throw new Error(`${name}:${field} 为空(${required ? "必填;" : ""}非空字符串)`)
@@ -107,9 +110,10 @@ const ACTION_SPECS = {
107
110
  board: { type: "string", description: "归属板块(缺省 = 不变)" },
108
111
  req_doc: { type: "string", description: "需求档指针(缺省 = 不变)" },
109
112
  task_book: { type: "string", description: "任务书指针(缺省 = 不变)" },
110
- evidence: { type: "string", description: "证据(缺省 = 不变)" },
113
+ evidence: { type: "string", description: "证据(缺省 = 不变;传入 = 追加——旧值非空 ⇒ 拼 `旧|新`(「|」零空格)、旧空直写;新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传)" },
114
+ evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换(旧文零保留);缺省 / false = 追加;旗独传(无新文)⇒ 拒" },
111
115
  trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "触发(缺省 = 不变)" },
112
- executor: { type: "string", description: "执行者 sessionId(可选——接手改写归属用;缺省 = 按状态迁移语义:进在途自动写本会话 / 出在途自动清空 / 其余不变)" },
116
+ executor: { type: "string", description: "执行者 sessionId(可选——接手改写归属用;缺省 = 按状态迁移语义:进入待设计 / 在途自动写本会话;出在途 / 撤回清空)" },
113
117
  },
114
118
  additionalProperties: false,
115
119
  },
@@ -120,6 +124,8 @@ const ACTION_SPECS = {
120
124
  cwd: { type: "string", description: CWD_DESC },
121
125
  id: { type: "number", description: "条目 id" },
122
126
  status: { type: "string", enum: ["已核销", "已废弃"], description: "目标态(勾销 / 追认核销 / 撤回)" },
127
+ evidence: { type: "string", description: "最小证据行(可选;传入 = 追加缺省——旧值非空 ⇒ 拼 `旧|新`(「|」零空格)、旧空直写;新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传。追认核销面判结果值须非空:缺 / 全空白 ⇒ 拒)" },
128
+ evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换(旧文零保留);缺省 / false = 追加;旗独传(无新文)⇒ 拒" },
123
129
  },
124
130
  additionalProperties: false,
125
131
  },
@@ -130,6 +136,7 @@ const ACTION_SPECS = {
130
136
  status: { type: "string", enum: ["待讨论", "待设计", "在途", "待核销", "已核销", "已废弃"], description: "按状态过滤(六态之一,缺省 = 不过滤)" },
131
137
  kind: { type: "string", enum: ["requirement", "tech_todo"], description: "按类别过滤" },
132
138
  board: { type: "string", description: "按归属板块过滤(需求档文档名)" },
139
+ trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "按技术待办触发过滤(三枚举,缺省 = 不过滤——无触发(NULL)不过滤)" },
133
140
  },
134
141
  additionalProperties: false,
135
142
  },
@@ -152,11 +159,12 @@ const UNION_PARAMETERS = {
152
159
  board: { type: "string", description: "归属板块(需求档文档名;add 可空 / update 缺省 = 不变 / query 过滤)" },
153
160
  req_doc: { type: "string", description: "需求档指针(add 可空 / update 缺省 = 不变)" },
154
161
  task_book: { type: "string", description: "任务书指针(add 可空 / update 缺省 = 不变;在途 / 待核销必填——咬合 CHECK)" },
155
- evidence: { type: "string", description: "最小证据行(add 可空 / update 缺省 = 不变;追认核销须非空)" },
156
- trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "技术待办触发(add / update 可空)" },
162
+ evidence: { type: "string", description: "最小证据行(add 可空;update / close 传入 = 追加缺省——旧空直写、旧非空拼 `旧|新`(「|」零空格)、新值全空白 ⇒ 拒;覆盖 = 与 `evidenceReplace: true` 同传;update 缺省 = 不变 / close 缺省 = 判行值;追认核销须非空)" },
163
+ evidenceReplace: { type: "boolean", description: "覆盖旗(布尔):与 evidence 同传 = 整段替换;缺省 / false = 追加;旗独传(无新文)⇒ 拒(update / close)" },
164
+ trigger: { type: "string", enum: ["归批", "条件", "认账不排期"], description: "技术待办触发(add / update 可空 / query 过滤)" },
157
165
  id: { type: "number", description: "条目 id(update / close 必填)" },
158
166
  status: { type: "string", enum: ["待讨论", "待设计", "在途", "待核销", "已核销", "已废弃"], description: "状态(close 必填 ∈ {已核销, 已废弃};update 迁移 / query 过滤——六态之一)" },
159
- executor: { type: "string", description: "执行者 sessionId(update 可选——接手改写归属;缺省 = 按状态迁移语义)" },
167
+ executor: { type: "string", description: "执行者 sessionId(update 可选——接手改写归属;缺省 = 按状态迁移语义:进入待设计 / 在途自动写本会话;出在途 / 撤回清空)" },
160
168
  },
161
169
  additionalProperties: false,
162
170
  }
@@ -213,9 +221,9 @@ function makeLedgerTool(actions, { readonly = false, writeFace = false } = {}) {
213
221
  return JSON.stringify(ledgerUpdate({ cwd: cwd ?? cwdOf(ctx), id, patch, executorSessionId }))
214
222
  }
215
223
  case "close":
216
- return JSON.stringify(ledgerClose({ cwd: rest.cwd ?? cwdOf(ctx), id: rest.id, status: rest.status }))
224
+ return JSON.stringify(ledgerClose({ cwd: rest.cwd ?? cwdOf(ctx), id: rest.id, status: rest.status, evidence: rest.evidence, evidenceReplace: rest.evidenceReplace }))
217
225
  case "query":
218
- return JSON.stringify(ledgerQuery({ cwd: rest.cwd ?? cwdOf(ctx), status: rest.status, kind: rest.kind, board: rest.board }), null, 2)
226
+ return JSON.stringify(ledgerQuery({ cwd: rest.cwd ?? cwdOf(ctx), status: rest.status, kind: rest.kind, board: rest.board, trigger: rest.trigger }), null, 2)
219
227
  case "count":
220
228
  return JSON.stringify({ count: ledgerCount({ cwd: rest.cwd ?? cwdOf(ctx) }) })
221
229
  default:
package/ledger.mjs CHANGED
@@ -18,12 +18,10 @@
18
18
  import { opendirSync, statSync } from "node:fs"
19
19
  import { basename, dirname, join, resolve } from "node:path"
20
20
  import { projectRootView } from "./manifest.mjs"
21
- import { ledgerDbPath, PENDING_STATUSES } from "./ledger-db.mjs"
21
+ import { ledgerDbPath, PENDING_STATUSES, AGING_DAYS } from "./ledger-db.mjs"
22
22
  import { ledgerQuery } from "./ledger-cmd.mjs"
23
23
  import { executorTail } from "./ledger-executors.mjs"
24
24
 
25
- /** 老化阈值(天——口径 = 需求档;`days` 参数可覆盖)。 */
26
- export const AGING_DAYS = 30
27
25
  /** 「可开批」阈值:同一板块(需求类条目 board 列)未决 ≥ 2。 */
28
26
  export const THRESHOLD_BOARD = 2
29
27
  /** 「可开批」阈值:需求池未决 ≥ 3。 */
@@ -65,8 +63,9 @@ export function findProject(anchor) {
65
63
  /** 同级枚举上限(N2 成本有界:父目录超此规模 = 缓存 / 临时等非仓族形态 → 退化空集,只显当前项目)。 */
66
64
  export const MAX_SIBLING_SCAN = 100
67
65
 
68
- /** 目录下已注册台账的直接子目录(目录名升序;不可读 → [];**惰性枚举 + 上限**——大目录零全量扫描)。 */
69
- function ledgerChildren(dir) {
66
+ /** 目录下已注册台账的直接子目录(目录名升序;不可读 → [];**惰性枚举 + 上限**——大目录零全量扫描)。
67
+ * 导出面 = 族发现(本档)∥ 轻通道闸候选枚举(`agent/light-round.mjs`——同级判据单源)。 */
68
+ export function ledgerChildren(dir) {
70
69
  const out = []
71
70
  let handle
72
71
  try { handle = opendirSync(dir) } catch { return out }
@@ -87,7 +86,7 @@ function ledgerChildren(dir) {
87
86
  }
88
87
 
89
88
  /** 项目族发现:{current, projects}——projects = current + 其同级含台账库目录(**发现序:current 在前**,
90
- * 其余按目录名升序);current 缺(容器目录,K6)→ 上下文目录向上最近「含台账子目录」者取其子目录。
89
+ * 其余按目录名升序);current 缺(容器目录,K6)→ 锚本地:取锚自身含台账库的直接子目录(不沿祖先链上找)。
91
90
  * **歧义根不充当项目**(轻通道轮 · 2026-10-04):命中锚若项目解析 `ambiguous`(≥2 候选——`projectRootView`)
92
91
  * ⇒ 该锚不可扫描(`openLedger` 歧义拒)——其键控库(先于 #828 写门的存量空壳 / 幽灵)不得遮蔽族发现;
93
92
  * 按容器形落子目录族(需求:打开容器根 ⇒ 族内合计——标记范围批 §1 原话)。 */
@@ -99,14 +98,7 @@ export function discoverFamily(anchor) {
99
98
  const siblings = ledgerChildren(dirname(current.root)).filter((p) => p.root !== current.root)
100
99
  return { current, projects: [current, ...siblings] }
101
100
  }
102
- let dir = resolve(anchor)
103
- for (;;) {
104
- const kids = ledgerChildren(dir)
105
- if (kids.length) return { current: null, projects: kids }
106
- const parent = dirname(dir)
107
- if (parent === dir) return { current: null, projects: [] }
108
- dir = parent
109
- }
101
+ return { current: null, projects: ledgerChildren(resolve(anchor)) }
110
102
  }
111
103
 
112
104
  /** scan 组装(SQLite 行集 + 计数单源 → scan 对象)——形状契约(设计档 §2.2):pool/tech/aged/
@@ -185,8 +177,9 @@ export function detailScans(scans, current) {
185
177
  return out
186
178
  }
187
179
 
188
- // ── re-export(拆分件接口——命令面接线 = 动态 import 本档,KD-M2-3) ──
189
- export { ALLOWED_MIGRATIONS, ledgerDbPath, ledgerKey, ledgerDirPath, openLedger, PENDING_STATUSES, _setLedgerDirForTest, _resetLedgerDirForTest, ensureExecutorColumn } from "./ledger-db.mjs"
180
+ // ── re-export(拆分件接口——命令面接线 = 动态 import 本档,KD-M2-3;`AGING_DAYS` 常量单源 = `ledger-db.mjs`
181
+ // ——2026-10-07 批 ledger-tool 自本档迁入,公共面零变) ──
182
+ export { AGING_DAYS, ALLOWED_MIGRATIONS, ledgerDbPath, ledgerKey, ledgerDirPath, openLedger, PENDING_STATUSES, _setLedgerDirForTest, _resetLedgerDirForTest, ensureExecutorColumn } from "./ledger-db.mjs"
190
183
  export { ledgerAdd, ledgerClose, ledgerCount, ledgerQuery, ledgerUpdate } from "./ledger-cmd.mjs"
191
184
  export { ledgerTool, ledgerReadTool, ledgerAddTool, ledgerCloseTool, ledgerCountTool, ledgerQueryTool, ledgerUpdateTool } from "./ledger-tools.mjs"
192
185
  export { runLedgerAudit, runLedgerMigrate } from "./ledger-migrate.mjs"
package/log.mjs CHANGED
@@ -21,7 +21,7 @@
21
21
  * 工具事件不记 args;URL 不入事件(llm/tool 事件从不携带 URL;err 文本经形态扫描)。
22
22
  * - 摘要截断(B 方案,2026-09-03 用户裁定):截断处带 "…" 标记(截后仍 ≤上限)。
23
23
  * - 测试隔离:node --test 进程(NODE_TEST_CONTEXT)默认不写盘——防测试事件污染真实
24
- * 诊断日志(两端测试套件都会跑真实 agent 管线);显式测试缝(_setLogsDirForTest)
24
+ * 诊断日志(两端测试都会跑真实 agent 管线);显式测试缝(_setLogsDirForTest)
25
25
  * 强制写入该目录(log.test.mjs 用它隔离临时目录——refinement #6)。
26
26
  * - 轮转清理(refinement #3——长驻进程覆盖):非仅启动时——每进程每日**首次写事件**
27
27
  * 时顺带清理 >1 天的 agent-*.log(extension host 可长驻数月,启动清理覆盖不到)。
@@ -91,7 +91,7 @@ export function discoverRepos(cwd) {
91
91
  * 的目录;无 ⇒ `null`(⇒ 调用方走发现兜底)。**嵌套合法**:子内归子、根其余归根。
92
92
  * **纯 fs**(只判存在性——不解析档内容 / 不问模式)· **不跨兄弟** · **无全局优先级**。
93
93
  * **覆盖位**(`_setProjectRootForTest` 在场)⇒ **头部短路**:直接返回覆盖值(不查档存在性——
94
- * 保「覆盖即覆盖值」语义,回归守卫 = 本批批内件基座腿(`docs/batches/2026-10-02-manifest-resolution-fix.test.mjs`——
94
+ * 保「覆盖即覆盖值」语义,回归守卫 = 本批单元测试档基座用例(`docs/batches/2026-10-02-manifest-resolution-fix.test.mjs`——
95
95
  * 基座三态 + 覆盖位 + 薄委托等价逐格)+ `docs/batches/2026-09-29-structure-split-2.test.mjs` 导出枚举探针(`Object.keys` 名面核))。
96
96
  * @param {string} [target] 目标路径(目录 / 文件)
97
97
  * @returns {string|null} 最近带档祖先目录绝对路径 / null
@@ -80,7 +80,7 @@ function isLineCountsValue(v) {
80
80
 
81
81
  /**
82
82
  * 形状校验(模块设计 §2.2)——枚举 / version 数值恒做;**纯函数、零 fs**(原 `{ cwd }`
83
- * 指针腿已随字段整链裁撤——KD-M1-5 墓志);不落盘。键存在性入 missingKeys(非拒):
83
+ * 指针分支已随字段整链裁撤——KD-M1-5 墓志);不落盘。键存在性入 missingKeys(非拒):
84
84
  * 缺键 = 便利 fallback(补默认值),不是错(与整档缺失两分——KD-M1-2)。
85
85
  * 未知键(#802 / KD-M1-36):顶层 + 四嵌套层产 `unknownKeys`(非拒——ok 定性零变;产线恰 =
86
86
  * fillDefaults 丢弃面;嵌套键用点形路径——同 missingKeys 口径)。
@@ -10,7 +10,7 @@ let exiting = false
10
10
  process.on("exit", () => { exiting = true })
11
11
 
12
12
  /** 测试缝(先例 `provider/rate.mjs` `_rateHooks`):组杀 / 单进程杀 / 定时器可注入
13
- * (POSIX 分流在生产 win32 机位的直驱 = 临时覆写 `process.platform`——见批内件 T-A12–A14)。 */
13
+ * (POSIX 分流在生产 win32 机位的直驱 = 临时覆写 `process.platform`——见单元测试档 T-A12–A14)。 */
14
14
  export const _killHooks = {
15
15
  groupKill: (pid, sig) => process.kill(-pid, sig),
16
16
  childKill: (child, sig) => child.kill(sig),
package/mcp.mjs CHANGED
@@ -52,7 +52,7 @@ export const _mcpHooks = {
52
52
  }
53
53
 
54
54
  /** initialize 请求参数单源(D-MC18):建连握手(doInitialize)与 transport 内 404 自愈重建共用——
55
- * 建连时经 `transport.setInitPayload(...)` 注入。导出面供批内件复用(参数单源不可分叉)。 */
55
+ * 建连时经 `transport.setInitPayload(...)` 注入。导出面供单元测试档复用(参数单源不可分叉)。 */
56
56
  export function buildInitParams() {
57
57
  return {
58
58
  protocolVersion: "2024-11-05",
@@ -2,7 +2,7 @@
2
2
  * memory/code-index.mjs — code and document chunking, language detection, symbol extraction
3
3
  */
4
4
 
5
- import { segmentCJK, CODE_EXTS, DOC_EXTS, SKIP_DIRS, BIG_FILE_LINES } from "./schema.mjs"
5
+ import { segmentCJK, BIG_FILE_LINES } from "./schema.mjs"
6
6
 
7
7
  /** Infer file language by extension */
8
8
  export function detectLanguage(filename) {
@@ -3,7 +3,7 @@
3
3
  * 内容 = `CODE_EMBED_BATCH` ∥ `codeSearch`(FTS5(BM25) + 向量余弦,RRF 合并;无 embedder ⇒ 纯 FTS,
4
4
  * ftsQuery 空且有 embedder ⇒ 纯向量)∥ `ensureCodeEmbeddings`(并发锁 + `_runEnsureCodeEmbeddings`)
5
5
  * ∥ `codeSearchTool`——迁出块逐字;原档 `memory/code-sync.mjs` 经 `export { … } from` 转口保名
6
- * (消费面 / 批内件 import 面零改)。
6
+ * (消费面 / 单元测试档 import 面零改)。
7
7
  */
8
8
  import { embed, embedTolerant, cosine, toBlob, fromBlob } from "../embedding.mjs"
9
9
  import { scanVectors, createTopK } from "./scan.mjs"
@@ -281,6 +281,6 @@ export async function reindexFile(memory, cwd, absPath) {
281
281
  }
282
282
 
283
283
  // ─── 迁出面(2026-10-01 拆分批 · #755 ∥ #786):检索面 → `memory/code-search.mjs`;清单面 →
284
- // `memory/file-list.mjs`——经本档转口保名(消费面 / 批内件 import 面零改),四起效点调用形零改。──────
284
+ // `memory/file-list.mjs`——经本档转口保名(消费面 / 单元测试档 import 面零改),四起效点调用形零改。──────
285
285
  export { codeSearch, ensureCodeEmbeddings, codeSearchTool } from "./code-search.mjs"
286
286
  export { listProjectFiles, indexExtensions } from "./file-list.mjs"
package/memory/docs.mjs CHANGED
@@ -104,13 +104,17 @@ export async function syncDocRoot(memory, dir, { onProgress, yieldFn = yieldTick
104
104
  /**
105
105
  * Doc search: FTS5(BM25) + optional vector cosine, RRF merged.
106
106
  * Falls back to pure FTS when no embedder; falls back to pure vector when ftsQuery is empty and embedder is present.
107
+ * `origins`(可选覆盖参 · 2026-10-10 #1100):缺省 `null` ⇒ 现行为(本函数自取 `searchOrigins`)**逐字**;
108
+ * 给定 ⇒ 以其为 origin 集(复用既有单/多 origin SQL 形);空数组 ⇒ 现「不过滤」语义(本设计调用方不产此形)。
109
+ * 消费方 = `thincoder-core/agent/setup.mjs` 注入块两路分取(配额策略 = `docs/core/design/AGENT-LOOP.md` §6.3)。
107
110
  */
108
- export async function docSearch(memory, query, { limit = 5 } = {}) {
111
+ export async function docSearch(memory, query, { limit = 5, origins: originsOverride = null } = {}) {
109
112
  const ftsQuery = buildFtsQuery(query)
110
113
  if (!ftsQuery && !memory.embedder) return []
111
114
 
112
115
  // §6.15 读面 origin 集(项目 ∪ 声明公共仓;单 origin ⇒ 逐字零行为——快径 SQL 形零改)
113
- const origins = searchOrigins(memory)
116
+ // #1100:`origins` 覆盖参在位时以其为集(缺省 `null` ⇒ 原式逐字)
117
+ const origins = originsOverride ?? searchOrigins(memory)
114
118
  const ftsOriginFilter = origins.length === 1 ? `AND d.origin = ?` : origins.length > 1 ? `AND d.origin IN (${origins.map(() => "?").join(", ")})` : ""
115
119
  const originParams = origins
116
120
 
@@ -2,7 +2,7 @@
2
2
  * memory/file-list.mjs — 项目文件清单面(自 `memory/code-sync.mjs` 迁出 · 2026-10-01 core 拆分批 #755 ∥ #786)。
3
3
  * 内容 = `indexExtensions`(内置扩展名表 ∪ 项目声明并集)∥ `listProjectFiles`(git 列面 + 非 git walk
4
4
  * 回退 + unlisted 计数;`index.excludePaths` §6.14 面① 双径过滤:git 列面筛行 ∥ walk 剪枝)——迁出块逐字。
5
- * 原档 `memory/code-sync.mjs` 经 `export { … } from` 转口保名(消费面 / 批内件 import 面零改)。
5
+ * 原档 `memory/code-sync.mjs` 经 `export { … } from` 转口保名(消费面 / 单元测试档 import 面零改)。
6
6
  */
7
7
  import { CODE_EXTS, DOC_EXTS } from "./schema.mjs"
8
8
  import { walkProjectFiles, isSkippedRelPath, extensionOf, createUnlistedTally, MAX_WALK_FILES } from "./file-walk.mjs"
@@ -10,20 +10,32 @@
10
10
  */
11
11
  import { readdir } from "node:fs/promises"
12
12
  import { join } from "node:path"
13
- import { SKIP_DIRS } from "./schema.mjs"
13
+ import { SKIP_DIRS, SKIP_DIRS_FOLD, SKIP_DIR_PREFIXES } from "./schema.mjs"
14
+
15
+ /** win32 fold index for the junk/product family (canonical spellings live in `SKIP_DIRS_FOLD`). */
16
+ const SKIP_DIRS_FOLD_LOWER = new Set([...SKIP_DIRS_FOLD].map((n) => n.toLowerCase()))
14
17
 
15
18
  /** Upper guard for the walk: stop after this many matched files (runaway trees). */
16
19
  export const MAX_WALK_FILES = 20000
17
20
 
18
21
  /**
19
- * Shared skip predicate for project-relative paths: any SKIP_DIRS basename, or any
20
- * dot-prefixed segment (`.git`, `.cache`, `.thincoder`…). Accepts both separators.
22
+ * Shared skip predicate for project-relative paths (§6.14 面④ family split · 台账 #1081): exact
23
+ * basename (`SKIP_DIRS`) ∨ segment prefix (`SKIP_DIR_PREFIXES` — `bazel-*`; POSIX literal ∥ win32
24
+ * folded) ∨ (win32 only) folded junk/product family (`SKIP_DIRS_FOLD`); dot segments via the dot
25
+ * rule. `platform` is a test seam (default `process.platform`; callers keep the one-arg form).
21
26
  */
22
- export function isSkippedRelPath(rel) {
27
+ export function isSkippedRelPath(rel, platform = process.platform) {
28
+ const fold = platform === "win32"
23
29
  return String(rel ?? "")
24
30
  .replace(/\\/g, "/")
25
31
  .split("/")
26
- .some((seg) => seg !== "" && (seg.startsWith(".") || SKIP_DIRS.has(seg)))
32
+ .some((seg) => {
33
+ if (seg === "") return false
34
+ if (seg.startsWith(".")) return true
35
+ if (SKIP_DIRS.has(seg)) return true
36
+ if (SKIP_DIR_PREFIXES.some((p) => (fold ? seg.toLowerCase() : seg).startsWith(p))) return true
37
+ return fold && SKIP_DIRS_FOLD_LOWER.has(seg.toLowerCase())
38
+ })
27
39
  }
28
40
 
29
41
  /** Lower-cased ".ext" of a path, or "" when it has no extension. */