Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ data class RuntimeActivityState(
val completedSessionIds: Set<String> = emptySet(),
/** Sessions whose current run has ended; late stream events must not resurrect them. */
val settledSessionIds: Set<String> = emptySet(),
val mutedSessionIds: Set<String> = emptySet(),
val permissions: List<PermissionRequest> = emptyList(),
val logs: List<RuntimeEventLog> = emptyList(),
val streamError: String? = null,
Expand Down Expand Up @@ -189,11 +190,22 @@ class RuntimeActivityRepository(
activeSessionIds = current.activeSessionIds + sessionId,
completedSessionIds = current.completedSessionIds - sessionId,
settledSessionIds = current.settledSessionIds - sessionId,
mutedSessionIds = current.mutedSessionIds - sessionId,
)
}
persistUnread()
}

fun markSessionAborted(sessionId: String) {
if (sessionId.isBlank()) return
mutableState.update { current ->
current.copy(
activeSessionIds = current.activeSessionIds - sessionId,
mutedSessionIds = current.mutedSessionIds + sessionId,
Comment on lines +203 to +204

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · medium]
markSessionAborted は呼び出し元(ChatViewModel.abort / 割り込み送信)で abortSessionsendMessage の成否に関わらず先に実行されます。abort が失敗したりランタイムが中断を無視して run が実際には継続している場合でも、ここで即座に activeSessionIds から除去され、drawer は停止状態を表示する一方、後続の SessionIdle が muted 扱いとなり完了通知も抑止されます。実際に run の終了が確認できてから muted へ移す(または abort 失敗時に状態を戻す)など、実行終了を確定させてから状態を変更する設計を検討してください。

)
Comment on lines +202 to +205

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · medium]
markSessionAbortedSessionError ハンドラ(settledSessionIds + sessionIdmutedSessionIds + sessionId を同時に設定)と異なり、settledSessionIds を更新していません。abort 後に遅延して届く MessagePartUpdated / MessagePartDelta / MessageUpdated は settled 判定(sessionId in current.settledSessionIds)に該当しないため、abort 済みセッションが activeSessionIds に再追加され、ドロワーの「実行中」表示が復活してしまいます。SessionError と同様に settledSessionIds も同時に設定することをお勧めします(markSessionRunning が次の run 開始時に解除するため、再開は妨げません)。

Suggestion:

Suggested change
current.copy(
activeSessionIds = current.activeSessionIds - sessionId,
mutedSessionIds = current.mutedSessionIds + sessionId,
)
current.copy(
activeSessionIds = current.activeSessionIds - sessionId,
settledSessionIds = current.settledSessionIds + sessionId,
mutedSessionIds = current.mutedSessionIds + sessionId,
)

}
}

