feat: add JavaScript session orchestration runtime

This commit is contained in:
shiyue
2026-08-26 10:09:26 +08:00
parent 05480e511d
commit f15bc92d1e
17 changed files with 1483 additions and 5 deletions

View File

@@ -0,0 +1,39 @@
# 标准 JavaScript 会话编排发现
## Requirements
- 提供 `@ccweb/session`,支持 `getCurrentConversationId``createConversation``sendMessage``selectSemanticBranch``getLastMessage`
- 脚本位于当前来源对话 cwd 下 `.ccweb/scripts/`,只允许 `.js`,目录使用 ESM。
- MCP 提供 create/write/run/get_run/stop 以及 `ccweb_javascript_session_api` manifest。
- 异步脚本通过 `runId` 查询;主动 stop 不插入来源消息;异常结束向来源对话插入通知。
## Research Findings
- `lib/ccweb-mcp-server.js` 已有 HTTP MCP 客户端,使用 `CC_WEB_MCP_URL``CC_WEB_MCP_TOKEN``CC_WEB_SOURCE_SESSION_ID`
- `server.js``/api/internal/mcp` 会鉴权后调用 `callInternalMcpTool`
- 现有会话入口为 `createMcpConversation``sendCrossConversationMessage``handleCodexAppSteerMessage`
- Codex App 完成路径 `handleCodexAppTurnComplete` 会持久化 assistant 消息并完成跨会话回复;需要复用其完成点实现脚本等待。
- `killProcess``handleCodexAppAbortSession` 可作为停止行为的参考,但脚本子进程需要独立 PID/日志/状态管理。
- codebase-memory 项目 `home-cc-web` 索引状态为 ready7740 nodes/17835 edges
## Technical Decisions
| Decision | Rationale |
|----------|-----------|
| 脚本包通过本地 `node_modules/@ccweb/session` 注入 | Node ESM 对裸包名解析稳定,不依赖不可靠的 `NODE_PATH` |
| 脚本运行凭据按 runId 绑定 | 避免把全局内部 MCP token 长期暴露给用户脚本 |
| 日志追加落盘get_run 分段读取 | 支持用户确认的无限输出,同时保护 MCP 响应大小 |
| 判断器一次性只读 Codex App turn | 不污染目标会话、工作区和普通 MCP 能力 |
## Issues Encountered
| Issue | Resolution |
|-------|------------|
| 根目录计划属于此前 hooks 验证任务 | 使用 scoped plan保留原有计划文件 |
## Resources
- `server.js`
- `lib/ccweb-mcp-server.js`
- `lib/codex-app-server-client.js`
- `.codex/skills/planning-with-files/`

View File

@@ -0,0 +1,85 @@
# 标准 JavaScript 会话编排进度
## Session: 2026-08-26
### Phase 1需求与代码链路确认
- **Status:** complete
- **Started:** 2026-08-26
- Actions taken:
- 完成 grilling 需求对齐,共确认 23 项决策。
- 读取项目 AGENTS.md 和相关技能说明。
- 使用 codebase-memory 确认索引 ready并核对 MCP/会话/steer 入口。
- 创建本任务 scoped planning 文件。
- Files created/modified:
- `.planning/2026-08-26-standard-javascript-session/task_plan.md`
- `.planning/2026-08-26-standard-javascript-session/findings.md`
- `.planning/2026-08-26-standard-javascript-session/progress.md`
### Phase 2MCP 契约与脚本运行基础
- **Status:** complete
- Actions taken:
- 新增 6 个脚本 MCP 工具定义,并接入 stdio/HTTP `tools/list`
- 在来源对话 cwd 下创建 `.ccweb/scripts/`,注入根 `package.json``node_modules/@ccweb/session` ESM 包。
- 增加脚本名、符号链接、脚本源代码大小校验和原子写入。
- 增加独立 Node 子进程、run.json、stdout/stderr 完整落盘、尾部/范围读取、重复启动拒绝、优雅停止/强制终止和重启恢复。
- Files created/modified:
- `lib/javascript-session-runtime.js`
- `lib/ccweb-mcp-server.js`
- `server.js`
### Phase 3会话标准包与服务端编排
- **Status:** complete
- Actions taken:
- 标准包 5 个函数通过短期 run token 调用现有 ccweb `/api/internal/mcp`,不重复实现会话。
- 创建/发送服务端等待目标助手消息完成;当前 Codex App 对话复用 steer。
- 语义分支使用一次性只读 Codex App 判断 turn严格候选匹配最多重试 3 次。
- 脚本异常结束/服务重启向来源对话插入带 runId、状态、原因和 stderr 尾部的通知;主动 stop 不插入。
- Files created/modified:
- `lib/javascript-session-runtime.js`
- `server.js`
### Phase 4测试与回归
- **Status:** complete
- Actions taken:
- 新增 `scripts/javascript-session-runtime-unit.js`,覆盖 ESM 裸包导入、目录边界、重复启动、状态查询、异常通知、主动停止和重启恢复。
- 真实服务验证 `tools/list`、API manifest、脚本执行和 `getCurrentConversationId()`
- 修复 Node 18 对 node_modules ESM 包边界的解析问题。
- Test results:
- `node scripts/javascript-session-runtime-unit.js`:通过
- `node scripts/regression.js`:通过
- `node --check server.js`:通过
- `node --check lib/javascript-session-runtime.js`:通过
- `node --check lib/ccweb-mcp-server.js`:通过
- `git diff --check`:通过
- 项目回归首次运行遇到既有 Codex App steer 模拟的时序抖动,立即重跑通过;未产生源码或运行态残留。
### Phase 5交付
- **Status:** complete
- 已清理根目录临时脚本测试产物和 TODO CSV保留源码、单测与 scoped planning 记录。
## Test Results
| Test | Input | Expected | Actual | Status |
|------|-------|----------|--------|--------|
| 代码索引状态 | `home-cc-web` | ready | ready | ✓ |
## Error Log
| Timestamp | Error | Attempt | Resolution |
|-----------|-------|---------|------------|
| 2026-08-26 | 用户级 planning-with-files 路径不存在 | 1 | 改用项目内技能路径 |
## 5-Question Reboot Check
| Question | Answer |
|----------|--------|
| Where am I? | Phase 2MCP 契约与脚本运行基础 |
| Where am I going? | 完成脚本工具、运行注册表、标准包和回归 |
| What's the goal? | 为 cc-web 提供标准 JavaScript 会话编排能力 |
| What have I learned? | 现有 MCP/会话/steer 入口可复用,完成点需接入服务端等待 |
| What have I done? | 完成需求收敛、代码索引核验和 scoped planning 文件 |

