codebee 0.1.19 → 0.1.20
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +14 -9
- package/app/core/pipeline.py +86 -5
- package/app/ui/app.js +25 -5
- package/app/ui/i18n.js +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,13 @@ README 元数据带回,供老版本在「发现新版本」时展示新版更
|
|
|
6
6
|
|
|
7
7
|
## 未发布
|
|
8
8
|
|
|
9
|
+
## v0.1.20(2026-09-20)
|
|
10
|
+
|
|
11
|
+
- 全类型交付契约:小说、文章、短视频、文档、翻译、调研、演讲、汇报、邮件、技术方案、简历按真实类型路由,并注入各自角色、成品结构和真实性约束
|
|
12
|
+
- token 预算修复:默认关闭压缩与续会话路径也累计真实 usage;压缩单次/重试防重复记账,预算熔断恢复有效
|
|
13
|
+
- 任务类型管理修复:15 种流程全部显示成本预估;类型切换不再串 rubric;编辑预置流程不再误建副本;direct 正确显示为直连引擎
|
|
14
|
+
- 全类型竞品雷达与经验沉淀:关键词库校正为 130 组并按小时轮换,新增当日七专项报告与后续深挖队列
|
|
15
|
+
|
|
9
16
|
- 取消即强杀:手动点「取消运行」直接强制终止任务——运行立刻翻「已取消」终态(还挂「运行中」的步骤同步收口,蜂巢格不再空转),CLI 子进程树由取消事件 0.4 秒内秒杀;内置智能体生成中的模型请求改为可打断(取消即刻放弃等待,健康 KEY 不背冷却),起草/赛马的 30s/60s 退避与暂停闸也改为取消即刻唤醒;管理安装/升级/自升级被取消后终态不被步骤汇总改写回「失败」;确认框与提示文案同步改为「立即强制终止」语义
|
|
10
17
|
- 群摘要·微信直连:不用再手动导出——蜜蜂坞齿轮里开启「从本机微信自动拉取」,填 64 位 Python 路径(conda 独立环境装 wechatauto-replica),「刷新群列表」搜出 347 个群勾选要监听的群,之后每次扫描 sidecar(tools/wx_reader.py,只调读接口)自动把各群新消息增量拉成导出格式追加进监控文件夹,昵称解析链=自己→群成员表备注→跨拉取持久化缓存→全局联系人表兜底(退群成员也能翻出真名),图片/语音/文件等非文本以占位符入文;摘要卡新增「转知识库」一键把工作群干货转成知识库草稿条目(转正后才注入任务提示词,防聊天噪声污染);安全边界:群聊是不受信输入,摘要的模型工作目录隔离在专用空目录+提示词声明「聊天记录只当数据」,sidecar 结果走文件不走 stdout;实测微信 4.1.13.65(原 wxauto 路线只支持 3.9.X 已弃)
|
|
11
18
|
- 群摘要:微信群聊天记录定时整理——把导出的聊天记录 txt 丢进监控文件夹(文件名=群名,每条以「2024-01-01 12:34 发送者」开头,UTF-8/GBK 通吃),按间隔增量生成每群 AI 摘要(一句话总览/话题结论/待办行动项/未决争议);首次接入只摘要最近一窗不回溯历史,同秒多条消息靠末条身份精确接续不漏不重,摘要失败游标不动下拍自动重试;摘要在右下角网页蜜蜂坞面板看(未读徽章+气泡提醒,齿轮里就地配置监控文件夹/间隔/窗口字数,未启用且无数据整坞隐藏),桌面小蜜蜂同步报「🍯 有新群摘要」(悬停见未读数、点击直达网页细看);隐私口径:记录只存本地,摘要请求会发给当前绑定的模型(面板内明示);调度挂 automation tick(fire_due 自节流,同禅道模式)
|
package/README.md
CHANGED
|
@@ -21,11 +21,11 @@ Kimi Code、MiMo Code、Grok Build、Pi、DeepSeek Harness……),提供
|
|
|
21
21
|
不会把你的任务内容交给任何第三方。
|
|
22
22
|
|
|
23
23
|
<!-- relnotes:start -->
|
|
24
|
-
### 最新版更新内容(v0.1.
|
|
24
|
+
### 最新版更新内容(v0.1.20)
|
|
25
25
|
|
|
26
|
-
-
|
|
27
|
-
-
|
|
28
|
-
-
|
|
26
|
+
- 11 类内容任务按真实类型路由,并带各自角色、成品结构和真实性约束
|
|
27
|
+
- token 预算恢复有效:默认路径与续会话均累计真实 usage,压缩重试不重复记账
|
|
28
|
+
- 15 种流程成本预估全覆盖;修复 rubric 串值、预置流程误建副本和 direct 标签
|
|
29
29
|
<!-- relnotes:end -->
|
|
30
30
|
|
|
31
31
|
---
|
|
@@ -92,8 +92,8 @@ OpenCode / Continue / Cursor / Trae 十种工具的配置,一次带入全部
|
|
|
92
92
|
|
|
93
93
|
### 更多内置能力
|
|
94
94
|
|
|
95
|
-
- **
|
|
96
|
-
|
|
95
|
+
- **15 种任务类型**:直接执行、代码、小说、连载小说、自媒体文章、短视频脚本、
|
|
96
|
+
文档、翻译、扫榜选材、调研报告、演讲稿、工作汇报、商务邮件、技术方案、简历;全部支持自定义流程
|
|
97
97
|
(引擎 / 评审维度 / 阈值 / 轮数 / 提示词覆盖);
|
|
98
98
|
- **质量闸门与裁决**:评审失败≠0 分照过、降级大纲不开写、超时但已落盘的稿件照常
|
|
99
99
|
送评审;需要人拍板的事项进「待裁决」队列(徽章 + 提示音),`tools/bee.py`
|
|
@@ -199,7 +199,8 @@ python tools\bee.py discard <task> :: 否决——删除任务分支(需输
|
|
|
199
199
|
## 任务类型与自定义流程
|
|
200
200
|
|
|
201
201
|
主表单的「类型」下拉即流程列表,点「管理」可新增/编辑自定义流程(引擎、评审维度、
|
|
202
|
-
阈值、轮数、产出文件名、起草/评审提示词覆盖)。内置
|
|
202
|
+
阈值、轮数、产出文件名、起草/评审提示词覆盖)。内置 15 种;禅道工单是自动转成
|
|
203
|
+
`code` 修复任务的集成入口,不另占菜单类型:
|
|
203
204
|
|
|
204
205
|
| 类型 | 引擎 | 流程 |
|
|
205
206
|
|---|---|---|
|
|
@@ -207,13 +208,17 @@ python tools\bee.py discard <task> :: 否决——删除任务分支(需输
|
|
|
207
208
|
| 💻 代码 | code | 实现 → 验证 → 跨厂商评审 → 自动修复/换将 |
|
|
208
209
|
| 📖 小说 | review | 起草 → 多维评审(情节/人物/文笔/节奏/吸引力)→ 修订 → 发布门禁 |
|
|
209
210
|
| 📚 连载小说 | review | 大纲 → 逐章起草/评审/修订 → 全局一致性评审 → 合并成书(断点续跑/继续连载) |
|
|
211
|
+
| 📰 自媒体文章 | review | 平台化标题/开头/结构 → 多维评审 → 修订 → 发布门禁 |
|
|
212
|
+
| 🎬 短视频脚本 | review | 画面 + 口播 + 字幕/音效 → 多维评审 → 修订 → 发布门禁 |
|
|
210
213
|
| 📝 文档 | review | 起草 → 多维评审(准确性/结构/表达/实用)→ 修订 → 门禁 |
|
|
211
214
|
| 🌐 翻译 | review | 起草 → 多维评审(忠实度/流畅度/术语/风格)→ 修订 → 门禁 |
|
|
215
|
+
| 📈 扫榜选材 | direct | 抓七猫 + 番茄公开榜单 → 跨平台题材/人设/切入点洞察 |
|
|
212
216
|
| 🔍 调研报告 | review | 起草 → 多维评审(全面性/深度/论据/结论)→ 修订 → 门禁 |
|
|
213
217
|
| 🎤 演讲稿 | review | 起草 → 多维评审(主题/结构/感染力/语言)→ 修订 → 门禁 |
|
|
214
|
-
|
|
|
218
|
+
| 📅 工作汇报 / ✉️ 商务邮件 / 📋 技术方案 / 📄 简历 | review | 各自专属角色、交付约束与评审维度 |
|
|
215
219
|
|
|
216
|
-
|
|
220
|
+
三种引擎:
|
|
221
|
+
- **direct 引擎**:单智能体直接执行目标,保留会话上下文,适合问答、探索和无需评审链的快捷任务;
|
|
217
222
|
- **code 引擎**:实现者在工作目录直接改代码 → 执行验证命令(确定性结果)→ 跨厂商评审
|
|
218
223
|
(结构化 JSON)→ 不通过则自动修复循环(至多 2 轮)→ 仍失败自动换将全量重实现;
|
|
219
224
|
- **review 引擎**:作者起草产出文件 → 多个评审智能体按维度独立打分 + 提问题 →
|
package/app/core/pipeline.py
CHANGED
|
@@ -502,6 +502,7 @@ def _spawn_step(session_run_id, role, agent, prompt, workdir, readonly, ev,
|
|
|
502
502
|
"error_code": ErrorCode.ENV_BLOCK, "sid": "",
|
|
503
503
|
"raw": {"exit_code": None}, "kind": agent.get("kind", "generic"),
|
|
504
504
|
"model": agent.get("model")}
|
|
505
|
+
usage_recorded = False
|
|
505
506
|
if _compaction_enabled() and not resume:
|
|
506
507
|
# Phase 2(1D):撑爆 → 压缩 → 守门重试;同时把 usage 累进 token_meter(1C)
|
|
507
508
|
session = _get_session(session_run_id)
|
|
@@ -511,6 +512,7 @@ def _spawn_step(session_run_id, role, agent, prompt, workdir, readonly, ev,
|
|
|
511
512
|
images=images, require_tools=require_tools)
|
|
512
513
|
|
|
513
514
|
def _call(p, **kw):
|
|
515
|
+
nonlocal usage_recorded
|
|
514
516
|
# 模型可见即已记录(§1A 不变量):入参/出参先落 session 日志
|
|
515
517
|
session.append("user_message", {"content": p, "role": role},
|
|
516
518
|
turn_id=str(step["n"]))
|
|
@@ -523,6 +525,7 @@ def _spawn_step(session_run_id, role, agent, prompt, workdir, readonly, ev,
|
|
|
523
525
|
from .token_meter import token_meter
|
|
524
526
|
token_meter.accumulate(session_run_id, r.get("usage"),
|
|
525
527
|
model=r.get("model") or "")
|
|
528
|
+
usage_recorded = True
|
|
526
529
|
except Exception:
|
|
527
530
|
pass
|
|
528
531
|
return r
|
|
@@ -534,6 +537,15 @@ def _spawn_step(session_run_id, role, agent, prompt, workdir, readonly, ev,
|
|
|
534
537
|
res = runner.run_agent(agent, prompt, workdir=workdir, readonly=readonly,
|
|
535
538
|
timeout=timeout, cancel_event=ev, log_path=str(log_abs),
|
|
536
539
|
resume=resume, images=images, require_tools=require_tools)
|
|
540
|
+
# 默认关闭压缩和 resume 都走直通分支,也必须把真实 usage 送进预算表;否则
|
|
541
|
+
# 下一步永远看到 used=0,max_tokens_per_run 只是一个无效设置。
|
|
542
|
+
if not usage_recorded:
|
|
543
|
+
try:
|
|
544
|
+
from .token_meter import token_meter
|
|
545
|
+
token_meter.accumulate(session_run_id, res.get("usage"),
|
|
546
|
+
model=res.get("model") or "")
|
|
547
|
+
except Exception:
|
|
548
|
+
pass
|
|
537
549
|
return res
|
|
538
550
|
|
|
539
551
|
|
|
@@ -1410,7 +1422,7 @@ def _pick_reviewer_legacy(agents, impl):
|
|
|
1410
1422
|
|
|
1411
1423
|
# ---------------------------------------------------------------- review 引擎(小说/文档/翻译/调研…通用)
|
|
1412
1424
|
|
|
1413
|
-
NOVEL_DRAFT_PROMPT = """
|
|
1425
|
+
NOVEL_DRAFT_PROMPT = """你是__ROLE__。请在当前工作目录中撰写/修订稿件文件:`__FILE__`(直接写入该文件)。文件必须以 UTF-8 编码保存(PowerShell 写文件显式加 -Encoding UTF8,禁止依赖默认编码)。
|
|
1414
1426
|
|
|
1415
1427
|
## 写作任务
|
|
1416
1428
|
__GOAL__
|
|
@@ -1426,6 +1438,69 @@ __RUBRIC__
|
|
|
1426
1438
|
- 只修改 `__FILE__` 这一个文件;保持 Markdown 结构。
|
|
1427
1439
|
- 完成后用 3 句话说明本轮写了什么。"""
|
|
1428
1440
|
|
|
1441
|
+
# review 引擎共用执行骨架,但交付物不能只靠 rubric 猜格式。每个内置类型给出
|
|
1442
|
+
# 最小成品契约,起草和修订都注入;自定义流程继续使用通用回退,避免强加结构。
|
|
1443
|
+
CONTENT_DELIVERY_CONTRACTS = {
|
|
1444
|
+
"novel": ("小说作者", [
|
|
1445
|
+
"遵守用户给定的题材、篇幅、视角和风格;未给出的核心设定不要擅自扩张。",
|
|
1446
|
+
"用场景、行动和对话推进冲突,人物动机与前后因果保持一致。",
|
|
1447
|
+
]),
|
|
1448
|
+
"article": ("平台内容主编", [
|
|
1449
|
+
"标题、开头钩子、正文层级和结尾行动建议要适配目标平台与读者。",
|
|
1450
|
+
"事实、数据和引语不得编造;缺少来源时明确标注待核实。",
|
|
1451
|
+
]),
|
|
1452
|
+
"video_script": ("短视频编导", [
|
|
1453
|
+
"按镜头或时间段写清画面、口播、字幕/音效与预计时长,前 3 秒给出钩子。",
|
|
1454
|
+
"每个画面都应可实际拍摄或制作,结尾给出自然的互动或转化动作。",
|
|
1455
|
+
]),
|
|
1456
|
+
"doc": ("技术文档编辑", [
|
|
1457
|
+
"先明确读者、目的和前置条件,再按可执行步骤组织正文。",
|
|
1458
|
+
"命令、参数、示例与限制必须一致;无法确认的内容明确标注。",
|
|
1459
|
+
]),
|
|
1460
|
+
"translation": ("专业译者与审校", [
|
|
1461
|
+
"忠实保留原文含义、语气、数字、专名、占位符、链接和 Markdown 结构,不增译或漏译。",
|
|
1462
|
+
"术语译法全文一致;歧义或无法确认的专名保留原文并加简短译注。",
|
|
1463
|
+
]),
|
|
1464
|
+
"research": ("研究分析师", [
|
|
1465
|
+
"围绕决策问题组织证据、对比、结论与可执行建议,避免资料堆砌。",
|
|
1466
|
+
"结论必须能回溯到来源;证据不足处明确写出不确定性和验证办法。",
|
|
1467
|
+
]),
|
|
1468
|
+
"speech": ("演讲撰稿人", [
|
|
1469
|
+
"按场合、听众和时长控制篇幅,使用适合现场说出的短句与自然转场。",
|
|
1470
|
+
"开场建立关系,主体围绕一个核心信息展开,结尾给出清晰收束或号召。",
|
|
1471
|
+
]),
|
|
1472
|
+
"weekly_report": ("业务汇报顾问", [
|
|
1473
|
+
"按成果与影响、关键数据、问题阻塞、下步行动(负责人/时间)组织内容。",
|
|
1474
|
+
"只使用用户提供或可核验的数据;缺失数字保留待补项,不虚构业绩。",
|
|
1475
|
+
]),
|
|
1476
|
+
"email": ("商务沟通顾问", [
|
|
1477
|
+
"包含明确主题、称呼、来意、必要背景、请求/下一步和得体落款。",
|
|
1478
|
+
"语气匹配双方关系;日期、承诺、附件与联系人不得凭空补造。",
|
|
1479
|
+
]),
|
|
1480
|
+
"tech_proposal": ("解决方案架构师", [
|
|
1481
|
+
"覆盖现状与目标、约束、候选方案对比、推荐架构、实施阶段、风险与回滚、验收指标。",
|
|
1482
|
+
"区分已知事实、假设和待验证项;成本收益给出计算口径而非虚构数字。",
|
|
1483
|
+
]),
|
|
1484
|
+
"resume": ("招聘与简历顾问", [
|
|
1485
|
+
"围绕目标岗位提炼真实经历,用行动、结果和技能关键词表达岗位匹配度。",
|
|
1486
|
+
"不得虚构经历、公司、学历、指标或技术栈;缺少量化数据时保留待补提示。",
|
|
1487
|
+
]),
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
def _content_role(task):
|
|
1492
|
+
"""返回内置类型的专业角色;自定义 review 流程使用中性角色。"""
|
|
1493
|
+
spec = CONTENT_DELIVERY_CONTRACTS.get(str(task.get("type") or ""))
|
|
1494
|
+
return spec[0] if spec else "内容交付专家"
|
|
1495
|
+
|
|
1496
|
+
|
|
1497
|
+
def _content_contract(task):
|
|
1498
|
+
"""把类型成品约束渲染为稳定提示块;无内置契约时不额外注入。"""
|
|
1499
|
+
spec = CONTENT_DELIVERY_CONTRACTS.get(str(task.get("type") or ""))
|
|
1500
|
+
if not spec:
|
|
1501
|
+
return ""
|
|
1502
|
+
return "\n\n## 本类型交付约束\n" + "\n".join("- " + item for item in spec[1])
|
|
1503
|
+
|
|
1429
1504
|
# 调研报告类稿件的追加要求(借鉴 gpt-researcher 迭代深研):有网络/读文件工具时
|
|
1430
1505
|
# 多源交叉验证,单源结论降权——调研的可信度来自证据链而非文采
|
|
1431
1506
|
RESEARCH_APPENDIX = """
|
|
@@ -1438,7 +1513,7 @@ RESEARCH_APPENDIX = """
|
|
|
1438
1513
|
- 结构硬性要求:报告第一段必须是「**核心结论**」三行以内的要点摘要(结论先行),
|
|
1439
1514
|
之后才展开分层论证 → 风险与局限(说明哪些结论证据不足)。"""
|
|
1440
1515
|
|
|
1441
|
-
NOVEL_REVISE_PROMPT = """
|
|
1516
|
+
NOVEL_REVISE_PROMPT = """你是__ROLE__。请根据下方汇总评审意见修订稿件文件:`__FILE__`(直接写入该文件)。文件必须以 UTF-8 编码保存(PowerShell 写文件显式加 -Encoding UTF8,禁止依赖默认编码)。
|
|
1442
1517
|
|
|
1443
1518
|
## 原始写作任务
|
|
1444
1519
|
__GOAL__
|
|
@@ -2689,13 +2764,15 @@ def _run_content_review(run, task, agents, ev, stats, mode):
|
|
|
2689
2764
|
impl, _ = _pick_implementer(agents, task.get("implementer"))
|
|
2690
2765
|
critics = _pick_critics_manual(agents, task)
|
|
2691
2766
|
else:
|
|
2692
|
-
|
|
2693
|
-
|
|
2767
|
+
task_type = task.get("type") or "novel"
|
|
2768
|
+
impl, route["author"] = router.pick(agents, "implement", task_type, stats)
|
|
2769
|
+
critics, route["critics"] = router.pick_critics(agents, task_type, stats, impl=impl)
|
|
2694
2770
|
if impl is None:
|
|
2695
2771
|
store.update_run(run_id, status="failed", error="没有可用智能体", ended_at=_now())
|
|
2696
2772
|
return
|
|
2697
2773
|
if resume_ctx is not None and mode == "auto":
|
|
2698
|
-
critics, route["critics"] = router.pick_critics(
|
|
2774
|
+
critics, route["critics"] = router.pick_critics(
|
|
2775
|
+
agents, task.get("type") or "novel", stats, impl=impl)
|
|
2699
2776
|
|
|
2700
2777
|
# ---- 规划(小说为模板计划)
|
|
2701
2778
|
_wait_gate(run_id, ev)
|
|
@@ -2739,6 +2816,7 @@ def _run_content_review(run, task, agents, ev, stats, mode):
|
|
|
2739
2816
|
|
|
2740
2817
|
def _draft_prompt_for(vfile):
|
|
2741
2818
|
p = (_tpl(task, "draft_prompt", NOVEL_DRAFT_PROMPT).replace("__FILE__", vfile)
|
|
2819
|
+
.replace("__ROLE__", _content_role(task))
|
|
2742
2820
|
.replace("__GOAL__", task["goal"])
|
|
2743
2821
|
.replace("__CONTEXT__", task.get("context") or "(无)")
|
|
2744
2822
|
.replace("__RUBRIC__", "、".join(dims) if dims else "(按流程默认维度)"))
|
|
@@ -2748,6 +2826,7 @@ def _run_content_review(run, task, agents, ev, stats, mode):
|
|
|
2748
2826
|
if is_research:
|
|
2749
2827
|
# 调研报告追加证据链要求(gpt-researcher 借鉴)
|
|
2750
2828
|
p += RESEARCH_APPENDIX
|
|
2829
|
+
p += _content_contract(task)
|
|
2751
2830
|
return p
|
|
2752
2831
|
|
|
2753
2832
|
best_of = max(1, min(3, int(task.get("best_of") or 1)))
|
|
@@ -2847,8 +2926,10 @@ def _run_content_review(run, task, agents, ev, stats, mode):
|
|
|
2847
2926
|
pass
|
|
2848
2927
|
else:
|
|
2849
2928
|
prompt = (NOVEL_REVISE_PROMPT.replace("__FILE__", ms_name)
|
|
2929
|
+
.replace("__ROLE__", _content_role(task))
|
|
2850
2930
|
.replace("__GOAL__", task["goal"])
|
|
2851
2931
|
.replace("__CRITIQUE__", "\n".join(crit_lines)))
|
|
2932
|
+
prompt += _content_contract(task)
|
|
2852
2933
|
_run_step(run_id, "revise-r%d" % r, modelhub.bind_agent(impl, difficulty), prompt,
|
|
2853
2934
|
workdir, readonly=False, ev=ev,
|
|
2854
2935
|
resume=resume_ctx["session"] if resume_ctx else None)
|
package/app/ui/app.js
CHANGED
|
@@ -13,6 +13,12 @@ async function loadFlows() {
|
|
|
13
13
|
renderTypeOptions();
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
+
function invalidateFlowRubricDraft(flowId) {
|
|
17
|
+
if (S.flowRubricDrafts) delete S.flowRubricDrafts[flowId];
|
|
18
|
+
const rubric = $("f-rubric");
|
|
19
|
+
if (rubric && rubric.dataset.flowId === flowId) rubric.dataset.flowId = "";
|
|
20
|
+
}
|
|
21
|
+
|
|
16
22
|
function flowById(id) {
|
|
17
23
|
return (S.flows || []).find((f) => f.id === id) || null;
|
|
18
24
|
}
|
|
@@ -78,7 +84,7 @@ function renderTypeMenu() {
|
|
|
78
84
|
* 拉到手后就地更新菜单项——菜单开着也不重建 DOM,不打断滚动/悬停 */
|
|
79
85
|
async function fetchFlowEstimates() {
|
|
80
86
|
if (!S.flows) return;
|
|
81
|
-
const targets = S.flows
|
|
87
|
+
const targets = S.flows;
|
|
82
88
|
const results = await Promise.all(targets.map(async (f) => {
|
|
83
89
|
try {
|
|
84
90
|
const d = await api("/api/usage/estimate?type=" + encodeURIComponent(f.id) + "&days=90");
|
|
@@ -157,8 +163,18 @@ function onTypeChange() {
|
|
|
157
163
|
if (flow.rounds) $("f-rounds").value = flow.rounds;
|
|
158
164
|
if (flow.threshold) $("f-threshold").value = flow.threshold;
|
|
159
165
|
if (flow.best_of) $("f-bestof").value = flow.best_of;
|
|
160
|
-
const
|
|
161
|
-
|
|
166
|
+
const rubric = $("f-rubric");
|
|
167
|
+
const previousFlow = rubric.dataset.flowId || "";
|
|
168
|
+
if (!S.flowRubricDrafts) S.flowRubricDrafts = {};
|
|
169
|
+
if (previousFlow && previousFlow !== flow.id) {
|
|
170
|
+
S.flowRubricDrafts[previousFlow] = rubric.value;
|
|
171
|
+
}
|
|
172
|
+
if (previousFlow !== flow.id) {
|
|
173
|
+
rubric.value = Object.prototype.hasOwnProperty.call(S.flowRubricDrafts, flow.id)
|
|
174
|
+
? S.flowRubricDrafts[flow.id]
|
|
175
|
+
: (flow.rubric || []).join(", ");
|
|
176
|
+
rubric.dataset.flowId = flow.id;
|
|
177
|
+
}
|
|
162
178
|
// 连载参数预填(用户可改/可清空 = 单稿件模式)
|
|
163
179
|
if (flow.serial) {
|
|
164
180
|
if (!$("f-chapters").value) $("f-chapters").value = flow.serial.chapters || "";
|
|
@@ -7417,7 +7433,8 @@ function openFlowsManager() {
|
|
|
7417
7433
|
const rows = flows.map((f) =>
|
|
7418
7434
|
'<div class="item"><div class="t"><span class="name">' + flowIconHtml(f) + " " + esc(t(f.name)) +
|
|
7419
7435
|
'</span><span class="tag">' + esc(f.id) + "</span>" +
|
|
7420
|
-
'<span class="tag">' + (f.engine === "code" ? t("代码引擎")
|
|
7436
|
+
'<span class="tag">' + (f.engine === "code" ? t("代码引擎")
|
|
7437
|
+
: f.engine === "direct" ? t("直连引擎") : t("评审引擎")) + "</span>" +
|
|
7421
7438
|
(f.serial ? '<span class="tag">' + t("连载 ") + f.serial.chapters + t(" 章") + "</span>" : "") +
|
|
7422
7439
|
(f.builtin ? '<span class="tag ok">' + t("预置") + '</span>' : '<span class="tag">' + t("自定义") + '</span>') +
|
|
7423
7440
|
(f.edited ? '<span class="tag">' + t("已改") + '</span>' : "") +
|
|
@@ -7440,6 +7457,7 @@ async function flowReset(fid) {
|
|
|
7440
7457
|
if (!await uiConfirm(t("把「") + fid + t("」恢复为内置默认配置?"), { ok: t("恢复") })) return;
|
|
7441
7458
|
try { await api("/api/flows/" + encodeURIComponent(fid) + "/reset", { method: "POST" }); }
|
|
7442
7459
|
catch (e) { toast(t("恢复失败:") + e.message, true); return; }
|
|
7460
|
+
invalidateFlowRubricDraft(fid);
|
|
7443
7461
|
await loadFlows();
|
|
7444
7462
|
openFlowsManager();
|
|
7445
7463
|
toast(t("已恢复默认"));
|
|
@@ -7456,7 +7474,8 @@ function flowForm(fid) {
|
|
|
7456
7474
|
'<div class="grid-2">' +
|
|
7457
7475
|
'<div class="field"><label>' + t("名称 ") + '<span class="req">*</span></label><input id="fl-name" value="' +
|
|
7458
7476
|
esc(f ? f.name : "") + '" placeholder="' + t("例:播客脚本") + '"></div>' +
|
|
7459
|
-
'<div class="field"><label>' + t("引擎 ") + '<span class="req">*</span></label><select id="fl-engine"' +
|
|
7477
|
+
'<div class="field"><label>' + t("引擎 ") + '<span class="req">*</span></label><select id="fl-engine" data-fid="' +
|
|
7478
|
+
esc(f ? f.id : "") + '"' +
|
|
7460
7479
|
(engLocked ? " disabled" : "") + '>' +
|
|
7461
7480
|
'<option value="review"' + (engine === "review" ? " selected" : "") + '>' + t("评审引擎(起草 → 多维评审 → 修订 → 门禁)") + '</option>' +
|
|
7462
7481
|
'<option value="code"' + (engine === "code" ? " selected" : "") + '>' + t("代码引擎(实现 → 验证 → 评审 → 修复)") + '</option>' +
|
|
@@ -7530,6 +7549,7 @@ async function saveFlow() {
|
|
|
7530
7549
|
await api("/api/flows", { method: "POST", body: JSON.stringify(payload) });
|
|
7531
7550
|
} catch (e) { toast(t("保存失败:") + e.message, true); return; }
|
|
7532
7551
|
closeModal();
|
|
7552
|
+
invalidateFlowRubricDraft(id);
|
|
7533
7553
|
await loadFlows();
|
|
7534
7554
|
openFlowsManager();
|
|
7535
7555
|
toast(t("流程已保存"));
|
package/app/ui/i18n.js
CHANGED
|
@@ -761,6 +761,7 @@
|
|
|
761
761
|
"手动:自行指定实现者与评审组": "Manual: pick implementer and reviewers yourself",
|
|
762
762
|
"代码引擎": "Code engine",
|
|
763
763
|
"评审引擎": "Review engine",
|
|
764
|
+
"直连引擎": "Direct engine",
|
|
764
765
|
"实现 → 验证 → 评审": "Implement → verify → review",
|
|
765
766
|
"起草 → 多维评审 → 门禁": "Draft → multi-dim review → gate",
|
|
766
767
|
"大纲 → 逐章起草评审 → 合并(可续跑)": "Outline → per-chapter draft & review → merge (resumable)",
|
package/package.json
CHANGED