cc 3 дней назад
Родитель
Сommit
1554ea959b

+ 3 - 3
.workbuddy/memory/2026-09-24.md

@@ -10,7 +10,7 @@
   - `views/codexAnalysis/codexAnalysis.vue` — 三栏骨架 + 空态示例。
   - `views/codexAnalysis/codexAnalysis.vue` — 三栏骨架 + 空态示例。
 - 改 2 处登记(无逻辑改动):`core/router/helper/routeHelper.ts` 加 `explicitDynamicViewMap['/codexAnalysis/codexAnalysis']`;
 - 改 2 处登记(无逻辑改动):`core/router/helper/routeHelper.ts` 加 `explicitDynamicViewMap['/codexAnalysis/codexAnalysis']`;
   `core/store/modules/menu.json` 在 10263「AI 数据分析」分组下加菜单项(id 10267,标题「Codex 分析」)。
   `core/store/modules/menu.json` 在 10263「AI 数据分析」分组下加菜单项(id 10267,标题「Codex 分析」)。
-- 计划文档 `codex-analysis-plan.md`(v1.0.0 → v1.9.0,逐版本 review)。
+- 计划文档 `../../docs/design/codex-analysis-plan.md`(v1.0.0 → v1.9.0,逐版本 review)。
 
 
 **验证**
 **验证**
 - `pnpm build` exit=0,产出 `dist/assets/codexAnalysis-*.{js,css}`。
 - `pnpm build` exit=0,产出 `dist/assets/codexAnalysis-*.{js,css}`。
@@ -58,7 +58,7 @@
 2. **计划有但没做**:产物栏「新产物角标」。已在 store 里按「上一轮快照对比」实现,
 2. **计划有但没做**:产物栏「新产物角标」。已在 store 里按「上一轮快照对比」实现,
    手动刷新即清除。
    手动刷新即清除。
 
 
-如实记录在 `codex-analysis-plan.md` 的「与计划的偏差」表里(关系图导出是 JSON 不是图、
+如实记录在 `../../docs/design/codex-analysis-plan.md` 的「与计划的偏差」表里(关系图导出是 JSON 不是图、
 HTML 报告里图表是折叠 option、未做行级 diff 染色、未真机 GUI 端到端验证)。
 HTML 报告里图表是折叠 option、未做行级 diff 染色、未真机 GUI 端到端验证)。
 
 
 ## v1.9.1 + v2.0.0(数据分析增强)
 ## v1.9.1 + v2.0.0(数据分析增强)
@@ -122,7 +122,7 @@ HTML 报告里图表是折叠 option、未做行级 diff 染色、未真机 GUI
 
 
 ## v2.1.0 可执行计划(只出计划,未实施)
 ## v2.1.0 可执行计划(只出计划,未实施)
 
 
-写进 `codex-analysis-plan.md` 第八节(并顺手把该文档编号从乱的「七插在四前」理顺成一→八):
+写进 `../../docs/design/codex-analysis-plan.md` 第八节(并顺手把该文档编号从乱的「七插在四前」理顺成一→八):
 - **v2.1.1 运行参数显式化**(sandbox / approvalPolicy 目前是我硬编码的 `workspace-write`+`on-request`,
 - **v2.1.1 运行参数显式化**(sandbox / approvalPolicy 目前是我硬编码的 `workspace-write`+`on-request`,
   属实现缺口,最该先修;危险选项要二次确认且不许作为全局默认静默继承)
   属实现缺口,最该先修;危险选项要二次确认且不许作为全局默认静默继承)
 - **v2.1.5 产物增强**(搜索 / 类型筛选 / 本轮变更 + patch 展开;patch 路径解析失败必须静默降级)
 - **v2.1.5 产物增强**(搜索 / 类型筛选 / 本轮变更 + patch 展开;patch 路径解析失败必须静默降级)

+ 3 - 3
.workbuddy/memory/MEMORY.md

@@ -67,7 +67,7 @@
 ## Codex 数据分析新包(`src/codex/`,2026-09-24 起)
 ## Codex 数据分析新包(`src/codex/`,2026-09-24 起)
 
 
 - 需求:**全新包、不复用 `src/ai/` 聊天代码、所有文件 `codex` 前缀**(含视图入口 `views/codexAnalysis/codexAnalysis.vue`)。
 - 需求:**全新包、不复用 `src/ai/` 聊天代码、所有文件 `codex` 前缀**(含视图入口 `views/codexAnalysis/codexAnalysis.vue`)。
-- 计划文档:仓库根 `codex-analysis-plan.md`(v1.0.0 → v1.9.0,逐版本停下 review)。
+- 计划文档:仓库根 `../../docs/design/codex-analysis-plan.md`(v1.0.0 → v1.9.0,逐版本停下 review)。
 - **v1.0.0 ~ v1.9.0 已全部落地**(骨架 / 会话 / 流式对话 / 富内容块 / 图表关系图 / 过程与审批 /
 - **v1.0.0 ~ v1.9.0 已全部落地**(骨架 / 会话 / 流式对话 / 富内容块 / 图表关系图 / 过程与审批 /
   产物 / 报告导出 / 恢复与诊断 / 打磨)。前端 31 个文件全 `codex` 前缀;主进程新增
   产物 / 报告导出 / 恢复与诊断 / 打磨)。前端 31 个文件全 `codex` 前缀;主进程新增
   `codexArtifactService.ts` + `codexArtifactCtl.ts`(root 白名单落盘 + 路径穿越校验 + 只读不删),
   `codexArtifactService.ts` + `codexArtifactCtl.ts`(root 白名单落盘 + 路径穿越校验 + 只读不删),
@@ -105,8 +105,8 @@
   - 接线模式:**页面在 setup 中实例化 composable + 同名解构**(页面代码/模板零改动),`provide` 放在 setup 末尾;
   - 接线模式:**页面在 setup 中实例化 composable + 同名解构**(页面代码/模板零改动),`provide` 放在 setup 末尾;
     wiring 插在「最后一个依赖声明」**之后**(锚点是依赖声明的最后一行)
     wiring 插在「最后一个依赖声明」**之后**(锚点是依赖声明的最后一行)
 
 
