ai_co_discuss 0.2.21 → 0.2.22

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.
@@ -239,7 +239,7 @@ running 状态即使只有 pending 事件,也必须先暂停才能重新启动
239
239
 
240
240
  - **凭据**:建房时由客户端提供房间控制凭据和每位成员的成员凭证,服务端只存哈希。用户身份的房间写入(发言、附件、存档、恢复、删除、打断)都须控制凭据;房间排序和归入文件夹只改共享目录,与站点级文件夹操作一样不需要凭据;无凭据、错误凭据、成员凭证或他房凭据一律只读。成员凭证只能以该成员身份发言或上传附件。声明字段(`author`、`source`、localhost)不代替凭据。
241
241
  - **成员**:`room_members` 以 `id` 为稳定主键,`access` 为 `runner`(接入程序投递)、`manual`(成员自行读写)或 `online`(只读、原文代发),`session_mode` 标记是否借用用户正在使用的会话。房主用控制凭据 `POST members` 运行中加入成员,`POST members/ID {action}` 让成员离开(`away`,作废其进行中执行与未投递请求)、请回(`active`,必须提供不同于原凭证的新凭证,旧凭证副本不再有效)或移除(`remove`,保留历史发言)。同一成员凭证不能复用于其他成员或房主;相同凭证与配置的邀请、请回重试返回已有结果。会话续接由本机接入程序负责,服务端不保存会话绑定。
242
- - **请求**:用户消息按结构化接收者 `to`(成员名)为每位成员生成一条请求;省略且仅有一位在场成员时默认点名该成员,多位成员时交给主持人(`route`),只有在线成员而没有主持人时只作留言,不广播。网页从点名按钮和正文中代码与引用之外的 `@成员` 生成 `to`。请求状态为 `pending`(待投递)→ `delivering`(处理中)→ `completed` / `cancelled` / `failed`。
242
+ - **请求**:用户消息按结构化接收者 `to`(成员名)为每位成员生成一条请求;省略且仅有一位在场成员时默认点名该成员,多位成员时交给主持人(`route`),只有在线成员而没有主持人时只作留言,不广播。网页只从正文中代码与引用之外的 `@成员` 生成 `to`,没有 @ 时省略 `to`。请求状态为 `pending`(待投递)→ `delivering`(处理中)→ `completed` / `cancelled` / `failed`。
243
243
  - **执行**:`member_executions` 每位成员最多一个 `dispatched/started`(部分唯一索引),不同成员可并行。接入程序用控制凭据 `GET inbox`、`POST executions {member_id, client_id}`(同一 `client_id` 返回同一执行)把成员的全部待投递请求打包;`POST executions/ID {action}` 支持 `renew`(租约 120 秒,不超过单次执行时限,默认 20 分钟)、`fail`(请求标记失败)和 `release`(仅确认未送达时退回队列)。租约过期的执行在下次读取或领取时标记失败,不自动重发。
244
244
  - **主持人与任务链**:用户是需求方,不担任主持人。只要有在场的非在线成员,房间就保持恰好一位主持人:成员变动时,离开或移出的主持人失去角色,由第一位在场的 runner 成员(没有则手动成员)接任;建房和每条房主消息前也做同样的补位,因此旧房间在下一条消息时自动获得主持人。房主用 `POST members/ID {action:"role", role:"host"}` 更换主持人(在线成员不能担任),把现任主持人改为 `member` 会被拒绝。多位成员时未点名的房主消息生成给主持人的 `route` 请求。主持人的回答可带 `to` 点名其他成员(不能点名自己),这些请求组成一个批次(`free_batches`);批次内全部进入终态后,只要有一个完成就给主持人生成一次 `handoff` 请求,全部失败则停下该任务链。每条房主消息开启一条任务链(`chain_id` 为该消息编号),链上 AI 发起的请求(派活与回交)最多 8 次,超出即停下(`free_chains`,原因 `budget` / `all_failed`),房主发新消息开启新链。Worker 用同一批次内的集合 SQL 完成回交,回交请求按(派活消息,主持人)唯一,重复执行不会重复唤醒。
245
245
  - **回应建议**:普通成员新发言的结构化 `to` 与正文在场成员 @ 合并保存到 `member_suggestions`(原消息编号、稳定成员编号),排除自己、代码与引用;不会创建请求。消息读取返回 `suggestions`,包含目标在场状态及已转换的请求编号。房主用 `POST suggestions {message_id,member_id}` 转换:同一事务记录用户确认消息,调用现有建请求路径,`message_id` 保留原回答、`chain_id` 使用用户确认消息编号、`created_by=user`。`UNIQUE(message_id,member_id)` 使重复或并发点击只生成一个请求及一条确认,终态不重新派发。权限、在场状态和未存档条件在写入事务内校验;Worker 使用批次断言。升级须应用包含新表的 schema;Python 重启时自动建表,D1 发布前显式执行。已有历史消息不补写建议。
