agent-supervisor-memory 0.2.41
Local-first MCP tools: workflow routing/spec + keyword memory (JSON) with optional toggles.
License: MIT
Requires Python: <4.0,>=3.10

# agent-supervisor-memory (MCP server)

本目录提供一个 **单进程** 的 Python MCP server（stdio），默认启用：

- `workflow_*`: 生成最小化 spec（lite/spike/heavy）+ 路由建议（模型/effort）
- `memory_*`: 个人记忆（JSON 存储，关键词+tags+key 命名空间检索；无向量数据库/无 embedding）

MCP server ID：`agent-supervisor-memory`（见 `mcp-tools/agent-supervisor-memory/src/agent_supervisor_memory/server.py`）。

代码结构（便于加载更小的上下文）：
- `server.py`: wiring（组合 config + services + tools）
- `config.py`: env/flag 解析（主前缀 `AGENT_SUPERVISOR_MEMORY_*`）
- `services/`: `dispatch.py` / `memory_store.py` / `policy.py` / `workflow_spec.py`
- `tools/`: `core.py` / `dispatch.py` / `memory.py` / `workflow.py`
- `utils/`: env/fs/text helpers（路径推断、tail 截断、verbosity 归一化）

## Quickstart

```sh
cd mcp-tools/agent-supervisor-memory
poetry install
poetry run agent-supervisor-memory
# 默认写入当前/最近的 .codex/codex-dispatch；如需跨多个 repo 共用 dispatch dir（避免 client cwd 不确定），显式传：
# poetry run agent-supervisor-memory --dispatch-dir "$HOME/.codex/codex-dispatch"
```

环境变量（优先顺序）：
- 推荐前缀：`AGENT_SUPERVISOR_MEMORY_*`（如 DISPATCH_DIR / MEMORY_PATH / POLICY_PATH / *_ENABLED / MEMORY_MAX_* / VERBOSE / VERBOSITY）

默认存储文件：

- Memory：优先使用“最近的祖先目录中已存在的 `.codex/`”下的 `.codex/memory.json`；若不存在，则用 `<cwd>/.codex/memory.json`
- Policy：同理，默认 `.codex/agent-policy.json`
- Dispatch：默认使用“最近的祖先目录中已存在的 `.codex/`”下的 `.codex/codex-dispatch/`；若不存在，则用 `<cwd>/.codex/codex-dispatch/`。也可通过 `--dispatch-dir` 指定一个 **base 目录**（推荐跨 repo 场景显式指定），server 会按项目自动分桶：
  - `<dispatch-dir>/<project_bucket>/<job_id>/{meta.json,stdout.jsonl,stderr.log,last_message.txt}`
  - `project_bucket` 会尽量从 `codex_dispatch_start(cwd=...)` 的 cwd 向上推断（优先 `.codex/`，其次 `.git/`），并用 hash 保证稳定且不会太长
  - `job_id` 形如 `<project_bucket>--<uuid>`

说明：Poetry 会统一管理虚拟环境与依赖，避免你本机 shell 的 `python/pip` alias 干扰。

### Dispatch 作业清理（pruning）

新增 CLI / env flags（默认关闭清理，仅启用“完成即清理”开关）：
- `--dispatch-prune-enabled`（默认 `false`）
- `--dispatch-prune-on-job-finish/--no-dispatch-prune-on-job-finish`（默认 `true`）
- `--dispatch-prune-on-startup`（默认 `false`，启用后 server 启动会遍历 `<dispatch-dir>/*/` 逐桶清理）
- `--dispatch-retain-max-jobs-per-bucket`（默认 `100`，超过后按时间保留最新的 N 个）
- `--dispatch-retain-max-age-days`（默认 `7`，超过天数优先清理）
- `--dispatch-retain-keep-failed/--no-dispatch-retain-keep-failed`（默认保留 failed）
- `--dispatch-prune-dry-run`（只打印，不删除）
- `--dispatch-prune-verbose`（stderr 打印筛选/删除详情）

删除规则：仅清理已完成/已取消（failed 取决于 keep-failed）；若 meta.pid 仍存活则跳过；每桶加 `.prune.lock` 避免并发；删除前先重命名为 `.__deleting__*`。

推荐用法：`agent-supervisor-memory --dispatch-prune-enabled --dispatch-prune-on-job-finish --dispatch-retain-keep-failed --dispatch-retain-max-age-days 7 --dispatch-retain-max-jobs-per-bucket 100`

## 如何引用（MCP client 注册）

