补齐自主Swarm终端验收与恢复门禁
新增Project Supervisor自主终端协作真实E2E与隔离AppData安全检查 为Swarm Chat输出脱敏单行turn report并补齐终端收束断言 强化Supervisor多专业并行委派与语义验收提示 修复静态委派和隔离all-join混合等待及认领恢复 为agent.message增加语义幂等持久化并避免重复消息伪造进展 补充消息幂等、混合恢复和turn report定向回归测试 增加根级与应用级自主验收命令并更新Runtime技术文档
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
"agent-run": "node scripts/run-cli-with-config.mjs --agent-run",
|
||||
"agent-run:smoke": "node scripts/smoke-agent-run-local-provider.mjs",
|
||||
"agent-runtime:real-e2e": "node scripts/agent-runtime-real-e2e.mjs",
|
||||
"agent-runtime:supervisor-swarm-autonomous-chat-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-autonomous-chat",
|
||||
"agent-runtime:supervisor-swarm-transient-retry-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite supervisor-swarm-transient-retry",
|
||||
"agent-runtime:steer-real-e2e": "node scripts/agent-runtime-steer-real-e2e.mjs",
|
||||
"agent-runtime:steer-runner-kill-real-e2e": "node scripts/agent-runtime-real-e2e.mjs --suite steer-runner-kill",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1596,12 +1596,23 @@ fn supervisor_delivery_pending_action_has_durable_side_effect_at(
|
||||
pending: &AgentRuntimePendingToolAction,
|
||||
) -> Result<bool, String> {
|
||||
match pending.action.tool.as_str() {
|
||||
"agent.run_status" => static_delegate_claim_exists_at(
|
||||
root,
|
||||
&pending.agent_id,
|
||||
&pending.run_id,
|
||||
&pending.action_id,
|
||||
),
|
||||
"agent.run_status" => {
|
||||
if static_delegate_claim_exists_at(
|
||||
root,
|
||||
&pending.agent_id,
|
||||
&pending.run_id,
|
||||
&pending.action_id,
|
||||
)? {
|
||||
Ok(true)
|
||||
} else {
|
||||
isolated_join_claim_exists_for_parent_action_at(
|
||||
root,
|
||||
&pending.agent_id,
|
||||
&pending.run_id,
|
||||
&pending.action_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
"agent.delegate" => {
|
||||
let target_agent_id =
|
||||
normalize_game_creator_runtime_agent_id(&agent_runtime_tool_input_text(
|
||||
@@ -6240,6 +6251,63 @@ fn persist_waiting_static_delegate_parent_context_at(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn persist_waiting_isolated_parent_context_at(
|
||||
root: &Path,
|
||||
runtime: &mut AgentRuntimeState,
|
||||
task: &str,
|
||||
plan: &AgentRuntimeToolPlan,
|
||||
observations: &mut Vec<AgentRuntimeToolObservation>,
|
||||
next_loop_index: usize,
|
||||
context_tracker: &mut AgentRuntimeContextWindowTracker,
|
||||
blocker: AgentRuntimeToolObservation,
|
||||
) -> Result<(), String> {
|
||||
let blocker_summary = blocker.summary();
|
||||
runtime.status = "running".to_string();
|
||||
runtime.phase = "waiting-for-isolated-join".to_string();
|
||||
runtime.current_action = "等待动态隔离 Agent 的 all-join".to_string();
|
||||
runtime.waiting_on = "隔离子 Agent 完成并由父 run 认领 all-join".to_string();
|
||||
runtime.next_step = "all-join ready 后自动唤醒当前父 run".to_string();
|
||||
runtime.observations.push(blocker_summary.clone());
|
||||
runtime.updated_at = unix_timestamp();
|
||||
context_tracker.record(&blocker);
|
||||
observations.push(blocker);
|
||||
persist_game_creator_agent_runtime_context(
|
||||
root,
|
||||
runtime,
|
||||
task,
|
||||
plan,
|
||||
observations,
|
||||
next_loop_index,
|
||||
context_tracker,
|
||||
)?;
|
||||
append_game_creator_agent_runtime_task(root, runtime)?;
|
||||
refresh_game_creator_agent_runtime_task_queue(root, runtime)?;
|
||||
write_game_creator_agent_runtime_state(root, runtime)?;
|
||||
let _ = append_game_creator_agent_runtime_event(
|
||||
root,
|
||||
runtime,
|
||||
"observation",
|
||||
"running",
|
||||
"waiting-for-isolated-join",
|
||||
&blocker_summary,
|
||||
None,
|
||||
);
|
||||
let _ = append_agent_db_record(
|
||||
root,
|
||||
serde_json::json!({
|
||||
"recordType": "agent.runtime.agent.isolated_join.waiting",
|
||||
"agentId": runtime.agent_id,
|
||||
"taskId": runtime.task_id,
|
||||
"sessionId": runtime.session_id,
|
||||
"runId": runtime.run_id,
|
||||
"status": "waiting-for-isolated-join",
|
||||
"nextLoopIndex": next_loop_index,
|
||||
}),
|
||||
);
|
||||
emit_game_creator_agent_runtime_update(root, &runtime.agent_id);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_game_creator_agent_background_task_pass_with_context(
|
||||
root: PathBuf,
|
||||
agent_id: String,
|
||||
@@ -6333,6 +6401,35 @@ async fn run_game_creator_agent_background_task_pass_with_context(
|
||||
}
|
||||
};
|
||||
if !consumed_steer {
|
||||
if let Some(blocker) =
|
||||
isolated_join_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
{
|
||||
let waits_for_join = blocker
|
||||
.detail
|
||||
.as_deref()
|
||||
.is_some_and(isolated_join_barrier_has_waiting_groups);
|
||||
if waits_for_join {
|
||||
if let Err(error) = persist_waiting_isolated_parent_context_at(
|
||||
&root,
|
||||
&mut runtime,
|
||||
&task,
|
||||
&plan,
|
||||
&mut observations,
|
||||
loop_index,
|
||||
&mut context_tracker,
|
||||
blocker,
|
||||
) {
|
||||
return fail_game_creator_agent_background_context_at(
|
||||
&root,
|
||||
&agent_id,
|
||||
&session_id,
|
||||
runtime,
|
||||
&format!("持久化动态隔离 Agent all-join 等待状态失败:{error}"),
|
||||
);
|
||||
}
|
||||
return AgentBackgroundTaskOutcome::WaitingForIsolatedJoin;
|
||||
}
|
||||
}
|
||||
if let Some(blocker) =
|
||||
static_delegate_completion_blocker_at(&root, &agent_id, &runtime.run_id)
|
||||
{
|
||||
@@ -11236,7 +11333,10 @@ impl AgentRuntimeContextWindowTracker {
|
||||
}
|
||||
|
||||
pub(crate) fn record(&mut self, observation: &AgentRuntimeToolObservation) {
|
||||
if observation.tool == "runtime.context" {
|
||||
if observation.tool == "runtime.context"
|
||||
|| (observation.tool == "agent.message"
|
||||
&& observation.detail.as_deref() == Some("messageAppended=false"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
self.observation_signatures
|
||||
@@ -19997,7 +20097,9 @@ pub(crate) async fn execute_game_creator_agent_runtime_tool_action_with_pending_
|
||||
.await
|
||||
}
|
||||
"blackboard.write" => observe_agent_runtime_blackboard_write(root, agent_id, &action.input),
|
||||
"agent.message" => observe_agent_runtime_agent_message(root, agent_id, &action.input),
|
||||
"agent.message" => {
|
||||
observe_agent_runtime_agent_message(root, agent_id, run_id, &action.input)
|
||||
}
|
||||
"agent.delegate" => observe_agent_runtime_project_snapshot_with_lock(
|
||||
root,
|
||||
agent_id,
|
||||
@@ -26274,9 +26376,10 @@ fn observe_agent_runtime_blackboard_write(
|
||||
}
|
||||
}
|
||||
|
||||
fn observe_agent_runtime_agent_message(
|
||||
pub(crate) fn observe_agent_runtime_agent_message(
|
||||
root: &Path,
|
||||
agent_id: &str,
|
||||
run_id: &str,
|
||||
input: &serde_json::Value,
|
||||
) -> AgentRuntimeToolObservation {
|
||||
let target_agent_id = agent_runtime_tool_input_text(input, &["agentId", "targetAgentId"]);
|
||||
@@ -26313,36 +26416,63 @@ fn observe_agent_runtime_agent_message(
|
||||
};
|
||||
let content = truncate_agent_runtime_text(sanitize_prompt_context(&content).as_str(), 1_200);
|
||||
let message = format!("来自 {agent_id} 的定向消息:{content}");
|
||||
let result = append_local_conversation_message_at(
|
||||
root,
|
||||
Some(&target_agent_id),
|
||||
LocalConversationMessage {
|
||||
role: "tool".to_string(),
|
||||
content: message,
|
||||
agent_id: None,
|
||||
},
|
||||
)
|
||||
.and_then(|conversation| {
|
||||
let relative_path =
|
||||
agent_runtime_relative_project_path(root, Path::new(&conversation.path))?;
|
||||
append_agent_db_record(
|
||||
root,
|
||||
serde_json::json!({
|
||||
"recordType": "agent.runtime.agent.message",
|
||||
"agentId": agent_id,
|
||||
"targetAgentId": target_agent_id.clone(),
|
||||
"targetSessionId": conversation.session_id,
|
||||
"path": relative_path,
|
||||
}),
|
||||
)
|
||||
});
|
||||
let result = resolve_agent_conversation_session_id_at(root, &target_agent_id, None, true)
|
||||
.and_then(|target_session_id| {
|
||||
let content_sha256 = format!("{:x}", Sha256::digest(content.as_bytes()));
|
||||
let semantic_identity = format!(
|
||||
"{agent_id}\n{run_id}\n{target_agent_id}\n{target_session_id}\n{content_sha256}"
|
||||
);
|
||||
let semantic_sha256 = format!("{:x}", Sha256::digest(semantic_identity.as_bytes()));
|
||||
let message_id = format!("agent-message-{semantic_sha256}");
|
||||
let audit_action_id = format!("action-{}", &semantic_sha256[..24]);
|
||||
append_local_conversation_message_for_session_idempotent_with_status_at(
|
||||
root,
|
||||
Some(&target_agent_id),
|
||||
Some(&target_session_id),
|
||||
LocalConversationMessage {
|
||||
role: "tool".to_string(),
|
||||
content: message,
|
||||
agent_id: None,
|
||||
},
|
||||
&message_id,
|
||||
)
|
||||
.and_then(|(conversation, appended)| {
|
||||
let relative_path =
|
||||
agent_runtime_relative_project_path(root, Path::new(&conversation.path))?;
|
||||
append_agent_db_agent_message_if_missing(
|
||||
root,
|
||||
agent_id,
|
||||
run_id,
|
||||
&audit_action_id,
|
||||
serde_json::json!({
|
||||
"recordType": "agent.runtime.agent.message",
|
||||
"agentId": agent_id,
|
||||
"runId": run_id,
|
||||
"actionId": audit_action_id,
|
||||
"messageId": message_id,
|
||||
"targetAgentId": target_agent_id,
|
||||
"targetSessionId": conversation.session_id,
|
||||
"path": relative_path,
|
||||
"contentSha256": content_sha256,
|
||||
"contentChars": content.chars().count(),
|
||||
}),
|
||||
)?;
|
||||
Ok(appended)
|
||||
})
|
||||
});
|
||||
match result {
|
||||
Ok(()) => AgentRuntimeToolObservation {
|
||||
Ok(true) => AgentRuntimeToolObservation {
|
||||
tool: "agent.message".to_string(),
|
||||
status: "ok".to_string(),
|
||||
summary: format!("已给 {target_agent_id} 留消息"),
|
||||
detail: Some(content),
|
||||
},
|
||||
Ok(false) => AgentRuntimeToolObservation {
|
||||
tool: "agent.message".to_string(),
|
||||
status: "ok".to_string(),
|
||||
summary: format!("给 {target_agent_id} 的相同定向消息已存在,未重复追加"),
|
||||
detail: Some("messageAppended=false".to_string()),
|
||||
},
|
||||
Err(error) => AgentRuntimeToolObservation {
|
||||
tool: "agent.message".to_string(),
|
||||
status: "failed".to_string(),
|
||||
@@ -29287,6 +29417,28 @@ fn claimed_isolated_join_count_for_parent_at(
|
||||
Ok(count)
|
||||
}
|
||||
|
||||
fn isolated_join_claim_exists_for_parent_action_at(
|
||||
root: &Path,
|
||||
parent_agent_id: &str,
|
||||
parent_run_id: &str,
|
||||
action_id: &str,
|
||||
) -> Result<bool, String> {
|
||||
for join in reconcile_all_isolated_groups_at(root)?
|
||||
.into_iter()
|
||||
.filter(|join| {
|
||||
join.parent_agent_id == parent_agent_id && join.parent_run_id == parent_run_id
|
||||
})
|
||||
{
|
||||
if read_isolated_join_delivery_at(root, &join)?.is_some_and(|delivery| {
|
||||
delivery.status == IsolatedAgentJoinDeliveryStatus::ClaimedByParent
|
||||
&& delivery.claimed_by_action_id.as_deref() == Some(action_id)
|
||||
}) {
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn ready_isolated_join_status_for_parent_at(
|
||||
root: &Path,
|
||||
parent_agent_id: &str,
|
||||
@@ -33552,7 +33704,7 @@ pub(crate) fn game_creator_agent_runtime_tool_plan_system_prompt_for_agent(
|
||||
return prompt;
|
||||
}
|
||||
let prompt = format!(
|
||||
"{prompt}\n\n你当前是项目唯一面向用户的 Project Supervisor,并拥有最终回复权。每一轮都必须把用户原始目标视为最高层业务目标,专业 Agent 回执只能补充证据,不能把回执内容改写成新目标。优先把边界清晰的专业工作通过带 acceptanceCriteria 和 expectedArtifacts 的 agent.delegate 分给静态专业 Agent,把互不重叠的临时并行检查通过 agent.spawn_isolated 分派;已有委派未收束时不要重复委派。需要等待专业 Agent 时返回空 response,让 Runtime 的 delegate/all-join 完成屏障保持同一父 run;取得 readyDelegateReceipts 或 readyIsolatedJoins 后直接整合结果。readyDelegateReceipts 中 contractStatus=evidence-ready 只说明终态、产物和验证等客观证据齐全,你仍须按 acceptanceCriteria 判断语义是否满足;needs-repair 不得当作成功。客观或语义不满足时可以发起一次新 agent.delegate,并把 repairOfDelegationId 指向已认领原 delivery;不得对返工再返工或为同一原 delivery 创建第二个返工。专业结果冲突且无法依据用户目标裁决时,合并问题后用一次 user.input_request 询问用户。只有实现路径、产品取舍或缺失事实会实质改变结果时才调用 user.input_request;项目内可读取事实、权限确认和工具失败不得伪装成用户问题。只在所有必要回执已认领、所有必要返工也已认领、项目副作用已验证且没有待确认动作或待回答请求时给用户最终回复。不要向用户暴露内部 task/event、工具计划、动态 child ID 或调试状态。"
|
||||
"{prompt}\n\n你当前是项目唯一面向用户的 Project Supervisor,并拥有最终回复权。每一轮都必须把用户原始目标视为最高层业务目标,专业 Agent 回执只能补充证据,不能把回执内容改写成新目标。总控不能替代已有专业角色完成其领域交付:只要仓库目标同时包含两个以上互不依赖的专业方向,就必须自行查看静态角色目录,选择最匹配的不同专业 Agent,并在同一个 native planning 批次用带 acceptanceCriteria 和 expectedArtifacts 的 agent.delegate 发起委派,让这些方向并行;用户不需要点名 Agent、指定数量或提醒并行。只有没有匹配专业角色、纯协调工作或一两步轻量读取时才由总控直接处理。互不重叠的临时并行检查通过 agent.spawn_isolated 分派;已有委派未收束时不要重复委派。需要等待专业 Agent 时返回空 response,让 Runtime 的 delegate/all-join 完成屏障保持同一父 run;取得 readyDelegateReceipts 或 readyIsolatedJoins 后直接整合结果。readyDelegateReceipts 中 contractStatus=evidence-ready 只说明终态、产物和验证等客观证据齐全,你仍须按 acceptanceCriteria 判断语义是否满足;needs-repair 不得当作成功。客观或语义不满足时可以发起一次新 agent.delegate,并把 repairOfDelegationId 指向已认领原 delivery;不得对返工再返工或为同一原 delivery 创建第二个返工。专业结果冲突且无法依据用户目标裁决时,合并问题后用一次 user.input_request 询问用户。只有实现路径、产品取舍或缺失事实会实质改变结果时才调用 user.input_request;项目内可读取事实、权限确认和工具失败不得伪装成用户问题。只在所有必要回执已认领、所有必要返工也已认领、项目副作用已验证且没有待确认动作或待回答请求时给用户最终回复。不要向用户暴露内部 task/event、工具计划、动态 child ID 或调试状态。"
|
||||
);
|
||||
format!(
|
||||
"{prompt}\n\n普通 agent.run_status 的 claimedDelegateContracts 只提供已认领合同目录。语义复核或返工前必须用原 delegationId 再调用 agent.run_status,读取 claimedDelegateContract 中未截断的 acceptanceCriteria 和 expectedArtifacts,并在 repair agent.delegate 中逐项原样提交。若返工因合同未完整继承而失败,失败 observation 中的 claimedDelegateContract 是同一 durable delivery 的权威快照,必须逐项据此修正;只有该字段缺失或身份不确定时才按同一 delegationId 重读,不得无目标地重复 run_status 或从 action_history 摘要猜测。"
|
||||
@@ -33620,6 +33772,9 @@ pub(crate) fn game_creator_agent_runtime_tool_plan_system_prompt() -> String {
|
||||
let prompt = format!(
|
||||
"{prompt} git.inspect 会返回 commitSnapshotFingerprint;只有当前非零 revision 已由本 run 验证通过,且已完整审阅变更时,才能用 project.git_commit 的 message、显式 paths、expectedHead 和 expectedSnapshotFingerprint 创建本地提交。project.git_commit 不允许访问 remote、切换分支或执行 merge、rebase、reset、stash、tag、submodule、worktree。"
|
||||
);
|
||||
let prompt = format!(
|
||||
"{prompt} 作为被委派的专业 Agent 时,agent.message 只用于确有必要的中途协调,不能替代自身终态交付;验收、产物和验证已完成后,必须把全部必要计划步骤更新为 completed,并用一次最终 response 收束为父 Agent 可认领的回执,不得反复给同一 Agent 留消息或重复读取同一证据来维持 run。"
|
||||
);
|
||||
let isolated_template_ids = GAME_CREATOR_AGENT_GROUP_DEFINITIONS
|
||||
.iter()
|
||||
.flat_map(|group| group.roles.iter().map(|role| role.task_id))
|
||||
|
||||
@@ -1608,6 +1608,86 @@ pub(crate) fn append_agent_db_record_if_missing_for_action(
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn append_agent_db_agent_message_if_missing(
|
||||
root: &Path,
|
||||
agent_id: &str,
|
||||
run_id: &str,
|
||||
action_id: &str,
|
||||
record: serde_json::Value,
|
||||
) -> Result<bool, String> {
|
||||
const RECORD_TYPE: &str = "agent.runtime.agent.message";
|
||||
let object = record
|
||||
.as_object()
|
||||
.ok_or_else(|| "Agent DB 定向消息审计必须是对象".to_string())?;
|
||||
let expected_fields = [
|
||||
"actionId",
|
||||
"agentId",
|
||||
"contentChars",
|
||||
"contentSha256",
|
||||
"messageId",
|
||||
"path",
|
||||
"recordType",
|
||||
"runId",
|
||||
"targetAgentId",
|
||||
"targetSessionId",
|
||||
];
|
||||
let mut actual_fields = object.keys().map(String::as_str).collect::<Vec<_>>();
|
||||
actual_fields.sort_unstable();
|
||||
if actual_fields != expected_fields
|
||||
|| record.get("recordType").and_then(serde_json::Value::as_str) != Some(RECORD_TYPE)
|
||||
|| record.get("agentId").and_then(serde_json::Value::as_str) != Some(agent_id)
|
||||
|| record.get("runId").and_then(serde_json::Value::as_str) != Some(run_id)
|
||||
|| record.get("actionId").and_then(serde_json::Value::as_str) != Some(action_id)
|
||||
{
|
||||
return Err("Agent DB 定向消息审计身份或字段无效".to_string());
|
||||
}
|
||||
if !action_id.strip_prefix("action-").is_some_and(|suffix| {
|
||||
suffix.len() == 24 && suffix.bytes().all(|byte| byte.is_ascii_hexdigit())
|
||||
}) {
|
||||
return Err("Agent DB 定向消息审计 actionId 无效".to_string());
|
||||
}
|
||||
let message_id = record
|
||||
.get("messageId")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or_default();
|
||||
let content_sha256 = record
|
||||
.get("contentSha256")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or_default();
|
||||
let path = record
|
||||
.get("path")
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.unwrap_or_default();
|
||||
if !is_valid_agent_db_prefixed_hex(message_id, "agent-message-", 64)
|
||||
|| !is_valid_agent_db_sha256(content_sha256)
|
||||
|| record
|
||||
.get("contentChars")
|
||||
.and_then(serde_json::Value::as_u64)
|
||||
.is_none_or(|count| count == 0 || count > 1_200)
|
||||
|| ["targetAgentId", "targetSessionId"].iter().any(|field| {
|
||||
record
|
||||
.get(*field)
|
||||
.and_then(serde_json::Value::as_str)
|
||||
.is_none_or(|value| value.trim().is_empty() || value.chars().any(char::is_control))
|
||||
})
|
||||
|| Path::new(path).is_absolute()
|
||||
|| !path.starts_with(".agent/conversations/agents/")
|
||||
|| !path.ends_with(".jsonl")
|
||||
|| path.split('/').any(|segment| segment == "..")
|
||||
{
|
||||
return Err("Agent DB 定向消息审计元数据无效".to_string());
|
||||
}
|
||||
append_agent_db_record_if_missing_for_action_internal(
|
||||
root,
|
||||
RECORD_TYPE,
|
||||
agent_id,
|
||||
run_id,
|
||||
action_id,
|
||||
record,
|
||||
|| {},
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn append_agent_db_process_reconciliation_if_missing_for_action(
|
||||
root: &Path,
|
||||
agent_id: &str,
|
||||
@@ -4878,7 +4958,7 @@ fn append_local_conversation_message_for_session_internal_at(
|
||||
message: LocalConversationMessage,
|
||||
message_id: Option<&str>,
|
||||
finalization_id: Option<&str>,
|
||||
) -> Result<LocalConversationResult, String> {
|
||||
) -> Result<(LocalConversationResult, bool), String> {
|
||||
let LocalConversationMessage {
|
||||
role,
|
||||
content,
|
||||
@@ -4930,7 +5010,8 @@ fn append_local_conversation_message_for_session_internal_at(
|
||||
if message_id.is_some() {
|
||||
return Err("带 messageId 的对话内容不能为空".to_string());
|
||||
}
|
||||
return read_local_conversation_for_session_at(root, agent_id, session_id);
|
||||
return read_local_conversation_for_session_at(root, agent_id, session_id)
|
||||
.map(|conversation| (conversation, false));
|
||||
}
|
||||
let message_id = message_id
|
||||
.map(normalize_local_conversation_message_id)
|
||||
@@ -5040,11 +5121,14 @@ fn append_local_conversation_message_for_session_internal_at(
|
||||
appended,
|
||||
)?;
|
||||
}
|
||||
Ok(local_conversation_result_from_persisted_records(
|
||||
&path,
|
||||
normalized_agent_id,
|
||||
normalized_session_id,
|
||||
&records,
|
||||
Ok((
|
||||
local_conversation_result_from_persisted_records(
|
||||
&path,
|
||||
normalized_agent_id,
|
||||
normalized_session_id,
|
||||
&records,
|
||||
),
|
||||
appended,
|
||||
))
|
||||
}
|
||||
|
||||
@@ -5057,6 +5141,7 @@ pub(crate) fn append_local_conversation_message_for_session_at(
|
||||
append_local_conversation_message_for_session_internal_at(
|
||||
root, agent_id, session_id, message, None, None,
|
||||
)
|
||||
.map(|(conversation, _)| conversation)
|
||||
}
|
||||
|
||||
pub(crate) fn append_local_conversation_message_for_session_idempotent_at(
|
||||
@@ -5074,6 +5159,24 @@ pub(crate) fn append_local_conversation_message_for_session_idempotent_at(
|
||||
Some(message_id),
|
||||
None,
|
||||
)
|
||||
.map(|(conversation, _)| conversation)
|
||||
}
|
||||
|
||||
pub(crate) fn append_local_conversation_message_for_session_idempotent_with_status_at(
|
||||
root: &Path,
|
||||
agent_id: Option<&str>,
|
||||
session_id: Option<&str>,
|
||||
message: LocalConversationMessage,
|
||||
message_id: &str,
|
||||
) -> Result<(LocalConversationResult, bool), String> {
|
||||
append_local_conversation_message_for_session_internal_at(
|
||||
root,
|
||||
agent_id,
|
||||
session_id,
|
||||
message,
|
||||
Some(message_id),
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
pub(crate) fn append_local_conversation_message_for_session_idempotent_with_finalization_at(
|
||||
@@ -5092,6 +5195,7 @@ pub(crate) fn append_local_conversation_message_for_session_idempotent_with_fina
|
||||
Some(message_id),
|
||||
Some(finalization_id),
|
||||
)
|
||||
.map(|(conversation, _)| conversation)
|
||||
}
|
||||
|
||||
pub(crate) fn append_local_conversation_message_at(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user