package/docs/workflow.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  成员回答下的执行报告列出请求编号、耗时、终态与工作区观察文件;失败或取消的报告保留在原请求下。报告展示最近 100 次执行;“未提供自动改动检测”表示非 Git 目录或未上报,不能据此判断没有文件变化。
16
16
 
17
- 自由协作房间从同一角色库选择一位或多位成员。用户是需求方而不是主持人:只要有能自行发言的成员(本机唤醒或手动接入),房间就始终有一位 AI 主持人,默认是第一位本机唤醒的成员,可点皇冠或在成员菜单中更换;主持人不能直接卸任,离开或被移出时由下一位成员接任,原先由用户主持的旧房间在下一条消息时自动指定。只有一位成员时,每条消息都交给它;有多位时,在输入框上方点选成员,或在正文中 `@成员`(代码和引用中的 @ 不算,输入框中会生效的 @ 显示为蓝色),被点名的成员各自处理;未点名的消息交给主持人分派。只有网页 AI 成员时没有主持人,未点名的消息只作留言。输入 `@` 后,输入框上方弹出在场成员菜单,继续输入可筛选;用方向键与 Enter / Tab 或点击补全,Esc 关闭。选择只修改草稿。输入框 Enter 发送、Shift + Enter 换行;成员处理中且草稿为空时,发送按钮变为红色停止按钮。自动成员空闲时,服务把它名下所有未处理的消息打包成一次执行交给原会话;执行期间再发的消息排队,下一次一起投递。手动成员自行读取自己的请求,在线成员由用户转交。成员按时间顺序处理,后面的消息可以修正前面的。
17
+ 自由协作房间从同一角色库选择一位或多位成员。用户是需求方而不是主持人:只要有能自行发言的成员(本机唤醒或手动接入),房间就始终有一位 AI 主持人,默认是第一位本机唤醒的成员,可点皇冠或在成员菜单中更换;主持人不能直接卸任,离开或被移出时由下一位成员接任,原先由用户主持的旧房间在下一条消息时自动指定。只有一位成员时,每条消息都交给它;有多位时,你的消息默认发给主持人;在正文中 `@成员` 才让其他成员发言(代码和引用中的 @ 不算,输入框中会生效的 @ 显示为蓝色),被点名的成员各自处理,主持人也可以 @ 其他成员派活。输入框下方的备注行显示这条消息会发给谁。只有网页 AI 成员时没有主持人,未点名的消息只作留言。输入 `@` 后,输入框上方弹出在场成员菜单,继续输入可筛选;用方向键与 Enter / Tab 或点击补全,Esc 关闭。选择只修改草稿。输入框 Enter 发送、Shift + Enter 换行;成员处理中且草稿为空时,发送按钮变为红色停止按钮。自动成员空闲时,服务把它名下所有未处理的消息打包成一次执行交给原会话;执行期间再发的消息排队,下一次一起投递。手动成员自行读取自己的请求,在线成员由用户转交。成员按时间顺序处理,后面的消息可以修正前面的。
18
18
 
19
19
  运行中可在右侧成员栏邀请 AI(沿用本机软件配置和本房间工作目录),也可让成员离开、请回或移除。自动成员的项目文件权限默认只读;需要它修改项目时,在邀请前选择“项目文件可写”,权限只应用于这位成员;App 与手动成员的文件权限由其软件设置,API 与网页成员不访问项目文件。填写已有会话 ID 时续接该原会话,不新建。离开会立即停止它的工作;请回时换发新凭证并续接它原来的会话。邀请时可选“手动接入”:成员不会被自动唤醒,而是得到一份私有凭据文件,由你在它的会话中按参与技能读写。绑定你正在使用的会话的成员会标注“借用会话”:它工作时不要在原窗口继续操作。
20
20
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai_co_discuss",
3
- "version": "0.2.21",
3
+ "version": "0.2.22",
4
4
  "bin": {
5
5
  "codiscuss": "runner/codiscuss.mjs"
6
6
  },
package/web/app.js CHANGED
@@ -37,7 +37,7 @@ const PLATFORM_IDENTITIES = {
37
37
  Grok: { initials: "GK", style: "grok", description: "AI 身份" },
38
38
  };
