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
107
108
//! `AuditSink` — typed channel for managed-agent audit events that
//! the SDK records into a session log.
//!
//! ## Why a `*_core` trait rather than `entelix-session::SessionLog`
//!
//! `SessionLog` is the persistence shape (`async append(events)`).
//! Tools / graphs / recipes that want to *emit* a single audit
//! event need a narrower surface: typed `record_*` methods with no
//! awareness of `GraphEvent`'s exact wire shape, ordinal accounting,
//! or `ThreadKey` plumbing. Pinning `AuditSink` in `entelix-core`
//! also breaks the dependency cycle that would otherwise force
//! `entelix-tools` and `entelix-graph` to depend on `entelix-session`.
//!
//! ## Wire-up
//!
//! Operators wire one `Arc<dyn AuditSink>` per agent run via
//! [`crate::context::ExecutionContext::with_audit_sink`]. The
//! `entelix-session` crate ships `SessionAuditSink` — a tiny
//! adapter that maps each `record_*` call onto a fire-and-forget
//! `SessionLog::append`. Recipes that don't wire a sink see no
//! change in behaviour: the absent extension makes every emit site
//! a no-op via `ctx.audit_sink()` returning `None`.
//!
//! ## Concurrency
//!
//! Methods are `&self` and synchronous so emit sites sit inside hot
//! dispatch loops without `.await` ceremony. Implementations that
//! ultimately persist via async backends spawn a detached task
//! inside the method — the audit channel is fire-and-forget by
//! design (an audit-sink failure must never block the agent).
use Arc;
/// Typed audit-event channel. See module docs.
/// `Arc`-shaped handle the [`crate::context::ExecutionContext`]
/// extension carries. Wraps `Arc<dyn AuditSink>` in a newtype so
/// the `Extensions` slot lookup by `TypeId` is unambiguous.
;