diff --git a/registry/coder/modules/claude-code/README.md b/registry/coder/modules/claude-code/README.md index 3e690ce2e..d3cee145c 100644 --- a/registry/coder/modules/claude-code/README.md +++ b/registry/coder/modules/claude-code/README.md @@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder/project" claude_api_key = "xxxx-xxxxx-xxxx" @@ -32,6 +32,10 @@ module "claude-code" { - You can get the API key from the [Anthropic Console](https://console.anthropic.com/dashboard). - You can get the Session Token using the `claude setup-token` command. This is a long-lived authentication token (requires Claude subscription) +### Session Resumption Behavior + +By default, Claude Code automatically resumes existing conversations when your workspace restarts. Sessions are tracked per workspace directory, so conversations continue where you left off. If no session exists (first start), your `ai_prompt` will run normally. To disable this behavior and always start fresh, set `continue = false` + ## Examples ### Usage with Agent Boundaries @@ -66,7 +70,7 @@ data "coder_parameter" "ai_prompt" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder/project" @@ -102,7 +106,7 @@ Run and configure Claude Code as a standalone CLI in your workspace. ```tf module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder" install_claude_code = true @@ -125,7 +129,7 @@ variable "claude_code_oauth_token" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder/project" claude_code_oauth_token = var.claude_code_oauth_token @@ -198,7 +202,7 @@ resource "coder_env" "bedrock_api_key" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder/project" model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0" @@ -255,7 +259,7 @@ resource "coder_env" "google_application_credentials" { module "claude-code" { source = "registry.coder.com/coder/claude-code/coder" - version = "3.2.1" + version = "3.2.2" agent_id = coder_agent.example.id workdir = "/home/coder/project" model = "claude-sonnet-4@20250514" diff --git a/registry/coder/modules/claude-code/main.test.ts b/registry/coder/modules/claude-code/main.test.ts index 9c132f1ab..a7c2dd14c 100644 --- a/registry/coder/modules/claude-code/main.test.ts +++ b/registry/coder/modules/claude-code/main.test.ts @@ -167,7 +167,7 @@ describe("claude-code", async () => { const { id } = await setup({ moduleVariables: { permission_mode: mode, - task_prompt: "test prompt", + ai_prompt: "test prompt", }, }); await execModuleScript(id); @@ -185,7 +185,7 @@ describe("claude-code", async () => { const { id } = await setup({ moduleVariables: { model: model, - task_prompt: "test prompt", + ai_prompt: "test prompt", }, }); await execModuleScript(id); @@ -198,13 +198,24 @@ describe("claude-code", async () => { expect(startLog.stdout).toContain(`--model ${model}`); }); - test("claude-continue-previous-conversation", async () => { + test("claude-continue-resume-existing-session", async () => { const { id } = await setup({ moduleVariables: { continue: "true", - task_prompt: "test prompt", + ai_prompt: "test prompt", }, }); + + // Create a mock session file with the predefined task session ID + const taskSessionId = "cd32e253-ca16-4fd3-9825-d837e74ae3c2"; + const sessionDir = `/home/coder/.claude/projects/-home-coder-project`; + await execContainer(id, ["mkdir", "-p", sessionDir]); + await execContainer(id, [ + "bash", + "-c", + `touch ${sessionDir}/session-${taskSessionId}.jsonl`, + ]); + await execModuleScript(id); const startLog = await execContainer(id, [ @@ -212,7 +223,9 @@ describe("claude-code", async () => { "-c", "cat /home/coder/.claude-module/agentapi-start.log", ]); - expect(startLog.stdout).toContain("--continue"); + expect(startLog.stdout).toContain("--resume"); + expect(startLog.stdout).toContain(taskSessionId); + expect(startLog.stdout).toContain("Resuming existing task session"); }); test("pre-post-install-scripts", async () => { diff --git a/registry/coder/modules/claude-code/main.tf b/registry/coder/modules/claude-code/main.tf index df3eaaa57..20a0cfee0 100644 --- a/registry/coder/modules/claude-code/main.tf +++ b/registry/coder/modules/claude-code/main.tf @@ -134,8 +134,8 @@ variable "resume_session_id" { variable "continue" { type = bool - description = "Load the most recent conversation in the current directory. Task will fail in a new workspace with no conversation/session to continue" - default = false + 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)." + default = true } variable "dangerously_skip_permissions" { diff --git a/registry/coder/modules/claude-code/scripts/start.sh b/registry/coder/modules/claude-code/scripts/start.sh index daef71a30..fb3180af9 100644 --- a/registry/coder/modules/claude-code/scripts/start.sh +++ b/registry/coder/modules/claude-code/scripts/start.sh @@ -64,37 +64,70 @@ function validate_claude_installation() { fi } -ARGS=() +TASK_SESSION_ID="cd32e253-ca16-4fd3-9825-d837e74ae3c2" -function build_claude_args() { - if [ -n "$ARG_MODEL" ]; then - ARGS+=(--model "$ARG_MODEL") +task_session_exists() { + if find "$HOME/.claude" -type f -name "*${TASK_SESSION_ID}*" 2> /dev/null | grep -q .; then + return 0 + else + return 1 fi +} - if [ -n "$ARG_RESUME_SESSION_ID" ]; then - ARGS+=(--resume "$ARG_RESUME_SESSION_ID") - fi +ARGS=() - if [ "$ARG_CONTINUE" = "true" ]; then - ARGS+=(--continue) +function start_agentapi() { + mkdir -p "$ARG_WORKDIR" + cd "$ARG_WORKDIR" + + if [ -n "$ARG_MODEL" ]; then + ARGS+=(--model "$ARG_MODEL") fi if [ -n "$ARG_PERMISSION_MODE" ]; then ARGS+=(--permission-mode "$ARG_PERMISSION_MODE") fi -} - -function start_agentapi() { - mkdir -p "$ARG_WORKDIR" - cd "$ARG_WORKDIR" - if [ -n "$ARG_AI_PROMPT" ]; then - ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT") - else - if [ -n "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" ]; then + if [ -n "$ARG_RESUME_SESSION_ID" ]; then + echo "Using explicit resume_session_id: $ARG_RESUME_SESSION_ID" + ARGS+=(--resume "$ARG_RESUME_SESSION_ID") + if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then ARGS+=(--dangerously-skip-permissions) fi + elif [ "$ARG_CONTINUE" = "true" ]; then + if task_session_exists; then + echo "Task session detected (ID: $TASK_SESSION_ID)" + ARGS+=(--resume "$TASK_SESSION_ID") + if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then + ARGS+=(--dangerously-skip-permissions) + fi + echo "Resuming existing task session" + else + echo "No existing task session found" + ARGS+=(--session-id "$TASK_SESSION_ID") + if [ -n "$ARG_AI_PROMPT" ]; then + ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT") + echo "Starting new task session with prompt" + else + if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then + ARGS+=(--dangerously-skip-permissions) + fi + echo "Starting new task session" + fi + fi + else + echo "Continue disabled, starting fresh session" + if [ -n "$ARG_AI_PROMPT" ]; then + ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT") + echo "Starting new session with prompt" + else + if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then + ARGS+=(--dangerously-skip-permissions) + fi + echo "Starting claude code session" + fi fi + printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")" if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then @@ -140,5 +173,4 @@ function start_agentapi() { } validate_claude_installation -build_claude_args start_agentapi