From 13f5c740a14cbf783ecc5ef393a71e0f26842610 Mon Sep 17 00:00:00 2001 From: john Date: Thu, 23 Jul 2026 21:31:09 +0800 Subject: [PATCH] feat: add page_data_dev code-run path and adm policy design docs Introduce Page Data dev repair intent routing, page_data_dev taskType autodetect, and help-code02 memindadm runtime policy specification. Co-authored-by: Cursor --- .env.example | 1 + chat-intent-router.mjs | 10 + chat-intent-router.test.mjs | 11 ++ chat-skills.mjs | 18 ++ chat-skills.test.mjs | 9 + docs/help-code01.md | 29 ++- docs/help-code02.md | 381 ++++++++++++++++++++++++++++++++++++ src/hooks/useTKMindChat.ts | 5 +- src/utils/agentRunMode.ts | 64 +++++- 9 files changed, 514 insertions(+), 14 deletions(-) create mode 100644 docs/help-code02.md diff --git a/.env.example b/.env.example index 4b8df20..0835a6c 100644 --- a/.env.example +++ b/.env.example @@ -35,6 +35,7 @@ H5_PUBLIC_BASE_URL=http://127.0.0.1:5173 # MEMIND_AGENT_CODE_RUNS_ENABLED=0 # VITE_AGENT_CODE_RUNS_ENABLED=0 # VITE_AGENT_CODE_RUNS_AUTODETECT=0 +# VITE_AGENT_PAGE_DATA_DEV_AUTODETECT=0 # Tool Gateway Queue v0(/agent/runs 后台任务队列,默认保守限流)。 # MEMIND_AGENT_RUN_QUEUE_CONCURRENCY=1 diff --git a/chat-intent-router.mjs b/chat-intent-router.mjs index c90989b..0d8da5f 100644 --- a/chat-intent-router.mjs +++ b/chat-intent-router.mjs @@ -3,6 +3,7 @@ import { buildWebNewsSkillPrompt, extractSelectedChatSkillName, hasExplicitChatSkillPrompt, + isPageDataDevIntent, isPageDataIntent, isPageGenerationIntent, isProductCampaignIntent, @@ -930,6 +931,15 @@ export function classifyWithRules({ reason, }, { source: 'rule' }), decisionContext); } + if (includeIntentPatterns && normalized && isPageDataDevIntent(normalized)) { + return finalizeRouterClassification(normalizeClassification({ + route: CHAT_INTENT_ROUTE.AGENT, + confidence: 0.94, + reason: 'Page Data 开发修复(修 bug / 测试回归)', + agent_brief: + '这是 Page Data 开发修复任务:优先根据 verify/报错信息修 public HTML 与 policy,不要重建问卷;建表/bind 缺失时说明需 Goose 或 repair 脚本。', + }, { source: 'rule' }), decisionContext); + } if ( includeIntentPatterns && normalized && diff --git a/chat-intent-router.test.mjs b/chat-intent-router.test.mjs index afb62a3..1402e10 100644 --- a/chat-intent-router.test.mjs +++ b/chat-intent-router.test.mjs @@ -1335,3 +1335,14 @@ test('createChatIntentRouter uses agent fallback for unmatched general questions assert.equal(result.source, 'fallback'); assert.equal(llmCalls.length, 0); }); + +test('classifyWithRules routes page data dev repair before new page-data collect', () => { + const result = classifyWithRules({ + text: '修复问卷 insert 403,columns_not_allowed', + sessionId: null, + sessionMessageCount: 0, + }); + assert.equal(result.route, CHAT_INTENT_ROUTE.AGENT); + assert.match(result.reason, /Page Data 开发修复/); + assert.equal(result.suggested_skill, undefined); +}); diff --git a/chat-skills.mjs b/chat-skills.mjs index 5cacbf0..2f031cd 100644 --- a/chat-skills.mjs +++ b/chat-skills.mjs @@ -71,9 +71,27 @@ function isInteractivePageDataIntent(text) { return INTERACTIVE_PAGE_DATA_SURFACE_PATTERN.test(text) || featureCount >= 2; } +const PAGE_DATA_DEV_INTENT_PATTERNS = [ + /columns_not_allowed/i, + /\bpage[\s-]?data[\s-]?dev\b/i, + /(?:insert|提交|写入).{0,16}(?:失败|403|报错|error)/iu, + /(?:修复|修|排查|debug|fix).{0,24}(?:问卷|page[\s-]?data|数据提交|插入|admin|后台|policy|绑定|bind)/iu, + /(?:问卷|page[\s-]?data|数据页|表单页).{0,24}(?:bug|坏了|不通|失败|有问题)/iu, + /(?:测试|verify).{0,16}(?:没过|失败|不通过)/iu, + /(?:repair|修复).{0,16}(?:bind|绑定|policy|策略)/iu, +]; + +/** Page Data 开发修复(Aider 兜底):修 bug / 测回归,不是新建问卷。 */ +export function isPageDataDevIntent(text) { + const normalized = String(text ?? '').trim(); + if (!normalized) return false; + return PAGE_DATA_DEV_INTENT_PATTERNS.some((pattern) => pattern.test(normalized)); +} + export function isPageDataIntent(text) { const normalized = String(text ?? '').trim(); if (!normalized) return false; + if (isPageDataDevIntent(normalized)) return false; return PAGE_DATA_INTENT_PATTERNS.some((pattern) => pattern.test(normalized)) || isInteractivePageDataIntent(normalized); } diff --git a/chat-skills.test.mjs b/chat-skills.test.mjs index 9df4c3e..53e55a2 100644 --- a/chat-skills.test.mjs +++ b/chat-skills.test.mjs @@ -6,6 +6,7 @@ import { CHAT_SKILL_DEFINITIONS, filterChatSkills, isExcelAnalysisIntent, + isPageDataDevIntent, isPageDataIntent, isPageGenerationIntent, } from './chat-skills.mjs'; @@ -168,3 +169,11 @@ test('buildAutoChatSkillPrefix enables publish for implicit page requests', () = assert.match(prefix, /static-page-publish/); assert.match(prefix, /禁止询问是否还要发布/); }); + +test('isPageDataDevIntent matches repair phrases but not new survey creation', () => { + assert.equal(isPageDataDevIntent('修复问卷 insert 403 columns_not_allowed'), true); + assert.equal(isPageDataDevIntent('page-data-dev 修 policy 白名单'), true); + assert.equal(isPageDataDevIntent('帮我做一个报名问卷'), false); + assert.equal(isPageDataIntent('帮我做一个报名问卷'), true); + assert.equal(isPageDataIntent('修复问卷 insert 403 columns_not_allowed'), false); +}); diff --git a/docs/help-code01.md b/docs/help-code01.md index e16d631..f3d78c9 100644 --- a/docs/help-code01.md +++ b/docs/help-code01.md @@ -177,17 +177,21 @@ MindSpace/{userId}/ ## 6. 开发闭环操作手册 -### 6.1 前置:本地开启 code run(仅 dev) +### 6.1 前置:开启 code run + +> **Phase 1(当前):** env + VITE,适合本机 dev。 +> **Phase 1.5(目标):** memindadm 配置 + `/auth/status` 下发,见 [help-code02.md](./help-code02.md)。 ```bash -# .env 示例(开发机) +# .env 示例(开发机,Phase 1) MEMIND_AGENT_CODE_RUNS_ENABLED=1 MEMIND_TOOL_GATEWAY_ENABLED=1 MEMIND_TOOL_GATEWAY_DEFAULT_EXECUTOR=aider -# 前端 build 时需注入(若走 H5 触发) VITE_AGENT_CODE_RUNS_ENABLED=1 +VITE_AGENT_PAGE_DATA_DEV_AUTODETECT=1 VITE_AGENT_CODE_RUNS_USER_IDS= # 或留空表示全用户(仅 dev) +# 可选:MEMIND_AGENT_CODE_RUN_TASK_TYPES=page_data_dev,h5_chat_code_task,... ``` 还需: @@ -317,9 +321,21 @@ OpenHands 任务类型保持现有 `repo_refactor,multi_file,complex_repo`;** ### Phase 1 — 最小 Memind 改动 -- [ ] 新增 `page_data_dev` taskType -- [ ] `chat-intent-router` 增加 Page Data 修 bug 意图 -- [ ] `agentRunMode.ts` 可选 autodetect 模式(dev only) +- [x] 新增 `page_data_dev` taskType +- [x] `chat-intent-router` 增加 Page Data 修 bug 意图 +- [x] `agentRunMode.ts` 可选 autodetect 模式(dev only,`VITE_AGENT_PAGE_DATA_DEV_AUTODETECT`) + +### Phase 1.5 — memindadm 运行时策略(推荐下一步) + +> 详细设计见 **[help-code02.md](./help-code02.md)**。 + +- [ ] `h5_agent_code_run_config` 表 + `agent-code-run-admin-config.mjs` +- [ ] memindadm API/UI:code run 开关、白名单、taskType、`pageDataDev.autodetect` +- [ ] `/auth/status` 下发 `agentCodeRun`,H5 不再依赖 `VITE_*` rebuild +- [ ] `agent-run-routes.mjs` 从 DB 读策略(env 仅紧急 override) +- [ ] Worker 拓扑(`MEMIND_TOOL_GATEWAY_ENABLED`)继续留 env + +**为何需要 Phase 1.5:** Phase 1 的 env/VITE 适合 dev;要解决「用户经常失败」的运营灰度,必须在 memindadm 按用户/任务类型动态开关,且 H5 需运行时生效。 ### Phase 2 — 自动化 dev loop @@ -337,6 +353,7 @@ OpenHands 任务类型保持现有 `repo_refactor,multi_file,complex_repo`;** | 资源 | 路径 / 命令 | |------|-------------| +| **adm 运行时策略(Phase 1.5)** | `docs/help-code02.md` | | Page Data API 用法 | `docs/page-data-api-usage.md` | | page-data-collect skill | `skills/page-data-collect/SKILL.md` | | 问卷 verify skill | `.claude/skills/memind-page-data-survey-verify/SKILL.md` | diff --git a/docs/help-code02.md b/docs/help-code02.md new file mode 100644 index 0000000..d50c057 --- /dev/null +++ b/docs/help-code02.md @@ -0,0 +1,381 @@ +# Help Code02:Code Run / Page Data Dev 的 memindadm 运行时策略 + +> 文档版本:2026-07-23 +> 状态:方案设计(未实施) +> 前置:[help-code01.md](./help-code01.md)(Aider 补充 Page Data 开发兜底) +> 关联:[memindadm-goose-gateway-design.md](./memindadm-goose-gateway-design.md) + +--- + +## 1. 为什么要做 Phase 1.5 + +Phase 1 用 **env + VITE_ 构建期变量** 控制 code run 与 `page_data_dev` autodetect。这在 dev/staging 可用,但对「用户经常页面失败、插入失败」的运营场景不够: + +| 问题 | env/VITE 的局限 | +|------|----------------| +| 改开关要 rebuild H5 | `VITE_*` 打进 bundle,memindadm 点了不生效 | +| 改开关要 SSH 改 `.env` | 103 Portal / worker 两套 env,易不一致 | +| 无法按用户即时灰度 | 白名单改 env 无审计、无 UI | +| 与现有 adm 能力分裂 | aider 能力已在 `h5_capability_grants`,策略却在 env | + +**目标:** 把 **「谁、哪种 task、是否 page_data_dev autodetect」** 迁入 memindadm;**部署拓扑**(worker 是否 spawn Aider)继续留 env。 + +--- + +## 2. 配置分层(硬边界) + +```mermaid +flowchart TB + subgraph adm ["memindadm 运行时策略(DB)"] + P1[codeRun.enabled] + P2[userAllowlist / roleAllowlist] + P3[taskTypeAllowlist 含 page_data_dev] + P4[pageDataDev.autodetect] + P5[requireValidation] + P6[generalCodeAutodetect] + end + + subgraph env ["部署 env(不改或只读 override)"] + E1[MEMIND_TOOL_GATEWAY_ENABLED] + E2[AIDER_BIN / OPENHANDS_BIN] + E3[worker 并发 / 超时 / guard] + E4[MEMIND_AGENT_RUN_* 紧急 override] + end + + subgraph cap ["已有用户能力(DB)"] + C1[h5_capability_grants: aider/openhands] + C2[h5_llm_executor_bindings] + end + + adm --> Portal[agent-run-routes 校验] + adm --> Auth["/auth/status → H5"] + cap --> Portal + env --> Worker[agent-run-worker] + Portal --> Worker +``` + +### 2.1 进 memindadm 的项 + +| 原 env / VITE | adm 字段 | 说明 | +|---------------|----------|------| +| `MEMIND_AGENT_CODE_RUNS_ENABLED` | `codeRun.enabled` | 后端是否接受 `tool_mode=code` | +| `MEMIND_AGENT_CODE_RUNS_USER_IDS` | `codeRun.userAllowlist` | 空 = 不限制(enabled 时) | +| `MEMIND_AGENT_CODE_RUN_TASK_TYPES` | `codeRun.taskTypeAllowlist` | 含 `page_data_dev`、`h5_chat_code_task` | +| `MEMIND_AGENT_CODE_RUNS_REQUIRE_VALIDATION` | `codeRun.requireValidation` | 是否强制 receipt | +| `VITE_AGENT_CODE_RUNS_ENABLED` | `codeRun.clientEnabled` | H5 是否展示/走 code 路径 | +| `VITE_AGENT_CODE_RUNS_AUTODETECT` | `codeRun.generalAutodetect` | 通用代码语义 autodetect | +| `VITE_AGENT_PAGE_DATA_DEV_AUTODETECT` | `pageDataDev.autodetect` | Page Data 修 bug 专用 autodetect | + +### 2.2 继续留 env 的项 + +| env | 原因 | +|-----|------| +| `MEMIND_TOOL_GATEWAY_ENABLED` | worker 进程级;Portal 与 worker 故意不同值 | +| `MEMIND_TOOL_GATEWAY_DEFAULT_EXECUTOR` | 机器上装的是 aider 还是 openhands | +| `MEMIND_AGENT_RUN_QUEUE_CONCURRENCY` 等 | SLO / guard / LaunchAgent | +| `MEMIND_AGENT_CODE_RUNS_*`(可选) | **紧急 override**:`MEMIND_CODE_RUN_POLICY_SOURCE=env` 时强制 env 优先 | + +--- + +## 3. 数据模型 + +### 3.1 表:`h5_agent_code_run_config` + +与 `h5_skill_runtime_config`、`h5_image_make_admin_config` 同模式:`config_scope='global'` 单行 JSON。 + +```sql +CREATE TABLE IF NOT EXISTS h5_agent_code_run_config ( + config_scope VARCHAR(32) PRIMARY KEY, + config_json JSON NOT NULL, + updated_by CHAR(36) NULL, + updated_at BIGINT NOT NULL +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; +``` + +### 3.2 默认 JSON Schema + +```json +{ + "codeRun": { + "enabled": false, + "clientEnabled": false, + "generalAutodetect": false, + "requireValidation": true, + "userAllowlist": [], + "taskTypeAllowlist": [ + "page_data_dev", + "h5_chat_code_task", + "page_edit_code_task" + ] + }, + "pageDataDev": { + "autodetect": false + }, + "meta": { + "notes": "" + } +} +``` + +### 3.3 生效优先级(与 Memory V2 adm 一致) + +参考 `memory-v2-admin-config.mjs` 的 `FIELD_SPECS + env override` 模式: + +```text +1. 若 MEMIND_CODE_RUN_POLICY_SOURCE=env → 仅读 env(紧急回滚) +2. 否则读 h5_agent_code_run_config(admin-db) +3. 若表为空且 MEMIND_AGENT_CODE_RUNS_ENABLED=1 → source=env-migration(兼容旧部署) +4. 否则 default(全 false,fail closed) +``` + +### 3.4 用户级能力(不重复造表) + +以下 **仍用现有表**,adm「Code Run 策略」页只读展示 + 链到用户能力编辑: + +- `h5_capability_grants`:`aider` / `openhands` +- `h5_llm_executor_bindings`:Aider 模型绑定 +- `h5_user_policies`:`code_delegate_executor`、`code_task_routing` + +**完整放行条件(与 today 相同,只是策略来源改为 DB):** + +```text +codeRun.enabled (adm) + AND user ∈ allowlist(若配置) + AND taskType ∈ taskTypeAllowlist + AND capabilities.aider(用户 grant) + AND executor binding 可用 + AND(若 requireValidation)message 含 validation metadata + AND toolGateway.enabled(env,worker 侧) +``` + +--- + +## 4. 服务模块:`agent-code-run-admin-config.mjs` + +建议新建,API 对齐 `skill-runtime-admin-config.mjs`: + +| 方法 | 用途 | +|------|------| +| `getAdminConfig()` | memindadm 编辑页 | +| `updateAdminConfig(patch, { updatedBy })` | 保存 + 审计 | +| `getRuntimeState()` | adm 运行时预览 | +| `getEffectivePolicy({ userId })` | Portal 校验用 | +| `getPublicClientPolicy({ userId })` | `/auth/status` 下发 H5 | + +### 4.1 `getEffectivePolicy` 返回示例 + +```json +{ + "source": "admin-db", + "updatedAt": 1753276800000, + "enabled": true, + "userAllowed": true, + "taskTypes": ["page_data_dev", "h5_chat_code_task", "page_edit_code_task"], + "requireValidation": true, + "pageDataDevAutodetect": true, + "generalAutodetect": false +} +``` + +### 4.2 `getPublicClientPolicy` 返回示例(按用户过滤后) + +```json +{ + "codeRun": { + "enabled": true, + "pageDataDevAutodetect": true, + "generalAutodetect": false + } +} +``` + +未登录或用户不在 allowlist 时:`codeRun.enabled=false`(或不返回该块)。 + +--- + +## 5. HTTP API + +### 5.1 memindadm(`admin-routes.mjs`) + +| 方法 | 路径 | 说明 | +|------|------|------| +| GET | `/api/admin/agent-code-run/config` | 读配置 + source + updatedBy | +| PUT/PATCH | `/api/admin/agent-code-run/config` | 更新 | +| GET | `/api/admin/agent-code-run/runtime` | 有效策略 + env 覆盖状态 + worker 只读摘要 | + +Worker 摘要可代理现有 `/api/runtime/status` 的 `toolRuntime.codeRunPolicy` 与 `queue`(**只读**,不在 adm 改 worker env)。 + +### 5.2 Portal 用户面 + +**扩展 `GET /auth/status`**(已登录用户): + +```json +{ + "authenticated": true, + "user": { "...": "..." }, + "capabilities": { "aider": true, "...": "..." }, + "skillRuntime": { "...": "..." }, + "agentCodeRun": { + "enabled": true, + "pageDataDevAutodetect": true, + "generalAutodetect": false + } +} +``` + +**扩展 `POST /api/agent/runs` 校验:** 从 `getEffectivePolicy(userId)` 读,不再只读 `process.env.MEMIND_AGENT_CODE_RUNS_*`。 + +### 5.3 只读运维 + +`GET /api/runtime/status` 增加: + +```json +{ + "toolRuntime": { + "codeRunPolicy": { + "source": "admin-db", + "enabled": true, + "userAllowlist": [], + "taskTypeAllowlist": ["page_data_dev", "..."], + "requireValidation": true, + "pageDataDevAutodetect": true, + "envOverrideActive": false + } + } +} +``` + +--- + +## 6. 前端改造(去掉 VITE_ 硬依赖) + +### 6.1 `src/utils/agentRunMode.ts` + +```typescript +// 优先级:runtime policy(/auth/status)> VITE_(dev fallback) + +let runtimePolicy: AgentCodeRunClientPolicy | null = null; + +export function applyAgentCodeRunClientPolicy(policy: AgentCodeRunClientPolicy | null) { + runtimePolicy = policy; +} + +function clientCodeRunsEnabled(): boolean { + if (runtimePolicy?.codeRun?.enabled != null) return runtimePolicy.codeRun.enabled; + return agentCodeRunsEnabled; // VITE fallback +} + +function clientPageDataDevAutodetect(): boolean { + if (runtimePolicy?.codeRun?.pageDataDevAutodetect != null) { + return runtimePolicy.codeRun.pageDataDevAutodetect; + } + return agentPageDataDevAutodetectEnabled; +} +``` + +### 6.2 注入时机 + +在现有 `/auth/status` 加载处(如 `src/api/client.ts` 或 auth hook): + +```typescript +const status = await fetchAuthStatus(); +applyAgentCodeRunClientPolicy(status.agentCodeRun ?? null); +``` + +**效果:** memindadm 打开 `pageDataDev.autodetect` 后,用户刷新页面即生效,**无需 rebuild H5**。 + +--- + +## 7. memindadm UI(Ops 后台) + +建议菜单位置:**系统 / Agent 运行时 → Code Run & Page Data Dev** + +| 控件 | 类型 | 说明 | +|------|------|------| +| Code Run 总开关 | toggle | `codeRun.enabled` | +| H5 客户端启用 | toggle | `codeRun.clientEnabled` | +| Page Data Dev Autodetect | toggle | `pageDataDev.autodetect` | +| 通用 Code Autodetect | toggle | `codeRun.generalAutodetect`(默认关) | +| 强制 Validation | toggle | `codeRun.requireValidation` | +| 用户白名单 | multi-select UUID | 空 = 全部(enabled 时) | +| Task Types | checkbox list | 至少含 `page_data_dev` | +| 当前 Worker 状态 | read-only | 来自 `/runtime/status` | +| Env Override 警告 | banner | `MEMIND_CODE_RUN_POLICY_SOURCE=env` 时显示 | + +**保存时:** 写 `updated_by` + `updated_at`;可选写 admin audit log(与 image-make 一致)。 + +--- + +## 8. 迁移与兼容 + +### 8.1 首次上线 + +1. 建表 `h5_agent_code_run_config`,默认全 `false` +2. 部署 `agent-code-run-admin-config.mjs` + admin API +3. **迁移脚本**(可选):若 env 已开启,import 到 DB 并提示改 `POLICY_SOURCE=admin` + +```bash +node scripts/migrate-agent-code-run-config-from-env.mjs --dry-run +node scripts/migrate-agent-code-run-config-from-env.mjs --apply +``` + +### 8.2 回滚 + +| 场景 | 操作 | +|------|------| +| adm 配错了 | memindadm 关 `codeRun.enabled` | +| 紧急全站关闭 | `MEMIND_CODE_RUN_POLICY_SOURCE=env` + unset `MEMIND_AGENT_CODE_RUNS_ENABLED` | +| worker 异常 | LaunchAgent 停 worker(现有 runbook),与 adm 无关 | + +### 8.3 与 help-code01 Phase 的关系 + +| Phase | 内容 | 配置来源 | +|-------|------|----------| +| Phase 1 ✅ | `page_data_dev` 意图 + autodetect 逻辑 | env/VITE | +| **Phase 1.5** | adm 运行时策略 + `/auth/status` | **DB + adm UI** | +| Phase 2 | verify → Aider dev loop 脚本 | 脚本 + adm 开关 | + +--- + +## 9. 实施清单(Phase 1.5) + +- [ ] `agent-code-run-admin-config.mjs` + 单测 +- [ ] `admin-routes.mjs`:`/agent-code-run/config`、`/runtime` +- [ ] `server.mjs`:`/auth/status` 增加 `agentCodeRun` +- [ ] `agent-run-routes.mjs`:改用 `getEffectivePolicy(userId)` +- [ ] `agentRunMode.ts`:runtime policy 优先于 VITE_ +- [ ] Ops UI 页面(可先做 JSON 编辑,后做表单) +- [ ] `migrate-agent-code-run-config-from-env.mjs` +- [ ] 更新 `docs/agent-run-worker-rollout-runbook.md` 与 `.env.example` +- [ ] verify:`agent-code-run-admin-config.test.mjs` + 扩展 `chat-agent-run-gate.test.mjs` + +--- + +## 10. 安全与审计 + +1. **仅 admin 可写** — 复用 `requireAdmin` +2. **默认 fail closed** — 新环境 adm 配置为空 = 全关 +3. **双闸门保留** — adm 开 + 用户 `aider` grant + worker gateway env +4. **审计字段** — `updated_by`、`updated_at`;重要变更写 admin audit +5. **生产建议** — 先 `userAllowlist` 小范围开 `page_data_dev`,再扩 `generalAutodetect` + +--- + +## 11. 相关文件索引 + +| 类型 | 路径 | +|------|------| +| 方案总览 | `docs/help-code01.md` | +| Goose 网关规划 | `docs/memindadm-goose-gateway-design.md` | +| 可复用 adm 模式 | `skill-runtime-admin-config.mjs` | +| env↔adm 映射先例 | `memory-v2-admin-config.mjs` | +| 后端 code run 门禁 | `agent-run-routes.mjs` | +| 前端 autodetect | `src/utils/agentRunMode.ts` | +| 运行时只读 | `server.mjs` → `runtimeCodeRunPolicyStatus()` | +| Worker runbook | `docs/agent-run-worker-rollout-runbook.md` | + +--- + +## 12. 一句话总结 + +**memindadm 管「策略」(谁、哪种 task、是否 page_data_dev);env 管「部署」(worker 是否 spawn Aider);H5 从 `/auth/status` 读运行时策略,不再依赖 rebuild VITE_。** diff --git a/src/hooks/useTKMindChat.ts b/src/hooks/useTKMindChat.ts index 117077d..09dbcf0 100644 --- a/src/hooks/useTKMindChat.ts +++ b/src/hooks/useTKMindChat.ts @@ -63,7 +63,7 @@ import { CHAT_IMAGE_UPLOAD_MAX_INPUT_BYTES, } from '../utils/imageUpload'; import { buildAbsoluteAssetDownloadUrl } from '../utils/mindspaceCards'; -import { resolveAgentRunOptions } from '../utils/agentRunMode'; +import { resolveAgentRunOptions, resolvePageDataDevTaskType } from '../utils/agentRunMode'; import { buildUserMessage, normalizeConversationMessages, @@ -1472,8 +1472,9 @@ export function useTKMindChat( } try { + const pageDataDevTaskType = resolvePageDataDevTaskType(trimmed); const runOptions = resolveAgentRunOptions(trimmed, { - taskType: 'h5_chat_code_task', + taskType: pageDataDevTaskType ?? 'h5_chat_code_task', userId: userRef.current?.id ?? null, requestId, mindspaceContext: options?.mindspaceContext ?? null, diff --git a/src/utils/agentRunMode.ts b/src/utils/agentRunMode.ts index 6061474..3ab99f6 100644 --- a/src/utils/agentRunMode.ts +++ b/src/utils/agentRunMode.ts @@ -28,6 +28,11 @@ export const agentCodeRunsEnabled = envFlag(import.meta.env.VITE_AGENT_CODE_RUNS export const agentCodeRunsAutodetectEnabled = envFlag( import.meta.env.VITE_AGENT_CODE_RUNS_AUTODETECT, ); +export const agentPageDataDevAutodetectEnabled = envFlag( + import.meta.env.VITE_AGENT_PAGE_DATA_DEV_AUTODETECT, +); + +export const PAGE_DATA_DEV_TASK_TYPE = 'page_data_dev'; function parseUserIdSet(value: unknown): Set { return new Set( @@ -46,6 +51,27 @@ export function agentCodeRunsEnabledForUser(userId?: string | null): boolean { return Boolean(userId && agentCodeRunUserIds.has(userId)); } +const PAGE_DATA_DEV_TASK_PATTERNS = [ + /columns_not_allowed/i, + /\bpage[\s-]?data[\s-]?dev\b/i, + /(?:insert|提交|写入).{0,16}(?:失败|403|报错|error)/iu, + /(?:修复|修|排查|debug|fix).{0,24}(?:问卷|page[\s-]?data|数据提交|插入|admin|后台|policy|绑定|bind)/iu, + /(?:问卷|page[\s-]?data|数据页|表单页).{0,24}(?:bug|坏了|不通|失败|有问题)/iu, + /(?:测试|verify).{0,16}(?:没过|失败|不通过)/iu, + /(?:repair|修复).{0,16}(?:bind|绑定|policy|策略)/iu, +]; + +export function isPageDataDevTaskText(text: string): boolean { + const normalized = String(text ?? '').trim(); + if (!normalized) return false; + return PAGE_DATA_DEV_TASK_PATTERNS.some((pattern) => pattern.test(normalized)); +} + +export function resolvePageDataDevTaskType(text: string): string | null { + if (!agentPageDataDevAutodetectEnabled) return null; + return isPageDataDevTaskText(text) ? PAGE_DATA_DEV_TASK_TYPE : null; +} + const CODE_TASK_PATTERNS = [ /\b(repo|repository|branch|commit|pull request|pr|diff|patch)\b/i, /\b(aider|openhands|codex|codebase|workspace)\b/i, @@ -185,6 +211,22 @@ export function buildAgentRunTaskValidation({ ); } + if (taskType === PAGE_DATA_DEV_TASK_TYPE) { + const taskReceiptPath = `.memind/agent-runs/${safeRequestId}-page-data-dev.json`; + expectedFiles.push({ path: taskReceiptPath, contains: PAGE_DATA_DEV_TASK_TYPE }); + instructions.push( + '', + '[Memind page-data-dev validation]', + `Before finishing this Page Data dev repair task, create or update ${taskReceiptPath}.`, + 'The file must be valid JSON and include:', + `- requestId: ${requestId}`, + `- taskType: ${PAGE_DATA_DEV_TASK_TYPE}`, + '- a brief summary of HTML/policy fixes or why no file change was needed.', + 'Only modify public/*.html and .mindspace/page-data-policies/*.json unless explicitly told otherwise.', + 'Do not recreate datasets or call private_data_execute unless the user pasted schema errors.', + ); + } + return { validation: expectedFiles.length ? { expectedFiles } : null, instruction: instructions.join('\n'), @@ -222,6 +264,7 @@ export function resolveAgentRunOptions( taskType = 'code_task', forceCode = false, allowAutodetect = agentCodeRunsAutodetectEnabled, + allowPageDataDevAutodetect = agentPageDataDevAutodetectEnabled, userId = null, requestId = null, mindspaceContext = null, @@ -230,6 +273,7 @@ export function resolveAgentRunOptions( taskType?: string; forceCode?: boolean; allowAutodetect?: boolean; + allowPageDataDevAutodetect?: boolean; userId?: string | null; requestId?: string | null; mindspaceContext?: MindSpaceChatContext | null; @@ -237,29 +281,37 @@ export function resolveAgentRunOptions( } = {}, ): AgentRunCreateOptions { const normalizedText = String(text ?? '').trim(); + const pageDataDevTaskType = + allowPageDataDevAutodetect && resolvePageDataDevTaskType(normalizedText); + const effectiveTaskType = pageDataDevTaskType ?? taskType; const shouldUseDeepReasoning = - forceCode || DEEP_REASONING_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText)); + forceCode || + Boolean(pageDataDevTaskType) || + DEEP_REASONING_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText)); if (!agentCodeRunsEnabledForUser(userId)) { - return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType } : {}; + return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType: effectiveTaskType } : {}; } - const shouldUseCode = forceCode || (allowAutodetect && CODE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText))); + const shouldUseCode = + forceCode || + Boolean(pageDataDevTaskType) || + (allowAutodetect && CODE_TASK_PATTERNS.some((pattern) => pattern.test(normalizedText))); if (!shouldUseCode) { - return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType } : {}; + return shouldUseDeepReasoning ? { forceDeepReasoning: true, taskType: effectiveTaskType } : {}; } const normalizedRequestId = requestId ?? crypto.randomUUID(); const receipt = buildAgentRunValidationReceipt(normalizedRequestId); const taskValidation = buildAgentRunTaskValidation({ requestId: normalizedRequestId, - taskType, + taskType: effectiveTaskType, text: normalizedText, mindspaceContext, pageEdit, }); return { toolMode: 'code', - taskType, + taskType: effectiveTaskType, forceDeepReasoning: true, validation: mergeAgentRunValidationFiles(receipt.validation, taskValidation.validation), validationInstruction: `${receipt.instruction}${taskValidation.instruction}`,