thincoder 0.12.58 → 0.12.59

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 (114) hide show
  1. package/CHANGELOG.md +42 -1
  2. package/README.md +1 -1
  3. package/bin/thincoder.mjs +8 -0
  4. package/package.json +1 -1
  5. package/src/acp/bridge.mjs +132 -26
  6. package/src/advisor/messages.mjs +34 -1
  7. package/src/advisor/run.mjs +89 -51
  8. package/src/advisor.mjs +15 -7
  9. package/src/agent/dispatch.mjs +91 -14
  10. package/src/agent/helpers.mjs +35 -4
  11. package/src/agent/setup.mjs +90 -19
  12. package/src/agent/spawn-child.mjs +25 -0
  13. package/src/agent-tools/advisor.mjs +24 -2
  14. package/src/agent-tools/consult.mjs +37 -6
  15. package/src/agent-tools/eng.mjs +2 -1
  16. package/src/agent-tools/goal.mjs +11 -1
  17. package/src/agent-tools/read-history.mjs +160 -0
  18. package/src/agent-tools/settings.mjs +162 -0
  19. package/src/agent-tools/skill.mjs +2 -1
  20. package/src/agent-tools/subagent-actions.mjs +432 -0
  21. package/src/agent-tools/subagent-async.mjs +427 -0
  22. package/src/agent-tools/subagent-scheduler.mjs +319 -0
  23. package/src/agent-tools/subagent.mjs +467 -193
  24. package/src/agent-tools/task.mjs +4 -3
  25. package/src/agent-tools/timer.mjs +9 -4
  26. package/src/agent-tools/verify.mjs +161 -49
  27. package/src/agent-tools.mjs +1 -0
  28. package/src/agent.mjs +161 -125
  29. package/src/auto-think.mjs +14 -0
  30. package/src/cli/make-agent.mjs +2 -1
  31. package/src/cli/permission.mjs +8 -1
  32. package/src/config.mjs +5 -0
  33. package/src/context.mjs +87 -27
  34. package/src/distill.mjs +19 -1
  35. package/src/escape.mjs +6 -4
  36. package/src/log.mjs +195 -0
  37. package/src/memory/code-sync.mjs +1 -1
  38. package/src/memory/core.mjs +126 -0
  39. package/src/memory/docs.mjs +196 -87
  40. package/src/memory.mjs +1 -1
  41. package/src/model-specs.mjs +15 -1
  42. package/src/prompts/advisor-design.md +46 -0
  43. package/src/prompts/advisor-round1.md +49 -2
  44. package/src/prompts/advisor-round2.md +47 -0
  45. package/src/prompts/advisor-round3.md +47 -0
  46. package/src/prompts/coder.md +22 -0
  47. package/src/prompts/consult-base.md +13 -0
  48. package/src/prompts/discipline.md +10 -5
  49. package/src/prompts/eng-coder.md +2 -2
  50. package/src/prompts/engineering-sub.md +23 -1
  51. package/src/prompts/engineering.md +106 -56
  52. package/src/prompts/explore.md +1 -2
  53. package/src/prompts/main.md +11 -6
  54. package/src/prompts/methodology-template.md +14 -0
  55. package/src/prompts/system.md +4 -2
  56. package/src/provider/core.mjs +56 -2
  57. package/src/tools/apply_patch.md +3 -1
  58. package/src/tools/bash.md +1 -1
  59. package/src/tools/delete.md +1 -0
  60. package/src/tools/edit-batch.mjs +31 -43
  61. package/src/tools/edit-diff.mjs +265 -0
  62. package/src/tools/edit.md +10 -8
  63. package/src/tools/execute.md +7 -7
  64. package/src/tools/execute.mjs +24 -20
  65. package/src/tools/file.mjs +18 -68
  66. package/src/tools/file_ops.md +2 -1
  67. package/src/tools/get_current_time.md +3 -1
  68. package/src/tools/hashline_edit.md +2 -0
  69. package/src/tools/index.mjs +3 -2
  70. package/src/tools/insert_after.md +2 -1
  71. package/src/tools/lint.md +2 -0
  72. package/src/tools/lsp.md +4 -1
  73. package/src/tools/patch.mjs +84 -13
  74. package/src/tools/pdf-parse-text.mjs +497 -0
  75. package/src/tools/pdf-parse-xref.mjs +499 -0
  76. package/src/tools/pdf.mjs +155 -0
  77. package/src/tools/question.md +2 -1
  78. package/src/tools/read.md +1 -0
  79. package/src/tools/read_pdf.md +21 -0
  80. package/src/tools/repomap.mjs +1 -1
  81. package/src/tools/shared.mjs +4 -12
  82. package/src/tools/system.mjs +6 -21
  83. package/src/tools/tree.md +2 -1
  84. package/src/tools/web.mjs +5 -3
  85. package/src/tools/websearch.md +2 -1
  86. package/src/tools/write.md +2 -0
  87. package/src/traces/trace-store.mjs +224 -0
  88. package/src/tui/agent-turn.mjs +385 -22
  89. package/src/tui/clipboard.mjs +15 -4
  90. package/src/tui/cmd-config.mjs +29 -9
  91. package/src/tui/cmd-extract.mjs +1 -1
  92. package/src/tui/cmd-mcp.mjs +9 -0
  93. package/src/tui/cmd-think.mjs +1 -1
  94. package/src/tui/index.mjs +29 -95
  95. package/src/tui/interaction.mjs +13 -2
  96. package/src/tui/key-handler.mjs +105 -155
  97. package/src/tui/key-modes.mjs +215 -0
  98. package/src/tui/layout.mjs +22 -1
  99. package/src/tui/mouse.mjs +40 -0
  100. package/src/tui/pickers.mjs +11 -3
  101. package/src/tui/render-conversation.mjs +13 -161
  102. package/src/tui/render-frame.mjs +27 -10
  103. package/src/tui/render-loop.mjs +4 -1
  104. package/src/tui/render-segments.mjs +165 -0
  105. package/src/tui/startup.mjs +36 -0
  106. package/src/tui/subagent-blocks.mjs +322 -144
  107. package/src/tui/subagent-panel.mjs +88 -13
  108. package/src/tui/tool-args.mjs +10 -2
  109. package/src/tui/tool-events.mjs +132 -100
  110. package/src/tui/update-notice.mjs +72 -0
  111. package/src/tui/wizard.mjs +36 -6
  112. package/src/agent-tools/escalate.mjs +0 -179
  113. package/src/agent-tools/subagent-check.mjs +0 -107
  114. package/src/tools/exec-prelude.mjs +0 -84
