ai_co_discuss 0.2.81 → 0.2.83

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.
@@ -18,8 +18,8 @@
18
18
 
19
19
  多位成员共享同一工作目录时,只改分给自己的文件;只提交自己的改动,切分支、reset、全仓 stash 或 checkout 须用户确认。
20
20
 
21
- ## 建议他人回应
21
+ ## 提到其他成员
22
22
 
23
- 普通成员可在回答中用 `--to 成员名` 或正文 `@成员` 提出回应建议,不能点名自己;这不会派发任务。网页显示“建议请 X 回应”,只有房主点击“转为请求”后才建立关联原回答的新用户任务链。
23
+ 普通成员可在回答中用 `--to 成员名` 或正文 `@成员` 提到他人,不能点名自己;这不会派发任务,也不会通知对方。需要别人补充时,在回答里写明缺什么,由用户或主持人决定是否请对方处理。
24
24
 
25
- 任务的 `request_messages` 可能指向已读游标之前的原回答,须用 `message view ROOM ID` 补读;用户确认消息不代替原建议。不要把建议显示或复制入口当作已经通知;代码与引用里的正文 @ 不生成建议。
25
+ 任务的 `request_messages` 可能指向已读游标之前的原回答,须用 `message view ROOM ID` 补读。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.2.81",
3
+ "version": "0.2.83",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
@@ -188,7 +188,10 @@ export class FreeRoomRunner {
188
188
  if (LIVE.includes(row.status) && !present.has(row.member)) { this.stop_member(row.member); this.save(row.member, { status: 'idle' }); }
189
189
  }
190
190
  await this.report_finished(active);
