Skip to content

Commit f1748c8

Browse files
authored
feat(coder-labs/modules/codex): add support for agentapi state_persistence (#785)
## Description - add support for agentapi state_persistence ## Type of Change - [ ] New module - [ ] New template - [ ] Bug fix - [x] Feature/enhancement - [x] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/coder-labs/modules/codex` **New version:** `v4.2.0` **Breaking change:** [ ] Yes [x] No ## Testing & Validation - [x] Tests pass (`bun test`) - [x] Code formatted (`bun fmt`) - [x] Changes tested locally ## Related Issues Closes: #783
1 parent f6a09d4 commit f1748c8

File tree

3 files changed

+234
-27
lines changed

3 files changed

+234
-27
lines changed

registry/coder-labs/modules/codex/README.md

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Run Codex CLI in your workspace to access OpenAI's models through the Codex inte
1313
```tf
1414
module "codex" {
1515
source = "registry.coder.com/coder-labs/codex/coder"
16-
version = "4.1.2"
16+
version = "4.2.0"
1717
agent_id = coder_agent.example.id
1818
openai_api_key = var.openai_api_key
1919
workdir = "/home/coder/project"
@@ -32,7 +32,7 @@ module "codex" {
3232
module "codex" {
3333
count = data.coder_workspace.me.start_count
3434
source = "registry.coder.com/coder-labs/codex/coder"
35-
version = "4.1.2"
35+
version = "4.2.0"
3636
agent_id = coder_agent.example.id
3737
openai_api_key = "..."
3838
workdir = "/home/coder/project"
@@ -51,7 +51,7 @@ For tasks integration with AI Bridge, add `enable_aibridge = true` to the [Usage
5151
```tf
5252
module "codex" {
5353
source = "registry.coder.com/coder-labs/codex/coder"
54-
version = "4.1.2"
54+
version = "4.2.0"
5555
agent_id = coder_agent.example.id
5656
workdir = "/home/coder/project"
5757
enable_aibridge = true
@@ -63,6 +63,8 @@ When `enable_aibridge = true`, the module:
6363
- Configures Codex to use the AI Bridge profile with `base_url` pointing to `${data.coder_workspace.me.access_url}/api/v2/aibridge/openai/v1` and `env_key` pointing to the workspace owner's session token
6464

6565
```toml
66+
profile = "aibridge" # sets the default profile to aibridge
67+
6668
[model_providers.aibridge]
6769
name = "AI Bridge"
6870
base_url = "https://example.coder.com/api/v2/aibridge/openai/v1"
@@ -75,8 +77,6 @@ model = "<model>" # as configured in the module input
7577
model_reasoning_effort = "<model_reasoning_effort>" # as configured in the module input
7678
```
7779

78-
Codex then runs with `--profile aibridge`
79-
8080
This allows Codex to route API requests through Coder's AI Bridge instead of directly to OpenAI's API.
8181
Template build will fail if `openai_api_key` is provided alongside `enable_aibridge = true`.
8282

@@ -94,7 +94,7 @@ data "coder_task" "me" {}
9494
9595
module "codex" {
9696
source = "registry.coder.com/coder-labs/codex/coder"
97-
version = "4.1.2"
97+
version = "4.2.0"
9898
agent_id = coder_agent.example.id
9999
openai_api_key = "..."
100100
ai_prompt = data.coder_task.me.prompt
@@ -112,7 +112,7 @@ This example shows additional configuration options for custom models, MCP serve
112112
```tf
113113
module "codex" {
114114
source = "registry.coder.com/coder-labs/codex/coder"
115-
version = "4.1.2"
115+
version = "4.2.0"
116116
agent_id = coder_agent.example.id
117117
openai_api_key = "..."
118118
workdir = "/home/coder/project"
@@ -148,6 +148,19 @@ module "codex" {
148148
- **Configuration**: Sets `OPENAI_API_KEY` environment variable and passes `--model` flag to Codex CLI (if variables provided)
149149
- **Session Continuity**: When `continue = true` (default), the module automatically tracks task sessions in `~/.codex-module/.codex-task-session`. On workspace restart, it resumes the existing session with full conversation history. Set `continue = false` to always start fresh sessions.
150150

151+
## State Persistence
152+
153+
AgentAPI can save and restore its conversation state to disk across workspace restarts. This complements `continue` (which resumes the Codex CLI session) by also preserving the AgentAPI-level context. Enabled by default, requires agentapi >= v0.12.0 (older versions skip it with a warning).
154+
155+
To disable:
156+
157+
```tf
158+
module "codex" {
159+
# ... other config
160+
enable_state_persistence = false
161+
}
162+
```
163+
151164
## Configuration
152165

153166
### Default Configuration

registry/coder-labs/modules/codex/main.tf

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ variable "install_agentapi" {
131131
variable "agentapi_version" {
132132
type = string
133133
description = "The version of AgentAPI to install."
134-
default = "v0.11.8"
134+
default = "v0.12.1"
135135
}
136136

137137
variable "codex_model" {
@@ -164,6 +164,12 @@ variable "continue" {
164164
default = true
165165
}
166166

167+
variable "enable_state_persistence" {
168+
type = bool
169+
description = "Enable AgentAPI conversation state persistence across restarts."
170+
default = true
171+
}
172+
167173
variable "codex_system_prompt" {
168174
type = string
169175
description = "System instructions written to AGENTS.md in the ~/.codex directory"
@@ -206,25 +212,26 @@ locals {
206212

207213
module "agentapi" {
208214
source = "registry.coder.com/coder/agentapi/coder"
209-
version = "2.0.0"
210-
211-
agent_id = var.agent_id
212-
folder = local.workdir
213-
web_app_slug = local.app_slug
214-
web_app_order = var.order
215-
web_app_group = var.group
216-
web_app_icon = var.icon
217-
web_app_display_name = var.web_app_display_name
218-
cli_app = var.cli_app
219-
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
220-
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
221-
module_dir_name = local.module_dir_name
222-
install_agentapi = var.install_agentapi
223-
agentapi_subdomain = var.subdomain
224-
agentapi_version = var.agentapi_version
225-
pre_install_script = var.pre_install_script
226-
post_install_script = var.post_install_script
227-
start_script = <<-EOT
215+
version = "2.2.0"
216+
217+
agent_id = var.agent_id
218+
folder = local.workdir
219+
web_app_slug = local.app_slug
220+
web_app_order = var.order
221+
web_app_group = var.group
222+
web_app_icon = var.icon
223+
web_app_display_name = var.web_app_display_name
224+
cli_app = var.cli_app
225+
cli_app_slug = var.cli_app ? "${local.app_slug}-cli" : null
226+
cli_app_display_name = var.cli_app ? var.cli_app_display_name : null
227+
module_dir_name = local.module_dir_name
228+
install_agentapi = var.install_agentapi
229+
agentapi_subdomain = var.subdomain
230+
agentapi_version = var.agentapi_version
231+
enable_state_persistence = var.enable_state_persistence
232+
pre_install_script = var.pre_install_script
233+
post_install_script = var.post_install_script
234+
start_script = <<-EOT
228235
#!/bin/bash
229236
set -o errexit
230237
set -o pipefail
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
run "test_codex_basic" {
2+
command = plan
3+
4+
variables {
5+
agent_id = "test-agent"
6+
workdir = "/home/coder"
7+
openai_api_key = "test-key"
8+
}
9+
10+
assert {
11+
condition = var.agent_id == "test-agent"
12+
error_message = "Agent ID should be set correctly"
13+
}
14+
15+
assert {
16+
condition = var.workdir == "/home/coder"
17+
error_message = "Workdir should be set correctly"
18+
}
19+
20+
assert {
21+
condition = var.install_codex == true
22+
error_message = "install_codex should default to true"
23+
}
24+
25+
assert {
26+
condition = var.install_agentapi == true
27+
error_message = "install_agentapi should default to true"
28+
}
29+
30+
assert {
31+
condition = var.report_tasks == true
32+
error_message = "report_tasks should default to true"
33+
}
34+
35+
assert {
36+
condition = var.continue == true
37+
error_message = "continue should default to true"
38+
}
39+
}
40+
41+
run "test_enable_state_persistence_default" {
42+
command = plan
43+
44+
variables {
45+
agent_id = "test-agent"
46+
workdir = "/home/coder"
47+
openai_api_key = "test-key"
48+
}
49+
50+
assert {
51+
condition = var.enable_state_persistence == true
52+
error_message = "enable_state_persistence should default to true"
53+
}
54+
}
55+
56+
run "test_disable_state_persistence" {
57+
command = plan
58+
59+
variables {
60+
agent_id = "test-agent"
61+
workdir = "/home/coder"
62+
openai_api_key = "test-key"
63+
enable_state_persistence = false
64+
}
65+
66+
assert {
67+
condition = var.enable_state_persistence == false
68+
error_message = "enable_state_persistence should be false when explicitly disabled"
69+
}
70+
}
71+
72+
run "test_codex_with_aibridge" {
73+
command = plan
74+
75+
variables {
76+
agent_id = "test-agent"
77+
workdir = "/home/coder"
78+
enable_aibridge = true
79+
}
80+
81+
assert {
82+
condition = var.enable_aibridge == true
83+
error_message = "enable_aibridge should be set to true"
84+
}
85+
}
86+
87+
run "test_aibridge_disabled_with_api_key" {
88+
command = plan
89+
90+
variables {
91+
agent_id = "test-agent"
92+
workdir = "/home/coder"
93+
openai_api_key = "test-key"
94+
enable_aibridge = false
95+
}
96+
97+
assert {
98+
condition = var.enable_aibridge == false
99+
error_message = "enable_aibridge should be false"
100+
}
101+
102+
assert {
103+
condition = coder_env.openai_api_key.value == "test-key"
104+
error_message = "OpenAI API key should be set correctly"
105+
}
106+
}
107+
108+
run "test_custom_options" {
109+
command = plan
110+
111+
variables {
112+
agent_id = "test-agent"
113+
workdir = "/home/coder/project"
114+
openai_api_key = "test-key"
115+
order = 5
116+
group = "ai-tools"
117+
icon = "/icon/custom.svg"
118+
web_app_display_name = "Custom Codex"
119+
cli_app = true
120+
cli_app_display_name = "Codex Terminal"
121+
subdomain = true
122+
report_tasks = false
123+
continue = false
124+
codex_model = "gpt-4o"
125+
codex_version = "0.1.0"
126+
agentapi_version = "v0.12.0"
127+
}
128+
129+
assert {
130+
condition = var.order == 5
131+
error_message = "Order should be set to 5"
132+
}
133+
134+
assert {
135+
condition = var.group == "ai-tools"
136+
error_message = "Group should be set to 'ai-tools'"
137+
}
138+
139+
assert {
140+
condition = var.icon == "/icon/custom.svg"
141+
error_message = "Icon should be set to custom icon"
142+
}
143+
144+
assert {
145+
condition = var.cli_app == true
146+
error_message = "cli_app should be enabled"
147+
}
148+
149+
assert {
150+
condition = var.subdomain == true
151+
error_message = "subdomain should be enabled"
152+
}
153+
154+
assert {
155+
condition = var.report_tasks == false
156+
error_message = "report_tasks should be disabled"
157+
}
158+
159+
assert {
160+
condition = var.continue == false
161+
error_message = "continue should be disabled"
162+
}
163+
164+
assert {
165+
condition = var.codex_model == "gpt-4o"
166+
error_message = "codex_model should be set to 'gpt-4o'"
167+
}
168+
}
169+
170+
run "test_no_api_key_no_aibridge" {
171+
command = plan
172+
173+
variables {
174+
agent_id = "test-agent"
175+
workdir = "/home/coder"
176+
}
177+
178+
assert {
179+
condition = var.openai_api_key == ""
180+
error_message = "openai_api_key should be empty when not provided"
181+
}
182+
183+
assert {
184+
condition = var.enable_aibridge == false
185+
error_message = "enable_aibridge should default to false"
186+
}
187+
}

0 commit comments

Comments
 (0)