@infomity/remogent 0.2.2 → 0.2.3

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,7 +2,7 @@
2
2
 
3
3
  <p align="center">
4
4
  <strong>Zero-Recurring-Cost Android Remote AI Agent & Terminal Client</strong><br>
5
- <em>Drive Gemini CLI, Claude Code CLI and Codex CLI directly from your phone over secure, pinned-TLS transport.</em>
5
+ <em>Drive Gemini CLI, Antigravity CLI, Claude Code CLI and Codex CLI directly from your phone over secure, pinned-TLS transport.</em>
6
6
  </p>
7
7
 
8
8
  <p align="center">
@@ -15,21 +15,26 @@
15
15
  </p>
16
16
 
17
17
  <p align="center">
18
- <img src="docs/screenshots/01-new-chat.png" alt="Remogent new chat screen with agent, folder and permission-mode chips" width="190">
19
- <img src="docs/screenshots/02-sessions.png" alt="Remogent drawer listing chat sessions across Claude Code, Gemini CLI and Codex CLI" width="190">
20
- <img src="docs/screenshots/03-chat.png" alt="Remogent chat showing a Bash tool card and Claude Code's reply" width="190">
18
+ <img src="docs/screenshots/01-new-chat.png" alt="Remogent new chat screen with agent, model and permission-mode chips" width="190">
19
+ <img src="docs/screenshots/05-agents.png" alt="Remogent agent picker with Claude Code, Gemini CLI, Antigravity CLI and Codex CLI" width="190">
20
+ <img src="docs/screenshots/09-folder.png" alt="Remogent working folder sheet with recent folders" width="190">
21
+ <img src="docs/screenshots/07-permissions.png" alt="Remogent permission mode sheet: Ask before acting, Auto-edit, Plan first, Full auto" width="190">
22
+ <br>
21
23
  <img src="docs/screenshots/04-approval.png" alt="Remogent tool approval sheet with diff preview and Deny, Allow once and For session buttons" width="190">
24
+ <img src="docs/screenshots/03-chat.png" alt="Remogent chat showing a shell tool card and the agent's reply" width="190">
25
+ <img src="docs/screenshots/02-sessions.png" alt="Remogent drawer listing chat sessions with their agent badges" width="190">
26
+ <img src="docs/screenshots/08-settings.png" alt="Remogent settings with theme, default working folder and pinned TLS fingerprint" width="190">
22
27
  </p>
23
28
 
24
29
  ---
25
30
 
26
31
  ## Overview
27
32
 
28
- **Remogent** enables software engineers to monitor, prompt, and approve actions from **Gemini CLI**, **Claude Code CLI** and **Codex CLI** from their Android phone without paying monthly cloud relay fees.
33
+ **Remogent** enables software engineers to monitor, prompt, and approve actions from **Gemini CLI**, **Antigravity CLI**, **Claude Code CLI** and **Codex CLI** from their Android phone without paying monthly cloud relay fees.
29
34
 
30
35
  ### Core Pillars
31
36
  - **$0.00 Infrastructure Cost**: Direct peer-to-peer transport over local Wi-Fi, Tailscale, WireGuard, or Cloudflare tunnels. No intermediary cloud servers, no subscriptions.
32
- - **Three AI Engines**: First-class support for **Gemini CLI** (stdio JSON-RPC 2.0 ACP protocol), **Claude Code CLI** (bidirectional stream-json protocol) and **Codex CLI** (`codex app-server` JSON-RPC).
37
+ - **Four AI Engines**: First-class support for **Gemini CLI** (stdio JSON-RPC 2.0 ACP protocol), **Antigravity CLI** (`agy acp`, the same ACP dialect), **Claude Code CLI** (bidirectional stream-json protocol) and **Codex CLI** (`codex app-server` JSON-RPC).
33
38
  - **Per-Chat Model Choice**: The model list is read from each engine's own CLI on your PC (so it follows your logins and settings, including a custom Claude provider). Pick a model when you start a chat and switch it between turns from the chat details; without a choice the engine uses its own default.
34
39
  - **Interactive Tool Approvals**: Destructive operations (`Bash`, file edits, network calls) trigger an audible/haptic approval bottom sheet on your phone with granular control: `Deny`, `Allow Once`, or `Allow for Session`.
