dsh-aris-panel 0.1.3 → 0.1.5

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/dsh/client.js CHANGED
@@ -222,52 +222,52 @@ function inferStage(ov) {
222
222
  }
223
223
 
224
224
  const WORKFLOW_CHIPS = [
225
- { num: 'W1', label: 'Idea Discovery 想法发现', entry: 'idea-discovery', desc: '文献→头脑风暴→新颖性→GPU试跑→排名' },
226
- { num: 'W1.5', label: 'Experiment Bridge 实验桥接', entry: 'experiment-bridge', desc: '计划→实现→审代码→部署→收集' },
227
- { num: 'W2', label: 'Auto Review Loop 自动审稿', entry: 'auto-review-loop', desc: '审→修→再审直到≥6分或4' },
228
- { num: 'W3', label: 'Paper Writing 论文写作', entry: 'paper-writing', desc: '叙事→大纲→图表→LaTeX→PDF→外部审' },
229
- { num: 'W4', label: 'Rebuttal 回复审稿', entry: 'rebuttal', desc: '解析意见→策略→起草→压力测试' },
230
- { num: 'W5', label: 'Resubmit 换会重投', entry: 'resubmit-pipeline', desc: '硬约束重投,不加实验不改bib' },
231
- { num: 'W6', label: 'Conference Talk 会议演讲', entry: 'paper-talk', desc: '论文→Beamer+PPTX+讲稿' },
225
+ { num: 'W1', label: 'Idea Discovery 想法发现', entry: 'idea-discovery', when: '开始新研究,有方向但还没想法时', desc: '完整想法发现管线:多源文献调研 → 头脑风暴 8-12 个具体想法 → 可行性与算力过滤 → 深度新颖性验证 + 魔鬼代言人审 → top 2-3 想法并行 GPU 试跑(30min-2h) → 按实验信号排名 → 精炼出问题锚定提案 → claim 驱动的实验路线图。产出 IDEA_REPORT.md 排名报告 + FINAL_PROPOSAL + EXPERIMENT_PLAN。' },
226
+ { num: 'W1.5', label: 'Experiment Bridge 实验桥接', entry: 'experiment-bridge', when: '已有实验计划,需要落地实现代码并跑出结果时', desc: '把实验计划变成运行结果:解析 EXPERIMENT_PLAN.md → 实现实验代码(带 argparse/日志/种子) → GPT-5.6-Sol 审代码抓逻辑 bug → 最小实验 sanity check → 部署 GPU 跑全套 → 收集结果更新实验追踪。' },
227
+ { num: 'W2', label: 'Auto Review Loop 自动审稿循环', entry: 'auto-review-loop', when: '论文/结果需要迭代打磨、想挂机自动改进时', desc: '无人值守审稿循环:外部审稿人深审论文/claims/实验指出弱点 → 自动修复(重写章节/加 baseline/补实验) → 重新评估,循环直到 ≥6/10 或 4 轮上限;>4 GPU 小时的实验自动跳过并标记人工跟进,断点可从 REVIEW_STATE.json 恢复。' },
228
+ { num: 'W3', label: 'Paper Writing 论文写作', entry: 'paper-writing', when: '叙事报告就绪、准备把研究写成论文时', desc: '从叙事报告到可投稿 PDF:claims-evidence 矩阵 + 章节规划 → 数据驱动图表 → 逐节 LaTeX 编译 PDF 修错 + 页数检查 → 两轮外部内容审 + 最终格式检查。' },
229
+ { num: 'W4', label: 'Rebuttal 回复审稿', entry: 'rebuttal', when: '收到会议/期刊审稿意见时', desc: '安全回复审稿意见:解析意见拆成 issue 卡片 → 全局策略 + 逐审稿人优先级 → 可选自动补实验 → 起草回复 → 6 项安全检查(覆盖/溯源/承诺/语气/一致性/字数) → GPT 压力测试 → 输出限字版 + 富文本版。' },
230
+ { num: 'W5', label: 'Resubmit 换会重投', entry: 'resubmit-pipeline', when: '论文需在硬约束下换到另一个会议重投时', desc: '换会议重投:硬约束(不加实验/不改 bib/不改框架/不覆盖旧稿) → 物理隔离 + 5 层匿名检查 → 白名单微编辑 → kill-argument 对抗门 → RESUBMIT_REPORT 七态账本。' },
231
+ { num: 'W6', label: 'Conference Talk 会议演讲', entry: 'paper-talk', when: '论文录用后准备会议演讲时', desc: '从论文到会议演讲:演讲大纲 → Beamer + PPTX + 讲稿 + Q&A 准备 → 逐页打磨 → claim/citation/匿名审计门。' },
232
232
  ]
233
233
  const AUDIT_CHIPS = [
234
- { label: '实验诚实度审计', skill: 'experiment-audit', desc: 'eval代码是否诚实(假GT/自归一化/幻影结果)' },
235
- { label: '结果→声明判定', skill: 'result-to-claim', desc: '结果是否科学地支持声明' },
236
- { label: '论文数字核对', skill: 'paper-claim-audit', desc: '论文报告的数字是否与原始结果一致' },
237
- { label: '引用真实性审计', skill: 'citation-audit', desc: '每个引用是否存在且上下文正确' },
238
- { label: '最强拒稿模拟', skill: 'kill-argument', desc: '200词最强拒稿+独立裁决' },
234
+ { label: '实验诚实度审计', skill: 'experiment-audit', when: '实验完成后、写 claim 之前', desc: '审计 eval 代码是否诚实:检查是否有假 ground truth、分数自归一化、幻影结果、范围不足——结果可信前必跑。' },
235
+ { label: '结果→声明判定', skill: 'result-to-claim', when: '实验完成、写论文之前', desc: '判定结果支持什么:Codex 评估实验对既定 claims 的支持度,路由下一步(转向/补充/确认),实验完成后、写论文前跑。' },
236
+ { label: '论文数字核对', skill: 'paper-claim-audit', when: '论文初稿完成后、投稿前', desc: '核对论文数字:零上下文审稿人核对每个数字/比较/范围声明是否与原始结果一致,防止确认偏差。' },
237
+ { label: '引用真实性审计', skill: 'citation-audit', when: '投稿前', desc: '审计每条引用:文献必须真实存在、归属正确、上下文确实支持——抓幻觉作者/错年份/假 venue/错上下文。' },
238
+ { label: '最强拒稿模拟', skill: 'kill-argument', when: '投稿前 / 冲顶会前', desc: '拒稿压力测试:新审稿人写 200 词最强拒稿信,第二名独立审稿人逐点辩护并暴露仍未解决的关键问题。' },
239
239
  ]
