flowcat_core/session.rs
1// SPDX-License-Identifier: Apache-2.0
2//
3//! Call bootstrap + finalize seam.
4//!
5//! The embedder implements [`SessionSource`] over its own control-plane HTTP
6//! API (resolve a run+token into a call config, upload artifacts, write the
7//! finalize). flowcat-core only sees the opaque shapes (see DESIGN.md
8//! "Trait contracts"). [`ResolvedCall`], [`Finalize`], [`UploadTarget`], and
9//! [`Usage`] are defined in [`crate::types`] and re-exported here.
10
11use async_trait::async_trait;
12
13use crate::error::FlowcatError;
14
15// Re-export so callers use `crate::session::{ResolvedCall, Finalize, ...}` as
16// the design lays out; the definitions live in `frame`.
17pub use crate::types::{Finalize, ResolvedCall, ToolDecl, UploadTarget, Usage};
18
19/// Bootstraps a call from a run id + per-call token and writes results back.
20///
21/// `Send + Sync` because it is shared across the spawned per-leg tasks of a call.
22#[async_trait]
23pub trait SessionSource: Send + Sync {
24 /// Resolve a run id + per-call token into the call's configuration.
25 async fn resolve(&self, run_id: i64, token: &str) -> Result<ResolvedCall, FlowcatError>;
26
27 /// Mark the run complete and persist usage / collected vars / artifact URLs.
28 async fn complete(&self, run_id: i64, token: &str, fin: Finalize) -> Result<(), FlowcatError>;
29
30 /// Obtain a (pre-signed) upload target for an artifact (`kind` = recording/transcript/…).
31 async fn artifact_upload_url(
32 &self,
33 run_id: i64,
34 token: &str,
35 kind: &str,
36 ) -> Result<UploadTarget, FlowcatError>;
37
38 /// PUT raw bytes to a (pre-signed) upload URL with the given content type.
39 async fn put_bytes(
40 &self,
41 url: &str,
42 bytes: Vec<u8>,
43 content_type: &str,
44 ) -> Result<(), FlowcatError>;
45
46 /// Fetch the current node's MCP/HTTP **workflow tools** (distinct from the
47 /// brain's graph transitions). These are the tools the control plane will
48 /// execute on the agent's behalf (see [`SessionSource::tool_call`]).
49 ///
50 /// Implementations **degrade gracefully**: any HTTP/parse failure returns
51 /// `Ok(vec![])` (the call proceeds with no node tools) rather than aborting
52 /// the live call. `params` is the tool's JSON-Schema (`input_schema`,
53 /// defaulting to an empty object).
54 async fn node_tools(
55 &self,
56 run_id: i64,
57 token: &str,
58 node_id: &str,
59 ) -> Result<Vec<ToolDecl>, FlowcatError>;
60
61 /// Relay a workflow tool call (name + args) to the control plane, which runs
62 /// the MCP/HTTP egress and returns the tool result `content` (fed back to the
63 /// model). The `is_error` flag from the control plane is folded into the
64 /// returned text — the model handles failures conversationally — so this
65 /// returns a single string in all cases. On a transport error it returns a
66 /// short "temporarily unavailable" message so the call continues.
67 async fn tool_call(
68 &self,
69 run_id: i64,
70 token: &str,
71 node_id: &str,
72 tool_name: &str,
73 args: &serde_json::Value,
74 ) -> Result<String, FlowcatError>;
75}