Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ This file defines the implementation boundary for agents working in `D:\GroupX`.
7. Natural-language mentions in model output do not trigger another CLI. Only an explicit GroupX tool call or a user UI routing command dispatches a new turn.
8. GroupX has no approval subsystem. If a native adapter emits an approval, permission, `requestUserInput`, question, or elicitation request, always fail the current Turn with `UNEXPECTED_NATIVE_INTERACTION` and perform bounded native cancellation/teardown. Do not relay it to the UI, persist a pending request, auto-decide it, or switch transport. `NATIVE_POLICY_BLOCKED` and public state `native_policy_blocked` are a separate path that requires an explicit external-policy preflight or native startup/session refusal; never infer them from an interaction request or its options.
9. Persist final semantic events and durable turn state. Do not persist every token delta.
10. Keep full transcript, curated public memory, per-Agent dated memory, generated summaries, configured Agent identity, and legacy identity records as distinct data classes.
10. Keep full transcript, curated public memory, per-Agent core memory, per-Agent dated memory, generated summaries, configured Agent identity, and legacy identity records as distinct data classes. Core memory is explicitly curated by the owning Agent or Web operator; dated memory is generated only from a successful Turn's bounded current message and final response. Reasoning and tool records never enter either layer.
11. Collect only fields defined by the GroupX data model. Do not intentionally ingest complete environment dumps, persist raw CLI configuration, or retain unbounded stderr. A bounded parser may read a native config file only to project explicitly allowlisted preflight fields; all other fields stay outside GroupX records and diagnostics. GroupX does not promise to detect or remove secrets that a user or model puts in ordinary message or memory content.
12. A2A is an optional edge adapter. Do not replace the internal GroupX Envelope with the full A2A task model in the first implementation.
13. v0.1 keeps `direct | structured` only as storage/history vocabulary; `structured` is the sole runnable product and release transport. Structured means Codex App Server over stdio plus Grok/Kimi ACP over stdio. Direct is deprecated: config parsing, Adapter factory, and runtime construction must reject it before opening runtime resources. Direct source and historical records remain only for audit/migration compatibility; do not restore a runtime entry, feature work, native-live Gate, release claim, or fallback. GroupX MCP current-Turn Agent calls are available only in structured mode.
Expand Down
272 changes: 124 additions & 148 deletions README.md

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions docs/ACCEPTANCE_TESTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
| G-006 | 重启后 queued Turn transport snapshot 不同 | `TRANSPORT_MODE_MISMATCH`,不跨 transport 派发 |
| G-007 | Kimi 官方默认全局配置 | global permission=`manual` 或配置键缺省不阻断 Structured ACP;new/load 后在首 prompt 前成功发送 `session/set_mode(auto)` |
| G-008 | Kimi session mode 负向 | `session/set_mode(auto)` 的明确 native policy 拒绝为 `NATIVE_POLICY_BLOCKED`;普通协议失败按 Adapter 错误收敛;不写全局配置、不 fallback |
| G-009 | 重复 `groupx start` | 同 config path + canonical config 的现有 runtime 被识别并复用,命令返回成功且不打开 Store/Adapter;另一 config、旧版 GroupX 或非 GroupX listener 明确报端口冲突,不杀进程、不换端口 |
| G-010 | 并发启动 bind 竞态 | 预检后 `EADDRINUSE` 会有界复查;同 key 赢家按复用成功,无法识别的 listener 返回友好错误,失败方不修改现有 binding/session |

精确 native profile:

Expand Down Expand Up @@ -158,12 +160,12 @@ binding 是 provenance/correlation handle,不是 secret、token 或本机抗
| I-011 | native reasoning delta 后刷新/重连 | delta 不逐条落库;terminal transaction 生成最多一条 `turn.reasoning.recorded`,按 seq 在 response/terminal 前回放 |
| I-012 | native tool started/completed 后刷新/重连 | live `tool.progress` 保持 transient;terminal transaction 生成 `tool.progress.recorded`,同一 `turnId + toolCallId` 仍合并为气泡内折叠记录 |

## 11. 公共记忆与身份记忆
## 11. 公共、核心、日期与身份记忆