240
240
  const MEMORY_CHIPS = [
241
- { label: 'research-wiki 知识库', skill: 'research-wiki', desc: '论文/想法/实验/声明跨会话沉淀' },
242
- { label: 'wiki 补全', skill: 'wiki-enrich', desc: '填充paper页面的TODO' },
243
- { label: 'meta-optimize 自我优化', skill: 'meta-optimize', desc: '分析使用日志改进ARIS自身' },
241
+ { label: 'research-wiki 知识库', skill: 'research-wiki', when: '项目初始化时初始化一次,之后持续使用', desc: '跨会话研究记忆:论文/想法/实验/声明四类实体 + 图谱边;失败想法成为反重复记忆,下次构思自动避开踩过的坑。' },
242
+ { label: 'wiki 补全', skill: 'wiki-enrich', when: '批量导入论文后', desc: '补全 research-wiki 的论文页 TODO 区,把批量导入留下的骨架填充成完整摘要。' },
243
+ { label: 'meta-optimize 自我优化', skill: 'meta-optimize', when: '使用一段时间、积累了运行日志后', desc: 'ARIS 自我优化:分析 .aris/meta 使用日志,提议改进 SKILL.md 与工作流默认值,改动需审稿门控 + 人工批准。' },
244
244
  ]
245
-
246
- function WbChipRow(props) {
247
- return h('div', { className: 'wb-chip-row' },
248
- props.items.map(function (it) {
249
- return h('button', {
250
- key: it.skill || it.entry,
251
- className: 'wb-pano-chip',
252
- onClick: function () { props.onPick(it.skill || it.entry) },
253
- title: it.desc || '',
254
- },
255
- it.num ? h('span', { className: 'wb-pano-num' }, it.num) : null,
256
- h('span', null, it.label))
257
- }))
245
+ function WbPanoCard(props) {
246
+ const it = props.item
247
+ const entry = it.skill || it.entry
248
+ return h('button', {
249
+ className: 'wb-pano-card',
250
+ onClick: function () { props.onPick(entry) },
251
+ title: it.desc || '',
252
+ },
253
+ h('div', { className: 'wb-pano-card-head' },
254
+ it.num ? h('span', { className: 'wb-pano-num' }, it.num) : null,
255
+ h('span', { className: 'wb-pano-card-title' }, it.label)),
256
+ it.when ? h('div', { className: 'wb-pano-card-when' }, '何时调用: ' + it.when) : null,
257
+ it.desc ? h('div', { className: 'wb-pano-card-desc' }, it.desc) : null,
258
+ h('div', { className: 'wb-pano-card-entry' }, '入口: ' + entry))
258
259
  }
259
260
 
260
261
  function WbPanorama(props) {
261
262
  return h('div', { className: 'wb-panorama' },
262
263
  h('div', { className: 'wb-guide-title' }, '功能全景:7 个工作流 + 审计链 + 记忆(点击选项卡填充到下方技能触发)'),
263
- h('div', { className: 'wb-pano-group' }, h('span', { className: 'wb-pano-group-label' }, '工作流'),
264
- h(WbChipRow, { items: WORKFLOW_CHIPS, onPick: props.onPick })),
265
- h('div', { className: 'wb-pano-group' }, h('span', { className: 'wb-pano-group-label' }, '审计链'),
266
- h(WbChipRow, { items: AUDIT_CHIPS, onPick: props.onPick })),
267
- h('div', { className: 'wb-pano-group' }, h('span', { className: 'wb-pano-group-label' }, '记忆'),
268
- h(WbChipRow, { items: MEMORY_CHIPS, onPick: props.onPick })))
264
+ h('div', { className: 'wb-pano-group' }, h('div', { className: 'wb-pano-group-label' }, '工作流'),
265
+ h('div', { className: 'wb-pano-grid' }, WORKFLOW_CHIPS.map(function (it) { return h(WbPanoCard, { key: it.entry, item: it, onPick: props.onPick }) }))),
266
+ h('div', { className: 'wb-pano-group' }, h('div', { className: 'wb-pano-group-label' }, '审计链'),
267
+ h('div', { className: 'wb-pano-grid' }, AUDIT_CHIPS.map(function (it) { return h(WbPanoCard, { key: it.skill, item: it, onPick: props.onPick }) }))),
268
+ h('div', { className: 'wb-pano-group' }, h('div', { className: 'wb-pano-group-label' }, '记忆'),
269
+ h('div', { className: 'wb-pano-grid' }, MEMORY_CHIPS.map(function (it) { return h(WbPanoCard, { key: it.skill, item: it, onPick: props.onPick }) }))))
269
270
  }
270
-
271
271
  function WbGuide(props) {
272
272
  const stage = props.stage
273
273
  const onPick = props.onPick
@@ -288,6 +288,137 @@ function WbGuide(props) {
288
288
  cur ? h('div', { className: 'wb-guide-next' }, '下一步建议:' + cur.next) : null)
289
289
  }
290
290
 
