1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Tool-dispatch approval primitives shared between the agent
//! runtime (`entelix-agents::ApprovalLayer`) and the graph runtime
//! (`entelix-graph::Command::ApproveTool` resume path).
//!
//! These types live in `entelix-core` so both downstream crates can
//! reference them without violating the workspace DAG (`entelix-graph`
//! depends on `entelix-core`; `entelix-agents` depends on both).
//!
//! Operators implement the `Approver` trait in `entelix-agents` and
//! return [`ApprovalDecision`] from `decide`. The HITL pause-and-
//! resume flow uses [`Command::ApproveTool`](entelix-graph) carrying
//! an `ApprovalDecision` directly — the SDK threads the decision to
//! the resumed dispatch through an internal context extension, so
//! operators never construct the carrier manually.
use HashMap;
use ;
/// Outcome of a single tool-dispatch approval decision.
///
/// Returned by `entelix_agents::Approver::decide`. The agent
/// runtime maps each variant onto the dispatch outcome:
///
/// - `Approve` → the inner tool service runs.
/// - `Reject { reason }` → the dispatch short-circuits with
/// `Error::InvalidRequest` carrying `reason` (the model receives
/// the lean text).
/// - `AwaitExternal` → the dispatch raises `Error::Interrupted`
/// with [`InterruptionKind::ApprovalPending`](crate::interruption::InterruptionKind::ApprovalPending)
/// carrying the `tool_use_id`. The graph dispatch loop
/// persists a checkpoint and bubbles the typed error to the
/// caller. Resume via
/// `entelix_graph::Command::ApproveTool { tool_use_id, decision }`
/// threads the operator's eventual decision back through; the
/// approver is not re-asked for that `tool_use_id`.
/// Resume-side mapping from `tool_use_id` to the operator's
/// decision. Attached to `ExecutionContext::extension` so the
/// agent's approval layer reads the decision during a re-fired
/// dispatch and short-circuits the approver for any `tool_use_id`
/// whose decision is present.
///
/// Two attachment paths:
///
/// - **Typed (recommended)**: `Command::ApproveTool { tool_use_id,
/// decision }` on `CompiledGraph::resume_with` constructs the
/// carrier internally. Operators using the graph-resume path
/// never touch this type directly.
/// - **Direct (advanced)**: operators dispatching through the
/// raw `ToolRegistry` (no graph, no checkpointer) attach the
/// carrier on the request `ExecutionContext`:
///
/// ```ignore
/// let mut pending = PendingApprovalDecisions::new();
/// pending.insert("tu-1", ApprovalDecision::Approve);
/// let ctx = ExecutionContext::new().add_extension(pending);
/// registry.dispatch("tu-1", "echo", input, &ctx).await?;
/// ```
///
/// The direct path is the canonical mechanism the SDK exposes
/// for non-graph dispatch (e.g. tests, custom embedded loops).
/// `Command::ApproveTool` is the higher-level convenience for
/// graph-driven agents.