Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2cc2319
docs: add Factory Droid bridge guide
heomin86 Aug 14, 2026
4d3a776
feat(usage): record streamAborted on mid-stream-death attempts
kartikkabadi Aug 14, 2026
0889973
feat(catalog): add modelPickerOrder to customize the Codex model-pick…
TooSpace Aug 14, 2026
1b68c4c
feat(combos): add imageInput auto|disabled capability control
eachann1024 Aug 7, 2026
23db3b6
fix(combos): address imageInput review on sparse API and guards
eachann1024 Aug 7, 2026
e95af38
fix(combos): scan image input only on Responses input shapes
eachann1024 Aug 10, 2026
1c84515
add MiniMax client integrations
Hylouis233 Aug 13, 2026
3eaddbf
fix(cli): harden MiniMax wrapper isolation
Hylouis233 Aug 15, 2026
f6d841b
feat(terminal-guard): opt-in no-tool-call continuation guard for open…
TooSpace Aug 14, 2026
3d6b614
test(terminal-guard): drive terminalContinuationGuard through the cor…
TooSpace Aug 14, 2026
3e8dae8
fix(usage): cover eager abort and cancellation races
lidge-jun Aug 15, 2026
949ccd9
fix(catalog): tolerate malformed model picker order
lidge-jun Aug 15, 2026
b7710d9
docs: repair Factory Droid bridge contract
lidge-jun Aug 15, 2026
9bf2b38
fix(combos): preserve image input controls
lidge-jun Aug 15, 2026
1da33da
test(terminal-guard): cover opt-in boundaries
lidge-jun Aug 15, 2026
9256f6d
fix(runtime): make self-launch argv standalone-aware
lidge-jun Aug 15, 2026
88a9612
Merge repair/260815-pr-1664 (PR #1664 cherry-pick+repair)
lidge-jun Aug 15, 2026
571521a
Merge repair/260815-pr-1669 (PR #1669 cherry-pick+repair)
lidge-jun Aug 15, 2026
a96de58
Merge repair/260815-pr-1644 (PR #1644 cherry-pick+repair)
lidge-jun Aug 15, 2026
1d8e909
Merge repair/260815-pr-1652 (PR #1652 cherry-pick+repair)
lidge-jun Aug 15, 2026
c0ee51e
Merge repair/260815-pr-1660 (PR #1660 cherry-pick+repair)
lidge-jun Aug 15, 2026
1bc7334
Merge repair/260815-pr-1165 (PR #1165 cherry-pick+repair)
lidge-jun Aug 15, 2026
2f276eb
fix(update): self-launch workers in standalone builds
lidge-jun Aug 15, 2026
b05c494
Merge repair/260815-pr-1664 follow-up (standalone update workers)
lidge-jun Aug 15, 2026
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
2 changes: 2 additions & 0 deletions docs-site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export default defineConfig({
translations: { ko: "가이드", "zh-CN": "指南", "zh-TW": "指南", ru: "Руководства", ja: "ガイド", tr: "Kılavuzlar" },
items: [
{ label: "Providers", translations: { ko: "프로바이더", "zh-CN": "提供商", "zh-TW": "供應商", ru: "Провайдеры", ja: "プロバイダー", tr: "Sağlayıcılar" }, slug: "guides/providers" },
{ label: "Factory Droid Bridge", translations: { ko: "Factory Droid 브리지" }, slug: "guides/factory-droid" },
{ label: "Model Routing", translations: { ko: "모델 라우팅", "zh-CN": "模型路由", "zh-TW": "模型路由", ru: "Маршрутизация моделей", ja: "モデルルーティング", tr: "Model Yönlendirme" }, slug: "guides/model-routing" },
{ label: "Codex Integration", translations: { ko: "Codex 통합", "zh-CN": "Codex 集成", "zh-TW": "Codex 整合", ru: "Интеграция с Codex", ja: "Codex 連携", tr: "Codex Entegrasyonu" }, slug: "guides/codex-integration" },
{ label: "Codex App Model Picker", translations: { ko: "Codex App 모델 선택기", "zh-CN": "Codex App 模型选择器", "zh-TW": "Codex App 模型選擇器", ru: "Выбор модели в Codex App", ja: "Codex App モデルピッカー", tr: "Codex App Model Seçici" }, slug: "guides/codex-app-models" },
Expand All @@ -95,6 +96,7 @@ export default defineConfig({
{ label: "opencode", translations: { ko: "opencode", "zh-CN": "opencode", "zh-TW": "opencode", ru: "opencode", ja: "opencode", tr: "opencode" }, slug: "guides/opencode" },
{ label: "Pi", translations: { ko: "Pi", "zh-CN": "Pi", "zh-TW": "Pi", ru: "Pi", ja: "Pi", tr: "Pi" }, slug: "guides/pi" },
{ label: "Integrations", translations: { ko: "연동", "zh-CN": "集成", "zh-TW": "整合", ru: "Интеграции", ja: "連携", tr: "Entegrasyonlar" }, slug: "guides/integrations" },
{ label: "MiniMax clients", translations: { ko: "MiniMax 클라이언트", "zh-CN": "MiniMax 客户端", "zh-TW": "MiniMax 客戶端", ru: "Клиенты MiniMax", ja: "MiniMax クライアント", tr: "MiniMax İstemcileri" }, slug: "guides/minimax" },
{ label: "Sidecars: Web Search & Vision", translations: { ko: "사이드카: 웹 검색 & 비전", "zh-CN": "边车:网络搜索与视觉", "zh-TW": "邊車:網路搜尋與視覺", ru: "Сайдкары: веб-поиск и зрение", ja: "サイドカー: ウェブ検索 & ビジョン", tr: "Sidecar'lar: Web Arama ve Görme" }, slug: "guides/sidecars" },
{ label: "Image Bridge", translations: { ko: "이미지 브릿지", "zh-CN": "图像桥接", "zh-TW": "圖像橋接", ru: "Image Bridge", ja: "画像ブリッジ", tr: "Image Bridge" }, slug: "guides/image-bridge" },
{ label: "Video Bridge", translations: { ko: "비디오 브릿지", "zh-CN": "视频桥接", "zh-TW": "影片橋接", ru: "Video Bridge", ja: "動画ブリッジ", tr: "Video Bridge" }, slug: "guides/video-bridge" },
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions docs-site/src/content/docs/guides/combos.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,14 @@ default and leaves the target's own behavior unchanged. Supported values are `lo
`high`, `xhigh`, `max`, and `ultra`; omit the field or set it to `null` to leave effort entirely to
the caller and target.

## Image / multimodal capability

By default a combo publishes the **intersection** of its targets' input modalities (image is
enabled only when every target advertises it). Set `imageInput: "disabled"` to force text-only
even when every target supports images — the catalog drops `image` from `inputModalities`, and
image-bearing requests are rejected with HTTP 400 before any target is called. `"auto"` (or
omitting the field) keeps the automatic intersection.

## Encrypted v2 sub-agent tasks

There is one important limitation for Codex v2 sub-agents ([issue #92](https://github.com/lidge-jun/opencodex/issues/92)).
Expand Down Expand Up @@ -304,6 +312,7 @@ Combos are stored in the top-level `combos` object, keyed by combo id:
| `strategy` | No | `"failover"` | `"failover"` or `"round-robin"`. |
| `stickyLimit` | No | `1` | Integer from 1 to 100 successful requests per round-robin selection. |
| `defaultEffort` | No | `null` | `low`, `medium`, `high`, `xhigh`, `max`, or `ultra`; applied only when the caller omits effort and the target advertises support. |
| `imageInput` | No | `"auto"` | `"auto"` or `"disabled"`. `"auto"` publishes image support only when every target supports images; `"disabled"` forces text-only (drops image from published modalities and rejects image-bearing requests before dispatch). |
| `alias` | No | none | Optional trimmed public model id; use the alias rules above. An empty value is stored as no alias. |
| `nativeAlias` | No | `false` | Explicitly permit a currently supported bare native `alias` to take routing and catalog precedence. Never inferred from the alias. |
| `displayName` | No | none | Bounded display-only catalog label. Required and non-empty when `nativeAlias` is true. |
Expand Down
177 changes: 177 additions & 0 deletions docs-site/src/content/docs/guides/factory-droid.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
---
title: Factory Droid bridge
description: Connect Factory Droid models to opencodex through a local Responses-compatible bridge.
---

Factory Droid is an agent runtime, not a documented OpenAI-compatible inference endpoint. If a
custom provider pointed at an internal Factory LLM URL returns `403 Forbidden`, changing only the
opencodex adapter or adding provider headers does not make that private route a supported public API.

The working integration is:

```text
Text-only Responses client
-> opencodex (http://127.0.0.1:10100/v1/responses)
-> local Responses bridge (http://127.0.0.1:11435/v1/responses)
-> official droid exec command
-> Factory account and selected model
```

This keeps the Factory credential inside the official Droid client. OpenCodex receives a separate,
local-only bridge token.

## What failed and why

| Symptom | Cause | Fix |
| --- | --- | --- |
| `403 Forbidden` from a Factory LLM URL | The URL is not a documented general-purpose OpenAI endpoint for third-party clients | Invoke Factory through the official Droid CLI or SDK |
| `404` at `/models/models` | The provider base URL already ended in `/models` | Use an API root as `baseUrl`; never include the discovery path |
| Model search fails | The bridge does not expose a complete live catalog | Set `liveModels: false` and provide a static `models` list |
| Loopback provider is rejected | Private-network access is denied by default | Set `allowPrivateNetwork: true` only for the loopback bridge |
| `${DROID_BRIDGE_TOKEN}` is unresolved | The variable is missing from the opencodex service environment | Inject it into the service process, not only an interactive shell |
| `OutputTextDelta without active item` | The bridge emitted a text delta before opening an output item and content part | Emit the complete Responses SSE lifecycle in order |

The same Factory credential can therefore work in `droid exec` while a direct request to an
undocumented LLM URL still returns `403`. Those results test different products and should not be
treated as contradictory.

## Prerequisites

1. Install and sign in to the [Droid CLI](https://docs.factory.ai/droid-cli/quickstart).
2. Confirm a bounded headless request works:

```bash
droid exec --model glm-5.2 --output-format json "Reply with DROID_OK only."
```

3. Run a local bridge that invokes `droid exec` (or the official Droid SDK) and exposes:

- `GET /healthz`
- `GET /v1/models`
- `POST /v1/responses`

Factory documents `droid exec` as its non-interactive automation surface and recommends JSON output
for scripts. For a longer-lived integration, Factory also documents stream JSON-RPC and official
TypeScript and Python SDKs in the
[Droid Exec guide](https://docs.factory.ai/droid-exec/overview).

## Bridge contract

Bind the bridge to `127.0.0.1`, require a randomly generated bearer token, cap request sizes, and
allowlist model IDs. The minimal bridge accepts only these Responses `input` shapes:

- a non-empty string; or
- an array containing only `message` items. Each message must have a `user`, `developer`, `system`,
or `assistant` role and either string content or text-only content parts (`input_text` for input
roles and `output_text` for assistant history).

Validate the complete request before invoking Droid. If an input part is an image or file, `tools`
contains any tool definition, or `input` contains a tool call or result (`function_call`,
`function_call_output`, `custom_tool_call`, or `custom_tool_call_output`), return HTTP `400` with a
Responses-style `invalid_request_error`. Use a stable bridge-specific code such as
`unsupported_bridge_input` and identify the rejected field in the message. Do this before starting
SSE, even when `stream: true`; never discard, stringify, or flatten unsupported content into the
prompt.

```json
{
"error": {
"type": "invalid_request_error",
"code": "unsupported_bridge_input",
"param": "tools",
"message": "The minimal Droid bridge does not accept tool definitions."
}
}
```

For an accepted request, the bridge should:

1. Convert the accepted Responses `input` to a prompt.
2. invoke `droid exec --model <id> --output-format json <prompt>`;
3. parse the final `result` and `session_id`;
4. return an OpenAI Responses envelope; and
5. map `previous_response_id` to the Droid session ID when continuation is required.

For streaming responses, emit this lifecycle in order:

```text
response.created
response.output_item.added
response.content_part.added
response.output_text.delta
response.output_text.done
response.content_part.done
response.output_item.done
response.completed
```

Do not expose the bridge on `0.0.0.0` and do not reuse the Factory credential as the bridge bearer
token.

## OpenCodex provider configuration

Create the custom provider with the explicit provider ID `droid`:

```bash
ocx provider add droid \
--adapter openai-responses \
--base-url http://127.0.0.1:11435/v1 \
--default-model glm-5.2 \
--allow-private-network
```

This creates the `providers.droid` config entry. In the dashboard, open **Providers → droid → Edit
JSON** and replace that provider's value with:

```json
{
"adapter": "openai-responses",
"baseUrl": "http://127.0.0.1:11435/v1",
"responsesPath": "/responses",
"allowPrivateNetwork": true,
"authMode": "key",
"apiKey": "${DROID_BRIDGE_TOKEN}",
"liveModels": false,
"models": ["glm-5.2", "glm-5.2-fast", "kimi-k3"],
"defaultModel": "glm-5.2"
}
```

The model IDs are examples. Keep only models that `droid exec` can use for the signed-in Factory
account. Do not add Factory-specific inference headers to this provider: its upstream is the local
bridge, not a Factory HTTP endpoint.

After saving a provider or changing its static catalog, synchronize and restart the Codex
app-server so new sessions read the updated catalog:

```bash
ocx sync --restart-codex
ocx doctor
```

Restarting Codex app-server processes interrupts active Codex work. Run the restart only after
finishing or saving those sessions.

## Verify the complete route

Check each boundary separately:

```bash
curl -fsS http://127.0.0.1:11435/healthz
ocx doctor
ocx access test droid/glm-5.2 --protocol responses
```

A provider row or model-picker entry proves only catalog visibility. The integration is working only
after the Responses probe returns through the `droid/<model>` route.

## Current limitation

The minimal bridge above translates text and the Responses SSE lifecycle. It does **not** implement
the full bidirectional Codex function/tool-call protocol. Codex App and `codex exec` normally send
tool definitions even when a prompt says not to call tools, and the current Codex CLI has no general
flag that removes those definitions. The minimal bridge must reject those requests with the `400`
contract above. Tool definitions, tool calls, tool results, permissions, cancellation, and rich
Droid events require a stateful bridge built on Factory's stream JSON-RPC mode or an official Droid
SDK. Treat `ocx access test` success as text-path verification, not Codex agent or tool-path
verification.
42 changes: 36 additions & 6 deletions docs-site/src/content/docs/guides/integrations.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
title: Integrations
description: Connect opencodex to OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code, Gajae Code and DeepSeek Harness from the dashboard — one switch per client, with a backup taken before every write.
description: Connect opencodex to OpenCode, Pi, OMP, Hermes, OpenClaw, Kimi Code, Gajae Code, DeepSeek Harness and MiniMax Code from the dashboard — one switch per client, with a backup taken before every write.
---

The **Integrations** tab writes opencodex's provider block into a client's own config
file, and removes it again. Eight clients work this way, each with a switch:
file, and removes it again. Nine clients work this way, each with a switch:

| Client | Config file | Format | When the change takes effect | Credential |
|---|---|---|---|---|
Expand All @@ -16,13 +16,20 @@ file, and removes it again. Eight clients work this way, each with a switch:
| Kimi Code | `~/.kimi-code/config.toml` | TOML | on restart, or `/reload` | loopback placeholder |
| Gajae Code | `~/.gjc/agent/models.yml` | YAML | new sessions, or when you open `/model` |`OPENCODEX_GAJAE_API_KEY` |
| DeepSeek Harness (DSH) | `$DSH_HOME/settings.yaml` (default `~/.dsh/settings.yaml`) | YAML | hot reload | non-secret loopback bearer placeholder |
| MiniMax Code | `~/.minimax/config.yaml` | YAML | new sessions, or after opening the model picker | loopback placeholder |

Managed DSH support has a compatibility floor of **DSH 0.1.0-rc.6**. OpenCodex owns only
`llm-pi-ai.providers.opencodex`; Apply and Refresh replace that fragment, Disable removes only that
fragment, and Restore puts back a recorded snapshot. DSH hot reloads provider changes. These
operations do not change the user's default model or the native `deepseek-official` provider.
The managed DSH integration is currently loopback-only and never writes a real credential.

MiniMax Code follows `MINIMAX_DATA_DIR`, then `MAVIS_DATA_DIR`, before falling
back to `~/.minimax`. Its managed block owns only `custom_provider.opencodex`.
It does not change `defaultModel`, the selected MiniMax credential source, or
the user's MiniMax login. Choose a `custom_provider:opencodex/<provider/model>`
entry in MCode after connecting it.

Paths honor each client's own environment override where it has one. For OMP,
`OMP_PROFILE` wins over `PI_PROFILE` by presence, even when explicitly empty. A named profile
uses `PI_CONFIG_DIR` as a directory name relative to the user's home and ignores `PI_CODING_AGENT_DIR`; without a named profile,
Expand Down Expand Up @@ -83,8 +90,8 @@ than 1000 levels — which locks the switch instead, so nothing is silently chan
**OMP** is unaffected by sibling edits too, for a different reason: its writer
patches only its own `providers.opencodex` range byte-wise, so the rest of the
file is never rewritten. For the remaining formats that can carry comments
(Hermes, OpenClaw, Kimi Code, Gajae Code — YAML, JSON5 and TOML written as whole
documents), or
(Hermes, OpenClaw, Kimi Code, Gajae Code, MiniMax Code — YAML, JSON5 and TOML
written as whole documents), or
whenever our own entries were edited, the switch locks and disable refuses rather
than guessing which edits were yours.

Expand All @@ -106,8 +113,8 @@ changed value and calling it success. You will see the file named and nothing on
disk will have moved. Editing that file by hand still works; it is only our
automatic rewrite that declines.

**Pi, Kimi Code, Gajae Code and the managed DSH integration only work against a loopback bind.**
The first three have no config field for the `x-opencodex-api-key` header a non-loopback bind
**Pi, Kimi Code, Gajae Code, MiniMax Code and the managed DSH integration only work against a loopback bind.**
The first four have no config field for the `x-opencodex-api-key` header a non-loopback bind
requires. DSH has a generic headers map, but rc.6 does not document that dedicated admission
header as a supported integration contract, so the managed writer fails closed instead of
guessing. Give them loopback access through an SSH tunnel or a local forwarder that adds the header.
Expand Down Expand Up @@ -138,6 +145,29 @@ ocx integration client history --client hermes
ocx integration client restore --op <opId> [--confirm-drift]
```

For MiniMax Code, connect the provider once and launch through the checked wrapper:

```bash
ocx integration client enable --client mcode
ocx mcode
```

The separate MiniMax platform CLI (`mmx`) is not a file-toggle integration. Its text
commands use MiniMax's Anthropic-compatible endpoint, so OpenCodex provides a
credential-isolated, loopback-only launcher:

```bash
ocx mmx text chat --model anthropic/claude-opus-5 --message "Hello"
ocx mmx text repl --model openai/gpt-5.6-sol
```

Only `mmx text chat` and `mmx text repl` are proxied. Run plain `mmx` for
MiniMax-native image, video, speech, music, vision, search, quota, auth, config, file
and update commands. The wrapper uses a temporary config containing only a non-secret
loopback placeholder; it never loads your `~/.mmx` OAuth or API-key credentials, and
it refuses `--api-key`, `--base-url` and `--region` overrides. See
[MiniMax clients](/guides/minimax/) for the complete workflow and limits.

`--confirm-drift` is never assumed. If the file changed after the operation you are
restoring, the command refuses and tells you, because replacing your newer edits is your
decision to make.
Expand Down
Loading
Loading