DirectProject 聊天事件改用 ts-rs 导出的 tagged enum 并删掉 turn id
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 5m11s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 4m51s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m2s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m14s
Project CI / Native shell tests (pull_request) Failing after 2m13s
Project CI / AI game creator shell web tests (pull_request) Failing after 46s
Project CI / Frontend tests (pull_request) Failing after 3m32s
Project CI / AI game creator shell Rust shard 1/4 (pull_request) Failing after 5m11s
Project CI / AI game creator shell Rust shard 2/4 (pull_request) Successful in 4m51s
Project CI / AI game creator shell Rust smoke (pull_request) Successful in 1m57s
Project CI / Backend tests (pull_request) Failing after 11s
Project CI / AI game creator shell Rust shard 3/4 (pull_request) Successful in 4m50s
Project CI / AI game creator shell Rust crates (pull_request) Successful in 2m2s
Project CI / Repository checks (pull_request) Failing after 15s
Project CI / AI game creator shell Rust shard 4/4 (pull_request) Successful in 4m14s
Project CI / Native shell tests (pull_request) Failing after 2m13s
Project CI / AI game creator shell web tests (pull_request) Failing after 46s
Project CI / Frontend tests (pull_request) Failing after 3m32s
- 新增 direct_thread_wire.rs:DirectThreadItem / DirectThreadEvent / 订阅与历史切片全部改成 ts-rs 导出的 tagged enum,取代原大而全的可空结构体
- 删除 direct_thread_raw_item.rs,模块注册与直通引用改到 direct_thread_wire
- 条目身份只看一个 itemId:工具条目的第二个 id 在 Rust 边界归一,不再对外暴露
- 删除 DirectProject 聊天事件里的 turn id:生命周期用无载荷的 turn.started / turn.completed{status} 表示
- append 直接接收 DirectThreadEvent 并返回同一事件,队列内部自算 seq
- 请求事件改为携带 DirectThreadRequestKind,去掉字符串中转
- 思考增量走 ReasoningDelta 通道,与正文增量共用 item.delta
- at 用 #[ts(as = "f64")] 对齐 Tauri JSON 通道的 number
- 用 cargo test export_bindings 重新生成 project-workspace/generated 绑定
This commit is contained in:
@@ -21,7 +21,7 @@ mod direct_project_history;
|
||||
mod direct_project_turn_history;
|
||||
mod direct_runtime;
|
||||
mod direct_thread_manager;
|
||||
mod direct_thread_raw_item;
|
||||
mod direct_thread_wire;
|
||||
mod direct_tool_bridge;
|
||||
mod direct_tool_calls;
|
||||
mod direct_tools_mcp;
|
||||
@@ -56,7 +56,7 @@ pub(crate) use direct_project_history::*;
|
||||
pub(crate) use direct_project_turn_history::*;
|
||||
pub(crate) use direct_runtime::*;
|
||||
pub(crate) use direct_thread_manager::*;
|
||||
pub(crate) use direct_thread_raw_item::*;
|
||||
pub(crate) use direct_thread_wire::*;
|
||||
pub(crate) use direct_tool_bridge::*;
|
||||
pub(crate) use direct_tool_calls::*;
|
||||
pub(crate) use direct_tools_mcp::*;
|
||||
|
||||
@@ -564,6 +564,17 @@ enum CodexTurnEvent {
|
||||
item_id: String,
|
||||
delta: String,
|
||||
},
|
||||
/// 思考正文增量:app-server `item/reasoning/summaryTextDelta` 的明文思考文本。
|
||||
///
|
||||
/// `item/reasoning/summaryTextDelta`(core `ReasoningContentDelta`)与
|
||||
/// `item/reasoning/textDelta`(core `ReasoningRawContentDelta`)都进这条通道:前者是
|
||||
/// reasoning item 的 `summary`,后者是它的 `content`,两段文本都随 `item/completed`
|
||||
/// 落进 `project.jsonl`、此前也已经在完成时展示给用户。plan 文本与命令输出仍然只降级为
|
||||
/// 活动状态,不下发正文。
|
||||
ReasoningDelta {
|
||||
item_id: String,
|
||||
delta: String,
|
||||
},
|
||||
IntermediateText(String),
|
||||
Activity(&'static str),
|
||||
Item {
|
||||
@@ -571,7 +582,7 @@ enum CodexTurnEvent {
|
||||
params: serde_json::Value,
|
||||
},
|
||||
Request {
|
||||
event_type: &'static str,
|
||||
kind: DirectThreadRequestKind,
|
||||
params: serde_json::Value,
|
||||
},
|
||||
RawItem(serde_json::Value),
|
||||
@@ -741,40 +752,14 @@ fn direct_codex_safe_activity_for_item_value(item: &serde_json::Value) -> &'stat
|
||||
direct_codex_safe_activity_for_item(item_type)
|
||||
}
|
||||
|
||||
/// Project an app-server item into the small public payload carried by the
|
||||
/// DirectProject event queue. Full item contents are persisted in JSONL and
|
||||
/// must not be forwarded through the runtime event stream.
|
||||
/// 运行态事件载荷:与历史切片同形的脱敏原始条目;拿不到条目时给空对象。
|
||||
/// 运行态事件载荷:与历史切片同形的脱敏原始条目;拿不到身份或类型就整条跳过。
|
||||
///
|
||||
/// 这里不生成工具卡片形状:标题、折叠摘要和可见性都是前端投影的职责。
|
||||
fn direct_thread_raw_item_payload(
|
||||
fn direct_thread_event_item(
|
||||
root: &std::path::Path,
|
||||
item: &serde_json::Value,
|
||||
turn_id: Option<&str>,
|
||||
completed: bool,
|
||||
now_ms: u64,
|
||||
) -> serde_json::Value {
|
||||
direct_thread_raw_item_from_value(root, item, turn_id, completed, now_ms)
|
||||
.and_then(|raw| serde_json::to_value(raw).ok())
|
||||
.unwrap_or_else(|| serde_json::json!({}))
|
||||
}
|
||||
|
||||
fn direct_thread_item_id(item: &serde_json::Value) -> Option<String> {
|
||||
item.get("id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
}
|
||||
|
||||
/// 原始 response item 的调用 id:工具条目的 app-server `itemId` 就是这个值,
|
||||
/// 所以它是两个 id 空间唯一的对齐点。
|
||||
fn direct_thread_item_call_id(item: &serde_json::Value) -> Option<String> {
|
||||
item.get("call_id")
|
||||
.or_else(|| item.get("callId"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::trim)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
) -> Option<DirectThreadItem> {
|
||||
direct_thread_item_from_value(root, item, direct_tool_call_now_ms())
|
||||
}
|
||||
|
||||
fn direct_codex_command_is_game_verification(command: &str) -> bool {
|
||||
@@ -991,19 +976,21 @@ fn direct_codex_safe_activity_for_notification(method: &str) -> Option<&'static
|
||||
}
|
||||
}
|
||||
|
||||
fn direct_codex_request_event_type(method: &str) -> Option<&'static str> {
|
||||
fn direct_codex_request_event_type(method: &str) -> Option<DirectThreadRequestKind> {
|
||||
match method {
|
||||
"item/fileChange/requestApproval"
|
||||
| "item/commandExecution/requestApproval"
|
||||
| "item/permissions/requestApproval" => Some("approval.requested"),
|
||||
"item/tool/requestUserInput" | "item/mcpToolCall/requestUserInput" => Some("ask.requested"),
|
||||
| "item/permissions/requestApproval" => Some(DirectThreadRequestKind::ApprovalRequested),
|
||||
"item/tool/requestUserInput" | "item/mcpToolCall/requestUserInput" => {
|
||||
Some(DirectThreadRequestKind::AskRequested)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn direct_codex_resolution_event_type(method: &str) -> Option<&'static str> {
|
||||
fn direct_codex_resolution_event_type(method: &str) -> Option<DirectThreadRequestKind> {
|
||||
match method {
|
||||
"serverRequest/resolved" => Some("request.resolved"),
|
||||
"serverRequest/resolved" => Some(DirectThreadRequestKind::RequestResolved),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@@ -1076,6 +1063,33 @@ fn direct_codex_notification_event(
|
||||
intermediate_text: Option<String>,
|
||||
safe_activity: Option<&'static str>,
|
||||
) -> Option<CodexTurnEvent> {
|
||||
// 思考正文走独立通道,交给 DirectProject 的运行态事件;它不因为
|
||||
// "preparing 活动" 的降级规则被丢掉,否则界面只能等 item/completed 才看到思考。
|
||||
//
|
||||
// 两条通知都下发正文,不下发活动文本:
|
||||
// - `item/reasoning/summaryTextDelta`(core `ReasoningContentDelta`)→ reasoning item 的 `summary`;
|
||||
// - `item/reasoning/textDelta`(core `ReasoningRawContentDelta`)→ reasoning item 的 `content`,
|
||||
// 正是 `project.jsonl` 里保存、并在此前 `item/completed` 已经展示给用户的同一段文本。
|
||||
// 因此这里只是把"完成时才看到"提前为"边生成边看到",没有放宽可见文本的范围;
|
||||
// 未识别的 plan 文本与命令输出仍然只降级为活动状态,不下发正文。
|
||||
if matches!(
|
||||
method,
|
||||
"item/reasoning/summaryTextDelta" | "item/reasoning/textDelta"
|
||||
) {
|
||||
return params
|
||||
.get("delta")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|delta| CodexTurnEvent::ReasoningDelta {
|
||||
item_id: params
|
||||
.get("itemId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(str::to_string)
|
||||
.unwrap_or_else(|| "direct-missing-item".to_string()),
|
||||
delta: delta.to_string(),
|
||||
});
|
||||
}
|
||||
let (activity, intermediate_text) = match (&intermediate_text, safe_activity) {
|
||||
(Some(_), Some(activity)) if activity == "preparing" => (Some(activity), None),
|
||||
_ => (safe_activity, intermediate_text),
|
||||
@@ -2943,19 +2957,7 @@ impl CodexAppServerConnection {
|
||||
turn_start_guard.armed = false;
|
||||
let direct_thread_id = history_root.to_string_lossy().into_owned();
|
||||
if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: "turn.started".to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id: None,
|
||||
call_id: None,
|
||||
payload: serde_json::json!({
|
||||
"threadId": thread_id,
|
||||
"turnId": turn_id,
|
||||
}),
|
||||
},
|
||||
);
|
||||
append_direct_thread_event(&direct_thread_id, DirectThreadEvent::turn_started());
|
||||
}
|
||||
let mut receiver = self.register_turn(&turn_id).await;
|
||||
let mut direct_project_history = DirectProjectHistoryAccumulator::default();
|
||||
@@ -3010,18 +3012,13 @@ impl CodexAppServerConnection {
|
||||
direct_project_history.observe_delta(&item_id, &delta);
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: "item.delta".to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id: Some(item_id.clone()),
|
||||
call_id: None,
|
||||
// 事件自足:增量也要说明它是哪类条目的正文,
|
||||
// 前端 reducer 不允许靠猜 itemId 的来源决定 kind。
|
||||
payload: serde_json::json!({
|
||||
"delta": delta.clone(),
|
||||
"kind": "message",
|
||||
}),
|
||||
},
|
||||
// 事件自足:增量自带 item 身份与正文类别(正文 / 思考),
|
||||
// 前端 reducer 不允许靠猜 itemId 的来源决定 kind。
|
||||
DirectThreadEvent::item_delta(
|
||||
item_id.clone(),
|
||||
DirectThreadDeltaKind::Message,
|
||||
delta.clone(),
|
||||
),
|
||||
);
|
||||
}
|
||||
streamed_text.push_str(&delta);
|
||||
@@ -3052,6 +3049,18 @@ impl CodexAppServerConnection {
|
||||
});
|
||||
}
|
||||
}
|
||||
Some(CodexTurnEvent::ReasoningDelta { item_id, delta }) => {
|
||||
if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::item_delta(
|
||||
item_id,
|
||||
DirectThreadDeltaKind::Reasoning,
|
||||
delta,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(CodexTurnEvent::IntermediateText(text)) => {
|
||||
if let Some(observer) = direct_observer.as_deref_mut() {
|
||||
observer(DirectCodexTurnObservation::IntermediateText(text));
|
||||
@@ -3064,13 +3073,7 @@ impl CodexAppServerConnection {
|
||||
"rawResponseItem/completed 缺少 item".to_string(),
|
||||
));
|
||||
}
|
||||
let entry_payload = direct_thread_raw_item_payload(
|
||||
history_root,
|
||||
&item,
|
||||
Some(turn_id.as_str()),
|
||||
true,
|
||||
direct_tool_call_now_ms(),
|
||||
);
|
||||
let entry_item = direct_thread_event_item(history_root, &item);
|
||||
let history_root = history_root.to_path_buf();
|
||||
let history_item = item.clone();
|
||||
tokio::task::spawn_blocking(move || {
|
||||
@@ -3084,20 +3087,15 @@ impl CodexAppServerConnection {
|
||||
})?
|
||||
.map_err(platform_llm::LlmError::InvalidRequest)?;
|
||||
direct_project_history.complete_item(&item);
|
||||
let item_id = direct_thread_item_id(&item);
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: "item.completed".to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id,
|
||||
call_id: direct_thread_item_call_id(&item),
|
||||
payload: entry_payload,
|
||||
},
|
||||
);
|
||||
if let Some(entry_item) = entry_item {
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::item_completed(entry_item),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(CodexTurnEvent::Request { event_type, params }) => {
|
||||
Some(CodexTurnEvent::Request { kind, params }) => {
|
||||
if self.inner.workspace_mode == CodexAppServerWorkspaceMode::DirectProject {
|
||||
let request_id = params
|
||||
.get("requestId")
|
||||
@@ -3107,15 +3105,7 @@ impl CodexAppServerConnection {
|
||||
.map(str::to_string);
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: event_type.to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id: None,
|
||||
call_id: None,
|
||||
payload: request_id
|
||||
.map(|id| serde_json::json!({ "requestId": id }))
|
||||
.unwrap_or_else(|| serde_json::json!({})),
|
||||
},
|
||||
DirectThreadEvent::request(kind, request_id),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3226,23 +3216,14 @@ impl CodexAppServerConnection {
|
||||
&& self.inner.workspace_mode
|
||||
== CodexAppServerWorkspaceMode::DirectProject
|
||||
{
|
||||
let item_id = direct_thread_item_id(item);
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: "item.started".to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id,
|
||||
call_id: None,
|
||||
payload: direct_thread_raw_item_payload(
|
||||
history_root,
|
||||
item,
|
||||
Some(turn_id.as_str()),
|
||||
completed,
|
||||
direct_tool_call_now_ms(),
|
||||
),
|
||||
},
|
||||
);
|
||||
if let Some(entry_item) =
|
||||
direct_thread_event_item(history_root, item)
|
||||
{
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadEvent::item_started(entry_item),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3284,13 +3265,7 @@ impl CodexAppServerConnection {
|
||||
{
|
||||
append_direct_thread_event(
|
||||
&direct_thread_id,
|
||||
DirectThreadRawEventDraft {
|
||||
event_type: "turn.completed".to_string(),
|
||||
turn_id: turn_id.clone(),
|
||||
item_id: None,
|
||||
call_id: None,
|
||||
payload: serde_json::json!({ "status": status }),
|
||||
},
|
||||
DirectThreadEvent::turn_completed(status.to_string()),
|
||||
);
|
||||
}
|
||||
match status {
|
||||
@@ -3985,8 +3960,8 @@ async fn read_game_creator_codex_app_server_stdout(
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let event = if let Some(event_type) = direct_codex_resolution_event_type(method) {
|
||||
CodexTurnEvent::Request { event_type, params }
|
||||
let event = if let Some(kind) = direct_codex_resolution_event_type(method) {
|
||||
CodexTurnEvent::Request { kind, params }
|
||||
} else if let Some(activity) = safe_activity {
|
||||
// Preparing notifications may carry private plan/reasoning text;
|
||||
// expose only the safe activity category. Other categories may
|
||||
@@ -4038,8 +4013,8 @@ async fn read_game_creator_codex_app_server_stdout(
|
||||
),
|
||||
method if direct_codex_request_event_type(method).is_some() => {
|
||||
CodexTurnEvent::Request {
|
||||
event_type: direct_codex_request_event_type(method)
|
||||
.expect("request event type checked above"),
|
||||
kind: direct_codex_request_event_type(method)
|
||||
.expect("request kind checked above"),
|
||||
params,
|
||||
}
|
||||
}
|
||||
@@ -4550,15 +4525,10 @@ mod tests {
|
||||
"arguments": { "path": "game/index.html", "token": "secret" },
|
||||
"result": { "content": "large output" }
|
||||
});
|
||||
assert_eq!(direct_thread_item_id(&item).as_deref(), Some("item-1"));
|
||||
// 运行态事件必须自足:载荷是脱敏原始条目,前端不需要再按 itemId 取快照。
|
||||
let payload = direct_thread_raw_item_payload(
|
||||
std::path::Path::new("."),
|
||||
&item,
|
||||
Some("turn-1"),
|
||||
false,
|
||||
1000,
|
||||
);
|
||||
let projected = direct_thread_event_item(std::path::Path::new("."), &item).expect("item");
|
||||
assert_eq!(projected.item_id(), "item-1");
|
||||
let payload = serde_json::to_value(&projected).expect("payload");
|
||||
assert_eq!(
|
||||
payload.get("itemType").and_then(serde_json::Value::as_str),
|
||||
Some("mcpToolCall")
|
||||
@@ -4567,10 +4537,6 @@ mod tests {
|
||||
payload.get("itemId").and_then(serde_json::Value::as_str),
|
||||
Some("item-1")
|
||||
);
|
||||
assert_eq!(
|
||||
payload.get("turnId").and_then(serde_json::Value::as_str),
|
||||
Some("turn-1")
|
||||
);
|
||||
// 卡片标题 / 折叠摘要 / kind 属于前端投影:载荷里不得出现这些 UI 语义。
|
||||
assert!(payload.get("toolCall").is_none(), "{payload}");
|
||||
assert!(payload.get("title").is_none(), "{payload}");
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
||||
//! 为什么不复用 `project.jsonl`:那条链路的回读只投影 `role ∈ {user, assistant}` 的
|
||||
//! 文本条目,而且会被注入 Codex 上下文。往里面塞新形状既装不下,又有污染模型上下文的风险。
|
||||
|
||||
use super::direct_thread_raw_item::sanitize_detail_text;
|
||||
use super::direct_thread_wire::sanitize_detail_text;
|
||||
use crate::config::{prepare_game_creator_private_path_for_read, write_game_creator_private_file};
|
||||
use crate::project::{enforce_project_permission_policy, project_append_lock_for};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -5388,7 +5388,7 @@ pub(crate) async fn read_direct_project_history_slice(
|
||||
.and_then(|item| item.get("id"))
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.map(str::to_string);
|
||||
let items = direct_thread_raw_items_from_history(root, &items, |item| {
|
||||
let items = direct_thread_items_from_history(root, &items, |item| {
|
||||
item.get("id")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.and_then(|id| item_timestamps.get(id).copied())
|
||||
|
||||
+1
-2
@@ -1,5 +1,4 @@
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectCodexUserRuntimeRegionPart } from './DirectCodexUserRuntimeRegionPart';
|
||||
|
||||
export type DirectCodexUserContentPart =
|
||||
|
||||
+7
-3
@@ -1,5 +1,9 @@
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectCodexUserMessageItem } from './DirectCodexUserMessageItem';
|
||||
|
||||
export type DirectCodexUserItem = DirectCodexUserMessageItem;
|
||||
/**
|
||||
* DirectProject 本轮 user input 的唯一结构化入口。
|
||||
*/
|
||||
export type DirectCodexUserItem = {
|
||||
type: 'message';
|
||||
} & DirectCodexUserMessageItem;
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectCodexUserItem } from './DirectCodexUserItem';
|
||||
|
||||
export type DirectCodexUserMessageEnvelope = { item: DirectCodexUserItem };
|
||||
+2
-4
@@ -1,11 +1,9 @@
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectCodexUserContentPart } from './DirectCodexUserContentPart';
|
||||
import type { DirectCodexUserRole } from './DirectCodexUserRole';
|
||||
|
||||
export type DirectCodexUserMessageItem = {
|
||||
type: 'message';
|
||||
role: DirectCodexUserRole;
|
||||
content: DirectCodexUserContentPart[];
|
||||
content: Array<DirectCodexUserContentPart>;
|
||||
id: string;
|
||||
};
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type DirectCodexUserRole = 'user';
|
||||
|
||||
+9
-9
@@ -1,13 +1,13 @@
|
||||
// This file is generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type DirectCodexUserRuntimeRegionPart = {
|
||||
label: string;
|
||||
runId?: string;
|
||||
versionId?: string;
|
||||
elementTag?: string;
|
||||
elementRole?: string;
|
||||
text?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
resourceIds: string[];
|
||||
runId: string | null;
|
||||
versionId: string | null;
|
||||
elementTag: string | null;
|
||||
elementRole: string | null;
|
||||
text: string | null;
|
||||
width: number | null;
|
||||
height: number | null;
|
||||
resourceIds: Array<string>;
|
||||
};
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectThreadEvent } from './DirectThreadEvent';
|
||||
|
||||
export type DirectThreadConsumeResult = { events: Array<DirectThreadEvent> };
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* 增量正文属于哪类条目。
|
||||
*/
|
||||
export type DirectThreadDeltaKind = 'message' | 'reasoning';
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectThreadDeltaKind } from './DirectThreadDeltaKind';
|
||||
import type { DirectThreadItem } from './DirectThreadItem';
|
||||
import type { DirectThreadRequestKind } from './DirectThreadRequestKind';
|
||||
|
||||
/**
|
||||
* Thread Manager 下发的运行态事件。
|
||||
*
|
||||
* 顺序由数组顺序给出(同一个 subscriber 的 `consume` 按队列顺序返回),因此不需要 `seq`:
|
||||
* 游标是 Thread Manager 的内部事实,不下发。
|
||||
*
|
||||
* 事件不带回合身份:DirectProject 同一时刻只有一个回合在跑,"当前回合是否还在跑"由
|
||||
* 生命周期事件在序列中的位置给出,`turn_id` 对前端没有任何额外信息。
|
||||
*/
|
||||
export type DirectThreadEvent =
|
||||
| { type: 'turn.started' }
|
||||
| { type: 'turn.completed'; status: string }
|
||||
| { type: 'item.started'; item: DirectThreadItem }
|
||||
| { type: 'item.completed'; item: DirectThreadItem }
|
||||
| {
|
||||
type: 'item.delta';
|
||||
itemId: string;
|
||||
kind: DirectThreadDeltaKind;
|
||||
delta: string;
|
||||
}
|
||||
| {
|
||||
type: 'request';
|
||||
kind: DirectThreadRequestKind;
|
||||
requestId: string | null;
|
||||
};
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* 一条文件变更。
|
||||
*/
|
||||
export type DirectThreadFileChange = {
|
||||
path: string;
|
||||
/**
|
||||
* `add` | `update` | `delete`
|
||||
*/
|
||||
kind: string;
|
||||
};
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectThreadItem } from './DirectThreadItem';
|
||||
|
||||
export type DirectThreadHistorySlice = {
|
||||
/**
|
||||
* 脱敏条目,顺序即文件顺序;与运行态事件里的条目同形。
|
||||
*/
|
||||
items: Array<DirectThreadItem>;
|
||||
hasMore: boolean;
|
||||
/**
|
||||
* 本次切片的原始 item id 锚点:无论切片里有没有可显示条目,分页都靠它向前。
|
||||
*/
|
||||
firstItemId: string | null;
|
||||
};
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectThreadFileChange } from './DirectThreadFileChange';
|
||||
|
||||
/**
|
||||
* 聊天视图的输入条目:一条 Codex 原始条目的脱敏投影。
|
||||
*
|
||||
* `itemType` 就是 Codex 的原始类型,逐字透传;前端按它决定投影成消息、思考还是工具卡片。
|
||||
* 未识别的类型走 [`DirectThreadItem::Other`],Rust 不替前端决定它是否可见。
|
||||
*
|
||||
* 条目上的 `at` 是只用于显示的毫秒时间戳:ts-rs 默认把 `u64` 映射成 `bigint`,
|
||||
* 而 Tauri 的 JSON 通道传过来的是 `number`,因此统一标 `#[ts(as = "f64")]` 对齐。
|
||||
*/
|
||||
export type DirectThreadItem =
|
||||
| {
|
||||
itemType: 'message';
|
||||
/**
|
||||
* 归一身份:全链路只有这一个 id。
|
||||
*/
|
||||
itemId: string;
|
||||
/**
|
||||
* 原始 role(`user` / `assistant` / `system` / …);显示与否由前端判断。
|
||||
*/
|
||||
role: string;
|
||||
text: string;
|
||||
at: number;
|
||||
}
|
||||
| { itemType: 'reasoning'; itemId: string; text: string; at: number }
|
||||
| {
|
||||
itemType: 'function_call';
|
||||
itemId: string;
|
||||
name: string;
|
||||
arguments: string;
|
||||
at: number;
|
||||
}
|
||||
| {
|
||||
itemType: 'function_call_output';
|
||||
itemId: string;
|
||||
output: string;
|
||||
at: number;
|
||||
}
|
||||
| {
|
||||
itemType: 'commandExecution';
|
||||
itemId: string;
|
||||
command: string;
|
||||
output: string | null;
|
||||
/**
|
||||
* app-server 原始状态:`inProgress` / `completed` / `failed` / `declined` / …
|
||||
*/
|
||||
status: string | null;
|
||||
exitCode: number | null;
|
||||
at: number;
|
||||
}
|
||||
| {
|
||||
itemType: 'fileChange';
|
||||
itemId: string;
|
||||
changes: Array<DirectThreadFileChange>;
|
||||
at: number;
|
||||
}
|
||||
| {
|
||||
itemType: 'mcpToolCall';
|
||||
itemId: string;
|
||||
tool: string;
|
||||
arguments: string;
|
||||
output: string | null;
|
||||
status: string | null;
|
||||
at: number;
|
||||
}
|
||||
| {
|
||||
itemType: 'webSearch';
|
||||
itemId: string;
|
||||
query: string | null;
|
||||
output: string | null;
|
||||
at: number;
|
||||
}
|
||||
| { itemType: 'contextCompaction'; itemId: string; at: number }
|
||||
| { itemType: 'other'; itemId: string; rawType: string; at: number };
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
/**
|
||||
* 审批 / 提问请求与解决:本轮只透传,不并入聊天状态。
|
||||
*/
|
||||
export type DirectThreadRequestKind =
|
||||
| 'approval.requested'
|
||||
| 'ask.requested'
|
||||
| 'request.resolved';
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { DirectThreadEvent } from './DirectThreadEvent';
|
||||
|
||||
export type DirectThreadSubscriptionBootstrap = {
|
||||
subscriptionId: string;
|
||||
/**
|
||||
* 首屏历史锚点:`project.jsonl` 里最后一条原始 item id。
|
||||
*/
|
||||
lastCompletedItemId: string | null;
|
||||
/**
|
||||
* 该 subscriber 此刻应当处理的运行态事件(游标已经在队尾)。
|
||||
*/
|
||||
events: Array<DirectThreadEvent>;
|
||||
};
|
||||
Reference in New Issue
Block a user