View File

@@ -0,0 +1,68 @@
# 标准 JavaScript 会话编排实施计划
## Goal
为 cc-web 增加受控的 JavaScript 脚本编排能力:通过 MCP 创建、写入、异步启动和停止脚本,并注入 `@ccweb/session` 标准包,让脚本 Promise 化调用现有 ccweb 会话。
## Current Phase
Phase 5交付收尾
## Phases
### Phase 1需求与代码链路确认
- [x] 汇总已确认的 API、脚本生命周期、异常通知和错误协议
- [x] 使用 codebase-memory 确认现有 MCP、会话创建、消息发送和 steer 入口
- **Status:** complete
### Phase 2MCP 契约与脚本运行基础
- [x] 新增脚本 MCP 工具定义和 API manifest验收tools/list 能发现 create/write/run/get_run/stop/api 工具,工具输入 schema 和 manifest 可 JSON 序列化
- [x] 新增脚本目录、ESM 标准包注入和路径校验;验收:只能访问当前 cwd/.ccweb/scripts 下的 .js包可被裸名 import
- [x] 新增异步脚本运行注册表、日志落盘、停止和重启恢复验收runId 查询、stdout/stderr 分段读取、stop 吊销凭据、重启标记 killed
- **Status:** complete
### Phase 3会话标准包与服务端编排
- [x] 实现 Promise 化的当前会话、创建、发送、最后消息和语义分支能力;验收:每个 API 返回约定字符串/ID错误携带稳定 code/details
- [x] 接入现有 ccweb 会话状态、Codex App steer 和异常来源会话通知;验收:同对话复用 steer不重复运行同脚本异常才插入来源通知
- **Status:** complete
### Phase 4测试与回归
- [x] 编写脚本 MCP、标准包和异常通知回归测试
- [x] 运行语法检查、单元回归和项目 regression
- [x] 修复发现的问题并记录验证结果
- **Status:** complete
### Phase 5交付
- [x] 清理临时 TODO CSV 和本计划状态
- [x] 汇总变更、测试结果、风险和未覆盖边界
- **Status:** complete
## Key Questions
1. 如何在不让脚本轮询的情况下等待现有会话完成?——由服务端等待会话状态和持久化消息变化。
2. 如何让同对话消息复用现有 steer且不重复启动同一脚本——调用现有 handleMessage/steer并按来源+脚本路径拒绝并发重复运行。
3. 如何在脚本异常结束时通知来源对话,同时区分主动 stop——运行注册表记录 stopRequested只有非主动失败插入通知。
## Decisions Made
| Decision | Rationale |
|----------|-----------|
| 独立 Node 子进程运行脚本 | 隔离脚本异常并支持异步执行 |
| `.js` + `type=module`,注入 `@ccweb/session` | 保持用户示例并支持显式 ESM import/顶层 await |
| 服务端实现 Promise 等待 | 脚本不实现监听、轮询和等待封装 |
| 同对话发送复用现有 steer/插入 | 避免并行 turn 和递归死锁 |
| 运行记录和 stdout/stderr 持久化 | 支持异步 runId 查询、重启标记和长输出分段读取 |
| 主动 stop 不插入来源对话 | MCP 成功/失败结果已经是调用反馈 |
| 非正常结束插入来源对话通知 | 让启动脚本的 Agent 感知异常并继续处理 |
## Errors Encountered
| Error | Attempt | Resolution |
|-------|---------|------------|
| 用户级 planning-with-files 技能路径不存在 | 1 | 改用项目内 `.codex/skills/planning-with-files/` 版本 |
## Notes
- 不覆盖根目录已有的旧 `task_plan.md``findings.md``progress.md`,本任务使用 scoped planning 目录。
- 按已确认需求不设置运行时长、输出和并发硬上限;完整日志落盘,单次 get_run 只返回尾部或显式范围,避免无限响应。
- 脚本按受信代码执行,不伪造 JavaScript 沙箱仍限制脚本路径、MCP 凭据生命周期和运行记录归属。