ai_co_discuss 0.3.30 → 0.3.31

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.
@@ -521,7 +521,7 @@ def describe_members(connection, room_id, token, payload):
521
521
 
522
522
 
523
523
  def member_labels(connection, room_id):
524
- """The owner's display labels of members by name; only screens use them, never addressing or credentials."""
524
+ """The owner's display labels of members by name; screens show them and the page turns a user's @label into the member name, never credentials."""
525
525
  return {row["name"]: row["label"] for row in connection.execute(
526
526
  "SELECT name,label FROM member_labels WHERE room_id=? ORDER BY name", (room_id,))}
527
527
 
package/docs/workflow.md CHANGED
@@ -15,7 +15,7 @@
15
15
 
16
16
  ## 自由协作房间
17
17
 
18
- - **点名**:`@成员` 后,它在自己的原会话中处理并回复,多位可并行。
18
+ - **点名**:`@成员` 后,它在自己的原会话中处理并回复,多位可并行。用界面上显示的昵称点名即可(如 `@Gemini`),输入 `@` 后从列表选择最方便。
19
19
  - **未点名的消息**:交给主持人分派;只有一位成员时直接交给它。
20
20
  - **主持人派活**:主持人点名一位成员时,该成员直接回复你;主持人需要接着安排(如做完再交评审)时会要求它做完交回,点名多位时都完成后主持人再汇总。你的一条消息最多让主持人推进 3 轮(主持人派活、成员回答、交回汇总为一轮),建房时在“推进方式”里或建房后在房间页的“协作设置”里调整(1–20 轮);到上限会停下等你,右栏点“继续”即接着推进。
21
21
  - **全体回答**:`@全体成员` 请所有成员各自回答,`@全体依次` 按发言顺序逐个回答。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.3.30",
3
+ "version": "0.3.31",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
package/web/app.js CHANGED
@@ -1036,10 +1036,13 @@ function message_payload() {
1036
1036
  const members = state.room.free?.members || [];
1037
1037
  const payload = { author: "用户", text, reply_to: state.reply_to, kind: "chat", submitted_by: "用户" };
1038
1038
  const active = members.filter(member => member.status === "active" && (member.access !== "manual" || member.registered_at)).map(member => member.name);
1039
+ // Members are named by the nickname shown on screen (or their name); the request carries the member name.
1040
+ const named_here = mentioned_members(text, active);
1041
+ const mentioned_member = name => named_here.has(name);
1039
1042
  const session = state.room.session;
1040
1043
  const session_live = session && ["active", "paused"].includes(session.status);
1041
1044
  if (session_live) {
1042
- const direct = active.filter(name => mentioned(text, name));
1045
+ const direct = active.filter(mentioned_member);
1043
1046
  if (direct.length) payload.to = direct;
1044
1047
  return payload;
1045
1048
  }
@@ -1047,7 +1050,7 @@ function message_payload() {
1047
1050
  // @全体依次 or @all-seq has them answer one after another, the host first); without any, the server gives the message to the only member or the host.
1048
1051
  const sequential = mentioned(text, "全体依次") || mentioned(text, "all-seq");
1049
1052
  const everyone = !sequential && (mentioned(text, "全体成员") || mentioned(text, "all"));
1050
- const named = sequential || everyone ? active : active.filter(name => mentioned(text, name));
1053
+ const named = sequential || everyone ? active : active.filter(mentioned_member);
1051
1054
  if (named.length) payload.to = named;
1052
1055
  if (sequential && named.length) payload.sequential = true;
1053
1056
  if (everyone && named.length) payload.order = "auto";
@@ -1056,6 +1059,32 @@ function message_payload() {
1056
1059
  return payload;
1057
1060
  }
1058
1061
 
1062
+ /**
1063
+ * How a member may be @named: by the nickname shown on screen, and always by its member name. A nickname shared with
1064
+ * another member (or equal to another member's name) is ambiguous and is not used, so the member name stays the only way.
1065
+ */
1066
+ function mention_handles(name, names) {
1067
+ const nickname = display_name(name);
1068
+ const taken = names.filter(other => other !== name).flatMap(other => [other, display_name(other)]);
1069
+ return nickname && nickname !== name && !taken.includes(nickname) ? [nickname, name] : [name];
1070
+ }
1071
+
1072
+ /**
1073
+ * The members a text @names, longest handle first: once "@Claude评审" names one member, the shorter "@Claude" inside it
1074
+ * does not also name another (Chinese nicknames have no space after them).
1075
+ */
1076
+ function mentioned_members(text, names) {
1077
+ let prose = text.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "").replace(/^>.*$/gm, "");
1078
+ const handles = names.flatMap(name => mention_handles(name, names).map(handle => ({ name, handle })))
1079
+ .sort((left, right) => right.handle.length - left.handle.length);
1080
+ const found = new Set();
1081
+ for (const { name, handle } of handles) {
1082
+ const pattern = new RegExp(`(^|[\\s((,,。;;::])@${handle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`, "gu");
1083
+ prose = prose.replace(pattern, (match, lead) => { found.add(name); return lead + " ".repeat(match.length - lead.length); });
1084
+ }
1085
+ return found;
1086
+ }
1087
+
1059
1088
  /** Whether plain text (not code or quotes) names a member with @, so a typed mention selects it. */
1060
1089
  function mentioned(text, name) {
1061
1090
  const prose = text.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "").replace(/^>.*$/gm, "");
@@ -4111,7 +4140,7 @@ window.discuss_view = { snapshot: view_snapshot, write_field: write_view_field,
4111
4140
  write_expert: write_view_expert, add_instance: add_view_instance, add_preset: add_preset_instance, platform_of, execute: execute_view_action, theme: change_view_theme,
4112
4141
  admin: { register: register_admin, login: login_admin, logout: logout_admin,
4113
4142
  update_profile: update_admin_profile, avatar_from_file: admin_avatar_from_file, validate: validate_admin_form },
4114
- request: request_json, rewind_plan, identities: IDENTITIES, display_name, sources: SOURCE_LABELS,
4143
+ request: request_json, rewind_plan, identities: IDENTITIES, display_name, mention_handles, mentioned_members, sources: SOURCE_LABELS,
4115
4144
  set_member_access, set_member_model, allow_denials, set_member_adapter, reload_room_models, limits_label, stop_reason_label, participant_model, participant_files, participant_session_hint, participant_access,
4116
4145
  turn_label, turn_guidance, round_groups, limit_rounds, progress_speaker, brief_meta, max_message_length: MAX_MESSAGE_LENGTH, provenance: message_provenance, format_time };
4117
4146