swarm repositories / source
aboutsummaryrefslogtreecommitdiff
path: root/crates/phone-opus/src/mcp/service.rs
diff options
context:
space:
mode:
authormain <main@swarm.moe>2026-03-24 19:09:28 -0400
committermain <main@swarm.moe>2026-03-24 19:09:28 -0400
commit8b090c3d0daf8b336aab9074b0d8aa31a688e232 (patch)
tree175d2f2313ea34038278cbd066f10fae6bf244b3 /crates/phone-opus/src/mcp/service.rs
parent57205cd29c41afe85c78a3b5c1962bb3d1f1b27c (diff)
downloadphone_opus-8b090c3d0daf8b336aab9074b0d8aa31a688e232.zip
Surface reusable consult context on failures
Diffstat (limited to 'crates/phone-opus/src/mcp/service.rs')
-rw-r--r--crates/phone-opus/src/mcp/service.rs67
1 files changed, 64 insertions, 3 deletions
diff --git a/crates/phone-opus/src/mcp/service.rs b/crates/phone-opus/src/mcp/service.rs
index 993a0e4..c5c2d66 100644
--- a/crates/phone-opus/src/mcp/service.rs
+++ b/crates/phone-opus/src/mcp/service.rs
@@ -16,7 +16,7 @@ use time::{OffsetDateTime, format_description::well_known::Rfc3339};
use users::get_current_uid;
use uuid::Uuid;
-use crate::mcp::fault::{FaultRecord, FaultStage};
+use crate::mcp::fault::{ConsultFaultContext, FaultContext, FaultRecord, FaultStage};
use crate::mcp::output::{
ToolOutput, fallback_detailed_tool_output, split_presentation, tool_success,
};
@@ -89,7 +89,7 @@ impl WorkerService {
let request = ConsultRequest::parse(args)
.map_err(|error| invalid_consult_request(self.generation, &operation, error))?;
let response = invoke_claude(&request)
- .map_err(|error| consult_fault(self.generation, &operation, error))?;
+ .map_err(|error| consult_fault(self.generation, &operation, &request, error))?;
consult_output(&request, &response, self.generation, &operation)?
}
other => {
@@ -844,9 +844,11 @@ fn invalid_consult_request(
fn consult_fault(
generation: Generation,
operation: &str,
+ request: &ConsultRequest,
error: ConsultInvocationError,
) -> FaultRecord {
- match error {
+ let context = consult_fault_context(request, &error);
+ let record = match error {
ConsultInvocationError::Spawn(source) => FaultRecord::process(
generation,
FaultStage::Claude,
@@ -857,7 +859,66 @@ fn consult_fault(
| ConsultInvocationError::Downstream(detail) => {
FaultRecord::downstream(generation, FaultStage::Claude, operation, detail)
}
+ };
+ record.with_context(context)
+}
+
+fn consult_fault_context(request: &ConsultRequest, error: &ConsultInvocationError) -> FaultContext {
+ let detail = match error {
+ ConsultInvocationError::Spawn(_) => None,
+ ConsultInvocationError::InvalidJson(detail)
+ | ConsultInvocationError::Downstream(detail) => Some(detail.as_str()),
+ };
+ let reused_session_id = request.reused_session_id();
+ let downstream_session_id = detail.and_then(downstream_session_id);
+ let resume_session_id = downstream_session_id
+ .clone()
+ .or_else(|| reused_session_id.clone());
+ let quota_reset_hint = detail.and_then(quota_reset_hint);
+ let quota_limited = quota_reset_hint.is_some();
+ let retry_hint = consult_retry_hint(quota_limited, resume_session_id.as_deref());
+ FaultContext {
+ consult: Some(ConsultFaultContext {
+ cwd: request.cwd.display(),
+ context_mode: request.context_mode().to_owned(),
+ reused_session_id,
+ downstream_session_id,
+ resume_session_id,
+ quota_limited,
+ quota_reset_hint,
+ retry_hint,
+ }),
+ }
+}
+
+fn downstream_session_id(detail: &str) -> Option<String> {
+ let value = serde_json::from_str::<Value>(detail).ok()?;
+ let session_id = value.get("session_id")?.as_str()?;
+ SessionHandle::parse(session_id).map(|session| session.display())
+}
+
+fn quota_reset_hint(detail: &str) -> Option<String> {
+ let (_, suffix) = detail.split_once("resets ")?;
+ let hint = suffix.trim();
+ (!hint.is_empty()).then(|| hint.to_owned())
+}
+
+fn consult_retry_hint(quota_limited: bool, resume_session_id: Option<&str>) -> Option<String> {
+ if quota_limited {
+ return Some(match resume_session_id {
+ Some(session_id) => format!(
+ "wait for the quota window to reset, then retry consult on the same cwd; phone_opus will reuse resume_session {session_id} automatically"
+ ),
+ None => {
+ "wait for the quota window to reset, then retry consult on the same cwd".to_owned()
+ }
+ });
}
+ resume_session_id.map(|session_id| {
+ format!(
+ "retry consult on the same cwd; phone_opus will reuse resume_session {session_id} automatically"
+ )
+ })
}
pub(crate) fn consult_job_tool_output(