Imported from acking-you/pocket-codex (
AGENTS.md). Install upstream withnpx skills add acking-you/pocket-codex. Copyright stays with the author.
Pocket-Codex Agent Guide
This document is the contract between human contributors, AI coding agents (Claude Code, Codex CLI, etc.) and the project itself. Read it before touching code.
1. Project intent
Pocket-Codex turns the upstream
codex app-server protocol into a
portable, multi-device experience, and additionally exposes the host's
Codex login as a relay-reachable Responses API endpoint for any device:
- A pure-Rust CLI (
pocket-codex) supervises a localcodex app-serverprocess on the machine that already has Codex installed. - The same CLI ships an in-process Responses API proxy that reuses
the host's
codex login(ChatGPT account orCODEX_ACCESS_TOKEN) to serve OpenAI-compatible/v1/responsesHTTP + WebSocket traffic, letting devices without Codex installed drive the same model through the relay. - The CLI uses
pb-mapperto register either service on a relay underpcx:<device>:<kind>:<name>keys, or to subscribe to remote ones, materialising them as local TCP endpoints. - A Flutter front-end (under
apps/flutter, driven throughflutter_rust_bridge) consumes the app-server JSON-RPC protocol directly to give every platform a native UI without re-implementing the model runtime.
Two ways to wire devices together, both first-class:
- Self-host — every device shares one relay address plus a 32-byte
MSG_HEADER_KEY, and talks to the relay directly underpcx:…keys. Selected by an explicit--relay. - Hosted account — the optional
pocket-codex-backendruns once on a server; devices sign in with GitHub, and the backend hands each account a short-lived relay credential confined to its ownpcxu:<user>:…namespace. Devices then talk to the relay directly: the relay's administrator key never reaches a client, accounts stay isolated from each other, and the backend is not on the data path.
The repository deliberately does not vendor a model runtime; the
user-supplied codex binary (and its login state) is the source of
truth.
2. Repository layout
apps/flutter/ # Flutter UI (FRB-driven, FVM-locked at 3.44.0)
assets/logo/ # Project artwork (poster.png, logo.png)
crates/ # all first-party Rust crates; see §3 for who owns what
deploy/ # hosted-backend deployment unit + config examples
deps/
codex/ # acking-you/codex fork, branch `pocket-codex`
# (git submodule) = upstream openai/codex main +
# our adaptations; see §8
docs/ # design notes, protocol references, CLI verification
scripts/ # install scripts, local CI, CI affected-surface gate
vendor/pagable/ # registry source with a 32-bit size-assertion fix
Cargo.toml is a workspace root; every crate under crates/ is a
workspace member (see the members list for the canonical set).
The Codex submodule under deps/ is kept out of the workspace via the
exclude list and retains its upstream lints/profiles. Cargo fetches pb-mapper
from its dedicated pocket-codex Git branch; Cargo.lock pins the exact commit
and its registry dependencies. No pb-mapper, kanal, or uni-stream submodules
or local dependency patches are needed.
vendor/pagable retains the former ARMv7 patch for provenance. It is excluded
and is no longer a dependency or Cargo patch. Keep its runtime source unchanged;
the provenance and patch scope are recorded in its README.pocket-codex.md.
Changes under vendor/ require the full Rust CI gate and must stay outside
first-party formatting and lint rewrites.
3. Crate responsibilities
Shared / host side:
| Crate | Owns |
|---|---|
pocket-codex-core |
configuration schema, on-disk state.toml, well-known paths, error types, service::{ServiceId, ServiceKind, sanitize_component, default_device_id} for pcx:<device>:<kind>:<name> relay keys — small, dependency-light |
pocket-codex-codex |
spawning / supervising / inspecting the external codex app-server child process, compatible JSON-RPC wire envelopes |
pocket-codex-pb |
async wrappers around the Git-pinned pb-mapper client SDK: RelaySession (address + credential), register / subscribe / status, publish (and the one name-conflict failure a caller must not retry), admin credential issuance, and credential keep-alive |
pocket-codex-api-proxy |
local Responses API proxy: forwards /v1/responses (HTTP + WS) to ChatGPT's Codex backend, reusing the host's codex login; shared by the CLI worker and the in-app host |
pocket-codex-host-svc |
host-side meta service — remote-viewable codex sessions, per-thread config, attachment upload — published on the relay as a third meta:<name> service |
pocket-codex-cli |
user-facing pocket-codex binary; account (login / logout / account), setup (init), high-level serve / connect / api {serve,connect} / services {list,default set} / status / stop, low-level codex {start,stop,status}, pb {register,subscribe,status}, remote-hint, version |
pocket_codex_bridge |
cdylib + staticlib consumed by Flutter via flutter_rust_bridge; auto-generated bindings live in lib/src/rust of the Flutter app |
Hosted-account mode (all optional — self-host never touches these):
| Crate | Owns |
|---|---|
pocket-codex-account-proto |
wire types and pcxu:<user>:… key namespacing shared between the backend and its CLI/app clients |
pocket-codex-auth |
GitHub device-flow + web auth-code login, session JWTs, refresh tokens |
pocket-codex-store |
SQLite persistence (users, refresh tokens, device flows) for the backend |
pocket-codex-backend |
the deployable binary: GitHub-login HTTP API + /v1/relay credential vending; see deploy/ |
When in doubt, prefer adding a new module to an existing crate over introducing a new crate. Crates are free; boundaries are not.
4. Engineering principles
We follow Linus Torvalds–style engineering. In short:
- Don't break userspace. Once a CLI flag, on-disk layout or wire-protocol field is documented, it is part of the contract. Add, don't mutate. If a breaking change is unavoidable, version it explicitly and write a migration note.
- KISS / YAGNI. Avoid speculative abstractions. Add a trait when there are at least two real implementations. Add a config knob when there is at least one real user who needs it.
- Critique code, not people. Be technical, be direct, be kind.
- Faithful upstream behaviour > local heuristics. If the upstream
codexorpb-mapperdoes something a particular way, mirror it instead of layering on top a fragile compatibility shim.
5. Code-editing rules
- Comments are written in English. Add a comment only when intent is non-obvious; obvious code does not need narration.
- Public items are documented (
missing_docs = "deny"is on at the workspace level). When you add a public function, write a doc comment. - No
unwrap()/expect()in non-test code without a// reason: ...follow-up.clippy::unwrap_usediswarnand we treat it asdenyin reviews. unsafeis forbidden by default — crate roots carry#![forbid(unsafe_code)]. The sanctioned exception ispocket_codex_bridge, whoseflutter_rust_bridge-generated code emitsunsafe. If you really need it elsewhere, justify it in review and gate it behind a Cargo feature.- Keep functions short and modules shallow. Refactor when nesting goes past three levels.
- Prefer
tracingoverprintln!/eprintln!for anything that is not CLI output the user explicitly asked for. - File paths in handoff messages and change descriptions follow
path:linecitations (e.g.crates/pocket-codex-cli/src/main.rs:42).
6. Workflow checklist
Use this as the default loop for any non-trivial change:
- Intake. Restate the task in your own words. Confirm the problem exists. Note any potential for breaking userspace.
- Context gathering. Locate the files that need to change. Stop as soon as you can name them; aim for ~5–8 tool calls in the first pass.
- Exploration. When ≥3 steps or multiple files are involved, walk dependencies, surface assumptions, and write down the output contract (files changed, expected behaviour, tests touched).
- Plan. Produce a multi-step plan that references concrete files and functions before you edit anything.
- Execute. Make the change. On failure, diagnose and adjust; if blocked, ask the user.
- Verify. Run the verification commands below and reflect: maintainability, tests, performance, security, backward compatibility. Fix issues before handoff.
- Hand off. Summarise the change, cite
path:line, list assumptions, state risks and next steps.
7. Verification commands
Run these (the full set) before claiming a task is done. CI runs the
same commands but scopes them to the surfaces your change touches: a
gate (scripts/ci_affected.py, stdlib-only) computes which crates are
affected from the diff and runs fmt/clippy/test only when a first-party
crate changed (clippy on the changed crates, test on them plus their
workspace dependents; a cross-cutting change — root manifest, lockfile,
rustfmt.toml, .cargo/, deps/, toolchain — falls back to the whole
workspace), and the Flutter job only when apps/flutter/ changed. A
change under .github/ or scripts/ forces the full Rust suite plus
Flutter. So locally you always run everything below; CI may legitimately
skip jobs your change does not touch.
The upstream/submodule code under deps/ is deliberately outside this
workspace's formatting and linting contract: do not run rustfmt,
clippy or other rewrite/lint commands against deps/ unless the task is
an intentional submodule bump or upstream contribution. In particular,
do not run cargo fmt --all; use the explicit first-party package
list below so path/patch dependencies under deps/ are never rewritten.
# Rust workspace — the -p list must cover every workspace member in
# `Cargo.toml`; add new crates here (and in ci.yml) when you create them.
cargo fmt --check \
-p pocket-codex-core -p pocket-codex-codex -p pocket-codex-pb \
-p pocket-codex-api-proxy -p pocket-codex-host-svc -p pocket-codex-cli \
-p pocket_codex_bridge -p pocket-codex-account-proto -p pocket-codex-store \
-p pocket-codex-auth -p pocket-codex-backend
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --locked
# Flutter front-end (requires `fvm install 3.44.0 --setup` once)
cd apps/flutter
fvm flutter pub get
dart format --output=none --set-exit-if-changed lib test integration_test
fvm flutter analyze
fvm flutter test
Optional but encouraged for crates touching FFI or the protocol layer:
cargo doc --workspace --no-deps
flutter_rust_bridge_codegen generate # after editing crates/pocket-codex-bridge/src/api/
Fonts (desktop-only Noto Sans SC)
Chinese renders thin and unevenly weighted on Windows with Flutter's default
fonts, so desktop builds bundle Noto Sans SC (apps/flutter/assets/fonts/,
SIL OFL 1.1). To keep the ~17 MB out of the Android/iOS artifacts, the font is
declared only in apps/flutter/pubspec-desktop.yaml, not the default
pubspec.yaml:
pubspec.yaml— the default used by mobile,flutter test, and local dev. No font. Source of truth for dependencies.pubspec-desktop.yaml— a copy ofpubspec.yamlplus afonts:block. The desktop release jobs (app-windows/app-linux/app-macosin.github/workflows/release.yml)cp pubspec-desktop.yaml pubspec.yamlbefore building. Keep its dependencies in sync withpubspec.yaml.
lib/src/fonts.dart sets the primary family to Noto Sans SC only on desktop
(gated on defaultTargetPlatform, so flutter test — forced to android — takes
the mobile branch); mobile/web use the OS CJK font (PingFang SC / Noto Sans CJK).
To preview the bundled font locally on desktop:
cp apps/flutter/pubspec-desktop.yaml apps/flutter/pubspec.yaml
(cd apps/flutter && fvm flutter pub get && fvm flutter run -d windows)
git checkout -- apps/flutter/pubspec.yaml # restore before committing
8. Working with submodules
deps/codex is a git submodule pinned to a specific commit — the only one
left. deps/pb-mapper (plus the deps/kanal and deps/uni-stream forks it
pulled in transitively) is gone. pb-mapper is a Cargo Git dependency on
https://github.com/acking-you/pb-mapper, branch pocket-codex; Cargo.lock
pins its exact commit. Push SDK fixes to that branch, then run
cargo update -p pb-mapper here and commit the resulting lockfile after
verification. Normal builds use --locked and do not automatically follow
branch updates. After pulling this repo, materialise the Codex submodule with:
git submodule update --init --recursive
8.1 External Codex only; built-in engine is unimplemented
All platforms must run Codex as an external executable. Do not compile, link, bundle, or launch the Codex model runtime in Pocket-Codex. The UI reserves a disabled Built-in engine / 内置引擎 option marked Not implemented yet / 暂未实现. Do not re-enable it through a Cargo feature, target-specific dependency, build script, or release packaging step without an explicit change to this product requirement.
- First-party crates may depend directly on upstream protocol crates only
(
codex-app-server-protocol/codex-protocol). No direct dependencies oncodex-app-server,codex-core,codex-arg0,codex-config, runtime extensions, or executable/helper crates, even as optional dependencies. - Upstream protocol crates currently have their own transitive support
dependencies. Audit the resolved graph on every submodule bump; do not
claim these are a standalone, dependency-free schema. The graph must not
include
codex-app-server,codex-core,codex-arg0,codex-exec,codex-tui,codex-windows-sandbox, orcodex-goal-extension. - Keep the external spawn/supervision/readiness/logging path. Resolve the
binary from an explicit path, saved configuration, then
PATH. Missing Codex must produce an installation/path error; never fall back to a bundled runtime. - Keep the existing bridge
embeddedfield/argument for compatibility: status reports false, explicit true starts fail before side effects, and legacy auto-host preferences restore through external Codex. Users who previously relied on embedded hosting must install Codex or select its path. - Do not build or package upstream Windows sandbox helpers. The installed external Codex owns its runtime, sandbox helpers, and version.
- Keep the standalone Responses API proxy functional: both HTTP POST and
WebSocket GET on
/v1/responsesmust forward requests, host authentication, streaming events, and upstream errors without requiring an embedded engine. Verify both transports when changing Codex dependencies or hosting. - Avoid parallel duplicate Rust builds and monitor disk space during local verification. Prefer reusing build caches; only remove reproducible build artifacts when cleanup is needed, never user data or source directories.
8.2 Updating the protocol submodule
deps/codex remains pinned to the pocket-codex branch of
git@github.com:acking-you/codex.git. Its historical host-integration shims
are not built into this application. Do not add new runtime shims for this
project. Continue to merge upstream into the shared fork branch rather than
rebasing published history, and record the verified submodule pointer here.
After a protocol bump:
- Inspect the protocol manifests and run
cargo metadata --format-version 1/cargo tree -p pocket_codex_bridgeto verify the dependency restriction above on all targets/features. - Mirror any still-required upstream WebSocket fork patches exactly and
regenerate
Cargo.lockwithout unrelated dependency upgrades. - Keep
sqlxcompatible with the protocol graph's transitivelibsqlite3-sysversion: Cargo permits only one native sqlite3 linker. - Keep the root Rust toolchain at or above the protocol crates' compiler floor and keep CI/release toolchains in sync.
- Run the full first-party verification in §7 and build the desktop UI.
Never run formatting or lint rewrites inside
deps/codexfor a routine application change.
9. Roadmap (rough)
The order below is our current best guess; it is not a contract.
-
CLI bootstrap (done).
pocket-codex version, configuration loading, basic logging, command-line schema + dispatcher. -
Codex process manager (done).
pocket-codex codex start|stop|statusspawning the user's localcodex app-server, persisting PID / listen URL metadata tostate.toml, surfacing logs. -
pb-mapper register / subscribe (done).
pocket-codex pb registerandpocket-codex pb subscribere-using the upstreamlocal::server::run_server_side_cli/local::client::run_client_side_clihelpers. -
Combined
serve/connectflow (done).pocket-codex servestarts or reuses the local app-server, registers it with a relay and tracks the daemonised pb-mapper worker instate.toml;pocket-codex connectsubscribes on the client side and prints the matchingcodex --remote ...command. -
Multi-device service selection + direct API proxy (done). Pocket-Codex service keys use
pcx:<device>:<service>:<name>; clients can discover services, set a local default target and choose app-server or direct Responses API proxy flows independently. -
Hosted account mode (done). Optional
pocket-codex-backend: GitHub login (device flow + web auth-code), SQLite-backed sessions, and/v1/relay, which vends each account a short-lived pb-mapper credential scoped to its ownpcxu:<user>:…namespace. Clients then register/connect against the relay directly — the backend holds the administrator key and stays off the data path. It previously brokered every byte on its own port; direct connect removed that hop, so backend availability is no longer a prerequisite for two of a user's own devices to talk. Self-host stays the escape hatch behind--relay. Deployment unit lives indeploy/. -
External Codex only (2026-09-13). All desktop hosts use an installed external
codex. The former embedded runtime is removed from builds and packaging; Built-in engine is a disabled, unimplemented placeholder. The bridge uses local wire envelopes: upstream protocol crates also pull in runtime support and are not linked into application artifacts (see §8.1). macOS GUI hosts resolve the login-shell PATH lazily in the Codex worker path; only Codex children receive it. Preserve inherited tool precedence, never mutate the process environment, and clean up the probe's owned process group. Native macOS host checks are part of the required CI result. -
App-server protocol sync (2026-09-24). Codex fork merged upstream main
c098f97e5atc08819510; CLI and UI share the acknowledged initialization handshake. Resume responses restore collaboration mode, opaquefileIdimage references keep a visible unavailable preview without host file reads, and paginated item timestamps remain distinct from whole-turn duration. Rust requires 1.95+. Flutter reads v2 accounts and current thread model/effort, and answers asynchronous questions withturn/steerwhile running orturn/startwhen idle. Strongly-typed JSON-RPC client (next). Replace theserde_json::Valuesurface inpocket-codex-codex::protocolwith local types verified against the upstream wire schema, without importing its runtime dependency graph. -
Mobile footprint and connection recovery (2026-09-25). Keep protocol envelopes local to avoid importing the upstream protocol crate's interpreter, database, and TLS dependencies. Resolve remote follow tunnels off the async worker; preserve failed monitoring refreshes for heartbeat/reconnect recovery. Open bounded 20-item tails, with older history available through normal pages.
-
Flutter UI evolution.
apps/flutterconsumes the bridge viaflutter_rust_bridge. P1 shipped: onboarding (relay+key,pcx1:import/export, persisted toconfig.toml0600), service discovery, API-service subscribe (local OpenAI-compatible endpoint), settings, responsive Material 3 (light/dark). P2 shipped: app-server remote control (threads, live event stream, approvals, attachments, per-thread config). P3 shipped: chat-first home —/resolves a host (last used → locally hosted → first reachable), auto-connects, and opens the latest session with all sessions in the sidebar; the services hub lives on at/manage; desktop auto-restores hosting on boot (ui_state.json). P4 adds structured Guardian review history, a searchable turn directory, bounded long-step browsing, and coordinated initial-load / continuation / navigation / monitoring feedback. The composer grows and shrinks with text, preserving the manually resized minimum height. Conversation drafts survive navigation during the app session, with sidebar draft/queue indicators, an expanded editor, explicit height reset, separate Queue/Stop controls, IME-safe desktop sending, and retryable attachments. New-session views focus on the project and input. Project headings reveal one new-conversation action on hover/keyboard focus, with right-click menus and touch long-press sheets. Unsent drafts are isolated by host and project; a server thread is created only on first send. Mobile messages offer long-press copy, text selection and native sharing. Editable conversations can reuse a user message in a separate editor; adding it preserves existing draft text and attachments and never sends automatically or rewrites history. Read-only views expose no edit action. -
Persistent controller history (2026-09-25). The App owns a configurable 512 MB disk cache shared across hosts, reusable after restart. Independent meta
/history/v1routes reconcile bounded windows and UTF-8 text prefixes through the user-requestedSessionHistorySourcetrait; Codex is the first production adapter. Show cached history before connection, keep stale state visibly read-only, and prefetch running tails only in the foreground. Seedocs/session-cache-sync.mdfor scope and limits. -
Controller turn modes (2026-09-25). New conversations default to
approvalsReviewer: auto_reviewwith on-request approval and the workspace sandbox. Retain manual, read-only and full-access presets; the existing persistedautotag keeps its manual-review meaning. Offer Fast only when the model catalog advertises support and explicitly senddefaultwhen switching it off. Running composer sends queue for a new turn by default; the optional Supplement control usesturn/steerwith an expected turn id and retains the draft on rejection. Queue draining preserves newer drafts. -
Native image results (2026-09-26). Plan mode lives under Advanced settings on desktop and mobile; explicit saved Plan sessions remain supported. Render native
imageGenerationitems as visible outputs with generation/loading, failure, preview and retry states. Recognize the exact rollout extension kindimage_gen.generation; only its artifact path authorizes a generated-image read. Refresh image references when monitoring another writer, keep loading state bounded by the confirmed active turn ID, and scope image bytes to host/thread. Bound inline results before FRB transport and render native images in the read-only viewer too. Seedocs/native-image-generation.md. -
Session file links (2026-09-26). Explicit Preview / Download actions resolve files against the selected host and session in live and read-only views. Local session viewers read this device directly; service-backed views require a shared-filesystem challenge or an in-process host before local reads. Remote previews are bounded; downloads stream to disposable controller files before native desktop/mobile saving. Preserve legacy Host Files downloads through root-confined
/fs/readonly on a missing new route. Keep automatic image authorization separate from explicit conversation-link reads. Remote localhost web links explain pb-mapper mapping and never open the controller's loopback address. Seedocs/session-file-links.md.
When you ship a milestone, update README.md (Status table) and
this file's roadmap so the source of truth stays in sync.
-
Relay recovery and maintenance (2026-10-03). Network workers retain pending registrations/subscriptions through offline startup. Runtime diagnostics report the SDK actually loaded by each worker; an older worker reports unknown.
pb restart --role <register|subscribe> --key <exact-key>verifies process identity and credentials before replacing that worker, preserving local Codex, API proxy and host supervisor processes. Seedocs/relay-worker-recovery.md. -
Low-latency relay and protocol sync (2026-10-04). SDK 0.6.0 uses a shared per-leg data key with disjoint directional nonce prefixes, without data-key derivation, extra setup round trips or frame bytes.
deps/codexmerges the freshly pulled upstream mainafb436df8batbeaefbdf0(0.2.7 corrects the stale local pin in 0.2.6). Aggregated command output, persisted truncation markers and future error variants retain their existing display behavior; interrupted turns may carry an explicit error. Native app-server tracking excludes Linux thread IDs and recognizes replaced executables; worker identity survives boot-time clock corrections. Seedocs/app-server-protocol-sync.md. -
Cached history and touch feedback (2026-10-04). Local session lists explain mobile filesystem unavailability, empty desktop history and retryable scan failures. Opening restores retained sequential and selected-turn windows locally within the memory budget; preserve real gaps and source generations. Streaming writes a separate 100-item checkpoint, never the full display view. Explicit history loads reveal and briefly highlight new rows; scroll loads preserve the reading anchor and short-page edge gestures can load history. Compact navigation is draggable and reads unloaded turns from the full directory. Mobile message press feedback remains until menu dismissal and respects reduced motion. See
docs/session-cache-sync.md.
UI and history maintenance (2026-09-13)
- Keep shared colors, typography, and control shapes in
theme.dart/desktop_theme.dart; use the sharedUtilityPageshell for secondary pages. Compact layouts need reachable touch targets and readable text, not a scaled desktop screenshot. Validate light/dark at phone, tablet, and desktop widths. - History cursors and idle snapshots belong to the bridge session. Cache at most eight thread snapshots / approximately 32 MiB, validate metadata before reuse, and invalidate in-memory snapshots on live changes or reconnect. Persistent controller windows use the separate configurable disk quota and survive reconnect; send manifests only for bytes still retained. Source replacement invalidates disk windows and queued checkpoints. Never turn a failed request into an authoritative end-of-history result.
- Timeline jumps create independent ascending turn windows. Show missing history between those windows at its actual position; a tail cursor does not mean there is more history above a fully loaded first turn. Use the exhausted turn-summary cursor to confirm the oldest boundary. Hovering the timeline must not trigger network reads; explicit selection and approaching a gap load one bounded page, with cached pages and in-flight requests reused. Coalesce adjacent missing ranges into one action without losing the next cursor. A monitoring refresh must not resurrect an exhausted window.
- Continuation across FRB uses
delta_only=true: send only newly read items, merge by item ID, and return an empty exhausted page without another RPC. Opening a turn still returns its cached prefix. Monitoring reads useinclude_turn_pages=false; do not retransmit selected windows already held by Flutter. Preserve the cumulative/default response for older callers. - Initial history uses a delayed static loading label, without mock conversation shimmer or overlapping outgoing/incoming transcripts. Supplementary loads keep the existing content visible. Show feedback only at the requested gap, top boundary, or selected turn; cancellation and new scroll intent must win over late anchor restoration. Background tail updates must remain quiet.
- Limit minimap tick density by available height. Keep the exact active marker and all turns reachable through a lazy searchable directory and keyboard. Long work lists use a bounded lazy viewport with explicit step ranges; never animate the height of hundreds of steps. Appends must preserve the reader's selected range, and a manually expanded group must stay open when it finishes.
- Guardian review history is display-only: recognize the explicit request envelope and the complete assessment schema, show outcome/risk/authorization/ rationale, and retain exact original copy plus lazy detail expansion. Never turn a historical model assessment into an interactive approval or execute command text extracted for presentation. Unknown formats keep normal rendering.
- Keep the composer compact by default, resizable with mouse/touch and accessibility actions, and persist its minimum height with the UI preferences. Grow and shrink with wrapped text up to the viewport cap, then scroll internally; automatic sizing must not overwrite the saved minimum. Keep draft text, selection, attachments, and pending queues scoped to the host and conversation (host and project before first send) for the app session; a queue resumes when its conversation is active. An upload completing after navigation must update its original draft. Failed attachments remain removable/retryable and block sending until resolved. Mobile Return inserts a newline; desktop Enter sends and Shift+Enter inserts a newline, while IME composition must never submit. The expanded editor shares the draft and returns focus and selection to the compact input.
- Branding uses the same blue/neutral palette as the UI. When changing it, update the brand masters and regenerate launcher, tray, and splash assets for every platform; keep both README posters and logo copies in sync.
- Transcript rows must remain lazy and keyed. Preserve the visible message when prepending history, and do not rescan the rest of a turn per item when grouping rows. Pagination failures require an explicit retry instead of a scroll-triggered request loop. Steering messages can split one physical turn into several work groups: show its total duration once, and animate only its current active group. Missing duration is not evidence of a running turn.
- Render the initial history before optional saved configuration and model discovery finish. Late metadata must not overwrite a user's new selection. Timeline selection jumps directly to the selected turn; only the latest selection may move the viewport, and prepend anchoring must not fight it.
- Connect to an in-app host through its registered loopback endpoint. Do not relay a device's own traffic or perform relay health probes to resolve that endpoint. Keep remote relay connections as a first-class path.
- Monitor other writers with
follow?metadata_only=true: emit liveness and an opaque rollout revision, then coalesce bounded app-server history refreshes. Preserve loaded prefixes and reading position. Keep the legacy full-snapshot endpoint compatible, but never use it for a new client's normal monitoring. Cache lifecycle scan positions for append-only rollouts so polling does not repeatedly scan hundreds of MiB; invalidate on replacement or truncation. - Discover running sessions in the background via
/sessions?running_only=true. Probe ownership in a batch and read lifecycle data only for held files. Polls must not overlap; preserve status across transient failures and give newer app-server events precedence over an older snapshot. Unopened sessions must enter and leave the Active section automatically on desktop and mobile. - Negotiate Zstd/Gzip for host HTTP responses and permessage-deflate for app-server WebSockets. Compression is optional and requires peer support; keep uncompressed peers compatible and never buffer SSE just to compress it. Verify large payload round trips and actual peer negotiation separately.
- Reproduce long-history issues with the opt-in native integration test:
fvm flutter test integration_test/local_history_performance_test.dart -d macos --dart-define=PCX_LIVE_UI=true --dart-define=PCX_HISTORY_THREAD_IDS=<id1>,<id2>. It reads existing sessions on the configured host, records open/navigation times and RSS, and switches repeatedly without sending turns or taking over. - Reference: T3 Code's virtualized timeline and scroll anchoring and pagination state tests.
10. Communication conventions
-
Reply in whatever language the person you are talking to is using. This file does not mandate one.
-
Lead with findings before summaries.
-
Cite files as
path:line. -
State assumptions explicitly. If an assumption could change the design or risk breakage/data loss, stop and ask.
-
Further reference: T3 Code's bounded work list and long-message folding preserves scroll anchors and virtualizes expanded activity. Our adaptive tick sampling and step-range controls are Pocket-Codex adaptations, not claimed upstream behavior. Guardian format is defined by
deps/codex/codex-rs/core/src/guardian/prompt.rs.
