codebee 0.1.101 → 0.1.103

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/LICENSE +21 -21
  3. package/README.md +3 -6
  4. package/app/.playwright-cli/console-2026-09-23T08-26-03-635Z.log +88 -0
  5. package/app/.playwright-cli/page-2026-09-23T08-26-03-845Z.yml +126 -0
  6. package/app/.playwright-cli/page-2026-09-23T08-29-44-203Z.png +0 -0
  7. package/app/.playwright-cli/page-2026-09-23T08-35-42-046Z.yml +210 -0
  8. package/app/.playwright-cli/page-2026-09-23T08-36-52-858Z.png +0 -0
  9. package/app/.playwright-cli/page-2026-09-23T08-37-33-938Z.png +0 -0
  10. package/app/.playwright-cli/run-detail-after.png +0 -0
  11. package/app/core/acceptance.py +100 -100
  12. package/app/core/acp_client.py +457 -426
  13. package/app/core/aiflavor.py +197 -197
  14. package/app/core/analytics.py +38 -38
  15. package/app/core/artifacts.py +148 -148
  16. package/app/core/assets.py +110 -110
  17. package/app/core/attachments.py +623 -623
  18. package/app/core/audit.py +30 -30
  19. package/app/core/backend_profiles.py +188 -188
  20. package/app/core/backup.py +470 -470
  21. package/app/core/beekeeper.py +183 -183
  22. package/app/core/benchstore.py +102 -102
  23. package/app/core/board.py +21 -17
  24. package/app/core/bookmeta.py +1036 -1036
  25. package/app/core/bookmeta_catalog.py +180 -180
  26. package/app/core/branching.py +137 -137
  27. package/app/core/builtin_agent.py +13 -4
  28. package/app/core/capability.py +144 -144
  29. package/app/core/catalog.py +28 -6
  30. package/app/core/chaptersafe.py +32 -32
  31. package/app/core/checkpoints.py +117 -31
  32. package/app/core/cleanup.py +303 -303
  33. package/app/core/command_auth.py +302 -302
  34. package/app/core/compaction.py +201 -201
  35. package/app/core/contracts.py +255 -255
  36. package/app/core/covergen.py +356 -356
  37. package/app/core/defectretro.py +43 -43
  38. package/app/core/diagnostics.py +114 -114
  39. package/app/core/dispatch.py +1 -1
  40. package/app/core/dispatch_log.py +128 -128
  41. package/app/core/env_scrub.py +85 -85
  42. package/app/core/errorlog.py +137 -137
  43. package/app/core/eval_matrix.py +91 -82
  44. package/app/core/evaluation.py +174 -174
  45. package/app/core/failure_notify.py +290 -290
  46. package/app/core/flow_graph.py +33 -33
  47. package/app/core/goal_service.py +167 -167
  48. package/app/core/health.py +389 -389
  49. package/app/core/history.py +38 -38
  50. package/app/core/hooks.py +162 -162
  51. package/app/core/knowledge_pipeline.py +86 -86
  52. package/app/core/manager.py +2385 -2385
  53. package/app/core/mcp_client.py +7 -2
  54. package/app/core/mocks.py +64 -64
  55. package/app/core/notify.py +201 -201
  56. package/app/core/novel_quality.py +181 -181
  57. package/app/core/openapi.py +217 -209
  58. package/app/core/operations.py +228 -228
  59. package/app/core/paihang.py +145 -145
  60. package/app/core/pipeline.py +6 -3
  61. package/app/core/policy.py +9 -0
  62. package/app/core/portguard.py +111 -111
  63. package/app/core/presence.py +77 -77
  64. package/app/core/preview.py +293 -293
  65. package/app/core/project_memory.py +206 -206
  66. package/app/core/prompt_guard.py +78 -78
  67. package/app/core/publish/__init__.py +7 -7
  68. package/app/core/publish/auto.py +659 -562
  69. package/app/core/publish/chaprows.py +138 -138
  70. package/app/core/publish/fanqie.py +16 -10
  71. package/app/core/publish/flow.py +29 -1
  72. package/app/core/publish/flows-fanqie-calibrated.json +442 -442
  73. package/app/core/publish/manager.py +1185 -1177
  74. package/app/core/publish/qimao.py +193 -193
  75. package/app/core/publish/volumes.py +108 -108
  76. package/app/core/publish/ws.py +139 -139
  77. package/app/core/quality_gate.py +201 -201
  78. package/app/core/redact.py +38 -38
  79. package/app/core/repeat_guard.py +129 -129
  80. package/app/core/retrieval.py +137 -137
  81. package/app/core/sandbox_runtime.py +95 -0
  82. package/app/core/selfupdate.py +384 -384
  83. package/app/core/session_log.py +161 -161
  84. package/app/core/sessions.py +312 -312
  85. package/app/core/settings.py +215 -215
  86. package/app/core/settings_schema.py +282 -282
  87. package/app/core/share_page.py +114 -114
  88. package/app/core/skill_scan.py +139 -139
  89. package/app/core/skillpacks/market/character-bible.md +66 -66
  90. package/app/core/skillpacks/market/code-risk-checklist.md +58 -58
  91. package/app/core/skillpacks/market/git-workflow.md +57 -57
  92. package/app/core/skillpacks/market/release-notes.md +72 -72
  93. package/app/core/skillpacks/market/weekly-report.md +71 -71
  94. package/app/core/skillpacks/market/worldview-consistency.md +70 -70
  95. package/app/core/step_runner.py +132 -132
  96. package/app/core/store.py +13 -3
  97. package/app/core/story_tracking.py +275 -275
  98. package/app/core/task_compile.py +199 -199
  99. package/app/core/telemetry.py +292 -292
  100. package/app/core/tlsctx.py +64 -64
  101. package/app/core/token_meter.py +147 -147
  102. package/app/core/tool_outputs.py +107 -107
  103. package/app/core/tracing.py +207 -207
  104. package/app/core/upstream.py +41 -41
  105. package/app/core/usage.py +1038 -1038
  106. package/app/core/volumes.py +389 -389
  107. package/app/core/webhooks.py +89 -89
  108. package/app/core/wxdigest.py +865 -865
  109. package/app/core/zentao.py +2692 -2692
  110. package/app/main.py +1 -0
  111. package/app/mcp_server.py +244 -244
  112. package/app/output/_createbook_body.json +1 -0
  113. package/app/output/_probe_fq_click.py +50 -0
  114. package/app/output/_probe_fq_form.py +42 -0
  115. package/app/output/_probe_fq_modal.py +55 -0
  116. package/app/output/_probe_fq_points.py +71 -0
  117. package/app/output/_probe_fq_submit.py +34 -0
  118. package/app/output/_probe_fq_watch.py +62 -0
  119. package/app/output/workbench-clean-preview.jpg +0 -0
  120. package/app/output/workbench-reference-render.png +0 -0
  121. package/app/output/workbench-shell.png +0 -0
  122. package/app/pick_dialog.py +110 -110
  123. package/app/ui/app.js +165 -29
  124. package/app/ui/artdiff.js +74 -74
  125. package/app/ui/hive3d.js +491 -223
  126. package/app/ui/i18n.js +18 -1
  127. package/app/ui/icons/bee.svg +79 -79
  128. package/app/ui/index.html +24 -4
  129. package/app/ui/manifest.json +16 -16
  130. package/app/ui/qrcode.js +2297 -2297
  131. package/app/ui/style.css +548 -18
  132. package/bin/tutti.js +147 -147
  133. package/package.json +1 -1
  134. package/tools/wx_reader.py +261 -261