35
40
  - **Biometric Guard**: Gate tool authorizations and sensitive executions behind on-device biometric fingerprint / face authentication.
@@ -57,7 +62,7 @@
57
62
 
58
63
  ### 1. Host Companion Setup (PC)
59
64
 
60
- **Prerequisites:** Node.js >= 20.x, and at least one of `gemini`, `claude` or `codex` installed (found automatically, no PATH editing needed).
65
+ **Prerequisites:** Node.js >= 20.x, and at least one of `gemini`, `agy`, `claude` or `codex` installed (found automatically, no PATH editing needed).
61
66
 
62
67
  ```bash
63
68
  # Easiest: one command, no clone needed (npm package @infomity/remogent).
@@ -80,7 +85,7 @@ cd Remogent/companion
80
85
  npm run setup
81
86
  ```
82
87
 
83
- `doctor` finds the Claude Code, Gemini and Codex CLIs by itself (PATH, then the native installer, npm -g, Volta, pnpm, nvm, Homebrew, scoop), so you never edit PATH, and tells you exactly what to install if something is missing. Run it any time with `remogent doctor` (or `npm run doctor` from source). `install` (the last step of `setup`) registers an autostart service that runs as your user (it needs your `claude` / `gemini` / `codex` logins), starts at sign-in and restarts if it crashes: Task Scheduler on Windows, launchd on macOS, `systemd --user` on Linux. After pairing, the host is simply there; no terminal to keep open. Related commands (from source use `node dist/index.js` instead of `remogent`):
88
+ `doctor` finds the Claude Code, Gemini, Antigravity and Codex CLIs by itself (PATH, then the native installer, npm -g, Volta, pnpm, nvm, Homebrew, scoop), so you never edit PATH, and tells you exactly what to install if something is missing. Run it any time with `remogent doctor` (or `npm run doctor` from source). `install` (the last step of `setup`) registers an autostart service that runs as your user (it needs your `claude` / `gemini` / `codex` logins), starts at sign-in and restarts if it crashes: Task Scheduler on Windows, launchd on macOS, `systemd --user` on Linux. After pairing, the host is simply there; no terminal to keep open. Related commands (from source use `node dist/index.js` instead of `remogent`):
84
89
 
85
90
  ```bash
86
91
  remogent pair # show the pairing QR again (new phone, or you closed the setup window)
@@ -105,7 +110,7 @@ To rotate the authentication secret at any time:
105
110
  node dist/index.js install --rotate # re-registers the service with a new secret; re-pair the phone afterwards
106
111
  ```
107
112
 
108
- Chats survive daemon restarts: transcripts are saved to `~/.remogent/sessions.json` (owner-only file, newest ~2 MiB per chat, 14 days / 100 chats). Claude Code and Codex chats resume with their full context on the next prompt; Gemini chats come back as read-only history. Pending approvals and "Allow for session" grants are never restored. To keep nothing on disk, start with `--no-persist`.
113
+ Chats survive daemon restarts: transcripts are saved to `~/.remogent/sessions.json` (owner-only file, newest ~2 MiB per chat, 14 days / 100 chats). Claude Code and Codex chats resume with their full context on the next prompt; Gemini and Antigravity chats come back as read-only history. Pending approvals and "Allow for session" grants are never restored. To keep nothing on disk, start with `--no-persist`.
109
114
 
110
115
  ---
111
116
 
@@ -162,13 +167,14 @@ Keep Tailscale running on the phone (Android may close VPN apps aggressively und
162
167
  1. In the active host card, tap **NEW AI SESSION**.
163
168
  2. Select your desired engine:
164
169
  - **Gemini CLI** (stdio ACP JSON-RPC)
170
+ - **Antigravity CLI** (stdio `agy acp`, same ACP dialect as Gemini; found in `~/.gemini/bin`). Note: inside folders listed in `trustedWorkspaces` of `~/.gemini/antigravity-cli/settings.json`, agy applies edits without asking even in Ask mode (shell commands still ask); the host warns in the chat and in `doctor`.
165
171
  - **Claude Code CLI** (stdio stream-json)
166
172
  - **Codex CLI** (stdio `codex app-server`; sign in once with `codex login`)
167
173
  3. Optionally tap the model chip to pick one of the models the engine's CLI lists (skip it to use the engine's default).