39
39
  const IDENTITIES = by_platform(PLATFORM_IDENTITIES, (identity, name) => ({ ...identity, label: name, description: "AI 实例" }));
40
- const state = { rooms: [], folders: [], room: null, page: 1, pages: 1, query: "", mention_only: false, free_to: [], free_notice: "", free_error: "",
40
+ const state = { rooms: [], folders: [], room: null, page: 1, pages: 1, query: "", mention_only: false, free_notice: "", free_error: "",
41
41
  free_proxy: "", create_parent: null,
42
42
  archive_view: false, directory_sort: "manual", management: null, dragged_room: null, folder_moves: new Map(), folder_version: 0,
43
43
  reply_to: null, turn_reply_to: null, tab: "topic", refresh_version: 0, context_dirty: false, topic_dirty: false,
@@ -795,8 +795,9 @@ function message_payload() {
795
795
  request_ids: state.room.free.requests.filter(item => item.member === online.name && item.status === "pending").map(item => item.id) };
796
796
  const payload = { author: "用户", text, reply_to: state.reply_to, kind: "chat", submitted_by: "用户" };
797
797
  const active = (state.room.free?.members || []).filter(member => member.status === "active").map(member => member.name);
798
- // One member is named by default; with several, the chips and @mentions outside code decide, never a broadcast.
799
- if (active.length > 1) payload.to = active.filter(name => state.free_to.includes(name) || mentioned(text, name));
798
+ // @mentions outside code name members; without any, the server gives the message to the only member or the host.
799
+ const named = active.filter(name => mentioned(text, name));
800
+ if (named.length) payload.to = named;
800
801
  return payload;
801
802
  }
802
803
  const mode = element("message-kind").value;
@@ -820,11 +821,6 @@ function mentioned(text, name) {
820
821
  return new RegExp(`(^|[\\s((,,。;;::])@${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\w-])`, "u").test(prose);
821
822
  }
822
823
 
823
- /** Toggle one member chip of the free-room composer. */
824
- function toggle_recipient(name) {
825
- state.free_to = state.free_to.includes(name) ? state.free_to.filter(item => item !== name) : [...state.free_to, name];
826
- }
827
-
828
824
  /** Choose an online member to forward the next answer for, or clear the choice. */
829
825
  function set_proxy(name) { state.free_proxy = state.free_proxy === name ? "" : name; }
830
826
 
@@ -2791,7 +2787,7 @@ function view_snapshot() {
2791
2787
  attachment_drafts: state.attachment_drafts.map(item => ({ id: item.id, file_name: item.file.name, size: item.file.size, preview_url: item.preview_url })),
2792
2788
  composer_error: state.composer_error, upload_status: state.upload_status, sending_message: state.sending_message,
2793
2789
  mention_only: state.mention_only, reply_to: state.reply_to, turn_reply_to: state.turn_reply_to,
2794
- free_to: [...state.free_to], free_notice: state.free_notice, free_error: state.free_error, free_proxy: state.free_proxy,
2790
+ free_notice: state.free_notice, free_error: state.free_error, free_proxy: state.free_proxy,
2795
2791
  create_parent: state.create_parent, access: access_snapshot(),
2796
2792
  tab: state.tab, snapshot_id: state.snapshot_id, context_dirty: state.context_dirty,
2797
2793
  theme: document.documentElement.dataset.theme, fields, experts, launch: structuredClone(state.launch) };
@@ -2965,7 +2961,7 @@ async function execute_view_action(name, value) {
2965
2961
  copy_online: () => copy_context(), copy_cli: () => copy_context("cli"), copy_background,
2966
2962
  choose_runtime, export_runtime, download_member_access, copy_member_join, pause_runtime, check_limits, pause_discussion, restart_discussion, force_close, conclude_discussion,
2967
2963
  save_launch_defaults, launch_current_room, resume_launch, discard_launch, retry_launch_connection: load_launch_settings,
2968
- load_room_configuration, save_room_configuration, interrupt_member, toggle_recipient, change_member, invite_member, interrupt_free, set_proxy, copy_online_notice,
2964
+ load_room_configuration, save_room_configuration, interrupt_member, change_member, invite_member, interrupt_free, set_proxy, copy_online_notice,
2969
2965
  prepare_brainstorm, return_conclusion, convert_suggestion,
2970
2966
  copy_reading: () => copy_text(`${location.origin}/r/${state.room.id}`), copy: copy_text };
2971
2967
  try {