291
+
292
+ const SKILL_ZH = {
293
+ "ablation-planner": "消融实验规划(主结果通过后规划消融)",
294
+ "academic-research-suite": "学术研究套件(深度调研/写作/审稿)",
295
+ "agent-browser": "浏览器自动化",
296
+ "alphaxiv": "论文速读(AlphaXiv 摘要)",
297
+ "analyze-results": "实验结果分析统计",
298
+ "arxiv": "arXiv 论文搜索下载",
299
+ "auto-paper-improvement-loop": "论文自动改进循环(2轮外部审+修复)",
300
+ "auto-review-loop": "自动审稿循环(审→修→再审)",
301
+ "auto-review-loop-llm": "LLM 版自动审稿循环",
302
+ "auto-review-loop-minimax": "MiniMax 版自动审稿循环",
303
+ "baseline": "基线管理(复现/对比)",
304
+ "canvas-design": "画布设计(海报/艺术图)",
305
+ "citation-audit": "引用审计(防幻觉引用)",
306
+ "claims-drafting": "专利权利要求撰写",
307
+ "claude-api": "Claude API 应用开发",
308
+ "comm-lit-review": "通信领域文献综述",
309
+ "context-window-management": "上下文窗口管理",
310
+ "deepxiv": "DeepXiv 分层论文检索",
311
+ "document-skills": "办公文档处理套件",
312
+ "docx": "Word 文档处理",
313
+ "dse-loop": "设计空间探索循环(EDA)",
314
+ "embodiment-description": "专利实施例撰写",
315
+ "exa-search": "Exa AI 网络搜索",
316
+ "experiment-audit": "实验诚实度审计",
317
+ "experiment-bridge": "实验桥接(计划→代码→结果)",
318
+ "experiment-plan": "实验路线图规划",
319
+ "experiment-queue": "实验队列调度(多任务)",
320
+ "feishu-notify": "飞书通知",
321
+ "figure-description": "专利附图说明",
322
+ "figure-spec": "确定性矢量图(FigureSpec→SVG)",
323
+ "find-skills": "技能查找与安装",
324
+ "firecrawl": "网页抓取(Firecrawl)",
325
+ "formula-derivation": "公式推导",
326
+ "gemini-search": "Gemini 论文搜索",
327
+ "gpt-image-2": "GPT Image 2 图像生成",
328
+ "grant-proposal": "基金申请书撰写",
329
+ "guizang-ppt-skill": "网页 PPT 生成",
330
+ "idea": "想法假设分析",
331
+ "idea-creator": "头脑风暴生成想法",
332
+ "idea-discovery": "想法发现全流程(W1)",
333
+ "idea-discovery-robot": "机器人领域想法发现",
334
+ "integrity-forensics": "论文诚信取证审计",
335
+ "interview-cheatsheet": "面试速查表生成",
336
+ "invention-structuring": "发明披露结构化",
337
+ "j-space": "内心工作空间(长任务推理)",
338
+ "jurisdiction-format": "专利国别格式转换",
339
+ "kill-argument": "最强拒稿模拟",
340
+ "latex-paper-en": "英文 LaTeX 论文助手",
341
+ "latex-thesis-zh": "中文 LaTeX 学位论文助手",
342
+ "mano-cua": "图形界面自动化(VLA)",
343
+ "markitdown": "文档转 Markdown",
344
+ "mermaid-diagram": "Mermaid 图表生成",
345
+ "meta-apply": "优化补丁落地(需人工批准)",
346
+ "meta-optimize": "ARIS 自我优化",
347
+ "monitor-experiment": "实验监控",
348
+ "novelty-check": "新颖性查新",
349
+ "openalex": "OpenAlex 学术检索",
350
+ "overleaf-sync": "Overleaf 双向同步",
351
+ "paper-claim-audit": "论文数字核对",
352
+ "paper-compile": "论文编译(LaTeX→PDF)",
353
+ "paper-figure": "论文图表生成",
354
+ "paper-illustration": "AI 插图生成(Gemini)",
355
+ "paper-illustration-image2": "AI 插图生成(GPT-image)",
356
+ "paper-plan": "论文大纲规划",
357
+ "paper-poster": "会议海报(已废弃)",
358
+ "paper-poster-html": "HTML 会议海报",
359
+ "paper-slides": "会议幻灯片生成",
360
+ "paper-talk": "会议演讲全流程",
361
+ "paper-write": "论文写作(逐节 LaTeX)",
362
+ "paper-writing": "论文写作全流程(W3)",
363
+ "patent-novelty-check": "专利查新",
364
+ "patent-pipeline": "专利申请全流程",
365
+ "patent-review": "专利审查意见",
366
+ "pixel-art": "像素画 SVG",
367
+ "ppt-master": "多格式 PPT 生成",
368
+ "prior-art-search": "现有技术检索",
369
+ "proof-checker": "数学证明验证",
370
+ "proof-orchestrator": "定理证明项目管理",
371
+ "proof-writer": "数学证明写作",
372
+ "qzcli": "启智平台作业管理",
373
+ "rebuttal": "审稿意见回复(W4)",
374
+ "render-html": "Markdown 产物渲染 HTML",
375
+ "research-lit": "文献调研",
376
+ "research-pipeline": "研究全流程(W1→W3)",
377
+ "research-refine": "研究方案精炼",
378
+ "research-refine-pipeline": "方案精炼+实验规划串联",
379
+ "research-review": "研究深度审稿",
380
+ "research-wiki": "研究知识库",
381
+ "resubmit-pipeline": "换会重投(W5)",
382
+ "result-to-claim": "结果→声明判定",
383
+ "run-experiment": "跑实验(GPU)",
384
+ "scientific-agent-skills-suite": "科研技能套件入口",
385
+ "semantic-scholar": "Semantic Scholar 检索",
386
+ "serverless-modal": "Modal 无服务器 GPU",
387
+ "slides-polish": "幻灯片逐页打磨",
388
+ "specification-writing": "专利说明书撰写",
389
+ "system-profile": "系统性能分析",
390
+ "training-check": "训练健康检查",
391
+ "vast-gpu": "Vast.ai 租卡",
392
+ "web-access": "联网访问",
393
+ "web-debug-search": "开发者问题检索",
394
+ "wiki-enrich": "wiki 补全",
395
+ "writing-systems-papers": "系统论文写作指南",
396
+ "xlsx": "Excel 表格处理",
397
+ }
398
+
399
+ function WbProgress(props) {
400
+ const ov = props.overview
401
+ const stage = props.stage
402
+ const pipeline = ov && ov.pipeline
403
+ const phases = pipeline && pipeline.phases ? pipeline.phases : []
404
+ const doneCount = phases.filter(function (p) { return p.status === 'accepted' || p.status === 'done' }).length
405
+ const active = phases.some(function (p) { return p.status === 'running' || p.status === 'pending' })
406
+ const pct = phases.length > 0 ? Math.round(doneCount / phases.length * 100) : 0
407
+ const cur = FLOW_STAGES.find(function (s) { return s.id === stage })
408
+ return h('div', { className: 'wb-progress' },
409
+ h('div', { className: 'wb-guide-title' }, '执行进度' + (active ? '(工作流执行中,每 10 秒自动刷新)' : '')),
410
+ pipeline ? h('div', null,
411
+ wbKey('工作流', (pipeline.runId || '(unnamed run)') + ' · executor: ' + (pipeline.executor || 'unknown')),
412
+ h('div', { className: 'wb-bar' }, h('div', { className: 'wb-bar-fill', style: { width: pct + '%' } })),
413
+ wbKey('阶段进度', doneCount + '/' + phases.length + ' 完成 (' + pct + '%)'),
414
+ h('ul', { className: 'wb-list' }, phases.map(function (p) {
415
+ return h('li', { key: p.phase },
416
+ h('span', { className: 'wb-phase-' + p.status }, p.status),
417
+ ' ', h('code', null, p.phase),
418
+ p.artifact ? ' → ' + p.artifact : '')
419
+ }))) : wbNote('尚无执行中的工作流 — 从上方「功能全景」触发一个工作流后,这里显示实时进度'),
420
+ cur ? h('div', { className: 'wb-guide-next' }, '当前阶段(我的5步): ' + cur.title + ' → 下一步: ' + cur.next) : null)
421
+ }
291
422
  function WbTrigger(props) {
292
423
  const connection = props.connection
293
424
  const sessionId = props.sessionId
@@ -296,12 +427,21 @@ function WbTrigger(props) {
296
427
  const args = props.args
297
428
  const [status, setStatus] = React.useState(undefined)
298
429
  const skillError = props.skillError
430
+ const [effort, setEffort] = React.useState('balanced')
431
+ const [assurance, setAssurance] = React.useState('draft')
432
+ const [difficulty, setDifficulty] = React.useState('medium')
433
+ const [extra, setExtra] = React.useState('')
434
+ const [venue, setVenue] = React.useState('')
435
+ const [gpu, setGpu] = React.useState('')
436
+ const [reviewer, setReviewer] = React.useState('auto')
437
+ const [humanCheckpoint, setHumanCheckpoint] = React.useState(false)
438
+ const [autoProceed, setAutoProceed] = React.useState(true)
299
439
  const [busy, setBusy] = React.useState(false)
300
440
 
301
441
  const doLaunch = function () {
302
442
  if (!skill) { setStatus('请先选择技能'); return }
303
443
  setBusy(true)
304
- connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: skill, args: args })
444
+ connection.rpc.call(WB_CHANNEL, 'launch', { sessionId: sessionId, skill: skill, args: args, autoProceed: autoProceed, effort: effort, assurance: assurance, difficulty: difficulty, venue: venue, gpu: gpu, reviewer: reviewer, humanCheckpoint: humanCheckpoint, extra: extra })
305
445
  .then(function (res) {
306
446
  if (res.ok) {
307
447
  setStatus(res.value.injected ? '已注入会话(' + res.value.method + '),执行已唤醒,请到会话窗口查看进度' : '未能注入,请复制提示词手动发送:' + res.value.prompt)
@@ -312,22 +452,81 @@ function WbTrigger(props) {
312
452
  .catch(function (e) { setStatus('触发失败:' + String(e)) })
313
453
  .finally(function () { setBusy(false) })
314
454
  }
455
+ const buildCmd = function () {
456
+ const params = []
457
+ if (autoProceed === false) params.push('AUTO_PROCEED: false')
458
+ if (effort !== 'balanced') params.push('effort: ' + effort)
459
+ if (assurance !== 'draft') params.push('assurance: ' + assurance)
460
+ if (difficulty !== 'medium') params.push('difficulty: ' + difficulty)
461
+ if (humanCheckpoint === true) params.push('human checkpoint: true')
462
+ if (venue) params.push('venue: ' + venue)
463
+ if (gpu) params.push('gpu: ' + gpu)
464
+ if (reviewer !== 'auto') params.push('reviewer: ' + reviewer)
465
+ const ex = extra.trim()
466
+ if (ex) params.push(ex)
467
+ const arg = args.trim()
468
+ return '/' + skill + (arg ? ' "' + arg + '"' : '') + (params.length > 0 ? ' — ' + params.join(', ') : '')
469
+ }
315
470
  const doCopy = function () {
316
471
  if (!skill) { setStatus('请先选择技能'); return }
317
- const text = '请使用 ARIS 技能 ' + WB_TICK + skill + WB_TICK + ' 完成对应工作流。' + (args ? ' 参数/方向:' + args : '')
318
- navigator.clipboard.writeText(text).then(function () { setStatus('提示词已复制,粘贴到会话发送即可') }).catch(function () { setStatus('复制失败,请手动复制:' + text) })
472
+ const text = buildCmd()
473
+ navigator.clipboard.writeText(text).then(function () { setStatus('命令已复制,粘贴到会话发送即可') }).catch(function () { setStatus('复制失败,请手动复制:' + text) })
319
474
  }
320
475
 
321
476
  const selectOptions = [h('option', { value: '', key: '' }, '— 选择 ARIS 技能 —')]
322
477
  ;(skills || []).forEach(function (s) {
323
- selectOptions.push(h('option', { key: s.name, value: s.name }, s.name + (s.description ? ' — ' + s.description.slice(0, 60) : '')))
478
+ selectOptions.push(h('option', { key: s.name, value: s.name }, s.name + ' — ' + (SKILL_ZH[s.name] || s.description || '').slice(0, 60)))
324
479
  })
325
480
 
326
481
  return h('div', { className: 'wb-trigger' },
327
482
  h('div', { className: 'wb-guide-title' }, '技能触发'),
328
483
  h('div', { className: 'wb-trigger-row' },
329
484
  h('select', { className: 'wb-skill-select', value: skill, onChange: function (e) { props.onSkill(e.target.value) } }, selectOptions),
330
- h('input', { className: 'wb-args', placeholder: '参数 / 研究方向(可选)', value: args, onChange: function (e) { props.onArgs(e.target.value) } })),
485
+ h('input', { className: 'wb-args', placeholder: '研究方向 (arguments):例如 discrete diffusion LMs 的 factorized gap', value: args, onChange: function (e) { props.onArgs(e.target.value) } })),
486
+ h('div', { className: 'wb-trigger-row wb-param-row' },
487
+ h('label', { className: 'wb-param-label' }, '无人值守: '),
488
+ h('select', { className: 'wb-param-select', value: autoProceed ? 'on' : 'off', onChange: function (e) { setAutoProceed(e.target.value === 'on') } },
489
+ h('option', { value: 'on' }, 'AUTO_PROCEED: true (自动继续)'),
490
+ h('option', { value: 'off' }, 'AUTO_PROCEED: false (每步确认)')),
491
+ h('label', { className: 'wb-param-label' }, '深度: '),
492
+ h('select', { className: 'wb-param-select', value: effort, onChange: function (e) { setEffort(e.target.value) } },
493
+ h('option', { value: 'lite' }, 'lite (轻量)'),
494
+ h('option', { value: 'balanced' }, 'balanced (默认)'),
495
+ h('option', { value: 'max' }, 'max (深度)'),
496
+ h('option', { value: 'beast' }, 'beast (极限)')),
497
+ h('label', { className: 'wb-param-label' }, '保证级: '),
498
+ h('select', { className: 'wb-param-select', value: assurance, onChange: function (e) { setAssurance(e.target.value) } },
499
+ h('option', { value: 'draft' }, 'draft (草稿)'),
500
+ h('option', { value: 'polished' }, 'polished (精修)'),
501
+ h('option', { value: 'conference-ready' }, 'conference-ready'),
502
+ h('option', { value: 'submission' }, 'submission (投稿级全审计)')),
503
+ h('label', { className: 'wb-param-label' }, '审稿强度: '),
504
+ h('select', { className: 'wb-param-select', value: difficulty, onChange: function (e) { setDifficulty(e.target.value) } },
505
+ h('option', { value: 'medium' }, 'medium (默认)'),
506
+ h('option', { value: 'hard' }, 'hard (更强)'),
507
+ h('option', { value: 'nightmare' }, 'nightmare (极限)'))),
508
+ h('div', { className: 'wb-trigger-row wb-param-row' },
509
+ h('label', { className: 'wb-param-label' }, '会议: '),
510
+ h('select', { className: 'wb-param-select', value: venue, onChange: function (e) { setVenue(e.target.value) } },
511
+ h('option', { value: '' }, '(默认)'),
512
+ h('option', { value: 'ICLR' }, 'ICLR'), h('option', { value: 'NeurIPS' }, 'NeurIPS'),
513
+ h('option', { value: 'ICML' }, 'ICML'), h('option', { value: 'CVPR' }, 'CVPR'),
514
+ h('option', { value: 'AAAI' }, 'AAAI'), h('option', { value: 'ACL' }, 'ACL')),
515
+ h('label', { className: 'wb-param-label' }, 'GPU: '),
516
+ h('select', { className: 'wb-param-select', value: gpu, onChange: function (e) { setGpu(e.target.value) } },
517
+ h('option', { value: '' }, '(默认)'), h('option', { value: 'local' }, 'local'),
518
+ h('option', { value: 'remote' }, 'remote'), h('option', { value: 'vast' }, 'vast'),
519
+ h('option', { value: 'modal' }, 'modal')),
520
+ h('label', { className: 'wb-param-label' }, '审稿路由: '),
521
+ h('select', { className: 'wb-param-select', value: reviewer, onChange: function (e) { setReviewer(e.target.value) } },
522
+ h('option', { value: 'auto' }, 'auto (默认)'), h('option', { value: 'codex' }, 'codex'),
523
+ h('option', { value: 'oracle-pro' }, 'oracle-pro'), h('option', { value: 'manual' }, 'manual')),
524
+ h('label', { className: 'wb-param-label' }, '人工确认: '),
525
+ h('select', { className: 'wb-param-select', value: humanCheckpoint ? 'true' : 'false', onChange: function (e) { setHumanCheckpoint(e.target.value === 'true') } },
526
+ h('option', { value: 'false' }, 'false (默认)'), h('option', { value: 'true' }, 'true (暂停批准)'))),
527
+ h('div', { className: 'wb-trigger-row' },
528
+ h('input', { className: 'wb-extra', placeholder: '其他参数 key: value, key2: value2(可选,如 sources: arxiv, web / gpu: vast)', value: extra, onChange: function (e) { setExtra(e.target.value) } })),
529
+ skill ? h('div', { className: 'wb-cmd-preview' }, 'ARIS 命令: ' + buildCmd()) : null,
331
530
  h('div', { className: 'wb-trigger-row' },
332
531
  h('button', { className: 'wb-btn', onClick: doLaunch, disabled: busy }, busy ? '触发中…' : '触发'),
333
532
  h('button', { className: 'wb-btn wb-btn-copy', onClick: doCopy }, '复制提示词')),
@@ -353,6 +552,48 @@ function WbArtifacts(props) {
353
552
  ' ', h('code', null, item.path))
354
553
  }))
355
554
  }
