Skip to content

Commit c853ea0

Browse files
committed
implement session fallback and improve CLI compatibility
1 parent ed046eb commit c853ea0

File tree

4 files changed

+91
-7
lines changed

4 files changed

+91
-7
lines changed

src/api/providers/__tests__/claude-code.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,30 @@ describe("ClaudeCodeHandler", () => {
121121
messages,
122122
path: mockOptions.claudeCodePath,
123123
modelId: mockOptions.apiModelId,
124+
taskId: undefined,
124125
})
125126
})
126127

128+
it("should call runClaudeCode with taskId from metadata", async () => {
129+
const testTaskId = "test-task-123"
130+
const messageGenerator = handler.createMessage(systemPrompt, messages, { taskId: testTaskId })
131+
const iterator = messageGenerator[Symbol.asyncIterator]()
132+
133+
// Trigger close immediately to end the iteration
134+
setImmediate(() => {
135+
mockProcess._simulateClose(0)
136+
})
137+
138+
await iterator.next()
139+
140+
expect(mockRunClaudeCode).toHaveBeenCalledWith({
141+
systemPrompt,
142+
messages,
143+
path: mockOptions.claudeCodePath,
144+
modelId: mockOptions.apiModelId,
145+
taskId: testTaskId,
146+
})
147+
})
127148
it("should handle successful Claude Code output", async () => {
128149
const messageGenerator = handler.createMessage(systemPrompt, messages)
129150
const chunks: any[] = []

src/api/providers/claude-code.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
2222
metadata?: ApiHandlerCreateMessageMetadata,
2323
): ApiStream {
2424
let claudeProcess: ChildProcess | null = null
25+
let retryWithoutSession = false
2526

2627
try {
2728
claudeProcess = runClaudeCode({
2829
systemPrompt,
2930
messages,
3031
path: this.options.claudeCodePath,
3132
modelId: this.getModel().id,
33+
taskId: retryWithoutSession ? undefined : metadata?.taskId,
3234
})
3335

3436
// Listen for abort signal if provided
@@ -87,6 +89,61 @@ export class ClaudeCodeHandler extends BaseProvider implements ApiHandler {
8789
}
8890

8991
if (exitCode !== null && exitCode !== 0) {
92+
// Detect session-related errors and execute fallback processing
93+
if (
94+
errorOutput.includes("No conversation found with session ID") &&
95+
!retryWithoutSession &&
96+
metadata?.taskId
97+
) {
98+
// Retry without session
99+
retryWithoutSession = true
100+
claudeProcess = runClaudeCode({
101+
systemPrompt,
102+
messages,
103+
path: this.options.claudeCodePath,
104+
modelId: this.getModel().id,
105+
taskId: undefined,
106+
})
107+
108+
// Reinitialize process
109+
dataQueue.length = 0
110+
errorOutput = ""
111+
exitCode = null
112+
processError = null
113+
114+
// Set up event listeners for the new process
115+
claudeProcess.stdout?.on("data", (data: Buffer) => {
116+
const output = data.toString()
117+
const lines = output.split("\n").filter((line: string) => line.trim() !== "")
118+
for (const line of lines) {
119+
dataQueue.push(line)
120+
}
121+
})
122+
123+
claudeProcess.stderr?.on("data", (data: Buffer) => {
124+
errorOutput += data.toString()
125+
})
126+
127+
claudeProcess.on("close", (code: number | null) => {
128+
exitCode = code
129+
})
130+
131+
claudeProcess.on("error", (error: Error) => {
132+
processError = error
133+
})
134+
135+
// Reset abort signal
136+
if (metadata?.signal) {
137+
metadata.signal.addEventListener("abort", () => {
138+
if (claudeProcess && !claudeProcess.killed) {
139+
claudeProcess.kill("SIGTERM")
140+
}
141+
})
142+
}
143+
144+
continue
145+
}
146+
90147
throw new Error(
91148
`Claude Code process exited with code ${exitCode}.${errorOutput ? ` Error output: ${errorOutput.trim()}` : ""}`,
92149
)

src/integrations/claude-code/__tests__/run.spec.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,9 @@ describe("runClaudeCode", () => {
3838
"/usr/local/bin/claude",
3939
expect.arrayContaining([
4040
"-p",
41-
"System: Test system prompt\n\nUser: Test message",
41+
JSON.stringify(params.messages),
42+
"--system-prompt",
43+
params.systemPrompt,
4244
"--verbose",
4345
"--output-format",
4446
"stream-json",

src/integrations/claude-code/run.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ function validateMessageContent(content: string): void {
1414
/\x1b\[/, // ANSI escape sequences
1515
/\$\(/, // Command substitution
1616
/`/, // Backticks for command substitution
17-
/\|\|/, // Logical OR that could chain commands
18-
/&&/, // Logical AND that could chain commands
19-
/;/, // Command separator
17+
/\s\|\|\s/, // Logical OR that could chain commands (with spaces to avoid matching //)
18+
/\s&&\s/, // Logical AND that could chain commands (with spaces)
19+
/;\s*\w/, // Command separator followed by word character
2020
/\n\s*[\w-]*\$\s/, // Newline followed by shell prompt patterns (e.g., "user$ ", "$ ")
2121
]
2222

@@ -104,15 +104,16 @@ export function runClaudeCode({
104104
messages,
105105
path,
106106
modelId,
107+
taskId,
107108
}: {
108109
systemPrompt: string
109110
messages: Anthropic.Messages.MessageParam[]
110111
path?: string
111112
modelId?: string
113+
taskId?: string
112114
}) {
113115
const claudePath = path || "claude"
114116
const workspacePath = getCwd()
115-
const sessionId = SessionManager.getSessionId(workspacePath)
116117

117118
// Serialize messages to JSON format for Claude CLI
118119
const serializedMessages = safeSerializeMessages(messages)
@@ -132,8 +133,11 @@ export function runClaudeCode({
132133
args.push("--model", modelId)
133134
}
134135

135-
// Note: Removed -r option as it requires an existing session ID from Claude CLI
136-
// Each call will be treated as a new conversation for now
136+
// Add -r option for session continuity only if taskId is explicitly provided
137+
// This avoids the "No conversation found" error for new sessions
138+
if (taskId) {
139+
args.push("-r", taskId)
140+
}
137141

138142
try {
139143
return execa(claudePath, args, {

0 commit comments

Comments
 (0)