package/app/ui/app.js CHANGED
@@ -451,7 +451,7 @@ function renderTypeOptions() {
451
451
  if (prev && flowById(prev) && (S.typeTouched || prev !== "direct")) sel.value = prev;
452
452
  // 偏好记忆:上次创建用的类型优先于出厂默认「直接执行」(老数据/已删流程回落)
453
453
  else if (S.lastPrefs && S.lastPrefs.type && flowById(S.lastPrefs.type)) sel.value = S.lastPrefs.type;
454
- // 首屏默认落在「直接执行」(快档位:单 CLI 直达,无拆解/评审);
454
+ // 首屏默认落在「直接执行」(快档位:直连模型 API,无拆解/评审);
455
455
  // 老数据或该流程被删时保持首项,不硬造一个不存在的值
456
456
  else if (flowById("direct")) sel.value = "direct";
457
457
  applyTaskPreferenceDefaults();
@@ -3123,7 +3123,20 @@ async function createTask() {
3123
3123
  workdir: $("f-workdir").value.trim(),
3124
3124
  thinking: $("f-thinking").value,
3125
3125
  };
3126
- if (!payload.goal) {
3126
+ const sandboxBackend = $("f-sandbox-backend").value;
3127
+ if (sandboxBackend === "docker") {
3128
+ const dockerImage = $("f-sandbox-image").value.trim();
3129
+ if (!dockerImage) {
3130
+ msg.className = "msg err";
3131
+ msg.textContent = t("选择 Docker 后请填写镜像名");
3132
+ $("f-sandbox-image").focus();
3133
+ resetSubmit();
3134
+ return;
3135
+ }
3136
+ payload.sandbox = { backend: "docker", docker_image: dockerImage };
3137
+ }
3138
+ // 扫榜选材允许目标留空(菜单承诺:默认分析总榜热门题材),其余类型必填
3139
+ if (!payload.goal && payload.type !== "rank_scan") {
3127
3140
  msg.className = "msg err";
3128
3141
  msg.textContent = t("请先填写目标");
3129
3142
  $("f-goal").focus();
@@ -3252,6 +3265,12 @@ async function createTask() {
3252
3265
  }
3253
3266
  }
3254
3267
 
3268
+ function syncSandboxComposer() {
3269
+ const docker = $("f-sandbox-backend") && $("f-sandbox-backend").value === "docker";
3270
+ const row = $("row-sandbox-image");
3271
+ if (row) row.classList.toggle("hidden", !docker);
3272
+ }
3273
+
3255
3274
  /* ---------------------------------------------------------- 附件(截图/文件) */
3256
3275
  /* 待提交附件:选中/粘贴即上传到服务端 pending 区,创建任务时随 payload 落盘
3257
3276
  * 到工作目录 _attachments/ 并注入上下文。图片另走 codex 原生 -i 直读;
@@ -3957,6 +3976,10 @@ async function newFromTaskAsync(id) {
3957
3976
  // 不随预填带走:新任务要么重新上传,要么由 _attachments 路径引用
3958
3977
  $("f-context").value = (tk.context || "").split("\n## 附件材料")[0].trimEnd();
3959
3978
  $("f-workdir").value = tk.workdir || "";
3979
+ const taskSandbox = tk.sandbox || {};
3980
+ $("f-sandbox-backend").value = taskSandbox.backend === "docker" ? "docker" : "native";
3981
+ $("f-sandbox-image").value = taskSandbox.docker_image || "";
3982
+ syncSandboxComposer();
3960
3983
  S.atts = []; renderAttachChips(); // 附件清单不继承:同目录引用已随 context 保留
3961
3984
  queueGitProbe(); // 工作目录变了,重新探测代码版本
3962
3985
  $("f-mode").value = ["fast", "expert", "manual"].includes(tk.mode) ? tk.mode : "auto";
@@ -4815,7 +4838,13 @@ window.loadRunCheckpoints = async function (requestedRunId) {
4815
4838
  const run = (S.lastRun && S.lastRun.id === runId) ? S.lastRun : latest;
4816
4839
  box.dataset.hasRestorable = files.some((file) => file.restorable) ? "true" : "false";
4817
4840
  refreshCheckpointRestoreAvailability(run);
4818
- body.innerHTML = (data.files && data.files.warning ? '<p class="hint">' + esc(data.files.warning) + "</p>" : "") +
4841
+ const checkpoint = data.checkpoint || {};
4842
+ const steps = Array.isArray(checkpoint.steps) ? checkpoint.steps : [];
4843
+ const stepSummary = steps.length ? '<div class="rd-checkpoint-steps"><strong>' + esc(t("步骤回放预览")) + '</strong>' +
4844
+ steps.map((step) => '<div class="rd-checkpoint-step"><span>#' + esc(String(step.step ?? "")) + ' · ' + esc(step.role || t("未命名步骤")) + '</span><span class="chip">' + esc(step.status || step.event || "unknown") + (step.attempt ? ' · ' + esc(t("第") + step.attempt + t("次")) : "") + '</span></div>').join("") +
4845
+ (checkpoint.truncated ? '<p class="hint">' + esc(t("仅显示最近 200 个步骤,共 ") + (checkpoint.total_steps || steps.length)) + '</p>' : '') +
4846
+ (checkpoint.replayable ? '<p class="hint">' + esc(t("所有步骤均已完成,可人工复核后继续")) + '</p>' : '<p class="hint">' + esc(t("无法安全自动回放:") + (checkpoint.blocked_reason || t("需要人工核对"))) + '</p>') + '</div>' : '';
4847
+ body.innerHTML = (data.files && data.files.warning ? '<p class="hint">' + esc(data.files.warning) + "</p>" : "") + stepSummary +
4819
4848
  (files.length ? files.map((file) => '<article class="rd-checkpoint-row"><strong>' + esc(file.path) + '</strong><span class="chip">' + esc(file.status) + (file.restorable ? " · " + esc(t("可恢复")) : "") + '</span><pre>' + esc(file.diff || "") + "</pre></article>").join("") : '<span class="hint">' + esc(t("此运行没有文件快照")) + "</span>");
4820
4849
  box.dataset.runId = runId;
4821
4850
  box.classList.remove("needs-load");
@@ -6185,6 +6214,11 @@ function pbBlock(task, platform) {
6185
6214
  S.pubAuto.running.platform === platform) ? S.pubAuto.running : null;
6186
6215
  if (run && run.status === "running") {
6187
6216
  btns += '<span class="pb-book">' + esc((run.as_draft ? t("存草稿中 ") : t("自动发布中 ")) + run.done + "/" + run.total) + "</span>";
6217
+ }
6218
+ if (run && Array.isArray(run.notes) && run.notes.length) {
6219
+ // 对账剔除/跳过/重试的过程记录(auto 层 notes),最近三条
6220
+ btns += '<div class="pb-hint">' +
6221
+ esc(run.notes.slice(-3).join(";")) + "</div>";
6188
6222
  } else if (au.pending > 0) {
6189
6223
  // 门禁不再禁用按钮:点了走 pbApiWithQualityOverride 的复审/强制对话,
6190
6224
  // 死按钮只会让用户无路可走(2026-10-08 实案:guard false 全灰点不了)
@@ -6209,9 +6243,6 @@ function pbBlock(task, platform) {
6209
6243
  const key = task.id + ":" + platform;
6210
6244
  const sig = its.map((x) => x.n).join(",");
6211
6245
  S.pbSel = S.pbSel || {};
6212
- if (!S.pbSel[key] || S.pbSel[key].sig !== sig)
6213
- S.pbSel[key] = { sig, sel: new Set(its.map((x) => x.n)) };
6214
- const sel = S.pbSel[key].sel;
6215
6246
  // 连续段按卷名分组;无卷计划的整块平铺不显示卷头
6216
6247
  const groups = [];
6217
6248
  for (const x of its) {
@@ -6219,10 +6250,22 @@ function pbBlock(task, platform) {
6219
6250
  if (g && g.v === x.v) g.ns.push(x.n);
6220
6251
  else groups.push({ v: x.v, ns: [x.n] });
6221
6252
  }
6253
+ if (!S.pbSel[key] || S.pbSel[key].sig !== sig)
6254
+ S.pbSel[key] = { sig, sel: new Set(its.map((x) => x.n)), groups };
6255
+ const sel = S.pbSel[key].sel;
6222
6256
  let chips = "";
6223
- for (const g of groups) {
6224
- if (groups.length > 1 || g.v)
6225
- chips += '<div class="pb-vol">' + esc(g.v || t("未分卷")) + "</div>";
6257
+ for (let gi = 0; gi < groups.length; gi++) {
6258
+ const g = groups[gi];
6259
+ if (groups.length > 1 || g.v) {
6260
+ // 卷头=整卷开关:全选打勾、部分选中显示 已选/总(2026-10-10 按卷选章)
6261
+ const cnt = g.ns.filter((n) => sel.has(n)).length;
6262
+ const allOn = cnt === g.ns.length;
6263
+ chips += '<div class="pb-vol"><label class="pb-vol-sel' + (allOn ? " on" : "") +
6264
+ '" title="' + esc(t("勾选卷头=整卷进所选,再点取消")) + '">' +
6265
+ '<input type="checkbox" ' + (allOn ? "checked" : "") +
6266
+ ' onchange="pbSelVolume(\'' + esc(task.id) + "','" + platform + "'," + gi + ',this.checked)">' +
6267
+ esc(g.v || t("未分卷")) + t("(") + (allOn ? "" : cnt + "/") + g.ns.length + t(")") + "</label></div>";
6268
+ }
6226
6269
  for (const n of g.ns)
6227
6270
  chips += '<label class="pb-chip' + (sel.has(n) ? " on" : "") + '">' +
6228
6271
  '<input type="checkbox" ' + (sel.has(n) ? "checked" : "") +
@@ -6580,6 +6623,17 @@ window.pbSelAll = function (taskId, platform, all) {
6580
6623
  S._pbSig = ""; pbKick();
6581
6624
  };
6582
6625
 
6626
+ window.pbSelVolume = function (taskId, platform, gi, on) {
6627
+ const key = taskId + ":" + platform;
6628
+ const ent = (S.pbSel || {})[key];
6629
+ const g = ent && ent.groups && ent.groups[gi];
6630
+ if (!g || !g.ns.length) return;
6631
+ if (on) for (const n of g.ns) ent.sel.add(n);
6632
+ else for (const n of g.ns) ent.sel.delete(n);
6633
+ pbSelCountSync(key);
6634
+ S._pbSig = ""; pbKick();
6635
+ };
6636
+
6583
6637
  window.pbPublishSelected = async function (taskId, platform, autoSubmit) {
6584
6638
  const key = taskId + ":" + platform;
6585
6639
  const ent = (S.pbSel || {})[key];
@@ -8578,15 +8632,15 @@ function hiveThinkLine(lines) {
8578
8632
  * localStorage 持久化/WebGL 不可用降级,以及把 renderHive 的泳道数据翻译给场景。 —— */
8579
8633
  let hiveScene = null; // Hive3D 场景实例(懒创建)
8580
8634
  let hiveSceneDead = false; // WebGL 不可用/上下文丢失——本次页面周期内不再尝试
8581
- let hiveMode = "3d"; // 当前视图模式(orch.hiveView)
8635
+ let hiveMode = "live3d"; // live3d is the real scene; reference and 2d remain opt-in views
8582
8636
  let hiveRunId = ""; // 最近一次 renderHive 的 run.id(尾巴/思考缓存键前缀)
8583
8637
  const hiveLiveMeta = {}; // rel -> { started_at }(3D 芯片秒表用)
8584
8638
  let hiveDialogPoll = null;
8585
8639
  let hiveDialogKey = "";
8586
8640
 
8587
- function setHiveSceneButtons(is3d) {
8641
+ function setHiveSceneButtons(mode) {
8588
8642
  document.querySelectorAll("[data-hive-view]").forEach((button) => {
8589
- const active = button.dataset.hiveView === (is3d ? "3d" : "2d");
8643
+ const active = button.dataset.hiveView === mode;
8590
8644
  button.classList.toggle("active", active);
8591
8645
  button.setAttribute("aria-pressed", active ? "true" : "false");
8592
8646
  });
@@ -8657,28 +8711,80 @@ function closeHiveMonitorLog() {
8657
8711
  function setHiveSceneMode(mode, opts) {
8658
8712
  const viewport = $("rd-hive-viewport");
8659
8713
  if (!viewport) return;
8660
- let is3d = mode !== "2d";
8661
- if (is3d) {
8714
+ let selected = ["live3d", "2d"].includes(mode) ? mode : "reference";
8715
+ if (selected !== "2d") {
8662
8716
  const sc = ensureHiveScene();
8663
8717
  if (!sc) {
8664
- is3d = false;
8718
+ selected = "2d";
8665
8719
  if (!(opts && opts.silent)) toast(t("当前环境不支持 WebGL,已切换 2D 列表"), true);
8666
8720
  }
8667
8721
  }
8668
- hiveMode = is3d ? "3d" : "2d";
8669
- if (hiveScene) hiveScene.setActive(is3d);
8670
- viewport.classList.toggle("hive-mode-3d", is3d);
8671
- viewport.classList.toggle("hive-mode-2d", !is3d);
8722
+ hiveMode = selected;
8723
+ if (hiveScene) {
8724
+ hiveScene.setDisplayMode(selected);
8725
+ hiveScene.setActive(selected !== "2d");
8726
+ }
8727
+ viewport.classList.toggle("hive-mode-3d", selected === "reference");
8728
+ viewport.classList.toggle("hive-mode-live3d", selected === "live3d");
8729
+ viewport.classList.toggle("hive-mode-2d", selected === "2d");
8672
8730
  const tools = document.querySelector(".hive-scene-tools");
8673
- if (tools) tools.classList.toggle("hive-2d", !is3d);
8674
- setHiveSceneButtons(is3d);
8675
- try { localStorage.setItem("orch.hiveView", hiveMode); } catch (e) { /* 隐私模式等 */ }
8731
+ if (tools) tools.classList.toggle("hive-2d", selected === "2d");
8732
+ setHiveSceneButtons(selected);
8733
+ const hint = document.querySelector(".hive-scene-hint");
8734
+ if (hint) {
8735
+ const hintKey = selected === "live3d"
8736
+ ? "拖动旋转 · Shift+拖动平移 · 滚轮缩放 · 双击复位 · 点击屏幕看日志"
8737
+ : selected === "2d" ? "点击格子看日志"
8738
+ : "拖动平移 · 滚轮缩放 · 双击复位 · 点击屏幕看日志";
8739
+ hint.dataset.i18n = hintKey;
8740
+ hint.textContent = t(hintKey);
8741
+ }
8742
+ try { localStorage.setItem("orch.hiveView.v2", hiveMode); } catch (e) { /* 隐私模式等 */ }
8743
+ }
8744
+
8745
+ function syncHiveFullscreenButton() {
8746
+ const stage = $("hive-scene-stage");
8747
+ const button = $("hive-scene-fullscreen");
8748
+ const active = !!stage && document.fullscreenElement === stage;
8749
+ if (stage) stage.classList.toggle("is-fullscreen", active);
8750
+ if (!button) return;
8751
+ const textKey = active ? "退出全屏" : "全屏";
8752
+ const labelKey = active ? "退出全屏" : "进入全屏";
8753
+ button.dataset.i18n = textKey;
8754
+ button.dataset.i18nTitle = labelKey;
8755
+ button.dataset.i18nAria = labelKey;
8756
+ button.textContent = t(textKey);
8757
+ button.title = t(labelKey);
8758
+ button.setAttribute("aria-label", t(labelKey));
8759
+ button.setAttribute("aria-pressed", active ? "true" : "false");
8760
+ }
8761
+
8762
+ async function toggleHiveFullscreen() {
8763
+ const stage = $("hive-scene-stage");
8764
+ if (!stage) return;
8765
+ try {
8766
+ if (document.fullscreenElement === stage) {
8767
+ await document.exitFullscreen();
8768
+ return;
8769
+ }
8770
+ if (typeof stage.requestFullscreen !== "function" || document.fullscreenEnabled === false) {
8771
+ toast(t("当前浏览器不支持全屏"), true);
8772
+ return;
8773
+ }
8774
+ await stage.requestFullscreen();
8775
+ } catch (error) {
8776
+ toast(t("全屏失败:") + (error && error.message ? error.message : ""), true);
8777
+ }
8676
8778
  }
8677
8779
 
8678
8780
  function setupHiveSceneControls() {
8679
8781
  const viewport = $("rd-hive-viewport");
8680
8782
  if (!viewport || viewport.dataset.controlsReady) return;
8681
8783
  viewport.dataset.controlsReady = "true";
8784
+ const fullscreenButton = $("hive-scene-fullscreen");
8785
+ if (fullscreenButton) fullscreenButton.addEventListener("click", toggleHiveFullscreen);
8786
+ document.addEventListener("fullscreenchange", syncHiveFullscreenButton);
8787
+ syncHiveFullscreenButton();
8682
8788
  const hiveDialog = $("hive-log-dialog");
8683
8789
  const hiveClose = $("hive-log-close");
8684
8790
  if (hiveClose) hiveClose.addEventListener("click", closeHiveMonitorLog);
@@ -8691,14 +8797,23 @@ function setupHiveSceneControls() {
8691
8797
  });
8692
8798
  document.querySelectorAll("[data-hive-scene-action]").forEach((button) => {
8693
8799
  button.addEventListener("click", () => {
8694
- if (hiveMode !== "3d" || !hiveScene) return;
8800
+ if (hiveMode === "2d" || !hiveScene) return;
8695
8801
  if (button.dataset.hiveSceneAction === "reset") hiveScene.resetView();
8696
8802
  else hiveScene.zoomAt(button.dataset.hiveSceneAction === "zoom-in" ? 1 / 1.18 : 1.18);
8697
8803
  });
8698
8804
  });
8699
- let saved = "3d";
8700
- try { saved = localStorage.getItem("orch.hiveView") || "3d"; } catch (e) { /* ignore */ }
8701
- setHiveSceneMode(saved === "2d" ? "2d" : "3d", { silent: true });
8805
+ let saved = "live3d";
8806
+ try {
8807
+ saved = localStorage.getItem("orch.hiveView.v2") || "";
8808
+ if (!saved) {
8809
+ // Migrate an explicitly selected 2D/live3D preference; the old default "reference"
8810
+ // was auto-persisted for every new visitor, so it is not a reliable user preference.
8811
+ const legacy = localStorage.getItem("orch.hiveView");
8812
+ saved = legacy === "2d" || legacy === "live3d" ? legacy : "live3d";
8813
+ localStorage.setItem("orch.hiveView.v2", saved);
8814
+ }
8815
+ } catch (e) { /* ignore storage restrictions and keep the live 3D default */ }
8816
+ setHiveSceneMode(["reference", "live3d", "2d"].includes(saved) ? saved : "live3d", { silent: true });
8702
8817
  }
8703
8818
 
8704
8819
  /* renderHive → 场景数据翻译:泳道=蜂巢塔的一层,格子按状态映射高度/颜色。
@@ -8758,7 +8873,8 @@ function hiveSceneSync(run, lanes, byStage) {
8758
8873
  const sc = ensureHiveScene();
8759
8874
  if (!sc) return;
8760
8875
  sc.sync(model); // 2D 模式也同步:切回 3D 时数据即 ready(画布未激活零 GPU 开销)
8761
- sc.setActive(hiveMode === "3d");
8876
+ sc.setDisplayMode(hiveMode);
8877
+ sc.setActive(hiveMode !== "2d");
8762
8878
  }
8763
8879
 
8764
8880
  window.renderHive = function (run) {
@@ -9322,6 +9438,20 @@ function chatThinkHTML(it) {
9322
9438
  '<div class="ct-body">' + esc(think) + "</div></details>";
9323
9439
  }
9324
9440
 
9441
+ function chatAcpEventsHTML(it) {
9442
+ const events = Array.isArray(it.acp_events) ? it.acp_events.slice(-8) : [];
9443
+ if (!events.length) return "";
9444
+ return '<details class="chat-think chat-acp-events"><summary>' + esc(t("ACP 活动")) +
9445
+ '<span class="ct-len">' + events.length + ' ' + esc(t("条")) + '</span></summary>' +
9446
+ '<div class="ct-acts">' + events.map((event) => {
9447
+ const type = String(event.sessionUpdate || "event");
9448
+ const label = event.title || event.status || event.modeId || "";
9449
+ const entries = Array.isArray(event.entries) ? event.entries.map((entry) =>
9450
+ '<div class="rd-acp-plan-entry">' + esc(entry.status || "") + ' · ' + esc(entry.content || "") + '</div>').join("") : "";
9451
+ return '<div><code>' + esc(type) + '</code>' + (label ? ' · ' + esc(label) : "") + entries + '</div>';
9452
+ }).join("") + '</div></details>';
9453
+ }
9454
+
9325
9455
  /* 运行中直连对话的快轮询:SSE/轮询最密 2s、闲时 8s,思考是逐句吐的——
9326
9456
  * 1.2s 直拉时间线把增量打印出来。run 终态、切详情、分区隐藏即停。 */
9327
9457
  function stopChatLive() {
@@ -9424,6 +9554,7 @@ function drawChatFlow(run, data, active) {
9424
9554
  (it.status || "") + ":" + String(it.text || "").length + ":" +
9425
9555
  String(it.thinking || "").length + ":" + String(it.stream || "").length + ":" +
9426
9556
  (it.activity || []).length + ":" + (it.followups || []).length +
9557
+ ":" + (it.acp_events || []).length +
9427
9558
  ":" + (it.consumed ? 1 : 0), "") + "|" + ((data && data.result) ? "r" : "");
9428
9559
  if (liveSig === chatLiveSig && flow.childElementCount) return;
9429
9560
  chatLiveSig = liveSig;
@@ -9497,6 +9628,7 @@ function drawChatFlow(run, data, active) {
9497
9628
  '<span class="chat-avatar" aria-hidden="true"><svg class="ico"><use href="#i-bee"></use></svg></span>' +
9498
9629
  '<div class="chat-bubble agent">' +
9499
9630
  chatThinkHTML(it) +
9631
+ chatAcpEventsHTML(it) +
9500
9632
  '<div class="chat-body">' + bodyHtml + "</div>" +
9501
9633
  '<div class="chat-meta">' + esc(it.who || "") + " " + esc(chatTime(it.at)) +
9502
9634
  (metaBad ? " · " + t(it.status === "failed" ? "失败" : "已取消") : "") + actsA + "</div>" +
@@ -14567,8 +14699,7 @@ async function suStartupCheck() {
14567
14699
  }
14568
14700
 
14569
14701
  /* ---------------------------------------------------------- 页签 & 初始化 */
14570
- const TAB_TITLES = { overview: "概览", tasks: "任务", runs: "运行记录", automation: "自动化", browser: "浏览器", zentao: "禅道 Bug 自动修复", __wxdigest: "群摘要", usage: "用量统计", agents: "本机智能体", models: "模型接入", bindings: "模型调度(可选)", skills: "经验库", knowledge: "知识库", market: "插件市场", orch: "编排设置", data: "数据与备份", appearance: "皮肤", about: "关于与更新" };
14571
- const SET_TABS = new Set(Object.keys(TAB_TITLES)); // 全部设置子页(__phone 是弹框,不算)
14702
+ const TAB_TITLES = { overview: "概览", tasks: "任务", runs: "运行记录", automation: "自动化", browser: "浏览器", zentao: "禅道 Bug 自动修复", __wxdigest: "群摘要", usage: "用量统计", agents: "本机智能体", models: "模型接入", bindings: "模型调度(可选)", skills: "经验库", knowledge: "知识库", market: "插件市场", orch: "编排设置", evalbench: "模型评测", data: "数据与备份", appearance: "皮肤", about: "关于与更新" };
14572
14703
 
14573
14704
  function tabTitle(name) {
14574
14705
  return t(TAB_TITLES[name]) || t("设置");
@@ -16070,6 +16201,9 @@ window.suRestart = suRestart;
16070
16201
  window.updDismiss = updDismiss;
16071
16202
 
16072
16203
  document.addEventListener("DOMContentLoaded", () => {
16204
+ // Initialize Hive3D controls independently of run data. A fresh workspace may have no
16205
+ // active run yet, but fullscreen/view controls must still be wired and usable.
16206
+ setupHiveSceneControls();
16073
16207
  // 远程地址里带的 ?token= 存起来并从地址栏抹掉,之后所有请求走请求头
16074
16208
  const urlTok = new URLSearchParams(location.search).get("token");
16075
16209
  if (urlTok) {
@@ -16411,6 +16545,8 @@ document.addEventListener("DOMContentLoaded", () => {
16411
16545
  localStorage.removeItem("orch.showArchived"); // 清掉旧版持久化残留,避免误解为默认选中
16412
16546
  $("f-resume-agent").addEventListener("change", loadSessions);
16413
16547
  $("f-resume-session").addEventListener("change", showResumeHint);
16548
+ $("f-sandbox-backend").addEventListener("change", syncSandboxComposer);
16549
+ syncSandboxComposer();
16414
16550
  /* 工作目录不再用「上次用过」的旧记忆预填:那样改默认保存路径后,新建任务仍会被
16415
16551
  * 旧目录盖过。首屏默认选中设置里的默认路径(loadSettings 落值),克隆/右键新建
16416
16552
  * 等显式入口各自直接写字段值,不跨刷新持久化。清掉旧版残留键,避免误解为默认选中。 */
package/app/ui/artdiff.js CHANGED
@@ -1,74 +1,74 @@
1
- /* 成品两版对比的纯 diff 计算(OpenCreator「版本化对比」借鉴)。
2
- * 独立文件无 DOM 依赖:app.js 用 window.ArtifactDiff 渲染,tests 用
3
- * module.exports 直测。算法:公共前后缀修剪 + 中段 LCS(上限保护,
4
- * 超限诚实降级为整块替换计数,不装作对齐了)。 */
5
- (function (root, factory) {
6
- var api = factory();
7
- if (typeof window !== "undefined") window.ArtifactDiff = api;
8
- if (typeof module !== "undefined" && module.exports) module.exports = api;
9
- })(typeof self !== "undefined" ? self : this, function () {
10
- "use strict";
11
-
12
- var MID_CAP = 400; // 中段行数单边上限
13
- var MID_PRODUCT_CAP = 160000; // LCS DP 格子上限(400×400)
14
-
15
- /* LCS 回溯产出统一 diff 行:[{t:"="|"-"/"+", s:行文本}](只对中段)。 */
16
- function lcsRows(A, B) {
17
- var n = A.length, m = B.length;
18
- var dp = [];
19
- for (var k = 0; k <= n; k++) dp.push(new Uint32Array(m + 1));
20
- for (var i = n - 1; i >= 0; i--)
21
- for (var j = m - 1; j >= 0; j--)
22
- dp[i][j] = A[i] === B[j] ? dp[i + 1][j + 1] + 1
23
- : (dp[i + 1][j] >= dp[i][j + 1] ? dp[i + 1][j] : dp[i][j + 1]);
24
- var rows = [];
25
- i = 0; j = 0;
26
- while (i < n && j < m) {
27
- if (A[i] === B[j]) { rows.push({ t: "=", s: A[i] }); i++; j++; }
28
- else if (dp[i + 1][j] >= dp[i][j + 1]) { rows.push({ t: "-", s: A[i] }); i++; }
29
- else { rows.push({ t: "+", s: B[j] }); j++; }
30
- }
31
- while (i < n) { rows.push({ t: "-", s: A[i] }); i++; }
32
- while (j < m) { rows.push({ t: "+", s: B[j] }); j++; }
33
- return rows;
34
- }
35
-
36
- /* 对外主口:旧版 a、新版 b → {
37
- * prefix/suffix: 相同行数(渲染端折叠展示),
38
- * identical: 布尔,两版一致,
39
- * rows: 中段统一 diff 行(超限时为 null),
40
- * oversized: {a, b} 超限时中段行数(诚实整块计数) } */
41
- function diff(a, b) {
42
- a = String(a == null ? "" : a);
43
- b = String(b == null ? "" : b);
44
- var A = a.split("\n"), B = b.split("\n");
45
- var p = 0;
46
- while (p < A.length && p < B.length && A[p] === B[p]) p++;
47
- var s = 0;
48
- while (s < A.length - p && s < B.length - p &&
49
- A[A.length - 1 - s] === B[B.length - 1 - s]) s++;
50
- var midA = A.slice(p, A.length - s), midB = B.slice(p, B.length - s);
51
- var out = { prefix: p, suffix: s, identical: !midA.length && !midB.length,
52
- rows: null, oversized: null };
53
- if (out.identical) return out;
54
- if (midA.length > MID_CAP || midB.length > MID_CAP ||
55
- midA.length * midB.length > MID_PRODUCT_CAP) {
56
- out.oversized = { a: midA.length, b: midB.length };
57
- return out;
58
- }
59
- out.rows = lcsRows(midA, midB);
60
- return out;
61
- }
62
-
63
- /* rows → 摘要计数(+x/-y,渲染端徽章用)。 */
64
- function counts(d) {
65
- var add = 0, del = 0;
66
- if (d.oversized) return { add: d.oversized.b, del: d.oversized.a };
67
- (d.rows || []).forEach(function (r) {
68
- if (r.t === "+") add++; else if (r.t === "-") del++;
69
- });
70
- return { add: add, del: del };
71
- }
72
-
73
- return { diff: diff, counts: counts, lcsRows: lcsRows };
74
- });
1
+ /* 成品两版对比的纯 diff 计算(OpenCreator「版本化对比」借鉴)。
2
+ * 独立文件无 DOM 依赖:app.js 用 window.ArtifactDiff 渲染,tests 用
3
+ * module.exports 直测。算法:公共前后缀修剪 + 中段 LCS(上限保护,
4
+ * 超限诚实降级为整块替换计数,不装作对齐了)。 */
5
+ (function (root, factory) {
6
+ var api = factory();
7
+ if (typeof window !== "undefined") window.ArtifactDiff = api;
8
+ if (typeof module !== "undefined" && module.exports) module.exports = api;
9
+ })(typeof self !== "undefined" ? self : this, function () {
10
+ "use strict";
11
+
12
+ var MID_CAP = 400; // 中段行数单边上限
13
+ var MID_PRODUCT_CAP = 160000; // LCS DP 格子上限(400×400)
14
+
15
+ /* LCS 回溯产出统一 diff 行:[{t:"="|"-"/"+", s:行文本}](只对中段)。 */
16
+ function lcsRows(A, B) {
17
+ var n = A.length, m = B.length;
18
+ var dp = [];
19
+ for (var k = 0; k <= n; k++) dp.push(new Uint32Array(m + 1));
20
+ for (var i = n - 1; i >= 0; i--)
21
+ for (var j = m - 1; j >= 0; j--)
22
+ dp[i][j] = A[i] === B[j] ? dp[i + 1][j + 1] + 1
23
+ : (dp[i + 1][j] >= dp[i][j + 1] ? dp[i + 1][j] : dp[i][j + 1]);
24
+ var rows = [];
25
+ i = 0; j = 0;
26
+ while (i < n && j < m) {
27
+ if (A[i] === B[j]) { rows.push({ t: "=", s: A[i] }); i++; j++; }
28
+ else if (dp[i + 1][j] >= dp[i][j + 1]) { rows.push({ t: "-", s: A[i] }); i++; }
29
+ else { rows.push({ t: "+", s: B[j] }); j++; }
30
+ }
31
+ while (i < n) { rows.push({ t: "-", s: A[i] }); i++; }
32
+ while (j < m) { rows.push({ t: "+", s: B[j] }); j++; }
33
+ return rows;
34
+ }
35
+
36
+ /* 对外主口:旧版 a、新版 b → {
37
+ * prefix/suffix: 相同行数(渲染端折叠展示),
38
+ * identical: 布尔,两版一致,
39
+ * rows: 中段统一 diff 行(超限时为 null),
40
+ * oversized: {a, b} 超限时中段行数(诚实整块计数) } */
41
+ function diff(a, b) {
42
+ a = String(a == null ? "" : a);
43
+ b = String(b == null ? "" : b);
44
+ var A = a.split("\n"), B = b.split("\n");
45
+ var p = 0;
46
+ while (p < A.length && p < B.length && A[p] === B[p]) p++;
47
+ var s = 0;
48
+ while (s < A.length - p && s < B.length - p &&
49
+ A[A.length - 1 - s] === B[B.length - 1 - s]) s++;
50
+ var midA = A.slice(p, A.length - s), midB = B.slice(p, B.length - s);
51
+ var out = { prefix: p, suffix: s, identical: !midA.length && !midB.length,
52
+ rows: null, oversized: null };
53
+ if (out.identical) return out;
54
+ if (midA.length > MID_CAP || midB.length > MID_CAP ||
55
+ midA.length * midB.length > MID_PRODUCT_CAP) {
56
+ out.oversized = { a: midA.length, b: midB.length };
57
+ return out;
58
+ }
59
+ out.rows = lcsRows(midA, midB);
60
+ return out;
61
+ }
62
+
63
+ /* rows → 摘要计数(+x/-y,渲染端徽章用)。 */
64
+ function counts(d) {
65
+ var add = 0, del = 0;
66
+ if (d.oversized) return { add: d.oversized.b, del: d.oversized.a };
67
+ (d.rows || []).forEach(function (r) {
68
+ if (r.t === "+") add++; else if (r.t === "-") del++;
69
+ });
70
+ return { add: add, del: del };
71
+ }
72
+
73
+ return { diff: diff, counts: counts, lcsRows: lcsRows };
74
+ });