555
+ function renderAudits(audits) {
556
+ if (!audits) return wbNote('(no data)')
557
+ const ORDER = [
558
+ ['experiment-audit', '实验诚实度审计'],
559
+ ['result-to-claim', '结果→声明判定'],
560
+ ['paper-claim-audit', '论文数字核对'],
561
+ ['citation-audit', '引用真实性审计'],
562
+ ['kill-argument', '最强拒稿模拟'],
563
+ ]
564
+ const keys = Object.keys(audits)
565
+ const done = keys.map(function (k) { return k.replace(/\.(md|json)$/, '') })
566
+ const next = ORDER.find(function (o) { return done.indexOf(o[0]) === -1 })
567
+ const children = []
568
+ children.push(h('div', { className: 'wb-audit-order' }, '建议顺序: ' + ORDER.map(function (o) { return o[1] }).join(' → ')))
569
+ if (keys.length === 0) {
570
+ children.push(wbNote('尚无审计产物'))
571
+ } else {
572
+ children.push(h('ul', { className: 'wb-list' }, keys.map(function (k) {
573
+ const a = audits[k]
574
+ const v = a.verdict || '?'
575
+ const cls = 'wb-verdict wb-verdict-' + v.toLowerCase()
576
+ return h('li', { key: k },
577
+ h('span', { className: cls }, v),
578
+ ' ', h('code', null, k))
579
+ })))
580
+ }
581
+ if (next) children.push(wbNote('下一步建议: 先跑 ' + next[0] + '(' + next[1] + ')'))
582
+ return h('div', null, children)
583
+ }
584
+
585
+ function renderArtifactChain(artifacts) {
586
+ if (!artifacts) return wbNote('(no data)')
587
+ const CHAIN = ['IDEA_REPORT.md', 'refine-logs/FINAL_PROPOSAL.md', 'refine-logs/EXPERIMENT_PLAN.md', 'EXPERIMENT_LOG.md', 'NARRATIVE_REPORT.md', 'paper/main.pdf']
588
+ const has = function (pp) { const e = artifacts[pp]; return !!e && e.exists }
589
+ return h('ul', { className: 'wb-list' }, CHAIN.map(function (pp, i) {
590
+ return h('li', { key: pp },
591
+ h('span', { className: has(pp) ? 'wb-dot-ok' : 'wb-dot-missing' }, has(pp) ? '●' : '○'),
592
+ ' ', h('code', null, pp),
593
+ i < CHAIN.length - 1 ? ' →' : '')
594
+ }))
595
+ }
596
+
356
597
  function renderIdeaBoard(ideas) {
357
598
  if (!ideas) return wbNote('(no data)')
358
599
  const out = []
@@ -440,6 +681,8 @@ function ArisWorkbenchView(props) {
440
681
  const [skillError, setSkillError] = React.useState(undefined)
441
682
  const [skill, setSkill] = React.useState('')
442
683
  const [args, setArgs] = React.useState('')
684
+ const [permPreset, setPermPreset] = React.useState('read-only')
685
+ const [permStatus, setPermStatus] = React.useState(undefined)
443
686
 
444
687
  React.useEffect(function () {
445
688
  let live = true
@@ -475,9 +718,9 @@ function ArisWorkbenchView(props) {
475
718
 
476
719
  React.useEffect(function () { fetchSkills() }, [fetchSkills])
477
720
 
478
- const load = React.useCallback(function () {
721
+ const load = React.useCallback(function (silent) {
479
722
  if (!sessionId) return
480
- setBusy(true)
723
+ if (!silent) setBusy(true)
481
724
  const timeout = new Promise(function (_, reject) {
482
725
  setTimeout(function () { reject(new Error('RPC 超时(6s)')) }, 6000)
483
726
  })
@@ -487,6 +730,7 @@ function ArisWorkbenchView(props) {
487
730
  connection.rpc.call(WB_CHANNEL, 'wiki', { sessionId: sessionId }),
488
731
  connection.rpc.call(WB_CHANNEL, 'ideas', { sessionId: sessionId }),
489
732
  connection.rpc.call(WB_CHANNEL, 'experiments', { sessionId: sessionId }),
733
+ connection.rpc.call(WB_CHANNEL, 'audits', { sessionId: sessionId }),
490
734
  ]),
491
735
  timeout,
492
736
  ]).then(function (results) {
@@ -496,16 +740,26 @@ function ArisWorkbenchView(props) {
496
740
  setData(undefined)
497
741
  return
498
742
  }
499
- setData({ overview: results[0].value, wiki: results[1].value, ideas: results[2].value, experiments: results[3].value })
743
+ setData({ overview: results[0].value, wiki: results[1].value, ideas: results[2].value, experiments: results[3].value, audits: results[4].value })
500
744
  setError(undefined)
501
745
  }).catch(function (cause) {
502
746
  setError(String(cause))
503
747
  setData(undefined)
504
- }).finally(function () { setBusy(false) })
748
+ }).finally(function () { if (!silent) setBusy(false) })
505
749
  }, [connection, sessionId])
506
750
 
507
751
  React.useEffect(function () { load() }, [load])
508
752
 
753
+ // 自动轮询:工作流执行中每 10 秒静默刷新进度
754
+ React.useEffect(function () {
755
+ const timer = setInterval(function () {
756
+ const p = data && data.overview && data.overview.pipeline
757
+ const active = p && p.phases && p.phases.some(function (x) { return x.status === 'running' || x.status === 'pending' })
758
+ if (active) load(true)
759
+ }, 10000)
760
+ return function () { clearInterval(timer) }
761
+ }, [data, load])
762
+
509
763
  const ov = data && data.overview
510
764
  const wiki = data && data.wiki
511
765
  const ideas = data && data.ideas
@@ -530,8 +784,22 @@ function ArisWorkbenchView(props) {
530
784
  if (error) children.push(h('div', { className: 'wb-error', key: 'err' }, error))
531
785
  if (sessionId === undefined && workspaces === undefined) children.push(wbNote('正在连接工作区…'))
532
786
  if (sessionId !== undefined) {
787
+
788
+ children.push(h('div', { className: 'wb-picker', key: 'pctl' },
789
+ h('label', null, '权限: '),
790
+ h('select', { className: 'wb-perm-select', value: permPreset, onChange: function (e) {
791
+ const v = e.target.value
792
+ setPermPreset(v)
793
+ connection.rpc.call(WB_CHANNEL, 'permission', { sessionId: sessionId, preset: v }).then(function (res) {
794
+ setPermStatus(res.ok ? '权限已设置: ' + v : (res.error ? res.error.message : '设置失败'))
795
+ }).catch(function (err) { setPermStatus('设置失败: ' + String(err)) })
796
+ } },
797
+ h('option', { value: 'read-only' }, 'Plan (只读)'),
798
+ h('option', { value: 'workspace-write' }, 'Accept Edits (可改文件)'),
799
+ h('option', { value: 'danger-full-access' }, 'Full Access (全自动)')),
800
+ permStatus ? h('span', { className: 'wb-perm-status' }, permStatus) : null))
533
801
  children.push(h(WbPanorama, { key: 'pano', onPick: function (name) { setSkill(name) } }))
534
- children.push(h(WbGuide, { key: 'guide', stage: stage, onPick: function (name) { setSkill(name) } }))
802
+ children.push(h(WbProgress, { key: 'progress', overview: ov, stage: stage }))
535
803
  children.push(h(WbTrigger, { key: 'trigger', connection: connection, sessionId: sessionId, skills: skills, skill: skill, args: args, skillError: skillError, onSkill: setSkill, onArgs: setArgs }))
536
804
  }
537
805
  if (sessionId !== undefined && data === undefined && !error) children.push(wbNote('加载中…'))
@@ -551,6 +819,10 @@ function ArisWorkbenchView(props) {
551
819
  wbKey('Last verdict', ov.state.last_verdict || '(n/a)')) : wbNote('尚无 review-stage/REVIEW_STATE.json(未跑过 auto-review-loop)'),
552
820
  h(WbArtifacts, (ov.artifacts && Object.keys(ov.artifacts).map(function (k) { return { path: k, exists: ov.artifacts[k].exists } })) || [])))
553
821
  children.push(h('div', { className: 'wb-grid2', key: 'status' }, [
822
+ h(WbSection, { title: '产物链', key: 'chain' },
823
+ renderArtifactChain(data.overview && data.overview.artifacts)),
824
+ h(WbSection, { title: '审计链状态', key: 'aud' },
825
+ renderAudits(data.audits)),
554
826
  h(WbSection, { title: 'Idea 看板', key: 'id' }, renderIdeaBoard(ideas)),
555
827
  h(WbSection, { title: '实验', key: 'ex' }, renderExperiments(exps)),
556
828
  h(WbSection, { title: 'Wiki 图谱', key: 'wk' }, renderWiki(wiki)),
@@ -622,7 +894,44 @@ const WB_CSS = `.wb-overlay { position: fixed; inset: 0; z-index: 9999; backgrou
622
894
  .wb-chip-row { display: flex; flex-wrap: wrap; gap: 6px; flex: 1; }
623
895
  .wb-pano-chip { background: #ffffff; color: #1f1f1f; border: 1px solid #d0d0d0; border-radius: 14px; padding: 3px 12px; font-size: 12px; cursor: pointer; display: inline-flex; align-items: center; gap: 5px; }
624
896
  .wb-pano-chip:hover { border-color: #007acc; color: #007acc; background: #eef4fb; }
625
- .wb-pano-num { font-weight: 800; color: #007acc; font-size: 11px; }`
897
+ .wb-pano-num { font-weight: 800; color: #007acc; font-size: 11px; }.wb-pano-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 8px; flex: 1; }
898
+ .wb-pano-card { text-align: left; background: #ffffff; border: 1px solid #d0d0d0; border-radius: 8px; padding: 10px 12px; cursor: pointer; display: flex; flex-direction: column; gap: 4px; }
899
+ .wb-pano-card:hover { border-color: #007acc; background: #eef4fb; }
900
+ .wb-pano-card-head { display: flex; align-items: baseline; gap: 6px; }
901
+ .wb-pano-num { font-weight: 800; color: #007acc; font-size: 12px; }
902
+ .wb-pano-card-title { font-size: 13px; font-weight: 600; }
903
+ .wb-pano-card-desc { font-size: 11px; color: #1f1f1f; line-height: 1.45; }
904
+ .wb-pano-card-entry { font-size: 11px; color: #0066b8; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }.wb-perm-select { background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; padding: 3px 8px; font-size: 12px; margin-right: 12px; }
905
+ .wb-perm-status { font-size: 11px; color: #107c10; margin-left: 6px; }
906
+
907
+ .wb-pano-card { min-height: 158px; padding: 12px 14px; }
908
+ .wb-pano-card-desc { line-height: 1.55; }
909
+ .wb-param-row { align-items: center; }
910
+ .wb-param-label { font-size: 11px; opacity: .7; }
911
+ .wb-param-select { background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; padding: 3px 6px; font-size: 12px; }
912
+ .wb-verdict { display: inline-block; border-radius: 8px; padding: 1px 8px; font-size: 10px; font-weight: 700; margin-right: 6px; }
913
+ .wb-verdict-pass { background: #dcffe4; color: #107c10; }
914
+ .wb-verdict-warn { background: #fff8c5; color: #9a6700; }
915
+ .wb-verdict-fail { background: #ffebe9; color: #c42b1c; }
916
+ .wb-verdict-blocked { background: #ffe4e6; color: #b91c1c; }
917
+ .wb-verdict-error { background: #f6f8fa; color: #57606a; }
918
+ .wb-verdict-not_applicable { background: #f6f8fa; color: #57606a; }
919
+ .wb-verdict-? { background: #f6f8fa; color: #57606a; }
920
+ .wb-extra { flex: 1 1 100%; background: #ffffff; color: #1f1f1f; border: 1px solid #c8c8c8; border-radius: 4px; padding: 5px 10px; font-size: 12px; min-width: 200px; }
921
+ .wb-cmd-preview { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; color: #24292e; background: #f5f5f5; border: 1px solid #e0e0e0; border-radius: 6px; padding: 8px 10px; margin: 6px 0 10px; word-break: break-all; }
922
+
923
+ .wb-pano-card-when { font-size: 13px; color: #1f1f1f; }
924
+
925
+ .wb-audit-order { font-size: 12px; color: #1f1f1f; margin-bottom: 6px; }
926
+ .wb-progress { margin-bottom: 18px; border: 1px solid #e0e0e0; border-radius: 6px; padding: 12px 16px; background: #f7f7f7; }
927
+ .wb-bar { height: 8px; background: #e8e8e8; border-radius: 4px; overflow: hidden; margin: 6px 0; }
928
+ .wb-bar-fill { height: 100%; background: #007acc; border-radius: 4px; transition: width .3s; }
929
+ .wb-phase-running { color: #0969da; font-weight: 600; }
930
+ .wb-phase-accepted { color: #107c10; font-weight: 600; }
931
+ .wb-phase-done { color: #9a6700; }
932
+ .wb-phase-failed { color: #c42b1c; }
933
+ .wb-phase-pending, .wb-phase-skipped { opacity: .5; }
934
+ `
626
935
 
627
936
  const CSS = `
628
937
  .aris-view { padding: 16px 20px; overflow-y: auto; font-size: 13px; line-height: 1.6; }
package/dsh/workbench.mjs CHANGED
@@ -204,6 +204,23 @@ export function registerWorkbench(ctx) {
204
204
  if (typeof sessionId !== 'string') {
205
205
  return { ok: false, error: { code: 'bad-request', message: 'sessionId is required', details: {} } }
206
206
  }
207
+ if (endpoint === 'permission') {
208
+ const preset = typeof payload === 'object' && payload !== null ? payload.preset : undefined
209
+ const allowed = ['read-only', 'workspace-write', 'danger-full-access']
210
+ if (typeof preset !== 'string' || allowed.indexOf(preset) === -1) {
211
+ return { ok: false, error: { code: 'bad-request', message: 'preset must be one of ' + allowed.join(', '), details: {} } }
212
+ }
213
+ try {
214
+ const session = scoped.sessions.get(sessionId)
215
+ if (!session || typeof session.append !== 'function') {
216
+ return { ok: false, error: { code: 'internal', message: 'session unavailable', details: {} } }
217
+ }
218
+ session.append('permission/preset', { preset: preset })
219
+ return { ok: true, value: { preset: preset } }
220
+ } catch (error) {
221
+ return { ok: false, error: { code: 'internal', message: String(error && error.message || error), details: {} } }
222
+ }
223
+ }
207
224
  const workspace = scoped.sessions.get(sessionId)?.header?.cwd
208
225
  if (typeof workspace !== 'string' || !isAbsolute(workspace)) {
209
226
  return { ok: true, value: { workspace: undefined } }
@@ -225,6 +242,10 @@ export function registerWorkbench(ctx) {
225
242
  'refine-logs/EXPERIMENT_PLAN.md',
226
243
  'research-wiki/index.md',
227
244
  'research-wiki/query_pack.md',
245
+ 'EXPERIMENT_LOG.md',
246
+ 'NARRATIVE_REPORT.md',
247
+ 'paper/main.tex',
248
+ 'paper/main.pdf',
228
249
  ]) {
229
250
  artifacts[rel] = await statFile(workspace, rel)
230
251
  }
@@ -245,8 +266,29 @@ export function registerWorkbench(ctx) {
245
266
  if (typeof skill !== 'string' || skill === '') {
246
267
  return { ok: false, error: { code: 'bad-request', message: 'skill is required', details: {} } }
247
268
  }
248
- const argText = typeof args === 'string' && args.trim() !== '' ? ' 参数/方向:' + args.trim() : ''
249
- const prompt = '请使用 ARIS 技能 ' + TICK + skill + TICK + ' 完成对应工作流。' + argText
269
+ const argText = typeof args === 'string' ? args.trim() : ''
270
+ const autoProceed = typeof payload === 'object' && payload !== null ? payload.autoProceed : undefined
271
+ const effort = typeof payload === 'object' && payload !== null ? payload.effort : undefined
272
+ const assurance = typeof payload === 'object' && payload !== null ? payload.assurance : undefined
273
+ const difficulty = typeof payload === 'object' && payload !== null ? payload.difficulty : undefined
274
+ const extra = typeof payload === 'object' && payload !== null ? payload.extra : undefined
275
+ const venue = typeof payload === 'object' && payload !== null ? payload.venue : undefined
276
+ const gpu = typeof payload === 'object' && payload !== null ? payload.gpu : undefined
277
+ const reviewer = typeof payload === 'object' && payload !== null ? payload.reviewer : undefined
278
+ const humanCheckpoint = typeof payload === 'object' && payload !== null ? payload.humanCheckpoint : undefined
279
+ const extraText = typeof extra === 'string' ? extra.trim() : ''
280
+ const params = []
281
+ if (autoProceed === false) params.push('AUTO_PROCEED: false')
282
+ if (humanCheckpoint === true) params.push('human checkpoint: true')
283
+ if (effort && effort !== 'balanced') params.push('effort: ' + effort)
284
+ if (assurance && assurance !== 'draft') params.push('assurance: ' + assurance)
285
+ if (difficulty && difficulty !== 'medium') params.push('difficulty: ' + difficulty)
286
+ if (venue) params.push('venue: ' + venue)
287
+ if (gpu) params.push('gpu: ' + gpu)
288
+ if (reviewer && reviewer !== 'auto') params.push('reviewer: ' + reviewer)
289
+ if (extraText) params.push(extraText)
290
+ const cmd = '/' + skill + (argText ? ' "' + argText + '"' : '') + (params.length > 0 ? ' — ' + params.join(', ') : '')
291
+ const prompt = '请执行以下 ARIS 命令:\n' + cmd
250
292
  const messageId = 'aris-launch-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8)
251
293
  const message = {
252
294
  id: messageId,
@@ -278,6 +320,18 @@ export function registerWorkbench(ctx) {
278
320
  }
279
321
  return { ok: true, value: { injected, method, prompt, sessionId, messageId } }
280
322
  }
323
+
324
+ case 'audits': {
325
+ const auditFiles = ['EXPERIMENT_AUDIT.md', 'EXPERIMENT_AUDIT.json', 'PAPER_CLAIM_AUDIT.md', 'PAPER_CLAIM_AUDIT.json', 'CITATION_AUDIT.md', 'CITATION_AUDIT.json', 'KILL_ARGUMENT.md', 'KILL_ARGUMENT.json']
326
+ const out = {}
327
+ for (const rel of auditFiles) {
328
+ const text = await readText(workspace, rel)
329
+ if (text === undefined) continue
330
+ const verdictMatch = /(PASS|WARN|FAIL|BLOCKED|ERROR|NOT_APPLICABLE)/.exec(text)
331
+ out[rel] = { exists: true, verdict: verdictMatch ? verdictMatch[1] : undefined, head: text.split(String.fromCharCode(10)).slice(0, 6) }
332
+ }
333
+ return { ok: true, value: out }
334
+ }
281
335
  default:
282
336
  return { ok: false, error: { code: 'internal', message: 'unknown ARIS workbench endpoint \"' + endpoint + '\"' } }
283
337
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-aris-panel",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "ARIS research-workflow skills + interactive workbench panel for DeepSeek Harness (user fork of dsh-aris: 82 skills, cross-model Codex review, idea/workflow launcher, research status board).",
5
5
  "license": "MIT",
6
6
  "type": "module",