不同 MCP client 的配置方式不同，但核心信息通常是：

- `command`: `uvx`
- `args`: `["agent-supervisor-memory", "...flags"]`

工具名引用方式是 `tool name`，例如：`memory_search`、`workflow_route`。

### 参考配置片段（TOML 风格）

如果你的 client 支持类似下面这种配置（你贴的 `exa` 例子就是这种风格），可以这样写：

```toml
[mcp_servers.supervisor_memory]
command = "uvx"
args = ["agent-supervisor-memory"]
# 或显式指定 dispatch dir（推荐跨 repo 场景）
# args = ["agent-supervisor-memory", "--dispatch-dir", "/ABS/PATH/TO/.codex/codex-dispatch"]
# 或仅配置 env：AGENT_SUPERVISOR_MEMORY_DISPATCH_DIR=/ABS/...
```

说明：

- `[mcp_servers.supervisor_memory]` 里的 `supervisor_memory` 是 **client 侧别名**，你可以随便取（只要不跟别的 server 重名）。
- 是否“唯一”由你的 client 配置决定；PyPI 包名的唯一性是另一层（发布时）。

### 常用开关

```toml
[mcp_servers.supervisor_memory]
command = "uvx"
args = ["agent-supervisor-memory", "--global-memory", "--global-policy"]
```

关闭某些能力（默认都是启用）：

```toml
[mcp_servers.supervisor_memory]
command = "uvx"
args = ["agent-supervisor-memory", "--no-memory", "--no-subagents", "--no-policy"]
```

### 高级覆盖（一般不需要）

为兼容少数 client 场景，仍保留路径覆盖参数（优先级最高；不建议作为默认配置）：

```sh
agent-supervisor-memory --dispatch-dir /abs/path/to/codex-dispatch --memory-path /abs/path/to/memory.json --policy-path /abs/path/to/agent-policy.json
```

每次 tool 调用也可传：

- `options.enable_memory`: `true|false`
- `options.enable_subagents`: `true|false`
- `options.idempotency_key`: `string`（`codex_dispatch_start`；避免 client 超时重试导致重复启动）

## 超时诊断（60s tool call）

- 每次 tool 调用会追加写入 `.codex/operations-log.md` 两行：`state=started` 与结束行（含 `elapsed_ms`），可用来定位“卡在哪个 tool/哪次调用”。
- 调用 `metrics_get` 可查看近 N 次调用的 `p50/p95/max` 延迟与最近调用列表。
- `workflow_route` 会返回一个 `recipe`（执行配方），建议默认 `budget_ms/max_refresh`、是否 refresh、以及 dispatch/patch 的保守参数，适合直接拿来当 Supervisor 的默认调用参数。

## project_status / project_tick 的轻量参数

为避免 client 的单次 tool call 60s 超时，以下行为默认更“保守”（更快返回）：
- `project_status` 默认不刷新 running tasks；传 `options.refresh=true` 或 `options.include_handles=true` 才刷新（可配 `max_refresh` / `budget_ms`，超限会返回 `partial=true` + `remaining_*`）。
- `project_status` 默认不生成 patch（`git diff` 可能很慢）；传 `options.include_patch=true` 才会触发生成。
- `project_tick` 默认 `refresh=true`，但同样遵守 `max_refresh` / `budget_ms`（`budget_ms<=0` 表示不限制；耗尽预算会返回 `partial=true`）。
- `project_run` 默认 `background=true`：只做排队（`state=queued/starting`），由后台 worker 执行 `git worktree add` + `dispatch.start`，避免一次 tool call 内做重活导致 60s 超时。

可用参数：
- `options.refresh`: `true|false`（`project_tick` 默认 `true`；`project_status` 默认 `false`）
- `options.include_patch`: `true|false`
- `options.max_refresh`: `int`（缺省：不限刷新次数）
- `options.budget_ms`: `int`（缺省：0，即不限制；到点会返回 `partial=true`）
- `options.background`: `true|false`（`project_run` 默认 `true`；设为 `false` 会直接进入 `starting` 状态，仍由后台 worker 完成重活）

## project_patch_list / project_patch_view（避免隐式 git diff）

默认不会为了“查看 patch”而隐式跑 `git diff` 生成 patch（这在任务多/改动大时可能非常慢）。

- `project_patch_list`: 传 `options.generate_patch=true` 才会尝试生成缺失 patch；否则会返回 `state=missing` 的条目并提示。支持 `options.budget_ms`，超时会返回 `partial=true`。
- `project_patch_view`: patch 文件缺失时默认返回 `E_PATCH_NOT_GENERATED`；传 `options.generate_patch=true` 才会尝试生成。支持 `options.budget_ms`。

