ai_co_discuss 0.2.63 → 0.2.64

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/server.py CHANGED
@@ -738,7 +738,7 @@ def start_discussion(connection, room_id, token, payload):
738
738
  if room["archived"]:
739
739
  raise ValueError("房间已存档;恢复后才能开启讨论")
740
740
  sync_participants(connection, room_id)
741
- active = active_members(connection, room_id)
741
+ active = active_members(connection, room_id, registered_only=False)
742
742
  host = next((row for row in active.values() if row["role"] == "host"), None)
743
743
  if not host or host["access"] != "runner":
744
744
  raise ValueError("开启讨论需要一位由本机托管的 AI 主持人;先在成员栏指定或转为自动")
package/docs/workflow.md CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
  自由协作房间从同一角色库选择一位或多位成员。用户是需求方而不是主持人:只要有能自行发言的成员(本机唤醒或手动接入),房间就始终有一位 AI 主持人,默认是第一位本机唤醒的成员,可点皇冠或在成员菜单中更换;主持人不能直接卸任,离开或被移出时由下一位成员接任,原先由用户主持的旧房间在下一条消息时自动指定。只有一位成员时,每条消息都交给它;有多位时,你的消息默认发给主持人;在正文中 `@成员` 才让其他成员发言(代码和引用中的 @ 不算,输入框中会生效的 @ 显示为蓝色),被点名的成员各自处理,主持人也可以 @ 其他成员派活。输入框下方的备注行显示这条消息会发给谁。只有网页 AI 成员时没有主持人,未点名的消息只作留言。输入 `@` 后,输入框上方弹出在场成员菜单,继续输入可筛选;用方向键与 Enter / Tab 或点击补全,Esc 关闭。选择只修改草稿。输入框 Enter 发送、Shift + Enter 换行;发送按钮始终用于发言,无正文或附件时禁用,成员处理中也不切换为停止按钮。自动成员空闲时,服务把它名下所有未处理的消息打包成一次执行交给原会话;执行期间再发的消息排队,下一次一起投递。手动成员自行读取自己的请求,在线成员由用户转交。成员按时间顺序处理,后面的消息可以修正前面的。
27
27
 
28
- 运行中可在右侧成员栏邀请 AI(沿用本机软件配置和本房间工作目录),也可让成员离开、请回或移除。自动成员的项目文件权限默认只读;需要它修改项目时,在邀请前选择“项目文件可写”,权限只应用于这位成员;App 与手动成员的文件权限由其软件设置,API 与网页成员不访问项目文件。填写已有会话 ID 时续接该原会话,不新建。离开会立即停止它的工作;请回时换发新凭证并续接它原来的会话。邀请时可选“手动接入”:成员不会被自动唤醒,而是得到一份私有凭据文件,由你在它的会话中按参与技能读写。绑定你正在使用的会话的成员会标注“借用会话”:它工作时不要在原窗口继续操作。
28
+ 运行中可在右侧成员栏邀请 AI(沿用本机软件配置和本房间工作目录),也可让成员离开、请回或移除。自动成员的项目文件权限默认只读;需要它修改项目时,在邀请前选择“项目文件可写”,权限只应用于这位成员;App 与手动成员的文件权限由其软件设置,API 与网页成员不访问项目文件。填写已有会话 ID 时续接该原会话,不新建。离开会立即停止它的工作;请回时换发新凭证并续接它原来的会话。邀请时可选“手动接入”:成员不会被自动唤醒,而是得到一份私有凭据文件,由你在它的会话中按参与技能读写。自由协作房间里,手动成员执行 `member register` 之前视为不存在:不能被点名,不收请求,不进“全体各自回应”和智能调度候选;注册后与其他成员一样可被点名,其 CLI 上报会话后本机服务按软件转为托管(Claude Code、Codex、agy),此后与自动成员无异。绑定你正在使用的会话的成员会标注“借用会话”:它工作时不要在原窗口继续操作。
29
29
 
30
30
  可以把一位本机成员设为主持人。之后没有点名的消息交给主持人分配;主持人在回答中点名成员派活,只点名一位成员时,其回答直接面向用户,主持人不再被唤醒;点名多位成员时,都结束后主持人再被唤醒一次,继续汇总或安排下一步。派出的成员全部失败时不再唤醒主持人;同一条消息引出的 AI 请求最多 8 次,用完即停,成员栏会提示,发一条新消息即重新开始。
31
31
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.2.63",
3
+ "version": "0.2.64",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
@@ -266,8 +266,9 @@ class RoomWorker {
266
266
  }
267
267
  /** A manual Claude or Codex expert that has registered and whose CLI reported its session is handed to the service, which resumes that session from the next turn on. */
268
268
  async adopt_manual_members() {
269
- if (this.config.mode === 'free') return;
270
- for (const member of this.runner.members || []) {
269
+ // A discussion runner lists members itself; a free room's registered manual members come from the room view.
270
+ const members = this.config.mode === 'free' ? (await api(this.config, '')).free?.members || [] : this.runner.members || [];
271
+ for (const member of members) {
271
272
  const key = `${member.name}:${member.reported_session}`;
272
273
  if (member.access !== 'manual' || member.status !== 'active' || !member.registered_at || !member.reported_session || this.adopt_failed.has(key)) continue;
273
274
  try {
package/web/app.js CHANGED
@@ -989,7 +989,7 @@ function reset_create_form() {
989
989
  state.create_parent = null;
990
990
  state.launch.create = true;
991
991
  state.launch.workspace = {};
992
- state.launch.scheduler = {};
992
+ state.launch.scheduler = default_scheduler();
993
993
  state.named_followup = false;
994
994
  state.create_discussion = false;
995
995
  state.launch.profiles = JSON.parse(JSON.stringify(state.launch.saved_profiles || {}));
@@ -1744,6 +1744,8 @@ async function load_launch_settings() {
1744
1744
  scheduler_providers: settings.scheduler_providers || {}, error: "",
1745
1745
  notice: local_connection ? "已连接本机服务,讨论保存在当前网站" : "" });
1746
1746
  Object.values(state.launch.profiles).forEach(fill_api_key);
1747
+ // Settings arrive after the form was reset; only an untouched draft gets the default.
1748
+ if (!Object.keys(state.launch.scheduler).length) state.launch.scheduler = default_scheduler();
1747
1749
  load_room_models();
1748
1750
  } catch (error) { state.launch.available = false; state.launch.room_models = null; state.launch.error = error.message; }
1749
1751
  finally { refresh_launch_recoveries(); render_launch_settings(); publish_view(); }
@@ -1953,6 +1955,15 @@ function write_create_discussion(value) {
1953
1955
  publish_view();
1954
1956
  }
1955
1957
 
1958
+ /** A fresh form turns smart scheduling on when some provider already has a saved key. */
1959
+ function default_scheduler() {
1960
+ const providers = state.launch.scheduler_providers || {};
1961
+ const provider = Object.keys(providers).find((name) => state.launch.api_keys?.[providers[name].base_url]);
1962
+ if (!provider) return {};
1963
+ const { base_url, models } = providers[provider];
1964
+ return { enabled: true, provider, model: models[0] || "", api_key_ref: state.launch.api_keys[base_url] };
1965
+ }
1966
+
1956
1967
  /** Change the smart scheduling draft; a new provider starts from its preset model and any key saved for its root. */
1957
1968
  function write_scheduler(field, value) {
1958
1969
  const scheduler = state.launch.scheduler;