| ID | 用例 | 通过标准 |
| --- | --- | --- |
| M-001 | 用户固定公共记忆 | author/source 可追溯,重启后可检索 |
| M-002 | 普通聊天 | 不自动升级 MemoryRecord |
| M-002 | 普通聊天 | 不自动升级公共/core MemoryRecord;仅成功 Agent Turn 自动形成自身 dated record |
| M-003 | Structured MCP memory.remember | author 来自 binding,不能冒充他人 |
| M-004 | supersede/retract | 追加版本/tombstone,不原地抹除 |
| M-005 | 用户身份记录 | subject 可选 Agent,author 固定 user:web |
Expand All @@ -175,6 +177,9 @@ binding 是 provenance/correlation handle,不是 secret、token 或本机抗
| M-011 | 摘要与 cursor 原子边界 | 只有已持久且嵌入 attempt 的摘要可写 `summary_through_seq`,native start 确认后才推进 `last_summary_seq` |
| M-012 | 压缩失败 | 尝试后续健康 Agent;全失败则 Turn 明确失败,原 transcript、旧摘要和 cursor 不变 |
| M-013 | durable reasoning/tool records | 时间线可回放,但 Context Packet、reply chain、压缩输入与自动记忆均不包含其正文 |
| M-014 | Structured `core_memory_remember` | wire 不含 scope/subject/author/binding;Broker 固定为调用 Agent 自己的 `agent_memory_type=core`,重试幂等 |
| M-015 | 成功/失败 Turn 的日期记忆 | completed 的 response/terminal/dated memory 同事务且仅一份;failed/cancelled/interrupted 不写;dated 只含有界当前消息和最终回复 |
| M-016 | Context Packet 两层记忆 | core 优先于其他可选区段;dated 按 actor 隔离,来源回复仍在 unread/reply chain 时不重复注入 |

## 12. Web 与本地传输

Expand All @@ -191,6 +196,8 @@ binding 是 provenance/correlation handle,不是 secret、token 或本机抗
| W-009 | 多实例名册 | 可添加两个以上 Codex App Server,稳定 id 唯一,name/cwd 独立;保存后 runtime 各有独立 actor/binding/session |
| W-010 | 运行中 Agent 设置 | `/setup` 载入现有名册,保存返回 restartRequired;不热换当前 session,不出现 access/approval/sandbox 控件 |
| W-011 | `groupx update` | 查询 npm latest;已最新/本地更高不安装,`--check` 无副作用,有更新时锁定精确版本并通过 shell-free npm 入口全局安装 |
| W-012 | 单房间上下文控件 | 输入窗口右上角显示明确标注的字符估算;手动压缩经 Broker/clientCommandId 单飞,保留最近消息与完整 transcript,reasoning/tool 记录不进入摘要 |
| W-013 | Agent 设置两层记忆 | core 独立展示并可维护;dated 只读按本地日期分组并允许显式移除;公共记忆仍在群聊左栏 |

loopback 与 binding 是产品范围/来源合同,不是认证或安全保证。

Expand Down
35 changes: 35 additions & 0 deletions docs/DECISIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
| D-015 | GroupX 不构成安全边界,也不实现审批子系统 | Accepted |
| D-016 | 所选 transport 派发不确定时绝不自动重放或切换 | Accepted |
| D-017 | 配置驱动的 Agent 名册(driver/name/自定义 id)与 npm CLI 分发 | Accepted |
| D-018 | 推理记录可回放但不进入上下文 | Accepted |
| D-019 | 工具进度可回放但不进入上下文 | Accepted |
| D-020 | `groupx start` 复用同配置的现有 runtime | Accepted |
| D-021 | Agent 记忆拆分为显式核心记忆与自动日期记忆 | Accepted |

## D-001:透明 Broker

Expand Down Expand Up @@ -126,6 +130,8 @@ Direct/Kimi one-shot 的旧 preflight 规则只保留作历史实现说明,不

Room Context Engine 使用累计 checkpoint summary + 近期真实消息,默认 `256,000` 字符硬上限、约 `75%` 的压缩软目标。预算不是 token window;不同 CLI/模型窗口无需伪装成相同。触发压缩时按房间配置顺序使用第一个健康 Agent,不可用或输出无效时再尝试下一个。摘要持久化、attempt `summary_through_seq` 与 delivery cursor 的推进顺序必须保证:失败只会让当前 Turn 明确失败,绝不会以裁剪历史换取继续运行。