-- 方案文档:仓库根 `cleaning-refactor-plan.md`。已拍板:**路由不动**(cleaning.vue 留作入口)、
-  通信走 **provide/inject + 领域 composable**、样式保持**全局 less 非 scoped**、**业务逻辑一律不许动**、
+- 方案文档:仓库根 `../../docs/design/cleaning-refactor-plan.md`。已拍板: **路由不动**(cleaning.vue 留作入口)、
+  通信走 **provide/inject + 领域 composable**、样式保持 **全局 less 非 scoped**、 **业务逻辑一律不许动**、
   **每阶段停下来 review**。
   **每阶段停下来 review**。
 - 目录:`cleaning/{types,constants,context}.ts` + `cleaning/{utils,composables,components,styles}/`
 - 目录:`cleaning/{types,constants,context}.ts` + `cleaning/{utils,composables,components,styles}/`
 - 沟通模式(Phase 3 起确定的**主力模式**,后续阶段照这个来):
 - 沟通模式(Phase 3 起确定的**主力模式**,后续阶段照这个来):

+ 1 - 1
.zcode/plans/plan-session_1bc2a9baf5931f11f8e15fbbfd50834d.md

@@ -155,7 +155,7 @@
 ## 四、验收标准
 ## 四、验收标准
 - **Commit 1**:上述行级诊断/计数测试全绿;真 DuckDB 上 DATE/TIME/TIMESTAMP/DECIMAL 不再整批失败;`ai_clean_reject` 能查到每一行被拒的列与原因;`row_total=row_loaded+row_rejected`(成功任务);未清洗 sheet 不再计成功。
 - **Commit 1**:上述行级诊断/计数测试全绿;真 DuckDB 上 DATE/TIME/TIMESTAMP/DECIMAL 不再整批失败;`ai_clean_reject` 能查到每一行被拒的列与原因;`row_total=row_loaded+row_rejected`(成功任务);未清洗 sheet 不再计成功。
 - **Commit 2**:golden set(同表头/同义词/列序/多列/少列/括号单位)档位符合预期;列序变化不写错列;同批次同 md5 全部入库;跨案件互不污染;CSV/多块/合并表头可入库。
 - **Commit 2**:golden set(同表头/同义词/列序/多列/少列/括号单位)档位符合预期;列序变化不写错列;同批次同 md5 全部入库;跨案件互不污染;CSV/多块/合并表头可入库。
-- **前后对比**:用 `smart-clean-test-cases.md` + 三个真实样本跑同一批,记录匹配漏斗、成功/失败行数、逐列错误率;以“自建表不参与匹配率、未清洗不计成功”后的真实基线为准。
+- **前后对比**:用 `../../docs/design/smart-clean-test-cases.md` + 三个真实样本跑同一批,记录匹配漏斗、成功/失败行数、逐列错误率;以“自建表不参与匹配率、未清洗不计成功”后的真实基线为准。
 
 
 ## 五、风险与顺序
 ## 五、风险与顺序
 - 拒绝记录表会随数据量增长:按 job/file 清理 + 可配置保留上限;应用日志只采样。
 - 拒绝记录表会随数据量增长:按 job/file 清理 + 可配置保留上限;应用日志只采样。

+ 1 - 1
QUICK_START.md

@@ -251,5 +251,5 @@ cd ai-frontend && pnpm type:check && pnpm lint:eslint
 |---|---|
 |---|---|
 | [AI_AGENT.md](AI_AGENT.md) | **仓库级编码约定**:分层落位、命名规范、Agent 工具清单与硬约束、前后端渲染契约 |
 | [AI_AGENT.md](AI_AGENT.md) | **仓库级编码约定**:分层落位、命名规范、Agent 工具清单与硬约束、前后端渲染契约 |
 | `ai-server/CODE_WIKI.md` | 模块级说明,⚠️ **部分章节仍是 Solon / `com.qingjian` 时代内容**(`app.yml`、contextPath `!/js/a/`、启动类 `Solon.start()`),以代码和本文配置为准 |
 | `ai-server/CODE_WIKI.md` | 模块级说明,⚠️ **部分章节仍是 Solon / `com.qingjian` 时代内容**(`app.yml`、contextPath `!/js/a/`、启动类 `Solon.start()`),以代码和本文配置为准 |
-| `continuous-insight-plan.md`、`tool-call-detail-plan.md` 等 | 各特性设计稿(根目录 `*-plan.md` / `*.md`) |
+| `docs/design/continuous-insight-plan.md`、`docs/design/tool-call-detail-plan.md` 等 | 各特性设计稿(根目录 `*-plan.md` / `*.md`) |
 | `readme.md` | 离线栅格地图(pmtiles 转换)与 `regInfo` 授权工具的零散说明 |
 | `readme.md` | 离线栅格地图(pmtiles 转换)与 `regInfo` 授权工具的零散说明 |

+ 2 - 2
api-method-refactor-plan.md → docs/design/api-method-refactor-plan.md

@@ -66,7 +66,7 @@
 
 
 ## 2. 后端改造清单(15 处)
 ## 2. 后端改造清单(15 处)
 
 
-| # | 文件(相对 `ai-server/src/main/java/com/zsjz/ai/`) | 行 | 现状 | 接口 | 形参 | 目标 | 前端传参 |
+| # | 文件(相对 `../../ai-server/src/main/java/com/zsjz/ai`) | 行 | 现状 | 接口 | 形参 | 目标 | 前端传参 |
 |---|---|---|---|---|---|---|---|
 |---|---|---|---|---|---|---|---|
 | 1 | `module/agent/controller/AgentChatController.java` | 62 | PUT | `/chat/sessions/{sessionId}/title` updateSessionTitle | 2(path+param) | **GET** | query: `title` |
 | 1 | `module/agent/controller/AgentChatController.java` | 62 | PUT | `/chat/sessions/{sessionId}/title` updateSessionTitle | 2(path+param) | **GET** | query: `title` |
 | 2 | 同上 | 70 | DELETE | ~~`/chat/sessions/{sessionId}`~~ → `/chat/sessions/{sessionId}/delete` deleteSession | 1 | **GET** | — |
 | 2 | 同上 | 70 | DELETE | ~~`/chat/sessions/{sessionId}`~~ → `/chat/sessions/{sessionId}/delete` deleteSession | 1 | **GET** | — |