## project_diagnose（失败模式聚合）

`project_diagnose(project_id)` 会读取该项目的 `.codex/operations-log.md`（按 project cwd 定位）与 `.codex/projects/<project_id>/traces.jsonl`，聚合慢调用/卡顿/缺失 patch 等信号，并返回建议的 `project_status/project_tick/project_patch_list` 参数组合。

## 模型与模式（跨模型路由）

本 server **不直接调用模型**；它只提供“建议路由”，由你的 Supervisor/Client 负责真正使用什么模型去执行。

- `policy_get`: 读取策略文件
- `workflow_route`: 根据任务文本 + `mode/effort/auto` 给出建议的 `supervisor_model/coder_model/effort`

默认配置：

- Supervisor：`codex-5.2`
- Coder：`gpt-5.1-codex-max`
- `saving`：默认 `effort=medium`
- `efficient`：默认 `effort=medium`

## 如何确认正在使用（VSCode/其他 client）

- 调用 `capabilities_get`，查看返回里的 `memory.mode` / `policy.mode`（`project|global|disabled`）以及落盘 `path`。
- 写入/检索验证：`memory_put` 写入一条，然后 `memory_search` 搜索关键字。

## VSCode：自动“主/子模型”分工（推荐）

如果你同时启用了：

- `mcp__supervisor_memory__*`（本 server 的 tools）
- `mcp__codex__codex` / `mcp__codex__codex-reply`（Codex MCP）

那么可以在一个 Supervisor 会话里自动启动 Coder 子会话，并显式指定 coder 使用 `model=gpt-5.1-codex-max`（或按策略自动选择）：

1) 生成最小 spec（供 coder 执行）：
- 调用 `mcp__supervisor_memory__workflow_ensure_spec`（输入你的任务文本）

2) 获取路由建议（选择 coder 模型与 effort）：
- 调用 `mcp__supervisor_memory__workflow_route`（`task_text` 传上一步的 spec）

3) 启动 coder 子会话并执行：
- 调用 `mcp__codex__codex`，并设置：
  - `cwd`: 项目根目录
  - `model`: 上一步返回的 `coder_model`
  - `prompt`: spec + 约束（例如：只做必要改动、不要跑 build gate、先做最小校验）

4) 迭代直至完成：
- 用 `mcp__codex__codex-reply`（`conversationId` 为上一步返回）继续

### 如果 `mcp__codex__codex` 超时/返回类型不兼容

部分环境下，Codex MCP tool 可能会遇到 tool-call 超时（例如 60s）或 “Unexpected response type”。
此时可以改用本 server 自带的 dispatch 工具：它会在本机启动 `codex exec` 作为后台任务，并通过轮询查询结果。

流程：

1) 启动任务（内部记录 `job_id`，可按需返回）：
- 调用 `codex_dispatch_start`（`model` 用 `workflow_route` 返回的 `coder_model`；`cwd` 为项目根目录；`prompt` 传 spec）
- 默认 **异步**：返回 `{state:\"running\", job_id, job_ref}`（`job_ref` 为本进程内短句柄）；如需 job 目录等完整字段，请传 `options={\"verbosity\":\"full\"}`。
- 如需进一步缩短返回：传 `options={\"omit_job_id\":true}`（仅保留 `job_ref`）；如需路径 artifacts：传 `options={\"include_artifacts\":true}`。
- 如需“阻塞等待”一小段时间：传 `options={\"wait\":true,\"max_wait_seconds\":45}`（到点仍在运行则返回 `state:\"running\"`，避免 tool-call 60s 超时）。
- 为避免大 prompt 导致 UI/context 膨胀：用 `prompt_path=\"/abs/path/to/spec.txt\"`（或 `options={\"prompt_path\":\"...\"}`）替代直接传 `prompt`。
- 为防 client 重试导致重复启动，传 `options.idempotency_key`（同 bucket + key 会复用同一 `job_id`，返回现有状态/handles）。
- 支持 `options.max_model_reasoning_effort`（例如 medium）来限制 codex exec 的推理开销；`options.fast_mode` 默认 `true`，在未显式传 `max_model_reasoning_effort/max_reasoning_effort` 时，会自动追加 `model_reasoning_effort="medium"`，传 `fast_mode=false` 可关闭这一默认 cap。
- 如项目根目录存在 `.codex/structured-request.json`，可用 `options={\"use_project_prompt\":true}` 让 server 自动读取作为 prompt（避免在参数里携带文本）。

