Compare commits
3
Commits
c48b60d383
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8128f9a1a2 | ||
|
|
62bd461356 | ||
|
|
a968ad297c |
+204
@@ -0,0 +1,204 @@
|
||||
# 协作与开发工作流
|
||||
|
||||
本仓库是 [`xai-org/grok-build`](https://github.com/xai-org/grok-build) 的 patch 化 fork。fork 多了哪些能力见 [README.md](README.md);这里只讲怎么改、怎么同步、怎么不把自己的活弄丢。
|
||||
|
||||
以 CraftBukkit / Spigot 的模型,在**不接受外部 PR 的上游**之上维护本地改动。
|
||||
|
||||
## 为什么是 patch,而不是 fork 后直接改
|
||||
|
||||
上游有两个决定性特征:
|
||||
|
||||
1. 它是 xAI 内部 monorepo 的**周期性单向导出**。历史是一串线性的 `Synced from monorepo`
|
||||
压扁提交,根目录 `SOURCE_REV` 记录对应的内部 commit。上游随时会把整棵树重新投放一次。
|
||||
2. 上游 `CONTRIBUTING.md` 明确写着**不接受任何外部 PR 或补丁**。
|
||||
|
||||
也就是说:改动推不回去,而上游会不断整体覆盖。直接在 fork 上改,每次同步都要打一场
|
||||
merge 混战,而且没人说得清「我们到底改了什么」。
|
||||
|
||||
这正是 CraftBukkit 面对 Mojang 的处境。解法也一样——**把改动本身作为版本库里的唯一真相**:
|
||||
|
||||
> `patches/` 是源码,`work/` 是编译产物。
|
||||
|
||||
## 目录结构
|
||||
|
||||
```
|
||||
newgrok/ ← 你提交的仓库
|
||||
├── patches/ ★ 唯一真相:0001-*.patch,一个补丁一件事
|
||||
├── scripts/ 工作流脚本
|
||||
├── upstream.rev 钉住的上游 commit
|
||||
├── Makefile
|
||||
├── upstream/ 派生:上游的纯净 clone (.gitignore)
|
||||
└── work/ 派生:upstream@rev + patches(.gitignore)
|
||||
```
|
||||
|
||||
对照 Spigot:
|
||||
|
||||
| 这里 | Spigot / BuildTools |
|
||||
|------|---------------------|
|
||||
| `upstream/` | CraftBukkit 的 clone(纯净上游) |
|
||||
| `work/` | 打完补丁的工作目录 |
|
||||
| `patches/` | `CraftBukkit-Patches/` |
|
||||
| `scripts/apply-patches.sh` | `applyPatches.sh` |
|
||||
| `scripts/rebuild-patches.sh` | `rebuildPatches.sh` |
|
||||
| `upstream.rev` | `versions/*.json` 里的版本钉 |
|
||||
|
||||
`upstream/` 和 `work/` 都**不进 git**——它们任何时候都能从 `upstream.rev` + `patches/`
|
||||
一字不差地重建。
|
||||
|
||||
两条规则,违反任意一条都会默默丢掉工作成果:
|
||||
|
||||
1. **Rust 源码改动写在 `work/`,不要手改 `patches/`。** 补丁文件是生成物,手改之后下次就合不上。
|
||||
2. **`make rebuild` 是离开 `work/` 的唯一出口。** `make apply` 会 `git reset --hard` + `git clean -fdx`。没 commit 并且没 export 的改动,下次 apply 就没了。
|
||||
|
||||
## 日常循环
|
||||
|
||||
改代码永远在 `work/` 里,**一个提交 = 一个补丁**:
|
||||
|
||||
```sh
|
||||
make apply # 拿到干净的、打好补丁的树
|
||||
$EDITOR work/crates/codegen/.../foo.rs # 改
|
||||
cd work && git add -A && git commit -m '...' # 提交(提交信息就是补丁标题)
|
||||
cd .. && make rebuild # 写回 patches/
|
||||
git add patches/ && git commit -m '...' # 提交到 fork 仓库
|
||||
```
|
||||
|
||||
**`make rebuild` 是唯一的出口。** 没跑过它的改动,下次 `make apply` 就没了。
|
||||
|
||||
修改已有的补丁,用普通的 git 手段就行——`work/` 是个正常的 git 仓库,`base` 标签是上游和我们的分界:
|
||||
|
||||
```sh
|
||||
cd work
|
||||
git rebase -i base # 改写、合并、重排、删除任意补丁
|
||||
cd .. && make rebuild # patches/ 会整体重新生成
|
||||
```
|
||||
|
||||
随时看当前状态:
|
||||
|
||||
```sh
|
||||
make status
|
||||
```
|
||||
|
||||
## 跟进上游同步
|
||||
|
||||
```sh
|
||||
make update-dry # 只看会拉进来什么,不动任何东西
|
||||
make update # 重新钉 upstream.rev,并把补丁前移到新基线
|
||||
```
|
||||
|
||||
`make update` 会:
|
||||
|
||||
1. `git fetch` 上游,列出 `upstream.rev..origin/main` 之间的新提交和 `SOURCE_REV` 变化
|
||||
2. 把 `upstream.rev` 更新为最新
|
||||
3. 在新基线上重放 `patches/`
|
||||
4. 干净通过则自动 `make rebuild`,让补丁上下文对齐新代码
|
||||
|
||||
冲突时脚本会停下并给出解决步骤——和处理 rebase 冲突完全一样:
|
||||
|
||||
```sh
|
||||
cd work
|
||||
git status # 看冲突文件
|
||||
$EDITOR <冲突文件> # 消掉冲突标记
|
||||
git add -A
|
||||
git am --continue # 或 git am --skip / git am --abort
|
||||
cd .. && make rebuild # 把前移后的结果写回去
|
||||
```
|
||||
|
||||
补丁数量多的时候,冲突是这个模型的固有成本,也是它的价值所在:冲突精确地指出上游改动
|
||||
撞上了你的哪一处改动,而不是让它悄悄消失。
|
||||
|
||||
## 全部命令
|
||||
|
||||
从仓库根目录跑 `make`(`make help` 列出全部目标)。脚本自己把 `~/.cargo/bin` 加进 `PATH`,交互 shell 里不必先有 `cargo`。
|
||||
|
||||
| 命令 | 作用 |
|
||||
|------|------|
|
||||
| `make setup` | 安装工具链(rustup / dotslash / protoc) |
|
||||
| `make apply` | `upstream.rev + patches/ → work/`;加 `FORCE=1` 丢弃 `work/` 里的未提交改动 |
|
||||
| `make rebuild` | `work/` 中 `base` 之上的提交 → `patches/` |
|
||||
| `make update` | 跟进最新上游同步并前移补丁 |
|
||||
| `make update-dry` | 预览上游差异,不做修改 |
|
||||
| `make build` / `make release` | 编译 debug / release 二进制 |
|
||||
| `make check` / `make clippy` / `make fmt` | 快速类型检查 / lint / 格式化 |
|
||||
| `make test` | 跑测试(`PKG=<crate>` 指定 crate) |
|
||||
| `make run` | 编译并启动 TUI |
|
||||
| `make status` | 显示当前钉住的 rev、补丁数、`work/` 状态 |
|
||||
| `make clean` | 删 `work/target` |
|
||||
| `make distclean` | 删 `work/` 和 `upstream/`(不动 `patches/`) |
|
||||
|
||||
`make build` 之后的额外参数会透传给 cargo,例如 `./scripts/build.sh --release --locked`。
|
||||
|
||||
**跑单测。** `make test` 是薄封装;要带 filter,直接走 `build.sh`,多出来的参数会原样转给 cargo:
|
||||
|
||||
```sh
|
||||
CARGO_CMD=test PKG=xai-grok-version ./scripts/build.sh test_fork_tag
|
||||
CARGO_CMD=test PKG=xai-grok-pager-bin ./scripts/build.sh version_output_writer -- --nocapture
|
||||
```
|
||||
|
||||
## 安全网
|
||||
|
||||
脚本刻意不会静默吞掉工作成果:
|
||||
|
||||
- `work/` 有**未提交改动**时,`make apply` 拒绝执行(提示先 commit + rebuild,或 `FORCE=1`)
|
||||
- `work/` 的提交数和 `patches/` 的文件数**对不上**时同样拒绝——这基本意味着你忘了 `make rebuild`。故意增删补丁文件时也会触发,检查分不清这两种情况
|
||||
- `git am` 进行到一半时,`make rebuild` / `make build` 会拒绝执行,而不是产出半成品
|
||||
- `FORCE=1`(即 `make apply FORCE=1`)是两条 apply 护栏的逃生口,意思是「丢掉 `work/`,按现在的 `patches/` 重放」
|
||||
- `make apply` 的 `git clean` 显式保留 `work/target`,否则每次打补丁都要付一次冷编译的代价
|
||||
|
||||
## 补丁格式
|
||||
|
||||
`rebuild` 用的是:
|
||||
|
||||
```
|
||||
git format-patch --no-stat -N --zero-commit --full-index --no-signature
|
||||
```
|
||||
|
||||
- `--zero-commit` —— 否则每次 rebase 后每个补丁的 `From <sha>` 行都会变,`git diff` 里全是噪音
|
||||
- `--full-index` —— 给 `git am --3way` 留下按 blob 哈希回退的余地,上游漂移时更容易自动合上
|
||||
|
||||
## 构建依赖
|
||||
|
||||
`make setup` 会处理,这里说明它在做什么:
|
||||
|
||||
- **Rust 1.94.0** —— 由上游 `rust-toolchain.toml` 钉住,rustup 自动装
|
||||
- **protoc** —— 只有 `xai-grok-tools-api` 需要(编译 `proto/grok-tools.proto`)。上游的解析顺序是
|
||||
`$PROTOC` → 向上查找 `bin/protoc` → `PATH`。`bin/protoc` 是个
|
||||
[DotSlash](https://dotslash-cli.com) wrapper,会按需下载 protoc 29.3;所以 `setup.sh`
|
||||
装 dotslash,失败则回退到系统 `protobuf-compiler`
|
||||
|
||||
两个 git 依赖(`our-forks/async-openai`、`helix-editor/nucleo`)都是公开可访问的。
|
||||
|
||||
在这台机器上(4 核 / 7 GB),`xai-grok-pager-bin` 冷编译 debug 大约 11 分钟,`work/target` 会长到约 24 GB,二进制大约 612 MB。`cargo check` 和 `cargo build` 各有一份缓存,所以 `make build` 之后再 `make check` 并不快(冷跑大约 6 分钟)。不到真的缺盘,别跑 `make clean` / `make distclean`。迭代时尽量用单 crate(`build.sh` 总会带 `-p`)。
|
||||
|
||||
## 上游树里该改哪
|
||||
|
||||
`work/` 里大约 91 个 workspace member。常动的几个:
|
||||
|
||||
| Crate | 角色 |
|
||||
|-------|------|
|
||||
| `xai-grok-pager-bin` | 组合根,产出 `xai-grok-pager` 二进制(发行名 `grok`) |
|
||||
| `xai-grok-pager` | TUI:scrollback、prompt、modal、渲染;用户手册也在这里 |
|
||||
| `xai-grok-shell` | Agent runtime + leader / stdio / headless 入口 |
|
||||
| `xai-grok-tools` | 工具实现(terminal、文件编辑、搜索……) |
|
||||
| `xai-grok-workspace` | 宿主文件系统、VCS、执行、checkpoint |
|
||||
|
||||
从上游继承的约束:
|
||||
|
||||
- **根目录 `Cargo.toml` 是生成的,当只读。** 改各 crate 自己的 `Cargo.toml`。它还带着 `[patch.crates-io]` 里对 `our-forks/async-openai` 的 git pin。
|
||||
- **永远指定 crate(`-p <crate>`)。** 全 workspace 编译慢到不现实;`build.sh` 会强制带 `-p`。
|
||||
- 动上游代码时,先看它已有的测试还成不成立。
|
||||
|
||||
## 示例补丁
|
||||
|
||||
`patches/0001` 和 `0002` 的提交信息标了 `[EXAMPLE PATCH]`,演示「改上游 Rust」和「新增文件」两种补丁,不需要就可以删。`0001` 同时也是 fork 品牌标记(`--version` 打出 `[newgrok]`),删之前想清楚。
|
||||
|
||||
删补丁文件要带 `FORCE=1`——`work/` 里还留着对应提交,删完之后 `patches/` 比 `work/` 少,安全检查会拦下来(它无法区分「你故意删的」和「你忘了 rebuild」):
|
||||
|
||||
```sh
|
||||
rm patches/0001-*.patch patches/0002-*.patch
|
||||
make apply FORCE=1
|
||||
```
|
||||
|
||||
## 许可
|
||||
|
||||
上游代码为 Apache-2.0(见 `work/LICENSE`)。`patches/` 里的改动同样按 Apache-2.0 分发。
|
||||
本仓库是非官方 fork,与 xAI 无关。
|
||||
@@ -1,189 +1,66 @@
|
||||
# newgrok — `xai-org/grok-build` 的 patch 化开发工作流
|
||||
# newgrok
|
||||
|
||||
以 CraftBukkit / Spigot 的模型,在**不接受外部 PR 的上游**之上维护本地改动。
|
||||
[`xai-org/grok-build`](https://github.com/xai-org/grok-build) 的非官方 fork。上游是 xAI 的终端 coding agent(全屏 TUI `grok`);本仓库在其上叠了一组可回放的补丁。
|
||||
|
||||
## 为什么是 patch,而不是 fork 后直接改
|
||||
本仓库与 xAI 无关。编出来的二进制会标成 `grok [newgrok] <version>`,不会和官方发行版搞混。
|
||||
|
||||
上游 [`xai-org/grok-build`](https://github.com/xai-org/grok-build) 有两个决定性特征:
|
||||
改代码、跟进上游、补丁怎么排,见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
1. 它是 xAI 内部 monorepo 的**周期性单向导出**。历史是一串线性的 `Synced from monorepo`
|
||||
压扁提交,根目录 `SOURCE_REV` 记录对应的内部 commit。上游随时会把整棵树重新投放一次。
|
||||
2. `CONTRIBUTING.md` 明确写着**不接受任何外部 PR 或补丁**。
|
||||
## 这个 fork 多了什么
|
||||
|
||||
也就是说:改动推不回去,而上游会不断整体覆盖。直接在 fork 上改,每次同步都要打一场
|
||||
merge 混战,而且没人说得清「我们到底改了什么」。
|
||||
### `/rc` — 当前会话镜像到 grok-glance
|
||||
|
||||
这正是 CraftBukkit 面对 Mojang 的处境。解法也一样——**把改动本身作为版本库里的唯一真相**:
|
||||
|
||||
> `patches/` 是源码,`work/` 是编译产物。
|
||||
|
||||
## 目录结构
|
||||
`/rc` 把你正在用的会话经 ACP 镜像到 grok-glance 服务器。终端里的 TUI 完全不受影响:不重启、不切 headless、不把会话交出去。glance 变成同一会话的第二个视图,两端都能跟 turn 流、发 prompt、打断正在跑的 turn,以及回答权限请求、提问和 plan 批准。**两边都能答;谁先答谁赢**,另一边的对话框会自己关掉。
|
||||
|
||||
```
|
||||
newgrok/ ← 你提交的仓库
|
||||
├── patches/ ★ 唯一真相:0001-*.patch,一个补丁一件事
|
||||
├── scripts/ 工作流脚本
|
||||
├── upstream.rev 钉住的上游 commit
|
||||
├── Makefile
|
||||
├── upstream/ 派生:上游的纯净 clone (.gitignore)
|
||||
└── work/ 派生:upstream@rev + patches(.gitignore)
|
||||
/rc 开关
|
||||
/rc on 连接(start / connect)
|
||||
/rc off 断开(stop / disconnect)
|
||||
/rc status 看链路状态
|
||||
```
|
||||
|
||||
对照 Spigot:
|
||||
在 `~/.grok/config.toml` 里配 glance:
|
||||
|
||||
| 这里 | Spigot / BuildTools |
|
||||
|------|---------------------|
|
||||
| `upstream/` | CraftBukkit 的 clone(纯净上游) |
|
||||
| `work/` | 打完补丁的工作目录 |
|
||||
| `patches/` | `CraftBukkit-Patches/` |
|
||||
| `scripts/apply-patches.sh` | `applyPatches.sh` |
|
||||
| `scripts/rebuild-patches.sh` | `rebuildPatches.sh` |
|
||||
| `upstream.rev` | `versions/*.json` 里的版本钉 |
|
||||
```toml
|
||||
[remote_control]
|
||||
url = "wss://glance.example.com/api/acp/agent"
|
||||
api_key = "glance_sk_..."
|
||||
auto_start = false # 启动就连,不用先敲 /rc
|
||||
```
|
||||
|
||||
`upstream/` 和 `work/` 都**不进 git**——它们任何时候都能从 `upstream.rev` + `patches/`
|
||||
一字不差地重建。
|
||||
`GROK_RC_URL` / `GROK_RC_API_KEY` / `GROK_RC_AUTO_START` 会覆盖文件里的值。API key 更适合走环境变量,不要把 bearer token 写进明文配置。服务端用 `glance apikey add <name>` 签发。
|
||||
|
||||
## 快速开始
|
||||
链路只活在当前会话里,退出即断,什么都不落盘。glance 连不上会退避重试,`/rc status` 会说明原因——对端挂了、慢了或中途被杀,都不会拖垮本地会话。远端 cancel 记成 `Client("glance")`(和按 Esc 同一类),远端 prompt 带 `clientIdentifier`,会话日志里分得清是谁做的。
|
||||
|
||||
### 专用工具优先
|
||||
|
||||
每个主会话用户 turn 会写入一份**只写一次、之后不再改**的 dispatch checklist,让模型在动手前先考虑 `explore` / search / read / plan / `deep-research`,而不是自己开一轮宽搜索,或拿 bash 去 `cat` / `grep` / `find` / `ls`。checklist 按当前工具集渲染进用户消息,前缀对 prompt cache 是稳定的。
|
||||
|
||||
如果 bash 还是做了有专用工具的文件操作,工具结果后面会跟一条 **nudge**(命令已经执行过了,只提醒下次换工具)。子 agent 同样会收到这条 nudge。
|
||||
|
||||
两段 reminder 在送给 compaction 摘要模型之前会被剥掉,避免把 agent 内部指令写进会话摘要。
|
||||
|
||||
### 会话级 `prompt_cache_key`
|
||||
|
||||
主 turn 会显式钉上会话级的 `prompt_cache_key`(就是 conversation id)。Responses 路径以前靠 `x_grok_conv_id` 回退也能走到同一把钥匙;钉死之后,Chat Completions 的 `user` 字段、以及 recap / `/btw` 这类复用父会话前缀的旁路调用,都会共用同一个 key。
|
||||
|
||||
刻意不按 agent 加后缀:那样会把旁路调用设计上要蹭的前缀缓存拆开。
|
||||
|
||||
## 从源码构建
|
||||
|
||||
```sh
|
||||
make setup # 装 Rust 工具链(1.94.0,由 rust-toolchain.toml 钉住)+ dotslash/protoc
|
||||
make setup # 装 Rust 1.94.0 + dotslash/protoc(幂等,可重跑)
|
||||
make apply # 用 upstream.rev + patches/ 生成 work/
|
||||
make build # 编译 work/target/debug/xai-grok-pager
|
||||
make run # 直接启动 TUI
|
||||
make build # 编出 work/target/debug/xai-grok-pager
|
||||
make run # 编完直接开 TUI
|
||||
```
|
||||
|
||||
`make setup` 是幂等的,可以随时重跑。
|
||||
`make setup` 会把工具链装到 `~/.cargo`,脚本自己把它加进 `PATH`。若要在交互 shell 里直接用 `cargo`:fish 执行 `source ~/.cargo/env.fish`,bash 执行 `source ~/.cargo/env`。
|
||||
|
||||
> Rust 装在 `~/.cargo`,脚本会自己把它加进 `PATH`。若要在交互 shell 里直接用 `cargo`:
|
||||
> fish 执行 `source ~/.cargo/env.fish`,bash 执行 `source ~/.cargo/env`。
|
||||
第一次启动会打开浏览器做官方登录,见上游的 [authentication guide](https://github.com/xai-org/grok-build/blob/main/crates/codegen/xai-grok-pager/docs/user-guide/02-authentication.md)。打好补丁之后,更完整的用户手册在 `work/crates/codegen/xai-grok-pager/docs/user-guide/`(`/rc` 写在 slash commands 那一页)。
|
||||
|
||||
## 日常循环
|
||||
|
||||
改代码永远在 `work/` 里,**一个提交 = 一个补丁**:
|
||||
|
||||
```sh
|
||||
make apply # 拿到干净的、打好补丁的树
|
||||
$EDITOR work/crates/codegen/.../foo.rs # 改
|
||||
cd work && git add -A && git commit -m '...' # 提交(提交信息就是补丁标题)
|
||||
cd .. && make rebuild # 写回 patches/
|
||||
git add patches/ && git commit -m '...' # 提交到 fork 仓库
|
||||
```
|
||||
|
||||
**`make rebuild` 是唯一的出口。** 没跑过它的改动,下次 `make apply` 就没了。
|
||||
|
||||
修改已有的补丁,用普通的 git 手段就行——`work/` 是个正常的 git 仓库:
|
||||
|
||||
```sh
|
||||
cd work
|
||||
git rebase -i base # 改写、合并、重排、删除任意补丁
|
||||
cd .. && make rebuild # patches/ 会整体重新生成
|
||||
```
|
||||
|
||||
随时看当前状态:
|
||||
|
||||
```sh
|
||||
make status
|
||||
```
|
||||
|
||||
## 跟进上游同步
|
||||
|
||||
```sh
|
||||
make update-dry # 只看会拉进来什么,不动任何东西
|
||||
make update # 重新钉 upstream.rev,并把补丁前移到新基线
|
||||
```
|
||||
|
||||
`make update` 会:
|
||||
|
||||
1. `git fetch` 上游,列出 `upstream.rev..origin/main` 之间的新提交和 `SOURCE_REV` 变化
|
||||
2. 把 `upstream.rev` 更新为最新
|
||||
3. 在新基线上重放 `patches/`
|
||||
4. 干净通过则自动 `make rebuild`,让补丁上下文对齐新代码
|
||||
|
||||
冲突时脚本会停下并给出解决步骤——和处理 rebase 冲突完全一样:
|
||||
|
||||
```sh
|
||||
cd work
|
||||
git status # 看冲突文件
|
||||
$EDITOR <冲突文件> # 消掉冲突标记
|
||||
git add -A
|
||||
git am --continue # 或 git am --skip / git am --abort
|
||||
cd .. && make rebuild # 把前移后的结果写回去
|
||||
```
|
||||
|
||||
补丁数量多的时候,冲突是这个模型的固有成本,也是它的价值所在:冲突精确地指出上游改动
|
||||
撞上了你的哪一处改动,而不是让它悄悄消失。
|
||||
|
||||
## 全部命令
|
||||
|
||||
| 命令 | 作用 |
|
||||
|------|------|
|
||||
| `make setup` | 安装工具链(rustup / dotslash / protoc) |
|
||||
| `make apply` | `upstream.rev + patches/ → work/`;加 `FORCE=1` 丢弃 `work/` 里的未提交改动 |
|
||||
| `make rebuild` | `work/` 中 `base` 之上的提交 → `patches/` |
|
||||
| `make update` | 跟进最新上游同步并前移补丁 |
|
||||
| `make update-dry` | 预览上游差异,不做修改 |
|
||||
| `make build` / `make release` | 编译 debug / release 二进制 |
|
||||
| `make check` / `make clippy` / `make fmt` | 快速类型检查 / lint / 格式化 |
|
||||
| `make test` | 跑测试(`PKG=<crate>` 指定 crate) |
|
||||
| `make run` | 编译并启动 TUI |
|
||||
| `make status` | 显示当前钉住的 rev、补丁数、`work/` 状态 |
|
||||
| `make clean` | 删 `work/target` |
|
||||
| `make distclean` | 删 `work/` 和 `upstream/`(不动 `patches/`) |
|
||||
|
||||
`make build` 之后的额外参数会透传给 cargo,例如 `./scripts/build.sh --release --locked`。
|
||||
|
||||
## 安全网
|
||||
|
||||
脚本刻意不会静默吞掉工作成果:
|
||||
|
||||
- `work/` 有**未提交改动**时,`make apply` 拒绝执行(提示先 commit + rebuild,或 `FORCE=1`)
|
||||
- `work/` 的提交数和 `patches/` 的文件数**对不上**时同样拒绝——这基本意味着你忘了 `make rebuild`
|
||||
- `git am` 进行到一半时,`make rebuild` / `make build` 会拒绝执行,而不是产出半成品
|
||||
- `make apply` 的 `git clean` 显式保留 `work/target`,否则每次打补丁都要付一次冷编译的代价
|
||||
|
||||
## 补丁格式
|
||||
|
||||
`rebuild` 用的是:
|
||||
|
||||
```
|
||||
git format-patch --no-stat -N --zero-commit --full-index --no-signature
|
||||
```
|
||||
|
||||
- `--zero-commit` —— 否则每次 rebase 后每个补丁的 `From <sha>` 行都会变,`git diff` 里全是噪音
|
||||
- `--full-index` —— 给 `git am --3way` 留下按 blob 哈希回退的余地,上游漂移时更容易自动合上
|
||||
|
||||
## 构建依赖
|
||||
|
||||
`make setup` 会处理,这里说明它在做什么:
|
||||
|
||||
- **Rust 1.94.0** —— 由上游 `rust-toolchain.toml` 钉住,rustup 自动装
|
||||
- **protoc** —— 只有 `xai-grok-tools-api` 需要(编译 `proto/grok-tools.proto`)。上游的解析顺序是
|
||||
`$PROTOC` → 向上查找 `bin/protoc` → `PATH`。`bin/protoc` 是个
|
||||
[DotSlash](https://dotslash-cli.com) wrapper,会按需下载 protoc 29.3;所以 `setup.sh`
|
||||
装 dotslash,失败则回退到系统 `protobuf-compiler`
|
||||
|
||||
两个 git 依赖(`our-forks/async-openai`、`helix-editor/nucleo`)都是公开可访问的。
|
||||
|
||||
## 示例补丁
|
||||
|
||||
`patches/` 里预置了两个补丁,提交信息都标了 `[EXAMPLE PATCH]`:
|
||||
|
||||
1. **`0001`** 改上游 Rust 源码——给 `xai-grok-version` 加 `FORK_NAME` / `fork_tag()`,
|
||||
并接进 pager 的版本输出,于是 `--version` 显示 `grok [newgrok] 1.0.3 (…)`。
|
||||
标记插在 `grok ` 之后而不是追加到末尾,因为 `main.rs` 的
|
||||
`version_output_writer_preserves_channel_aware_contract` 断言该行仍以 channel label
|
||||
(或 `)`)结尾——**改上游代码时顺手确认它的测试仍然成立**,这个补丁本身就是个例子。
|
||||
2. **`0002`** 新增文件——根目录 `FORK.md`。
|
||||
|
||||
不需要就直接删。注意要带 `FORCE=1`——`work/` 里还留着这两个补丁的提交,而删掉补丁文件后
|
||||
`patches/` 比 `work/` 少,安全检查会拦下来(它无法区分「你故意删的」和「你忘了 rebuild」):
|
||||
|
||||
```sh
|
||||
rm patches/0001-*.patch patches/0002-*.patch
|
||||
make apply FORCE=1
|
||||
```
|
||||
日常开发循环、跟进上游、以及 `make` 全表见 [CONTRIBUTING.md](CONTRIBUTING.md)。
|
||||
|
||||
## 许可
|
||||
|
||||
上游代码为 Apache-2.0(见 `work/LICENSE`)。`patches/` 里的改动同样按 Apache-2.0 分发。
|
||||
本仓库是非官方 fork,与 xAI 无关。
|
||||
|
||||
@@ -18,24 +18,24 @@ test asserts the line still ends with the channel label (or ")").
|
||||
delete the patch file and run 'make apply'.
|
||||
|
||||
diff --git a/crates/codegen/xai-grok-pager-bin/src/main.rs b/crates/codegen/xai-grok-pager-bin/src/main.rs
|
||||
index ac1adc4fc7800e507742d4ae4bb7067edf2959f5..19238e835bb37d8d9670c70b0402d43caf248827 100644
|
||||
index 29beeff904c352b1c4f029c972312a83521f44db..0a618cf1b6331e5d9699ea4c44a99ae081fcc1bf 100644
|
||||
--- a/crates/codegen/xai-grok-pager-bin/src/main.rs
|
||||
+++ b/crates/codegen/xai-grok-pager-bin/src/main.rs
|
||||
@@ -1788,7 +1788,8 @@ fn install_heap_profile_hooks() {
|
||||
@@ -1787,7 +1787,8 @@ fn install_heap_profile_hooks() {
|
||||
}
|
||||
fn version_text(channel_label: &str) -> String {
|
||||
format!(
|
||||
- "grok {}\n",
|
||||
+ "grok {} {}\n",
|
||||
+ xai_grok_version::fork_tag(),
|
||||
xai_grok_version::display_version_with_commit(env!("VERSION_WITH_COMMIT"), channel_label,)
|
||||
)
|
||||
}
|
||||
xai_grok_version::display_version_with_commit(
|
||||
xai_grok_version::full_version(),
|
||||
channel_label,
|
||||
diff --git a/crates/codegen/xai-grok-version/src/lib.rs b/crates/codegen/xai-grok-version/src/lib.rs
|
||||
index 29fdfc1e64ea1df96f0ca3e1afe78c9ac37843bd..f56d96d34b828a0c10267fca407bc0b14e0c2a55 100644
|
||||
index b7d224fc92d5e25ba5dae888fb60335c3c0933ad..200d31e3d27e71bae5537ad93e210cbfd86adc3e 100644
|
||||
--- a/crates/codegen/xai-grok-version/src/lib.rs
|
||||
+++ b/crates/codegen/xai-grok-version/src/lib.rs
|
||||
@@ -9,6 +9,15 @@ pub const VERSION: &str = match option_env!("GROK_VERSION") {
|
||||
@@ -11,6 +11,15 @@ pub const VERSION: &str = match option_env!("GROK_VERSION") {
|
||||
None => env!("CARGO_PKG_VERSION"),
|
||||
};
|
||||
|
||||
@@ -48,12 +48,12 @@ index 29fdfc1e64ea1df96f0ca3e1afe78c9ac37843bd..f56d96d34b828a0c10267fca407bc0b1
|
||||
+ format!("[{FORK_NAME}]")
|
||||
+}
|
||||
+
|
||||
/// [`TEST_VERSION_ENV`] override first, then [`VERSION`]. Trimmed so
|
||||
/// non-semver-aware callers can pass the result straight into parsing.
|
||||
pub fn installed() -> String {
|
||||
@@ -72,4 +81,12 @@ mod tests {
|
||||
assert_eq!(display_version(""), VERSION);
|
||||
assert!(display_version(" [stable]").ends_with("[stable]"));
|
||||
/// Runtime-injected `"<version> (<shortcommit>)"` string. Only the release
|
||||
/// binary stamps the commit hash in its own build.rs and injects it here at
|
||||
/// startup, so the big lib crates don't recompile on every commit.
|
||||
@@ -100,4 +109,12 @@ mod tests {
|
||||
set_full_version("second (bbbbbbb)");
|
||||
assert_eq!(full_version(), "first (aaaaaaa)");
|
||||
}
|
||||
+
|
||||
+ /// The fork marker must stay non-empty and bracketed -- the pager splices it
|
||||
|
||||
@@ -5,10 +5,10 @@ Subject: [PATCH] Add [remote_control] config section for the /rc glance bridge
|
||||
|
||||
|
||||
diff --git a/crates/codegen/xai-grok-shell/src/agent/config.rs b/crates/codegen/xai-grok-shell/src/agent/config.rs
|
||||
index 3dd50b3217936911d6858e4c9c169d7609e1a0fd..f39d1bd460a4635dd4767afe92622ab9b04952a1 100644
|
||||
index 2a2ab0491b226cee84fd968869a2daaff707f9a7..be842a31d205db6bc88ac8f46c0af8e2cecbb4ef 100644
|
||||
--- a/crates/codegen/xai-grok-shell/src/agent/config.rs
|
||||
+++ b/crates/codegen/xai-grok-shell/src/agent/config.rs
|
||||
@@ -1014,6 +1014,79 @@ pub struct CliConfig {
|
||||
@@ -1023,6 +1023,79 @@ pub struct CliConfig {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub session_picker_grouped: Option<bool>,
|
||||
}
|
||||
@@ -88,7 +88,7 @@ index 3dd50b3217936911d6858e4c9c169d7609e1a0fd..f39d1bd460a4635dd4767afe92622ab9
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
#[serde(default)]
|
||||
pub struct DiagnosticsConfig {
|
||||
@@ -1348,6 +1421,9 @@ pub struct Config {
|
||||
@@ -1361,6 +1434,9 @@ pub struct Config {
|
||||
pub paths: PathsConfig,
|
||||
#[serde(default, skip_serializing)]
|
||||
pub cli: CliConfig,
|
||||
@@ -98,7 +98,7 @@ index 3dd50b3217936911d6858e4c9c169d7609e1a0fd..f39d1bd460a4635dd4767afe92622ab9
|
||||
#[serde(default, skip_serializing)]
|
||||
pub models: ModelsConfig,
|
||||
#[serde(default, skip_serializing)]
|
||||
@@ -1759,6 +1835,7 @@ impl Default for Config {
|
||||
@@ -1767,6 +1843,7 @@ impl Default for Config {
|
||||
feedback: FeedbackConfig::default(),
|
||||
paths: PathsConfig::default(),
|
||||
cli: CliConfig::default(),
|
||||
|
||||
@@ -40,10 +40,10 @@ class as Esc -- rather than faking a keypress and lying in the session log.
|
||||
Remote prompts carry clientIdentifier for the same reason.
|
||||
|
||||
diff --git a/Cargo.lock b/Cargo.lock
|
||||
index 3f214ce0ec69942190a8d35ca99f0b7662bee8c7..65ffbd9a021c7e7de45248a3237f52ae02e763a4 100644
|
||||
index 8fc3009c68e60a71ed400d7b3a2fb318c7455956..bcd7f081a49128e010d3b3122038002b7e58b9ca 100644
|
||||
--- a/Cargo.lock
|
||||
+++ b/Cargo.lock
|
||||
@@ -13696,6 +13696,7 @@ dependencies = [
|
||||
@@ -13737,6 +13737,7 @@ dependencies = [
|
||||
"dunce",
|
||||
"enum_delegate",
|
||||
"flate2",
|
||||
@@ -51,7 +51,7 @@ index 3f214ce0ec69942190a8d35ca99f0b7662bee8c7..65ffbd9a021c7e7de45248a3237f52ae
|
||||
"git2",
|
||||
"image",
|
||||
"indexmap",
|
||||
@@ -13724,6 +13725,7 @@ dependencies = [
|
||||
@@ -13765,6 +13766,7 @@ dependencies = [
|
||||
"tempfile",
|
||||
"textwrap",
|
||||
"tokio",
|
||||
@@ -60,7 +60,7 @@ index 3f214ce0ec69942190a8d35ca99f0b7662bee8c7..65ffbd9a021c7e7de45248a3237f52ae
|
||||
"toml",
|
||||
"toml_edit 0.22.27",
|
||||
diff --git a/crates/codegen/xai-grok-pager/Cargo.toml b/crates/codegen/xai-grok-pager/Cargo.toml
|
||||
index db32aa6610bb1276b53842117cf99d7602a5f736..c0a2d49158def2249255c25a3abef59b6e857cad 100644
|
||||
index 86d9d26dd190714acd133643dc73ce6e332c1e63..cc56f87f878c18533dc595f40f3ece490d3131d9 100644
|
||||
--- a/crates/codegen/xai-grok-pager/Cargo.toml
|
||||
+++ b/crates/codegen/xai-grok-pager/Cargo.toml
|
||||
@@ -92,6 +92,11 @@ xai-grok-update = { path = "../xai-grok-update" }
|
||||
@@ -76,7 +76,7 @@ index db32aa6610bb1276b53842117cf99d7602a5f736..c0a2d49158def2249255c25a3abef59b
|
||||
# Crash-recovery registry of open TUI sessions.
|
||||
xai-grok-active-sessions = { workspace = true }
|
||||
diff --git a/crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md b/crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md
|
||||
index 1a06af17055384f657adf066af8e8b30b79d809c..1ad8905a4495a8b37a5339ede68cba8304f418bf 100644
|
||||
index 723efc5b91874535777cca954e62547106b0d11f..6149b9fb68a86cbcc78b27d5998f9c1e27f62937 100644
|
||||
--- a/crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md
|
||||
+++ b/crates/codegen/xai-grok-pager/docs/user-guide/04-slash-commands.md
|
||||
@@ -103,6 +103,34 @@ Rename the current session. Alias: `/title`.
|
||||
@@ -115,10 +115,10 @@ index 1a06af17055384f657adf066af8e8b30b79d809c..1ad8905a4495a8b37a5339ede68cba83
|
||||
|
||||
## Model and Mode
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/acp_handler/mod.rs b/crates/codegen/xai-grok-pager/src/app/acp_handler/mod.rs
|
||||
index 80aa6e5544434d0403cc87a5bda8628ab4e0d5c6..358a7ea9c234613bfcaf701e60315ba27d2bc7b5 100644
|
||||
index 87667c5843cca2c4d2f56d41bc67103bd8296e85..5dea13dda409c058a0d4a1841eb882fef6e96fd6 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/acp_handler/mod.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/acp_handler/mod.rs
|
||||
@@ -152,6 +152,16 @@ use workflow_ingest::*;
|
||||
@@ -153,6 +153,16 @@ use workflow_ingest::*;
|
||||
/// background agent must still land in its own scrollback so the user sees
|
||||
/// the full turn after switching back.
|
||||
pub(crate) fn handle(msg: AcpClientMessage, app: &mut AppView) -> bool {
|
||||
@@ -136,7 +136,7 @@ index 80aa6e5544434d0403cc87a5bda8628ab4e0d5c6..358a7ea9c234613bfcaf701e60315ba2
|
||||
AcpClientMessage::SessionNotification(notif) => {
|
||||
let mut meta = NotificationMeta::from_json(notif.request.meta.as_ref());
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/actions.rs b/crates/codegen/xai-grok-pager/src/app/actions.rs
|
||||
index 1d205af8f0ba4ed065087a84e5050d35b2edd804..1644f044962b9fb1d9c4573b2b02db4798ecafda 100644
|
||||
index ac4b7057e3a24800969cbae01271bfa76fae469c..ce40302b71943058a974c38274dea3903bc13aeb 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/actions.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/actions.rs
|
||||
@@ -30,6 +30,22 @@ pub enum SwitchModelError {
|
||||
@@ -162,7 +162,7 @@ index 1d205af8f0ba4ed065087a84e5050d35b2edd804..1644f044962b9fb1d9c4573b2b02db47
|
||||
/// Synchronous, side-effect-free user intent.
|
||||
///
|
||||
/// Produced by [`super::input`] from key/mouse events.
|
||||
@@ -714,6 +730,11 @@ pub enum Action {
|
||||
@@ -717,6 +733,11 @@ pub enum Action {
|
||||
/// tasks as a system block (`/tasks`). The surface minimal mode uses in
|
||||
/// place of the `TasksPane`.
|
||||
ShowTasks,
|
||||
@@ -175,10 +175,10 @@ index 1d205af8f0ba4ed065087a84e5050d35b2edd804..1644f044962b9fb1d9c4573b2b02db47
|
||||
ShowPlan,
|
||||
/// Enter plan mode. If a description is provided, also start a turn
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/app_view.rs b/crates/codegen/xai-grok-pager/src/app/app_view.rs
|
||||
index 5a960e588c81ecc9d642217455fa28a87fc84b28..c7a0e0e729560569dac2e603a6178cec81dfd121 100644
|
||||
index abbbf08a7239da006a7bf14a749230ec5398b276..b336028865a9c0c58dd0de4ea1197050c267df49 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/app_view.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/app_view.rs
|
||||
@@ -1217,6 +1217,18 @@ pub struct AppView {
|
||||
@@ -1229,6 +1229,18 @@ pub struct AppView {
|
||||
/// combinations are unrepresentable; production mutates it only through the
|
||||
/// `AppView::voice_*` transition methods.
|
||||
pub voice_state: VoiceState,
|
||||
@@ -197,7 +197,7 @@ index 5a960e588c81ecc9d642217455fa28a87fc84b28..c7a0e0e729560569dac2e603a6178cec
|
||||
}
|
||||
/// Reshow window elapsed? None/0 = never. Unparseable ack fails open (show).
|
||||
fn privacy_banner_reshow_elapsed(acked_at: &str, reshow_days: Option<u64>) -> bool {
|
||||
@@ -1648,6 +1660,9 @@ impl AppView {
|
||||
@@ -1667,6 +1679,9 @@ impl AppView {
|
||||
voice_auth: None,
|
||||
voice_cmd_tx: None,
|
||||
voice_state: VoiceState::Idle,
|
||||
@@ -208,10 +208,10 @@ index 5a960e588c81ecc9d642217455fa28a87fc84b28..c7a0e0e729560569dac2e603a6178cec
|
||||
}
|
||||
/// Seed `deferred_model_switch` from CLI `-m`. The CLI effort token is
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/app_view_tests.rs b/crates/codegen/xai-grok-pager/src/app/app_view_tests.rs
|
||||
index 272f3b11dcf8b83935622dd01142bb4a15e2e7fc..2cd3d0eb554b1a4d9572efa3353c1db48aa178a0 100644
|
||||
index 8de13f2db0bf9dd4f0630bfc8b099ada608c4a38..b456dac46ea39af343f4f0fd84ce7012e67fc97d 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/app_view_tests.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/app_view_tests.rs
|
||||
@@ -305,6 +305,9 @@ pub(crate) fn test_app() -> AppView {
|
||||
@@ -308,6 +308,9 @@ pub(crate) fn test_app() -> AppView {
|
||||
voice_auth: None,
|
||||
voice_cmd_tx: None,
|
||||
voice_state: VoiceState::Idle,
|
||||
@@ -511,7 +511,7 @@ index 0000000000000000000000000000000000000000..43a3fb0c4694c7da7741b683f5286c71
|
||||
+ }
|
||||
+}
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/dispatch/router.rs b/crates/codegen/xai-grok-pager/src/app/dispatch/router.rs
|
||||
index 61b37dcd39deb4d6dde7873f6ad29ef68c537b41..f5895ee6181bc0e50766c538f7110e246fbfba8b 100644
|
||||
index 86df4292ad50f986a716298ef85c0a73bd9e12ab..1d2e07109f57220660c6737290dd949fa29e5f73 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/dispatch/router.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/dispatch/router.rs
|
||||
@@ -48,6 +48,7 @@ use super::prompt::{
|
||||
@@ -544,10 +544,10 @@ index 61b37dcd39deb4d6dde7873f6ad29ef68c537b41..f5895ee6181bc0e50766c538f7110e24
|
||||
Action::EnterPlanMode { description } => dispatch_enter_plan_mode(app, description),
|
||||
Action::SetPlanMode(kind) => set_plan_mode(app, kind),
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/dispatch/tests/mod.rs b/crates/codegen/xai-grok-pager/src/app/dispatch/tests/mod.rs
|
||||
index 4aa72560f1ff7c9158e7905f53a83ec51cbea714..270ec9d3d2a19550cabd1884bf4eeb2ce77326e0 100644
|
||||
index 63d41e80a915f5ce67ecbeca00669628e568ede8..c41f28667fca9caf278155a8f211139e66b6eee9 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/dispatch/tests/mod.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/dispatch/tests/mod.rs
|
||||
@@ -293,6 +293,9 @@ fn test_app() -> AppView {
|
||||
@@ -296,6 +296,9 @@ fn test_app() -> AppView {
|
||||
voice_auth: None,
|
||||
voice_cmd_tx: None,
|
||||
voice_state: VoiceState::Idle,
|
||||
@@ -558,10 +558,10 @@ index 4aa72560f1ff7c9158e7905f53a83ec51cbea714..270ec9d3d2a19550cabd1884bf4eeb2c
|
||||
}
|
||||
/// Build a default `AgentSession` for
|
||||
diff --git a/crates/codegen/xai-grok-pager/src/app/event_loop.rs b/crates/codegen/xai-grok-pager/src/app/event_loop.rs
|
||||
index 134a05c92417bf9038e9c784c0ee34b9b9d5bec7..1d31be3fe37b6cbbdf62c498fc30cb973d2d6e0b 100644
|
||||
index d0f8962eb2d298072d4a232c7ec50b5ddec5ba37..30cca1ed9792f09785d3227d424a170e2cd8d111 100644
|
||||
--- a/crates/codegen/xai-grok-pager/src/app/event_loop.rs
|
||||
+++ b/crates/codegen/xai-grok-pager/src/app/event_loop.rs
|
||||
@@ -1707,6 +1707,12 @@ pub(crate) async fn run(
|
||||
@@ -1737,6 +1737,12 @@ pub(crate) async fn run(
|
||||
let mut voice_rx = None::<tokio::sync::mpsc::Receiver<xai_grok_voice::VoiceEvent>>;
|
||||
let voice_auth_factory = connection.auth_manager.clone();
|
||||
|
||||
@@ -574,7 +574,7 @@ index 134a05c92417bf9038e9c784c0ee34b9b9d5bec7..1d31be3fe37b6cbbdf62c498fc30cb97
|
||||
// Animation tick: only scheduled when there are running entries.
|
||||
let mut tick_interval = tick_interval;
|
||||
let mut animation_tick_at: Option<Instant> = None;
|
||||
@@ -2128,6 +2134,19 @@ pub(crate) async fn run(
|
||||
@@ -2158,6 +2164,19 @@ pub(crate) async fn run(
|
||||
presenter.request_presentation(&mut app, terminal, false);
|
||||
}
|
||||
|
||||
@@ -594,7 +594,7 @@ index 134a05c92417bf9038e9c784c0ee34b9b9d5bec7..1d31be3fe37b6cbbdf62c498fc30cb97
|
||||
// Stop voice if the user has left the recording session (see method).
|
||||
app.enforce_voice_session_bound();
|
||||
|
||||
@@ -3023,6 +3042,37 @@ pub(crate) async fn run(
|
||||
@@ -3053,6 +3072,37 @@ pub(crate) async fn run(
|
||||
presenter.request(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -53,10 +53,10 @@ index 27f75124fca625dcb9724185c26172926bb46346..d6224d9ea3b5a665c0d41fc7ca756193
|
||||
/// signal.
|
||||
/// One-shot; skipped for the harness that owns this surface.
|
||||
diff --git a/crates/codegen/xai-grok-shell/src/session/acp_session_impl/turn.rs b/crates/codegen/xai-grok-shell/src/session/acp_session_impl/turn.rs
|
||||
index e9d50a9331bf32946e5174e3c6399251b8e52ecb..a0d84cde22c11cef0172b0e151c50d7638581857 100644
|
||||
index 355d5146e2859778b9c3c99e71404ce9c0d3087f..f2e65c4f3758c7619b5e73b0d3eceda657552aca 100644
|
||||
--- a/crates/codegen/xai-grok-shell/src/session/acp_session_impl/turn.rs
|
||||
+++ b/crates/codegen/xai-grok-shell/src/session/acp_session_impl/turn.rs
|
||||
@@ -760,6 +760,12 @@ impl SessionActor {
|
||||
@@ -803,6 +803,12 @@ impl SessionActor {
|
||||
attached_image_refs,
|
||||
))
|
||||
.await;
|
||||
@@ -66,10 +66,10 @@ index e9d50a9331bf32946e5174e3c6399251b8e52ecb..a0d84cde22c11cef0172b0e151c50d76
|
||||
+ } else {
|
||||
+ user_message
|
||||
+ };
|
||||
let prompt_text_for_hook = user_message.clone();
|
||||
let prompt_text_for_hook = Some(user_message.clone());
|
||||
{
|
||||
if trace_gcs_config.is_some() {
|
||||
@@ -790,9 +796,7 @@ impl SessionActor {
|
||||
@@ -832,9 +838,7 @@ impl SessionActor {
|
||||
}
|
||||
super::super::PromptOrigin::PlanResume => ConversationItem::user(user_message),
|
||||
super::super::PromptOrigin::User => {
|
||||
@@ -81,10 +81,10 @@ index e9d50a9331bf32946e5174e3c6399251b8e52ecb..a0d84cde22c11cef0172b0e151c50d76
|
||||
.events
|
||||
.take_prior_interrupt_category()
|
||||
diff --git a/crates/codegen/xai-grok-shell/src/session/acp_session_tests/cancel_running_task_tests.rs b/crates/codegen/xai-grok-shell/src/session/acp_session_tests/cancel_running_task_tests.rs
|
||||
index 5fed4c6a9ed403990385898903141fdac785f7cf..6a58ef9ab31dd7260e4f776fcbec9cbbbee10828 100644
|
||||
index 391b6dc17a46bb37d86bb479822001adbefdb1cd..485c5aa81b467271ac848ac5323ef04e9640c6b4 100644
|
||||
--- a/crates/codegen/xai-grok-shell/src/session/acp_session_tests/cancel_running_task_tests.rs
|
||||
+++ b/crates/codegen/xai-grok-shell/src/session/acp_session_tests/cancel_running_task_tests.rs
|
||||
@@ -1476,7 +1476,15 @@ async fn handle_prompt_frames_interrupt_on_user_message() {
|
||||
@@ -1492,7 +1492,15 @@ async fn handle_prompt_frames_interrupt_on_user_message() {
|
||||
.expect("the user message must be in the conversation");
|
||||
let text = user.text_content();
|
||||
let expected_assembled = format!("<user_query>\n{query}\n</user_query>");
|
||||
@@ -101,7 +101,7 @@ index 5fed4c6a9ed403990385898903141fdac785f7cf..6a58ef9ab31dd7260e4f776fcbec9cbb
|
||||
assert!(!actor.events.take_pending_interrupt_reminder());
|
||||
prompt_task.abort();
|
||||
})
|
||||
@@ -1573,12 +1581,18 @@ async fn handle_prompt_send_now_frames_interjection_envelope() {
|
||||
@@ -1589,12 +1597,18 @@ async fn handle_prompt_send_now_frames_interjection_envelope() {
|
||||
})
|
||||
.expect("the send-now user message must be in the conversation");
|
||||
let expected_assembled = format!("<user_query>\n{query}\n</user_query>");
|
||||
@@ -216,7 +216,7 @@ index b84ecab7966431a349ff7d264434cb1cf2901865..5665800490fa8fce4811275041959350
|
||||
use xai_grok_tools::types::output::{MCPOutput, ToolOutput, ToolRunResult};
|
||||
use xai_grok_tools::util::base64_images::{ExtractedImage, IMAGE_CONTENT_PLACEHOLDER};
|
||||
diff --git a/crates/codegen/xai-grok-shell/src/session/compaction.rs b/crates/codegen/xai-grok-shell/src/session/compaction.rs
|
||||
index 1b4f84ac1dcca97133abf6d2f9ebce5d289ee98b..2b0f02a8128f4cea7feb4c2facd51625821e86d1 100644
|
||||
index 0225e170b5b70e24e5ddbd598aa9422a010a1e02..e169f7001473a79b98d4cf373dd12727221fbcaa 100644
|
||||
--- a/crates/codegen/xai-grok-shell/src/session/compaction.rs
|
||||
+++ b/crates/codegen/xai-grok-shell/src/session/compaction.rs
|
||||
@@ -31,7 +31,79 @@ use xai_chat_state::compaction_utils::{
|
||||
|
||||
@@ -10,10 +10,10 @@ one key. Do not agent-scope: that would split the prefix those side
|
||||
calls are built to ride.
|
||||
|
||||
diff --git a/crates/codegen/xai-chat-state/src/actor/request_builder.rs b/crates/codegen/xai-chat-state/src/actor/request_builder.rs
|
||||
index 4d9ce755a316ab0cca430aa3126d3ceb18e541b5..7c787b6dce09682445438274ed5a35008f242a6d 100644
|
||||
index 64f497a0b0419b167faea18e35b32b941080620e..109a97521e590dc1f14d367c3d349600001558f8 100644
|
||||
--- a/crates/codegen/xai-chat-state/src/actor/request_builder.rs
|
||||
+++ b/crates/codegen/xai-chat-state/src/actor/request_builder.rs
|
||||
@@ -105,7 +105,10 @@ impl ChatStateActor {
|
||||
@@ -106,7 +106,10 @@ impl ChatStateActor {
|
||||
x_grok_deployment_id: None,
|
||||
x_grok_user_id: None,
|
||||
trace,
|
||||
@@ -26,7 +26,7 @@ index 4d9ce755a316ab0cca430aa3126d3ceb18e541b5..7c787b6dce09682445438274ed5a3500
|
||||
json_schema: None,
|
||||
}
|
||||
diff --git a/crates/codegen/xai-chat-state/src/actor/tests.rs b/crates/codegen/xai-chat-state/src/actor/tests.rs
|
||||
index 604a121b4897d1e245a15f99318130af0ad02462..e9899af98ecf80eb541ce120f4aad489e0e25dae 100644
|
||||
index 07d581d48e78a7c69e78a7f0d8d2ce1844a3190b..dee7b542755b8d97dfcc4e621d01bb809488d017 100644
|
||||
--- a/crates/codegen/xai-chat-state/src/actor/tests.rs
|
||||
+++ b/crates/codegen/xai-chat-state/src/actor/tests.rs
|
||||
@@ -1576,6 +1576,7 @@ async fn build_request_includes_all_messages() {
|
||||
@@ -38,10 +38,10 @@ index 604a121b4897d1e245a15f99318130af0ad02462..e9899af98ecf80eb541ce120f4aad489
|
||||
|
||||
#[tokio::test]
|
||||
diff --git a/crates/codegen/xai-grok-sampling-types/src/conversation.rs b/crates/codegen/xai-grok-sampling-types/src/conversation.rs
|
||||
index 982839bbe822874651bc37e257e4e4a7442f6f87..91d61f3a46524cdf5c11cb753c739abe8481f503 100644
|
||||
index 7bd0cd870c5400ef4d3fb2ba6ce50cfc53ffd62c..db582daced370d89cc1158d5cbbf81b7a156ba4f 100644
|
||||
--- a/crates/codegen/xai-grok-sampling-types/src/conversation.rs
|
||||
+++ b/crates/codegen/xai-grok-sampling-types/src/conversation.rs
|
||||
@@ -623,10 +623,29 @@ pub struct ConversationRequest {
|
||||
@@ -627,10 +627,29 @@ pub struct ConversationRequest {
|
||||
/// JSON Schema for structured output (strict mode).
|
||||
pub json_schema: Option<serde_json::Value>,
|
||||
/// Sticky routing key for prompt-cache reuse; overrides `x_grok_conv_id` for routing.
|
||||
@@ -71,7 +71,7 @@ index 982839bbe822874651bc37e257e4e4a7442f6f87..91d61f3a46524cdf5c11cb753c739abe
|
||||
/// Strip every image; returns the stripped URLs.
|
||||
pub fn strip_images(&mut self) -> Vec<Arc<str>> {
|
||||
strip_images_where(&mut self.items, |_| true)
|
||||
@@ -2399,6 +2418,35 @@ mod tests {
|
||||
@@ -2416,6 +2435,35 @@ mod tests {
|
||||
use crate::tool_overrides::*;
|
||||
use assert_matches::assert_matches;
|
||||
|
||||
@@ -108,15 +108,24 @@ index 982839bbe822874651bc37e257e4e4a7442f6f87..91d61f3a46524cdf5c11cb753c739abe
|
||||
#[test]
|
||||
fn prompt_cache_key_reaches_the_wire_only_where_the_backend_claims() {
|
||||
diff --git a/crates/codegen/xai-grok-sampling-types/src/conversation/chat_completions.rs b/crates/codegen/xai-grok-sampling-types/src/conversation/chat_completions.rs
|
||||
index dac77d82f7f96836f5f4ed26f2e12eb13164e4f5..b80820fbbf3c96e348f44880c432361eb537817a 100644
|
||||
index dac77d82f7f96836f5f4ed26f2e12eb13164e4f5..05a683317ae774f3ba674202ff12ae3ae50f56ec 100644
|
||||
--- a/crates/codegen/xai-grok-sampling-types/src/conversation/chat_completions.rs
|
||||
+++ b/crates/codegen/xai-grok-sampling-types/src/conversation/chat_completions.rs
|
||||
@@ -295,7 +295,7 @@ impl From<ConversationRequest> for ChatCompletionRequest {
|
||||
@@ -251,6 +251,8 @@ impl From<ChatResponseMessage> for ConversationItem {
|
||||
|
||||
impl From<ConversationRequest> for ChatCompletionRequest {
|
||||
fn from(req: ConversationRequest) -> Self {
|
||||
+ // Copy before any field moves — `session_cache_key` borrows `req`.
|
||||
+ let user = req.session_cache_key().map(str::to_owned);
|
||||
let messages: Vec<ChatRequestMessage> = conversation_to_chat_messages(req.items);
|
||||
|
||||
let tools_is_empty = req.tools.is_empty();
|
||||
@@ -295,7 +297,7 @@ impl From<ConversationRequest> for ChatCompletionRequest {
|
||||
top_p: req.top_p,
|
||||
frequency_penalty: None,
|
||||
presence_penalty: None,
|
||||
- user: None,
|
||||
+ user: req.session_cache_key().map(str::to_owned),
|
||||
+ user,
|
||||
tools,
|
||||
tool_choice,
|
||||
search_parameters: None,
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
# Pinned upstream revision of https://github.com/xai-org/grok-build.git
|
||||
# Bump with: make update (never edit by hand unless you know why)
|
||||
eb267feff13129e568df38fb6fdf0ceb65f735d6
|
||||
5163763e703c319e4554c2f455535c5adb6e51e8
|
||||
|
||||
Reference in New Issue
Block a user