pi-grok-agent 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +222 -87
- package/docs/usage.md +2 -0
- package/package.json +1 -1
- package/src/model/provider.ts +1 -0
- package/src/model/session.ts +27 -4
package/README.md
CHANGED
|
@@ -2,57 +2,99 @@
|
|
|
2
2
|
|
|
3
3
|
Grok's agent. Pi's workflow.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
```sh
|
|
6
|
+
pi install npm:pi-grok-agent
|
|
7
|
+
```
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
That is the whole installation. Then pick a Grok model in Pi's `/models` and send a message. See the [quick start](#quick-start).
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
This package runs [Grok Build](https://docs.x.ai/build/overview) as a model in the [Pi coding agent](https://github.com/earendil-works/pi). Grok keeps its own harness, its native tools, and its session history. Pi drives the turns and adds what it gives every model: the transcript, permission dialogs, tool gates, and extension tools.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
Two things this package is not:
|
|
14
|
+
|
|
15
|
+
- It connects the Grok Build agent over ACP, not the xAI chat-completions API. You need the Grok Build CLI and its own login.
|
|
16
|
+
- Its tool gates are not an operating-system sandbox. Grok runs with your user's permissions.
|
|
15
17
|
|
|
16
|
-
|
|
18
|
+
Version 0.1.1 on [npm](https://www.npmjs.com/package/pi-grok-agent) and as a [GitHub release](https://github.com/JangMan-J/pi-grok-agent/releases/tag/v0.1.1). Developed and tested on Linux with Node.js 26.10.0, Pi 0.87.1, and Grok Build 1.0.41. Other versions and platforms are untested. The recorded runs are in [docs/launch-verification.md](docs/launch-verification.md).
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
The tool gates are not an operating-system sandbox.
|
|
20
|
+
[Requirements](#requirements) · [Quick start](#quick-start) · [First result](#first-result) · [What Pi adds](#what-pi-adds) · [Models](#models) · [Limitations](#limitations) · [Safety](#safety) · [Reference](docs/usage.md)
|
|
20
21
|
|
|
21
22
|
## Requirements
|
|
22
23
|
|
|
23
24
|
| Item | Requirement |
|
|
24
25
|
| --- | --- |
|
|
25
|
-
|
|
|
26
|
-
|
|
|
27
|
-
|
|
|
28
|
-
|
|
|
29
|
-
|
|
|
30
|
-
| Optional | A terminal with inline image support. ImageMagick 7 (`magick`)
|
|
26
|
+
| Grok account | A [grok.com](https://grok.com) account, any membership tier. A free account works: verified live on 2026-09-28 with Grok Build 1.0.41, where headless turns returned `PONG` and a read-only tool turn answered correctly ([docs/launch-verification.md](docs/launch-verification.md)). Grok usage counts against that account. |
|
|
27
|
+
| Grok Build CLI | `grok` on `PATH`, or its path in `PI_GROK_BINARY`. |
|
|
28
|
+
| Grok login | Only if Grok Build is not signed in yet. Run `/grok login` in Pi, or `grok login` in a terminal. Both use Grok's own device-code sign-in, and Grok keeps the credential in `~/.grok/auth.json`; Pi stores nothing (`src/login.ts`). Pi's `/login` xAI entry is a separate login and does not sign in Grok Build, and `XAI_API_KEY` does not replace it. |
|
|
29
|
+
| Pi | Tested with 0.87.1. Other versions are untested. [Install Pi first](https://github.com/earendil-works/pi#quick-start). |
|
|
30
|
+
| Node.js | 22.19 or later, Pi's minimum (`engines` in `package.json`). Only 26.10.0 is tested here. |
|
|
31
|
+
| Optional | A terminal with inline image support. ImageMagick 7 (`magick`) shows JPEG, WebP, and GIF inline; PNG needs no converter. |
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Pi shows the cost Grok reports for each turn. Which models your account may use is Grok's decision, not this package's — see [Models](#models).
|
|
33
34
|
|
|
34
35
|
## Quick start
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
1. Install the package:
|
|
38
|
+
|
|
39
|
+
```sh
|
|
40
|
+
pi install npm:pi-grok-agent
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
2. Start Pi in your project and pick a Grok model in `/models`: Grok 4.7, Grok 4.7 Build Fast, Grok 4.6, or Grok 4.5.
|
|
44
|
+
3. If Grok Build is not signed in, run `/grok login` and approve the code in your browser. Grok runs its own device-code sign-in and stores the credential in `~/.grok/auth.json`; Pi stores nothing (`src/login.ts`).
|
|
45
|
+
4. Send a message and watch the `grok-tool` lines appear. See [First result](#first-result) for what a healthy first turn looks like.
|
|
46
|
+
|
|
47
|
+
The first Grok turn starts the local gateway that ships with the package and waits for it, about 5 seconds. The gateway listens on loopback at `127.0.0.1:2419`; `GROK_ACP_URL` picks another loopback port.
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
<details>
|
|
52
|
+
<summary>Diagram source (mermaid)</summary>
|
|
53
|
+
|
|
54
|
+
```mermaid
|
|
55
|
+
flowchart LR
|
|
56
|
+
subgraph PI["Pi process (one per terminal)"]
|
|
57
|
+
direction TB
|
|
58
|
+
UI["Pi TUI<br/>transcript, dialogs, /models"]
|
|
59
|
+
EXT["pi-grok-agent extension<br/>provider grok/*"]
|
|
60
|
+
TOOLS["Pi extension tools"]
|
|
61
|
+
UI --- EXT
|
|
62
|
+
EXT --- TOOLS
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
subgraph GW["Gateway (one per machine, 127.0.0.1:2419)"]
|
|
66
|
+
direction TB
|
|
67
|
+
WS["WebSocket /ws<br/>bearer secret"]
|
|
68
|
+
GUARD["Reverse-request guard<br/>ack deadlines, fail closed"]
|
|
69
|
+
MCP["MCP relay<br/>HTTP /mcp/<token>"]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
subgraph GROK["Grok Build"]
|
|
73
|
+
direction TB
|
|
74
|
+
BRIDGE["grok agent --leader stdio<br/>one bridge per Pi connection"]
|
|
75
|
+
LEADER["grok agent leader<br/>harness, native tools, subagents"]
|
|
76
|
+
STORE[("~/.grok<br/>login, sessions")]
|
|
77
|
+
BRIDGE --- LEADER
|
|
78
|
+
LEADER --- STORE
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
EXT == "ACP over WebSocket" ==> WS
|
|
82
|
+
WS == "stdio" ==> BRIDGE
|
|
83
|
+
WS -.- GUARD
|
|
84
|
+
LEADER -. "lent-tool calls (HTTP)" .-> MCP
|
|
85
|
+
MCP -. "_x.ai/mcp/sdk_call" .-> EXT
|
|
86
|
+
EXT -. "starts it if nothing listens" .-> GW
|
|
39
87
|
```
|
|
40
88
|
|
|
41
|
-
|
|
89
|
+
</details>
|
|
42
90
|
|
|
43
|
-
Pi
|
|
91
|
+
Pi drives the turn and Grok drives the callbacks, both over the Agent Client Protocol. [How it works →](docs/usage.md#components) · [Gateway guard →](docs/usage.md#gateway-guard) · [Design notes →](docs/first-class-model.md)
|
|
44
92
|
|
|
45
|
-
|
|
93
|
+
There is no global install and no gateway to start by hand: the recorded install runs used only `pi install npm:pi-grok-agent`, with nothing listening on that port and no `pi-grok-gateway` on `PATH` ([docs/launch-verification.md](docs/launch-verification.md), rows `G2, npm, one command` and `G2, public npm`).
|
|
46
94
|
|
|
47
|
-
|
|
48
|
-
git clone https://github.com/JangMan-J/pi-grok-agent.git
|
|
49
|
-
cd pi-grok-agent
|
|
50
|
-
npm install --omit=dev
|
|
51
|
-
npm run server # terminal A: the gateway
|
|
52
|
-
pi -e . --model grok/grok-4.7 # terminal B: this Pi process only; or pi install . for every session
|
|
53
|
-
```
|
|
95
|
+
The gateway keeps running after Pi exits, and every Pi process on the machine shares it. Its first start creates the shared secret `~/.pi/agent/grok-ws.secret` with mode 0600 and logs to `~/.pi/agent/grok-ws.log` (`src/launch.ts`, `src/config.ts`). To stop it, disable auto-start, or run it yourself under a service manager, see [gateway auto-start](docs/usage.md#gateway-auto-start).
|
|
54
96
|
|
|
55
|
-
|
|
97
|
+
Pi loads the extension at every start, for every model. To remove it, run `pi remove npm:pi-grok-agent`.
|
|
56
98
|
|
|
57
99
|
## First result
|
|
58
100
|
|
|
@@ -64,113 +106,206 @@ Read package.json and tell me the package name and the npm scripts. Do not chang
|
|
|
64
106
|
|
|
65
107
|
Expected result:
|
|
66
108
|
|
|
67
|
-
- One line for each Grok tool call, for example `✓ grok read_file …` or `✓ grok hashline_read …`, with its duration. Grok chooses the tool.
|
|
109
|
+
- One line for each Grok tool call, for example `✓ grok read_file …` or `✓ grok hashline_read …`, with its duration. Grok chooses the tool, so do not require a particular tool name.
|
|
68
110
|
- Thinking text that contains `[grok <tool>]` lines.
|
|
69
|
-
- An answer that names the package and its scripts.
|
|
111
|
+
- An answer that names the package and its scripts, with no edits.
|
|
70
112
|
- A footer cost that comes from Grok's usage report.
|
|
71
113
|
|
|
114
|
+
This is a check for your installation, not a saved transcript. These display paths have source support in `src/model.ts` and `src/model/session.ts` and unit tests in `test/model.test.ts`.
|
|
115
|
+
|
|
72
116
|
Then run `/grok debug`. It shows the gateway connection, the Grok session ID, the permission modes, token usage, and the lent Pi tools.
|
|
73
117
|
|
|
74
|
-
If the result is different, see [Troubleshooting](docs/usage.md#troubleshooting). Please report what you saw, as described in [Feedback](#feedback).
|
|
118
|
+
If the result is different, see [Troubleshooting](docs/usage.md#troubleshooting). If the model is missing from `/models`, inspect Pi's extension load error. If sign-in fails, use `/grok login`, not Pi's xAI login. Please report what you saw, as described in [Feedback](#feedback).
|
|
119
|
+
|
|
120
|
+
## What Pi adds
|
|
121
|
+
|
|
122
|
+
Grok Build already runs an agent with tools, and it can serve ACP to any client. What this package adds is the rest of Pi around that agent:
|
|
123
|
+
|
|
124
|
+
| You want to… | What happens | Basis |
|
|
125
|
+
| --- | --- | --- |
|
|
126
|
+
| Keep Grok's native tools | Grok executes the tools its own harness offers — file, shell, search, web, subagent, media. Pi records each call in the transcript and executes none of them. | `test/model.test.ts` |
|
|
127
|
+
| See Grok's work as you go | Each native call becomes thinking text plus a `grok-tool` entry, with its status and duration. | `src/model/session.ts` |
|
|
128
|
+
| Control edits from Pi | Pi gates Grok's tools before they run through a `pre_tool_use` hook. A read-only Pi session denies Grok's edits and shell; `/grok perms ask` confirms each call. | `src/model/hooks.ts`, `test/hooks.test.ts` |
|
|
129
|
+
| Decide inside Pi | Grok's permission prompts become Pi dialogs, and Grok's `ask_user_question` becomes one Pi dialog per question. | `src/model/permissions.ts`, `src/model/questions.ts`, `test/questions.test.ts` |
|
|
130
|
+
| Reuse Pi extension tools | Pi lends extension tools to Grok over MCP. Grok calls them as `pi__<name>`, Pi executes the call, and the result continues the same Grok turn. | `src/model/session.ts`, `evidence/model-probe.json` |
|
|
131
|
+
| Feed checks back to Grok | After a Grok edit, a syntax check runs on the file and a failure goes back to Grok in the same turn. A configured `stopCheck` can hold the end of a turn. | `src/model/hooks.ts`, `evidence/hooks-live.json` |
|
|
132
|
+
|
|
133
|
+
### One turn
|
|
134
|
+
|
|
135
|
+

|
|
136
|
+
|
|
137
|
+
<details>
|
|
138
|
+
<summary>Diagram source (mermaid)</summary>
|
|
139
|
+
|
|
140
|
+
```mermaid
|
|
141
|
+
sequenceDiagram
|
|
142
|
+
autonumber
|
|
143
|
+
participant P as Pi (extension)
|
|
144
|
+
participant G as Gateway
|
|
145
|
+
participant L as Grok leader
|
|
146
|
+
|
|
147
|
+
P->>L: initialize, authenticate (cached_token), through the gateway
|
|
148
|
+
alt first turn of this Pi session
|
|
149
|
+
P->>L: session/new: rules = Pi system prompt, client hooks, MCP server "pi"
|
|
150
|
+
else after a reconnect
|
|
151
|
+
P->>L: session/load (same Grok session)
|
|
152
|
+
end
|
|
153
|
+
P->>L: session/set_config_option: model, reasoning_effort
|
|
154
|
+
P->>L: session/prompt: only the new messages
|
|
155
|
+
|
|
156
|
+
loop while Grok works
|
|
157
|
+
L-->>P: session/update: text, thinking, tool calls
|
|
158
|
+
Note over P: Grok's own tools become thinking lines<br/>and grok-tool entries, never Pi tool calls
|
|
159
|
+
L->>G: _x.ai/hooks/run (pre_tool_use)
|
|
160
|
+
G->>P: forwarded, with an ack deadline
|
|
161
|
+
P-->>G: pi/gate-ack, then allow or deny from Pi's tool gate
|
|
162
|
+
G-->>L: answer, or deny if Pi missed the deadline
|
|
163
|
+
opt Grok asks
|
|
164
|
+
L->>P: session/request_permission or _x.ai/ask_user_question (guarded like hooks)
|
|
165
|
+
P-->>L: answer from a Pi dialog, or cancelled if Pi is gone
|
|
166
|
+
end
|
|
167
|
+
opt Grok calls a lent Pi tool
|
|
168
|
+
L->>G: MCP tools/call over HTTP
|
|
169
|
+
G->>P: _x.ai/mcp/sdk_call
|
|
170
|
+
P-->>G: result from the Pi tool
|
|
171
|
+
G-->>L: HTTP response
|
|
172
|
+
end
|
|
173
|
+
opt you press Enter mid-turn
|
|
174
|
+
P->>L: _x.ai/interject
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
L->>G: _x.ai/hooks/run (stop)
|
|
178
|
+
G->>P: forwarded
|
|
179
|
+
P-->>L: continue, or block until the stop check passes
|
|
180
|
+
L-->>P: prompt result: stop reason, usage, cost
|
|
181
|
+
```
|
|
75
182
|
|
|
76
|
-
|
|
183
|
+
</details>
|
|
77
184
|
|
|
78
|
-
|
|
185
|
+
### Gates, checks, and lent tools
|
|
79
186
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
187
|
+
- Grok keeps the full tool results in its own context. Pi shows a shortened copy: 400 characters in the thinking stream, up to 8000 in the stored `grok-tool` entry, and up to 600 in the expanded entry (`src/model/session.ts`, `src/model.ts`). Lent Pi tool results go back to Grok complete.
|
|
188
|
+
- Gate precedence is fixed: a `denyGrokTools` entry always wins, an explicit `allowGrokTools` entry then allows — including past the read-only mirror — and only then does the capability mirror apply. By default, a Pi session without `edit` or `write` denies Grok's edit tools, and a session without `bash` denies Grok's shell (`capabilityGate` in `src/model/hooks.ts`).
|
|
189
|
+
- `/grok perms read-only`, `ask`, `auto`, and `yolo` change Pi's gate. Grok's own permission prompts are a separate layer ([details](docs/usage.md#grok-permission-prompts)).
|
|
190
|
+
- The default lent-tool policy is `extensions`, which excludes Pi's core tools — `read`, `bash`, `edit`, `write`, `grep`, `find`, `ls` (`PI_CORE_TOOLS` in `src/config.ts`).
|
|
191
|
+
- The built-in post-edit syntax check covers TypeScript, JavaScript, Python, JSON, and Rust, chosen by file extension; anything else needs a configured `postEditCheck` (`BUILTIN_CHECKS` in `src/model/hooks.ts`).
|
|
85
192
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
193
|
+
### Turn controls
|
|
194
|
+
|
|
195
|
+
- Pi's thinking level sets Grok's reasoning effort. Escape cancels the Grok turn (`src/model/provider.ts`, `src/model/session.ts`).
|
|
196
|
+
- Mid-turn Enter sends the text to Grok's `_x.ai/interject` method. Unit-tested against a mocked Grok (`test/steer.test.ts`); its effect on a live running turn is not yet verified, and a `grok-steer` entry proves dispatch, not that Grok acted on it. Alt+Enter queues a follow-up turn, as usual in Pi.
|
|
197
|
+
- Grok plan mode, `/goal`, and `/compact` are available through `/grok plan`, `/grok goal`, and `/grok compact`. Send a normal prompt first so a Grok session exists ([commands](docs/usage.md#slash-command-grok)).
|
|
198
|
+
- After a switch from `grok/*` to another model in the same Pi session, mid-turn Enter goes to that model. The stored Grok session is used again when you switch back (`test/extension.test.ts`).
|
|
199
|
+
|
|
200
|
+
## Models
|
|
90
201
|
|
|
91
|
-
|
|
202
|
+
| Model ID | Name in `/models` | Reasoning efforts | Context window |
|
|
203
|
+
| --- | --- | --- | --- |
|
|
204
|
+
| `grok/grok-4.7` | Grok 4.7 | low, medium, high, xhigh | 500,000 tokens |
|
|
205
|
+
| `grok/grok-4.7-build-fast` | Grok 4.7 Build Fast | low, medium, high, xhigh | 500,000 tokens |
|
|
206
|
+
| `grok/grok-4.6` | Grok 4.6 | low, medium, high, xhigh | 500,000 tokens |
|
|
207
|
+
| `grok/grok-4.5` | Grok 4.5 | low, medium, high | 500,000 tokens |
|
|
92
208
|
|
|
93
|
-
|
|
209
|
+
These are the IDs the extension registers (`MODEL_IDS` in `src/model.ts`), not a claim that your account may use all of them. Which models an account has is Grok's decision: `grok models` lists them, and the free account tested here lists only `grok-4.7`. The saved probes used `grok/grok-4.7`.
|
|
94
210
|
|
|
95
|
-
|
|
96
|
-
- Grok keeps the full tool results in its own context. Pi shows a shortened copy: 400 characters in the thinking stream, up to 8000 characters in the stored `grok-tool` entry, and up to 600 characters in the expanded entry.
|
|
97
|
-
- Grok's permission prompts become Pi dialogs. Grok's `ask_user_question` becomes Pi dialogs, one per question.
|
|
98
|
-
- Pi can gate Grok's tools. By default, a Pi session without `edit` or `write` denies Grok's edit tools, and a session without `bash` denies Grok's shell. `/grok perms read-only`, `ask`, `auto`, and `yolo` change this gate. A `denyGrokTools` entry always wins. Grok's own permission prompts are separate ([details](docs/usage.md#grok-permission-prompts)).
|
|
99
|
-
- Optional checks run around Grok's tools. After a Grok edit, a syntax check runs on the file. A failure goes back to Grok in the same turn. A configured `stopCheck` can hold the end of a turn.
|
|
100
|
-
- Pi can lend its extension tools to Grok. They appear to Grok as `pi__<name>`, and Pi executes them.
|
|
101
|
-
- Mid-turn Enter sends the text to Grok's `_x.ai/interject` method. Its effect on the running turn is not yet verified live. Alt+Enter queues a follow-up turn, as usual in Pi.
|
|
102
|
-
- Pi's thinking level sets Grok's reasoning effort. Escape cancels the Grok turn.
|
|
103
|
-
- Grok plan mode, `/goal`, and `/compact` are available through `/grok plan`, `/grok goal`, and `/grok compact`.
|
|
211
|
+
In 0.1.0 the model picked in Pi was never sent to Grok, so every `grok/*` choice ran Grok's default. Fixed in 0.1.1: Pi sets Grok's `model` config option, and a model the account lacks fails the turn with the list of available models instead of quietly running another one.
|
|
104
212
|
|
|
105
|
-
|
|
213
|
+
Pi's metadata also sets a 32,000-token output limit and a per-token cost of zero, because Grok's rates are not published to Pi; the per-turn cost comes from Grok's own usage report. Other extensions and settings can select these models by ID. The full control mapping is in [docs/usage.md](docs/usage.md#models-and-pi-controls).
|
|
106
214
|
|
|
107
215
|
## Generated images and video
|
|
108
216
|
|
|
109
|
-
When a Grok tool result has the type `ImageGen`, `ImageEdit`, `ImageToVideo`, `ReferenceToVideo`, or `VideoGen`, Pi copies the file to `.pi/grok-images/` in the Pi working directory. That directory gets a `.gitignore` that ignores everything in it.
|
|
217
|
+
When a Grok tool result has the type `ImageGen`, `ImageEdit`, `ImageToVideo`, `ReferenceToVideo`, or `VideoGen`, Pi copies the file to `.pi/grok-images/` in the Pi working directory. That directory gets a `.gitignore` that ignores everything in it. If the copy fails or copying is off, the entry keeps Grok's original path (`copyMedia` in `src/model/session.ts`).
|
|
110
218
|
|
|
111
|
-
After the turn, Pi shows a `grok-media` message with the file path. PNG, JPEG, WebP, and GIF images also show inline when the terminal supports images
|
|
219
|
+
After the turn, Pi shows a `grok-media` message with the file path. PNG, JPEG, WebP, and GIF images also show inline when the terminal supports images: PNG shows directly, and Pi converts JPEG, WebP, and GIF to PNG with `magick` first. Without `magick`, you see only the path for those formats. Video files show as a path only; Pi does not play video.
|
|
112
220
|
|
|
113
|
-
Live probes cover `image_gen` only (
|
|
221
|
+
Live probes cover `image_gen` only ([evidence/image-probe.json](evidence/image-probe.json)). The image-edit and video result types are recognized in code but not yet probed with a live Grok run. The media message is for display only: the provider removes it from the prompt, so Grok does not receive its own image back.
|
|
114
222
|
|
|
115
|
-
Images that you attach in Pi go to Grok as a temporary file
|
|
223
|
+
Images that you attach in Pi go to Grok as a temporary file under `pi-grok-images` in the system temp directory, written with mode 0600 (`src/model/provider.ts`). Grok reads the file with its own tools. The path route is the one that works: the probe also sent an ACP image block directly, and Grok did not see it.
|
|
116
224
|
|
|
117
225
|
## Limitations
|
|
118
226
|
|
|
119
227
|
- The auto-started gateway runs until you stop it or log out. With auto-start off, the gateway must run before the first Grok turn; until it has created its secret file, Grok turns fail with a message that names the file and the command.
|
|
120
|
-
- Run one gateway for each port and leader socket. A second
|
|
228
|
+
- Run one gateway for each port and leader socket. A second default launch exits with `EADDRINUSE` and leaves the running gateway and its leader alone. [Run a separate gateway](docs/usage.md#run-a-second-isolated-gateway) for a demo or a test.
|
|
121
229
|
- Grok's native tool calls are not Pi tool calls. Pi records them as thinking text and `grok-tool` entries, and no model receives those entries.
|
|
122
|
-
- Pi compaction and Grok compaction are separate. Pi sends only the new messages of each turn
|
|
230
|
+
- Pi compaction and Grok compaction are separate. Pi sends only the new messages of each turn, and only a new Grok session also receives the earlier Pi transcript as text, cut to the last 60,000 characters. Pi's `/compact` does not compact Grok's history; use `/grok compact`.
|
|
123
231
|
- Grok reads the lent Pi tool list once for each Grok session. A changed tool set needs a new Pi session.
|
|
124
|
-
- A gateway restart loses the turn in progress. The next turn reconnects and loads the same Grok session.
|
|
125
|
-
- Hashline edits (`hashline_read`, `hashline_edit`, `hashline_grep`) occur only when `~/.grok/config.toml` sets `[toolset] file_toolset = "hashline"`. Otherwise Grok uses tools such as `read_file` and `search_replace`.
|
|
126
|
-
- After a switch from `grok/*` to another model in the same Pi session, mid-turn Enter goes to that model, not to the earlier Grok session. The Grok session stays stored and is used again when you switch back.
|
|
232
|
+
- A gateway restart loses the turn in progress. The next turn reconnects and loads the same Grok session. No saved probe covers that restart, so live recovery is unverified ([docs/launch-verification.md](docs/launch-verification.md) lists reconnect as not run).
|
|
233
|
+
- Hashline edits (`hashline_read`, `hashline_edit`, `hashline_grep`) occur only when `~/.grok/config.toml` sets `[toolset] file_toolset = "hashline"`. Otherwise Grok uses tools such as `read_file` and `search_replace`. That switch is Grok's own configuration; this repository does not test it, though `evidence/hooks-probe.json` does show hashline calls.
|
|
127
234
|
- The provider does not call Pi's `onPayload` and `onResponse` stream hooks.
|
|
128
|
-
- Cost per token is
|
|
235
|
+
- Cost per token is zero in the model metadata. The per-turn cost comes from Grok's report, converted at 1e9 ticks per US dollar. That ratio is inferred by cross-check against SuperGrok rates and is not documented by Grok (`src/model/session.ts`). Without a usage report, usage and cost read as zero — that is not evidence of free usage.
|
|
129
236
|
|
|
130
237
|
## Safety
|
|
131
238
|
|
|
132
|
-
- Grok runs with the permissions of your operating-system user. The Grok session directory is not a sandbox.
|
|
133
|
-
- The gateway listens on loopback only and requires the bearer secret for the WebSocket. It never starts Grok with `--always-approve`. Grok sessions use Grok's `default` permission mode unless you set `grokMode
|
|
134
|
-
- `/grok perms yolo`, `grokMode: "yolo"` or `"auto"`, and `headlessPermissions: "allow"` each remove a check. Use them only in a workspace you can lose. [usage.md](docs/usage.md#grok-permission-prompts) explains how they differ.
|
|
135
|
-
-
|
|
136
|
-
- `
|
|
137
|
-
- Pi
|
|
239
|
+
- Grok runs with the permissions of your operating-system user. The Grok session directory is not a sandbox, and Pi packages run code with your permissions. Read the source before you install it.
|
|
240
|
+
- The gateway listens on loopback only and requires the bearer secret for the WebSocket. It never starts Grok with `--always-approve`. Grok sessions use Grok's `default` permission mode unless you set `grokMode` (`scripts/server.ts`, `src/model/connection.ts`).
|
|
241
|
+
- `/grok perms yolo`, `grokMode: "yolo"` or `"auto"`, and `headlessPermissions: "allow"` each remove a different check. Use them only in a workspace you can lose. [usage.md](docs/usage.md#grok-permission-prompts) explains how they differ.
|
|
242
|
+
- Headless use does not imply approval. By default, headless Pi cancels Grok's permission prompts and returns a cancelled answer to questions (`src/model/permissions.ts`, `src/model/questions.ts`).
|
|
243
|
+
- Hook-handler errors fail open: a throwing handler continues the turn (`src/model/session.ts`). On timeout the gateway guard is stricter and differs by tier — it denies an unanswered pre-tool gate and rejects an unanswered permission prompt, but continues post-tool and stop hooks past their deadlines (`scripts/server.ts`). These are not equivalent guarantees.
|
|
244
|
+
- Pi sends its system prompt to Grok as session rules, plus user messages and, for a new Grok session, the earlier Pi transcript.
|
|
245
|
+
- `postEditCheck` and `stopCheck` run as shell commands (`bash -lc`) in the working directory. Treat those settings as executable code.
|
|
138
246
|
|
|
139
247
|
Files and network endpoints are listed in [docs/usage.md](docs/usage.md#files-and-network).
|
|
140
248
|
|
|
249
|
+
## What the saved probes establish
|
|
250
|
+
|
|
251
|
+
These are recorded results from one environment, not a guarantee for other versions or platforms. The 2026-09-28 run used Linux, Node.js 26.10.0, Pi 0.87.1, Grok Build 1.0.41, and `grok/grok-4.7`. Eight probes ran in sequence and every one exited 0. They ran against an isolated gateway on port 2429, with the production gateway on 2419 running and untouched throughout — so the saved results did not exercise the default endpoint. Full record: [docs/launch-verification.md](docs/launch-verification.md).
|
|
252
|
+
|
|
253
|
+
| Area | Saved result | Evidence |
|
|
254
|
+
| --- | --- | --- |
|
|
255
|
+
| Native execution | Grok read a token and wrote a file. Pi executed zero tools. This run used `headlessPermissions: "allow"`. | [model-live-gateway-extensions.json](evidence/model-live-gateway-extensions.json) |
|
|
256
|
+
| Pi controls | Read-only denial, post-edit repair, and a stop check passed. | [hooks-live.json](evidence/hooks-live.json) |
|
|
257
|
+
| Lent tools | Grok called a Pi-only tool and answered with the token it returned. | [model-probe.json](evidence/model-probe.json) |
|
|
258
|
+
| Gateway guard | No acknowledgment denied a write. Disconnect prevented a write. An acknowledged dialog accepted a later answer. | [gateway-guard-probe.json](evidence/gateway-guard-probe.json) |
|
|
259
|
+
| MCP gate | A tool marked read-only reached its server. An unmarked tool was denied. | [mcp-gate-probe.json](evidence/mcp-gate-probe.json) |
|
|
260
|
+
| Questions | The answer from a Pi dialog reached Grok. | [question-probe.json](evidence/question-probe.json) |
|
|
261
|
+
| Images | `image_gen` produced a JPEG. An attached image worked by file path, and did not work as an ACP image block. | [image-probe.json](evidence/image-probe.json) |
|
|
262
|
+
|
|
263
|
+
One caveat worth stating plainly: [hooks-probe.json](evidence/hooks-probe.json) records injected `additionalContext` and reports that it was not reflected in the answer. Context delivery alone does not prove that Grok follows it.
|
|
264
|
+
|
|
265
|
+
Live steering, restart recovery, image editing, and video generation remain unverified here. The saved results do not establish those capabilities.
|
|
266
|
+
|
|
141
267
|
## Documentation
|
|
142
268
|
|
|
143
|
-
- [docs/usage.md](docs/usage.md)
|
|
144
|
-
- [docs/first-class-model.md](docs/first-class-model.md)
|
|
145
|
-
- [docs/demo.md](docs/demo.md)
|
|
269
|
+
- [docs/usage.md](docs/usage.md) — settings, lent tools, permissions, the gateway guard, hooks, `/grok` commands, the isolated gateway, checks, and troubleshooting. Ships in the npm package.
|
|
270
|
+
- [docs/first-class-model.md](docs/first-class-model.md) — design, turn mapping, and the development record. Ships in the npm package.
|
|
271
|
+
- [docs/demo.md](docs/demo.md) — a reproducible storyboard for a 30 to 60 second demo. It is a recording plan, not an existing capture.
|
|
272
|
+
- [docs/launch-verification.md](docs/launch-verification.md) — the recorded live runs and install-path checks, with versions, behind the verified claims in this README.
|
|
273
|
+
- [AGENTS.md](AGENTS.md) — the source map, invariants, test commands, and the documentation-claim rule for changes to this repository.
|
|
274
|
+
|
|
275
|
+
On npmjs.com, relative links like these are rewritten to the matching file on GitHub, so they resolve on both pages. In an installed `node_modules` copy only `README.md`, `LICENSE`, `docs/usage.md`, and `docs/first-class-model.md` are present.
|
|
146
276
|
|
|
147
277
|
## Checks
|
|
148
278
|
|
|
279
|
+
These commands run from a clone of the repository, not from the installed package:
|
|
280
|
+
|
|
149
281
|
```sh
|
|
150
282
|
npm install # development dependencies, including TypeScript
|
|
151
283
|
npm run check # tsc --noEmit
|
|
152
284
|
npm test # unit tests plus gateway tests against a fake grok binary, no Grok calls
|
|
153
285
|
```
|
|
154
286
|
|
|
155
|
-
`test/gateway.test.ts` starts the real gateway with `test/fixtures/fake-grok.ts` as the Grok binary
|
|
156
|
-
|
|
157
|
-
The live probes in `scripts/` use your Grok login, cost Grok usage, and write results to `evidence/`. Create that directory first. See [Live probes](docs/usage.md#live-probes).
|
|
287
|
+
`test/gateway.test.ts` starts the real gateway with `test/fixtures/fake-grok.ts` as the Grok binary, in a scratch `HOME`. It checks leader ownership at startup and shutdown, and the guard's answers on Pi's wire: one answer per request, the deadline for each tier, and fail-closed on disconnect. Nothing in `npm test` contacts Grok.
|
|
158
288
|
|
|
159
|
-
|
|
289
|
+
The rest of the unit tests cover the turn split around a lent tool call, abort and resend, prompt tail selection, display-only messages, usage mapping, tool classification and gates, `/grok perms` modes, guard tier validation, question dialogs, `/grok login` output parsing, steering against a mocked Grok, the steer handler across a model switch, `/grok` command timeout, and media copies.
|
|
160
290
|
|
|
161
|
-
|
|
291
|
+
Before packaging, run `npm pack --dry-run`: the `files` list in `package.json` decides the tarball. The live probes in `scripts/` use your Grok login, spend Grok usage, and write to `evidence/`, which you must create first. Run them only intentionally — see [live probes](docs/usage.md#live-probes). `scripts/reconnect-probe.ts` hardcodes the default port and stops that gateway, so it never runs isolated.
|
|
162
292
|
|
|
163
293
|
## For coding agents
|
|
164
294
|
|
|
165
|
-
To evaluate or set up this package, use
|
|
166
|
-
|
|
167
|
-
Headless use needs attention: the default policy cancels Grok permission prompts, and questions receive a cancelled answer.
|
|
168
|
-
|
|
169
|
-
For changes to this repository, read [AGENTS.md](AGENTS.md). It contains the source map and test
|
|
295
|
+
To evaluate or set up this package, use [Requirements](#requirements), [Quick start](#quick-start), and the [first-result check](#first-result). The [reference](docs/usage.md) lists exact settings and commands.
|
|
296
|
+
|
|
297
|
+
Select a model by ID when you drive Pi non-interactively; the IDs are in [Models](#models). Headless use needs attention: the default policy cancels Grok permission prompts, and questions receive a cancelled answer. Read [headless permission behavior](docs/usage.md#grok-permission-prompts) before unattended use. Do not assume automatic approval.
|
|
298
|
+
|
|
299
|
+
For changes to this repository, read [AGENTS.md](AGENTS.md). It contains the source map, the invariants, and the rule that every documentation claim points to source, a unit test, or a probe result.
|
|
170
300
|
|
|
171
301
|
## Feedback
|
|
172
302
|
|
|
173
|
-
[Report a first-run problem](https://github.com/JangMan-J/pi-grok-agent/issues/new?template=first-run.yml), or [open an issue](https://github.com/JangMan-J/pi-grok-agent/issues). Include
|
|
303
|
+
[Report a first-run problem](https://github.com/JangMan-J/pi-grok-agent/issues/new?template=first-run.yml), or [open an issue](https://github.com/JangMan-J/pi-grok-agent/issues). Include:
|
|
304
|
+
|
|
305
|
+
- the output of `node --version`, `pi --version`, and `grok --version`;
|
|
306
|
+
- the model ID, the prompt, and the last setup step that succeeded;
|
|
307
|
+
- what you expected and what you saw;
|
|
308
|
+
- a redacted excerpt of `/grok debug`, if you have one.
|
|
174
309
|
|
|
175
310
|
Before you post, remove secrets, tokens, session IDs, and private paths from all output. Do not attach raw logs or session files. Post a short excerpt that you have read, preferably from a synthetic demo project.
|
|
176
311
|
|
package/docs/usage.md
CHANGED
|
@@ -67,6 +67,8 @@ A clone also auto-starts the gateway: it runs `scripts/server.ts` from the check
|
|
|
67
67
|
| `grok/grok-4.6` | Grok 4.6 | low, medium, high, xhigh |
|
|
68
68
|
| `grok/grok-4.5` | Grok 4.5 | low, medium, high |
|
|
69
69
|
|
|
70
|
+
Which of these a Grok account may use depends on the account. Grok reports the allowed models for each session; a free account on 2026-09-28 had only `grok-4.7`. Pi switches the Grok session to the model picked in `/models`. A model the account lacks fails the turn with the list of available models; before 0.1.1, Grok silently ran its default model instead.
|
|
71
|
+
|
|
70
72
|
Each model has a 500,000-token context window and a 32,000-token output limit in Pi's metadata. Per-token cost is zero in the metadata. The turn cost comes from Grok's `turn_completed` report, converted at 1e9 ticks per US dollar. That ratio is inferred from Grok's rates. It is not documented by Grok.
|
|
71
73
|
|
|
72
74
|
Pi controls work as usual:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-grok-agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Pi model provider for Grok Build: Grok keeps its native harness and tools while Pi drives the session over a local WebSocket ACP gateway",
|
package/src/model/provider.ts
CHANGED
|
@@ -129,6 +129,7 @@ export function createGrokStream(connection: GrokModelConnection, sessions: Sess
|
|
|
129
129
|
session.piToolNames = piTools.map((t) => t.name);
|
|
130
130
|
session.tools = selectPiTools(piTools, sessions.piTools ?? 'extensions');
|
|
131
131
|
await session.attach(getCurrentSystemPrompt(context.messages) || undefined);
|
|
132
|
+
await session.applyModel(model.id); // before the effort: grok-4.5 has no xhigh
|
|
132
133
|
await session.applyEffort(options?.reasoning); // Pi's thinking level drives Grok's reasoning_effort
|
|
133
134
|
signal?.throwIfAborted();
|
|
134
135
|
const { history, tail } = splitTail(context.messages);
|
package/src/model/session.ts
CHANGED
|
@@ -135,13 +135,33 @@ export class GrokModelSession {
|
|
|
135
135
|
if (this.activePrompt) { this.activePrompt = undefined; this.rejectParked('Grok connection dropped; the turn was lost.'); }
|
|
136
136
|
this.reconnected = this.connection.lastDrop ?? 'reconnected';
|
|
137
137
|
}
|
|
138
|
-
const { sessionId } = await this.connection.attachSession({
|
|
138
|
+
const { sessionId, response } = await this.connection.attachSession({
|
|
139
139
|
sessionId: this.grokSessionId, cwd: this.cwd, serverId: this.serverId, serverName: 'pi', rules,
|
|
140
140
|
offerPiTools: this.tools.length > 0, grokMode: this.grokMode,
|
|
141
141
|
handlers: { onUpdate: (n) => this.onUpdate(n), onMcp: (m) => this.onMcp(m), onPermission: (r) => this.permission(r), onHookRun: (p, gate) => this.onHookRun(p, gate), onHookEvent: (p) => { void this.onHookRun(p); }, onQuestion: (q) => this.ask(q), onSessionExt: (u) => this.onSessionExt(u) },
|
|
142
142
|
});
|
|
143
143
|
this.grokSessionId = sessionId;
|
|
144
144
|
this.attachedGeneration = this.connection.generation;
|
|
145
|
+
// Grok reports the session's model and the models this account may use as the `model` config option.
|
|
146
|
+
const modelOption = ((response as { configOptions?: { id?: string; currentValue?: string; options?: { value?: string }[] }[] }).configOptions ?? []).find((o) => o.id === 'model');
|
|
147
|
+
if (modelOption) {
|
|
148
|
+
this.grokModel = modelOption.currentValue;
|
|
149
|
+
this.grokModels = (modelOption.options ?? []).map((o) => o.value).filter((v): v is string => !!v);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Grok's current model for this session, and the models the signed-in account may use, from the `model` config option. */
|
|
154
|
+
grokModel?: string;
|
|
155
|
+
grokModels?: string[];
|
|
156
|
+
|
|
157
|
+
/** Switch Grok to the model picked in Pi. Without this Grok runs its default model whatever Pi shows. */
|
|
158
|
+
async applyModel(modelId: string) {
|
|
159
|
+
if (modelId === this.grokModel) return;
|
|
160
|
+
if (this.grokModels?.length && !this.grokModels.includes(modelId)) {
|
|
161
|
+
throw new Error(`${modelId} is not available on this Grok account. Available: ${this.grokModels.join(', ')}. Pick one of those in /models.`);
|
|
162
|
+
}
|
|
163
|
+
await this.setConfigOption('model', modelId);
|
|
164
|
+
this.grokModel = modelId;
|
|
145
165
|
}
|
|
146
166
|
|
|
147
167
|
detach() {
|
|
@@ -436,20 +456,23 @@ export class GrokModelSession {
|
|
|
436
456
|
|
|
437
457
|
/** Saved media path from a Grok media tool result (`{ type: "ImageGen", path, filename, session_folder }` and kin). */
|
|
438
458
|
export function mediaPath(value: unknown): string | undefined {
|
|
439
|
-
const v = (value ?? {}) as
|
|
459
|
+
const v = (value ?? {}) as { path?: unknown; type?: unknown };
|
|
440
460
|
return typeof v.path === 'string' && /^(ImageGen|ImageEdit|ImageToVideo|ReferenceToVideo|VideoGen)$/.test(String(v.type ?? '')) ? v.path : undefined;
|
|
441
461
|
}
|
|
442
462
|
|
|
463
|
+
/** The fields `resultText` looks at in a Grok tool result envelope. */
|
|
464
|
+
type ResultEnvelope = { FileContent?: { raw_output?: unknown; content?: unknown }; output?: unknown; stdout?: unknown; text?: unknown; content?: unknown; message?: unknown };
|
|
465
|
+
|
|
443
466
|
/** Best-effort plain text from a Grok tool result envelope (e.g. ReadFile.FileContent.raw_output, or a string). */
|
|
444
467
|
export function resultText(value: unknown, limit = 8000): string | undefined {
|
|
445
468
|
if (value == null) return undefined;
|
|
446
469
|
if (typeof value === 'string') return value.slice(0, limit);
|
|
447
470
|
const media = mediaPath(value);
|
|
448
471
|
if (media) return media;
|
|
449
|
-
const v = value as
|
|
472
|
+
const v = value as ResultEnvelope;
|
|
450
473
|
const nested = v.FileContent?.raw_output ?? v.FileContent?.content ?? v.output ?? v.stdout ?? v.text ?? v.content ?? v.message;
|
|
451
474
|
if (typeof nested === 'string') return nested.slice(0, limit);
|
|
452
|
-
if (Array.isArray(nested)) return nested.map((c) => (typeof c === 'string' ? c : c?.text ?? '')).join('').slice(0, limit) || undefined;
|
|
475
|
+
if (Array.isArray(nested)) return nested.map((c: unknown) => (typeof c === 'string' ? c : String((c as { text?: unknown } | null)?.text ?? ''))).join('').slice(0, limit) || undefined;
|
|
453
476
|
return JSON.stringify(value).slice(0, limit);
|
|
454
477
|
}
|
|
455
478
|
|