diff --git a/docs/architecture/assistant-thread-information-architecture.md b/docs/architecture/assistant-thread-information-architecture.md
index c5213b79..42148f2b 100644
--- a/docs/architecture/assistant-thread-information-architecture.md
+++ b/docs/architecture/assistant-thread-information-architecture.md
@@ -1,6 +1,6 @@
# Assistant TaskThread 信息架构
-本文描述当前 XWorkmate 中,线程信息如何围绕 `TaskThread` 进入 UI、进入 controller / runtime 的执行请求构造、再通过 `Go Agent-core` 回写到 UI。
+本文描述当前 XWorkmate 中,线程信息如何围绕 `TaskThread` 进入 UI、进入 controller / runtime 的执行请求构造、再通过 `GoTaskService` 回写到 UI。
本文统一采用 `TaskThread` 聚合对象作为线程信息架构主语义。
@@ -57,7 +57,7 @@ TaskThread
- 当前线程执行模式
- provider / endpoint 绑定
-- 为 agent-core / runtime 协调层提供调度输入
+- 为 `GoTaskService / runtime` 协调层提供调度输入
### 2.4 contextState
@@ -98,7 +98,7 @@ flowchart LR
D3 --> E
D4 --> E
- E --> F["Go Agent-core\nDesktop: local bridge\nWeb: remote ACP / RPC"]
+ E --> F["GoTaskService\nDesktop: GatewayRuntime / ExternalCodeAgentAcpDesktopTransport\nWeb: relay / ExternalCodeAgentAcpWebTransport"]
F --> G["执行结果"]
G --> H["回写线程上下文\n(主体区域 同步显示)"]
@@ -111,7 +111,7 @@ flowchart LR
这张图表达的是当前线程信息架构,而不是旧的“工作目录 fallback 流程”:
- `读取 TaskThread` 是 UI 与执行层共享的唯一线程信息入口
-- `构造执行请求` 在 agent-core / runtime 协调层完成
+- `构造执行请求` 在 `GoTaskService / runtime` 协调层完成
- `右栏显示` 明确依赖 `TaskThread` 当前记录
- `workspaceBinding` 更新只允许发生在当前线程已完整的前提下
- prompt 中的 `workspace_root` side-channel 已退出主链;workspace 更新只允许来自 create/load 显式绑定或结构化执行结果回写
@@ -123,9 +123,9 @@ flowchart LR
| 当前线程身份 | `threadId` | UI 按 `threadId` 选中线程,再读取完整 `TaskThread` |
| owner 信息 | `ownerScope` | 线程归属、owner 展示与 remote owner path 推导 |
| 工作空间路径展示 | `workspaceBinding.displayPath` | 右栏当前路径展示 |
-| 执行工作空间 | `workspaceBinding.workspacePath` | agent-core / runtime 构造执行请求时使用 |
+| 执行工作空间 | `workspaceBinding.workspacePath` | `GoTaskService / runtime` 构造执行请求时使用 |
| 工作空间类型 | `workspaceBinding.workspaceKind` | 区分 `localFs / remoteFs` |
-| 执行模式 | `executionBinding.executionMode` | 映射 Go Agent-core 调度输入与 transport 选择 |
+| 执行模式 | `executionBinding.executionMode` | 映射 `GoTaskService` 调度输入与 transport 选择 |
| provider / endpoint | `executionBinding.providerId / endpointId` | 当前执行通道来源 |
| 消息历史 | `contextState.messages` | 主体区域消息列表 |
| 模型 | `contextState.selectedModelId` | 当前线程模型选择 |
@@ -143,7 +143,7 @@ flowchart LR
- UI 仍保持现有结构与呈现方式
- UI 不负责执行请求构造
-- controller / runtime 负责根据 `TaskThread` 构造请求并调用 `Go Agent-core`
+- controller / runtime 负责根据 `TaskThread` 构造请求并调用 `GoTaskService`
- 执行结果先回写线程上下文,主体区域同步显示
- 右栏显示与预览结果来自当前 `TaskThread` 最新记录
- Desktop / Web 共用同一套 session 语义,只保留 local bridge / remote ACP-RPC transport 差异
diff --git a/docs/architecture/assistant-thread-target-model-20260328.md b/docs/architecture/assistant-thread-target-model-20260328.md
index 3658c360..71310274 100644
--- a/docs/architecture/assistant-thread-target-model-20260328.md
+++ b/docs/architecture/assistant-thread-target-model-20260328.md
@@ -11,8 +11,9 @@
3. UI 选中线程后,系统必须读取完整 `TaskThread`,而不是从页面状态拼装线程信息。
4. `TaskThread` 持久化 schema 保持不变,但 `workspaceBinding` 在 create/load 时必须完整;缺失 binding 的旧记录按非法数据处理并跳过加载。
5. 执行请求由 controller / runtime 根据 `ownerScope / workspaceBinding / executionBinding / contextState` 构造。
-6. controller / runtime 统一通过 `Go Agent-core` 调度执行:Desktop 走 App 内 local bridge,Web 走远端 ACP / RPC endpoint。
+6. controller / runtime 统一通过 `GoTaskService` 调度执行:OpenClaw task 走 `TaskThread -> GoTaskService -> GatewayRuntime / Web relay -> OpenClaw gateway`;`singleAgent / multiAgent` 走 `TaskThread -> GoTaskService -> ExternalCodeAgentAcp* -> ACP/provider route`。
7. 执行结果先回写 `TaskThread.contextState`,主体区域同步显示;UI 与执行始终只读取当前 `TaskThread.workspaceBinding`,不再存在 runtime first-binding 或 fallback 到 `main`。
+8. `contextState` 是线程上下文真相源;`lifecycleState` 只表达生命周期摘要;controller 侧缓存不承载线程持久语义。
## 2. TaskThread 结构
@@ -77,7 +78,7 @@ ExecutionBinding
- 定义线程当前执行模式
- 定义 provider / endpoint 绑定
-- 为 agent-core / runtime 协调层提供调度输入
+- 为 `GoTaskService / runtime` 协调层提供调度输入
### 2.4 contextState
@@ -132,7 +133,7 @@ flowchart LR
D3 --> E
D4 --> E
- E --> F["Go Agent-core\nDesktop: local bridge\nWeb: remote ACP / RPC"]
+ E --> F["GoTaskService\nDesktop: GatewayRuntime / ExternalCodeAgentAcpDesktopTransport\nWeb: relay / ExternalCodeAgentAcpWebTransport"]
F --> G["执行结果"]
G --> H["回写线程上下文\n(主体区域 同步显示)"]
@@ -146,8 +147,8 @@ flowchart LR
1. UI 仍保持现有形态,但只负责选择 `threadId` 与消费回写结果。
2. 线程的执行输入来自完整 `TaskThread`。
-3. `构造执行请求` 属于 agent-core / runtime 协调层,不属于 UI。
-4. `Go Agent-core` 是唯一执行调度面;Desktop / Web 共用同一套 session 语义,只在 transport 上有差异。
+3. `构造执行请求` 属于 `GoTaskService / runtime` 协调层,不属于 UI。
+4. `GoTaskService` 是唯一执行调度面;Desktop / Web 共用同一套 session 语义,只在 transport 上有差异。
5. `回写线程上下文` 是执行结束后的第一落点;主体区域同步显示依赖这一回写。
6. `workspaceBinding` 不是运行时补齐对象;线程在 create/load 时必须已经完整。
7. `右栏显示` 与执行请求都读取当前 `TaskThread.workspaceBinding`,因此它与主体区域共享同一线程事实来源。
@@ -161,10 +162,10 @@ flowchart LR
- UI 不是工作空间推断器。
- UI 不是线程状态的独立真相源。
-### 4.2 agent-core / runtime 协调层约束
+### 4.2 GoTaskService / runtime 协调层约束
- 根据 `ownerScope / workspaceBinding / executionBinding / contextState` 构造执行请求。
-- 负责把线程请求调度到 `Go Agent-core`,而不是让 Flutter UI 直接承担 runtime 职责。
+- 负责把线程请求调度到 `GoTaskService`,而不是让 Flutter UI 直接承担 runtime 职责。
- 接收执行结果并驱动 `TaskThread` 回写。
### 4.3 TaskThread 约束
@@ -179,7 +180,7 @@ flowchart LR
- [task-thread-session-key-isolation-20260329.md](task-thread-session-key-isolation-20260329.md)
补充“任务线必须先成为真实 `TaskThread/sessionKey`”的隔离约束,说明为什么 single-agent 的工作目录只能围绕当前线程身份解析。
- [assistant-thread-information-architecture.md](assistant-thread-information-architecture.md)
- 说明线程信息如何进入 UI、agent-core / runtime 请求构造、结果回写和右栏展示。
+ 说明线程信息如何进入 UI、`GoTaskService / runtime` 请求构造、结果回写和右栏展示。
- [xworkmate-internal-state-architecture.md](xworkmate-internal-state-architecture.md)
说明控制器、状态存储和派生 UI 状态如何围绕 `TaskThread` 组织。
diff --git a/docs/architecture/task-thread-session-key-isolation-20260329.md b/docs/architecture/task-thread-session-key-isolation-20260329.md
index 20002253..a3adfb61 100644
--- a/docs/architecture/task-thread-session-key-isolation-20260329.md
+++ b/docs/architecture/task-thread-session-key-isolation-20260329.md
@@ -21,7 +21,7 @@ currentSessionKey
-> normalizedAssistantSessionKeyInternal(sessionKey)
-> assistantWorkspacePathForSession(sessionKey)
-> resolveSingleAgentWorkingDirectoryForSessionInternal(sessionKey)
--> GoAgentCoreSessionRequest.workingDirectory
+-> GoTaskServiceRequest.workingDirectory
```
这条链路说明:
@@ -201,7 +201,7 @@ flowchart LR
J --> L["executionBinding"]
J --> M["contextState"]
- K --> N["GoAgentCoreSessionRequest.workingDirectory"]
+ K --> N["GoTaskServiceRequest.workingDirectory"]
L --> O["provider / execution mode"]
M --> P["messages / model / skills"]
diff --git a/docs/architecture/xworkmate-internal-state-architecture.md b/docs/architecture/xworkmate-internal-state-architecture.md
index 5a13b6ae..c68bc0cb 100644
--- a/docs/architecture/xworkmate-internal-state-architecture.md
+++ b/docs/architecture/xworkmate-internal-state-architecture.md
@@ -8,11 +8,11 @@ Last Updated: 2026-03-29
- Settings 中心配置状态
- 当前 `TaskThread` 状态
-- agent-core / runtime 协调状态
+- `GoTaskService / runtime` 协调状态
- 派生 UI 状态
- 技能、模型、执行通道与会话内容
-本文以 Desktop 为主说明,因为 Desktop 控制器拥有最完整的运行时与持久化路径;Web 保持同一 `TaskThread` 与 session 语义,但 transport 走远端 ACP / RPC。
+本文以 Desktop 为主说明,因为 Desktop 控制器拥有最完整的运行时与持久化路径;Web 保持同一 `TaskThread` 与 session 语义,但 transport 走远端 ACP / relay。
## 1. Core Rule
@@ -58,10 +58,10 @@ graph TB
webCurrentThreadId["_currentThreadId"]
end
- subgraph R["Agent-Core / Runtime Coordination"]
+ subgraph R["GoTaskService / Runtime Coordination"]
threadReader["read TaskThread by threadId"]
requestBuilder["build execution request"]
- dispatcher["dispatch to Go Agent-core\nDesktop: local bridge\nWeb: remote ACP / RPC"]
+ dispatcher["dispatch to GoTaskService\nDesktop: GatewayRuntime / ExternalCodeAgentAcpDesktopTransport\nWeb: relay / ExternalCodeAgentAcpWebTransport"]
resultWriter["write result back to TaskThread"]
end
@@ -104,7 +104,7 @@ graph TB
- `TaskThread` 是线程主状态,不再由散落 session 字段共同充当
- `threadId` 是读取线程状态的唯一入口键
-- `build execution request` 属于 agent-core / runtime 协调层
+- `build execution request` 属于 `GoTaskService / runtime` 协调层
- UI 只消费当前 `TaskThread` 与派生状态
## 3. State Ownership
@@ -184,13 +184,13 @@ Ownership summary:
- `TaskThread` 在 create/load 时必须已经拥有完整 `workspaceBinding`
- 缺少 `workspaceBinding` 的旧记录属于非法线程数据,应在恢复阶段跳过并通过启动告警暴露
-### 3.3 Agent-Core / Runtime 协调状态
+### 3.3 GoTaskService / Runtime 协调状态
Primary responsibilities:
- 根据 `threadId` 读取完整 `TaskThread`
- 基于 `ownerScope / workspaceBinding / executionBinding / contextState` 构造执行请求
-- 调度到 `Go Agent-core`
+- 调度到 `GoTaskService`
- 接收执行结果并回写 `TaskThread`
重要规则:
@@ -200,6 +200,7 @@ Primary responsibilities:
- 工作空间选择不再通过旧式运行前猜测获得
- 不允许 runtime fallback 到 `main`、`Directory.current` 或 prompt first-binding
- 结果回写先更新线程上下文,再驱动主体区域与右栏刷新
+- controller 侧 runtime cache 只允许承载瞬时 streaming / pending / preview 状态,不承载线程长期语义
- Desktop / Web 共用相同 session 生命周期;不再单独发明 relay-only 执行协议
### 3.4 Derived UI State
@@ -244,7 +245,7 @@ Examples:
3. `executionBinding`
4. `contextState`
-然后由 agent-core / runtime 协调层构造执行请求并调度运行。
+然后由 `GoTaskService / runtime` 协调层构造执行请求并调度运行。
### 4.3 结果回写优先级
@@ -270,7 +271,7 @@ flowchart LR
D3 --> E
D4 --> E
- E --> F["Go Agent-core\nDesktop: local bridge\nWeb: remote ACP / RPC"]
+ E --> F["GoTaskService\nDesktop: GatewayRuntime / ExternalCodeAgentAcpDesktopTransport\nWeb: relay / ExternalCodeAgentAcpWebTransport"]
F --> G["执行结果"]
G --> H["回写线程上下文\n(主体区域 同步显示)"]
diff --git a/docs/architecture/xworkmate-layered-architecture.md b/docs/architecture/xworkmate-layered-architecture.md
index 462094b8..f6d7cfe3 100644
--- a/docs/architecture/xworkmate-layered-architecture.md
+++ b/docs/architecture/xworkmate-layered-architecture.md
@@ -9,7 +9,7 @@ Last Updated: 2026-03-29
- 本地用户、Web 用户、远程租户如何进入系统
- 任务线程如何成为 UI 与执行之间的控制面主对象
-- Desktop / Mobile / Web 三个界面层如何共用同一套 agent core
+- Desktop / Mobile / Web 三个界面层如何共用同一套 `GoTaskService` 执行主链
- 本地 agent、OpenClaw Gateway、ACP endpoint、AI Gateway、Skills / MCP
等扩展能力应该落在哪一层
@@ -21,7 +21,7 @@ Last Updated: 2026-03-29
## 为什么要重新规划
-如果只用“用户 -> UI -> Agent-core -> 服务”四层表达,当前项目里最关键的
+如果只用“用户 -> UI -> GoTaskService -> 服务”四层表达,当前项目里最关键的
一个事实会被隐藏掉:
**XWorkmate 的运行主对象不是某个页面,也不是某个 gateway session,而是
@@ -40,7 +40,7 @@ Last Updated: 2026-03-29
1. 访问与归属层
2. 多端 UI 层
3. 线程控制面
-4. Agent-core 调度层
+4. `GoTaskService` 调度层
5. 对接服务与扩展层
6. 安全与持久化基座(横切,不单独作为主业务层)
@@ -48,8 +48,8 @@ Last Updated: 2026-03-29
- UI 不是执行状态真值源
- `TaskThread` 才是线程级控制面真值源
-- Agent-core 负责把线程状态翻译成可执行请求
-- 真正的 provider / gateway / ACP / Skills / MCP 都应放在 Agent-core 之下
+- `GoTaskService` 负责把线程状态翻译成可执行请求
+- 真正的 provider / gateway / ACP / Skills / MCP 都应放在 `GoTaskService` 之下
## 整体架构
@@ -77,16 +77,16 @@ flowchart TB
C7["线程持久化
SettingsStore / WebSessionRepository"]
end
- subgraph L4["④ Agent-core 调度层"]
+ subgraph L4["④ GoTaskService 调度层"]
D1["AppControllerDesktop / AppControllerWeb"]
- D2["GoAgentCoreClient 抽象"]
+ D2["GoTaskService / GoTaskServiceClient"]
D3["RuntimeCoordinator / GatewayRuntime / ModeSwitcher"]
D4["CodeAgentNodeOrchestrator"]
D5["SingleAgentRunner / DirectSingleAgentAppServerClient / CodexRuntime"]
D6["MultiAgentOrchestrator / MultiAgentMountManager"]
D7["CodexConfigBridge / OpencodeConfigBridge"]
- D8["Desktop transport
GoAgentCoreDesktopTransport / go_core / codex_ffi_bindings"]
- D9["Web transport
GoAgentCoreWebTransport / WebAcpClient / Relay"]
+ D8["Desktop ACP transport
ExternalCodeAgentAcpDesktopTransport / go_core / codex_ffi_bindings"]
+ D9["Web ACP transport
ExternalCodeAgentAcpWebTransport / WebAcpClient / Relay"]
end
subgraph L5["⑤ 对接服务与扩展层"]
@@ -215,9 +215,9 @@ flowchart TB
- `RuntimeCoordinator`
- `GatewayRuntime`
- `CodeAgentNodeOrchestrator`
-- `GoAgentCoreClient`
-- `GoAgentCoreDesktopTransport`
-- `GoAgentCoreWebTransport`
+- `GoTaskServiceClient`
+- `ExternalCodeAgentAcpDesktopTransport`
+- `ExternalCodeAgentAcpWebTransport`
- `SingleAgentRunner`
- `MultiAgentOrchestrator`
- `MultiAgentMountManager`
@@ -227,7 +227,7 @@ flowchart TB
重规划后的职责边界应当是:
- `AppController*` 负责从 `TaskThread` 解析出当前线程的执行上下文
-- `GoAgentCoreClient` 负责统一 Desktop / Web 的 agent-core 会话调用抽象
+- `GoTaskServiceClient` 负责统一 Desktop / Web 的执行请求与结果映射抽象
- `RuntimeCoordinator` / `GatewayRuntime` 负责 runtime 与 gateway 连接能力
- `CodeAgentNodeOrchestrator` 负责 app-mediated cooperative node metadata
- `MultiAgentOrchestrator` / `MultiAgentMountManager` 负责协作执行与挂载
@@ -289,17 +289,15 @@ flowchart LR
C --> C3["executionBinding"]
C --> C4["contextState"]
- C1 --> D["构造 GoAgentCoreSessionRequest
或 Gateway 执行请求"]
+ C1 --> D["构造 GoTaskServiceRequest
或 Gateway 执行请求"]
C2 --> D
C3 --> D
C4 --> D
D --> E{"executionMode"}
- E -->|localAgent| F["GoAgentCoreDesktopTransport / SingleAgentRunner"]
- E -->|gatewayLocal| G["GatewayRuntime / OpenClaw local"]
- E -->|gatewayRemote| H["GatewayAcpClient / WebAcpClient / Remote ACP"]
+ E -->|openclaw task| G["GoTaskService -> GatewayRuntime / Web relay"]
+ E -->|singleAgent / multiAgent| H["GoTaskService -> ExternalCodeAgentAcp* / ACP route"]
- F --> I["执行结果 / delta / resolvedWorkingDirectory"]
G --> I
H --> I
@@ -327,17 +325,17 @@ flowchart LR
| 访问与归属层 | `ThreadOwnerScope`、`DeviceIdentityStore`、Web session identity | `lib/runtime/runtime_models_runtime_payloads.dart`, `lib/runtime/device_identity_store.dart`, `lib/web/web_session_repository.dart` | 定义线程归属、设备身份、远程会话身份 |
| 多端 UI 层 | `AppShellDesktop`、`mobile_shell_*`、`AppShellWeb`、`AssistantPage`、`SettingsPage` | `lib/app/`, `lib/features/assistant/`, `lib/features/mobile/`, `lib/features/settings/` | 接收用户操作、展示线程与设置 |
| 线程控制面 | `TaskThread` + thread records | `lib/runtime/runtime_models_runtime_payloads.dart`, `lib/runtime/settings_store.dart`, `lib/web/web_session_repository.dart` | 保存线程级真值状态 |
-| Agent-core 调度层 | `AppControllerDesktop/Web`、`GoAgentCoreClient`、`RuntimeCoordinator`、`CodeAgentNodeOrchestrator`、`MultiAgentOrchestrator` | `lib/app/`, `lib/runtime/`, `lib/web/` | 把线程状态翻译为执行请求并协调 transport |
-| 对接服务与扩展层 | local agent、OpenClaw Gateway、ACP endpoint、AI Gateway、Skills / MCP / adapters | `lib/runtime/go_agent_core_desktop_transport.dart`, `lib/web/go_agent_core_web_transport.dart`, `lib/runtime/multi_agent_mounts.dart` | 真实执行与扩展接入 |
+| `GoTaskService` 调度层 | `AppControllerDesktop/Web`、`GoTaskServiceClient`、`RuntimeCoordinator`、`CodeAgentNodeOrchestrator`、`MultiAgentOrchestrator` | `lib/app/`, `lib/runtime/`, `lib/web/` | 把线程状态翻译为执行请求并协调 transport |
+| 对接服务与扩展层 | local agent、OpenClaw Gateway、ACP endpoint、AI Gateway、Skills / MCP / adapters | `lib/runtime/external_code_agent_acp_desktop_transport.dart`, `lib/web/external_code_agent_acp_web_transport.dart`, `lib/runtime/multi_agent_mounts.dart` | 真实执行与扩展接入 |
| 安全与持久化基座 | `SettingsStore`、`SecretStore`、`SecureConfigStore`、`WebStore` | `lib/runtime/`, `lib/web/web_store.dart` | 提供持久化与 secret 保护 |
## 三端职责矩阵
-| 平台 | UI 入口 | 线程控制面 | agent-core 重点 | 当前执行特点 |
+| 平台 | UI 入口 | 线程控制面 | `GoTaskService` 重点 | 当前执行特点 |
| --- | --- | --- | --- | --- |
| Desktop | `AppShellDesktop` + workspace 页面 | `TaskThread` 持久化最完整 | `AppControllerDesktop` + `RuntimeCoordinator` + Desktop transport | 支持本地 single-agent、gateway local、gateway remote |
| Mobile | `mobile_shell_*` | 复用同一线程模型 | 仍走 native host/controller 体系 | 当前以 remote gateway 场景为主 |
-| Web | `AppShellWeb` | 同 schema 的 thread records | `AppControllerWeb` + `GoAgentCoreWebTransport` + relay/acp client | 远程 ACP / relay / AI Gateway 路径 |
+| Web | `AppShellWeb` | 同 schema 的 thread records | `AppControllerWeb` + `ExternalCodeAgentAcpWebTransport` + relay/acp client | 远程 ACP / relay / AI Gateway 路径 |
## 对你给出的旧图,按代码需要做的三个修正
@@ -358,14 +356,14 @@ flowchart LR
从代码现实出发,当前更准确的 seam 是:
-- `GoAgentCoreClient`
-- `GoAgentCoreDesktopTransport`
-- `GoAgentCoreWebTransport`
+- `GoTaskServiceClient`
+- `ExternalCodeAgentAcpDesktopTransport`
+- `ExternalCodeAgentAcpWebTransport`
- `GatewayAcpClient`
- `WebAcpClient`
- `MultiAgentOrchestrator`
-因此,新的整体架构里应把“broker / ACP / transport”归到 Agent-core 调度层内部,
+因此,新的整体架构里应把“broker / ACP / transport”归到 `GoTaskService` 调度层内部,
而不是单独挂成一个与 UI 并列的主系统。
### 修正 3:`Assistant composer / Settings / Feature flags` 属于 UI 层,不属于运行时层
@@ -395,7 +393,7 @@ flowchart LR
如果未来新增新的执行目标或新的 gateway 类型:
- 先扩展 `executionBinding`
-- 再扩展 `GoAgentCoreClient` transport 或 runtime coordinator
+- 再扩展 `GoTaskServiceClient` transport 或 runtime coordinator
- 不要先改页面分支逻辑
### 新 provider / adapter / MCP / skill capability
@@ -425,7 +423,7 @@ flowchart LR
- 一个以 `TaskThread` 为控制面核心的多端 agent workspace App
- UI 负责交互
- 线程控制面负责真值
-- Agent-core 负责调度与 transport
+- `GoTaskService` 负责调度与 transport
- Gateway / ACP / local agent / Skills / MCP 负责实际执行与扩展
一句话概括:
diff --git a/lib/app/app_controller_desktop_core.dart b/lib/app/app_controller_desktop_core.dart
index e8407da7..1dce5bab 100644
--- a/lib/app/app_controller_desktop_core.dart
+++ b/lib/app/app_controller_desktop_core.dart
@@ -29,7 +29,7 @@ import '../runtime/codex_config_bridge.dart';
import '../runtime/code_agent_node_orchestrator.dart';
import '../runtime/assistant_artifacts.dart';
import '../runtime/desktop_thread_artifact_service.dart';
-import '../runtime/go_agent_core_desktop_transport.dart';
+import '../runtime/external_code_agent_acp_desktop_transport.dart';
import '../runtime/go_task_service_client.dart';
import '../runtime/go_task_service_desktop_service.dart';
import '../runtime/go_gateway_runtime_desktop_client.dart';
@@ -221,7 +221,7 @@ class AppController extends ChangeNotifier {
gateway: runtimeCoordinatorInternal.gateway,
acpTransport: ExternalCodeAgentAcpDesktopTransport(
acpClient: gatewayAcpClientInternal,
- endpointResolver: resolveGoAgentCoreEndpointForTargetInternal,
+ endpointResolver: resolveExternalAcpEndpointForTargetInternal,
goCoreLocator: goCoreLocatorInternal,
),
);
@@ -318,11 +318,6 @@ class AppController extends ChangeNotifier {
>{};
final Map aiGatewayStreamingTextBySessionInternal =
{};
- final Map singleAgentRuntimeModelBySessionInternal =
- {};
- final Map>
- latestRoutingResolutionBySessionInternal =
- >{};
final Map
syncedGoAgentProvidersInternal = {};
final DesktopThreadArtifactService threadArtifactServiceInternal =
diff --git a/lib/app/app_controller_desktop_go_agent_core_routing.dart b/lib/app/app_controller_desktop_external_acp_routing.dart
similarity index 73%
rename from lib/app/app_controller_desktop_go_agent_core_routing.dart
rename to lib/app/app_controller_desktop_external_acp_routing.dart
index 068a278e..99d03c14 100644
--- a/lib/app/app_controller_desktop_go_agent_core_routing.dart
+++ b/lib/app/app_controller_desktop_external_acp_routing.dart
@@ -38,7 +38,7 @@ import '../runtime/skill_directory_access.dart';
import 'app_controller_desktop_core.dart';
import 'app_controller_desktop_thread_sessions.dart';
-extension AppControllerDesktopGoAgentCoreRouting on AppController {
+extension AppControllerDesktopExternalAcpRouting on AppController {
Future>
buildExternalAcpSyncedProvidersInternal() async {
final providers = [];
@@ -75,27 +75,4 @@ extension AppControllerDesktopGoAgentCoreRouting on AppController {
);
await goTaskServiceClientInternal.syncExternalProviders(providers);
}
-
- void updateLatestRoutingResolutionInternal(
- String sessionKey,
- GoTaskServiceResult result,
- ) {
- final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
- sessionKey,
- );
- latestRoutingResolutionBySessionInternal[normalizedSessionKey] =
- {
- 'resolvedExecutionTarget': result.resolvedExecutionTarget,
- 'resolvedEndpointTarget': result.resolvedEndpointTarget,
- 'resolvedProviderId': result.resolvedProviderId,
- 'resolvedModel': result.resolvedModel.trim(),
- 'resolvedSkills': result.resolvedSkills,
- 'skillResolutionSource': result.skillResolutionSource,
- 'skillCandidates': result.skillCandidates,
- 'needsSkillInstall': result.needsSkillInstall,
- 'skillInstallRequestId': result.skillInstallRequestId,
- 'memorySources': result.memorySources,
- 'updatedAtMs': DateTime.now().millisecondsSinceEpoch,
- };
- }
}
diff --git a/lib/app/app_controller_desktop_runtime_coordination_impl.dart b/lib/app/app_controller_desktop_runtime_coordination_impl.dart
index f19e4c95..6c587b1d 100644
--- a/lib/app/app_controller_desktop_runtime_coordination_impl.dart
+++ b/lib/app/app_controller_desktop_runtime_coordination_impl.dart
@@ -45,7 +45,7 @@ import 'app_controller_desktop_workspace_execution.dart';
import 'app_controller_desktop_settings_runtime.dart';
import 'app_controller_desktop_thread_storage.dart';
import 'app_controller_desktop_skill_permissions.dart';
-import 'app_controller_desktop_go_agent_core_routing.dart';
+import 'app_controller_desktop_external_acp_routing.dart';
import 'app_controller_desktop_runtime_helpers.dart';
Future refreshAcpCapabilitiesRuntimeInternal(
@@ -101,7 +101,7 @@ Future refreshSingleAgentCapabilitiesRuntimeInternal(
next[provider] = DirectSingleAgentCapabilities(
available: true,
supportedProviders: [provider],
- endpoint: 'go-agent-core',
+ endpoint: 'go-task-service',
);
}
controller.singleAgentCapabilitiesByProviderInternal = next;
diff --git a/lib/app/app_controller_desktop_runtime_helpers.dart b/lib/app/app_controller_desktop_runtime_helpers.dart
index 710cad6e..84dc10ae 100644
--- a/lib/app/app_controller_desktop_runtime_helpers.dart
+++ b/lib/app/app_controller_desktop_runtime_helpers.dart
@@ -669,7 +669,7 @@ extension AppControllerDesktopRuntimeHelpers on AppController {
);
}
- Uri? resolveGoAgentCoreEndpointForTargetInternal(
+ Uri? resolveExternalAcpEndpointForTargetInternal(
AssistantExecutionTarget target,
) {
if (target == AssistantExecutionTarget.singleAgent) {
diff --git a/lib/app/app_controller_desktop_settings.dart b/lib/app/app_controller_desktop_settings.dart
index ac55257b..c37878eb 100644
--- a/lib/app/app_controller_desktop_settings.dart
+++ b/lib/app/app_controller_desktop_settings.dart
@@ -269,7 +269,6 @@ extension AppControllerDesktopSettings on AppController {
aiGatewayStreamingClientsInternal.clear();
aiGatewayPendingSessionKeysInternal.clear();
aiGatewayAbortedSessionKeysInternal.clear();
- latestRoutingResolutionBySessionInternal.clear();
singleAgentExternalCliPendingSessionKeysInternal.clear();
assistantThreadTurnQueuesInternal.clear();
multiAgentRunPendingInternal = false;
diff --git a/lib/app/app_controller_desktop_single_agent.dart b/lib/app/app_controller_desktop_single_agent.dart
index 1755176e..c6a5f706 100644
--- a/lib/app/app_controller_desktop_single_agent.dart
+++ b/lib/app/app_controller_desktop_single_agent.dart
@@ -45,7 +45,7 @@ import 'app_controller_desktop_workspace_execution.dart';
import 'app_controller_desktop_settings_runtime.dart';
import 'app_controller_desktop_thread_storage.dart';
import 'app_controller_desktop_skill_permissions.dart';
-import 'app_controller_desktop_go_agent_core_routing.dart';
+import 'app_controller_desktop_external_acp_routing.dart';
import 'app_controller_desktop_runtime_helpers.dart';
extension AppControllerDesktopSingleAgent on AppController {
@@ -100,12 +100,12 @@ extension AppControllerDesktopSingleAgent on AppController {
final fallbackReason = provider == null
? (selection == SingleAgentProvider.auto
? appText(
- '当前没有可用的 Go Agent-core Provider。',
- 'No Go Agent-core provider is currently available.',
+ '当前没有可用的 GoTaskService Provider。',
+ 'No GoTaskService provider is currently available.',
)
: appText(
- '当前 Go Agent-core 不支持 ${selection.label}。',
- 'Go Agent-core does not currently support ${selection.label}.',
+ '当前 GoTaskService 不支持 ${selection.label}。',
+ 'GoTaskService does not currently support ${selection.label}.',
))
: null;
if (provider == null && !routing.isAuto) {
@@ -205,25 +205,17 @@ extension AppControllerDesktopSingleAgent on AppController {
},
);
final resolvedRuntimeModel = result.resolvedModel.trim();
- updateLatestRoutingResolutionInternal(sessionKey, result);
- if (resolvedRuntimeModel.isNotEmpty) {
- singleAgentRuntimeModelBySessionInternal[sessionKey] =
- resolvedRuntimeModel;
- }
- final resolvedGatewayEntryState =
- (result.resolvedExecutionTarget == 'gateway' ||
- result.resolvedExecutionTarget == 'gateway-chat')
- ? (result.resolvedEndpointTarget.trim().isNotEmpty
- ? result.resolvedEndpointTarget.trim()
- : AssistantExecutionTarget.local.promptValue)
- : result.resolvedExecutionTarget == 'single-agent'
- ? AssistantExecutionTarget.singleAgent.promptValue
- : (sessionTarget == AssistantExecutionTarget.auto
- ? AssistantExecutionTarget.auto.promptValue
- : AssistantExecutionTarget.singleAgent.promptValue);
+ final resolvedGatewayEntryState = goTaskServiceGatewayEntryState(
+ requestedTarget: sessionTarget,
+ result: result,
+ );
upsertTaskThreadInternal(
sessionKey,
gatewayEntryState: resolvedGatewayEntryState,
+ latestResolvedRuntimeModel: resolvedRuntimeModel,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: result.success ? 'success' : 'error',
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
);
final resolvedWorkspaceKind = result.resolvedWorkspaceRefKind;
@@ -256,6 +248,10 @@ extension AppControllerDesktopSingleAgent on AppController {
upsertTaskThreadInternal(
sessionKey,
gatewayEntryState: 'only-chat',
+ latestResolvedRuntimeModel: resolvedAiGatewayModel,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'fallback',
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
);
await sendAiGatewayMessageInternal(
@@ -295,8 +291,8 @@ extension AppControllerDesktopSingleAgent on AppController {
sessionKey,
assistantErrorMessageInternal(
appText(
- 'Go Agent-core 执行失败:${result.errorMessage}',
- 'Go Agent-core execution failed: ${result.errorMessage}',
+ 'GoTaskService 执行失败:${result.errorMessage}',
+ 'GoTaskService execution failed: ${result.errorMessage}',
),
),
);
@@ -308,8 +304,8 @@ extension AppControllerDesktopSingleAgent on AppController {
sessionKey,
assistantErrorMessageInternal(
appText(
- 'Go Agent-core 没有返回可显示的输出。',
- 'Go Agent-core returned no displayable output.',
+ 'GoTaskService 没有返回可显示的输出。',
+ 'GoTaskService returned no displayable output.',
),
),
);
@@ -332,6 +328,13 @@ extension AppControllerDesktopSingleAgent on AppController {
);
} catch (error) {
clearAiGatewayStreamingTextInternal(sessionKey);
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
appendAssistantThreadMessageInternal(
sessionKey,
assistantErrorMessageInternal(error.toString()),
@@ -452,6 +455,15 @@ extension AppControllerDesktopSingleAgent on AppController {
error: false,
),
);
+ upsertTaskThreadInternal(
+ sessionKey,
+ gatewayEntryState: 'only-chat',
+ latestResolvedRuntimeModel: model,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'success',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
} on AiGatewayAbortExceptionInternal catch (error) {
final partial = error.partialText.trim();
if (partial.isNotEmpty) {
@@ -470,7 +482,21 @@ extension AppControllerDesktopSingleAgent on AppController {
),
);
}
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'aborted',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
} catch (error) {
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
appendAssistantThreadMessageInternal(
sessionKey,
assistantErrorMessageInternal(aiGatewayErrorLabelInternal(error)),
@@ -698,6 +724,13 @@ extension AppControllerDesktopSingleAgent on AppController {
}
aiGatewayPendingSessionKeysInternal.remove(normalizedSessionKey);
clearAiGatewayStreamingTextInternal(normalizedSessionKey);
+ upsertTaskThreadInternal(
+ normalizedSessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'aborted',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
recomputeTasksInternal();
notifyIfActiveInternal();
}
diff --git a/lib/app/app_controller_desktop_skill_permissions.dart b/lib/app/app_controller_desktop_skill_permissions.dart
index 233c9de4..988d6330 100644
--- a/lib/app/app_controller_desktop_skill_permissions.dart
+++ b/lib/app/app_controller_desktop_skill_permissions.dart
@@ -273,6 +273,10 @@ extension AppControllerDesktopSkillPermissions on AppController {
ThreadSelectionSource? assistantModelSource,
ThreadSelectionSource? selectedSkillsSource,
String? gatewayEntryState,
+ String? latestResolvedRuntimeModel,
+ String? lifecycleStatus,
+ double? lastRunAtMs,
+ String? lastResultCode,
}) {
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
sessionKey,
@@ -385,10 +389,14 @@ extension AppControllerDesktopSkillPermissions on AppController {
selectedSkillsSource:
selectedSkillsSource ??
existing?.contextState.selectedSkillsSource,
+ latestResolvedRuntimeModel: latestResolvedRuntimeModel,
gatewayEntryState: gatewayEntryState,
);
final nextStatus =
- lifecycleState?.status ?? existing?.lifecycleState.status ?? 'ready';
+ lifecycleStatus ??
+ lifecycleState?.status ??
+ existing?.lifecycleState.status ??
+ 'ready';
final nextLifecycleState =
(lifecycleState ??
existing?.lifecycleState ??
@@ -407,6 +415,8 @@ extension AppControllerDesktopSkillPermissions on AppController {
existing?.archived ??
isAssistantTaskArchived(normalizedSessionKey),
status: nextStatus,
+ lastRunAtMs: lastRunAtMs,
+ lastResultCode: lastResultCode,
);
final nextRecord = TaskThread(
threadId: normalizedSessionKey,
diff --git a/lib/app/app_controller_desktop_thread_actions.dart b/lib/app/app_controller_desktop_thread_actions.dart
index 7d891b9a..8527c935 100644
--- a/lib/app/app_controller_desktop_thread_actions.dart
+++ b/lib/app/app_controller_desktop_thread_actions.dart
@@ -341,14 +341,26 @@ extension AppControllerDesktopThreadActions on AppController {
},
);
clearAiGatewayStreamingTextInternal(sessionKey);
+ upsertTaskThreadInternal(
+ sessionKey,
+ gatewayEntryState: goTaskServiceGatewayEntryState(
+ requestedTarget: currentTarget,
+ result: result,
+ ),
+ latestResolvedRuntimeModel: result.resolvedModel.trim(),
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: result.success ? 'success' : 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
if (!result.success) {
appendLocalSessionMessageInternal(
sessionKey,
assistantErrorMessageInternal(
result.errorMessage.trim().isEmpty
? appText(
- 'Go Agent-core 执行失败。',
- 'Go Agent-core execution failed.',
+ 'GoTaskService 执行失败。',
+ 'GoTaskService execution failed.',
)
: result.errorMessage,
),
@@ -362,8 +374,8 @@ extension AppControllerDesktopThreadActions on AppController {
sessionKey,
assistantErrorMessageInternal(
appText(
- 'Go Agent-core 没有返回可显示的输出。',
- 'Go Agent-core returned no displayable output.',
+ 'GoTaskService 没有返回可显示的输出。',
+ 'GoTaskService returned no displayable output.',
),
),
persistInThreadContext: true,
@@ -387,6 +399,13 @@ extension AppControllerDesktopThreadActions on AppController {
);
} catch (error) {
clearAiGatewayStreamingTextInternal(sessionKey);
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
appendLocalSessionMessageInternal(
sessionKey,
assistantErrorMessageInternal(error.toString()),
@@ -417,6 +436,13 @@ extension AppControllerDesktopThreadActions on AppController {
// Best effort cancellation only.
}
multiAgentRunPendingInternal = false;
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'aborted',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
recomputeTasksInternal();
notifyIfActiveInternal();
return;
@@ -434,6 +460,13 @@ extension AppControllerDesktopThreadActions on AppController {
);
aiGatewayPendingSessionKeysInternal.remove(sessionKey);
clearAiGatewayStreamingTextInternal(sessionKey);
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'aborted',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
recomputeTasksInternal();
notifyIfActiveInternal();
return;
@@ -458,6 +491,13 @@ extension AppControllerDesktopThreadActions on AppController {
);
aiGatewayPendingSessionKeysInternal.remove(sessionKey);
clearAiGatewayStreamingTextInternal(sessionKey);
+ upsertTaskThreadInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'aborted',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
recomputeTasksInternal();
notifyIfActiveInternal();
return;
diff --git a/lib/app/app_controller_desktop_thread_sessions.dart b/lib/app/app_controller_desktop_thread_sessions.dart
index 0332d5f5..fef7a809 100644
--- a/lib/app/app_controller_desktop_thread_sessions.dart
+++ b/lib/app/app_controller_desktop_thread_sessions.dart
@@ -68,14 +68,6 @@ extension AppControllerDesktopThreadSessions on AppController {
);
}
- Map latestRoutingResolutionForSession(String sessionKey) {
- final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
- sessionKey,
- );
- return latestRoutingResolutionBySessionInternal[normalizedSessionKey] ??
- const {};
- }
-
int assistantSkillCountForSession(String sessionKey) {
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
sessionKey,
@@ -123,11 +115,11 @@ extension AppControllerDesktopThreadSessions on AppController {
sessionKey,
);
final target = assistantExecutionTargetForSession(normalizedSessionKey);
- final latestRouting = latestRoutingResolutionForSession(
- normalizedSessionKey,
- );
final latestResolvedModel =
- latestRouting['resolvedModel']?.toString().trim() ?? '';
+ taskThreadForSessionInternal(normalizedSessionKey)
+ ?.latestResolvedRuntimeModel
+ .trim() ??
+ '';
if (target == AssistantExecutionTarget.singleAgent ||
target == AssistantExecutionTarget.auto) {
if (latestResolvedModel.isNotEmpty) {
@@ -318,8 +310,9 @@ extension AppControllerDesktopThreadSessions on AppController {
final normalizedSessionKey = normalizedAssistantSessionKeyInternal(
sessionKey,
);
- return singleAgentRuntimeModelBySessionInternal[normalizedSessionKey]
- ?.trim() ??
+ return taskThreadForSessionInternal(normalizedSessionKey)
+ ?.latestResolvedRuntimeModel
+ .trim() ??
'';
}
@@ -409,17 +402,14 @@ extension AppControllerDesktopThreadSessions on AppController {
final target = assistantExecutionTargetForSession(normalizedSessionKey);
if (target == AssistantExecutionTarget.singleAgent ||
target == AssistantExecutionTarget.auto) {
- final latestRouting = latestRoutingResolutionForSession(
- normalizedSessionKey,
- );
- final latestResolvedExecutionTarget =
- latestRouting['resolvedExecutionTarget']?.toString().trim() ?? '';
- final latestResolvedEndpointTarget =
- latestRouting['resolvedEndpointTarget']?.toString().trim() ?? '';
- final latestResolvedProviderId =
- latestRouting['resolvedProviderId']?.toString().trim() ?? '';
- final latestResolvedModel =
- latestRouting['resolvedModel']?.toString().trim() ?? '';
+ final thread = taskThreadForSessionInternal(normalizedSessionKey);
+ final resolvedGatewayEntryState = switch (
+ thread?.gatewayEntryState?.trim() ?? ''
+ ) {
+ 'auto' => '',
+ final value => value,
+ };
+ final latestResolvedModel = thread?.latestResolvedRuntimeModel.trim() ?? '';
final primaryLabel = target == AssistantExecutionTarget.auto
? 'Auto'
: target.label;
@@ -427,7 +417,7 @@ extension AppControllerDesktopThreadSessions on AppController {
? appText('当前: ', 'Current: ')
: '';
if (target == AssistantExecutionTarget.auto &&
- latestResolvedExecutionTarget.isEmpty) {
+ resolvedGatewayEntryState.isEmpty) {
final autoReady = autoRouteReadyForSession(normalizedSessionKey);
return AssistantThreadConnectionState(
executionTarget: target,
@@ -443,22 +433,23 @@ extension AppControllerDesktopThreadSessions on AppController {
);
}
if (target == AssistantExecutionTarget.auto &&
- latestResolvedExecutionTarget.isNotEmpty) {
- final detail = switch (latestResolvedExecutionTarget) {
- 'gateway' => joinConnectionPartsInternal([
- latestResolvedEndpointTarget.isEmpty
- ? appText('OpenClaw Gateway', 'OpenClaw Gateway')
- : latestResolvedEndpointTarget,
+ resolvedGatewayEntryState.isNotEmpty) {
+ final detail = switch (resolvedGatewayEntryState) {
+ 'local' => joinConnectionPartsInternal([
+ appText('OpenClaw Gateway', 'OpenClaw Gateway'),
latestResolvedModel,
]),
- 'multi-agent' => joinConnectionPartsInternal([
- appText('Multi-Agent', 'Multi-Agent'),
+ 'remote' => joinConnectionPartsInternal([
+ appText('OpenClaw Gateway', 'OpenClaw Gateway'),
latestResolvedModel,
]),
_ => joinConnectionPartsInternal([
- latestResolvedProviderId.isEmpty
- ? appText('Single Agent', 'Single Agent')
- : latestResolvedProviderId,
+ singleAgentResolvedProviderForSession(normalizedSessionKey)
+ ?.label
+ .isNotEmpty ==
+ true
+ ? singleAgentResolvedProviderForSession(normalizedSessionKey)!.label
+ : appText('Single Agent', 'Single Agent'),
latestResolvedModel,
]),
};
diff --git a/lib/app/app_controller_desktop_workspace_execution.dart b/lib/app/app_controller_desktop_workspace_execution.dart
index 618e6166..613c1dc9 100644
--- a/lib/app/app_controller_desktop_workspace_execution.dart
+++ b/lib/app/app_controller_desktop_workspace_execution.dart
@@ -111,11 +111,11 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
singleAgentProvider: currentSingleAgentProvider,
);
}
- singleAgentRuntimeModelBySessionInternal.remove(sessionKey);
upsertTaskThreadInternal(
sessionKey,
singleAgentProvider: sanitizedProvider,
singleAgentProviderSource: ThreadSelectionSource.explicit,
+ latestResolvedRuntimeModel: '',
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
);
recomputeTasksInternal();
@@ -180,7 +180,11 @@ extension AppControllerDesktopWorkspaceExecution on AppController {
sessionKey,
);
if (resolvedTarget != AssistantExecutionTarget.singleAgent) {
- singleAgentRuntimeModelBySessionInternal.remove(normalizedSessionKey);
+ upsertTaskThreadInternal(
+ normalizedSessionKey,
+ latestResolvedRuntimeModel: '',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
}
if (!matchesSessionKey(
normalizedSessionKey,
diff --git a/lib/app/app_controller_web_core.dart b/lib/app/app_controller_web_core.dart
index c799c100..ad491161 100644
--- a/lib/app/app_controller_web_core.dart
+++ b/lib/app/app_controller_web_core.dart
@@ -8,7 +8,7 @@ import '../runtime/assistant_artifacts.dart';
import '../runtime/go_task_service_client.dart';
import '../runtime/runtime_models.dart';
import '../web/web_acp_client.dart';
-import '../web/go_agent_core_web_transport.dart';
+import '../web/external_code_agent_acp_web_transport.dart';
import '../web/go_task_service_web_service.dart';
import '../web/web_ai_gateway_client.dart';
import '../web/web_artifact_proxy_client.dart';
@@ -105,8 +105,6 @@ class AppController extends ChangeNotifier {
final Map streamingTextBySessionInternal = {};
final Map> threadTurnQueuesInternal =
>{};
- final Map singleAgentRuntimeModelBySessionInternal =
- {};
final WebTasksController tasksControllerInternal = WebTasksController();
String currentSessionKeyInternal = '';
String? lastAssistantErrorInternal;
diff --git a/lib/app/app_controller_web_gateway_chat.dart b/lib/app/app_controller_web_gateway_chat.dart
index 4e43fa83..207a6cb0 100644
--- a/lib/app/app_controller_web_gateway_chat.dart
+++ b/lib/app/app_controller_web_gateway_chat.dart
@@ -142,6 +142,13 @@ extension AppControllerWebGatewayChat on AppController {
text: error.toString(),
error: true,
);
+ upsertThreadRecordInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
lastAssistantErrorInternal = error.toString();
pendingSessionKeysInternal.remove(sessionKey);
streamingTextBySessionInternal.remove(sessionKey);
@@ -213,6 +220,13 @@ extension AppControllerWebGatewayChat on AppController {
acpBusyInternal = false;
pendingSessionKeysInternal.remove(sessionKey);
clearStreamingTextInternal(sessionKey);
+ upsertThreadRecordInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: lastAssistantErrorInternal == null ? 'success' : 'error',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
await persistThreadsInternal();
notifyChangedInternal();
}
@@ -294,6 +308,18 @@ extension AppControllerWebGatewayChat on AppController {
),
);
}
+ upsertThreadRecordInternal(
+ sessionKey,
+ gatewayEntryState: goTaskServiceGatewayEntryState(
+ requestedTarget: target,
+ result: result,
+ ),
+ latestResolvedRuntimeModel: result.resolvedModel.trim(),
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'success',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
appendAssistantMessageInternal(
sessionKey: sessionKey,
text: message,
diff --git a/lib/app/app_controller_web_helpers.dart b/lib/app/app_controller_web_helpers.dart
index 06195746..ce488ab4 100644
--- a/lib/app/app_controller_web_helpers.dart
+++ b/lib/app/app_controller_web_helpers.dart
@@ -384,6 +384,13 @@ extension AppControllerWebHelpers on AppController {
text: text,
error: false,
);
+ upsertThreadRecordInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: 'success',
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
}
if (state == 'final' || state == 'aborted' || state == 'error') {
pendingSessionKeysInternal.remove(sessionKey);
@@ -394,6 +401,17 @@ extension AppControllerWebHelpers on AppController {
error: true,
);
}
+ upsertThreadRecordInternal(
+ sessionKey,
+ lifecycleStatus: 'ready',
+ lastRunAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ lastResultCode: switch (state) {
+ 'aborted' => 'aborted',
+ 'error' => 'error',
+ _ => 'success',
+ },
+ updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
+ );
clearStreamingTextInternal(sessionKey);
unawaited(refreshRelaySessions());
unawaited(refreshRelayHistory(sessionKey: sessionKey));
@@ -470,8 +488,12 @@ extension AppControllerWebHelpers on AppController {
ThreadSelectionSource? selectedSkillsSource,
String? gatewayEntryState,
bool clearGatewayEntryState = false,
+ String? latestResolvedRuntimeModel,
String? workspacePath,
WorkspaceKind? workspaceKind,
+ String? lifecycleStatus,
+ double? lastRunAtMs,
+ String? lastResultCode,
}) {
final key = normalizedSessionKeyInternal(sessionKey);
final resolvedTarget =
@@ -500,6 +522,7 @@ extension AppControllerWebHelpers on AppController {
assistantModelSource ?? existing.contextState.selectedModelSource,
selectedSkillsSource:
selectedSkillsSource ?? existing.contextState.selectedSkillsSource,
+ latestResolvedRuntimeModel: latestResolvedRuntimeModel,
gatewayEntryState: gatewayEntryState ?? existing.gatewayEntryState,
clearGatewayEntryState: clearGatewayEntryState,
workspaceBinding:
@@ -531,7 +554,11 @@ extension AppControllerWebHelpers on AppController {
providerSource:
singleAgentProviderSource ?? existing.executionBinding.providerSource,
),
- lifecycleState: existing.lifecycleState.copyWith(status: 'ready'),
+ lifecycleState: existing.lifecycleState.copyWith(
+ status: lifecycleStatus ?? 'ready',
+ lastRunAtMs: lastRunAtMs,
+ lastResultCode: lastResultCode,
+ ),
),
);
recomputeDerivedWorkspaceStateInternal();
diff --git a/lib/app/app_controller_web_session_actions.dart b/lib/app/app_controller_web_session_actions.dart
index e66fd9b7..599efae7 100644
--- a/lib/app/app_controller_web_session_actions.dart
+++ b/lib/app/app_controller_web_session_actions.dart
@@ -154,11 +154,11 @@ extension AppControllerWebSessionActions on AppController {
if (singleAgentProviderForSession(sessionKey) == resolvedProvider) {
return;
}
- singleAgentRuntimeModelBySessionInternal.remove(sessionKey);
upsertThreadRecordInternal(
sessionKey,
singleAgentProvider: resolvedProvider,
singleAgentProviderSource: ThreadSelectionSource.explicit,
+ latestResolvedRuntimeModel: '',
updatedAtMs: DateTime.now().millisecondsSinceEpoch.toDouble(),
);
await persistThreadsInternal();
diff --git a/lib/app/app_controller_web_sessions.dart b/lib/app/app_controller_web_sessions.dart
index f708a702..d76fe42b 100644
--- a/lib/app/app_controller_web_sessions.dart
+++ b/lib/app/app_controller_web_sessions.dart
@@ -150,10 +150,11 @@ extension AppControllerWebSessions on AppController {
singleAgentUsesAiChatFallbackForSession(currentSessionKeyInternal);
String singleAgentRuntimeModelForSession(String sessionKey) {
- return singleAgentRuntimeModelBySessionInternal[normalizedSessionKeyInternal(
- sessionKey,
- )]
- ?.trim() ??
+ return taskThreadForSessionInternal(
+ normalizedSessionKeyInternal(sessionKey),
+ )
+ ?.latestResolvedRuntimeModel
+ .trim() ??
'';
}
diff --git a/lib/runtime/direct_single_agent_app_server_client.dart b/lib/runtime/direct_single_agent_app_server_client.dart
index 67cad407..d8c2a300 100644
--- a/lib/runtime/direct_single_agent_app_server_client.dart
+++ b/lib/runtime/direct_single_agent_app_server_client.dart
@@ -1,6 +1,6 @@
// Legacy compatibility surface retained while the app imports are cleaned up.
//
// The direct single-agent app-server runtime has been retired in favor of the
-// GoAgentCore ACP path. This library intentionally exports only the capability
+// GoTaskService ACP lane. This library intentionally exports only the capability
// DTOs still consumed by the UI-facing state layer.
export 'direct_single_agent_app_server_client_protocol.dart';
diff --git a/lib/runtime/go_agent_core_desktop_transport.dart b/lib/runtime/external_code_agent_acp_desktop_transport.dart
similarity index 98%
rename from lib/runtime/go_agent_core_desktop_transport.dart
rename to lib/runtime/external_code_agent_acp_desktop_transport.dart
index 68c9bca0..843e6a2e 100644
--- a/lib/runtime/go_agent_core_desktop_transport.dart
+++ b/lib/runtime/external_code_agent_acp_desktop_transport.dart
@@ -90,8 +90,8 @@ class ExternalCodeAgentAcpDesktopTransport implements ExternalCodeAgentAcpTransp
final endpoint = await _resolveEndpoint(request.target);
if (endpoint == null) {
throw const GatewayAcpException(
- 'Missing Go Agent-core endpoint',
- code: 'GO_AGENT_CORE_ENDPOINT_MISSING',
+ 'Missing external ACP endpoint',
+ code: 'EXTERNAL_ACP_ENDPOINT_MISSING',
);
}
var streamedText = '';
diff --git a/lib/runtime/go_agent_core_client.dart b/lib/runtime/go_agent_core_client.dart
deleted file mode 100644
index 55d567bd..00000000
--- a/lib/runtime/go_agent_core_client.dart
+++ /dev/null
@@ -1,584 +0,0 @@
-import 'runtime_models.dart';
-
-class GoAgentCoreCapabilities {
- const GoAgentCoreCapabilities({
- required this.singleAgent,
- required this.multiAgent,
- required this.providers,
- required this.raw,
- });
-
- const GoAgentCoreCapabilities.empty()
- : singleAgent = false,
- multiAgent = false,
- providers = const {},
- raw = const {};
-
- final bool singleAgent;
- final bool multiAgent;
- final Set providers;
- final Map raw;
-}
-
-class GoAgentCoreSyncedProvider {
- const GoAgentCoreSyncedProvider({
- required this.providerId,
- required this.label,
- required this.endpoint,
- required this.authorizationHeader,
- required this.enabled,
- });
-
- final String providerId;
- final String label;
- final String endpoint;
- final String authorizationHeader;
- final bool enabled;
-
- Map toJson() {
- return {
- 'providerId': providerId.trim(),
- 'label': label.trim(),
- 'endpoint': endpoint.trim(),
- 'authorizationHeader': authorizationHeader.trim(),
- 'enabled': enabled,
- };
- }
-}
-
-enum GoAgentCoreRoutingMode { auto, explicit }
-
-class GoAgentCoreAvailableSkill {
- const GoAgentCoreAvailableSkill({
- required this.id,
- required this.label,
- required this.description,
- this.installed = true,
- });
-
- final String id;
- final String label;
- final String description;
- final bool installed;
-
- Map toJson() {
- return {
- 'id': id.trim(),
- 'label': label.trim(),
- 'description': description.trim(),
- 'installed': installed,
- };
- }
-}
-
-class GoAgentCoreRoutingConfig {
- const GoAgentCoreRoutingConfig({
- required this.mode,
- required this.preferredGatewayTarget,
- required this.explicitExecutionTarget,
- required this.explicitProviderId,
- required this.explicitModel,
- required this.explicitSkills,
- required this.allowSkillInstall,
- required this.availableSkills,
- this.installApproval,
- });
-
- const GoAgentCoreRoutingConfig.auto({
- this.preferredGatewayTarget = '',
- this.availableSkills = const [],
- }) : mode = GoAgentCoreRoutingMode.auto,
- explicitExecutionTarget = '',
- explicitProviderId = '',
- explicitModel = '',
- explicitSkills = const [],
- allowSkillInstall = false,
- installApproval = null;
-
- final GoAgentCoreRoutingMode mode;
- final String preferredGatewayTarget;
- final String explicitExecutionTarget;
- final String explicitProviderId;
- final String explicitModel;
- final List explicitSkills;
- final bool allowSkillInstall;
- final List availableSkills;
- final GoAgentCoreSkillInstallApproval? installApproval;
-
- bool get isAuto => mode == GoAgentCoreRoutingMode.auto;
-
- Map toJson() {
- return {
- 'routingMode': mode.name,
- if (preferredGatewayTarget.trim().isNotEmpty)
- 'preferredGatewayTarget': preferredGatewayTarget.trim(),
- if (explicitExecutionTarget.trim().isNotEmpty)
- 'explicitExecutionTarget': explicitExecutionTarget.trim(),
- if (explicitProviderId.trim().isNotEmpty)
- 'explicitProviderId': explicitProviderId.trim(),
- if (explicitModel.trim().isNotEmpty)
- 'explicitModel': explicitModel.trim(),
- 'explicitSkills': explicitSkills
- .map((item) => item.trim())
- .where((item) => item.isNotEmpty)
- .toList(growable: false),
- 'allowSkillInstall': allowSkillInstall,
- 'availableSkills': availableSkills
- .map((item) => item.toJson())
- .toList(growable: false),
- if (installApproval != null) 'installApproval': installApproval!.toJson(),
- };
- }
-}
-
-class GoAgentCoreSkillInstallApproval {
- const GoAgentCoreSkillInstallApproval({
- required this.requestId,
- required this.approvedSkillKeys,
- });
-
- final String requestId;
- final List approvedSkillKeys;
-
- Map toJson() {
- return {
- 'requestId': requestId.trim(),
- 'approvedSkillKeys': approvedSkillKeys
- .map((item) => item.trim())
- .where((item) => item.isNotEmpty)
- .toList(growable: false),
- };
- }
-}
-
-class GoAgentCoreSessionRequest {
- const GoAgentCoreSessionRequest({
- required this.sessionId,
- required this.threadId,
- required this.target,
- required this.prompt,
- required this.workingDirectory,
- required this.model,
- required this.thinking,
- required this.selectedSkills,
- required this.inlineAttachments,
- required this.localAttachments,
- required this.aiGatewayBaseUrl,
- required this.aiGatewayApiKey,
- required this.agentId,
- required this.metadata,
- this.routing,
- this.provider = SingleAgentProvider.auto,
- this.resumeSession = false,
- this.multiAgent = false,
- });
-
- final String sessionId;
- final String threadId;
- final AssistantExecutionTarget target;
- final String prompt;
- final String workingDirectory;
- final String model;
- final String thinking;
- final List selectedSkills;
- final List inlineAttachments;
- final List localAttachments;
- final String aiGatewayBaseUrl;
- final String aiGatewayApiKey;
- final String agentId;
- final Map metadata;
- final GoAgentCoreRoutingConfig? routing;
- final SingleAgentProvider provider;
- final bool resumeSession;
- final bool multiAgent;
-
- String get mode {
- if (multiAgent) {
- return 'multi-agent';
- }
- return switch (target) {
- AssistantExecutionTarget.auto => 'single-agent',
- AssistantExecutionTarget.singleAgent => 'single-agent',
- AssistantExecutionTarget.local => _gatewaySessionMode,
- AssistantExecutionTarget.remote => _gatewaySessionMode,
- };
- }
-
- String get routingExecutionTarget {
- if (multiAgent) {
- return 'multi-agent';
- }
- return switch (target) {
- AssistantExecutionTarget.auto => 'single-agent',
- AssistantExecutionTarget.singleAgent => 'single-agent',
- AssistantExecutionTarget.local => 'gateway',
- AssistantExecutionTarget.remote => 'gateway',
- };
- }
-
- bool get hasInlineAttachments => inlineAttachments.isNotEmpty;
-
- GoAgentCoreRoutingConfig get effectiveRouting =>
- routing ?? _synthesizedRouting();
-
- Map toAcpParams() {
- final resolvedRouting = effectiveRouting;
- final params = {
- 'sessionId': sessionId,
- 'threadId': threadId,
- 'mode': mode,
- 'taskPrompt': prompt,
- 'workingDirectory': workingDirectory.trim(),
- 'selectedSkills': selectedSkills,
- 'attachments':