@@ -314,7 +314,7 @@ return aiHttp.getRaw<void>(`${BASE}/sessions/${id}/delete`, { caseId });
 
 
 ---
 ---
 
 
-## 8. 规范条文(写入 `AI_AGENT.md`「接口方法规范」)
+## 8. 规范条文(写入 `../../AI_AGENT.md`「接口方法规范」)
 
 
 1. 只用 **GET / POST**,禁止 `PUT` / `DELETE` / `PATCH`。
 1. 只用 **GET / POST**,禁止 `PUT` / `DELETE` / `PATCH`。
 2. 方法带 `@RequestBody` → 必须 POST。
 2. 方法带 `@RequestBody` → 必须 POST。

+ 0 - 0
cleaning-refactor-plan.md → docs/design/cleaning-refactor-plan.md


+ 6 - 6
codex-analysis-plan.md → docs/design/codex-analysis-plan.md

@@ -1,14 +1,14 @@
 # Codex 数据分析(前端新包)开发计划
 # Codex 数据分析(前端新包)开发计划
 
 
-> 目标:在 `ai-electron/frontend` 里新开一个**独立包**,基于已集成的 Codex 基座,做一个数据分析类的对话产品。
-> 约束:**不复用** `src/ai/` 的聊天代码;新建代码文件**一律 `codex` 前缀**;业务/后端接口 `ai-server` 不动。
-> 版本号用于跟踪进度:**每个版本一次提交 + 一次验收**,未验收不进下一版。
+> 目标:在 `../../ai-electron/frontend` 里新开一个 **独立包**,基于已集成的 Codex 基座,做一个数据分析类的对话产品。
+> 约束: **不复用** `src/ai/` 的聊天代码;新建代码文件 **一律 `codex` 前缀**;业务/后端接口 `ai-server` 不动。
+> 版本号用于跟踪进度: **每个版本一次提交 + 一次验收**,未验收不进下一版。
 
 
 ---
 ---
 
 
 ## 一、现状盘点(已确认)
 ## 一、现状盘点(已确认)
 
 
-### 1.1 Codex 基座已就绪(主进程 `ai-electron/electron/`)
+### 1.1 Codex 基座已就绪(主进程 `../../ai-electron/electron`)
 
 
 | 层 | 文件 | 状态 |
 | 层 | 文件 | 状态 |
 | --- | --- | --- |
 | --- | --- | --- |
@@ -275,8 +275,8 @@ src/codex/
 ## 六、边界与验证
 ## 六、边界与验证
 
 
 - **不改**:`ai-server` 后端、`src/ai/**` 的聊天代码(仅 D2 里 `codexApi.ts` 可能变成转发,行为不变)。
 - **不改**:`ai-server` 后端、`src/ai/**` 的聊天代码(仅 D2 里 `codexApi.ts` 可能变成转发,行为不变)。
-- **验证**:每版 `pnpm build`(cwd `ai-electron/frontend`)+ `pnpm type:check`。
-  注意:`type:check` 仓库本身有约 89 条既有报错(集中在 `call/trans/graph/otg/person`),判断标准是**本包文件 0 报错**,不看退出码。
+- **验证**:每版 `pnpm build`(cwd `../../ai-electron/frontend`)+ `pnpm type:check`。
+  注意:`type:check` 仓库本身有约 89 条既有报错(集中在 `call/trans/graph/otg/person`),判断标准是 **本包文件 0 报错**,不看退出码。
 - **清理**:不留临时脚本、临时日志。
 - **清理**:不留临时脚本、临时日志。
 
 
 ---
 ---

+ 13 - 13
continuous-insight-plan.md → docs/design/continuous-insight-plan.md

@@ -23,7 +23,7 @@
 | 3 | **下一步思路(Next Steps)** | 可执行动作清单,每条指明"在哪做"(跳转到对应页面/工具) |
 | 3 | **下一步思路(Next Steps)** | 可执行动作清单,每条指明"在哪做"(跳转到对应页面/工具) |
 | 4 | **追问下钻** | 对任一条线索继续追问,走普通对话流(复用现有富渲染块) |
 | 4 | **追问下钻** | 对任一条线索继续追问,走普通对话流(复用现有富渲染块) |
 
 
-### 1.1 线索类型(沿用 `AI_AGENT.md` 领域术语)
+### 1.1 线索类型(沿用 `../../AI_AGENT.md` 领域术语)
 
 
 | 线索类型 | 触发特征(从持续联系数据可判) |
 | 线索类型 | 触发特征(从持续联系数据可判) |
 |---|---|
 |---|---|
@@ -188,7 +188,7 @@
 
 
 #### 4.1.2 表结构
 #### 4.1.2 表结构
 
 
