diff --git a/bin/omarchy-agent-usage-agy b/bin/omarchy-agent-usage-agy index 44c0d2bbabd..0c1ba1ccae4 100755 --- a/bin/omarchy-agent-usage-agy +++ b/bin/omarchy-agent-usage-agy @@ -151,8 +151,14 @@ def harness_login(source, data): } +# omp and pi both move their whole agent tree when PI_CODING_AGENT_DIR is +# set, so both sign-ins are looked for where it points. +def omp_agent_dir(): + return Path(os.environ.get("PI_CODING_AGENT_DIR") or (Path.home() / ".omp" / "agent")) + + def omp_logins(): - database = Path.home() / ".omp" / "agent" / "agent.db" + database = omp_agent_dir() / "agent.db" if not database.is_file(): return [] try: diff --git a/shell/plugins/agents/README.md b/shell/plugins/agents/README.md index 581e3852243..8afc5a65de3 100644 --- a/shell/plugins/agents/README.md +++ b/shell/plugins/agents/README.md @@ -170,7 +170,7 @@ The collector asks the client's own key endpoint for the subscription's session ### Antigravity -The record's id is `agy`, the name the default agent knows it by. The plan and limits are asked of Google's Cloud Code API with whichever Antigravity sign-in is on the machine: the one `agy` keeps in the Secret Service keyring (service `gemini`, username `antigravity`) or, without a keyring, in `antigravity-oauth-token` under `AGY_DIR`; omp's `google-antigravity` sign-in in `~/.omp/agent/agent.db`; pi's in `auth.json` (under `PI_CODING_AGENT_DIR` when set); and opencode's `google` sign-in, only when the Antigravity plugin's `antigravity-accounts.json` is there. The token refreshed most recently is asked first, and one Google refuses gives way to the next. Those stores are only ever read. A lapsed token shows the last limits as paused until the harness that owns it runs again; a lapsed omp sign-in is asked through `omp usage`, which renews it. Each Google account keeps its own last answer, so another account's limits never stand in. `CLOUD_CODE_URL` moves the endpoint, over https only. +The record's id is `agy`, the name the default agent knows it by. The plan and limits are asked of Google's Cloud Code API with whichever Antigravity sign-in is on the machine: the one `agy` keeps in the Secret Service keyring (service `gemini`, username `antigravity`) or, without a keyring, in `antigravity-oauth-token` under `AGY_DIR`; omp's `google-antigravity` sign-in in `~/.omp/agent/agent.db`; pi's in `~/.pi/agent/auth.json`, both under `PI_CODING_AGENT_DIR` instead when it is set, since omp and pi each move their agent tree there; and opencode's `google` sign-in, only when the Antigravity plugin's `antigravity-accounts.json` is there. The token refreshed most recently is asked first, and one Google refuses gives way to the next. Those stores are only ever read. A lapsed token shows the last limits as paused until the harness that owns it runs again; a lapsed omp sign-in is asked through `omp usage`, which renews it. Each Google account keeps its own last answer, so another account's limits never stand in. `CLOUD_CODE_URL` moves the endpoint, over https only. Local stats count `agy`'s history prompts, its conversations (a subagent's conversation counts toward the one that spawned it), and the tokens each transcript step reports, under the model the step or the last Model Selection names. Antigravity answers that pi, omp, OpenClaw (`OPENCLAW_STATE_DIR`), and opencode's plugin recorded count too, a forked copy once. No other collector counts these. diff --git a/test/shell.d/agent-usage-agy-harness-test.sh b/test/shell.d/agent-usage-agy-harness-test.sh index 3181b82d1b0..a72d160a033 100755 --- a/test/shell.d/agent-usage-agy-harness-test.sh +++ b/test/shell.d/agent-usage-agy-harness-test.sh @@ -43,8 +43,8 @@ def machine(): os.environ["PATH"] = f"{stubs}:{REAL_PATH}" -def omp_login(token, expires, email): - database = home() / ".omp" / "agent" / "agent.db" +def omp_login(token, expires, email, directory=None): + database = Path(directory or home() / ".omp" / "agent") / "agent.db" database.parent.mkdir(parents=True, exist_ok=True) database.unlink(missing_ok=True) conn = sqlite3.connect(database) @@ -78,7 +78,7 @@ def agy(*accepted): # Every store the collector reads, so a test can tell none was written. def stores(): return sorted((str(path), hashlib.md5(path.read_bytes()).hexdigest()) - for root in (".omp", ".pi", ".local/share/opencode", ".gemini", "pi-elsewhere") + for root in (".omp", ".pi", ".local/share/opencode", ".gemini", "pi-elsewhere", "omp-elsewhere") for path in (home() / root).rglob("*") if path.is_file()) @@ -195,6 +195,16 @@ record, asked = agy("moved-token") expect("Antigravity collector reads pi's sign-in where PI_CODING_AGENT_DIR puts it", [asked, record["limits"][0]["percent"]], [["moved-token"], 0.25], record) expect("Antigravity collector never writes pi's store", stores(), before) +# omp reads the same variable for its whole agent tree, so its database moves +# with it. +machine() +omp_login("moved-omp-token", ms_in(3600), "me@example.com", home() / "omp-elsewhere") +os.environ["PI_CODING_AGENT_DIR"] = str(home() / "omp-elsewhere") +before = stores() +record, asked = agy("moved-omp-token") +expect("Antigravity collector reads omp's sign-in where PI_CODING_AGENT_DIR puts it", [asked, record["limits"][0]["percent"]], [["moved-omp-token"], 0.25], record) +expect("Antigravity collector never writes omp's store", stores(), before) + # ---------------------------------------------------------- local stats # OpenClaw keeps pi's format, one directory per agent; its entries carry no