单房间 UI 可显示 checkpoint + 未压缩消息的字符估算并显式请求压缩。该命令仍由 Broker 接收、以 `clientCommandId` 单飞,复用自动压缩的摘要 CAS,保留最近 12 条消息;它不是清空会话或删除 transcript,也不把字符估算称为 token 数。

## D-009:并发

决定:每个 Structured Agent lane FIFO 单飞;不同 Agent 并行。`@all` 不串行等待。
Expand Down Expand Up @@ -243,6 +249,35 @@ runtime 启动时把名册中的自定义/改名 agent upsert 进 actors 表,

迁移与回滚:不新增表或列。旧 reader 可忽略未知 event type;回滚停止生成新记录即可,既有事件仍不会成为上下文输入。

## D-020:`groupx start` 是幂等的启动或复用

决定:loopback HTTP listener 继续是单 runtime 的原子租约,CLI 不新增第二套锁文件。正式 runtime 的 `GET /api/health` 返回 `service="groupx"`、`protocol="groupx.runtime/1"` 和 `runtimeKey`;key 由 canonical config 与 canonical config path 做 SHA-256,只用于判断重复启动是否指向同一配置,不是 secret 或认证机制。

CLI 在创建 Store、Adapter 和 native session 前先探测目标 origin:

1. service/protocol/key 全部相同:打印现有 URL、按 `--no-open` 决定是否打开页面,然后以成功状态退出;
2. GroupX key 不同、旧版/不兼容 GroupX 或其他 HTTP listener:明确提示冲突;
3. listener 不可达:正常尝试 `listen`;若发生 `EADDRINUSE`,最多有界复查三次以收敛并发启动竞态;
4. GroupX 永不自动杀掉占用进程,也不自动选择下一个端口,因为那可能创建两个 Broker 共用一份 SQLite 与 native session lineage。

回滚边界:移除 CLI 预检只会恢复原始 `EADDRINUSE` UX;健康响应新增字段是向后兼容的 JSON 扩展,不改变 REST 写合同、数据库 schema 或 Agent 协议。

## D-021:Agent 核心记忆与按日期记忆分层

决定:每个 Agent 的私有记忆拆成两个不能互相冒充的数据层:

- `core` 是少量、长期、显式维护的核心记忆。Structured Agent 通过绑定到自身的 `core_memory_remember` 工具主动写入;工具输入不接受 scope、subject、author 或 binding,Broker 一律从当前 session binding 固定为调用 Agent 自己。Web Agent 设置可以追加、替换和撤回同一 Agent 的 core;
- `dated` 是成功 Turn 的自动工作记录。Broker 在成功 terminal transaction 内,从该 Turn 有界的当前 `message.created` 与最终 Agent response 构造一条 dated 记录,并追加对应 `memory.remembered` durable event。失败、取消、中断或终态不明的 Turn 不生成 dated;
- dated 的日期来自记录 `created_at`,不是模型提供的正文或参数。自动记录不读取 reasoning、tool、stderr、native payload、公共记忆或完整历史;
- Context Packet 分别投影 `[agent_core_memory]` 与 `[agent_dated_memory]`。core 在可选记忆区段中优先;dated 受字符预算约束,并在其来源回复已由当前消息、reply chain 或未读 transcript 表达时去重;
- 公共房间记忆继续使用 room scope,和两个 Agent 层完全分离。普通聊天不会自动成为公共或 core MemoryRecord。

持久化:schema v6 在 `memory_records` 增加仅对 `scope_type=agent` 有效的 `agent_memory_type=core|dated`。升级前已有 Agent 记忆保守迁移为 `core`;room/correlation 记录保持空值。supersede 必须保留原层,不能借替换把 dated 转成 core 或反向转换。

原因:核心记忆需要 Agent 主动筛选,自动日期记忆需要保留连续工作事实;混为一类会让自动摘要稀释长期偏好,也会让 Web/工具误把系统生成记录当成 Agent 明确承诺。把自动写入放在 terminal transaction 内可避免成功响应与日期记忆部分提交,同时保持 Broker 唯一写者。