/** Records that a run finished, leaving the chat unread until it is opened. */
fun markSessionFinished(
sessionId: String,
Expand Down Expand Up @@ -251,14 +263,18 @@ class RuntimeActivityRepository(
}
is OpenCodeEvent.SessionIdle -> {
markRuntimeIdle(event.sessionId)
var muted = false
mutableState.update { current ->
muted = event.sessionId in current.mutedSessionIds
current.copy(
activeSessionIds = current.activeSessionIds - event.sessionId,
completedSessionIds = current.completedSessionIds + event.sessionId,
settledSessionIds = current.settledSessionIds + event.sessionId,
mutedSessionIds = current.mutedSessionIds - event.sessionId,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[maintainability · low]
mutedSessionIds のエントリは、同じセッションが以後 SessionIdle / SessionStatusChanged / markSessionRunning でクリアされるまで残り続けます。abort 直後に接続が切れるなど、以後そのセッションのイベントが一切来ないケースではエントリが残り続け、ランタイム切断時のリセットでも activeSessionIdspermissions しか消えないため累積します。さらに、古い muted エントリが残った状態で同じセッションが(markSessionRunning を伴わないイベント起点で)再実行されると、本来通知すべき完了の SessionIdle が誤って抑止されるリスクがあります。切断時・ランタイム切替時や新規 run 開始時に muted を解除するなど、ライフサイクルを明確にすることを推奨します。

)
}
appendLog(messages.eventCompleted, null, event.sessionId)
if (muted) return
parentResolutionOf(target, event.sessionId).onSuccess { parentId ->
if (parentId == null) {
onSessionIdle?.invoke(event.sessionId, sessionTitle(target, event.sessionId), target.id)
Expand Down Expand Up @@ -301,6 +317,7 @@ class RuntimeActivityRepository(
} else {
current.settledSessionIds - event.sessionId
},
mutedSessionIds = current.mutedSessionIds - event.sessionId,
)
}
if (event.status != "idle") {
Expand All @@ -314,6 +331,7 @@ class RuntimeActivityRepository(
current.copy(
activeSessionIds = current.activeSessionIds - sessionId,
settledSessionIds = current.settledSessionIds + sessionId,
mutedSessionIds = current.mutedSessionIds + sessionId,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@ class ChatViewModel(
* events arrive. Deriving it from events alone left every chat on the idle marker.
*/
private val onRunStateChanged: (String, Boolean) -> Unit = { _, _ -> },
private val onSessionAborted: (String) -> Unit = {},
private val draftRepo: DraftRepository? = null,
/**
* Starts the periodic connection probe. It runs an unbounded polling loop, which a virtual
Expand Down Expand Up @@ -793,6 +794,7 @@ class ChatViewModel(
_uiState.update { it.copy(attachments = emptyList(), imagePreviews = emptyList()) }
return
}
val interrupting = _uiState.value.isRunning

val userMessage =
ChatMessage(
Expand Down Expand Up @@ -848,6 +850,7 @@ class ChatViewModel(
}
refreshContextUsage(targetSessionId)
}
if (interrupting) onSessionAborted(targetSessionId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · low]
onSessionAborted(markSessionAborted) が currentBackend.sendMessage の成功を待たずに呼ばれています。sendMessage が例外を投げた場合、この runCatching ブロックの onFailure でエラー表示になりますが、その時点で session は既に mutedSessionIds に追加され activeSessionIds からも削除されています。そのため、中断されるはずだった従来の実行が(sendMessage 失敗で実際には中断されず)継続し、後で完了しても SessionIdle 通知が抑制されたままになります。sendMessage 成功後に呼び出すか、失敗時に mute 状態を巻き戻す処理を検討してください。

currentBackend.sendMessage(
Comment on lines +853 to 854

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · high]
割り込み送信時に onSessionAborted を呼んでいますが、これは abortSession を呼ぶ前の段階です。markSessionAbortedmutedSessionIds に session を追加し、以降の SessionIdle イベントによる完了通知を永久に抑制します。もし sendMessage(割り込み)が後のコルーチン内で失敗して abortSession が実行されない/失敗する場合、このセッションの完了通知が以後送られなくなります。onSessionAborted は実際に割り込み(abortSession/sendMessage)が成功した後に呼ぶか、失敗時に mutedSessionIds から除外するリカバリ処理を入れてください。

targetSessionId,
PromptRequest(
Expand Down Expand Up @@ -1395,6 +1398,7 @@ class ChatViewModel(
val currentBackend = backend ?: return
val sessionId = _uiState.value.sessionId ?: return
viewModelScope.launch {
onSessionAborted(sessionId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · medium]
abortSession の成否に関わらず onSessionAborted が先に呼ばれます。abortSession が失敗して実行が続いた場合でも、session は activeSessionIds から除去され mutedSessionIds に残るため、ドロワーのスピナーが早期に消え、かつ後続の完了通知(SessionIdle)も抑制されます。runCatching { ... }.onSuccess { onSessionAborted(sessionId) } のように成功時のみ通知するか、失敗時に markSessionRunning 相当で状態を復元することを検討してください。

runCatching { currentBackend.abortSession(sessionId) }
Comment on lines +1401 to 1402

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · high]
onSessionAborted(sessionId)runCatching { currentBackend.abortSession(sessionId) } の直前(実行前)に呼ばれています。abortSession が失敗して .onFailure でエラー表示になるケースでも、mutedSessionIds に session が残ったままとなり、その後の実際の SessionIdle イベントによる完了通知が抑制されます。markSessionAbortedabortSession の成功後(.onSuccess 内)に呼ぶように変更してください。

.onSuccess {
_uiState.update {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import com.yugahashimoto.andcode.core.api.OpenCodeTodo
import com.yugahashimoto.andcode.core.api.PromptAttachment
import com.yugahashimoto.andcode.core.api.QuestionRequest
import com.yugahashimoto.andcode.runtime.PermissionResponse
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
Expand Down Expand Up @@ -69,6 +70,7 @@ class ClaudeCodeRuntime(
private class SessionProcess(
val process: Process,
val readerJob: Job,
val parser: ClaudeStreamJsonParser,
val permissionMode: ClaudePermissionMode,
val directory: String,
val model: String?,
Expand Down Expand Up @@ -280,6 +282,7 @@ class ClaudeCodeRuntime(
): Result<Unit> =
runCatching {
val session = ensureProcess(sessionId, directory.ifBlank { "/workspace" }, permissionMode, model, effort)
session.parser.beginTurn()
recordUserMessage(sessionId, prompt, attachments)
session.process.outputStream.apply {
write((json.encodeToString(JsonObject.serializer(), userMessage(prompt, attachments)) + "\n").toByteArray())
Expand Down Expand Up @@ -382,20 +385,33 @@ class ClaudeCodeRuntime(
val requestedModel = model
val readerJob =
scope.launch {
runCatching {
process.inputStream.bufferedReader().forEachLine { line ->
handleLine(sessionId, parser, line, requestedModel)
}
}
val streamFailure =
runCatching {
process.inputStream.bufferedReader().forEachLine { line ->
handleLine(sessionId, parser, line, requestedModel)
}
}.exceptionOrNull()
.takeUnless { it is CancellationException }
messageStore.flush()
// A CLI that exits mid-turn would otherwise leave the chat spinning forever.
events.tryEmit(OpenCodeEvent.SessionIdle(sessionId))
synchronized(this@ClaudeCodeRuntime) {
if (sessions[sessionId]?.process === process) sessions.remove(sessionId)
}
// stop() cancels this job; the abort path owns the state transitions, and an idle
// emitted here would announce the killed run as completed.
if (!isActive) return@launch
// A CLI that exits before its result line would otherwise leave the chat spinning
// forever; that is a failure, not a completion. A finished turn already emitted its
// own idle, and repeating it on process exit would re-announce the old run.
if (!parser.turnFinished) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[other · low]
前回の実装ではプロセス終了時に常に events.tryEmit(OpenCodeEvent.SessionIdle(sessionId)) が送出されていましたが、今は parser.turnFinished == false のときだけ送出されます。turnFinishedbeginTurn()send 時)で false にリセットされるため、既存プロセス再利用中(ensureProcess が既存セッションを返すケース)に beginTurn と result 行の間に終了が起きる場合は問題ありませんが、一度 turnFinished == true になった直後(result 行受信後から次の send までの間)にプロセスが終了した場合、この分岐は何も送出しません。この期間では既に result 行で SessionIdle が送出済みなので問題ありません。ロジックは妥当です。

val exitCode = runCatching { process.exitValue() }.getOrNull()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[maintainability · low]
runCatching { process.exitValue() }exitCode == 0 を「正常終了」として扱わず、!parser.turnFinished だけでエラーを判定しています。正常終了コード 0 かつ未完了ターン(例: リザルト行未受信のまま終了)でも「エラー」扱いで問題ないなら、exitCode を条件に含めるかコメントで意図を明示することを推奨します。また詳細メッセージが null のときは exit code が使われますが、メッセージの生成は ClaudeMessages 側に委ねられており、ここでは判断が分かりにくいです。

events.tryEmit(
OpenCodeEvent.SessionError(sessionId, messages.processExited(exitCode, streamFailure?.message)),
)
Comment on lines +407 to +409

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · low]
sessions マップからの削除(synchronized ブロック)の後に、ロック外で SessionError/SessionIdle を送出しています。reader のクリーンアップ処理中に別スレッドの send() が同じ sessionId で新しいプロセスを作成した場合、古いプロセスの失敗イベントが新しい実行に誤って紐づき、誤ったエラー表示や早期の idle 通知を引き起こす可能性があります。イベント送出を削除と同一の同期ブロック内で行うか、送出直前に sessions[sessionId]?.process === process を再確認してから送出することを推奨します。

Comment on lines +407 to +409

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · low]
sessions マップからの削除(synchronized ブロック)の後に、ロック外で SessionError/SessionIdle を送出しています。reader のクリーンアップ処理中に別スレッドの send() が同じ sessionId で新しいプロセスを作成した場合、古いプロセスの失敗イベントが新しい実行に誤って紐づき、誤ったエラー表示や早期の idle 通知を引き起こす可能性があります。イベント送出を削除と同一の同期ブロック内で行うか、送出直前に sessions[sessionId]?.process === process を再確認してから送出することを推奨します。

events.tryEmit(OpenCodeEvent.SessionIdle(sessionId))
}
Comment on lines +405 to +411

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · high]
ここで parser.turnFinishedtrue(result 行を受信後にプロセス終了)の場合、streamFailure が null 以外でも SessionError はまったく発行されません。しかし handleLine はパースされたイベントだけで SessionError を emit するため、turnFinished == trueis_error の result 行を受信した場合でも、ここではエラーも idle も送出されません。この場合、呼び出し側でチャットがスピナー状態のままになる可能性があります。parser.turnFinished に加えて streamFailure やエラーフラグも考慮して終了処理を判断すべきです。

}

return SessionProcess(process, readerJob, effectiveMode, directory, model, effort)
return SessionProcess(process, readerJob, parser, permissionMode, directory, model, effort)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · high]
SessionProcess に保存するモードが effectiveMode(実際にプロセス起動に使用したモード)から permissionMode(リクエスト元のモード)に変更されています。しかし ensureProcess の再利用判定(existing.permissionMode == permissionMode)は保存値と要求値を比較し、プロセスは effectiveMode で起動されます。ブリッジ未導入時に permissionMode = ASKeffectiveMode = ACCEPT_EDITS でプロセスを作成した後、ブリッジが導入されて再び ASK で送信すると、比較が ASK == ASK で一致してしまい、ACCEPT_EDITS のままの古いプロセスが再利用されます。本来は effectiveMode が昇格するため再起動すべきであり、「permission mode は起動時に一度だけ読み込まれる」という上のコメントとも矛盾します。起動時に実際に使用した effectiveMode を保存してください。

Suggestion:

Suggested change
return SessionProcess(process, readerJob, parser, permissionMode, directory, model, effort)
return SessionProcess(process, readerJob, parser, effectiveMode, directory, model, effort)

.also { sessions[sessionId] = it }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ interface ClaudeMessages {

fun signInExited(exitCode: Int): String

fun processExited(
exitCode: Int?,
detail: String?,
): String

/** English fallbacks for unit tests and any construction path without a [Context]. */
companion object Default : ClaudeMessages {
override val runtimeMissing = "The Linux environment is not installed yet"
Expand All @@ -29,6 +34,14 @@ interface ClaudeMessages {
override val updateFailed = "Claude Code update failed"

override fun signInExited(exitCode: Int) = "Claude Code sign-in stopped (exit code $exitCode)"

override fun processExited(
exitCode: Int?,
detail: String?,
): String {
val cause = detail ?: exitCode?.let { "exit code $it" } ?: "process exited"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[maintainability · low]
cause の計算ロジック(detail ?: exitCode?.let ... ?: process exited)が Default 実装と AndroidClaudeMessages 実装の両方に重複しています。今後文言を変更する際に片方だけ修正されるリスクがあるため、共通ヘルパー関数に抽出して再利用することを推奨します。

return "Claude Code stopped before finishing the turn ($cause)"
}
}
}

Expand All @@ -41,4 +54,12 @@ class AndroidClaudeMessages(private val context: Context) : ClaudeMessages {
override val updateFailed get() = context.getString(R.string.claude_error_update_failed)

override fun signInExited(exitCode: Int): String = context.getString(R.string.claude_error_sign_in_exit, exitCode)

override fun processExited(
exitCode: Int?,
detail: String?,
): String {
val cause = detail ?: exitCode?.let { "exit code $it" } ?: "process exited"
return context.getString(R.string.claude_error_process_exited, cause)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[other · low]
exit code や process exited という英語文字列が翻訳済みリソースの %1$s にそのまま埋め込まれます。多言語対応のアプリのため、日本語などの表示時にも英語が混在してしまいます。文言全体をリソース側に寄せる(例: exitCode を個別フォーマット引数として渡す)ことを検討してください。

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ class ClaudeStreamJsonParser(

private var currentMessageId: String? = null

@Volatile
var turnFinished: Boolean = false
private set
Comment on lines +48 to +50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[maintainability · low]
Parsed.turnFinished フィールド(42 行目)とこの @Volatile プロパティ turnFinished でターン完了状態が二重管理されています。どちらも parseResult 内で true に設定しており、将来の変更で不整合が生じやすくなっています。Parsed.turnFinished を廃止して本プロパティに一本化するか、プロパティを Parsed から導出する形にするなど、単一の情報源に整理することをお勧めします。

Comment on lines +48 to +50

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[maintainability · low]
Parsed.turnFinished フィールド(42 行目)とこの @Volatile プロパティ turnFinished でターン完了状態が二重管理されています。どちらも parseResult 内で true に設定しており、将来の変更で不整合が生じやすくなっています。Parsed.turnFinished を廃止して本プロパティに一本化するか、プロパティを Parsed から導出する形にして、単一の情報源に整理することをお勧めします。


fun beginTurn() {
turnFinished = false
}
Comment on lines +52 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · medium]
beginTurn()turnFinished を無条件に false へリセットしますが、send() が書き込み失敗(broken pipe 等)で失敗したケースでは、send()onFailureSessionError/SessionIdle を送出した後も turnFinished == false のままになります。すると reader ジョブのプロセス終了処理(ClaudeCodeRuntimeif (!parser.turnFinished) 分岐)でも同じ SessionError/SessionIdle が再送出され、エラー/完了イベントが重複します。送信失敗パスで turnFinished を true にする、あるいはターン完了イベントの送出元を一箇所に集約し、二重送出を防いでください。

Comment on lines +52 to +54

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[bug · medium]
beginTurn()turnFinished を無条件に false へリセットしますが、ClaudeCodeRuntime.send() が書き込み失敗(broken pipe 等)で失敗した場合、onFailureSessionError/SessionIdle を送出した後に turnFinished が false のまま残ります。すると reader ジョブのプロセス終了処理(ClaudeCodeRuntimeif (!parser.turnFinished) 分岐)でも同じ SessionError/SessionIdle が再送出され、エラー/完了イベントが重複するリスクがあります。送信失敗パスでも turnFinished を true にするか、ターン終了イベントの送出元を一箇所に集約して二重送出を防いでください。


/** Tool calls that have not received a matching tool_result from Claude Code yet. */
private val openTools = linkedMapOf<String, OpenTool>()
private val messagesById = linkedMapOf<String, OpenCodeMessage>()
Expand Down Expand Up @@ -157,6 +165,7 @@ class ClaudeStreamJsonParser(
}

private fun parseResult(root: JsonObject): Parsed {
turnFinished = true
val claudeSessionId = root.string("session_id")
val isError = root["is_error"]?.jsonPrimitive?.contentOrNull == "true"
val subtype = root.string("subtype")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ fun AndCodeApp(
onPermissionResolved = app.activityRepository::resolvePermission,
onQuestionResolved = app.notifications::cancelQuestion,
onSessionCreated = app.catalogRepository::refreshSessionsOnly,
onSessionAborted = app.activityRepository::markSessionAborted,
onRunStateChanged = { sessionId, running ->
if (running) {
app.activityRepository.markSessionRunning(sessionId)
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ar/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">تعذّر إرسال الرمز</string>
<string name="claude_error_install_failed">فشل تثبيت Claude Code</string>
<string name="claude_error_update_failed">فشل تحديث Claude Code</string>
<string name="claude_error_process_exited">توقف Claude Code قبل إنهاء الدور (%1$s)</string>
<string name="setup_download_agents_description">تحضير بيئة Linux المشتركة والوكلاء الذين اخترتهم.</string>
<string name="settings_agents_row">الوكلاء</string>
<string name="settings_agents_section">الوكلاء</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-es/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">No se pudo enviar el código</string>
<string name="claude_error_install_failed">Falló la instalación de Claude Code</string>
<string name="claude_error_update_failed">Falló la actualización de Claude Code</string>
<string name="claude_error_process_exited">Claude Code se detuvo antes de terminar el turno (%1$s)</string>
<string name="setup_download_agents_description">Prepara el entorno Linux compartido y los agentes que eligió.</string>
<string name="settings_agents_row">Agentes</string>
<string name="settings_agents_section">Agentes</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-fr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">Impossible d’envoyer le code</string>
<string name="claude_error_install_failed">L’installation de Claude Code a échoué</string>
<string name="claude_error_update_failed">La mise à jour de Claude Code a échoué</string>
<string name="claude_error_process_exited">Claude Code s’est arrêté avant de terminer le tour (%1$s)</string>
<string name="setup_download_agents_description">Prépare l’environnement Linux partagé et les agents que vous avez choisis.</string>
<string name="settings_agents_row">Agents</string>
<string name="settings_agents_section">Agents</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ja/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@
<string name="claude_error_submit_code">コードを送信できませんでした</string>
<string name="claude_error_install_failed">Claude Codeのインストールに失敗しました</string>
<string name="claude_error_update_failed">Claude Codeの更新に失敗しました</string>
<string name="claude_error_process_exited">Claude Codeがターンを完了する前に停止しました(%1$s)</string>
<string name="setup_download_agents_description">共有のLinux環境と、選んだエージェントを準備します。</string>
<string name="settings_agents_row">エージェント</string>
<string name="settings_agents_section">エージェント</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-pt-rBR/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">Não foi possível enviar o código</string>
<string name="claude_error_install_failed">A instalação do Claude Code falhou</string>
<string name="claude_error_update_failed">A atualização do Claude Code falhou</string>
<string name="claude_error_process_exited">O Claude Code parou antes de concluir o turno (%1$s)</string>
<string name="setup_download_agents_description">Prepara o ambiente Linux compartilhado e os agentes escolhidos.</string>
<string name="settings_agents_row">Agentes</string>
<string name="settings_agents_section">Agentes</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">Не удалось отправить код</string>
<string name="claude_error_install_failed">Не удалось установить Claude Code</string>
<string name="claude_error_update_failed">Не удалось обновить Claude Code</string>
<string name="claude_error_process_exited">Claude Code остановился, не завершив ход (%1$s)</string>
<string name="setup_download_agents_description">Подготовка общей среды Linux и выбранных агентов.</string>
<string name="settings_agents_row">Агенты</string>
<string name="settings_agents_section">Агенты</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values-zh-rCN/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,7 @@
<string name="claude_error_submit_code">无法提交代码</string>
<string name="claude_error_install_failed">Claude Code 安装失败</string>
<string name="claude_error_update_failed">Claude Code 更新失败</string>
<string name="claude_error_process_exited">Claude Code 在完成本轮前退出(%1$s)</string>
<string name="setup_download_agents_description">准备共享的 Linux 环境以及你选择的智能体。</string>
<string name="settings_agents_row">智能体</string>
<string name="settings_agents_section">智能体</string>
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -943,6 +943,7 @@
<string name="claude_error_submit_code">Could not submit the code</string>
<string name="claude_error_install_failed">Claude Code installation failed</string>
<string name="claude_error_update_failed">Claude Code update failed</string>
<string name="claude_error_process_exited">Claude Code stopped before finishing the turn (%1$s)</string>
<string name="setup_download_agents_description">Prepare the shared Linux environment and the agents you picked.</string>
<string name="settings_agents_row">Agents</string>
<string name="settings_agents_section">Agents</string>
Expand Down
Loading
Loading