mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IJPL-233558 extract codex session support
GitOrigin-RevId: d6844015bdfa7e74935877310f518d8cd65ec740
This commit is contained in:
committed by
intellij-monorepo-bot
parent
52c122642d
commit
d673f2a758
Generated
+1
@@ -50,6 +50,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/chat/intellij.agent.workbench.chat.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/chat/intellij.agent.workbench.chat.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/claude/common/intellij.agent.workbench.claude.common.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/claude/common/intellij.agent.workbench.claude.common.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/codex/common/intellij.agent.workbench.codex.common.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/codex/common/intellij.agent.workbench.codex.common.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/codex/sessions/intellij.agent.workbench.codex.sessions.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/codex/sessions/intellij.agent.workbench.codex.sessions.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/plugin/intellij.agent.workbench.plugin.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/plugin/intellij.agent.workbench.plugin.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/agent-workbench/sessions/intellij.agent.workbench.sessions.iml" filepath="$PROJECT_DIR$/plugins/agent-workbench/sessions/intellij.agent.workbench.sessions.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/android/android-adb/intellij.android.adb.iml" filepath="$PROJECT_DIR$/android/android-adb/intellij.android.adb.iml" />
|
||||
|
||||
@@ -877,6 +877,7 @@ plugins/IntelliLang/tests
|
||||
plugins/agent-workbench/chat
|
||||
plugins/agent-workbench/claude/common
|
||||
plugins/agent-workbench/codex/common
|
||||
plugins/agent-workbench/codex/sessions
|
||||
plugins/agent-workbench/plugin
|
||||
plugins/agent-workbench/sessions
|
||||
plugins/ant
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
### auto-generated section `build intellij.agent.workbench.codex.sessions` start
|
||||
load("@rules_jvm//:jvm.bzl", "jvm_library", "resourcegroup")
|
||||
|
||||
resourcegroup(
|
||||
name = "sessions_resources",
|
||||
srcs = glob(["resources/**/*"]),
|
||||
strip_prefix = "resources"
|
||||
)
|
||||
|
||||
jvm_library(
|
||||
name = "sessions",
|
||||
module_name = "intellij.agent.workbench.codex.sessions",
|
||||
visibility = ["//visibility:public"],
|
||||
srcs = glob(["src/**/*.kt", "src/**/*.java", "src/**/*.form"], allow_empty = True),
|
||||
resources = [":sessions_resources"],
|
||||
deps = [
|
||||
"@lib//:kotlin-stdlib",
|
||||
"//platform/core-api:core",
|
||||
"//platform/projectModel-api:projectModel",
|
||||
"//platform/util",
|
||||
"//platform/platform-api:ide",
|
||||
"//platform/platform-util-io:ide-util-io",
|
||||
"//plugins/agent-workbench/codex/common",
|
||||
"//libraries/jackson/jackson",
|
||||
]
|
||||
)
|
||||
|
||||
jvm_library(
|
||||
name = "sessions_test_lib",
|
||||
visibility = ["//visibility:public"],
|
||||
srcs = glob(["testSrc/**/*.kt", "testSrc/**/*.java", "testSrc/**/*.form"], allow_empty = True),
|
||||
associates = [":sessions"],
|
||||
deps = [
|
||||
"@lib//:kotlin-stdlib",
|
||||
"//platform/core-api:core",
|
||||
"//platform/projectModel-api:projectModel",
|
||||
"//platform/util",
|
||||
"//platform/platform-api:ide",
|
||||
"//platform/platform-util-io:ide-util-io",
|
||||
"//plugins/agent-workbench/codex/common",
|
||||
"//libraries/jackson/jackson",
|
||||
"//libraries/junit5",
|
||||
"//platform/testFramework",
|
||||
"//platform/testFramework:testFramework_test_lib",
|
||||
"//platform/testFramework/junit5",
|
||||
"//platform/testFramework/junit5:junit5_test_lib",
|
||||
"//libraries/assertj-core",
|
||||
]
|
||||
)
|
||||
### auto-generated section `build intellij.agent.workbench.codex.sessions` end
|
||||
|
||||
### auto-generated section `test intellij.agent.workbench.codex.sessions` start
|
||||
load("@community//build:tests-options.bzl", "jps_test")
|
||||
|
||||
jps_test(
|
||||
name = "sessions_test",
|
||||
runtime_deps = [":sessions_test_lib"]
|
||||
)
|
||||
### auto-generated section `test intellij.agent.workbench.codex.sessions` end
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" packagePrefix="com.intellij.agent.workbench.codex.sessions" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/testSrc" isTestSource="true" packagePrefix="com.intellij.agent.workbench.codex.sessions" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="kotlin-stdlib" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core" />
|
||||
<orderEntry type="module" module-name="intellij.platform.projectModel" />
|
||||
<orderEntry type="module" module-name="intellij.platform.util" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />
|
||||
<orderEntry type="module" module-name="intellij.agent.workbench.codex.common" />
|
||||
<orderEntry type="module" module-name="intellij.libraries.jackson" />
|
||||
<orderEntry type="module" module-name="intellij.libraries.junit5" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework.junit5" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.libraries.assertj.core" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
<idea-plugin>
|
||||
<!-- region Generated dependencies - run `Generate Product Layouts` to regenerate -->
|
||||
<dependencies>
|
||||
<module name="intellij.agent.workbench.codex.common"/>
|
||||
</dependencies>
|
||||
<!-- endregion -->
|
||||
|
||||
</idea-plugin>
|
||||
@@ -0,0 +1,32 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.agent.workbench.codex.common.normalizeRootPath
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlin.io.path.invariantSeparatorsPathString
|
||||
|
||||
class CodexAppServerSessionBackend : CodexSessionBackend {
|
||||
override suspend fun listThreads(path: String, @Suppress("UNUSED_PARAMETER") openProject: Project?): List<CodexBackendThread> {
|
||||
val workingDirectory = resolveProjectDirectoryFromPath(path) ?: return emptyList()
|
||||
val codexService = service<SharedCodexAppServerService>()
|
||||
val threads = codexService.listThreads(workingDirectory)
|
||||
return threads.map { CodexBackendThread(it) }
|
||||
}
|
||||
|
||||
override suspend fun prefetchThreads(paths: List<String>): Map<String, List<CodexBackendThread>> {
|
||||
if (paths.isEmpty()) return emptyMap()
|
||||
val codexService = service<SharedCodexAppServerService>()
|
||||
val allThreads = codexService.listAllThreads()
|
||||
val pathToCwd = paths.mapNotNull { path ->
|
||||
resolveProjectDirectoryFromPath(path)?.let { dir ->
|
||||
path to normalizeRootPath(dir.invariantSeparatorsPathString)
|
||||
}
|
||||
}
|
||||
return pathToCwd.associate { (path, cwdFilter) ->
|
||||
val matching = allThreads.filter { it.cwd == cwdFilter }
|
||||
path to matching.map { CodexBackendThread(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions.codex
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.openapi.diagnostic.debug
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
@@ -77,3 +77,4 @@ internal fun registerShutdownOnCancellation(scope: CoroutineScope, onShutdown: (
|
||||
onShutdown()
|
||||
}
|
||||
}
|
||||
|
||||
+12
-8
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions.providers.codex
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
// @spec community/plugins/agent-workbench/spec/agent-sessions-codex-rollout-source.spec.md
|
||||
|
||||
@@ -9,8 +9,6 @@ import com.fasterxml.jackson.core.JsonToken
|
||||
import com.intellij.agent.workbench.codex.common.CodexThread
|
||||
import com.intellij.agent.workbench.codex.common.forEachObjectField
|
||||
import com.intellij.agent.workbench.codex.common.readStringOrNull
|
||||
import com.intellij.agent.workbench.sessions.AgentSessionActivity
|
||||
import com.intellij.agent.workbench.sessions.codex.resolveProjectDirectoryFromPath
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
@@ -24,7 +22,7 @@ private const val ROLLOUT_FILE_PREFIX = "rollout-"
|
||||
private const val ROLLOUT_FILE_SUFFIX = ".jsonl"
|
||||
private const val MAX_TITLE_LENGTH = 120
|
||||
|
||||
internal class CodexRolloutSessionBackend(
|
||||
class CodexRolloutSessionBackend(
|
||||
private val codexHomeProvider: () -> Path = { Path.of(System.getProperty("user.home"), ".codex") },
|
||||
) : CodexSessionBackend {
|
||||
private val jsonFactory = JsonFactory()
|
||||
@@ -97,6 +95,7 @@ internal class CodexRolloutSessionBackend(
|
||||
pendingUserInputAt = null
|
||||
}
|
||||
}
|
||||
|
||||
"agent_message" -> {
|
||||
latestAgentMessageAt = maxTimestamp(latestAgentMessageAt, eventTimestamp)
|
||||
}
|
||||
@@ -122,6 +121,7 @@ internal class CodexRolloutSessionBackend(
|
||||
pendingUserInputAt = null
|
||||
}
|
||||
}
|
||||
|
||||
"assistant" -> {
|
||||
latestAgentMessageAt = maxTimestamp(latestAgentMessageAt, eventTimestamp)
|
||||
}
|
||||
@@ -143,10 +143,10 @@ internal class CodexRolloutSessionBackend(
|
||||
val hasUnread = latestAgentMessageAt > latestUserMessageAt
|
||||
val hasPendingUserInput = pendingUserInputAt != null
|
||||
val activity = when {
|
||||
hasPendingUserInput || hasUnread -> AgentSessionActivity.UNREAD
|
||||
reviewing -> AgentSessionActivity.REVIEWING
|
||||
processing -> AgentSessionActivity.PROCESSING
|
||||
else -> AgentSessionActivity.READY
|
||||
hasPendingUserInput || hasUnread -> CodexSessionActivity.UNREAD
|
||||
reviewing -> CodexSessionActivity.REVIEWING
|
||||
processing -> CodexSessionActivity.PROCESSING
|
||||
else -> CodexSessionActivity.READY
|
||||
}
|
||||
|
||||
val fallbackUpdatedAt = runCatching { Files.getLastModifiedTime(path).toMillis() }.getOrDefault(0L)
|
||||
@@ -198,9 +198,11 @@ internal class CodexRolloutSessionBackend(
|
||||
"git" -> {
|
||||
gitBranch = parseNestedStringField(parser, "branch")
|
||||
}
|
||||
|
||||
"item" -> {
|
||||
itemType = parseNestedStringField(parser, "type")
|
||||
}
|
||||
|
||||
else -> parser.skipChildren()
|
||||
}
|
||||
true
|
||||
@@ -210,6 +212,7 @@ internal class CodexRolloutSessionBackend(
|
||||
parser.skipChildren()
|
||||
}
|
||||
}
|
||||
|
||||
else -> parser.skipChildren()
|
||||
}
|
||||
true
|
||||
@@ -304,3 +307,4 @@ private fun maxTimestamp(current: Long, candidate: Long?): Long {
|
||||
if (candidate == null) return current
|
||||
return if (candidate > current) candidate else current
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.agent.workbench.codex.common.CodexThread
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
data class CodexBackendThread(
|
||||
val thread: CodexThread,
|
||||
val activity: CodexSessionActivity = CodexSessionActivity.READY,
|
||||
)
|
||||
|
||||
enum class CodexSessionActivity {
|
||||
UNREAD,
|
||||
REVIEWING,
|
||||
PROCESSING,
|
||||
READY,
|
||||
}
|
||||
|
||||
interface CodexSessionBackend {
|
||||
suspend fun listThreads(path: String, openProject: Project?): List<CodexBackendThread>
|
||||
|
||||
/**
|
||||
* Prefetch threads for multiple paths in a single backend call.
|
||||
* Returns a map of path to threads. Empty map means no prefetch (use per-path calls).
|
||||
*/
|
||||
suspend fun prefetchThreads(paths: List<String>): Map<String, List<CodexBackendThread>> = emptyMap()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
private const val BACKEND_OVERRIDE_PROPERTY = "agent.workbench.codex.sessions.backend"
|
||||
private const val APP_SERVER_BACKEND = "app-server"
|
||||
|
||||
fun createDefaultCodexSessionBackend(): CodexSessionBackend {
|
||||
return when (System.getProperty(BACKEND_OVERRIDE_PROPERTY)) {
|
||||
APP_SERVER_BACKEND -> CodexAppServerSessionBackend()
|
||||
else -> CodexRolloutSessionBackend()
|
||||
}
|
||||
}
|
||||
|
||||
+5
-4
@@ -1,5 +1,5 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions.codex
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.agent.workbench.codex.common.CodexAppServerClient
|
||||
import com.intellij.agent.workbench.codex.common.CodexThread
|
||||
@@ -10,19 +10,19 @@ import java.nio.file.Path
|
||||
import kotlin.io.path.invariantSeparatorsPathString
|
||||
|
||||
@Service(Service.Level.APP)
|
||||
internal class SharedCodexAppServerService(serviceScope: CoroutineScope) {
|
||||
class SharedCodexAppServerService(serviceScope: CoroutineScope) {
|
||||
private val client = CodexAppServerClient(coroutineScope = serviceScope)
|
||||
|
||||
init {
|
||||
registerShutdownOnCancellation(serviceScope) { client.shutdown() }
|
||||
}
|
||||
|
||||
suspend fun listThreads(projectPath: Path): List<CodexThread> {
|
||||
internal suspend fun listThreads(projectPath: Path): List<CodexThread> {
|
||||
val cwdFilter = normalizeRootPath(projectPath.invariantSeparatorsPathString)
|
||||
return client.listThreads(archived = false, cwdFilter = cwdFilter)
|
||||
}
|
||||
|
||||
suspend fun listAllThreads(): List<CodexThread> {
|
||||
internal suspend fun listAllThreads(): List<CodexThread> {
|
||||
return client.listThreads(archived = false, cwdFilter = null)
|
||||
}
|
||||
|
||||
@@ -37,3 +37,4 @@ internal class SharedCodexAppServerService(serviceScope: CoroutineScope) {
|
||||
return thread
|
||||
}
|
||||
}
|
||||
|
||||
+18
-19
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.agent.workbench.sessions.codex.registerShutdownOnCancellation
|
||||
import com.intellij.agent.workbench.sessions.codex.resolveProjectDirectory
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
@@ -15,6 +13,7 @@ import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.io.TempDir
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
class CodexProjectSessionServiceTest {
|
||||
@TempDir
|
||||
@@ -27,13 +26,13 @@ class CodexProjectSessionServiceTest {
|
||||
val scope = CoroutineScope(coroutineContext + Job(parentJob))
|
||||
val shutdown = CompletableDeferred<Unit>()
|
||||
|
||||
registerShutdownOnCancellation(scope) {
|
||||
shutdown.complete(Unit)
|
||||
}
|
||||
registerShutdownOnCancellation(scope) {
|
||||
shutdown.complete(Unit)
|
||||
}
|
||||
|
||||
scope.cancel()
|
||||
|
||||
withTimeout(1_000) {
|
||||
withTimeout(1.seconds) {
|
||||
shutdown.await()
|
||||
}
|
||||
}
|
||||
@@ -47,10 +46,10 @@ class CodexProjectSessionServiceTest {
|
||||
Files.writeString(miscXml, "<project />")
|
||||
|
||||
val resolved = resolveProjectDirectory(
|
||||
recentProjectPath = miscXml,
|
||||
projectFilePath = null,
|
||||
basePath = null,
|
||||
guessedProjectDir = null,
|
||||
recentProjectPath = miscXml,
|
||||
projectFilePath = null,
|
||||
basePath = null,
|
||||
guessedProjectDir = null,
|
||||
)
|
||||
|
||||
assertThat(resolved).isEqualTo(projectRoot)
|
||||
@@ -64,10 +63,10 @@ class CodexProjectSessionServiceTest {
|
||||
Files.writeString(iprFile, "<project></project>")
|
||||
|
||||
val resolved = resolveProjectDirectory(
|
||||
recentProjectPath = null,
|
||||
projectFilePath = iprFile.toString(),
|
||||
basePath = null,
|
||||
guessedProjectDir = null,
|
||||
recentProjectPath = null,
|
||||
projectFilePath = iprFile.toString(),
|
||||
basePath = null,
|
||||
guessedProjectDir = null,
|
||||
)
|
||||
|
||||
assertThat(resolved).isEqualTo(projectRoot)
|
||||
@@ -79,10 +78,10 @@ class CodexProjectSessionServiceTest {
|
||||
Files.createDirectories(projectRoot)
|
||||
|
||||
val resolved = resolveProjectDirectory(
|
||||
recentProjectPath = null,
|
||||
projectFilePath = null,
|
||||
basePath = null,
|
||||
guessedProjectDir = projectRoot,
|
||||
recentProjectPath = null,
|
||||
projectFilePath = null,
|
||||
basePath = null,
|
||||
guessedProjectDir = projectRoot,
|
||||
)
|
||||
|
||||
assertThat(resolved).isEqualTo(projectRoot)
|
||||
+7
-7
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions
|
||||
package com.intellij.agent.workbench.codex.sessions
|
||||
|
||||
import com.intellij.agent.workbench.sessions.providers.codex.CodexRolloutSessionBackend
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -42,7 +41,7 @@ class CodexRolloutSessionBackendTest {
|
||||
assertThat(thread.thread.id).isEqualTo("session-abc")
|
||||
assertThat(thread.thread.title).isEqualTo("Fix flaky test")
|
||||
assertThat(thread.thread.updatedAt).isEqualTo(Instant.parse("2026-02-13T10:00:30.000Z").toEpochMilli())
|
||||
assertThat(thread.activity).isEqualTo(AgentSessionActivity.UNREAD)
|
||||
assertThat(thread.activity).isEqualTo(CodexSessionActivity.UNREAD)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,7 +90,7 @@ class CodexRolloutSessionBackendTest {
|
||||
assertThat(threads).hasSize(1)
|
||||
val thread = threads.single()
|
||||
assertThat(thread.thread.id).isEqualTo("session-review")
|
||||
assertThat(thread.activity).isEqualTo(AgentSessionActivity.REVIEWING)
|
||||
assertThat(thread.activity).isEqualTo(CodexSessionActivity.REVIEWING)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +130,7 @@ class CodexRolloutSessionBackendTest {
|
||||
val threads = backend.listThreads(path = projectDir.toString(), openProject = null)
|
||||
|
||||
assertThat(threads.map { it.thread.id }).containsExactly("session-ready")
|
||||
assertThat(threads.single().activity).isEqualTo(AgentSessionActivity.READY)
|
||||
assertThat(threads.single().activity).isEqualTo(CodexSessionActivity.READY)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,7 +156,7 @@ class CodexRolloutSessionBackendTest {
|
||||
val threads = backend.listThreads(path = projectDir.toString(), openProject = null)
|
||||
|
||||
assertThat(threads).hasSize(1)
|
||||
assertThat(threads.single().activity).isEqualTo(AgentSessionActivity.PROCESSING)
|
||||
assertThat(threads.single().activity).isEqualTo(CodexSessionActivity.PROCESSING)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -183,7 +182,7 @@ class CodexRolloutSessionBackendTest {
|
||||
val threads = backend.listThreads(path = projectDir.toString(), openProject = null)
|
||||
|
||||
assertThat(threads).hasSize(1)
|
||||
assertThat(threads.single().activity).isEqualTo(AgentSessionActivity.UNREAD)
|
||||
assertThat(threads.single().activity).isEqualTo(CodexSessionActivity.UNREAD)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,3 +221,4 @@ private fun writeRollout(file: Path, lines: List<String>) {
|
||||
Files.createDirectories(file.parent)
|
||||
Files.write(file, lines)
|
||||
}
|
||||
|
||||
@@ -3,5 +3,6 @@
|
||||
- name: intellij.agent.workbench.plugin
|
||||
contentModules:
|
||||
- name: intellij.agent.workbench.codex.common
|
||||
- name: intellij.agent.workbench.codex.sessions
|
||||
- name: intellij.agent.workbench.sessions
|
||||
- name: intellij.agent.workbench
|
||||
|
||||
@@ -10,6 +10,9 @@
|
||||
- name: lib/modules/intellij.agent.workbench.codex.common.jar
|
||||
contentModules:
|
||||
- name: intellij.agent.workbench.codex.common
|
||||
- name: lib/modules/intellij.agent.workbench.codex.sessions.jar
|
||||
contentModules:
|
||||
- name: intellij.agent.workbench.codex.sessions
|
||||
- name: lib/modules/intellij.agent.workbench.sessions.jar
|
||||
contentModules:
|
||||
- name: intellij.agent.workbench.sessions
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
<content namespace="jetbrains">
|
||||
<module name="intellij.agent.workbench.claude.common"/>
|
||||
<module name="intellij.agent.workbench.codex.common"/>
|
||||
<module name="intellij.agent.workbench.codex.sessions"/>
|
||||
<module name="intellij.agent.workbench.sessions"/>
|
||||
<module name="intellij.agent.workbench.chat"/>
|
||||
</content>
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.junit.platform.suite.api.Suite
|
||||
@Suite
|
||||
@SelectPackages(
|
||||
"com.intellij.agent.workbench.chat",
|
||||
"com.intellij.agent.workbench.codex.sessions",
|
||||
"com.intellij.agent.workbench.sessions",
|
||||
)
|
||||
class AgentWorkbenchAllTestsSuite
|
||||
|
||||
@@ -27,6 +27,7 @@ jvm_library(
|
||||
"//platform/platform-util-io:ide-util-io",
|
||||
"//plugins/agent-workbench/claude/common",
|
||||
"//plugins/agent-workbench/codex/common",
|
||||
"//plugins/agent-workbench/codex/sessions",
|
||||
"//plugins/agent-workbench/chat",
|
||||
"//libraries/jackson/jackson",
|
||||
"@lib//:jna",
|
||||
@@ -55,6 +56,8 @@ jvm_library(
|
||||
"//platform/platform-util-io:ide-util-io",
|
||||
"//plugins/agent-workbench/claude/common",
|
||||
"//plugins/agent-workbench/codex/common",
|
||||
"//plugins/agent-workbench/codex/sessions",
|
||||
"//plugins/agent-workbench/codex/sessions:sessions_test_lib",
|
||||
"//plugins/agent-workbench/chat",
|
||||
"//plugins/agent-workbench/chat:chat_test_lib",
|
||||
"//libraries/jackson/jackson",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.util.io" />
|
||||
<orderEntry type="module" module-name="intellij.agent.workbench.claude.common" />
|
||||
<orderEntry type="module" module-name="intellij.agent.workbench.codex.common" />
|
||||
<orderEntry type="module" module-name="intellij.agent.workbench.codex.sessions" />
|
||||
<orderEntry type="module" module-name="intellij.agent.workbench.chat" />
|
||||
<orderEntry type="module" module-name="intellij.libraries.jackson" />
|
||||
<orderEntry type="library" name="jna" level="project" />
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
<!-- region Generated dependencies - run `Generate Product Layouts` to regenerate -->
|
||||
<module name="intellij.agent.workbench.claude.common"/>
|
||||
<module name="intellij.agent.workbench.codex.common"/>
|
||||
<module name="intellij.agent.workbench.codex.sessions"/>
|
||||
<module name="intellij.agent.workbench.chat"/>
|
||||
<module name="intellij.platform.compose"/>
|
||||
<!-- endregion -->
|
||||
|
||||
@@ -7,7 +7,7 @@ package com.intellij.agent.workbench.sessions
|
||||
|
||||
import com.intellij.agent.workbench.chat.AgentChatEditorService
|
||||
import com.intellij.agent.workbench.codex.common.CodexCliNotFoundException
|
||||
import com.intellij.agent.workbench.sessions.codex.SharedCodexAppServerService
|
||||
import com.intellij.agent.workbench.codex.sessions.SharedCodexAppServerService
|
||||
import com.intellij.agent.workbench.sessions.providers.AgentSessionSource
|
||||
import com.intellij.agent.workbench.sessions.providers.codex.CodexCliCommands
|
||||
import com.intellij.agent.workbench.sessions.providers.createDefaultAgentSessionSources
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// Copyright 2000-2026 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.agent.workbench.sessions.providers.codex
|
||||
|
||||
import com.intellij.agent.workbench.codex.common.CodexThread
|
||||
import com.intellij.agent.workbench.sessions.AgentSessionActivity
|
||||
import com.intellij.openapi.project.Project
|
||||
|
||||
internal data class CodexBackendThread(
|
||||
val thread: CodexThread,
|
||||
val activity: AgentSessionActivity = AgentSessionActivity.READY,
|
||||
)
|
||||
|
||||
internal interface CodexSessionBackend {
|
||||
suspend fun listThreads(path: String, openProject: Project?): List<CodexBackendThread>
|
||||
}
|
||||
|
||||
@@ -5,42 +5,38 @@ package com.intellij.agent.workbench.sessions.providers.codex
|
||||
// @spec community/plugins/agent-workbench/spec/agent-sessions-codex-rollout-source.spec.md
|
||||
|
||||
import com.intellij.agent.workbench.codex.common.CodexThread
|
||||
import com.intellij.agent.workbench.codex.common.normalizeRootPath
|
||||
import com.intellij.agent.workbench.codex.sessions.CodexBackendThread
|
||||
import com.intellij.agent.workbench.codex.sessions.CodexSessionActivity
|
||||
import com.intellij.agent.workbench.codex.sessions.CodexSessionBackend
|
||||
import com.intellij.agent.workbench.codex.sessions.createDefaultCodexSessionBackend
|
||||
import com.intellij.agent.workbench.sessions.AgentSessionActivity
|
||||
import com.intellij.agent.workbench.sessions.AgentSessionProvider
|
||||
import com.intellij.agent.workbench.sessions.AgentSessionThread
|
||||
import com.intellij.agent.workbench.sessions.AgentSubAgent
|
||||
import com.intellij.agent.workbench.sessions.codex.SharedCodexAppServerService
|
||||
import com.intellij.agent.workbench.sessions.codex.resolveProjectDirectoryFromPath
|
||||
import com.intellij.agent.workbench.sessions.providers.BaseAgentSessionSource
|
||||
import com.intellij.openapi.components.service
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlin.io.path.invariantSeparatorsPathString
|
||||
|
||||
internal class CodexSessionSource : BaseAgentSessionSource(provider = AgentSessionProvider.CODEX, canReportExactThreadCount = false) {
|
||||
internal class CodexSessionSource(
|
||||
private val backend: CodexSessionBackend = createDefaultCodexSessionBackend(),
|
||||
) : BaseAgentSessionSource(provider = AgentSessionProvider.CODEX, canReportExactThreadCount = false) {
|
||||
override suspend fun listThreads(path: String, openProject: Project?): List<AgentSessionThread> {
|
||||
val workingDirectory = resolveProjectDirectoryFromPath(path) ?: return emptyList()
|
||||
val codexService = service<SharedCodexAppServerService>()
|
||||
val threads = codexService.listThreads(workingDirectory)
|
||||
return threads.map { it.toAgentSessionThread() }
|
||||
return backend.listThreads(path = path, openProject = openProject).map { it.toAgentSessionThread() }
|
||||
}
|
||||
|
||||
override suspend fun prefetchThreads(paths: List<String>): Map<String, List<AgentSessionThread>> {
|
||||
if (paths.isEmpty()) return emptyMap()
|
||||
val codexService = service<SharedCodexAppServerService>()
|
||||
val allThreads = codexService.listAllThreads()
|
||||
val pathToCwd = paths.mapNotNull { path ->
|
||||
resolveProjectDirectoryFromPath(path)?.let { dir ->
|
||||
path to normalizeRootPath(dir.invariantSeparatorsPathString)
|
||||
}
|
||||
}
|
||||
return pathToCwd.associate { (path, cwdFilter) ->
|
||||
val matching = allThreads.filter { it.cwd == cwdFilter }
|
||||
path to matching.map { it.toAgentSessionThread() }
|
||||
val prefetched = backend.prefetchThreads(paths)
|
||||
if (prefetched.isEmpty()) return emptyMap()
|
||||
return prefetched.mapValues { (_, threads) ->
|
||||
threads.map { it.toAgentSessionThread() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun CodexThread.toAgentSessionThread(): AgentSessionThread {
|
||||
private fun CodexBackendThread.toAgentSessionThread(): AgentSessionThread {
|
||||
return thread.toAgentSessionThread(activity = activity)
|
||||
}
|
||||
|
||||
private fun CodexThread.toAgentSessionThread(activity: CodexSessionActivity): AgentSessionThread {
|
||||
return AgentSessionThread(
|
||||
id = id,
|
||||
title = title,
|
||||
@@ -49,5 +45,15 @@ private fun CodexThread.toAgentSessionThread(): AgentSessionThread {
|
||||
provider = AgentSessionProvider.CODEX,
|
||||
subAgents = subAgents.map { AgentSubAgent(it.id, it.name) },
|
||||
originBranch = gitBranch,
|
||||
activity = activity.toAgentSessionActivity(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun CodexSessionActivity.toAgentSessionActivity(): AgentSessionActivity {
|
||||
return when (this) {
|
||||
CodexSessionActivity.UNREAD -> AgentSessionActivity.UNREAD
|
||||
CodexSessionActivity.REVIEWING -> AgentSessionActivity.REVIEWING
|
||||
CodexSessionActivity.PROCESSING -> AgentSessionActivity.PROCESSING
|
||||
CodexSessionActivity.READY -> AgentSessionActivity.READY
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ targets:
|
||||
- ../../sessions/src/SessionTreeRows.kt
|
||||
- ../../sessions/src/AgentSessionsToolWindow.kt
|
||||
- ../../sessions/src/AgentSessionsService.kt
|
||||
- ../../sessions/src/codex/SharedCodexAppServerService.kt
|
||||
- ../../codex/sessions/src/SharedCodexAppServerService.kt
|
||||
- ../../sessions/src/AgentSessionCli.kt
|
||||
- ../../sessions/src/providers/codex/CodexCliCommands.kt
|
||||
- ../../sessions/resources/messages/AgentSessionsBundle.properties
|
||||
@@ -81,4 +81,3 @@ Define project/worktree `New Session` actions (`+` and quick-provider icon) and
|
||||
- `../agent-sessions.spec.md`
|
||||
- `../agent-sessions-codex-rollout-source.spec.md`
|
||||
- `../agent-dedicated-frame.spec.md`
|
||||
|
||||
|
||||
@@ -3,10 +3,12 @@ name: Codex Sessions Rollout Source
|
||||
description: Codex thread list source and activity indicators for Agent Threads.
|
||||
targets:
|
||||
- ../sessions/src/providers/codex/*.kt
|
||||
- ../codex/sessions/src/*.kt
|
||||
- ../sessions/src/SessionTreeStyle.kt
|
||||
- ../sessions/src/AgentSessionModels.kt
|
||||
- ../sessions/src/AgentSessionsService.kt
|
||||
- ../sessions/testSrc/*.kt
|
||||
- ../codex/sessions/testSrc/*.kt
|
||||
---
|
||||
|
||||
# Codex Sessions Rollout Source
|
||||
@@ -51,7 +53,7 @@ Codex thread discovery for Agent Threads defaults to rollout files under `~/.cod
|
||||
- `CodexSessionSource` maps rollout backend data directly and does not use `CodexSessionBranchStore` fallback.
|
||||
|
||||
## Testing / Local Run
|
||||
- `./tests.cmd '-Dintellij.build.test.patterns=com.intellij.agent.workbench.sessions.CodexRolloutSessionBackendTest'`
|
||||
- `./tests.cmd '-Dintellij.build.test.patterns=com.intellij.agent.workbench.codex.sessions.CodexRolloutSessionBackendTest'`
|
||||
- `./tests.cmd '-Dintellij.build.test.patterns=com.intellij.agent.workbench.sessions.AgentSessionCliTest'`
|
||||
|
||||
## References
|
||||
|
||||
Reference in New Issue
Block a user