168
174
  4. Set the working directory (e.g. `.` or an absolute project path).
169
175
  5. Choose the permission mode:
170
176
  - `Ask` (Default): Every mutating shell command or file edit prompts for your confirmation.
171
- - `Auto (YOLO)`: Automatically approves commands (Gemini `yolo` / Claude `bypassPermissions` / Codex no sandbox, never ask).
177
+ - `Auto (YOLO)`: Automatically approves commands (Gemini `yolo` / Antigravity `auto` / Claude `bypassPermissions` / Codex no sandbox, never ask).
172
178
  - `Plan Only`: Read-only execution mode.
173
179
  6. Tap **START SESSION**.
174
180
  7. Prompt the agent in real time, inspect reasoning in collapsible thought bubbles, and monitor tool outputs.
@@ -1,4 +1,6 @@
1
+ import { readFileSync } from "node:fs";
1
2
  import { homedir } from "node:os";
3
+ import { join, resolve, sep } from "node:path";
2
4
  import { RpcError, RpcPeer } from "./rpc.js";
3
5
  import { findOnPath, killTree, spawnCli } from "./util.js";
4
6
  const isObj = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
@@ -93,11 +95,50 @@ function toEvent(update) {
93
95
  return undefined; // plan, available_commands_update, current_mode_update, user_message_chunk (history on load)
94
96
  }
95
97
  }
96
- const MODE_IDS = { ask: "default", plan: "plan", auto_edit: "autoEdit", auto: "yolo" };
98
+ const GEMINI_MODE_IDS = { ask: "default", plan: "plan", auto_edit: "autoEdit", auto: "yolo" };
99
+ // Antigravity CLI (`agy acp`, verified on agy 1.5.6): same ACP dialect, different launch args and mode ids.
100
+ const AGY_MODE_IDS = { ask: "default", plan: "plan", auto_edit: "accept-edits", auto: "auto" };
101
+ const GEMINI_FLAVOR = { engine: "gemini", label: "gemini", args: ["--acp"], modeIds: GEMINI_MODE_IDS, signIn: "run `gemini` once on the host to sign in" };
102
+ const AGY_FLAVOR = { engine: "agy", label: "agy", args: ["acp"], modeIds: AGY_MODE_IDS, signIn: "run `agy` once on the host to sign in" };
97
103
  /** Path of the `gemini` executable if it is installed on this host. */
98
104
  export function findGemini() {
99
105
  return findOnPath("gemini");
100
106
  }
107
+ /** Path of the Antigravity CLI (`agy`, installed to ~/.gemini/bin) if it is installed on this host. */
108
+ export function findAgy() {
109
+ return findOnPath("agy");
110
+ }
111
+ /** Where Antigravity keeps its settings (shared `~/.gemini` config dir). */
112
+ export const AGY_SETTINGS_FILE = () => join(homedir(), ".gemini", "antigravity-cli", "settings.json");
113
+ /**
114
+ * The trusted workspace (`trustedWorkspaces` in agy's settings) that contains `cwd`, if any. Verified on agy 1.5.6: inside a
115
+ * trusted folder agy applies file edits WITHOUT a `session/request_permission` even in its default mode (shell commands still
116
+ * ask), and `--settings` cannot override it for an `acp` session. Best effort: unreadable settings mean "not trusted".
117
+ */
118
+ export function agyTrustedWorkspace(cwd) {
119
+ let list;
120
+ try {
121
+ list = JSON.parse(readFileSync(AGY_SETTINGS_FILE(), "utf8"))["trustedWorkspaces"];
122
+ }
123
+ catch {
124
+ return undefined;
125
+ }
126
+ if (!Array.isArray(list))
127
+ return undefined;
128
+ const norm = (p) => {
129
+ const r = resolve(p);
130
+ return process.platform === "win32" ? r.toLowerCase() : r;
131
+ };
132
+ const here = norm(cwd);
133
+ for (const t of list) {
134
+ if (typeof t !== "string" || !t)
135
+ continue;
136
+ const n = norm(t);
137
+ if (here === n || here.startsWith(n.endsWith(sep) ? n : n + sep))
138
+ return t;
139
+ }
140
+ return undefined;
141
+ }
101
142
  function lastLine(s) {
102
143
  const lines = s.trim().split(/\r?\n/);
103
144
  return lines[lines.length - 1] ?? "";
@@ -120,15 +161,15 @@ function parseGeminiModels(res) {
120
161
  });