回滚边界:停止生成新 dated 或隐藏 core tool 不会破坏已有记录。回滚代码仍必须把未知 `agent_memory_type` fail-closed,不能把 dated 无条件当 core 注入;若降级到 schema v5,需要显式导出/重建数据库,不做破坏性原地降级。

## 决策变更规则

任何 Accepted 决策变更必须同时提供:
Expand Down
28 changes: 17 additions & 11 deletions docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ SQLite/WAL 保存:
Memory Service 提供:

- 显式记忆写入;
- 绑定到调用 Agent 自身的核心记忆写入;
- 成功 Turn 的自动日期记忆归档;
- 作用域与来源校验;
- supersede/retract;
- 按 scope/kind/text/cursor 检索;
Expand All @@ -236,11 +238,12 @@ groupx.ask
groupx.read
groupx.memory.search
groupx.memory.remember
groupx.core_memory_remember
groupx.identity.read
groupx.identity.remember
```

工具调用方来自 session binding。Agent 只能以自己的 actor 身份写 identity recordWeb UI 可以为任意 Agent 写用户来源的 identity record。
工具调用方来自 session binding。`core_memory_remember` 没有 scope/subject/author 输入,只能写调用 Agent 自己的 core;Agent 也只能以自己的 actor 身份写 identity recordWeb UI 可以为任意 Agent 写用户来源的 identity/core record。

`groupx.send` 持久化后异步返回;`groupx.ask` 等待目标 terminal response 并将结果带回当前 CLI 回合;`groupx.read` 查询异步 correlation。同步 ask 遇到 active causal stack 中的祖先 Agent 时返回 `CAUSAL_CYCLE`,避免相互等待死锁。

Expand All @@ -254,8 +257,10 @@ REST 负责有副作用的用户命令;SSE 负责服务端事件流。

| Method | Path | 作用 |
| --- | --- | --- |
| GET | `/api/health` | Broker、数据库和 Adapter 健康 |
| GET | `/api/health` | runtime 身份、Broker、数据库和 Adapter 健康 |
| GET | `/api/bootstrap` | 当前房间投影、Agent、游标和能力摘要 |
| GET | `/api/context` | 当前单房间的 Context Packet 字符预算估算 |
| POST | `/api/context/compact` | 显式滚动压缩较早消息,保留近期原文和完整 transcript |
| GET | `/api/events?afterSeq=` | SSE 增量事件;支持 `Last-Event-ID` |
| POST | `/api/messages` | 用户定向消息或 `@all` |
| POST | `/api/turns/:id/cancel` | 请求原生取消 |
Expand All @@ -279,10 +284,11 @@ M1 UI 使用原生 HTML/CSS/TypeScript,避免在首版引入大型框架。

- 左侧:Agent 状态、cwd、会话状态、能力、重启按钮和可折叠的公共记忆;
- 中间:群聊、发送者徽标、reply/forward、目标选择和取消;
- Agent 设置:每个 Agent 的稳定身份与按日期分组的独立记忆;不保留右侧记忆栏;
- 底部:composer,明确选择 `@codex/@grok/@kimi/@all`。
- Agent 设置:每个 Agent 的稳定身份、可维护的核心记忆与按日期分组的自动记忆;不保留右侧记忆栏;
- 底部:composer,左侧明确选择 `@codex/@grok/@kimi/@all`,输入区域右上角显示字符用量并提供“压缩会话”

UI 只根据 Envelope actor 渲染发送者,不解析正文决定头像或身份。
用量控件显示的是 active checkpoint 加其后 `message.created` 的保守字符估算,不是模型 token window;目标 Agent 身份、记忆与原生 instructions 仍会另占空间。手动压缩同样经过 Broker 和 `clientCommandId` receipt,保留最近 12 条消息原文,不删除 transcript。
`tool.progress` 按 `turnId + toolCallId` 归并到对应 Agent 的会话气泡,started/completed 更新同一条;默认只显示一行工具名与状态,用户点击“展开”后才显示受限长度的结构化详情。terminal 时同形投影写成 `tool.progress.recorded`,刷新后仍走同一归并/折叠路径。两种事件都不得回退成独立的全量 JSON 事件卡,也不得进入 Agent 上下文。
首版将模型输出作为普通文本渲染,不执行其中的 HTML/脚本。服务器只绑定 loopback;GroupX 不在其上叠加认证、Origin 防护或浏览器安全策略。

Expand Down Expand Up @@ -345,12 +351,12 @@ GroupX 只承诺命令接收与派发记录幂等,不虚构模型执行的 exa
每个 Adapter 维护 `lastDeliveredSeq`。一次 prompt 的 Context Packet 最多包含:

1. GroupX 协议与 Agent 设置中的稳定身份;
2. 与当前 actor 相关的兼容身份记录
3. 当前 Agent 的独立记忆(按 `created_at` 日期展示、按 actor scope 注入);
4. 当前有效的固定公共记忆
5. 自上次投递后的相关消息增量
6. 当前目标消息和 reply chain
7. 必要时的持久滚动摘要检查点
2. 当前目标消息、完整 reply chain 与必要的持久滚动摘要检查点
3. 当前 Agent 的核心记忆(`agent_memory_type=core`,可选区段中最高优先级);
4. 自上次投递后的相关消息增量
5. 当前 Agent 的自动日期记忆(`agent_memory_type=dated`;若来源回复已在增量/reply chain 中则去重)
6. 当前有效的固定公共记忆
7. 与当前 actor 相关的兼容身份记录

不把完整数据库或完整房间历史重复注入每个 turn。默认 Context Packet 硬上限是 `256,000` 字符,Room Context Engine 以其约 `75%`(默认 `192,000` 字符)作为压缩软目标,给原生 instructions、工具调用和回复留余量;这是跨 Agent 的确定性字符预算,不等同于某个模型的 token window(例如 Codex UI 可能显示约 258k tokens)。用户可通过 `limits.contextCharacters` 覆盖硬上限。

Expand Down Expand Up @@ -460,7 +466,7 @@ Agent `enabled` 默认 true;`enabled: false` 的 agent 不建 Adapter、不进

`groupx init` 启动一个临时 loopback 引导服务并打开浏览器;首次 `groupx start` 没有配置时复用同一流程。引导页可创建多个相同 driver 实例并填写 id/name/cwd/command;保存严格配置后,CLI 启动正式 runtime,临时服务通过同源 launch 状态通知当前页面,并在正式服务 ready 后自动跳转到群聊。运行中的 `/setup` 使用同一合同编辑名册;保存只更新配置文件并提示重启,不自动跳转,也不在运行时热增删 Adapter/session。setup API 不暴露 transport、access、approval 或 sandbox 字段。

正式 runtime 必须先成功绑定配置的 loopback HTTP 端口,才能执行 stale Agent instance/session recovery。该监听是单 runtime 启动租约:若端口已被另一个 GroupX 占用,新进程以 `EADDRINUSE` 失败且不得把现有 runtime 的 ready binding 标成 interrupted,也不得留下永久 queued Turn。
正式 runtime 必须先成功绑定配置的 loopback HTTP 端口,才能执行 stale Agent instance/session recovery。该监听是单 runtime 启动租约。`GET /api/health` 同时返回固定 `service=groupx`、协议版本和由 canonical config + canonical config path 生成的非秘密 `runtimeKey`。CLI 在构造 SQLite/Adapter 前探测该身份:相同 key 直接复用现有页面并成功退出;不同 key、旧版 GroupX 或非 GroupX listener 明确报冲突,不杀进程、不自动换端口。预检与 bind 之间仍可能竞态,因此实际 `EADDRINUSE` 后最多有界复查三次;若竞态赢家是同一 key,同样按复用成功收敛。失败进程不得把现有 runtime 的 ready binding 标成 interrupted,也不得留下永久 queued Turn。

公开配置没有 `access` 字段。`access` 在 v0.1 内部恒为 unrestricted;Adapter 根据 Agent + transport 生成固定 argv/session mode。用户不能通过 `extraArgs` 改写、删除或替换这些访问参数。

Expand Down
Loading