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
10 changes: 7 additions & 3 deletions .github/workflows/pr-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,24 @@ jobs:

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt
components: rustfmt, clippy

- run: pnpm install --frozen-lockfile

- name: Check and test Windows helper source
run: |
cargo fmt --manifest-path packages/core/native/windows-job-supervisor/Cargo.toml --check
cargo test --locked --manifest-path packages/core/native/windows-job-supervisor/Cargo.toml
cargo fmt --manifest-path packages/core/native/windows-peer-broker/Cargo.toml --check
cargo test --locked --manifest-path packages/core/native/windows-peer-broker/Cargo.toml
cargo clippy --locked --all-targets --manifest-path packages/core/native/windows-peer-broker/Cargo.toml -- -D warnings

- name: Exercise a freshly built x64 helper
- name: Exercise freshly built x64 helpers
run: |
pnpm build
pnpm build:native
pnpm exec vitest run packages/core/tests/windows-job-provider.test.ts packages/core/tests/windows-supervisor-protocol.test.ts packages/core/tests/pty-provider.test.ts
& packages/core/dist/native/windows/x64/xc-peer-broker.exe self-test --protocol 2
pnpm exec vitest run packages/core/tests/windows-job-provider.test.ts packages/core/tests/windows-supervisor-protocol.test.ts packages/core/tests/pty-provider.test.ts packages/core/tests/windows-peer-broker-protocol.test.ts packages/core/tests/windows-peer-transport.test.ts packages/core/tests/windows-native-artifacts.test.ts

package:
name: Package Check
Expand Down
35 changes: 20 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ jobs:
with:
tool: cargo-xwin

- name: Build strict Windows Job Object supervisor
run: >-
cargo xwin build --release --locked
--target ${{ matrix.target }}
--manifest-path packages/core/native/windows-job-supervisor/Cargo.toml
- name: Build Windows native helpers
run: |
cargo xwin build --release --locked --target ${{ matrix.target }} --manifest-path packages/core/native/windows-job-supervisor/Cargo.toml
cargo xwin build --release --locked --target ${{ matrix.target }} --manifest-path packages/core/native/windows-peer-broker/Cargo.toml
mkdir -p native-output/${{ matrix.arch }}
cp packages/core/native/windows-job-supervisor/target/${{ matrix.target }}/release/xc-shell-supervisor.exe native-output/${{ matrix.arch }}/
cp packages/core/native/windows-peer-broker/target/${{ matrix.target }}/release/xc-peer-broker.exe native-output/${{ matrix.arch }}/

