Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
PMH_DISCOVERY_PROVIDER=codex
PMH_CODEX_MODEL=gpt-5.6-terra
PMH_CODEX_REASONING_EFFORT=high
# Optional: how heuristic discovery reaches Codex with the ChatGPT OAuth cache.
# app-server (default) drives a local `codex app-server` process, the client the
# subscription accepts. responses calls chatgpt.com directly and is currently
# rejected (HTTP 403) for subscription OAuth; keep it only as an explicit opt-in.
PMH_CODEX_DISCOVERY_TRANSPORT=app-server

# Optional DeepSeek credential. Keep the value out of Git. A credential does
# not authorize background spend; automation remains a separate setting.
Expand Down
14 changes: 12 additions & 2 deletions PLANS.md
Original file line number Diff line number Diff line change
Expand Up @@ -984,8 +984,18 @@ one hook, expose the same preflight control, and disable every scan launch while
blocked; Agent Operations retains the full execution console rather than making
ordinary scan pages download it. API-key profiles also require a fresh
configuration-only preflight, so configured-but-missing secrets cannot bypass
the synchronous dispatcher. An app-server-backed Codex runtime is the next
usable-route candidate. Other legacy AI workloads remain later adoption work.
the synchronous dispatcher. The app-server-backed Codex runtime is now the
default `DISCOVERY_SCOUT` transport (2026-08-24): a fresh operator desk showed
the in-process Responses route rejected by the `CODEX_USAGE` probe (HTTP 403)
while every app-server route was `USABLE`, so `CodexAppServerAgentPort` drives
the same `DiscoveryAgentSession` tool loop through `codex app-server` dynamic
tools, the legacy scout route binds the shared Codex runtime definition and
its account preflight, and the direct Responses transport remains an explicit
`PMH_CODEX_DISCOVERY_TRANSPORT=responses` opt-in. The same desk also showed
Terra/high mechanism research stalling silently past the 300 s run budget
twice (invocation 3, zero events) while Terra/medium completed in 2 m 42 s, so
effort is an operator lever, not a correctness guarantee. Other legacy AI
workloads remain later adoption work.
Cold-start use then exposed a separate operability failure: the listener was
reachable while Studio waited 8–25 seconds with a generic Connecting screen.
The control plane now exposes a provider-free `/api/v1/readiness` envelope with
Expand Down
15 changes: 14 additions & 1 deletion docs/OPERATIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ environment only seeds a new database.
Default configuration:

- provider: `CODEX`
- runtime: Codex app-server for the selected long-loop workloads
- runtime: Codex app-server for the selected long-loop workloads and for
heuristic discovery (`DISCOVERY_SCOUT`)
- credential: local Codex OAuth cache
- model: `gpt-5.6-terra`
- reasoning effort: `high`
Expand All @@ -111,12 +112,24 @@ or Terra plus a model-supported reasoning effort. Runtime, credential binding,
model profile, and workload route are represented separately; the model's
reasoning effort remains part of that model profile.

Heuristic discovery reaches Codex through `PMH_CODEX_DISCOVERY_TRANSPORT`.
`app-server` (default) drives a local `codex app-server --stdio` process, the
first-party client that the ChatGPT subscription accepts, and the
`DISCOVERY_SCOUT` route then shares the Codex runtime's zero-inference account
preflight. `responses` calls `chatgpt.com/backend-api/codex/responses` directly
with a third-party originator; the service currently rejects that route for
subscription OAuth (HTTP 403), so it stays an explicit opt-in for API-style
credentials. The setting is process-level, not part of the durable Studio
configuration, and Readiness reports the active transport as
`CODEX_APP_SERVER` or `VERCEL_AI_SDK`.

To seed a new store explicitly:

```dotenv
PMH_DISCOVERY_PROVIDER=codex
PMH_CODEX_MODEL=gpt-5.6-terra
PMH_CODEX_REASONING_EFFORT=high
PMH_CODEX_DISCOVERY_TRANSPORT=app-server
PMH_DEEPSEEK_AUTOMATION_ENABLED=0
```