@@ -7,7 +7,7 @@ import { join } from "node:path"
7
7
  import { embed, cosine, toBlob, fromBlob } from "../embedding.mjs"
8
8
  import { commitAndPush } from "../git/gitmem.mjs"
9
9
  import { DOC_EXTS, SKIP_DIRS, MAX_DOC_FILE_BYTES } from "./schema.mjs"
10
- import { buildFtsQuery, put, search, putMarkdown, deleteByUid, EMBED_TEXT_MAX_LEN } from "./core.mjs"
10
+ import { buildFtsQuery, put, search, putMarkdown, deleteByUid, matchMemoryRows, deleteWhere, clearPersonal, EMBED_TEXT_MAX_LEN } from "./core.mjs"
11
11
  import { _upsertDocFile, yieldTick } from "./code-index.mjs"
12
12
  import { markIndexedCommit, listProjectFiles } from "./code-sync.mjs"
13
13
 
@@ -164,7 +164,8 @@ export function docSearchTool(memory) {
164
164
  return {
165
165
  name: "doc_search",
166
166
  description:
167
- "Search the project's documentation (README, design docs, guides, markdown files) for relevant information. Use this to find design decisions, coding conventions, architecture docs, or project rules. Prefer this over code_search when you need to understand the project's intended design rather than existing implementation.",
167
+ "Search the project's documentation (README, design docs, guides, markdown files) for relevant information. Use this to find design decisions, coding conventions, architecture docs, or project rules. Prefer this over code_search when you need to understand the project's intended design rather than existing implementation. " +
168
+ "Returns matching doc chunks: path, heading, line range, relevance score, content excerpt.",
168
169
  parameters: {
169
170
  type: "object",
170
171
  properties: {
@@ -186,9 +187,45 @@ export function docSearchTool(memory) {
186
187
 
187
188
  // ---------------------------------------------------------------- agent tools
188
189
 
190
+ /** §6 shared tool surface — action enum / parameter shapes / descriptions byte-identical
191
+ * with thincoder-vscode/src/memory.mjs (MEMORY.md §6 D-M1/F-M6); scope VALUES per end
192
+ * (VS Code has no team layer and rejects it with CLI guidance). */
193
+ const MEMORY_ACTIONS = ["search", "put", "list", "delete", "clear"]
194
+ const MEMORY_SCOPES = ["personal", "project", "team"]
195
+ const MEMORY_TOOL_DESCRIPTION =
196
+ "Manage long-term memory in ONE tool — the action parameter picks the operation:\n" +
197
+ "- search — find knowledge saved in previous sessions (query, optional scope/limit); results include every entry's id\n" +
198
+ "- put — save a piece of knowledge for future sessions (type: rule = coding standards, knowledge = project facts, decision = architecture decisions, pattern = debugging/workflow patterns; title/content/tags/scope)\n" +
199
+ "- list — inventory what memory holds: optional scope/type/keyword filters, limit default 50; one row per entry: id [type] title (date); a truncated list notes the full count\n" +
200
+ "- delete — SINGLE: {id, scope} deletes one entry by the id shown in put/search/list output. BATCH: {scope + type and/or keyword} deletes every matching entry in that scope — a call without confirm:true is refused and returns the count plus a preview (re-send with confirm:true to execute); scope-wide wipes without filters are refused on every layer\n" +
201
+ "- clear — {scope: \"personal\", confirm: true} wipes ALL personal memory entries. clear is personal-only: a missing scope or a project/team scope is refused (use delete batch filters on shared layers)\n" +
202
+ "Deleting project/team (CLI) entries removes the local markdown file and its index row — team deletion is local only and a later team sync may resurrect the file while the remote still has it.\n" +
203
+ "Save bugs, conventions, and preferences here — they persist across sessions."
204
+
205
+ function validateTypeFilter(type) {
206
+ if (type === undefined || type === null || type === "") return null
207
+ const t = String(type)
208
+ if (!["rule", "knowledge", "decision", "pattern"].includes(t)) throw new Error(`Invalid memory type "${t}"; expected one of: rule, knowledge, decision, pattern`)
209
+ return t
210
+ }
211
+
212
+ function normalizeLimit(limit, dflt) {
213
+ const n = Number(limit)
214
+ return Number.isFinite(n) && n > 0 ? Math.floor(n) : dflt
215
+ }
216
+
217
+ function fmtDate(ts) {
218
+ return ts ? new Date(ts).toISOString().slice(0, 10) : "?"
219
+ }
220
+
221
+ const listRowLine = (r) => `${r.id} [${r.type}] ${r.title}(${fmtDate(r.ts)})`
222
+
189
223
  /**
190
- * Generate the three memory-related agent tools (following the tools.mjs tool shape).
191
- * memory_put and memory_delete are side-effecting (need permission confirmation), memory_search is read-only.
224
+ * Generate the memory agent tool ONE `memory` tool with five actions (MEMORY.md §6 D-M1).
225
+ * search/list are read-only actions (planMode pass / no permission ask dispatch classifies
226
+ * them action-level, same as subagent check/status); put keeps its side-effect permission
227
+ * gate; batch delete/clear gate on confirm:true + scope inside the tool (direct-delete
228
+ * ruling — the confirm parameter IS the gate) and stay non-readonly like the retired tools.
192
229
  * opts: { cwd, projectDir, author, team: { dir, name } | null }
193
230
  */
194
231
  export function memoryTools(memory, opts = {}) {
@@ -196,99 +233,171 @@ export function memoryTools(memory, opts = {}) {
196
233
  const dirs = { project: projectDir, team: opts.team?.dir ?? null }
197
234
  return [
198
235
  {
199
- name: "memory_put",
200
- description:
201
- "Save a piece of knowledge to long-term memory. Use when you learn something worth remembering across sessions: a project convention, a debugging insight, an architecture decision. Types: rule (coding standards), knowledge (project facts), decision (architecture decisions), pattern (debugging/workflow patterns). Scopes: personal (default, private to you), project (shared via this repo's .thincoder/memory/), team (org-wide team repo, if configured).",
236
+ name: "memory",
237
+ description: MEMORY_TOOL_DESCRIPTION,
202
238
  parameters: {
203
239
  type: "object",
204
240
  properties: {
205
- type: { type: "string", enum: ["rule", "knowledge", "decision", "pattern"] },
206
- title: { type: "string", description: "Short title" },
207
- content: { type: "string", description: "Full content to remember" },
208
- tags: { type: "string", description: "Space-separated tags" },
209
- scope: { type: "string", enum: ["personal", "project", "team"], description: "Where to save (default personal)" },
241
+ action: { type: "string", enum: MEMORY_ACTIONS, description: "Operation to run (required)" },
242
+ scope: { type: "string", enum: MEMORY_SCOPES, description: "Where the memory lives: personal (private), project (shared via this repo's .thincoder/memory/), team (CLI only). put defaults to personal; search/list search every layer when omitted; delete/clear require it" },
243
+ type: { type: "string", enum: ["rule", "knowledge", "decision", "pattern"], description: "Entry type: put = what to save; list/delete batch = filter by type" },
244
+ title: { type: "string", description: "put: short title" },
245
+ content: { type: "string", description: "put: full content to remember" },
246
+ tags: { type: "string", description: "put: space-separated tags" },
247
+ query: { type: "string", description: "search: natural-language query" },
248
+ keyword: { type: "string", description: "list/delete batch: filter matching title/content" },
249
+ id: { type: "string", description: "delete single: the entry id from put/search/list output" },
250
+ limit: { type: "number", description: "Max rows: list 50 by default, search 5 by default" },
251
+ confirm: { type: "boolean", description: "delete batch/clear: must be true — without it the tool refuses" },
210
252
  },
211
- required: ["type", "title", "content"],
253
+ required: ["action"],
212
254
  },
213
255
  readonly: false,
214
256
  async execute(args) {
215
- const scope = args.scope ?? "personal"
216
- if (scope === "personal") {
217
- const id = await put(memory, args)
218
- return `Saved to personal memory (id=personal:${id}): [${args.type}] ${args.title}`
219
- }
220
- if (scope === "project") {
221
- if (!projectDir) throw new Error("project scope unavailable: no project directory configured")
222
- const filename = await putMarkdown(memory, {
223
- layer: "project",
224
- dir: projectDir,
225
- type: args.type,
226
- title: args.title,
227
- content: args.content,
228
- tags: (args.tags ?? "").split(/\s+/).filter(Boolean),
229
- author: opts.author ?? "unknown",
230
- })
231
- return `Saved to project memory (id=project:${projectDir}:${filename}): [${args.type}] ${args.title}`
257
+ const action = String(args?.action ?? "")
258
+ if (!MEMORY_ACTIONS.includes(action)) {
259
+ throw new Error(`memory: unknown action "${action}" — expected one of: ${MEMORY_ACTIONS.join("/")}`)
232
260
  }
233
- if (!opts.team?.dir) {
234
- throw new Error("team scope not configured: set memory.team in ~/.thincoder/config.json")
261
+ switch (action) {
262
+ case "search": return execSearch(memory, args)
263
+ case "put": return execPut(memory, args, opts, dirs)
264
+ case "list": return execList(memory, args, dirs)
265
+ case "delete": return execDelete(memory, args, dirs)
266
+ case "clear": return execClear(memory, args)
235
267
  }
236
- const filename = await putMarkdown(memory, {
237
- layer: "team",
238
- dir: opts.team.dir,
239
- type: args.type,
240
- title: args.title,
241
- content: args.content,
242
- tags: (args.tags ?? "").split(/\s+/).filter(Boolean),
243
- author: opts.author ?? "unknown",
244
- })
245
- await commitAndPush(opts.team.dir, filename, `memory: [${args.type}] ${args.title}`)
246
- return `Saved to team memory and pushed (id=team:${opts.team.dir}:${filename}): [${args.type}] ${args.title}`
247
- },
248
- },
249
- {
250
- name: "memory_search",
251
- description:
252
- "Search long-term memory across all layers (personal/project/team) for relevant knowledge saved in previous sessions. Use the same language as the memories being searched.",
253
- parameters: {
254
- type: "object",
255
- properties: {
256
- query: { type: "string", description: "Natural language search query" },
257
- limit: { type: "number", description: "Max results (default 5)" },
258
- },
259
- required: ["query"],
260
- },
261
- readonly: true,
262
- async execute(args) {
263
- const results = await search(memory, args.query, { limit: args.limit ?? 5 })
264
- if (results.length === 0) return "(no matching memories)"
265
- return results.map((r) => `[${r.layer}][${r.type}] ${r.title} (id=${r.id})\n${r.content}`).join("\n\n")
266
- },
267
- },
268
- {
269
- name: "memory_delete",
270
- description:
271
- "Delete a memory entry by its id (as returned by memory_put / memory_search) and scope. " +
272
- "Scope is required and must match the id prefix — this prevents accidental deletion in another scope. " +
273
- "Returns the deleted entry's title and content so the deletion is auditable and recoverable.",
274
- parameters: {
275
- type: "object",
276
- properties: {
277
- id: { type: "string", description: "Entry id: personal:<n> / project:<origin>:<path> / team:<origin>:<path>" },
278
- scope: { type: "string", enum: ["personal", "project", "team"], description: "Scope of the entry to delete (required)" },
279
- },
280
- required: ["id", "scope"],
281
- },
282
- readonly: false,
283
- async execute(args) {
284
- const uid = String(args.id)
285
- const prefix = uid.split(":")[0]
286
- const uidScope = prefix === "personal" || prefix === "project" || prefix === "team" ? prefix : /^\d+$/.test(prefix) ? "personal" : null
287
- if (!uidScope) throw new Error(`invalid memory id: ${uid}`)
288
- if (uidScope !== args.scope) throw new Error(`id prefix ${prefix}: 与 scope ${args.scope} 不匹配`)
289
- const entry = await deleteByUid(memory, uid, { dirs })
290
- return `Deleted ${entry.id}: ${entry.title}\n${(entry.content ?? "").slice(0, 500)}`
291
268
  },
292
269
  },
293
270
  ]
294
271
  }
272
+
273
+ /** action search — the retired search tool surface (read-only, same output contract). */
274
+ async function execSearch(memory, args) {
275
+ const scope = args.scope
276
+ if (scope !== undefined && scope !== null && !MEMORY_SCOPES.includes(String(scope))) {
277
+ throw new Error(`memory search: invalid scope "${scope}"`)
278
+ }
279
+ const query = String(args.query ?? "").trim()
280
+ if (!query) return "(no matching memories)" // 空 query 短路——两端同语义(评审 code review #4)
281
+ const limit = normalizeLimit(args.limit, 5)
282
+ let results
283
+ if (!scope) {
284
+ results = await search(memory, query, { limit })
285
+ } else {
286
+ // scope filter: oversample then slice the requested layer (results keep global rank order).
287
+ // 窗口 = max(limit*4, 20) 是召回上限——大库 + 高 limit 时该层结果可能不足 limit(接受的取舍——评审 code review #3)
288
+ const wide = await search(memory, query, { limit: Math.max(limit * 4, 20) })
289
+ results = wide.filter((r) => r.layer === String(scope)).slice(0, limit)
290
+ }
291
+ if (results.length === 0) return "(no matching memories)"
292
+ return results.map((r) => `[${r.layer}][${r.type}] ${r.title} (id=${r.id})\n${r.content}`).join("\n\n")
293
+ }
294
+
295
+ /** action put — the retired put tool surface (side-effect gate, unchanged semantics). */
296
+ async function execPut(memory, args, opts, dirs) {
297
+ const scope = String(args.scope ?? "personal")
298
+ if (!MEMORY_SCOPES.includes(scope)) throw new Error(`memory put: invalid scope "${scope}"`)
299
+ if (scope === "personal") {
300
+ const id = await put(memory, { type: args.type, title: args.title, content: args.content, tags: args.tags ?? "" })
301
+ return `Saved to personal memory (id=personal:${id}): [${args.type}] ${args.title}`
302
+ }
303
+ if (scope === "project") {
304
+ if (!dirs.project) throw new Error("project scope unavailable: no project directory configured")
305
+ const filename = await putMarkdown(memory, {
306
+ layer: "project",
307
+ dir: dirs.project,
308
+ type: args.type,
309
+ title: args.title,
310
+ content: args.content,
311
+ tags: (args.tags ?? "").split(/\s+/).filter(Boolean),
312
+ author: opts.author ?? "unknown",
313
+ })
314
+ return `Saved to project memory (id=project:${dirs.project}:${filename}): [${args.type}] ${args.title}`
315
+ }
316
+ if (!dirs.team) {
317
+ throw new Error("team scope not configured: set memory.team in ~/.thincoder/config.json")
318
+ }
319
+ const filename = await putMarkdown(memory, {
320
+ layer: "team",
321
+ dir: dirs.team,
322
+ type: args.type,
323
+ title: args.title,
324
+ content: args.content,
325
+ tags: (args.tags ?? "").split(/\s+/).filter(Boolean),
326
+ author: opts.author ?? "unknown",
327
+ })
328
+ await commitAndPush(dirs.team, filename, `memory: [${args.type}] ${args.title}`)
329
+ return `Saved to team memory and pushed (id=team:${dirs.team}:${filename}): [${args.type}] ${args.title}`
330
+ }
331
+
332
+ /** action list — new inventory action (read-only): scope/type/keyword filters + limit truncation note. */
333
+ async function execList(memory, args, dirs) {
334
+ const scope = args.scope ?? null
335
+ if (scope && !MEMORY_SCOPES.includes(String(scope))) throw new Error(`memory list: invalid scope "${scope}"`)
336
+ const rows = await matchMemoryRows(memory, {
337
+ scope: scope ? String(scope) : null,
338
+ type: validateTypeFilter(args.type),
339
+ keyword: args.keyword ? String(args.keyword).trim() : null,
340
+ projectDir: dirs.project,
341
+ teamDir: dirs.team,
342
+ })
343
+ if (rows.length === 0) return "0 条匹配"
344
+ const limit = normalizeLimit(args.limit, 50)
345
+ const shown = rows.slice(0, limit)
346
+ const lines = shown.map(listRowLine)
347
+ if (rows.length > shown.length) lines.unshift(`${shown.length} 条——截断前 ${rows.length}`)
348
+ return lines.join("\n")
349
+ }
350
+
351
+ /** action delete — single ({ id, scope } — §0.1-era single-delete semantics) + batch (scope + type/keyword + confirm). */
352
+ async function execDelete(memory, args, dirs) {
353
+ const hasId = args.id !== undefined && args.id !== null && String(args.id) !== ""
354
+ if (hasId) return execDeleteSingle(memory, args, dirs)
355
+ // batch form
356
+ const scope = args.scope
357
+ if (!scope) throw new Error("batch delete requires scope plus type and/or keyword filter")
358
+ if (!MEMORY_SCOPES.includes(String(scope))) throw new Error(`memory delete: invalid scope "${scope}"`)
359
+ const type = validateTypeFilter(args.type)
360
+ const keyword = args.keyword ? String(args.keyword).trim() : null
361
+ if (!type && !keyword) {
362
+ throw new Error("batch delete requires type and/or keyword filter — a scope-wide wipe without filters is refused (personal full wipe is the clear action)")
363
+ }
364
+ if (scope === "project" && !dirs.project) throw new Error("project scope unavailable: no project directory configured")
365
+ if (scope === "team" && !dirs.team) throw new Error("team scope not configured: set memory.team in ~/.thincoder/config.json")
366
+ const filters = { scope: String(scope), type, keyword }
367
+ const rows = await matchMemoryRows(memory, { ...filters, projectDir: dirs.project, teamDir: dirs.team })
368
+ if (rows.length === 0) return "0 条匹配"
369
+ if (args.confirm !== true) {
370
+ const lines = [rows.length > 5 ? `将删 ${rows.length} 条:前 5 条预览` : `将删 ${rows.length} 条`]
371
+ lines.push(...rows.slice(0, 5).map(listRowLine))
372
+ if (rows.length > 5) lines.push(`5 条——截断前 ${rows.length}`)
373
+ lines.push("confirm:true required — re-send with it to execute the deletion")
374
+ return lines.join("\n")
375
+ }
376
+ const n = await deleteWhere(memory, filters, { dirs })
377
+ return `Deleted ${n} entries in scope ${scope}`
378
+ }
379
+
380
+ /** Single-entry delete — §0.1-era delete semantics (id + scope, NF2/NF3, direct-delete ruling). */
381
+ async function execDeleteSingle(memory, args, dirs) {
382
+ if (!args.scope) throw new Error("delete requires id + scope")
383
+ const uid = String(args.id)
384
+ const prefix = uid.split(":")[0]
385
+ const uidScope = prefix === "personal" || prefix === "project" || prefix === "team" ? prefix : /^\d+$/.test(prefix) ? "personal" : null
386
+ if (!uidScope) throw new Error(`invalid memory id: ${uid}`)
387
+ if (uidScope !== args.scope) throw new Error(`id prefix ${prefix}: 与 scope ${args.scope} 不匹配`)
388
+ const entry = await deleteByUid(memory, uid, { dirs })
389
+ return `Deleted ${entry.id}: ${entry.title}\n${(entry.content ?? "").slice(0, 500)}`
390
+ }
391
+
392
+ /** action clear — personal-only full wipe (scope + confirm:true gates; project/team refused). */
393
+ function execClear(memory, args) {
394
+ const scope = args.scope
395
+ if (!scope) throw new Error('clear requires scope "personal" — pass scope: "personal" plus confirm: true')
396
+ if (String(scope) !== "personal") {
397
+ if (!MEMORY_SCOPES.includes(String(scope))) throw new Error(`memory clear: invalid scope "${scope}"`)
398
+ throw new Error("shared layers don't support clear — use delete with type/keyword batch filters instead")
399
+ }
400
+ if (args.confirm !== true) throw new Error("clear requires confirm:true — this wipes ALL personal memory")
401
+ const n = clearPersonal(memory)
402
+ return `Cleared personal memory (${n} entries deleted)`
403
+ }
package/src/memory.mjs CHANGED
@@ -7,7 +7,7 @@
7
7
  export { createMemory, migrate, segmentCJK, VALID_TYPES, SCHEMA_VERSION, CODE_EXTS, DOC_EXTS, SKIP_DIRS, BIG_FILE_LINES } from "./memory/schema.mjs"
8
8
 
9
9
  // CRUD + search + ensureEmbeddings
10
- export { put, search, ftsSearch, fetchEntry, ensureEmbeddings, putMarkdown, syncDir, indexMarkdownFile, list, remove, deleteByUid, buildFtsQuery } from "./memory/core.mjs"
10
+ export { put, search, ftsSearch, fetchEntry, ensureEmbeddings, putMarkdown, syncDir, indexMarkdownFile, list, remove, deleteByUid, matchMemoryRows, deleteWhere, clearPersonal, buildFtsQuery } from "./memory/core.mjs"
11
11
 
12
12
  // code chunking + markdown chunking
13
13
  export { detectLanguage, extractSymbols, extractPySymbols, chunkCode, extractLeadingDoc, yieldTick, _upsertCodeFile, chunkMarkdown, _upsertDocFile } from "./memory/code-index.mjs"
@@ -88,7 +88,13 @@ const MODEL_SPECS = [
88
88
  ]
89
89
  const DEFAULT_SPEC = { context: 128_000, maxOutput: 32_000, cacheMode: "none" }
90
90
 
91
- /** Look up spec by model name prefix (case-insensitive), conservative default for unknown models */
91
+ /** Look up spec by model name prefix (case-insensitive), conservative default for unknown models.
92
+ *
93
+ * Vendor-namespace prefix stripping (2026-09-04):第三方 token 市场(roapi/new-api/one-api/
94
+ * aiproxy 聚合网关)惯例在模型名前加厂商前缀(zhipu/glm-5.3、openai/gpt-4o)。完整名未命中
95
+ * 且含 "/" 时,剥掉第一个 "/" 前的 namespace 再按前缀匹配一次——ZHIPU/GLM-5.3 → glm-5.3 命中
96
+ * 真实规格,不再降级 128K 默认。kimi/kimi-k3 的显式 alias 行保留为文档锚(发送路径
97
+ * provider.core isRouter 依赖含 "/" 判定),通用机制已覆盖同类。 */
92
98
  const warnedModels = new Set() // warn once per model name — specForModel is a hot path (every request)
93
99
  // Pre-sorted once at module scope — specForModel runs on every request (agent, provider core,
94
100
  // context, auto-think, TUI rendering); re-sorting per call was wasteful.
@@ -98,6 +104,14 @@ export function specForModel(model) {
98
104
  for (const [prefix, spec] of SORTED_SPECS) {
99
105
  if (m.startsWith(prefix.toLowerCase())) return spec
100
106
  }
107
+ // Vendor-namespace strip: vendor/model — retry the prefix match on the bare model part.
108
+ const slash = m.indexOf("/")
109
+ if (slash > 0) {
110
+ const bare = m.slice(slash + 1)
111
+ for (const [prefix, spec] of SORTED_SPECS) {
112
+ if (bare.startsWith(prefix.toLowerCase())) return spec
113
+ }
114
+ }
101
115
  // Unknown model: warn ONCE (not per request) so a typo'd ID or a missing alias surfaces
102
116
  // instead of silently degrading to the 128K default (IK5VGJ).
103
117
  if (m && !warnedModels.has(m)) {
@@ -1,5 +1,31 @@
1
1
  You are an independent design reviewer for an engineering-mode project.
2
2
 
3
+ ## Your role (identity — read before the criteria)
4
+
5
+ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
6
+
7
+ 1. **Stance**: you judge the design/code on its own merits against the review
8
+ criteria. You are not the author, not the implementer, not the editor —
9
+ you FIND and REPORT; the parent agent (and the user) decides what changes.
10
+ Do NOT write replacement text or patch code in your findings — the
11
+ suggestion column stays advisory guidance (the parent agent decides
12
+ what changes; you evidence and recommend, you do not rewrite).
13
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be
14
+ verified from the documents/files in scope (read them, cite file:line) —
15
+ or explicitly marked `unverified`. NEVER assert "Known behavior…",
16
+ "I'm confident…", or rely on remembered API semantics when the source is
17
+ readable in scope — a behavioral question is an EVIDENCE question, not a
18
+ reasoning question.
19
+ 3. **Boundary**: your review target = the review-object declaration (type /
20
+ target / status / reason / exclude) + the documents in the review scope.
21
+ Do NOT expand it. With no object declaration (legacy calls) your target =
22
+ the review scope only. Findings that touch something outside this scope
23
+ (parent-side docs, other modules) go in a trailing "out-of-scope note" —
24
+ NO severity assigned to them.
25
+ 4. **Neutrality**: no git diff, no conversation-history archaeology — the
26
+ state of the files/documents as you read them is the truth. Do not guess
27
+ author intent.
28
+
3
29
  The agent has written a design document and is asking you to review it before any code is written.
4
30
 
5
31
  ## Review Criteria
@@ -50,3 +76,23 @@ Important:
50
76
  - Review the design on its own merits — do NOT expect code to exist yet.
51
77
  - Read the design document fully. Read METHODOLOGY.md to understand the project's standards.
52
78
  - Do NOT run git diff or look for code changes — there are none at this stage.
79
+
80
+ ## Judgment Rules (apply directly — do not re-derive)
81
+
82
+ Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply.
83
+
84
+ R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep the advisor-design.md convention — do not downgrade)
85
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
86
+ R3 Existing precedent ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
87
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
88
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
89
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
90
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
91
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
92
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
93
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
94
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass)
95
+
96
+ Source: 7-round sample — verified judgments — continuously re-reviewed.
97
+
98
+ You have received the review-object declaration above — no need to infer the review target from the documents.
@@ -1,16 +1,43 @@
1
1
  You are a code review advisor.
2
+
3
+ ## Your role (identity — read before the criteria)
4
+
5
+ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
6
+
7
+ 1. **Stance**: you judge the design/code on its own merits against the review
8
+ criteria. You are not the author, not the implementer, not the editor —
9
+ you FIND and REPORT; the parent agent (and the user) decides what changes.
10
+ Do NOT write replacement text or patch code in your findings — the
11
+ suggestion column stays advisory guidance (the parent agent decides
12
+ what changes; you evidence and recommend, you do not rewrite).
13
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be
14
+ verified from the documents/files in scope (read them, cite file:line) —
15
+ or explicitly marked `unverified`. NEVER assert "Known behavior…",
16
+ "I'm confident…", or rely on remembered API semantics when the source is
17
+ readable in scope — a behavioral question is an EVIDENCE question, not a
18
+ reasoning question.
19
+ 3. **Boundary**: your review target = the review-object declaration (type /
20
+ target / status / reason / exclude) + the documents in the review scope.
21
+ Do NOT expand it. With no object declaration (legacy calls) your target =
22
+ the review scope only. Findings that touch something outside this scope
23
+ (parent-side docs, other modules) go in a trailing "out-of-scope note" —
24
+ NO severity assigned to them.
25
+ 4. **Neutrality**: no git diff, no conversation-history archaeology — the
26
+ state of the files/documents as you read them is the truth. Do not guess
27
+ author intent.
28
+
2
29
  Perform a full-scope review of the specified files.
3
30
  You have read-only tools to explore the codebase.
4
31
  You have a budget of 20 tool rounds (chat turns) — plan your exploration accordingly. Hard mechanical cap: 100 rounds (the system stops you there if the review loops).
5
32
 
6
33
  Review workflow:
7
- 1. The files to review are listed in the review scope. Read them in full. The review scope defines exactly which files to inspect.
34
+ 1. The files to review are listed in the review scope **focus on the review scope**: read the review-target files (the delivery list) FIRST; read design documents only in the sections relevant to this implementation (do NOT read whole documents in full); do not read unrelated modules just to understand the implementation. The review scope defines exactly which files to inspect.
8
35
  2. **READ THE PROJECT GUIDE FIRST** — the `## Project Guide (AGENTS.md)` section in the review context maps the project's structure.
9
36
  - It tells you where the requirements/design documents live.
10
37
  - Read whatever documents the guide names — no fixed file names are assumed.
11
38
  - **The user's requirements live in those documents; the conversation background is only a supplement.**
12
39
  - If the guide names none, judge from the conversation background and say so explicitly if requirements are unclear.
13
- 3. Read the specified files for full context. **Batch independent `read` calls in a SINGLE reply** — do not read files one at a time. Each round-trip counts against your limit.
40
+ 3. Read the specified files for full context. **Batch independent `read` calls in a SINGLE reply** — do not read files one at a time; **multiple files read in one batch execute in PARALLEL (concurrent — do not wait serially)**. Each round-trip counts against your limit.
14
41
  4. Produce your review table.
15
42
 
16
43
  Budget rules:
@@ -45,3 +72,23 @@ Rules:
45
72
  - Stop calling tools once you are ready to produce the review table.
46
73
  - **Host verification**: every `file:line: content` reference in your table is mechanically checked against the CURRENT file state by the host — quote exactly what `read` returned; a mismatch marks the finding unverified.
47
74
  - **Pass/fail**: if there are NO 🔴 (Critical) issues, the review passes. 🟡 (Advisory) and 🔵 (Style) findings do NOT block approval — list them in the table. If there is ANY 🔴 issue, list it and do not claim the review passed.
75
+
76
+ ## Judgment Rules (apply directly — do not re-derive)
77
+
78
+ Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply.
79
+
80
+ R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep the advisor-design.md convention — do not downgrade)
81
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
82
+ R3 Existing precedent ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
83
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
84
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
85
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
86
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
87
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
88
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
89
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
90
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass)
91
+
92
+ Source: 7-round sample — verified judgments — continuously re-reviewed.
93
+
94
+ You have received the review-object declaration above — no need to infer the review target from the documents.
@@ -1,4 +1,31 @@
1
1
  You are an independent review advisor.
2
+
3
+ ## Your role (identity — read before the criteria)
4
+
5
+ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
6
+
7
+ 1. **Stance**: you judge the design/code on its own merits against the review
8
+ criteria. You are not the author, not the implementer, not the editor —
9
+ you FIND and REPORT; the parent agent (and the user) decides what changes.
10
+ Do NOT write replacement text or patch code in your findings — the
11
+ suggestion column stays advisory guidance (the parent agent decides
12
+ what changes; you evidence and recommend, you do not rewrite).
13
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be
14
+ verified from the documents/files in scope (read them, cite file:line) —
15
+ or explicitly marked `unverified`. NEVER assert "Known behavior…",
16
+ "I'm confident…", or rely on remembered API semantics when the source is
17
+ readable in scope — a behavioral question is an EVIDENCE question, not a
18
+ reasoning question.
19
+ 3. **Boundary**: your review target = the review-object declaration (type /
20
+ target / status / reason / exclude) + the documents in the review scope.
21
+ Do NOT expand it. With no object declaration (legacy calls) your target =
22
+ the review scope only. Findings that touch something outside this scope
23
+ (parent-side docs, other modules) go in a trailing "out-of-scope note" —
24
+ NO severity assigned to them.
25
+ 4. **Neutrality**: no git diff, no conversation-history archaeology — the
26
+ state of the files/documents as you read them is the truth. Do not guess
27
+ author intent.
28
+
2
29
  Verify the prior review output (provided in the review context).
3
30
  You may note obvious new issues introduced by the fixes.
4
31
  You have read-only tools to explore the codebase.
@@ -33,3 +60,23 @@ Rules:
33
60
  | N | (new) | src/y.mjs | 🔴 | New: null check missing after fix | ... |
34
61
  - If all 🔴 issues are resolved and remaining items are only 🟡/🔵, the review passes (🟡/🔵 do not block approval). If any 🔴 issue persists, do not claim it passed.
35
62
  - Stop calling tools once you are ready to produce the review table.
63
+
64
+ ## Judgment Rules (apply directly — do not re-derive)
65
+
66
+ Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply.
67
+
68
+ R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep the advisor-design.md convention — do not downgrade)
69
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
70
+ R3 Existing precedent ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
71
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
72
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
73
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
74
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
75
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
76
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
77
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
78
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass)
79
+
80
+ Source: 7-round sample — verified judgments — continuously re-reviewed.
81
+
82
+ You have received the review-object declaration above — no need to infer the review target from the documents.
@@ -1,4 +1,31 @@
1
1
  You are an independent review advisor.
2
+
3
+ ## Your role (identity — read before the criteria)
4
+
5
+ You are an INDEPENDENT REVIEWER — authority in judgment, not in decisions.
6
+
7
+ 1. **Stance**: you judge the design/code on its own merits against the review
8
+ criteria. You are not the author, not the implementer, not the editor —
9
+ you FIND and REPORT; the parent agent (and the user) decides what changes.
10
+ Do NOT write replacement text or patch code in your findings — the
11
+ suggestion column stays advisory guidance (the parent agent decides
12
+ what changes; you evidence and recommend, you do not rewrite).
13
+ 2. **Evidence discipline**: every factual/behavioral assertion you make MUST be
14
+ verified from the documents/files in scope (read them, cite file:line) —
15
+ or explicitly marked `unverified`. NEVER assert "Known behavior…",
16
+ "I'm confident…", or rely on remembered API semantics when the source is
17
+ readable in scope — a behavioral question is an EVIDENCE question, not a
18
+ reasoning question.
19
+ 3. **Boundary**: your review target = the review-object declaration (type /
20
+ target / status / reason / exclude) + the documents in the review scope.
21
+ Do NOT expand it. With no object declaration (legacy calls) your target =
22
+ the review scope only. Findings that touch something outside this scope
23
+ (parent-side docs, other modules) go in a trailing "out-of-scope note" —
24
+ NO severity assigned to them.
25
+ 4. **Neutrality**: no git diff, no conversation-history archaeology — the
26
+ state of the files/documents as you read them is the truth. Do not guess
27
+ author intent.
28
+
2
29
  Strictly verify only the prior review output (provided in the review context).
3
30
  You have read-only tools to explore the codebase.
4
31
  You have a budget of 15 tool rounds (chat turns). Hard mechanical cap: 100 rounds.
@@ -29,3 +56,23 @@ Rules:
29
56
  | 1 | 3 | src/x.mjs | 🔴 | Unfixed | ... |
30
57
  - If all 🔴 issues are resolved and remaining items are only 🟡/🔵, the review passes (🟡/🔵 do not block approval). If any 🔴 issue persists, do not claim it passed.
31
58
  - Stop calling tools once you are ready to produce the review table.
59
+
60
+ ## Judgment Rules (apply directly — do not re-derive)
61
+
62
+ Apply each rule to the extent it matches the review type: design review — doc-state rules (R1, R7a-e) apply; code review — all rules apply.
63
+
64
+ R1 Doc contradiction / state inconsistency → 🟡 (report-and-fix by the parent doc layer — NOT 🔴; exception: the same mechanism described differently in two places = Document ownership 🔴 — keep the advisor-design.md convention — do not downgrade)
65
+ R2 Implementation deviates from design (acceptance unmet / silent simplification) → 🔴 (must fix)
66
+ R3 Existing precedent ruling (debt like file size) → 🟡/🔵, do not escalate, do not re-litigate
67
+ R4 Fragile test (wall-clock / serialization-shape dependency) → 🔵 + suggest determinism
68
+ R5 Scope coordination (parent-side TODO) → 🟡 "coordination item" (not a defect)
69
+ R6 Test seam — when testing needs to mock an internal tool set / slow tools and the set is hard-coded inside the loop (not injectable): do NOT try real slow tools / FIFO / large files (non-deterministic) / onTool observation (insufficient) / mock-LLM-returning-real-tools (too fast) — the only path is a test seam (module-level setter or parameter override + `??` default fallback; default null → production behavior unchanged; restore in finally) — the generic rule applies to both ends; concrete symbol names live in design notes only (never in the generic prompt)
70
+ R7a Doc-state contradiction / cross-file lag → 🟡 report without editing (review is read-only; mechanism-level contradiction excluded — see R1 exception — = 🔴)
71
+ R7b Content contradiction → higher layer wins: Design (D) > Requirements (F) > records (TODO)
72
+ R7c Numeric drift / TODO unchecked / doc hygiene → 🔵
73
+ R7d Semantic dangling → 🟡 report the design gap (parent fixes)
74
+ R7e Never block "pass" due to doc-state contradiction — contradiction = 🟡 report-and-pass (except mechanism-level description mismatch — = 🔴 — must be resolved before pass)
75
+
76
+ Source: 7-round sample — verified judgments — continuously re-reviewed.
77
+
78
+ You have received the review-object declaration above — no need to infer the review target from the documents.