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.
- package/backend/free_room.py +1 -1
- package/docs/workflow.md +1 -1
- package/package.json +1 -1
- package/web/app.js +32 -3
- package/web/modern.css +1 -1
- package/web/modern.js +247 -241
package/backend/free_room.py
CHANGED
|
@@ -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;
|
|
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
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(
|
|
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(
|
|
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
|
|