ai_co_discuss 0.2.46 → 0.2.48

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/web/app.js CHANGED
@@ -716,12 +716,19 @@ function floor_status(room) {
716
716
 
717
717
  /** A round completes only after all configured experts have contributed. */
718
718
  function progress_round(room) {
719
- if (!Array.isArray(room.rounds)) return "轮次待同步";
720
- const latest = room.rounds.at(-1);
719
+ const groups = round_groups(room);
720
+ if (!groups) return "轮次待同步";
721
+ const latest = groups.at(-1);
721
722
  const participants = Object.keys(room.experts).length;
722
- if (latest && !latest.completed) return `第 ${latest.number} 轮 · ${latest.speakers.length}/${participants} 人已发言`;
723
+ const completed = groups.filter((item) => item.completed).length;
724
+ if (latest && !latest.completed) return `第 ${latest.number} 轮 · ${latest.opinions ?? latest.speakers.length}/${participants} 人已发言`;
723
725
  return ["pending", "notified"].includes(room.current_turn?.status)
724
- ? `第 ${room.completed_rounds + 1} 轮 · 0/${participants} 人已发言` : `已完成 ${room.completed_rounds} 轮`;
726
+ ? `第 ${completed + 1} 轮 · 0/${participants} 人已发言` : `已完成 ${completed} 轮`;
727
+ }
728
+
729
+ /** Rounds as labelled in the UI: counted by the round budget when the room has one, otherwise by the roster. */
730
+ function round_groups(room) {
731
+ return Array.isArray(room.round_groups) ? room.round_groups : Array.isArray(room.rounds) ? room.rounds : null;
725
732
  }
726
733
 
727
734
  /** Describe the designated respondent without claiming that their app is generating. */
@@ -1527,8 +1534,8 @@ async function submit_room(event, folder_id = null) {
1527
1534
  const payload = room_creation_payload(folder_id);
1528
1535
  const free = payload.mode === "free";
1529
1536
  // A discussion can still be created without the local service and started later from the room;
1530
- // a free room needs the service only for members it wakes.
1531
- const launching = free ? Object.values(payload.experts).includes("cli") : state.launch.create && state.launch.available;
1537
+ // a free room needs the service only for members it wakes or for smart scheduling.
1538
+ const launching = free ? Object.values(payload.experts).includes("cli") || (state.launch.available && Boolean(creation_launch_options().scheduler)) : state.launch.create && state.launch.available;
1532
1539
  if (launching && !state.launch.available) throw new Error("本机唤醒的成员需要连接本机服务;也可改为手动接入或网页代发");
1533
1540
  const launch_draft = launching ? creation_launch_options() : null;
1534
1541
  const signature = JSON.stringify({ payload, launch_draft });
@@ -2315,7 +2322,7 @@ async function retry_execution(event_id) {
2315
2322
  show_toast(`已重新安排 ${execution.participant} 执行,原失败记录保留`);
2316
2323
  }
2317
2324
 
2318
- /** Skip the selected assignment; a paused automatic run resumes with the host instead of retrying it. */
2325
+ /** Skip the selected assignment: the user posts a "skipped" answer for the expert, so the plan moves on to the next one like after any answer. */
2319
2326
  async function skip_turn() {
2320
2327
  const room = state.room;
2321
2328
  const turn = room.current_turn;
@@ -2324,18 +2331,21 @@ async function skip_turn() {
2324
2331
  const runtime = room.runtime || {};
2325
2332
  const resume = runtime.mode === "automatic" && (runtime.status === "paused" || runtime.expired);
2326
2333
  const revision = runtime.revision;
2327
- let cancelled = false;
2334
+ let saved = false;
2328
2335
  try {
2329
- await request_json(`${path}/turns/${turn.id}`, { actor: "用户", status: "cancelled" });
2330
- cancelled = true;
2336
+ const floor = room.floor;
2337
+ if (floor.holder !== turn.expert) await request_json(`${path}/floor`, { actor: "用户", speaker: turn.expert, action: "take", revision: floor.revision });
2338
+ await request_json(`${path}/messages`, { author: turn.expert, submitted_by: "用户", kind: "opinion", turn_id: turn.id,
2339
+ text: "(本次发言已被用户跳过)", submission_id: new_submission_id() });
2340
+ saved = true;
2331
2341
  if (resume) await request_json(`${path}/runtime`, { action: "start", mode: "automatic", revision, deadline: 0 });
2332
2342
  } catch (error) {
2333
- if (cancelled && resume) throw new Error(`本次发言已跳过,但未能继续讨论:${error.message}。核对后可点“重启”继续`);
2343
+ if (saved && resume) throw new Error(`本次发言已跳过,但未能继续讨论:${error.message}。核对后可点“重启”继续`);
2334
2344
  throw error;
2335
2345
  } finally {
2336
2346
  if (state.room?.id === room_id) await refresh_room();
2337
2347
  }
2338
- if (state.room?.id === room_id) show_toast(resume ? "已跳过本次发言并恢复讨论,等待主持人继续安排" : "已跳过本次发言,等待主持人继续安排");
2348
+ if (state.room?.id === room_id) show_toast("已跳过本次发言,按计划交给下一位");
2339
2349
  }
2340
2350
 
2341
2351
  /** Resume the automatic run after a user decision made while paused or just reopened. */
@@ -3341,7 +3351,7 @@ window.discuss_view = { snapshot: view_snapshot, write_field: write_view_field,
3341
3351
  update_profile: update_admin_profile, avatar_from_file: admin_avatar_from_file, validate: validate_admin_form },
3342
3352
  request: request_json, identities: IDENTITIES, display_name, sources: SOURCE_LABELS, phases: PHASE_LABELS,
3343
3353
  runtime_label, execution_label, limits_label, participant_model, participant_access, can_retry_execution,
3344
- turns: TURN_LABELS, turn_label, turn_guidance, floor_status, progress_round, limit_rounds, progress_speaker, brief_meta, max_message_length: MAX_MESSAGE_LENGTH, provenance: message_provenance, format_time };
3354
+ turns: TURN_LABELS, turn_label, turn_guidance, floor_status, progress_round, round_groups, limit_rounds, progress_speaker, brief_meta, max_message_length: MAX_MESSAGE_LENGTH, provenance: message_provenance, format_time };
3345
3355
 
3346
3356
  restore_theme();
3347
3357
  restore_directory_sort();