mirror of
https://github.com/coder/registry.git
synced 2026-06-02 20:48:14 +00:00
fix: resolve issues with claude-code session resumption (#496)
## Description Fixes session resumption logic by having the continue flag decide whether to continue a workspace based on session history ## Type of Change - [ ] New module - [ ] New template - [X] Bug fix - [X] Feature/enhancement - [ ] Documentation - [ ] Other ## Module Information <!-- Delete this section if not applicable --> **Path:** `registry/coder/modules/claude-code` **New version:** `v3.2.2` **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 --> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -13,7 +13,7 @@ Run the [Claude Code](https://docs.anthropic.com/en/docs/agents-and-tools/claude
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
claude_api_key = "xxxx-xxxxx-xxxx"
|
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 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)
|
- 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
|
## Examples
|
||||||
|
|
||||||
### Usage with Agent Boundaries
|
### Usage with Agent Boundaries
|
||||||
@@ -66,7 +70,7 @@ data "coder_parameter" "ai_prompt" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
|
|
||||||
@@ -102,7 +106,7 @@ Run and configure Claude Code as a standalone CLI in your workspace.
|
|||||||
```tf
|
```tf
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder"
|
workdir = "/home/coder"
|
||||||
install_claude_code = true
|
install_claude_code = true
|
||||||
@@ -125,7 +129,7 @@ variable "claude_code_oauth_token" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
claude_code_oauth_token = var.claude_code_oauth_token
|
claude_code_oauth_token = var.claude_code_oauth_token
|
||||||
@@ -198,7 +202,7 @@ resource "coder_env" "bedrock_api_key" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
model = "global.anthropic.claude-sonnet-4-5-20250929-v1:0"
|
||||||
@@ -255,7 +259,7 @@ resource "coder_env" "google_application_credentials" {
|
|||||||
|
|
||||||
module "claude-code" {
|
module "claude-code" {
|
||||||
source = "registry.coder.com/coder/claude-code/coder"
|
source = "registry.coder.com/coder/claude-code/coder"
|
||||||
version = "3.2.1"
|
version = "3.2.2"
|
||||||
agent_id = coder_agent.example.id
|
agent_id = coder_agent.example.id
|
||||||
workdir = "/home/coder/project"
|
workdir = "/home/coder/project"
|
||||||
model = "claude-sonnet-4@20250514"
|
model = "claude-sonnet-4@20250514"
|
||||||
|
|||||||
@@ -167,7 +167,7 @@ describe("claude-code", async () => {
|
|||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
permission_mode: mode,
|
permission_mode: mode,
|
||||||
task_prompt: "test prompt",
|
ai_prompt: "test prompt",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
@@ -185,7 +185,7 @@ describe("claude-code", async () => {
|
|||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
model: model,
|
model: model,
|
||||||
task_prompt: "test prompt",
|
ai_prompt: "test prompt",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
await execModuleScript(id);
|
await execModuleScript(id);
|
||||||
@@ -198,13 +198,24 @@ describe("claude-code", async () => {
|
|||||||
expect(startLog.stdout).toContain(`--model ${model}`);
|
expect(startLog.stdout).toContain(`--model ${model}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("claude-continue-previous-conversation", async () => {
|
test("claude-continue-resume-existing-session", async () => {
|
||||||
const { id } = await setup({
|
const { id } = await setup({
|
||||||
moduleVariables: {
|
moduleVariables: {
|
||||||
continue: "true",
|
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);
|
await execModuleScript(id);
|
||||||
|
|
||||||
const startLog = await execContainer(id, [
|
const startLog = await execContainer(id, [
|
||||||
@@ -212,7 +223,9 @@ describe("claude-code", async () => {
|
|||||||
"-c",
|
"-c",
|
||||||
"cat /home/coder/.claude-module/agentapi-start.log",
|
"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 () => {
|
test("pre-post-install-scripts", async () => {
|
||||||
|
|||||||
@@ -134,8 +134,8 @@ variable "resume_session_id" {
|
|||||||
|
|
||||||
variable "continue" {
|
variable "continue" {
|
||||||
type = bool
|
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"
|
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 = false
|
default = true
|
||||||
}
|
}
|
||||||
|
|
||||||
variable "dangerously_skip_permissions" {
|
variable "dangerously_skip_permissions" {
|
||||||
|
|||||||
@@ -64,37 +64,70 @@ function validate_claude_installation() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TASK_SESSION_ID="cd32e253-ca16-4fd3-9825-d837e74ae3c2"
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
ARGS=()
|
ARGS=()
|
||||||
|
|
||||||
function build_claude_args() {
|
function start_agentapi() {
|
||||||
|
mkdir -p "$ARG_WORKDIR"
|
||||||
|
cd "$ARG_WORKDIR"
|
||||||
|
|
||||||
if [ -n "$ARG_MODEL" ]; then
|
if [ -n "$ARG_MODEL" ]; then
|
||||||
ARGS+=(--model "$ARG_MODEL")
|
ARGS+=(--model "$ARG_MODEL")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -n "$ARG_RESUME_SESSION_ID" ]; then
|
|
||||||
ARGS+=(--resume "$ARG_RESUME_SESSION_ID")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$ARG_CONTINUE" = "true" ]; then
|
|
||||||
ARGS+=(--continue)
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -n "$ARG_PERMISSION_MODE" ]; then
|
if [ -n "$ARG_PERMISSION_MODE" ]; then
|
||||||
ARGS+=(--permission-mode "$ARG_PERMISSION_MODE")
|
ARGS+=(--permission-mode "$ARG_PERMISSION_MODE")
|
||||||
fi
|
fi
|
||||||
|
|
||||||
}
|
if [ -n "$ARG_RESUME_SESSION_ID" ]; then
|
||||||
|
echo "Using explicit resume_session_id: $ARG_RESUME_SESSION_ID"
|
||||||
function start_agentapi() {
|
ARGS+=(--resume "$ARG_RESUME_SESSION_ID")
|
||||||
mkdir -p "$ARG_WORKDIR"
|
if [ "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" = "true" ]; then
|
||||||
cd "$ARG_WORKDIR"
|
|
||||||
if [ -n "$ARG_AI_PROMPT" ]; then
|
|
||||||
ARGS+=(--dangerously-skip-permissions "$ARG_AI_PROMPT")
|
|
||||||
else
|
|
||||||
if [ -n "$ARG_DANGEROUSLY_SKIP_PERMISSIONS" ]; then
|
|
||||||
ARGS+=(--dangerously-skip-permissions)
|
ARGS+=(--dangerously-skip-permissions)
|
||||||
fi
|
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
|
fi
|
||||||
|
|
||||||
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
|
printf "Running claude code with args: %s\n" "$(printf '%q ' "${ARGS[@]}")"
|
||||||
|
|
||||||
if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then
|
if [ "${ARG_ENABLE_BOUNDARY:-false}" = "true" ]; then
|
||||||
@@ -140,5 +173,4 @@ function start_agentapi() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
validate_claude_installation
|
validate_claude_installation
|
||||||
build_claude_args
|
|
||||||
start_agentapi
|
start_agentapi
|
||||||
|
|||||||
Reference in New Issue
Block a user