Install Android runtime guidance into Codex homes

Install a generated AGENTS.md in the shared Agent Codex home and every per-session Genie home so hosted runtimes pick up the same device-operating guidance at bootstrap.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Iliyan Malchev
2026-03-21 08:59:45 -07:00
parent 7e30a81550
commit 4086f04ddb
6 changed files with 129 additions and 4 deletions

View File

@@ -8,6 +8,7 @@ import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.openai.codex.bridge.HostedCodexConfig
import java.io.File
import java.io.InterruptedIOException
import java.io.IOException
@@ -67,7 +68,7 @@ class CodexdForegroundService : Service() {
val socketPath = intent.getStringExtra(EXTRA_SOCKET_PATH) ?: defaultSocketPath()
val codexHome = intent.getStringExtra(EXTRA_CODEX_HOME) ?: defaultCodexHome()
File(codexHome).mkdirs()
HostedCodexConfig.installAgentsFile(File(codexHome))
val codexdBinary = resolveCodexdBinary()
val args = mutableListOf(

View File

@@ -33,3 +33,7 @@ android {
targetCompatibility = androidJavaVersion
}
}
dependencies {
testImplementation("junit:junit:4.13.2")
}

View File

@@ -7,13 +7,13 @@ import java.nio.file.Files;
public final class HostedCodexConfig {
public static final String ANDROID_HTTP_PROVIDER_ID = "android-openai-http";
private static final String AGENTS_FILENAME = "AGENTS.md";
private HostedCodexConfig() {}
public static void write(File codexHome, String baseUrl) throws IOException {
if (!codexHome.isDirectory() && !codexHome.mkdirs()) {
throw new IOException("failed to create codex home at " + codexHome.getAbsolutePath());
}
ensureCodexHome(codexHome);
installAgentsFile(codexHome);
String escapedBaseUrl = baseUrl
.replace("\\", "\\\\")
@@ -29,4 +29,67 @@ public final class HostedCodexConfig {
new File(codexHome, "config.toml").toPath(),
configToml.getBytes(StandardCharsets.UTF_8));
}
public static void installAgentsFile(File codexHome) throws IOException {
ensureCodexHome(codexHome);
Files.write(
new File(codexHome, AGENTS_FILENAME).toPath(),
buildAgentsMarkdown().getBytes(StandardCharsets.UTF_8));
}
static String buildAgentsMarkdown() {
return """
# Android Agent/Genie Runtime Notes
This Codex runtime is operating on an Android device through the Agent Platform.
## If you are the Agent
- The user interacts only with the Agent.
- Plan the work, choose the target package or packages, and start one Genie session per target app that needs to be driven.
- Delegate objectives, not tool choices. Tell each Genie what outcome it must achieve in its paired app and let the Genie choose its own tools.
- Answer Genie questions directly when you can. If the answer depends on user intent or missing constraints, ask the user.
- Keep auth, upstream access, and any internet-facing model traffic on the Agent side.
## If you are a Genie
- You are paired with exactly one target app sandbox for this session.
- Solve the delegated objective inside that sandbox by using the normal Codex tool path and the Android tools that are available on-device.
- Ask the Agent a concise free-form question only when you are blocked on missing intent, missing constraints, or a framework-owned action.
- Do not assume you can reach the internet directly. Model and auth traffic are Agent-owned.
- Do not rely on direct cross-app `bindService(...)` or raw local sockets to reach the Agent. Use the framework-managed session bridge.
## Shell and device tooling
- Prefer standard Android shell tools first: `cmd`, `am`, `pm`, `input`, `uiautomator`, `dumpsys`, `wm`, `settings`, `content`, `logcat`.
- Do not assume desktop/Linux extras such as `python3`, GNU `date -d`, or other non-stock userland tools are present.
- When a command affects app launch or user-visible state, prefer an explicit `--user 0` when the tool supports it.
- Keep temporary artifacts in app-private storage such as the current app `files/` or `cache/` directories, or under `$CODEX_HOME`. Do not rely on shared storage.
## UI inspection and files
- In self-target Genie mode, prefer `uiautomator dump /proc/self/fd/1` or `uiautomator dump /dev/stdout` when stdout capture is acceptable.
- Plain `uiautomator dump` writes to the app-private dump directory.
- Explicit shared-storage targets such as `/sdcard/...` are redirected back into app-private storage in self-target mode.
- Do not assume `/sdcard` or `/data/local/tmp` are readable or writable from the paired app sandbox.
## Presentation semantics
- Detached launch, shown-detached, and attached are different states.
- `targetDetached=true` means the target is still detached even if it is visible in a detached or mirrored presentation.
- If the task says the app should be visible to the user, do not claim success until the target is attached unless the task explicitly allows detached presentation.
- Treat framework session state as the source of truth for presentation state.
## Working style
- Prefer solving tasks with normal shell/tool use before reverse-engineering APK contents.
- When you need to ask a question, make it specific and short so the Agent can either answer directly or escalate it to the user.
""";
}
private static void ensureCodexHome(File codexHome) throws IOException {
if (!codexHome.isDirectory() && !codexHome.mkdirs()) {
throw new IOException("failed to create codex home at " + codexHome.getAbsolutePath());
}
}
}

View File

@@ -0,0 +1,52 @@
package com.openai.codex.bridge;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import org.junit.Test;
public final class HostedCodexConfigTest {
@Test
public void writeInstallsConfigAndAgentsFile() throws Exception {
File codexHome = Files.createTempDirectory("hosted-codex-home").toFile();
HostedCodexConfig.write(codexHome, "http://127.0.0.1:8080");
String configToml =
new String(
Files.readAllBytes(new File(codexHome, "config.toml").toPath()),
StandardCharsets.UTF_8);
String agentsMarkdown =
new String(
Files.readAllBytes(new File(codexHome, "AGENTS.md").toPath()),
StandardCharsets.UTF_8);
assertTrue(configToml.contains("model_provider = \"android-openai-http\""));
assertTrue(configToml.contains("base_url = \"http://127.0.0.1:8080\""));
assertEquals(HostedCodexConfig.buildAgentsMarkdown(), agentsMarkdown);
}
@Test
public void installAgentsFileWritesExpectedGuidance() throws Exception {
File codexHome = Files.createTempDirectory("hosted-codex-agents").toFile();
HostedCodexConfig.installAgentsFile(codexHome);
String agentsMarkdown =
new String(
Files.readAllBytes(new File(codexHome, "AGENTS.md").toPath()),
StandardCharsets.UTF_8);
assertEquals(HostedCodexConfig.buildAgentsMarkdown(), agentsMarkdown);
assertTrue(agentsMarkdown.contains("The user interacts only with the Agent."));
assertTrue(
agentsMarkdown.contains(
"Do not rely on direct cross-app `bindService(...)` or raw local sockets"));
assertTrue(
agentsMarkdown.contains(
"If the task says the app should be visible to the user, do not claim success until the target is attached"));
}
}

View File

@@ -5,6 +5,7 @@ import android.app.agent.GenieRequest
import android.app.agent.GenieService
import android.content.Context
import android.util.Log
import com.openai.codex.bridge.HostedCodexConfig
import java.io.BufferedWriter
import java.io.Closeable
import java.io.File
@@ -78,6 +79,7 @@ class CodexAppServerHost(
deleteRecursively()
mkdirs()
}
HostedCodexConfig.installAgentsFile(codexHome)
val proxy = GenieLocalCodexProxy(
sessionId = request.sessionId,
socketDirectory = context.cacheDir,

View File

@@ -28,6 +28,9 @@ The current repo now contains these implementation slices:
- The current session bridge exposes small fixed-form calls, and the Genie
runtime already uses it to fetch Agent-owned runtime metadata from the
hosted Agent Codex runtime, including auth status and configured model/provider.
- The Android host layer now installs a generated `AGENTS.md` into the Agent
Codex home and every per-session Genie Codex home so the hosted runtimes get
the same device-operating guidance at startup.
- Target-package planning now relies on the hosted Agent Codex runtime using
standard Android shell tools already available on-device (`cmd package`, `pm`,
`am`) instead of Kotlin-side app discovery wrappers.