Expand Down
33 changes: 26 additions & 7 deletions packages/control-plane/src/agent-execution-substrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2327,14 +2327,27 @@ export type LegacyAiConfigurationImport = Readonly<{
modelInvocationsCreated: 0;
}>;

/**
* The Codex app-server runtime definition shared by the operator portfolio and
* the legacy discovery route, so both resolve to one runtime identity and one
* zero-inference account preflight.
*/
export const CODEX_APP_SERVER_RUNTIME_VERSION = "codex-app-server-v2:0.147";

export type LegacyDiscoveryTransport = "APP_SERVER" | "RESPONSES";

export function importLegacyAiRuntimeConfiguration(
configuration: AiRuntimeConfiguration,
options: Readonly<{ discoveryTransport: LegacyDiscoveryTransport }>,
): LegacyAiConfigurationImport {
const runtimeDefinition = buildAgentRuntimeDefinition({
kind: "HARNESS_IN_PROCESS",
version: "ai-sdk-loop-v1",
});
const codex = configuration.provider === "CODEX";
// Every caller states the transport the discovery workers really use; there
// is deliberately no default, because the system default (app-server) and
// the historical composition (in-process) are different compositions.
const appServer = codex && options.discoveryTransport === "APP_SERVER";
const runtimeDefinition = buildAgentRuntimeDefinition(appServer
? { kind: "CODEX", version: CODEX_APP_SERVER_RUNTIME_VERSION }
: { kind: "HARNESS_IN_PROCESS", version: "ai-sdk-loop-v1" });
const credentialBinding = buildCredentialBinding(codex
? {
kind: "CODEX_OAUTH",
Expand Down Expand Up @@ -2373,8 +2386,13 @@ export function importLegacyAiRuntimeConfiguration(
}),
createdAt: configuration.updatedAt,
});
// The durable store keys profiles and routes by (key, revision), so the
// app-server binding must carry its own keys: one configuration revision may
// legitimately retain both the in-process and the app-server composition.
const executionProfile = buildExecutionProfile({
profileKey: "legacy-discovery-execution",
profileKey: appServer
? "legacy-discovery-app-server-execution"
: "legacy-discovery-execution",
revision: configuration.revision,
runtimeDefinition,
credentialBinding,
Expand All @@ -2390,7 +2408,7 @@ export function importLegacyAiRuntimeConfiguration(
createdAt: configuration.updatedAt,
});
const workloadRoute = buildWorkloadRoute({
routeKey: "legacy-discovery-default",
routeKey: appServer ? "legacy-discovery-app-server" : "legacy-discovery-default",
revision: configuration.revision,
taskKind: "DISCOVERY_SCOUT",
executionProfileId: executionProfile.executionProfileId,
Expand Down Expand Up @@ -2418,8 +2436,9 @@ export class AgentExecutionRegistry {

public importLegacyConfiguration(
configuration: AiRuntimeConfiguration,
options: Readonly<{ discoveryTransport: LegacyDiscoveryTransport }>,
): LegacyAiConfigurationImport {
const imported = importLegacyAiRuntimeConfiguration(configuration);
const imported = importLegacyAiRuntimeConfiguration(configuration, options);
const batch: AgentExecutionBatch = {
runtimeDefinitions: [imported.runtimeDefinition],
credentialBindings: [imported.credentialBinding],
Expand Down
3 changes: 2 additions & 1 deletion packages/control-plane/src/agent-runtime-portfolio.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
buildExecutionProfile,
buildModelProfile,
buildWorkloadRoute,
CODEX_APP_SERVER_RUNTIME_VERSION,
type AgentExecutionBatch,
} from "./agent-execution-substrate.js";
import type { AiRuntimeConfiguration } from "./ai-runtime-configuration.js";
Expand All @@ -24,7 +25,7 @@ export function buildDefaultAgentRuntimePortfolio(
const pi = buildAgentRuntimeDefinition({ kind: "PI", version: "pi-cli-v1" });
const codex = buildAgentRuntimeDefinition({
kind: "CODEX",
version: "codex-app-server-v2:0.147",
version: CODEX_APP_SERVER_RUNTIME_VERSION,
});
const inProcess = buildAgentRuntimeDefinition({
kind: "HARNESS_IN_PROCESS",
Expand Down
Loading