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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
//! **The loaded set** (REMOTE §5, bl-c907): which of a tool host's advertised
//! tools an agent has made callable, and the file that survives the step.
//!
//! REMOTE §5: *"Loading is the agent's own point-in-time act. From a `get`, the
//! agent loads a client's tools; loaded definitions are callable from that turn
//! on … the invariant is that nothing but an explicit load ever changes the
//! tool surface."*
//!
//! ```text
//! <yog-state-root>/loaded/<workspace>/<agent>.json
//! ```
//!
//! **It is durable because a driver is not.** Each step is a fresh process
//! (§2.11's exec baton), so a set held in RAM would be unloaded by the next
//! hop — the load act would last exactly one turn, which is not what "callable
//! from that turn on" says. It sits under yog's own state root rather than in
//! the workspace, because the workspace is the conversation's git repository
//! and a yog document does not belong in an agent's worktree.
//!
//! **The definition is frozen at the load act, not re-read at assembly.** The
//! file carries the whole advertised element — name, description, JSON Schema —
//! as it stood when the agent loaded it, so [`crate::tool_host::Injection::tools`]
//! is a pure local file read that needs no engine and cannot fail. That is the
//! REMOTE §5 rule (bl-bc7c) rather than a shortcut: *"definitions frozen in the
//! prefix, presence answered at invocation"*. A prefix that changed when a
//! client reconnected would put a connectivity-rate fact inside the model's
//! cached context, which is the whole defect §5 was amended to remove; and the
//! staleness that freezing admits is corrected where §5 already corrects it —
//! *"a client refuses a tool it no longer carries"*, in band, at the call.
//!
//! **The key is the agent, and there is no inheritance.** The set belongs to
//! the agent that loaded it, so a fresh conversation — and a freshly dispatched
//! subagent — starts clean and loads what it needs through the same `clients`
//! tool every agent always has.
//!
//! **Two writers, and they are symmetric** (REMOTE §5.2, bl-3455). [`add`] is
//! the load act's, [`remove`] the unload act's, and neither resolves a name:
//! each takes entries the act already resolved — load's against what the client
//! advertises right now, unload's against what this document actually holds —
//! so the whole-or-not-at-all rule lives once, in the act, and this module only
//! ever seals a set it was handed.
use io;
use ;
use ;
use cratestr_of;
use crate;
/// The loaded-set root's leaf under yog's state root.
pub const LOADED: &str = "loaded";
/// One loaded remote tool: the client that advertises it, and the definition
/// frozen as it read at the load act.
/// [`Eq`] for [`Tool`]'s own reason: a schema that came through a JSON decoder
/// cannot hold a `NaN`, so equality here is reflexive by construction.
/// True iff `name` is a name a provider will accept for a tool: ASCII letters,
/// digits, `_` and `-`, one to sixty-four of them. The advertised half is
/// already a path component (§5.1) and a client identity already is one, but
/// neither rules out the characters a tool block refuses — so the composed
/// name is checked once, here, and a load that cannot produce a callable name
/// declines naming it.
/// This agent's document. A workspace or agent that is not a plain path
/// component has no document — the same emptiness a fresh agent reads, rather
/// than a name that addresses the filesystem.
/// The set as JSON — one array, each element the advertised three facts with
/// the client beside them. The tool half is spelled by
/// [`tools::one`](crate::registry::tools::one), the same encoder the
/// advertisement and the boundary codec spend, so a stored definition and a
/// presented one cannot drift.
/// Read a set back strictly, naming the offending key — the advertisement's
/// own decode discipline, applied to yog's own document.
/// What this agent has loaded. A document that is absent, unreadable or
/// undecodable reads as the **empty set** — which is also what every agent
/// reads before its first load, so no reader carries two cases.
/// Add `entries` to this agent's set and answer the whole of it. Union by
/// presented name, later wins: re-loading a tool the client has re-advertised
/// refreshes the frozen definition, which is the only way a definition ever
/// changes in place.
/// Drop `gone` from this agent's set and answer what is left (REMOTE §5.2,
/// bl-3455). The caller resolved those entries against this very document, so
/// a name it does not hold refused the act before this was reached — which is
/// why there is no miss to report here and why the last unload leaves an empty
/// array rather than a special case: [`read`] already answers an empty set for
/// a document that is absent, unreadable or empty, so a set emptied and a set
/// never written read alike.
/// This agent's set with every entry `named` presents dropped — the half both
/// writers share, because a load replaces by presented name exactly as an
/// unload deletes by it.
/// Write `kept` as this agent's whole set, sorted, and answer it back.