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
//! **The operator's document** (yog's `docs/REMOTE.md` §5.2, §5.4; DESIGN
//! §3.4): what this box offers, and — by dropping half of it — what it says it
//! offers.
//!
//! ```json
//! [{"name": "Bash",
//! "description": "run a command in a shell",
//! "input_schema": {"type": "object",
//! "properties": {"command": {"type": "string"}},
//! "required": ["command"]},
//! "command": ["/usr/local/libexec/thrall-tools/bash-tool"],
//! "cwd": "/srv/work"}]
//! ```
//!
//! **One document, two readings.** The first three keys *are* REMOTE §5.1's
//! advertised element, verbatim, and [`advertisement`] is the whole of the
//! derivation: drop `command` and `cwd`. So what this box offers and what it
//! can actually run cannot drift, which is the entire reason the config is not
//! a pair of lists an operator has to keep in step.
//!
//! **Local config gates what is enabled, and this document is the gate.** A
//! tool absent from it is a tool this box does not have. Server-side
//! adjudication is unchanged, stacks on top, and fails closed — but it stacks
//! on *this*, so removing a capability from this machine is deleting an entry
//! here rather than editing anything, on the box whose operator is the one
//! entitled to decide (DESIGN §3.4).
//!
//! **`command` is an argv, spawned directly.** There is no shell and no
//! interpolation of the invocation's input into it. A shell would make the
//! declared `input_schema` advisory and turn an operator's config file into a
//! command-injection surface for anything a model can type; the input reaches
//! the command the way a local tool contract already delivers one — the JSON on
//! stdin, bytes on stdout, the exit code the verdict (bl-4cda).
//!
//! **It sits beside the channel material, not inside anything generated**
//! (`<thrall-data-root>/tools.json`, the sibling of `wire/`), for the material's
//! reason exactly: it describes *this machine*, it is written by the operator's
//! hand, and nothing thrall generates may sit where a rebuild would take it.
use ;
use Value;
use crate;
use crate;
/// The document's leaf under the data root.
pub const TOOLS: &str = "tools.json";
/// **The example document, embedded** (bl-bb7d) — the bytes of
/// `docs/tools.example.json`, compiled in.
///
/// The example is the one artifact of bl-102b's fix that its own audience
/// could not reach: `cargo install thrall` is the route the README teaches, and
/// it puts a binary on a box and leaves every file of this repository in the
/// repository. A registry install therefore had the README's prose and nothing
/// it could copy into place.
///
/// It is an embed rather than a shipped path because a file inside a downloaded
/// crate is not a file an operator can find: the route to it runs through the
/// registry cache, a version directory and a checksum, none of which the person
/// writing `tools.json` knows. A binary that prints its own example needs none
/// of them — `thrall --example-tools > <data root>/tools.json` is the whole
/// gesture, and it works identically on a box that has this repository and on
/// one that has only the binary.
///
/// **It is the file and never a copy of it.** The same bytes are parsed by
/// [`read`] in `config/tests/example.rs`, carried by the README byte for byte,
/// and printed by the flag; one document, three readers, no drift.
pub const EXAMPLE: &str = include_str!;
/// One tool this box offers: the advertised half, and the local half that is
/// never presented to anyone.
/// This box's document, under a data root.
/// Read it, or say why it is not one.
///
/// **An absent document is a refusal rather than the empty set.** A foot with
/// nothing to offer has nothing to do, and starting one is an explicit act that
/// deserves an explicit answer — the same posture absent channel material takes
/// (`channel::material`), and the opposite of the *presented* set, where an
/// empty array is a legitimate thing to say.
/// One element: the advertised three read by the **same** decoder the wire
/// spends, then the local two.
///
/// **`cwd` must be absolute, and that is decided here** (bl-3c93). Whether a
/// path is absolute is a property of the document — static, knowable at the
/// read, and never changing afterwards — so it belongs with the empty argv and
/// the unusable name rather than at the first invocation. It is the refusal
/// `paths::root` already makes about the data root, in this operator's other
/// document: a relative path resolves against whatever directory the
/// supervisor happened to start this process in, which nobody wrote down and
/// which changes when the unit file does. Whether the directory *exists* is a
/// fact about the box rather than about the file, can change between the read
/// and the run, and is answered by the spawn (`exec::child`).
/// **The advertisement, derived** (REMOTE §5.2): the same document with the
/// local half dropped. The one derivation, so this box cannot offer what it
/// cannot run.
/// Which element an invocation names, by position — an index rather than a
/// borrow, so the caller resolves it against the very list it passed in.