Skip to content

Commit d3b40c0

Browse files
feat: add session resumption to codex (#506)
## Description Add continue variable, and logic for resuming task sessions <!-- Briefly describe what this PR does and why --> ## Type of Change - [ ] New module - [ ] New template - [ ] Bug fix - [X] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/coder-labs/modules/codex` **New version:** `v3.1.0` **Breaking change:** [ ] Yes [X] No ## Testing & Validation - [X] Tests pass (`bun test`) - [X] Code formatted (`bun fmt`) - [X] Changes tested locally ## Related Issues <!-- Link related issues or write "None" if not applicable -->
1 parent 01f5100 commit d3b40c0

File tree

5 files changed

+301
-47
lines changed

5 files changed

+301
-47
lines changed

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

Lines changed: 5 additions & 4 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 = "3.0.0"
16+
version = "3.1.0"
1717
agent_id = coder_agent.example.id
1818
openai_api_key = var.openai_api_key
1919
workdir = "/home/coder/project"
@@ -33,7 +33,7 @@ module "codex" {
3333
module "codex" {
3434
count = data.coder_workspace.me.start_count
3535
source = "registry.coder.com/coder-labs/codex/coder"
36-
version = "3.0.0"
36+
version = "3.1.0"
3737
agent_id = coder_agent.example.id
3838
openai_api_key = "..."
3939
workdir = "/home/coder/project"
@@ -61,7 +61,7 @@ module "coder-login" {
6161
6262
module "codex" {
6363
source = "registry.coder.com/coder-labs/codex/coder"
64-
version = "3.0.0"
64+
version = "3.1.0"
6565
agent_id = coder_agent.example.id
6666
openai_api_key = "..."
6767
ai_prompt = data.coder_parameter.ai_prompt.value
@@ -84,6 +84,7 @@ module "codex" {
8484
- **System Prompt**: If `codex_system_prompt` is set, writes the prompt to `AGENTS.md` in the `~/.codex/` directory
8585
- **Start**: Launches Codex CLI in the specified directory, wrapped by AgentAPI
8686
- **Configuration**: Sets `OPENAI_API_KEY` environment variable and passes `--model` flag to Codex CLI (if variables provided)
87+
- **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.
8788

8889
## Configuration
8990

@@ -107,7 +108,7 @@ For custom Codex configuration, use `base_config_toml` and/or `additional_mcp_se
107108
```tf
108109
module "codex" {
109110
source = "registry.coder.com/coder-labs/codex/coder"
110-
version = "3.0.0"
111+
version = "3.1.0"
111112
# ... other variables ...
112113
113114
# Override default configuration

registry/coder-labs/modules/codex/main.test.ts

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,4 +368,90 @@ describe("codex", async () => {
368368
expect(prompt.exitCode).not.toBe(0);
369369
expect(prompt.stderr).toContain("No such file or directory");
370370
});
371+
372+
test("codex-continue-capture-new-session", async () => {
373+
const { id } = await setup({
374+
moduleVariables: {
375+
continue: "true",
376+
ai_prompt: "test task",
377+
},
378+
});
379+
380+
const workdir = "/home/coder";
381+
const expectedSessionId = "019a1234-5678-9abc-def0-123456789012";
382+
const sessionsDir = "/home/coder/.codex/sessions";
383+
const sessionFile = `${sessionsDir}/${expectedSessionId}.jsonl`;
384+
385+
await execContainer(id, ["mkdir", "-p", sessionsDir]);
386+
await execContainer(id, [
387+
"bash",
388+
"-c",
389+
`echo '{"id":"${expectedSessionId}","cwd":"${workdir}","created":"2024-10-24T20:00:00Z","model":"gpt-4-turbo"}' > ${sessionFile}`,
390+
]);
391+
392+
await execModuleScript(id);
393+
394+
await expectAgentAPIStarted(id);
395+
396+
const trackingFile = "/home/coder/.codex-module/.codex-task-session";
397+
const maxAttempts = 30;
398+
let trackingFileContents = "";
399+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
400+
const result = await execContainer(id, [
401+
"bash",
402+
"-c",
403+
`cat ${trackingFile} 2>/dev/null || echo ""`,
404+
]);
405+
if (result.stdout.trim().length > 0) {
406+
trackingFileContents = result.stdout;
407+
break;
408+
}
409+
await new Promise((resolve) => setTimeout(resolve, 500));
410+
}
411+
412+
expect(trackingFileContents).toContain(`${workdir}|${expectedSessionId}`);
413+
414+
const startLog = await readFileContainer(
415+
id,
416+
"/home/coder/.codex-module/agentapi-start.log",
417+
);
418+
expect(startLog).toContain("Capturing new session ID");
419+
expect(startLog).toContain("Session tracked");
420+
expect(startLog).toContain(expectedSessionId);
421+
});
422+
423+
test("codex-continue-resume-existing-session", async () => {
424+
const { id } = await setup({
425+
moduleVariables: {
426+
continue: "true",
427+
ai_prompt: "test prompt",
428+
},
429+
});
430+
431+
const workdir = "/home/coder";
432+
const mockSessionId = "019a1234-5678-9abc-def0-123456789012";
433+
const trackingFile = "/home/coder/.codex-module/.codex-task-session";
434+
435+
await execContainer(id, ["mkdir", "-p", "/home/coder/.codex-module"]);
436+
await execContainer(id, [
437+
"bash",
438+
"-c",
439+
`echo "${workdir}|${mockSessionId}" > ${trackingFile}`,
440+
]);
441+
442+
await execModuleScript(id);
443+
444+
const startLog = await execContainer(id, [
445+
"bash",
446+
"-c",
447+
"cat /home/coder/.codex-module/agentapi-start.log",
448+
]);
449+
expect(startLog.stdout).toContain("Found existing task session");
450+
expect(startLog.stdout).toContain(mockSessionId);
451+
expect(startLog.stdout).toContain("Resuming existing session");
452+
expect(startLog.stdout).toContain(
453+
`Starting Codex with arguments: --model gpt-4-turbo resume ${mockSessionId}`,
454+
);
455+
expect(startLog.stdout).not.toContain("test prompt");
456+
});
371457
});

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

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,12 @@ variable "ai_prompt" {
137137
default = ""
138138
}
139139

140+
variable "continue" {
141+
type = bool
142+
description = "Automatically continue existing sessions on workspace restart. When true, resumes existing conversation if found, otherwise runs prompt or starts new session. When false, always starts fresh (ignores existing sessions)."
143+
default = true
144+
}
145+
140146
variable "codex_system_prompt" {
141147
type = string
142148
description = "System instructions written to AGENTS.md in the ~/.codex directory"
@@ -187,8 +193,9 @@ module "agentapi" {
187193
ARG_OPENAI_API_KEY='${var.openai_api_key}' \
188194
ARG_REPORT_TASKS='${var.report_tasks}' \
189195
ARG_CODEX_MODEL='${var.codex_model}' \
190-
ARG_CODEX_START_DIRECTORY='${var.workdir}' \
196+
ARG_CODEX_START_DIRECTORY='${local.workdir}' \
191197
ARG_CODEX_TASK_PROMPT='${base64encode(var.ai_prompt)}' \
198+
ARG_CONTINUE='${var.continue}' \
192199
/tmp/start.sh
193200
EOT
194201

@@ -206,7 +213,7 @@ module "agentapi" {
206213
ARG_BASE_CONFIG_TOML='${base64encode(var.base_config_toml)}' \
207214
ARG_ADDITIONAL_MCP_SERVERS='${base64encode(var.additional_mcp_servers)}' \
208215
ARG_CODER_MCP_APP_STATUS_SLUG='${local.app_slug}' \
209-
ARG_CODEX_START_DIRECTORY='${var.workdir}' \
216+
ARG_CODEX_START_DIRECTORY='${local.workdir}' \
210217
ARG_CODEX_INSTRUCTION_PROMPT='${base64encode(var.codex_system_prompt)}' \
211218
/tmp/install.sh
212219
EOT

0 commit comments

Comments
 (0)