2) 轮询结果：
- 调用 `codex_dispatch_status(job_id=... 可省略)` 直到 `state` 变为 `completed|failed`（默认仅回传最小字段）
- 默认输出最小化：运行/完成返回 `{state, message}`（`message` 为提取后的“最后一条用户可读消息”，非原始 JSONL）；失败返回 `{state:'failed', exit_code?, message}`（message 优先 stderr 摘要）；未找到返回 `{state:'not_found'}`。默认不会返回 `job_id` / `meta` / `artifacts` / `last_message`；若需原始 JSONL/full meta，请用 `options={\"verbosity\":\"full\"}`。
- 为减少 context 噪声：`message` 默认优先使用 `summary` 的最后一行（running/长输出时尤其稳定），避免被 cat/sed 等命令输出污染。
- 默认 tail 读取约 2KB 的 stdout/stderr（可通过 `stdout_tail_bytes`/`stderr_tail_bytes` 调整）；需要完整日志时，请使用 full 模式。
- `job_id` 参数可选：缺省时自动使用 server 进程内记录的上一次 `codex_dispatch_start` job；也可用 `job_ref`（短句柄）替代 `job_id`。
- 增量轮询（推荐，减少重复 tail）：传 `stdout_cursor`/`stderr_cursor`（首次传 `0`），并设置 `stdout_tail_bytes`/`stderr_tail_bytes` 作为“本次最多返回的新增字节”；响应会附带 `cursor={stdout,stderr}` 与 `stdout_delta`/`stderr_delta`（如有新增）。
- 默认增量轮询只回 `{state,cursor,has_*_delta,*_delta_len}`；如需实际文本，传 `options={\"include_deltas\":true,\"delta_max_chars\":1200}`。
- 如需原始/完整响应（含 `meta`/`artifacts`/`last_message` 等），请传 `options={\"verbosity\":\"full\"}` 或 `options={\"verbose\":true}`，或在启动 server 时设置 `AGENT_SUPERVISOR_MEMORY_VERBOSE=1`（或 `AGENT_SUPERVISOR_MEMORY_VERBOSITY=full`）。
- summary 游标：`options.include_summary_cursor=true` 时，`codex_dispatch_status` 输出会附带基于内部增量的 `summary_cursor`（cursor/非 cursor 模式均支持）；`codex_dispatch_wait_all` 也会在 summary-only entries 与详细 jobs 中附带 `summary_cursor`，便于增量判断摘要是否更新。
- `codex_dispatch_status` 在非 full 模式下、且 `state=='running'` 时，会返回 `next_poll_ms`（默认开启）；`options.include_next_poll_ms=false` 或 `options.omit_next_poll_ms=true` 可关闭。
- 批量等待：`codex_dispatch_wait_all(job_ids , options?)` 建议始终显式传 `job_ids`（例如来自 `project_status` 的 task state）。若省略 `job_ids` 且当前 server 进程未记录过 started jobs，会返回 `E_JOB_IDS_REQUIRED`（避免误导性地返回空列表）。默认 summary-only（返回 `{state, jobs:[{job_id,state,summary?}], counts?}`，每个 job 都包含状态及近期 summary；跳过 stdout/stderr tail）；若需 jobs 详情/last_message/artifacts，传 `options={\"summary_only\":false}`。`options.problem_only=true`（可配合 `options.problem_states`，支持 JSON list 或逗号分隔字符串，默认集合为 failed/timeout/not_found/canceled）时 jobs 仅保留问题状态且强制返回 counts；`options.include_problem_job_ids=true` 会额外返回 `failed_job_ids` / `timeout_job_ids` / `not_found_job_ids` / `canceled_job_ids`（以及额外的 `problem_job_ids` map）；`options.include_summary_cursor=true` 让 summary-only entries/详细 jobs 附带 `summary_cursor`。
- 取消任务：`codex_dispatch_cancel(job_id|job_ref)` 会发送 SIGTERM 并标记 cancel requested；任务退出后状态会变为 `canceled|failed|completed`。
- 事件级增量（推荐，最省 context）：`codex_dispatch_events(job_id|job_ref, cursor=0)` 返回解析后的 stdout.jsonl events（默认已过滤噪声并压缩 text），并用 `cursor` 增量拉取；`options.max_paths`（默认 20，仅 compact 生效）限制 `file_change` 事件的 `paths` 长度并返回 `paths_total`/`paths_extra`，`text` 仍展示前 3 个路径并用 `+N` 表示剩余数量；如需原始事件对象（不裁剪 `paths`），传 `options={\"raw\":true}`。