191
- for (const member of inbox.members) {
191
+ // Start in the room's speaking order: execution ids follow it, and the website lays out working cards by them.
192
+ const rank = (room.speaking_order?.items || []).map(item => item.name);
193
+ const place = name => rank.includes(name) ? rank.indexOf(name) : rank.length;
194
+ for (const member of [...inbox.members].sort((left, right) => place(left.name) - place(right.name))) {
192
195
  const row = this.row(member.name);
193
196
  if (row && LIVE.includes(row.status)) await this.reconcile(member, row, active, inbox.execution_minutes);
194
197
  else if (!inbox.archived && member.pending.some(request => request.purpose !== 'group') && !this.members.get(member.name)?.size
package/web/app.js CHANGED
@@ -791,15 +791,10 @@ function message_markup(message) {
791
791
  /** Render continuous history while retaining expanded answers and reading position. */
792
792
  function render_messages(result) {
793
793
  const previous = new Map(state.messages.map((message) => [message.id, message]));
794
- const requests = new Map((state.room?.free?.requests || []).map(request => [`${request.message_id}:${request.member_id}`, request]));
795
794
  state.messages = result.messages.map((message) => {
796
795
  const saved = previous.get(message.id);
797
- const suggestions = (saved?.suggestions || message.suggestions || []).map(suggestion => {
798
- const request = requests.get(`${message.id}:${suggestion.member_id}`);
799
- return request && suggestion.request_id !== request.id ? { ...suggestion, request_id: request.id } : suggestion;
800
- });
801
- if (saved) return suggestions.some((item, index) => item !== saved.suggestions?.[index]) ? { ...saved, suggestions } : saved;
802
- return { ...message, suggestions, rendered_text: window.discuss_markdown.render_markdown(message.text),
796
+ if (saved) return saved;
797
+ return { ...message, rendered_text: window.discuss_markdown.render_markdown(message.text),
803
798
  rendered_summary: window.discuss_markdown.render_markdown(message.summary || "") };
804
799
  });
805
800
  state.message_total = result.total;
@@ -1872,11 +1867,15 @@ async function retry_launch_connection() {
1872
1867
  /** Read saved choices for the active room without using drafts or delaying discussion refreshes. */
1873
1868
  async function load_room_models() {
1874
1869
  const room = state.room;
1875
- if (!state.launch.available || room?.runtime?.mode !== "automatic") return;
1870
+ // A free room runs no discussion yet still has members this computer wakes, so either one makes the models worth reading.
1871
+ const woken = (room?.members || []).filter(member => member.access === "runner").map(member => member.name);
1872
+ if (!state.launch.available || (room?.runtime?.mode !== "automatic" && !woken.length)) return;
1873
+ // Inviting or removing a member in a free room leaves the runtime revision alone, so the woken names are part of the key.
1874
+ const revision = `${room.runtime?.revision ?? 0}:${woken.join(",")}`;
1876
1875
  const previous = state.launch.room_models;
1877
- if (previous?.room_id === room.id && previous.revision === room.runtime.revision
1876
+ if (previous?.room_id === room.id && previous.revision === revision
1878
1877
  && (previous.loading || previous.participants)) return;
1879
- const current = { room_id: room.id, revision: room.runtime.revision, loading: true, participants: null };
1878
+ const current = { room_id: room.id, revision, loading: true, participants: null };
1880
1879
  state.launch.room_models = current;
1881
1880
  try {
1882
1881
  const result = await local_launch_request("room-models", { room_id: room.id });
@@ -1892,7 +1891,8 @@ async function load_room_models() {
1892
1891
  /** Describe registered launch choices; an empty choice shows what the software's own settings select, or says the software decides. */
1893
1892
  function participant_model(name, room, saved) {
1894
1893
  // Nothing to say beats a placeholder: a manual member's settings live in its own session and unread ones are not guessed.
1895
- if (room?.runtime?.mode !== "automatic" || (room.members || []).find(member => member.name === name)?.access === "manual") return "";
1894
+ const member = (room?.members || []).find(item => item.name === name);
1895
+ if (member ? member.access !== "runner" : room?.runtime?.mode !== "automatic") return "";
1896
1896
  const profile = saved?.room_id === room.id ? saved.participants?.[name] : null;
1897
1897
  if (!profile) return "";
1898
1898
  if (["codex", "antigravity"].includes(profile.adapter)) return "模型与强度由 App 设置";
@@ -2673,17 +2673,6 @@ async function interrupt_member(member_id) {
2673
2673
  if (state.room?.id === room_id) await refresh_room();
2674
2674
  }
2675
2675
 
2676
- /** Confirm one ordinary member's suggestion, keeping retries bound to its source and target. */
2677
- async function convert_suggestion({ message_id, member_id }) {
2678
- const room_id = state.room.id;
2679
- const result = await request_json(`/api/rooms/${room_id}/suggestions`, { message_id, member_id });
2680
- if (state.room?.id !== room_id) return;
2681
- state.messages = state.messages.map(message => message.id === message_id ? { ...message,
2682
- suggestions: message.suggestions.map(suggestion => suggestion.member_id === member_id
2683
- ? { ...suggestion, request_id: result.request.id } : suggestion) } : message);
2684
- await refresh_room();
2685
- }
2686
-
2687
2676
  /** Adopt smart scheduling's recommendation: interrupt the host now, or ask the member once the host is done. */
2688
2677
  async function adopt_recommendation({ message_id, mode }) {
2689
2678
  const room_id = state.room.id;
@@ -3458,7 +3447,7 @@ async function execute_view_action(name, value) {
3458
3447
  choose_runtime, export_runtime, download_member_access, copy_member_access_text, copy_member_join, copy_member_launch: name => copy_member_join(name, true), pause_runtime, check_limits, pause_discussion, restart_discussion, retry_execution, conclude_discussion, start_room_discussion, end_room_discussion, reopen_discussion,
3459
3448
  save_launch_defaults, launch_current_room, resume_launch, discard_launch, retry_launch_connection, check_launch_connection: load_launch_settings,
3460
3449
  load_room_configuration, save_room_configuration, interrupt_member, change_member, reorder_speakers, invite_member, set_proxy, copy_online_notice,
3461
- prepare_brainstorm, return_conclusion, convert_suggestion, adopt_recommendation, rate_decision,
3450
+ prepare_brainstorm, return_conclusion, adopt_recommendation, rate_decision,
3462
3451
  busy: () => { throw new Error("上一个操作仍在处理中,请稍候;长时间无响应请刷新页面"); },
3463
3452
  copy_reading: () => copy_text(`${location.origin}/r/${state.room.id}`), copy: copy_text };
3464
3453
  if (!["refresh", "busy"].includes(name)) state.free_error = "";