121
162
  }
122
163
  /** Asks a short-lived `gemini --acp` process for its model list (initialize + session/new; no model call is made). */
123
- async function queryGeminiModels(binary) {
124
- const child = spawnCli(binary, ["--acp"], homedir());
164
+ async function queryAcpModels(binary, f) {
165
+ const child = spawnCli(binary, f.args, homedir());
125
166
  const rpc = new RpcPeer(child);
126
167
  let stderrTail = "";
127
168
  child.stderr.on("data", (d) => {
128
169
  stderrTail = (stderrTail + d.toString()).slice(-2000);
129
170
  });
130
- child.on("error", (e) => rpc.failAll(new Error(`gemini failed to start: ${e.message}`)));
131
- child.on("exit", (code) => rpc.failAll(new Error(stderrTail ? `gemini exited (code ${code}): ${lastLine(stderrTail)}` : `gemini exited (code ${code})`)));
171
+ child.on("error", (e) => rpc.failAll(new Error(`${f.label} failed to start: ${e.message}`)));
172
+ child.on("exit", (code) => rpc.failAll(new Error(stderrTail ? `${f.label} exited (code ${code}): ${lastLine(stderrTail)}` : `${f.label} exited (code ${code})`)));
132
173
  let timer;
133
174
  try {
134
175
  const work = (async () => {
@@ -136,7 +177,7 @@ async function queryGeminiModels(binary) {
136
177
  return parseGeminiModels(await rpc.request("session/new", { cwd: homedir(), mcpServers: [] }));
137
178
  })();
138
179
  const timeout = new Promise((_, reject) => {
139
- timer = setTimeout(() => reject(new Error("gemini did not answer in time")), 20_000);
180
+ timer = setTimeout(() => reject(new Error(`${f.label} did not answer in time`)), 20_000);
140
181
  });
141
182
  return await Promise.race([work, timeout]);
142
183
  }
@@ -146,12 +187,12 @@ async function queryGeminiModels(binary) {
146
187
  killTree(child);
147
188
  }
148
189
  }
149
- export function createGeminiAdapter(binary) {
190
+ function createAcpAdapter(binary, f) {
150
191
  return {
151
- engine: "gemini",
152
- listModels: () => queryGeminiModels(binary),
192
+ engine: f.engine,
193
+ listModels: () => queryAcpModels(binary, f),
153
194
  async start({ cwd, permissionMode, model }, sink) {
154
- const child = spawnCli(binary, ["--acp"], cwd);
195
+ const child = spawnCli(binary, f.args, cwd);
155
196
  const rpc = new RpcPeer(child);
156
197
  let closed = false; // set once the session is intentionally closed or has died
157
198
  let inTurn = false; // a prompt request is in flight (its rejection reports process death)
@@ -170,8 +211,8 @@ export function createGeminiAdapter(binary) {
170
211
  sink.emit({ kind: "error", message: err.message });
171
212
  sink.emit({ kind: "status", status: "error" });
172
213
  };
173
- child.on("exit", (code) => died(`gemini exited (code ${code})`));
174
- child.on("error", (e) => died(`gemini failed to start: ${e.message}`));
214
+ child.on("exit", (code) => died(`${f.label} exited (code ${code})`));
215
+ child.on("error", (e) => died(`${f.label} failed to start: ${e.message}`));
175
216
  rpc.onNotification = (method, params) => {
176
217
  if (method !== "session/update" || !isObj(params) || !isObj(params["update"]))
177
218
  return;
@@ -221,32 +262,43 @@ export function createGeminiAdapter(binary) {
221
262
  const res = await rpc.request("session/new", { cwd, mcpServers: [] });
222
263
  sessionId = isObj(res) ? str(res["sessionId"]) : undefined;
223
264
  if (!sessionId)
224
- throw new Error("gemini did not return a sessionId");
265
+ throw new Error(`${f.label} did not return a sessionId`);
225
266
  // set_model accepts any id without complaint (host validated it against the listed models); skip it when the
226
267
  // CLI already runs that model.
227
268
  const startModel = isObj(res) && isObj(res["models"]) ? str(res["models"]["currentModelId"]) : undefined;
228
269
  if (model && model !== startModel)
229
270
  await rpc.request("session/set_model", { sessionId, modelId: model });
230
271
  if (permissionMode !== "ask") {
231
- await rpc.request("session/set_mode", { sessionId, modeId: MODE_IDS[permissionMode] });
272
+ await rpc.request("session/set_mode", { sessionId, modeId: f.modeIds[permissionMode] });
232
273
  }
233
274
  }
234
275
  catch (e) {
235
276
  closed = true;
236
277
  killTree(child);
237
278
  const msg = e instanceof Error ? e.message : String(e);
238
- throw new Error(/auth/i.test(msg) ? `${msg} (run \`gemini\` once on the host to sign in)` : msg);
279
+ throw new Error(/auth/i.test(msg) ? `${msg} (${f.signIn})` : msg);
280
+ }
281
+ // agy auto-applies edits inside its trusted workspaces even in default mode (shell commands still ask); say so, because
282
+ // "Ask" would otherwise look like it gates edits.
283
+ if (f.engine === "agy" && permissionMode === "ask") {
284
+ const trusted = agyTrustedWorkspace(cwd);
285
+ if (trusted) {
286
+ sink.emit({
287
+ kind: "error",
288
+ message: `Heads-up: Antigravity trusts ${trusted}, so in Ask mode it edits files in this folder without asking (shell commands still ask). To get edit approvals, remove it from "trustedWorkspaces" in ~/.gemini/antigravity-cli/settings.json or start the chat in a folder outside it.`,
289
+ });
290
+ }
239
291
  }
240
292
  const sid = sessionId;
241
293
  return {
242
294
  async prompt(text) {
243
295
  if (closed)
244
- throw new Error("gemini process is not running");
296
+ throw new Error(`${f.label} process is not running`);
245
297
  inTurn = true;
246
298
  try {
247
299
  const res = await rpc.request("session/prompt", { sessionId: sid, prompt: [{ type: "text", text }] });
248
300
  const stop = isObj(res) ? str(res["stopReason"]) : undefined;
249
- const usage = isObj(res) ? res["_meta"] : undefined;
301
+ const usage = isObj(res) ? (res["usage"] ?? res["_meta"]) : undefined; // agy reports token usage in `usage`, Gemini CLI in `_meta`
250
302
  if (stop === "refusal")
251
303
  sink.emit({ kind: "error", message: "the model refused this request" });
252
304
  sink.emit({
@@ -271,7 +323,7 @@ export function createGeminiAdapter(binary) {
271
323
  },
272
324
  async setModel(next) {
273
325
  if (closed)
274
- throw new Error("gemini process is not running");
326
+ throw new Error(`${f.label} process is not running`);
275
327
  if (inTurn)
276
328
  throw new Error("a turn is already running");
277
329
  await rpc.request("session/set_model", { sessionId: sid, modelId: next });
@@ -284,4 +336,10 @@ export function createGeminiAdapter(binary) {
284
336
  },
285
337
  };
286
338
  }
339
+ export function createGeminiAdapter(binary) {
340
+ return createAcpAdapter(binary, GEMINI_FLAVOR);
341
+ }
342
+ export function createAgyAdapter(binary) {
343
+ return createAcpAdapter(binary, AGY_FLAVOR);
344
+ }
287
345
  //# sourceMappingURL=gemini.js.map
@@ -25,7 +25,7 @@ export function extraSearchDirs() {
25
25
  const win = process.platform === "win32";
26
26
  const home = homedir();
27
27
  const env = process.env;
28
- const dirs = [dirname(process.execPath), join(home, ".local", "bin"), join(home, ".claude", "local")];
28
+ const dirs = [dirname(process.execPath), join(home, ".local", "bin"), join(home, ".claude", "local"), join(home, ".gemini", "bin")];
29
29
  if (win) {
30
30
  dirs.push(env["APPDATA"] && join(env["APPDATA"], "npm"), env["LOCALAPPDATA"] && join(env["LOCALAPPDATA"], "pnpm"), env["LOCALAPPDATA"] && join(env["LOCALAPPDATA"], "Volta", "bin"), env["NVM_SYMLINK"], env["ProgramFiles"] && join(env["ProgramFiles"], "nodejs"), join(home, "scoop", "shims"), join(home, ".bun", "bin"));
31
31
  }
package/dist/index.js CHANGED
@@ -10,9 +10,9 @@ import { DEFAULT_PORT, loadOrCreateIdentity, savedPort } from "./identity.js";
10
10
  import { isPortFree, pickFreePort } from "./port.js";
11
11
  import { createClaudeAdapter, findClaude } from "./adapters/claude.js";
12
12
  import { createCodexAdapter, findCodex } from "./adapters/codex.js";
13
- import { createGeminiAdapter, findGemini } from "./adapters/gemini.js";
13
+ import { AGY_SETTINGS_FILE, agyTrustedWorkspace, createAgyAdapter, createGeminiAdapter, findAgy, findGemini } from "./adapters/gemini.js";
14
14
  import { killTree, spawnCli } from "./adapters/util.js";
15
- import { SessionManager } from "./sessions.js";
15
+ import { DEFAULT_PROJECTS_DIR, SessionManager } from "./sessions.js";
16
16
  import { SESSIONS_FILE, SessionStore } from "./store.js";
17
17
  import { logToFile } from "./logfile.js";
18
18
  import { LOG_FILE, installService, probeHealth, serviceStatus, stopService, uninstallService, waitHealthy } from "./service.js";
@@ -108,13 +108,16 @@ async function start(flags) {
108
108
  const geminiBin = findGemini();
109
109
  if (geminiBin)
110
110
  sessions.registerAdapter(createGeminiAdapter(geminiBin));
111
+ const agyBin = findAgy();
112
+ if (agyBin)
113
+ sessions.registerAdapter(createAgyAdapter(agyBin));
111
114
  const claudeBin = findClaude();
112
115
  if (claudeBin)
113
116
  sessions.registerAdapter(createClaudeAdapter(claudeBin));
114
117
  const codexBin = findCodex();
115
118
  if (codexBin)
116
119
  sessions.registerAdapter(createCodexAdapter(codexBin));
117
- console.log(`Engines: ${sessions.engines().join(", ") || "none found (install gemini, claude or codex CLI)"}`);
120
+ console.log(`Engines: ${sessions.engines().join(", ") || "none found (install gemini, agy, claude or codex CLI)"}`);
118
121
  if (persist) {
119
122
  const restored = await sessions.restore();
120
123
  console.log(`Sessions: ${restored} restored from ${SESSIONS_FILE} (--no-persist disables saving)`);
@@ -275,9 +278,11 @@ async function doctor(flags) {
275
278
  const claude = findClaude();
276
279
  const gemini = findGemini();
277
280
  const codex = findCodex();
281
+ const agy = findAgy();
278
282
  for (const [name, file, install] of [
279
283
  ["Claude Code", claude, "install it from https://claude.com/product/claude-code, then run `claude` once to sign in"],
280
284
  ["Gemini CLI", gemini, "install it with `npm i -g @google/gemini-cli`, then run `gemini` once to sign in"],
285
+ ["Antigravity CLI (agy)", agy, "install Antigravity CLI (it puts `agy` in ~/.gemini/bin), then run `agy` once to sign in"],
281
286
  ["Codex CLI", codex, "install it with `npm i -g @openai/codex`, then run `codex login` once to sign in"],
282
287
  ]) {
283
288
  if (!file) {
@@ -290,8 +295,13 @@ async function doctor(flags) {
290
295
  else
291
296
  warn(`${name} found at ${file} but \`--version\` did not answer; run it in a terminal once to sign in or repair it`);
292
297
  }
293
- if (!claude && !gemini && !codex)
294
- bad("no engine found: the app has nothing to drive. Install Claude Code, Gemini CLI or Codex CLI (see above), then run `doctor` again");
298
+ if (!claude && !gemini && !codex && !agy)
299
+ bad("no engine found: the app has nothing to drive. Install Claude Code, Gemini CLI, Antigravity CLI or Codex CLI (see above), then run `doctor` again");
300
+ if (agy) {
301
+ const trusted = agyTrustedWorkspace(DEFAULT_PROJECTS_DIR);
302
+ if (trusted)
303
+ warn(`Antigravity trusts ${trusted}, which contains Remogent's default chat folder (${DEFAULT_PROJECTS_DIR}): in Ask mode agy edits files there without asking (shell commands still ask). For edit approvals remove it from "trustedWorkspaces" in ${AGY_SETTINGS_FILE()}`);
304
+ }
295
305
  const addrs = [];
296
306
  for (const [iface, list] of Object.entries(networkInterfaces())) {
297
307
  for (const a of list ?? []) {
@@ -408,7 +418,7 @@ else {
408
418
  " firewall Windows: allow the phone through Defender Firewall on private networks (add --elevate to get the permission prompt)\n" +
409
419
  " status Show autostart state and whether the host answers\n" +
410
420
  " pair Show the pairing QR again (for a new phone), close itself once the phone connects; the installed host keeps running\n" +
411
- " doctor Check Node, the Claude/Gemini/Codex CLIs (found automatically), network address and autostart, and say how to fix gaps\n" +
421
+ " doctor Check Node, the Claude/Gemini/Antigravity/Codex CLIs (found automatically), network address and autostart, and say how to fix gaps\n" +
412
422
  " start Run the host in this terminal (no autostart)\n" +
413
423
  "Options: [--port N] [--host IP] [--rotate] [--no-qr] [--show-url] [--no-persist] [--log FILE] [--no-wait] [--timeout MIN]");
414
424
  process.exit(cmd === "help" || cmd === "--help" ? 0 : 1);
package/dist/protocol.js CHANGED
@@ -9,7 +9,7 @@ export class ProtocolError extends Error {
9
9
  this.reqId = reqId;
10
10
  }
11
11
  }
12
- const ENGINES = ["gemini", "claude", "codex"];
12
+ const ENGINES = ["gemini", "claude", "codex", "agy"];
13
13
  const MODES = ["ask", "plan", "auto_edit", "auto"];
14
14
  const DECISIONS = ["deny", "allow_once", "allow_session"];
15
15
  const MAX_MODEL_ID = 200;
package/dist/sessions.js CHANGED
@@ -453,7 +453,7 @@ export class SessionManager {
453
453
  if (s.lastStallWarnAt && now - s.lastStallWarnAt < this.stallRepeatMs)
454
454
  continue;
455
455
  s.lastStallWarnAt = now;
456
- const who = s.engine === "gemini" ? "Gemini" : s.engine === "codex" ? "Codex" : "Claude";
456
+ const who = s.engine === "gemini" ? "Gemini" : s.engine === "codex" ? "Codex" : s.engine === "agy" ? "Antigravity" : "Claude";
457
457
  const how = silent >= 120_000 ? `${Math.round(silent / 60_000)} min` : `${Math.round(silent / 1000)} s`;
458
458
  const what = s.toolRunning ? "a tool call is still running" : `${who} may be stuck waiting on the model API`;
459
459
  this.emit(s, { kind: "error", message: `No output from ${who} for ${how}: ${what}. Tap Stop and resend if it does not recover.` }, true);
package/dist/store.js CHANGED
@@ -15,7 +15,7 @@ function valid(p) {
15
15
  if (!isObj(p))
16
16
  return false;
17
17
  return (typeof p["id"] === "string" &&
18
- (p["engine"] === "claude" || p["engine"] === "gemini" || p["engine"] === "codex") &&
18
+ (p["engine"] === "claude" || p["engine"] === "gemini" || p["engine"] === "codex" || p["engine"] === "agy") &&
19
19
  typeof p["cwd"] === "string" &&
20
20
  typeof p["permissionMode"] === "string" &&
21
21
  typeof p["createdAt"] === "number" &&
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@infomity/remogent",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Remogent host: drive Claude Code, Gemini CLI and Codex CLI on your PC from the Remogent Android app. No cloud relay, no account.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",