pi-grok-agent 0.1.0 → 0.1.2

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 CHANGED
@@ -2,178 +2,65 @@
2
2
 
3
3
  Grok's agent. Pi's workflow.
4
4
 
5
- Run Grok Build as a model in the [Pi coding agent](https://github.com/earendil-works/pi).
6
- Grok keeps its native tools and history. You keep Pi's transcript, extensions, and permission dialogs.
7
-
8
- [Quick start](#quick-start) · [First result](#first-result) · [Reference](docs/usage.md) · [Limitations](#limitations) · [Apache-2.0](LICENSE)
9
-
10
- After [setup](#quick-start), select Grok like any Pi model:
11
-
12
- ```sh
13
- pi --model grok/grok-4.7
14
- ```
15
-
16
- Status: version 0.1.0. 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.
17
-
18
- This package connects an agent, not the xAI chat-completions API. It needs a logged-in Grok Build CLI and a local gateway.
19
- The tool gates are not an operating-system sandbox.
20
-
21
- ## Requirements
22
-
23
- | Item | Requirement |
24
- | --- | --- |
25
- | Node.js and npm | Node.js 22.19 or later is Pi's minimum. This package is tested only on 26.10.0. |
26
- | [Pi](https://github.com/earendil-works/pi#quick-start) | Tested with 0.87.1. Other versions are untested. |
27
- | [Grok Build CLI](https://docs.x.ai/build/overview) | `grok` on `PATH`, or its path in `PI_GROK_BINARY`. |
28
- | Grok login | Run `/grok login` in Pi, or `grok login` in a terminal. Both use Grok's own sign-in, and Grok keeps the credential in `~/.grok/auth.json`. Pi's `/login` xAI entry is a separate login and does not sign in Grok Build. |
29
- | Free local port | `127.0.0.1:2419`, or another loopback port in `GROK_ACP_URL`. |
30
- | Optional | A terminal with inline image support. ImageMagick 7 (`magick`) to show JPEG, WebP, and GIF images inline. PNG needs no converter. |
31
-
32
- Grok usage counts against your Grok account. Pi shows the cost that Grok reports for each turn.
33
-
34
- ## Quick start
35
-
36
5
  ```sh
37
6
  pi install npm:pi-grok-agent
38
- pi --model grok/grok-4.7
39
7
  ```
40
8
 
41
- The first Grok turn starts the local gateway that comes with the package and waits for it, about 5 seconds. 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. [Gateway auto-start](docs/usage.md#gateway-auto-start) explains how to stop it, or how to run it yourself with `npm install -g pi-grok-agent` and `pi-grok-gateway`.
9
+ Run [Grok Build](https://docs.x.ai/build/overview) as an additional model provider in [Pi coding agent](https://github.com/earendil-works/pi). Grok keeps its native environment, tools, and session history. Pi provides the DIY harness, turn control, permission requests, and extensions.
42
10
 
43
- Pi loads the extension at every start, for every model. To remove it, run `pi remove npm:pi-grok-agent`.
44
-
45
- ### From a clone
46
-
47
- ```sh
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
- ```
54
-
55
- `pi install git:github.com/JangMan-J/pi-grok-agent` also works. A clone auto-starts the gateway from its own checkout, so `npm run server` is optional. A git install uses the same code path, but that is not yet tested live.
56
-
57
- ## First result
58
-
59
- Start Pi in any project directory that has a `package.json`, and send this prompt:
11
+ ## How it connects
60
12
 
61
13
  ```text
62
- Read package.json and tell me the package name and the npm scripts. Do not change files.
14
+ ┌─────────────────────┐ ┌──────────────────┐ ┌─────────────────────┐
15
+ │ PI — drives turn │ │ GATEWAY │ │ GROK BUILD — works │
16
+ │ transcript, gates, │ ACP │ one per machine │ stdio │ own tools, agents, │
17
+ │ dialogs │ over WS │ 127.0.0.1:2419 │ │ own history │
18
+ │ grok provider ext │◄───────►│ guard + MCP │◄───────►│ ~/.grok login │
19
+ └─────────────────────┘ └──────────────────┘ └─────────────────────┘
63
20
  ```
64
21
 
65
- Expected result:
66
-
67
- - One line for each Grok tool call, for example `✓ grok read_file …` or `✓ grok hashline_read …`, with its duration. Grok chooses the tool.
68
- - Thinking text that contains `[grok <tool>]` lines.
69
- - An answer that names the package and its scripts.
70
- - A footer cost that comes from Grok's usage report.
71
-
72
- Then run `/grok debug`. It shows the gateway connection, the Grok session ID, the permission modes, token usage, and the lent Pi tools.
22
+ Pi drives the session using [Agent Client Protocol](https://agentclientprotocol.com) over WebSockets. Grok returns streaming responses, thinking blocks, as well as image and video requests. All of Grok's tool requests are routed back to Pi through callbacks over an MCP loopback. The first Grok turn auto-starts the local gateway (`127.0.0.1:2419` by default) ; every Pi process on the machine attaches to it. Details: [docs/architecture-diagram.md](docs/architecture-diagram.md) · [docs/usage.md](docs/usage.md).
73
23
 
74
- If the result is different, see [Troubleshooting](docs/usage.md#troubleshooting). Please report what you saw, as described in [Feedback](#feedback).
24
+ ## Function
75
25
 
76
- ## Why use this?
26
+ - **Pi sets permissions and boundaries.** Read-only, ask, auto, and YOLO permission modes are supported.
27
+ - **Pi makes decisions.** Grok's tool call requests, turn order, user_ask_question prompts are delegated to Pi.
28
+ - **Grok can use Pi's extension tools** Lent over MCP as `pi__<name>`, with the result continuing the same Grok turn.
29
+ - **Grok keeps all of his tools and extensions.** My own observations have been that Grok performs better with his native toolset, so this project's purpose is to keep his tools without buying the shed.
77
30
 
78
- For Pi users who want Grok Build's native tools in their existing agent workflow:
31
+ ## Models
79
32
 
80
- | You want to… | What this package adds |
81
- | --- | --- |
82
- | Keep Grok's native tools | Grok runs its own harness. Pi shows its tool calls in the transcript. |
83
- | Control edits from Pi | [Pi's tool gate](docs/usage.md#grok-permission-prompts) can deny Grok's edit and shell tools or ask before each call. |
84
- | Reuse Pi extension tools | [Lent tools](docs/usage.md#lent-pi-tools) reach Grok over MCP. Pi executes those calls. |
33
+ | Model ID | Name in `/models` | Reasoning efforts | Context window |
34
+ | --- | --- | --- | --- |
35
+ | `grok/grok-4.7` | Grok 4.7 | low, medium, high, xhigh | 500,000 tokens |
36
+ | `grok/grok-4.7-build-fast` | Grok 4.7 Build Fast | low, medium, high, xhigh | 500,000 tokens |
37
+ | `grok/grok-4.6` | Grok 4.6 | low, medium, high, xhigh | 500,000 tokens |
38
+ | `grok/grok-4.5` | Grok 4.5 | low, medium, high, xhigh | 500,000 tokens |
85
39
 
86
- ```text
87
- Pi ⇄ local ACP gateway ⇄ Grok Build
88
- └─ native tools and agent history
89
- ```
90
40
 
91
- The gateway carries the Agent Client Protocol (ACP) over WebSocket and stdio. [How it works →](docs/usage.md#components)
92
-
93
- ### More controls
94
-
95
- - Grok's native tools do the work. Grok executes the tools its harness offers, such as file, shell, search, web, subagent, and media tools. Pi does not execute them.
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`.
104
-
105
- Models: `grok/grok-4.7`, `grok/grok-4.7-build-fast`, `grok/grok-4.6`, `grok/grok-4.5`. Each has a 500,000-token context window.
106
-
107
- ## Generated images and video
108
-
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.
110
-
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. PNG shows directly. 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
-
113
- Live probes cover `image_gen` only (`evidence/image-probe.json`, run recorded in [docs/launch-verification.md](docs/launch-verification.md)). 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. Pi does not send it back to Grok.
114
-
115
- Images that you attach in Pi go to Grok as a temporary file path under the system temp directory (`pi-grok-images`). Grok reads the file with its own tools.
116
-
117
- ## Limitations
118
-
119
- - 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 `npm run server` with the default settings 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
- - 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. Only when it creates a new Grok session does it also send the earlier Pi transcript as text, cut to the last 60,000 characters.
123
- - 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.
127
- - The provider does not call Pi's `onPayload` and `onResponse` stream hooks.
128
- - Cost per token is set to zero in the model metadata. The per-turn cost comes from Grok's report.
41
+ Model availability in Pi is determined by your [account access](https://grok.com).
129
42
 
130
43
  ## Safety
131
44
 
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
- - Pi sends its system prompt to Grok as session rules. Pi sends user messages and, for a new Grok session, the earlier Pi transcript.
136
- - `postEditCheck` and `stopCheck` run as shell commands (`bash -lc`) in the working directory.
137
- - Pi packages run code with your permissions. Read the source before you install it.
138
-
139
- Files and network endpoints are listed in [docs/usage.md](docs/usage.md#files-and-network).
45
+ - This package connects the Grok Build agent over ACP, not the xAI chat-completions API. The ACP protocol does not provide complete visibility or control of an agent, and not all functions or extensions of Grok Build have been tested for safety in this configuration. The tool gates are not an operating-system sandbox: Grok runs with your user's permissions.
46
+ - The gateway listens on loopback only, requires a bearer secret, and never starts Grok with `--always-approve`. Headless use does not imply approval: by default, headless Pi cancels Grok's permission prompts. The `postEditCheck` and `stopCheck` settings run as shell commands — treat them as executable code.
140
47
 
141
- ## Documentation
142
-
143
- - [docs/usage.md](docs/usage.md): settings, lent tools, permissions, gateway guard, hooks, `/grok` commands, isolated gateway, checks, and troubleshooting.
144
- - [docs/first-class-model.md](docs/first-class-model.md): design, turn mapping, and the development record.
145
- - [docs/demo.md](docs/demo.md): a reproducible storyboard for a 30 to 60 second demo.
146
-
147
- ## Checks
148
-
149
- ```sh
150
- npm install # development dependencies, including TypeScript
151
- npm run check # tsc --noEmit
152
- npm test # unit tests plus gateway tests against a fake grok binary, no Grok calls
153
- ```
48
+ ## Notes
154
49
 
155
- `test/gateway.test.ts` starts the real gateway with `test/fixtures/fake-grok.ts` as the Grok binary. 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.
50
+ - Not compatible with API key access. A Grok account is required, any membership tier. If your OAuth token expires you can '/login' from either Grok Build or Pi with '/grok login' to renew credentials.
51
+ - Token read, token write, cache read and cache hit % displayed in Pi are taken from Grok Build. Pi may occasionally report inaccurate data during long multistep tool calls, but will correct onn the next turn.
156
52
 
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).
158
-
159
- ## Relation to other ACP clients
160
-
161
- Grok Build can serve ACP to any ACP client. This package connects that agent to Pi as a model provider. Pi's transcript, dialogs, tool gates, lent tools, and model selection then apply to Grok's own harness.
162
-
163
- ## For coding agents
53
+ ## Documentation
164
54
 
165
- To evaluate or set up this package, use the [requirements](#requirements), [quick start](#quick-start), and [first-result check](#first-result).
166
- The [reference](docs/usage.md) lists exact settings and commands.
167
- Headless use needs attention: the default policy cancels Grok permission prompts, and questions receive a cancelled answer.
168
- Read [headless permission behavior](docs/usage.md#grok-permission-prompts) before unattended use. Do not assume automatic approval.
169
- For changes to this repository, read [AGENTS.md](AGENTS.md). It contains the source map and test commands.
55
+ - [docs/usage.md](docs/usage.md) — settings, lent tools, permissions, the gateway guard, hooks, `/grok` commands, troubleshooting
56
+ - [docs/architecture-diagram.md](docs/architecture-diagram.md) — the diagram in mermaid and ASCII
57
+ - [docs/first-class-model.md](docs/first-class-model.md) — design and turn mapping
58
+ - [docs/launch-verification.md](docs/launch-verification.md) — recorded live runs behind the verified claims
170
59
 
171
60
  ## Feedback
172
61
 
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 the output of `node --version`, `pi --version`, and `grok --version`, the model ID, the prompt, what you expected, and what you saw. The `/grok debug` output helps.
174
-
175
- 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.
62
+ - [Open an issue](https://github.com/JangMan-J/pi-grok-agent/issues) with the output of `node --version`, `pi --version`, and `grok --version`, the model ID, and a short redacted excerpt of `/grok debug`.
176
63
 
177
64
  ## License
178
65
 
179
- [Apache License 2.0](LICENSE).
66
+ - [Apache License 2.0](LICENSE).
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.0",
3
+ "version": "0.1.2",
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",
@@ -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);
@@ -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 Record<string, any>;
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 Record<string, any>;
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