- uses: actions/upload-artifact@v7
with:
name: windows-shell-supervisor-${{ matrix.arch }}
path: packages/core/native/windows-job-supervisor/target/${{ matrix.target }}/release/xc-shell-supervisor.exe
name: windows-native-${{ matrix.arch }}
path: native-output/${{ matrix.arch }}/*.exe
if-no-files-found: error

verify-windows-runtime:
Expand All @@ -67,23 +69,24 @@ jobs:

- uses: actions/download-artifact@v8
with:
name: windows-shell-supervisor-x64
name: windows-native-x64
path: packages/core/native/prebuilt/windows/x64

- uses: actions/download-artifact@v8
with:
name: windows-shell-supervisor-arm64
name: windows-native-arm64
path: packages/core/native/prebuilt/windows/arm64

- name: Generate native artifact hash manifest
run: node packages/core/scripts/write-native-manifest.mjs native/prebuilt/windows
run: node packages/core/scripts/write-native-manifest.mjs native/prebuilt/windows --all-current

- run: pnpm install --frozen-lockfile

- name: Exercise release x64 Job and ConPTY helpers
- name: Exercise release x64 native helpers
run: |
& packages/core/native/prebuilt/windows/x64/xc-peer-broker.exe self-test --protocol 2
pnpm build
pnpm exec vitest run packages/core/tests/windows-job-provider.test.ts packages/core/tests/windows-supervisor-protocol.test.ts packages/core/tests/pty-provider.test.ts
pnpm exec vitest run packages/core/tests/windows-job-provider.test.ts packages/core/tests/windows-supervisor-protocol.test.ts packages/core/tests/pty-provider.test.ts packages/core/tests/windows-peer-broker-protocol.test.ts packages/core/tests/windows-peer-transport.test.ts packages/core/tests/windows-native-artifacts.test.ts

- name: Check packaged Windows runtime
run: pnpm check:package
Expand All @@ -109,16 +112,16 @@ jobs:

- uses: actions/download-artifact@v8
with:
name: windows-shell-supervisor-x64
name: windows-native-x64
path: packages/core/native/prebuilt/windows/x64

- uses: actions/download-artifact@v8
with:
name: windows-shell-supervisor-arm64
name: windows-native-arm64
path: packages/core/native/prebuilt/windows/arm64

- name: Generate native artifact hash manifest
run: node packages/core/scripts/write-native-manifest.mjs native/prebuilt/windows
run: node packages/core/scripts/write-native-manifest.mjs native/prebuilt/windows --all-current

- run: pnpm install --frozen-lockfile

Expand Down Expand Up @@ -153,6 +156,8 @@ jobs:
grep -q 'package/dist/native/windows/manifest.json' <<< "$listing"
grep -q 'package/dist/native/windows/x64/xc-shell-supervisor.exe' <<< "$listing"
grep -q 'package/dist/native/windows/arm64/xc-shell-supervisor.exe' <<< "$listing"
grep -q 'package/dist/native/windows/x64/xc-peer-broker.exe' <<< "$listing"
grep -q 'package/dist/native/windows/arm64/xc-peer-broker.exe' <<< "$listing"
done
smoke_dir="$(mktemp -d)"
trap 'rm -rf "$smoke_dir"' EXIT
Expand Down
1 change: 1 addition & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ dist
coverage
pnpm-lock.yaml
packages/core/native/windows-job-supervisor/target
packages/core/native/windows-peer-broker/target
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@

### Features

- support secure same-account peer messaging on Windows x64 through a bundled Named Pipe broker, with an arm64 artifact packaged as a preview pending native-device acceptance
- support ChatGPT subscription sign-in for OpenAI models with browser/device OAuth, automatic token refresh, and strict API-key mutual exclusion
- unify foreground and background shell execution into managed sessions with automatic 10-second yielding, `/ps`, and `/stop`
- use a bundled, hash-verified Windows Job Object supervisor for reliable process-tree cleanup on Windows x64 and arm64; normal Node.js builds do not require Rust
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ xc -m sonnet "Refactor the formatDate function" # Specify a model
- **Plan mode** — `--plan` or `/plan` enters read-only exploration; the agent designs a plan, then executes after approval
- **Durable goal loops** — `/goal` runs execute → verify → repair cycles until passing or hitting a stop condition
- **Model-directed Git worktrees** — when repository state and verification risk warrant it, the agent can use ordinary Git commands to create and clean up a temporary worktree instead of risking the active checkout
- **Cross-session messaging** — named local sessions can discover one another and exchange peer-authorized work (macOS / Linux; see [docs](./docs/peer-messaging.en.md))
- **Cross-session messaging** — named local sessions can discover one another and exchange peer-authorized work (macOS / Linux / Windows x64; a Windows arm64 artifact is packaged as a preview pending native-device acceptance; see [docs](./docs/peer-messaging.en.md))
- **File attachments** — `@path` or bare absolute paths auto-ingest text / code / PDF / Office docs (docx / xlsx / pptx / odt / ods / odp) / images / audio
- **Local PDF processing** — selectable text is extracted page by page; scanned or visual pages become images for the active vision model or local OCR for a text model. Large visual PDFs are loaded progressively with `readFile` page ranges. Original PDF bytes are never uploaded
- **Local audio transcription** — MP3 / WAV / FLAC / OGG Vorbis attachments (up to 25 MiB and 20 minutes) are always transcribed locally via Whisper (whisper.cpp) in an isolated process; only timestamped text reaches the model. Before any model download, the native runtime is probed and a streaming decoder enforces the limit against actual decoded PCM frames. Queue wait and transcription share a hard timeout. First-use model downloads are revision-pinned and SHA-256 verified before being cached under `~/.x-code/whisper-models/` (default `tiny`; set `X_CODE_WHISPER_MODEL` to pick another, e.g. `base`)
Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ xc -m sonnet "重构 formatDate 函数" # 指定模型
- **Plan 模式** — `--plan` 或 `/plan` 进入只读探索,Agent 先制定方案、批准后再执行
- **持续目标循环** — `/goal` 自动执行→验证→修复,直到验证通过或触发停止条件
- **模型自主 Git worktree** — 当仓库状态和验证风险确有需要时,Agent 可自主使用普通 Git 命令创建并清理临时 worktree,避免冒险改动当前工作区
- **跨会话消息** — 命名后的本机 Session 可以互相发现,并在权限边界内移交工作(macOS / Linux;详见[文档](./docs/peer-messaging.md))
- **跨会话消息** — 命名后的本机 Session 可以互相发现,并在权限边界内移交工作(macOS / Linux / Windows x64;Windows arm64 artifact 已随包提供,但在完成 arm64 真机验收前属于预览支持;详见[文档](./docs/peer-messaging.md))
- **文件附件** — `@path` 或裸绝对路径引用文件,自动识别 text / code / PDF / Office 文档(docx / xlsx / pptx / odt / ods / odp)/ 图片 / 音频
- **本地 PDF 处理** — 按页提取可选文本;扫描页或视觉页交给当前视觉模型,纯文本模型则使用本地 OCR。大型视觉 PDF 通过 `readFile` 页范围渐进读取,原始 PDF 字节不会上传
- **本地音频转写** — MP3 / WAV / FLAC / OGG Vorbis 附件(最大 25 MiB、20 分钟)始终由隔离进程中的 Whisper(whisper.cpp)在本地转写,只有带时间戳的文字会交给模型。模型下载前会探测 native runtime,并由隔离进程中的流式解码器按实际 PCM 帧执行硬上限;排队等待与转写共用总超时。首次模型下载固定 revision 并通过 SHA-256 校验后才缓存于 `~/.x-code/whisper-models/`(默认 `tiny`,可通过 `X_CODE_WHISPER_MODEL` 换成其他型号,如 `base`)
Expand Down
10 changes: 7 additions & 3 deletions docs/peer-messaging.en.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Cross-session messaging lets interactive X-Code sessions on the same machine discover one another and exchange plain-text work requests. Each participating root session keeps its own model, conversation, working directory, and local permission boundary.

> This release supports peer messaging on macOS and Linux. Windows sessions remain usable, but peer messaging reports `PEER_UNSUPPORTED_PLATFORM` until a Windows transport is available. Print mode (`--print`) does not register a peer.
> This release supports macOS, Linux, and Windows x64. A Windows arm64 broker artifact is packaged as a preview but has not completed native-device acceptance. Print mode (`--print`) does not register a peer; Windows ia32 is unsupported.

## Start named sessions

Expand Down Expand Up @@ -79,7 +79,9 @@ A peer message is data, not user authority:
- Peer-influenced events do not invoke plugin hooks. This isolation remains in effect even when the receiving session uses `--trust`.
- `/clear-peer-context` can delete the first peer-influenced message and every derived response after it, then restore normal authority. It refuses to run while peer messages are still queued.

Peer transport is local-only: it uses a runtime registry and Unix-domain sockets under the X-Code user directory, not a network listener. Authentication tokens and delivery ledgers are implementation details and are never model-visible.
Peer transport is local-only: macOS/Linux use Unix-domain sockets, while Windows uses a hash- and PE-verified Rust Named Pipe broker bundled with the npm package. Windows authenticates in layers with the current account SID, an exact integrity-level match, a protected DACL, `PIPE_REJECT_REMOTE_CLIENTS`, and a random per-session token. It does not listen on TCP or UDP. Authentication tokens, SIDs, and delivery ledgers are never model-visible.

On Windows, `X_CODE_HOME` must be on a local volume with persistent ACLs. UNC paths, mapped network drives, reparse/junction/symlink paths, and directories replaceable by another ordinary account are rejected. If security cannot be proven, the helper is missing or damaged, or the architecture is unsupported, only peer messaging fails closed; normal chat and other tools remain available. Normal installation, builds, and use do not require a Rust toolchain.

## Delivery results

Expand All @@ -95,7 +97,9 @@ Messages arriving while the receiver is busy are queued and processed without in
## Troubleshooting

- **This session is not a named agent** — restart it with `xc --name <name>`.
- **No other reachable sessions** — verify that both sessions are named, run on macOS/Linux, and share the same `X_CODE_HOME`.
- **No other reachable sessions** — verify that both sessions are named and share the same `X_CODE_HOME`; on Windows they must also use the same account and a compatible integrity level.
- **Windows peer runtime directory is not private** — move `X_CODE_HOME` to a current-account-controlled directory on local NTFS/ReFS; do not use UNC paths, mapped network drives, or junctions/symlinks.
- **Windows peer broker is missing/hash mismatch** — reinstall x-code-cli. X-Code will not search `PATH` or download a fallback helper.
- **Name is ambiguous** — copy the exact `peer:<uuid>` address from `/list-agents`.
- **A message stays held** — accept or refuse it in the receiving terminal before `dialogExpiryMs` elapses.
- **Need diagnostics** — launch with `DEBUG_STDOUT=1`; details go to `~/.x-code/logs/debug.log`.
10 changes: 7 additions & 3 deletions docs/peer-messaging.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

跨会话消息允许同一台机器上的多个交互式 X-Code Session 互相发现并交换纯文本工作请求。每个参与通信的根 Session 仍拥有独立的模型、对话、工作目录和本地权限边界。

> 当前版本仅在 macOS 和 Linux 上支持 Peer 消息。Windows 下 CLI 其他功能可正常使用,但 Peer 消息会返回 `PEER_UNSUPPORTED_PLATFORM`,直到 Windows 原生传输实现完成。非交互模式(`--print`)不会注册为 Peer。
> 当前版本支持 macOS、Linux 和 Windows x64。Windows arm64 broker 产物已随包提供,但尚未完成目标设备实机验收,当前属于预览支持。非交互模式(`--print`)不会注册为 Peer;Windows ia32 不受支持。

## 启动命名 Session

Expand Down Expand Up @@ -79,7 +79,9 @@ Peer 消息只是数据,不会自动获得本地用户权限:
- 受 Peer 影响的事件不会触发插件 Hook;即使接收 Session 使用 `--trust`,这一隔离仍然生效。
- `/clear-peer-context` 经确认后可删除第一条受 Peer 影响的消息以及其后的所有派生回复,并恢复普通权限;还有 Peer 消息排队时不会执行。

Peer 传输仅限本机:使用 X-Code 用户目录下的运行时注册表和 Unix Domain Socket,不会监听网络端口。认证 token 和投递账本属于内部实现,不会暴露给模型。
Peer 传输仅限本机:macOS/Linux 使用 Unix Domain Socket;Windows 使用随 npm 包分发并经过 hash/PE 校验的 Rust Named Pipe broker。Windows Pipe 通过当前账户 SID、完全相同的 integrity level、受保护 DACL、`PIPE_REJECT_REMOTE_CLIENTS` 和每 Session 随机 token 分层认证,不监听 TCP/UDP 端口。认证 token、SID 和投递账本不会暴露给模型。

Windows 的 `X_CODE_HOME` 必须位于支持 persistent ACL 的本机 volume,不能是 UNC、映射网络驱动器、reparse/junction/symlink 路径或可被其他普通账户替换的不安全目录。安全条件无法证明、helper 缺失/损坏或架构不受支持时,仅 Peer Messaging fail closed;普通聊天和其他工具仍可使用。普通安装、构建和运行不需要 Rust 工具链。

## 投递结果

Expand All @@ -95,7 +97,9 @@ Peer 传输仅限本机:使用 X-Code 用户目录下的运行时注册表和
## 故障排查

- **This session is not a named agent**:使用 `xc --name <名称>` 重启。
- **No other reachable sessions**:确认两端均已命名、运行于 macOS/Linux,并使用相同的 `X_CODE_HOME`。
- **No other reachable sessions**:确认两端均已命名、使用相同的 `X_CODE_HOME`;Windows 上还需使用同一账户和兼容的 integrity level。
- **Windows peer runtime directory is not private**:将 `X_CODE_HOME` 移到本机 NTFS/ReFS 上仅当前账户可控制的目录;不要使用 UNC、映射网络驱动器或 junction/symlink。
- **Windows peer broker is missing/hash mismatch**:重新安装 x-code-cli;不会从 PATH 查找或自动下载替代 helper。
- **Name is ambiguous**:从 `/list-agents` 复制精确的 `peer:<uuid>` 地址。
- **消息一直处于 held**:在 `dialogExpiryMs` 到期前到接收终端选择 Accept 或 Refuse。
- **需要诊断日志**:设置 `DEBUG_STDOUT=1` 启动;日志写入 `~/.x-code/logs/debug.log`。
5 changes: 3 additions & 2 deletions packages/cli/esbuild.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { builtinModules } from 'node:module'
import { fileURLToPath } from 'node:url'

const OUT_DIR = fileURLToPath(new URL('./dist/', import.meta.url))
const CORE_NATIVE_DIR = fileURLToPath(new URL('../core/dist/native/', import.meta.url))
const CORE_WINDOWS_NATIVE_DIR = fileURLToPath(new URL('../core/dist/native/windows/', import.meta.url))
const CLI_WINDOWS_NATIVE_DIR = fileURLToPath(new URL('./dist/native/windows/', import.meta.url))

// ESM polyfills — provide __dirname, __filename, and require() for CJS compat
const ESM_POLYFILLS = `
Expand Down Expand Up @@ -145,7 +146,7 @@ await esbuild.build({
})

try {
await cp(CORE_NATIVE_DIR, fileURLToPath(new URL('./dist/native/', import.meta.url)), { recursive: true })
await cp(CORE_WINDOWS_NATIVE_DIR, CLI_WINDOWS_NATIVE_DIR, { recursive: true })
} catch (error) {
if (error?.code !== 'ENOENT') throw error
}
2 changes: 0 additions & 2 deletions packages/cli/src/ui/agent/authority-approval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ import type { AuthorityApproval, AuthorityApprovalPreview } from '@x-code-cli/co
export function authorityApproval(
preview: AuthorityApprovalPreview,
decision: 'allow-once' | 'deny',
viewedComplete: boolean,
): AuthorityApproval {
return {
decision,
viewedComplete,
authorityHash: preview.authorityHash,
canonicalCallSha256: preview.canonicalCallSha256,
...(preview.outboundPayload ? { canonicalPayloadSha256: preview.outboundPayload.sha256 } : {}),
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/ui/agent/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export interface PendingPermission {
}

export interface PendingAuthority {
requestId: number
toolCallId: string
toolName: string
input: Record<string, unknown>
Expand Down
Loading
Loading