-DDL 落 `sql/`(沿用项目现有目录与命名习惯)。字段风格**对齐现有的 `agent_chat_session` / `agent_message`**:
+DDL 落 `../../sql`(沿用项目现有目录与命名习惯)。字段风格**对齐现有的 `agent_chat_session` / `agent_message`**:
 时间列用 `create_at` / `update_at`(`LocalDateTime`,`@TableField(fill = FieldFill.INSERT / INSERT_UPDATE)`),
 时间列用 `create_at` / `update_at`(`LocalDateTime`,`@TableField(fill = FieldFill.INSERT / INSERT_UPDATE)`),
 半结构化数据用 `jsonb`(`@TableField(typeHandler = JsonbTypeHandler.class, jdbcType = JdbcType.OTHER)`)。
 半结构化数据用 `jsonb`(`@TableField(typeHandler = JsonbTypeHandler.class, jdbcType = JdbcType.OTHER)`)。
 字段命名也与 `agent_message` 保持一致(`role` / `content` / `content_type` / `token_count` / `metadata` /
 字段命名也与 `agent_message` 保持一致(`role` / `content` / `content_type` / `token_count` / `metadata` /
@@ -410,7 +410,7 @@ zsjz:
 
 
 **决策:放在 `module/agent/insight/` 子包,不新建 `module/insight`。**
 **决策:放在 `module/agent/insight/` 子包,不新建 `module/insight`。**
 
 
-理由:`AI_AGENT.md` §4 规定 `<domain>` 为固定取值集合(`agent/call/dm/.../trans`),`insight` 不在其中;且该能力与 agent 子系统强耦合(复用 `HarnessAgent`、`AgentStateStore`、工具与 `SqlResultStore`)。**「独立存储」的诉求由独立的两张表满足**,模块不必强行拆。
+理由:`../../AI_AGENT.md` §4 规定 `<domain>` 为固定取值集合(`agent/call/dm/.../trans`),`insight` 不在其中;且该能力与 agent 子系统强耦合(复用 `HarnessAgent`、`AgentStateStore`、工具与 `SqlResultStore`)。**「独立存储」的诉求由独立的两张表满足**,模块不必强行拆。
 
 
 ```
 ```
 ai-server/src/main/java/com/zsjz/ai/module/agent/insight/
 ai-server/src/main/java/com/zsjz/ai/module/agent/insight/
@@ -532,7 +532,7 @@ sysPrompt(session) =
 
 
 **返回形态**:`/insight/**` 与 `/chat/**` 一致采用**裸返回**(不走 `Result<T>` 包装),
 **返回形态**:`/insight/**` 与 `/chat/**` 一致采用**裸返回**(不走 `Result<T>` 包装),
 理由:前端 `aiHttp.*Raw`(`isTransformResponse:false`)可直接复用,避免两套取值逻辑。
 理由:前端 `aiHttp.*Raw`(`isTransformResponse:false`)可直接复用,避免两套取值逻辑。
-(这是对 `AI_AGENT.md` §4「新增代码一律用 Result」的**有意偏离**,与既有 `/chat/**` 保持同一风格,文档化在此。)
+(这是对 `../../AI_AGENT.md` §4「新增代码一律用 Result」的**有意偏离**,与既有 `/chat/**` 保持同一风格,文档化在此。)
 
 
 ### 4.5 静态人设提示词要点(`prompts/insight/CALL_CONTINUOUS_INSIGHT.md`)
 ### 4.5 静态人设提示词要点(`prompts/insight/CALL_CONTINUOUS_INSIGHT.md`)
 
 
@@ -590,7 +590,7 @@ ai-frontend/src/
 | `<style>` | 抽屉样式与页面 `.continuous-page` 变量对齐,复用 `trans-analysis-table-ui.less` 的色板与圆角 |
 | `<style>` | 抽屉样式与页面 `.continuous-page` 变量对齐,复用 `trans-analysis-table-ui.less` 的色板与圆角 |
 
 
 **图标(踩坑点)**:`AI 研判` 用 `i-mdi:auto-fix`、线索卡等级用 `i-mdi:alert-decagram-outline` 等,
 **图标(踩坑点)**:`AI 研判` 用 `i-mdi:auto-fix`、线索卡等级用 `i-mdi:alert-decagram-outline` 等,
-**必须**在 `uno.config.ts` 的 `safelist` 中登记(`AI_AGENT.md` §8 坑 1 —— `menu.json` / 动态拼类名不会被扫描)。
+**必须**在 `uno.config.ts` 的 `safelist` 中登记(`../../AI_AGENT.md` §8 坑 1 —— `menu.json` / 动态拼类名不会被扫描)。
 
 
 ### 5.3 状态机(`useContinuousInsight`)
 ### 5.3 状态机(`useContinuousInsight`)
 
 
@@ -642,9 +642,9 @@ idle → creating(建会话)→ streaming(首轮/追问)→ done
 - [x] 前端 `pnpm type:check` + `pnpm lint:eslint` 无错
 - [x] 前端 `pnpm type:check` + `pnpm lint:eslint` 无错
       (注:仓库 `person` / `trans` 模块有大量**既有**类型错误,与本功能无关;
       (注:仓库 `person` / `trans` 模块有大量**既有**类型错误,与本功能无关;
       本次改动文件单独跑 ESLint / Stylelint / 类型检查均为 0 错)
       本次改动文件单独跑 ESLint / Stylelint / 类型检查均为 0 错)
-- [x] **新增两张表**:`agent_insight_session` / `agent_insight_message`,DDL 落 `sql/`;
-      确认**未改动** `agent_chat_session` / `agent_message` / 元数据三表
-      (`InsightStorageContractTest#insightTablesAreIndependentFromChatTables` 断言 DDL 不引用通用对话表)
+- [x] **新增两张表**:`agent_insight_session` / `agent_insight_message`,DDL 落 `../../sql`;
+  确认 **未改动** `agent_chat_session` / `agent_message` / 元数据三表
+  (`InsightStorageContractTest#insightTablesAreIndependentFromChatTables` 断言 DDL 不引用通用对话表)
 - [x] **`uk_aim_session_seq (session_id, seq)` 唯一约束到位**;`seq` 由 `UPDATE ... RETURNING last_seq` 原子分配
 - [x] **`uk_aim_session_seq (session_id, seq)` 唯一约束到位**;`seq` 由 `UPDATE ... RETURNING last_seq` 原子分配
       (两条都有测试;另有「全 insight 包不出现 `max(seq)`」的扫描断言)
       (两条都有测试;另有「全 insight 包不出现 `max(seq)`」的扫描断言)
 - [x] **没有任何 SQL 只按 `session_id` 查**(消息查询一律带 `case_id`),并有测试覆盖
 - [x] **没有任何 SQL 只按 `session_id` 查**(消息查询一律带 `case_id`),并有测试覆盖
@@ -716,7 +716,7 @@ idle → creating(建会话)→ streaming(首轮/追问)→ done
 
 
 ### 10.1 后端新增(23 个 Java + 2 XML + 1 提示词 + 1 DDL)
 ### 10.1 后端新增(23 个 Java + 2 XML + 1 提示词 + 1 DDL)
 
 
-落位 `ai-server/src/main/java/com/zsjz/ai/module/agent/insight/`:
+落位 `../../ai-server/src/main/java/com/zsjz/ai/module/agent/insight`:
 
 
 | 目录 | 文件 |
 | 目录 | 文件 |
 |---|---|
 |---|---|
@@ -733,7 +733,7 @@ idle → creating(建会话)→ streaming(首轮/追问)→ done
 | `controller/` | `ContinuousInsightController` |
 | `controller/` | `ContinuousInsightController` |
 
 
 资源:`resources/mappers/insight/{AgentInsightSessionMapper,AgentInsightMessageMapper}.xml`、
 资源:`resources/mappers/insight/{AgentInsightSessionMapper,AgentInsightMessageMapper}.xml`、
-`resources/prompts/insight/CALL_CONTINUOUS_INSIGHT.md`、`sql/insight_agent_tables.sql`。
+`resources/prompts/insight/CALL_CONTINUOUS_INSIGHT.md`、`../../sql/insight_agent_tables.sql`。
 
 
 改动既有文件 2 处:
 改动既有文件 2 处:
 - `module/agent/tools/AgentToolRegistry.java` —— `registerBusinessTools(Toolkit)` 保留原签名,新增
 - `module/agent/tools/AgentToolRegistry.java` —— `registerBusinessTools(Toolkit)` 保留原签名,新增
@@ -780,7 +780,7 @@ idle → creating(建会话)→ streaming(首轮/追问)→ done
 而 `CREATE TABLE IF NOT EXISTS` 对「表已存在」的情况**整句跳过**,连内部约束一起跳过;
 而 `CREATE TABLE IF NOT EXISTS` 对「表已存在」的情况**整句跳过**,连内部约束一起跳过;
 只有语句外的 `CREATE INDEX IF NOT EXISTS` 才会被补建。
 只有语句外的 `CREATE INDEX IF NOT EXISTS` 才会被补建。
 
 
-**已修复**:新增 `sql/insight_agent_tables_align.sql`(幂等补丁,23 条语句全部执行成功),补齐:
+**已修复**:新增 `../../sql/insight_agent_tables_align.sql`(幂等补丁,23 条语句全部执行成功),补齐:
 
 
 | 对象 | 状态 |
 | 对象 | 状态 |
 |---|---|
 |---|---|
@@ -796,9 +796,9 @@ idle → creating(建会话)→ streaming(首轮/追问)→ done
 
 
 ### 10.5 未做 / 待办
 ### 10.5 未做 / 待办
 
 
-- **`table_info` / `table_field` 元数据未登记**:经核对,这两张元数据表登记的是**案情/公安数据库表**
+- **`table_info` / `table_field` 元数据未登记**:经核对,这两张元数据表登记的是 **案情/公安数据库表**
   (`cert_info` / `entry_rec` / `vehicle_info` …,`classify='公安数据库'`),供 Agent 的 SQL schema 检索使用;
   (`cert_info` / `entry_rec` / `vehicle_info` …,`classify='公安数据库'`),供 Agent 的 SQL schema 检索使用;
-  本功能两张表建在 **master 平台库**、Agent 不检索它们,故**不属于** `AI_AGENT.md` §5「新增表结构同步补元数据」的适用范围。
+  本功能两张表建在 **master 平台库**、Agent 不检索它们,故 **不属于** `../../AI_AGENT.md` §5「新增表结构同步补元数据」的适用范围。
 - **需要真实数据库的用例未覆盖**:`seq` 并发不重号、级联删除生效这两条依赖 PG 行级锁与真实外键,
 - **需要真实数据库的用例未覆盖**:`seq` 并发不重号、级联删除生效这两条依赖 PG 行级锁与真实外键,
   本项目没有 Testcontainers / H2 基础设施,无法在内存库里验证 —— 已用静态断言覆盖其前置条件(见 §10.6)。
   本项目没有 Testcontainers / H2 基础设施,无法在内存库里验证 —— 已用静态断言覆盖其前置条件(见 §10.6)。
 - **`GET /insight/continuous/clues`** 已按设计**仅预留**,未接任何页面。
 - **`GET /insight/continuous/clues`** 已按设计**仅预留**,未接任何页面。

+ 0 - 0
controller-request-body-audit.md → docs/design/controller-request-body-audit.md


+ 2 - 2
electric-beacon-darwin-pwxCEck4.md → docs/design/electric-beacon-darwin-pwxCEck4.md

@@ -23,7 +23,7 @@
 - `src/App.vue` 重写:移除 AI 悬浮面板(2 处 Teleport、全部 `ai*` 状态/函数/`.ai-*` 样式),保留主题/水印/授权逻辑
 - `src/App.vue` 重写:移除 AI 悬浮面板(2 处 Teleport、全部 `ai*` 状态/函数/`.ai-*` 样式),保留主题/水印/授权逻辑
 - `src/core/store/modules/menu.json`:删除「AI 助手」IFRAME 节点(id 10261)
 - `src/core/store/modules/menu.json`:删除「AI 助手」IFRAME 节点(id 10261)
 - `package.json`:移除 `@jeesite/cms-lib`
 - `package.json`:移除 `@jeesite/cms-lib`
-- `AI_AGENT.md` §6:前端消费方指向 `ai-frontend/src/ai/api/`
+- `../../AI_AGENT.md` §6:前端消费方指向 `ai-frontend/src/ai/api/`
 
 
 **后端**
 **后端**
 - 删除 `templates/chat.html`、`static/js/chat.js`、`static/js/marked.min.js`、`static/css/chat.css`、`static/css/theme.css`、`static/layui/`
 - 删除 `templates/chat.html`、`static/js/chat.js`、`static/js/marked.min.js`、`static/css/chat.css`、`static/css/theme.css`、`static/layui/`
@@ -187,7 +187,7 @@ ai-frontend/src/ai/
 | **markdown 样式** | ✅ `streamdown` 样式已随聊天 chunk CSS 打包 |
 | **markdown 样式** | ✅ `streamdown` 样式已随聊天 chunk CSS 打包 |
 | `menu.json` / `package.json` JSON 合法性 | ✅ |
 | `menu.json` / `package.json` JSON 合法性 | ✅ |
 | 旧 AI 残留(`cms-lib` / `/src/cms` / `chatView` / `/ai/call`) | ✅ 无(仅 memory/计划文档中作为"已删除"的描述出现) |
 | 旧 AI 残留(`cms-lib` / `/src/cms` / `chatView` / `/ai/call`) | ✅ 无(仅 memory/计划文档中作为"已删除"的描述出现) |
-| `AI_AGENT.md` 同步 | ✅ 目录树 `cms/`→`ai/`;§6 控制器/工具清单纠正 + 新增"前端渲染管线";§8 新增四个坑;§11 context-path 标记为已实证修复 |
+| `../../AI_AGENT.md` 同步 | ✅ 目录树 `cms/`→`ai/`;§6 控制器/工具清单纠正 + 新增"前端渲染管线";§8 新增四个坑;§11 context-path 标记为已实证修复 |
 | **真实启动 ai-server** | ✅ `Tomcat started on port 8980 (http) with context path '/js/a'` + `Started App in 3.719 seconds`(修掉 mappers 残留后) |
 | **真实启动 ai-server** | ✅ `Tomcat started on port 8980 (http) with context path '/js/a'` + `Started App in 3.719 seconds`(修掉 mappers 残留后) |
 | **DB 迁移实际执行** | ✅ 对 `zsjz-ai` 执行 `user_id` 加列 + 索引(additive/幂等),存量 4 会话 `user_id` 全 NULL、`case_id` 全有值 |
 | **DB 迁移实际执行** | ✅ 对 `zsjz-ai` 执行 `user_id` 加列 + 索引(additive/幂等),存量 4 会话 `user_id` 全 NULL、`case_id` 全有值 |
 | 端到端对话链路 | ❌ 未能完成,见 §7(开发库缺平台库结构 + 缺客户端运行时资产) |
 | 端到端对话链路 | ❌ 未能完成,见 §7(开发库缺平台库结构 + 缺客户端运行时资产) |

+ 4 - 4
electron-migration-plan.md → docs/design/electron-migration-plan.md

@@ -19,10 +19,10 @@
 | `SettingFooter.vue` 两个入口按钮/弹窗/引导事件监听、`components/index.ts` 两个导出 | 一并清理 |
 | `SettingFooter.vue` 两个入口按钮/弹窗/引导事件监听、`components/index.ts` 两个导出 | 一并清理 |
 | `caseConfigGuide.ts` 的 `hydratePersistedCaseGlobalConfig()` 调用与两个弹窗事件常量 | 保留 `initGlobalCaseConfig()`(现在只从后端读配置)与 `OPEN_SETTING_DRAWER_EVENT` |
 | `caseConfigGuide.ts` 的 `hydratePersistedCaseGlobalConfig()` 调用与两个弹窗事件常量 | 保留 `initGlobalCaseConfig()`(现在只从后端读配置)与 `OPEN_SETTING_DRAWER_EVENT` |
 
 
-**删除后路径怎么配(重要)**:两个路径本来就存在**服务端**配置里(`SystemConfManager` → `QingJian/conf/c5e503ad.json` 的 `mapLocal` / `cellLocal` 字段),
+**删除后路径怎么配(重要)**:两个路径本来就存在**服务端**配置里(`SystemConfManager` → `../../QingJian/conf/c5e503ad.json` 的 `mapLocal` / `cellLocal` 字段),
 前端只是通过 `/sys/getMaplocal`、`/sys/getCellLocal` 读回来缓存。所以 UI 删掉后有两种设置方式:
 前端只是通过 `/sys/getMaplocal`、`/sys/getCellLocal` 读回来缓存。所以 UI 删掉后有两种设置方式:
 1. 调用接口:`GET /js/a/sys/mapLocal?path=...`、`GET /js/a/sys/cellLocal?path=...`(`/sys/cellLocal` 会校验文件存在且非目录)
 1. 调用接口:`GET /js/a/sys/mapLocal?path=...`、`GET /js/a/sys/cellLocal?path=...`(`/sys/cellLocal` 会校验文件存在且非目录)
-2. 直接编辑 `QingJian/conf/c5e503ad.json`:`{"mapLocal":"E:/map","cellLocal":"E:/cell/xxx.bin"}`(**改完需重启后端**,配置是启动时读入内存的)
+2. 直接编辑 `../../QingJian/conf/c5e503ad.json`:`{"mapLocal":"E:/map","cellLocal":"E:/cell/xxx.bin"}`( **改完需重启后端**,配置是启动时读入内存的)
 
 
 ### 3. 离线地图瓦片:方案 A 已落地 ✅
 ### 3. 离线地图瓦片:方案 A 已落地 ✅
 **后端**(`SystemController` + `SystemService`)
 **后端**(`SystemController` + `SystemService`)
@@ -52,7 +52,7 @@
 | 1 | 窗口最小化/最大化/关闭 | `window.electron.windowMinimize/Maximize/Close` + `window:maximize-change` 事件 | ✅ **已完成**:4 个组件 + `useElectronWindow` 已删除(本就没有页面挂载它们,纯死代码) | 无 | — |
 | 1 | 窗口最小化/最大化/关闭 | `window.electron.windowMinimize/Maximize/Close` + `window:maximize-change` 事件 | ✅ **已完成**:4 个组件 + `useElectronWindow` 已删除(本就没有页面挂载它们,纯死代码) | 无 | — |
 | 2 | 版本号展示 | `useElectronWindow().getVersion()` | ✅ **已完成**:AppLogo/ProductInfoModal 改用构建期 `__APP_INFO__.pkg.version`(vite define 已注入) | 无 | — |
 | 2 | 版本号展示 | `useElectronWindow().getVersion()` | ✅ **已完成**:AppLogo/ProductInfoModal 改用构建期 `__APP_INFO__.pkg.version`(vite define 已注入) | 无 | — |
 | 3 | 离线地图瓦片读取 | `ElectronPmtilesSource.getBytes()` 走 ipc `readBinaryFileRange` | ✅ **已完成**:后端 `GET /sys/map/pmtiles`(支持 Range)+ 前端 `HttpPmtilesSource`;未配置目录时回退前端静态 `resource/map/china.pmtiles` | 已实现 | — |
 | 3 | 离线地图瓦片读取 | `ElectronPmtilesSource.getBytes()` 走 ipc `readBinaryFileRange` | ✅ **已完成**:后端 `GET /sys/map/pmtiles`(支持 Range)+ 前端 `HttpPmtilesSource`;未配置目录时回退前端静态 `resource/map/china.pmtiles` | 已实现 | — |
-| 4 | 离线地图目录选择 | ipc `controller/os/selectFolder` | ✅ **已删除**功能;路径改由服务端配置(`/sys/mapLocal` 或 `QingJian/conf/c5e503ad.json`) | 已实现 | — |
+| 4 | 离线地图目录选择 | ipc `controller/os/selectFolder` | ✅ **已删除**功能;路径改由服务端配置(`/sys/mapLocal` 或 `../../QingJian/conf/c5e503ad.json`) | 已实现 | — |
 | 5 | 基站库(.bin)选择 | ipc `selectBinFile` + 读前 16 字节校验 | ✅ **已删除**功能;路径改由服务端配置(`/sys/cellLocal` 或同一配置文件) | 已实现 | — |
 | 5 | 基站库(.bin)选择 | ipc `selectBinFile` + 读前 16 字节校验 | ✅ **已删除**功能;路径改由服务端配置(`/sys/cellLocal` 或同一配置文件) | 已实现 | — |
 | 6 | 案件全局配置持久化 | ipc `readCaseGlobalConfig` / `writeCaseGlobalConfig` | 改存后端(按 caseId)——`/sys/getMaplocal`、`/sys/cellLocal` 接口已存在,直接复用;纯本机偏好可退化为 `localStorage` | 复用现有接口 | ★ |
 | 6 | 案件全局配置持久化 | ipc `readCaseGlobalConfig` / `writeCaseGlobalConfig` | 改存后端(按 caseId)——`/sys/getMaplocal`、`/sys/cellLocal` 接口已存在,直接复用;纯本机偏好可退化为 `localStorage` | 复用现有接口 | ★ |
 | 7 | 授权文件选择 | ipc `controller/effect/selectFile` → 传**服务端路径**给 `/sys/noNet` | ✅ **已完成**:`<input type=file accept=".xlts">` + `POST /sys/noNet`(multipart),后端落盘到 LICENSE_PATH 并校验激活 | 已实现 | — |
 | 7 | 授权文件选择 | ipc `controller/effect/selectFile` → 传**服务端路径**给 `/sys/noNet` | ✅ **已完成**:`<input type=file accept=".xlts">` + `POST /sys/noNet`(multipart),后端落盘到 LICENSE_PATH 并校验激活 | 已实现 | — |
@@ -101,7 +101,7 @@ export class FilePmtilesSource {
 
 
 这三处的路径最终都存到**服务端**(`/sys/mapLocal`、`/sys/cellLocal`、`/sys/upgrade`),Electron 里只是用对话框拿到一个字符串。Web 里最贴近的做法是**后端提供受限的目录浏览**:
 这三处的路径最终都存到**服务端**(`/sys/mapLocal`、`/sys/cellLocal`、`/sys/upgrade`),Electron 里只是用对话框拿到一个字符串。Web 里最贴近的做法是**后端提供受限的目录浏览**:
 
 
-- `GET /sys/browse?path=&root=` → 返回该目录下的子目录/文件(**必须白名单根目录 + normalize 防 `../` 穿越**,参考 `AgentPythonFileController.assertSafeSegment` 的写法)
+- `GET /sys/browse?path=&root=` → 返回该目录下的子目录/文件( **必须白名单根目录 + normalize 防 `../../..` 穿越**,参考 `AgentPythonFileController.assertSafeSegment` 的写法)
 - 前端把「选择文件夹」弹窗换成「服务端目录浏览弹窗」,点选结果仍是路径字符串 → 后续链路一行不用改
 - 前端把「选择文件夹」弹窗换成「服务端目录浏览弹窗」,点选结果仍是路径字符串 → 后续链路一行不用改
 - 若不想做浏览弹窗:退化为「文本框输入路径 + 保存时后端校验(存在/后缀/可读)」,弹一条明确的错误提示
 - 若不想做浏览弹窗:退化为「文本框输入路径 + 保存时后端校验(存在/后缀/可读)」,弹一条明确的错误提示
 
 

+ 0 - 0
enc.md → docs/design/enc.md


+ 1 - 1
smart-clean-test-cases.md → docs/design/smart-clean-test-cases.md

@@ -97,7 +97,7 @@
   `AiCleanStore` 有意为之,防被既有匹配器命中)。
   `AiCleanStore` 有意为之,防被既有匹配器命中)。
 - AI 表同表头二次清洗:header_md5 唯一键下**复用**已有模板与动态表(存档规则零模型),数据按 job 并入/可撤销。
 - AI 表同表头二次清洗:header_md5 唯一键下**复用**已有模板与动态表(存档规则零模型),数据按 job 并入/可撤销。
 
 
-### 测试数据(`ai-electron/frontend/public/__test__/`)
+### 测试数据(`../../ai-electron/frontend/public/__test__`)
 
 
 | 文件 | 构造 | 预期 |
 | 文件 | 构造 | 预期 |
 |---|---|---|
 |---|---|---|

+ 1 - 1
tool-call-detail-plan.md → docs/design/tool-call-detail-plan.md

@@ -109,7 +109,7 @@ NPE 发生在响应式流内部,会**直接打断整条 SSE**。
 
 
 ### 4.1 后端:结果归一化(修源头,新数据不再脏)
 ### 4.1 后端:结果归一化(修源头,新数据不再脏)
 
 
-**文件**:`ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentChatServiceImpl.java`
+**文件**:`../../ai-server/src/main/java/com/zsjz/ai/module/agent/service/impl/AgentChatServiceImpl.java`
 
 
 1. 新增归一化工具方法(幂等,失败原样返回):
 1. 新增归一化工具方法(幂等,失败原样返回):
 
 

+ 8 - 8
user-auth-datasource-plan.md → docs/design/user-auth-datasource-plan.md

@@ -1,10 +1,10 @@
 # 清鉴(zsjz-ai)用户体系 + 数据源管理重构 · 设计文档
 # 清鉴(zsjz-ai)用户体系 + 数据源管理重构 · 设计文档
 
 
-> 状态:**已实施**(阶段 0~3 完成,数据库迁移已执行,已端到端验证)
-> 目标:为现有**单机/客户端形态**的服务端补上「多用户同时使用」能力。
+> 状态: **已实施**(阶段 0~3 完成,数据库迁移已执行,已端到端验证)
+> 目标:为现有 **单机/客户端形态**的服务端补上「多用户同时使用」能力。
 >
 >
 > 实施要点与踩坑记录见 `.workbuddy-ai/memory/2026-09-18.md`。
 > 实施要点与踩坑记录见 `.workbuddy-ai/memory/2026-09-18.md`。
-> 迁移脚本 `sql/user.sql` **已在 `192.168.0.109:5432/zsjz-ai` 执行**;
+> 迁移脚本 `../../sql/user.sql` **已在 `192.168.0.109:5432/zsjz-ai` 执行**;
 > 内置账号 **admin / admin123**(首次登录后请立即改密)。
 > 内置账号 **admin / admin123**(首次登录后请立即改密)。
 
 
 ## 0. 问题背景
 ## 0. 问题背景
@@ -191,13 +191,13 @@ zsjz:
 - 同 JVM 同路径多次 `DriverManager.getConnection()` → **默认复用同一个 native database instance**(`jdbc_instance_cache` 默认 true)。重复开同一案件不会真正开第二个实例。
 - 同 JVM 同路径多次 `DriverManager.getConnection()` → **默认复用同一个 native database instance**(`jdbc_instance_cache` 默认 true)。重复开同一案件不会真正开第二个实例。
 - `duplicate()` 返回独立连接对象(独立 autoCommit/事务/pendingQueries),共享同一 instance。驱动内部已有 `connRefLock`,项目里那层 `synchronized` 冗余但无害,保留并加注释。
 - `duplicate()` 返回独立连接对象(独立 autoCommit/事务/pendingQueries),共享同一 instance。驱动内部已有 `connRefLock`,项目里那层 `synchronized` 冗余但无害,保留并加注释。
 - **`DuckDBAppender` 线程封闭**(构造时绑定 `ownerThreadId`),跨线程使用直接抛 `SQLException`。⇒ 必须审计所有 Loader 实例,**禁止把 `AbstractDataLoader` 放进静态缓存/共享 Map**。
 - **`DuckDBAppender` 线程封闭**(构造时绑定 `ownerThreadId`),跨线程使用直接抛 `SQLException`。⇒ 必须审计所有 Loader 实例,**禁止把 `AbstractDataLoader` 放进静态缓存/共享 Map**。
-- 跨进程打开同一 `.duckdb` 会因文件锁失败 ⇒ **部署约束:ai-server 单实例,不可水平扩容共享同一份 `QingJian/` 目录**。
+- 跨进程打开同一 `.duckdb` 会因文件锁失败 ⇒ **部署约束:ai-server 单实例,不可水平扩容共享同一份 `../../QingJian` 目录**。
 
 
 ---
 ---
 
 
 ## 4. 用户体系设计
 ## 4. 用户体系设计
 
 
-### 4.1 表结构(新增 `sql/user.sql`,PostgreSQL 方言)
+### 4.1 表结构(新增 `../../sql/user.sql`,PostgreSQL 方言)
 
 
 ```sql
 ```sql
 CREATE TABLE IF NOT EXISTS sys_user (
 CREATE TABLE IF NOT EXISTS sys_user (
@@ -439,13 +439,13 @@ return CaseContextHolder.callWith(agentCaseId, () -> originalTool.call(args));
 
 
 ## 7. 存量数据迁移
 ## 7. 存量数据迁移
 
 
-1. 执行 `sql/user.sql`:建 `sys_user` + 索引;插入内置管理员(预生成 BCrypt 哈希)
+1. 执行 `../../sql/user.sql`:建 `sys_user` + 索引;插入内置管理员(预生成 BCrypt 哈希)
 2. `case_info` 加 `owner_id` + 索引;唯一约束改为 `(owner_id, name)`
 2. `case_info` 加 `owner_id` + 索引;唯一约束改为 `(owner_id, name)`
 3. `UPDATE case_info SET owner_id = <admin id> WHERE owner_id IS NULL` → 现有 5 个案件全部归 admin
 3. `UPDATE case_info SET owner_id = <admin id> WHERE owner_id IS NULL` → 现有 5 个案件全部归 admin
 4. 案件库文件路径不变(`case_info.db_path` 已是绝对路径),无需搬迁
 4. 案件库文件路径不变(`case_info.db_path` 已是绝对路径),无需搬迁
 5. `case_info.pwd` 字段保留但不再校验
 5. `case_info.pwd` 字段保留但不再校验
 6. `sys_user` 属平台表,**不需要**登记 `table_info` / `table_field`
 6. `sys_user` 属平台表,**不需要**登记 `table_info` / `table_field`
-7. 无 Flyway/Liquibase ⇒ 迁移脚本 + 执行步骤写入 `sql/` 与 README,运维手工执行
+7. 无 Flyway/Liquibase ⇒ 迁移脚本 + 执行步骤写入 `../../sql` 与 README,运维手工执行
 
 
 ---
 ---
 
 
@@ -479,7 +479,7 @@ return CaseContextHolder.callWith(agentCaseId, () -> originalTool.call(args));
 | 跨进程文件锁 | 同 `.duckdb` 两进程打开会失败 | 部署约束:**ai-server 单实例**,不可水平扩容 |
 | 跨进程文件锁 | 同 `.duckdb` 两进程打开会失败 | 部署约束:**ai-server 单实例**,不可水平扩容 |
 | Redis 硬依赖 | Redis 挂 ⇒ 全站无法登录 | 运维保证 Redis 可用性 |
 | Redis 硬依赖 | Redis 挂 ⇒ 全站无法登录 | 运维保证 Redis 可用性 |
 | 401 语义冲突 | license 与登录都用 401 | 已拆分:license → `40301` |
 | 401 语义冲突 | license 与登录都用 401 | 已拆分:license → `40301` |
-| `AI_AGENT.md §8.4` 写的是"鉴权靠 Sa-Token Cookie,前端不注入任何 token 头" | 与新决策矛盾 | **本次改造必须同步更新 `AI_AGENT.md` §8.4 与 §4.5** |
+| `AI_AGENT.md §8.4` 写的是"鉴权靠 Sa-Token Cookie,前端不注入任何 token 头" | 与新决策矛盾 | **本次改造必须同步更新 `../../AI_AGENT.md` §8.4 与 §4.5** |
 | 顺手修 | `Result.java:3` 脏 import `io.milvus.param.R;`;`SUCCEED_CODE/FAILURE_CODE` 非 final | 一并清理 |
 | 顺手修 | `Result.java:3` 脏 import `io.milvus.param.R;`;`SUCCEED_CODE/FAILURE_CODE` 非 final | 一并清理 |
 
 
 ### 待验证(实现阶段实测,不要当成已确认)
 ### 待验证(实现阶段实测,不要当成已确认)