Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aac54035f2 | ||
|
|
db79b50d7b | ||
|
|
e0205e52e7 |
@@ -0,0 +1,331 @@
|
||||
# Row 229 design: a per-instance pi agent dir for Chet's Codex login (#428, Gitea #1730)
|
||||
|
||||
Design round 2, Gosalyn, 2026-10-22, for Webby's webby-0148 (R1, R2, N1-N5). Brief:
|
||||
`docs/plans/2026-10-20_case2-rows.md`, "Brief: row B, a per-instance pi agent dir for Chet's Codex login
|
||||
(#428)". Base: `refactor` at aca16404c. Pi is the pinned
|
||||
`@earendil-works/pi-coding-agent` in the repository's `node_modules`. Cites into pi are
|
||||
`node_modules/@earendil-works/pi-coding-agent/dist/...` (`pi:`) and
|
||||
`node_modules/@earendil-works/pi-ai/dist/...` (`pi-ai:`).
|
||||
|
||||
This is a design. Nothing in it is built or tested. The brief's gate holds: no build row starts until
|
||||
the live check in section 9 passes, run with Jason's or Mos's go, after 10-27. Mos ruled that "starts
|
||||
after 10-27" covers the build and the live check, not the design, and that the check gates every build
|
||||
row (mos-1306). Mos asks Jason for the go when the build rows are ready.
|
||||
|
||||
## 0. Summary
|
||||
|
||||
- Each pi instance whose route uses provider `openai-codex` gets its own agent dir,
|
||||
`<dataRoot>/pi-agents/<business>/<instance>/`. The host makes it with the same checks as a thread store:
|
||||
a real directory, owned by the bus user, mode 0700, no symlink in the path.
|
||||
- The launcher binds that dir read-write at the same path into the session and sets
|
||||
`PI_CODING_AGENT_DIR` to it through the pi module's `env()`. No other instance's session sees it.
|
||||
- Pi refreshes in place under its own file lock. The new tokens are on the host disk when the run ends,
|
||||
so the next run starts with them.
|
||||
- No tool in the session can read the dir. The gate holds every path tool to the workspace, and a role
|
||||
with `bash` refuses at launch with `pi-agent-dir-shell`.
|
||||
- Before each launch the host checks the dir's contents against a short allowlist: `auth.json`, its
|
||||
`auth.json.lock` dir, and a `settings.json` holding only the three keys a fresh `/login` writes. Pi
|
||||
reads `models.json`, `models-store.json` and `settings.json` from the agent dir, and any of them could
|
||||
send the token elsewhere or run a command. Anything outside the allowlist refuses `pi-agent-dir-unsafe`.
|
||||
- The pi module's preflight runs `pi auth check --provider openai-codex --no-refresh --json` against the
|
||||
dir. No login refuses `pi-login-missing`, and a broken file refuses `pi-login-invalid`.
|
||||
- The human logs in with a new `mosaic pi-login <business> <instance>`. It runs pi's own `/login` with
|
||||
the dir set, `--offline`, `--no-session` and an empty temporary cwd, so Mosaic gets its own fresh
|
||||
grant and pi writes nothing else there. It never copies Hermes's `auth.json`.
|
||||
- Chet's route has no fallback by default (mos-1306). A fallback would quietly move him back onto Bean's
|
||||
Z.ai key.
|
||||
- Two build rows: 229a, the store, the checks, the bind, the env, the preflight and the tests. 229b, the
|
||||
login command and the README.
|
||||
|
||||
## 1. Facts at aca16404c
|
||||
|
||||
Pi side:
|
||||
- `getAgentDir()` returns `PI_CODING_AGENT_DIR`, else `~/.pi/agent` (`pi:config.js:421`). `auth.json`,
|
||||
`settings.json`, `models.json`, `themes/`, `tools/`, `bin/`, `prompts/`, `sessions/` and `pi-debug.log`
|
||||
all sit under it (`pi:config.js:430-462`), and so do `models-store.json`
|
||||
(`pi:core/models-store.js:30`), a cached provider catalog, and `keybindings.json`
|
||||
(`pi:core/keybindings.js:301`).
|
||||
- `checkProviderAuth` builds a model runtime, and `ModelRuntime.create` loads `models.json` from the
|
||||
agent dir (`pi:core/model-runtime.js:76`). So the auth check itself reads `models.json`.
|
||||
- Interactive mode downloads `fd` and `rg` into `<agentDir>/bin/` when they are missing
|
||||
(`pi:modes/interactive/interactive-mode.js:699-705`, `pi:utils/tools-manager.js:300-330`) unless
|
||||
`PI_OFFLINE` is set, which `--offline` does (`pi:main.js:440-444`, `pi:utils/tools-manager.js:13`).
|
||||
Without `--no-session` it makes `<agentDir>/sessions/` (`pi:core/session-manager.js:242-252`).
|
||||
- A fresh interactive start writes `lastChangelogVersion` to `settings.json`
|
||||
(`pi:modes/interactive/interactive-mode.js:918`). A first `/login` then selects the provider's
|
||||
default model with `persist: true` (`:4766`), which writes `defaultProvider` and `defaultModel`
|
||||
(`pi:core/settings-manager.js:470-476`). `enabledModels` changes only when scoped models are set
|
||||
(`pi:core/agent-session.js:1272-1285`), and a fresh dir has none. No settings write runs outside
|
||||
interactive mode.
|
||||
- `AuthStorage` writes `auth.json` with mode 0600 only when it creates the file
|
||||
(`pi:core/auth-storage.js:14-15`). It never fixes the mode of an existing file. It takes a
|
||||
proper-lockfile lock before a refresh write (`:39`, `:85`), which is the directory `auth.json.lock`
|
||||
beside the file, and writes with `writeFileSync` (`:66`, `:140`). The lock is a sibling path, so the
|
||||
dir itself must be writable. A file-only bind isn't enough.
|
||||
- pi-ai refreshes under a double-checked lock and treats a token with less than 5 minutes left as
|
||||
expired (`pi-ai:...resolve.js:62-74`). Two sessions sharing one dir therefore serialize their refreshes.
|
||||
- The `openai-codex` OAuth client has a browser flow with a callback on `localhost:1455` and a device-code
|
||||
flow at `auth.openai.com/codex/device` (`pi-ai:auth/oauth/openai-codex.js:26-33`). A refresh posts
|
||||
`grant_type=refresh_token` (`:125-140`), and the response carries a new refresh token. The old one is
|
||||
then spent.
|
||||
- Startup migrations write into the agent dir only when legacy files exist there: `oauth.json`, session
|
||||
files in the dir root, `commands/`, `tools/` (`pi:migrations.js:17-63`, `:76-129`, `:161-177`).
|
||||
`settings-manager.js:101` writes `settings.json` when a setting changes.
|
||||
- `pi auth check --provider <p> [--json] [--credentials] [--no-refresh]` (`pi:cli/auth-command.js:5`,
|
||||
`:23-25`). `checkProviderAuth` (`pi:cli/auth-check.js:5-34`) returns `ready`, `not_ready` or `invalid`.
|
||||
It builds its model runtime with `allowModelNetwork: false` (`:43-49`). With `--no-refresh` it only
|
||||
checks that a credential is present: a revoked refresh token still reads `ready`. It prints no
|
||||
credential unless `--credentials` is given. `auth print-bearer-token` and `print-api-key` print
|
||||
secrets, and Mosaic never runs them.
|
||||
|
||||
Mosaic side:
|
||||
- `binds()` lists every mount and says why (`packages/seat/src/sandbox.mjs:102-131`). The thread store
|
||||
binds read-write at `:119-125`. `sandboxArgv` sets `HOME` to the run's own `home/` (`:163-166`).
|
||||
- `threadStore()` makes `<dataRoot>/threads/<business>/<thread>/` and refuses a symlink, a non-directory,
|
||||
another owner or any group or other mode bit (`packages/seat/src/session.mjs:59-72`). `ENV_ALLOW`
|
||||
doesn't list `PI_CODING_AGENT_DIR` (`:83-88`).
|
||||
- The launcher's check order is at `packages/cli/src/launcher.mjs:1-40`. One instance per thread runs at
|
||||
a time (`:289`, `registry.running(b.id, instance, thread)`), so a thread instance such as Chet can run in
|
||||
two threads at once. Preflight is `harness.preflight({ env })` (`:333`). A failed primary preflight
|
||||
takes the stated fallback (`:334-345`, row 65). The thread store is made at `:388-394`, mounts at `:456`,
|
||||
the session file with `harnessEnv: bundle.env` at `:461-484`, the paths at `:486-495`.
|
||||
- The pi module's `preflight()` checks only the binary (`packages/plugins/harness/pi/index.mjs:33-37`).
|
||||
`env({ policy, tools, session })` builds the harness env (`:63-71`). The adapter runs pi with
|
||||
`--no-extensions`, `--no-skills`, `--no-prompt-templates`, `--no-approve` and `--offline`
|
||||
(`adapters/pi/adapter.sh:104-123`).
|
||||
- The gate refuses a path tool whose path is outside the workspace (`packages/harness/src/gate.mjs:79-83`,
|
||||
`:152`). Chet's role, `roles/companion.json`, has tools `["read","web"]`.
|
||||
|
||||
## 2. Where the dir lives and who can read it
|
||||
|
||||
`<dataRoot>/pi-agents/<business>/<instance>/`. A new seat function `agentStore(dataRoot, business,
|
||||
instance)` in `session.mjs`, beside `threadStore()`, makes each level with mode 0700 and the same refusal:
|
||||
a symlink, a non-directory, another owner or any 0077 mode bit refuses `pi-agent-dir-unsafe`. Business and
|
||||
instance must match `ID_PATTERN` and hold no `..`.
|
||||
|
||||
The dir belongs to the instance, not the thread. Chet's threads share one login, and pi's lock keeps
|
||||
their refreshes apart (section 1). Only the bus user can read it on the host. A seat that shares the bus
|
||||
uid can read it too, which is the same exposure the thread stores and the bus's own credential files
|
||||
have today (case 2 r11 B1). This row doesn't change that.
|
||||
|
||||
Which routes get one. The pi module exports `agentStore({ provider })`, true for `openai-codex`. The
|
||||
launcher asks the primary route's module, and the fallback's if one is taken. A module without the hook
|
||||
gets no dir. A provider key route such as Bean's Z.ai keeps working exactly as today.
|
||||
|
||||
## 3. How the launcher mounts it
|
||||
|
||||
After step 5 picks the route and before the bwrap check:
|
||||
1. If the module says the route needs a store, the launcher calls `agentStore()`. A refusal stops the
|
||||
launch with its code.
|
||||
2. The contents check in section 5 runs.
|
||||
3. The module's preflight runs with the dir (section 6).
|
||||
4. `paths` gains `agentStore`, and `binds()` adds one entry:
|
||||
`{ kind: "rw", src: dir, why: "the instance's pi agent dir, its own login, kept across runs (row 229)" }`.
|
||||
It binds at the same path, so the path pi sees is the host path.
|
||||
5. The session object passed to `bundle` gains `agentDir`, and the pi module's `env()` adds
|
||||
`PI_CODING_AGENT_DIR: session.agentDir` when it is set. It reaches pi through `session.harnessEnv`,
|
||||
which the runner spreads into the adapter's env. `ENV_ALLOW` doesn't change, so no host value of
|
||||
`PI_CODING_AGENT_DIR` ever passes.
|
||||
|
||||
The preflight signature changes to `preflight({ env, agentDir })`. Every other module ignores the new key,
|
||||
so their behaviour doesn't change. The adapter contract in `adapters/README.md` gains one line for it.
|
||||
|
||||
## 4. How a refresh survives the run
|
||||
|
||||
Pi writes the refreshed `auth.json` into the bound dir, which is the host dir. Nothing copies it in or out.
|
||||
A run that dies mid-write leaves pi's lock behind. Pi's lock goes stale on its own timer. The contents
|
||||
check in section 5 accepts a lock entry and never deletes one, so the host never breaks a lock a running
|
||||
session holds.
|
||||
|
||||
Two clients on one grant would race: a refresh spends the old refresh token, and the other client's next
|
||||
refresh fails. Mosaic avoids that by never sharing a grant. `mosaic pi-login` makes a new one (section 7).
|
||||
Hermes keeps its own grant. Whether OpenAI revokes an older grant when a new one is made, or limits grants
|
||||
per account, I don't know. Section 9's live check exists to find out.
|
||||
|
||||
## 5. What the session can read, and what the dir may hold
|
||||
|
||||
The session's tools:
|
||||
- `read` and every other path tool go through the gate, which refuses a path outside the workspace. The
|
||||
agent dir is outside it.
|
||||
- `web` sends a URL and gets text back. It has no file argument.
|
||||
- `bash` would reach the dir, since pi itself must open it. A role whose tool list has `bash`, or a route
|
||||
with the harness's built-in shell, refuses `pi-agent-dir-shell` at launch. The same check runs in
|
||||
`validate` for the role file, so the conflict shows before a launch.
|
||||
- The pi process reads the dir. That is the point of the bind.
|
||||
|
||||
The dir's contents. Pi reads more than `auth.json` from the agent dir. A `models.json` there can set a
|
||||
provider's base URL, which would send the OAuth token to another host, and `models-store.json` is a
|
||||
cached catalog of the same kind. `settings.json` is worse than it looks. Every dangerous setting is a
|
||||
key pi knows, so a check against pi's own key set would pass all of them (webby-0148 R1):
|
||||
- `httpProxy` applies to every request pi makes, the Codex bearer and the refresh POST included
|
||||
(`pi:main.js:455`, `:685`)
|
||||
- `packages`, `extensions`, `skills`, `prompts` and `themes` name code or text to load
|
||||
- `shellPath`, `shellCommandPrefix`, `npmCommand`, `externalEditorCommand` and `sessionDir` name a
|
||||
command to run or a place to write
|
||||
|
||||
`--no-extensions` and the other flags turn discovery off, but this design doesn't rest on them alone.
|
||||
Before each launch the host lists the dir and allows only:
|
||||
- `auth.json`, a regular file, mode 0600, owned by the bus user. Pi sets 0600 only when it creates the
|
||||
file, so this check is what holds the mode
|
||||
- `auth.json.lock`, a directory, owned by the bus user. It is the only directory allowed, and the check
|
||||
never deletes it (section 4)
|
||||
- `settings.json`, a regular file of at most 4 KiB that parses to a plain object, empty or with only
|
||||
these keys: `lastChangelogVersion`, a string of at most 32 characters matching
|
||||
`^\d+\.\d+\.\d+[0-9A-Za-z.+-]*$`, `defaultProvider`, exactly `openai-codex`, and `defaultModel`, a
|
||||
string of at most 64 characters matching `^[A-Za-z0-9._-]+$`. These are the keys a fresh `/login`
|
||||
writes (section 1). Any other key refuses, `httpProxy` and the paths and commands above by name
|
||||
|
||||
Anything else refuses `pi-agent-dir-unsafe` and names the entry: `models.json`, `models-store.json`,
|
||||
`keybindings.json`, `extensions/`, `tools/`, `bin/`, `themes/`, `prompts/`, `skills/`, `sessions/`,
|
||||
`pi-debug.log`, `oauth.json`, a symlink, a device, or any other file or directory. Round 1 allowed
|
||||
`models-store.json` and `keybindings.json`. Round 2 refuses both: the first can carry a base URL like
|
||||
`models.json`, and nothing in a headless run or a login needs the second. Nothing in the session can
|
||||
create a refused entry, because no session tool reaches the dir. The check guards against a human
|
||||
copying a whole `~/.pi/agent` in by hand, and against a login that wrote more than it should.
|
||||
|
||||
The settings allowlist is the three keys above, written in Mosaic's code, not read from pi. A test pins
|
||||
the sorted list of `SettingsManager` setter names in the pinned package by its sha256, so a pi bump that
|
||||
adds, drops or renames any setting fails the suite, and the human reviewing the bump re-checks the three
|
||||
keys before re-pinning.
|
||||
|
||||
## 6. How a launch with no login refuses
|
||||
|
||||
The pi module's `preflight({ env, agentDir })`, after the binary check, runs when `agentDir` is set:
|
||||
|
||||
```
|
||||
node <pi bin> auth check --provider openai-codex --no-refresh --json
|
||||
```
|
||||
|
||||
It runs only after the section 5 contents check passes, because the auth check loads `models.json` from
|
||||
the dir (section 1). It uses `PI_CODING_AGENT_DIR=<agentDir>`, `HOME` and the cwd both set to one empty
|
||||
temporary dir made with `mkdtemp` under the scratch tmp and removed afterwards, and `PATH` from the
|
||||
system. It runs on the host with a 10-second timeout. Never `--credentials`. Results:
|
||||
- exit 0, `ready`: pass
|
||||
- exit 1, `not_ready`: `pi-login-missing`, with the message "run `mosaic pi-login <business> <instance>`"
|
||||
- exit 2, `invalid`, a timeout or any other exit: `pi-login-invalid`
|
||||
|
||||
The check never refreshes, so a launch never spends a refresh token on the host. That leaves one gap I
|
||||
can't close without a network call: a revoked or expired refresh token still reads `ready`. Pi then
|
||||
fails the first turn when its refresh fails, and the turn's failure is visible in the run record. The
|
||||
fix is the same login command. I chose `--no-refresh` over a refresh at preflight because a host-side
|
||||
refresh would race a running session of the same instance.
|
||||
|
||||
Fallback. Row 65 lets a failed primary preflight take the stated fallback. Chet's route ships with none
|
||||
(mos-1306). A fallback onto Bean's Z.ai key would make a lost login silent: Chet answers on another
|
||||
provider and Melanie can't tell. Without one, the launch refuses with the preflight's code. A human can
|
||||
still add a fallback through `routes.set`. No agent path can: Chet is a `companion` instance, a creative
|
||||
definition (`packages/business/src/vocabulary.mjs:207`), and `creativeHumanOnly`
|
||||
(`packages/business/src/launch-settings.mjs:13-17`, called for routes at `:293`) refuses
|
||||
`organization-creative-human-only` unless the actor is the business file's human. The tester is not
|
||||
that human, though `routes.set` is a tester verb (`packages/cli/src/broker.mjs:139`). This row only
|
||||
makes none the default.
|
||||
|
||||
## 7. The login command (229b)
|
||||
|
||||
`mosaic pi-login <business> <instance>` in `packages/cli`, a human verb:
|
||||
1. Refuses unless the instance's primary route is pi with provider `openai-codex`
|
||||
(`pi-login-not-needed`), and unless the instance isn't running (`instance-running`).
|
||||
2. Calls `agentStore()` and runs the contents check.
|
||||
3. Moves an existing `auth.json` out of the dir, to
|
||||
`<dataRoot>/pi-agents/<business>/.replaced/<instance>-<UTC>.json` at 0600, so every login is a fresh
|
||||
grant and the contents allowlist doesn't grow. It never reads or copies `~/.pi/agent` or Hermes's
|
||||
files.
|
||||
4. Runs interactive pi on the attached terminal with `PI_CODING_AGENT_DIR=<dir>`, `HOME` and the cwd
|
||||
both set to one empty temporary dir under the scratch tmp, and these flags: `--offline`, so pi
|
||||
downloads no `fd` or `rg` into `<dir>/bin/` and sends no telemetry, `--no-session`, so it makes no
|
||||
`<dir>/sessions/`, and the `--no-extensions`, `--no-skills` and `--no-prompt-templates` the adapter
|
||||
uses. `--offline` doesn't block `/login`: it sets `PI_OFFLINE` and skips the version check and the
|
||||
catalog refresh, and the OAuth flow makes its own requests. The command tells the human to type
|
||||
`/login`, choose OpenAI Codex and pick the device code flow, since the callback on `localhost:1455`
|
||||
won't reach a browser on another machine.
|
||||
5. Afterwards runs the section 5 contents check, then the section 6 check, and prints only their
|
||||
status. A login that left any entry outside the allowlist refuses `pi-agent-dir-unsafe` here, before
|
||||
any launch.
|
||||
|
||||
The command prints no token. `.replaced/` gets the `agentStore()` checks. It can't collide with an
|
||||
instance dir, since `ID_PATTERN` doesn't allow a leading dot.
|
||||
|
||||
`reset.sh` wipes the data root, and the login with it. The README says so.
|
||||
|
||||
## 8. Files the build touches
|
||||
|
||||
229a:
|
||||
- `packages/seat/src/session.mjs`: `agentStore()`, the contents check
|
||||
- `packages/seat/src/sandbox.mjs`: the `agentStore` bind in `binds()`
|
||||
- `packages/cli/src/launcher.mjs`: the store, the check and the preflight argument after step 5, the
|
||||
`pi-agent-dir-shell` refusal, `paths.agentStore`, `agentDir` on the session
|
||||
- no change to `packages/harness/src/bundle.mjs`, which already passes `{ ...session, ...model }` to `env()`
|
||||
(`:375`), so `agentDir` on the launcher's session reaches the module
|
||||
- `packages/plugins/harness/pi/index.mjs`: `agentStore()`, the preflight, `PI_CODING_AGENT_DIR` in `env()`
|
||||
- `adapters/README.md`: the `agentDir` preflight key
|
||||
- the role validator for `pi-agent-dir-shell`
|
||||
- tests in `packages/seat/tests`, `packages/cli/tests`, `packages/plugins/harness/pi/tests`
|
||||
|
||||
229b:
|
||||
- `packages/cli/src/pi-login.mjs` and its dispatch
|
||||
- `packages/cli/README.md`
|
||||
- tests in `packages/cli/tests`
|
||||
|
||||
## 9. The live check (the gate)
|
||||
|
||||
Run after 10-27, with Jason's or Mos's go, on Chet's real ChatGPT login. A human does the login.
|
||||
1. Hermes answers as Chet. Record the time.
|
||||
2. `mosaic pi-login` for Chet. The status reads `ready`.
|
||||
3. Launch Chet on the new route. Run one turn.
|
||||
4. Force a Mosaic refresh. The human edits only `expires` in Mosaic's `auth.json` to a past time, then
|
||||
runs one more turn. It succeeds, and `auth.json` changed. After steps 2, 3 and 4 the record lists the
|
||||
dir's entry names, and each passes the section 5 check.
|
||||
5. Hermes answers again. This is the check that Mosaic's refresh didn't revoke Hermes's grant.
|
||||
6. Wait for Hermes's own next refresh, at least one access-token lifetime, then run a Mosaic turn. This is
|
||||
the check the other way.
|
||||
7. The record lists each step's time and result, and never a token.
|
||||
|
||||
Any failure stops the row. The answer then goes to Jason: Chet waits until Hermes stops, or he moves
|
||||
alone. No build row starts before step 6 passes.
|
||||
|
||||
## 10. Tests the build rows carry
|
||||
|
||||
229a:
|
||||
- `agentStore()` refuses a symlinked level, a wrong owner, a 0750 dir and a `..` id
|
||||
- the contents check passes the allowed set and refuses each listed entry, `models-store.json`,
|
||||
`keybindings.json`, `bin/` and `pi-debug.log` included, and a second directory beside
|
||||
`auth.json.lock`
|
||||
- `settings.json` passes when absent, `{}` and with the three allowed keys. It refuses each of
|
||||
`httpProxy`, `packages`, `extensions`, `skills`, `prompts`, `themes`, `shellPath`,
|
||||
`shellCommandPrefix`, `npmCommand`, `externalEditorCommand` and `sessionDir` by name, a
|
||||
`defaultProvider` other than `openai-codex`, a non-string or overlong value, an array, a file over
|
||||
4 KiB and a file that doesn't parse
|
||||
- `auth.json` at 0644 refuses, so a file that pi didn't create still meets the mode
|
||||
- the bump test: the sha256 of the sorted `SettingsManager` setter names in the pinned package matches
|
||||
the pinned value, so any change to pi's settings key set fails it
|
||||
- a `models.json` in the dir refuses `pi-agent-dir-unsafe` before the fake pi bin runs. The fake bin
|
||||
records each call, and the record is empty
|
||||
- the preflight's fake pi bin records its cwd and `HOME`, which are the same empty temporary dir
|
||||
- the tester's `routes.set` adding a fallback to Chet's route refuses `organization-creative-human-only`
|
||||
- a companion role with `bash` refuses `pi-agent-dir-shell`
|
||||
- `binds()` adds the entry only when `agentStore` is set
|
||||
- `env()` sets `PI_CODING_AGENT_DIR` only when `agentDir` is set
|
||||
- a host `PI_CODING_AGENT_DIR` never reaches the session
|
||||
- preflight maps exit 0, 1, 2 and a timeout to pass, `pi-login-missing` and `pi-login-invalid`. A fake
|
||||
pi bin records its argv, which must hold `--no-refresh` and never `--credentials`
|
||||
- a route with no store gets no bind and no preflight change. Bean's route is unchanged
|
||||
- Chet's route with a failed preflight and no fallback refuses with the preflight's code
|
||||
- a mutant without the contents check fails, and so does one without the shell refusal
|
||||
- every test uses `mktemp -d` under the scratch tmp, never `/tmp`, and no real login
|
||||
|
||||
229b:
|
||||
- `pi-login` refuses a non-codex route and a running instance
|
||||
- it moves an existing `auth.json` out of the dir, never reads `~/.pi/agent`, and prints only the status
|
||||
- its fake pi bin records argv, which holds `--offline` and `--no-session`, and a cwd that is an empty
|
||||
temporary dir
|
||||
- after a fake login that writes `auth.json`, `auth.json.lock/` and a `settings.json` with the three keys,
|
||||
the dir holds only allowlisted entries and the command reports `ready`
|
||||
- a fake login that also writes `bin/fd` or `sessions/` makes the command refuse `pi-agent-dir-unsafe`
|
||||
|
||||
## 11. Open items
|
||||
|
||||
- Whether OpenAI revokes or caps grants per account. Section 9 answers it.
|
||||
- Whether pi's request-time refresh works under the adapter's flags. I read nothing that blocks it.
|
||||
`--offline` affects only the model catalog (`pi:core/model-runtime.js:88`). Section 9 step 4 proves it.
|
||||
- Whether a real `/login` on the pinned pi writes only the three settings keys. Section 1 reads it from
|
||||
the code. Section 9's dir listings prove it, and a fourth key there stops the row for a design change.
|
||||
Reference in new issue
Block a user