### Project Orchestrator（多任务并行编排）

提供一组轻量 `project_*` tools，用于 Supervisor 将一个大需求拆成多个 task 并行启动多个 codex coder（后台 `codex exec`）。

- 默认输出极简：不会返回 `job_id` / `job_ref`；需要时传 `options.include_handles=true`。
- 轻量持久化：写入项目级 `.codex/projects/<project_id>/`，会话断开后仍可恢复查看状态与摘要。
- 项目落盘位置：`project_create(cwd=...)` 会按该目录所属 repo 的 `.codex/projects/` 保存项目，并写入全局 `~/.codex/projects-index.json` 方便在 server cwd 变化时定位。
- 全局索引：`project_*` tools 会优先读取 `~/.codex/projects-index.json` 查找项目的绝对路径；若索引缺失才回退到当前 cwd 推断的 `.codex/projects/`。
- 摘要优先：`project_status` 返回每个 task 的最近摘要行，并附带轻量 timing（`running_for_sec` / `last_output_sec_ago` / `stalled_suspected`）。
- 并行冲突控制（轻量）：task 可声明 `locks:[\"path/prefix\"]`（按前缀匹配），`project_run` 会避免同时运行冲突锁的任务。
- Patch-only（默认）：`project_run` 会为每个 task 创建独立 git worktree；patch 文件在 `project_patch_list/view/apply(_all)` 时按需生成（避免在轮询路径里跑 `git diff` 导致卡顿）。
- 自动记忆（超短）：每次 `project_collect` 会按关键词从输入/报告中提取 2–5 条“偏好/约束”，并写入 memory（是否为全局由 `--global-memory` 决定）。

## 兼容性（Compatibility）

保留 `MCP_*` 路径/开关 与 `SUPERVISOR_MEMORY_*`（verbosity 只读）的解析，便于旧配置继续工作；新配置请统一使用 `AGENT_SUPERVISOR_MEMORY_*`。

## Tools

- `capabilities_get`
- `health_get`（快速健康检查，无磁盘 I/O）
- `metrics_get`（最近调用延迟统计）
- `policy_get`
- `memory_put`
- `memory_search`
- `memory_delete`
- `memory_compact`（生成 `profile.json`，把“杂乱记忆”压缩为分类摘要）
- `workflow_ensure_spec`
- `workflow_route`
- `subagents_echo`（示例）
- `codex_dispatch_start`（启动 `codex exec` 后台任务）
- `codex_dispatch_status`（轮询任务状态/输出）
- `codex_dispatch_cancel`（取消后台任务）
- `codex_dispatch_events`（增量读取 stdout.jsonl events）
- `project_create`（创建项目记录）
- `project_plan_set`（写入 task plan，自动落盘 prompts）
- `project_run`（按依赖/并行度启动可运行 tasks）
- `project_status`（最小状态+摘要）
- `project_collect`（生成一轮汇总报告到 `.codex/projects/.../reports/`）
- `project_patch_list`（列出各 task patch）
- `project_patch_view`（patch 预览：stat + 头部片段）
- `project_patch_apply`（将 patch 应用到主 repo：先 check 再 apply）
- `project_patch_apply_all`（批量 apply patches：遇到错误即停止）
- `project_worktree_list`（列出本项目 worktrees）
- `project_worktree_cleanup`（清理已完成且 patch 已 apply 的 worktree；默认不 force）
- `project_tick`（单入口：刷新状态→启动任务→汇总）
- `project_diagnose`（超时/卡顿/失败模式诊断 + 建议参数）

## 发布到 PyPI（维护者）

PyPI 不允许覆盖已发布的同版本号，所以每次发布前都需要 bump version。

```sh
cd mcp-tools/agent-supervisor-memory

# 1) release helper（bump patch + build + publish + 清理 uv/uvx 缓存）
chmod +x scripts/bump_patch_and_clear_uv_cache.sh # 一次即可
./scripts/bump_patch_and_clear_uv_cache.sh [--dry-run] [--verbose] [--testpypi] [--cache-only]
# token：优先用 Poetry 配置（如 `poetry config pypi-token.pypi ...`）；也可用环境变量覆盖：`PYPI_TOKEN` / `TESTPYPI_TOKEN`
```