1use serde::{Deserialize, Serialize};
11use std::borrow::Cow;
12use std::collections::HashMap;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
16#[serde(rename_all = "kebab-case")]
17pub enum CapabilityOutcome {
18 Native,
19 RealizeVia,
20 Degrade,
21 Fail,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
26#[serde(rename_all = "lowercase")]
27pub enum ProvidedBy {
28 Runtime,
29 Warble,
30 None,
31}
32
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
36#[serde(rename_all = "kebab-case")]
37pub enum Criticality {
38 SafetyCritical,
39 Required,
40 BestEffort,
41}
42
43pub(crate) const CONTEXT_ISOLATION_CAPABILITY: &str = "context_isolation";
47
48#[derive(Debug, Clone)]
49pub struct CapabilityEntry {
50 pub outcome: CapabilityOutcome,
51 pub via: Option<Cow<'static, str>>,
55 pub provided_by: ProvidedBy,
56 pub criticality: Criticality,
57 pub note: Option<Cow<'static, str>>,
58}
59
60pub type CapabilityProfile = HashMap<String, CapabilityEntry>;
61
62fn profile(
65 entries: impl IntoIterator<Item = (&'static str, CapabilityEntry)>,
66) -> CapabilityProfile {
67 entries
68 .into_iter()
69 .map(|(k, v)| (k.to_string(), v))
70 .collect()
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75pub enum TargetId {
76 Headless,
77 Interactive,
78 CodexInteractive,
79}
80
81impl TargetId {
82 pub fn as_str(&self) -> &'static str {
83 match self {
84 TargetId::Headless => "claude-code:headless",
85 TargetId::Interactive => "claude-code:interactive",
86 TargetId::CodexInteractive => "codex:interactive",
87 }
88 }
89
90 pub fn mode(&self) -> &'static str {
93 match self {
94 TargetId::Headless => "headless",
95 TargetId::Interactive => "interactive",
96 TargetId::CodexInteractive => "interactive",
100 }
101 }
102
103 pub fn parse(value: &str) -> Option<TargetId> {
104 match value {
105 "claude-code:headless" => Some(TargetId::Headless),
106 "claude-code:interactive" => Some(TargetId::Interactive),
107 "codex:interactive" => Some(TargetId::CodexInteractive),
108 _ => None,
109 }
110 }
111
112 pub fn profile(&self) -> CapabilityProfile {
113 match self {
114 TargetId::Headless => headless_profile(),
115 TargetId::Interactive => interactive_profile(),
116 TargetId::CodexInteractive => codex_interactive_profile(),
117 }
118 }
119}
120
121pub const DEFAULT_TARGET: TargetId = TargetId::Headless;
123
124pub fn is_known_target(value: &str) -> bool {
125 TargetId::parse(value).is_some()
126}
127
128pub fn known_target_names() -> [&'static str; 3] {
129 [
130 TargetId::Headless.as_str(),
131 TargetId::Interactive.as_str(),
132 TargetId::CodexInteractive.as_str(),
133 ]
134}
135
136fn entry(
137 outcome: CapabilityOutcome,
138 via: Option<&'static str>,
139 provided_by: ProvidedBy,
140 criticality: Criticality,
141 note: Option<&'static str>,
142) -> CapabilityEntry {
143 CapabilityEntry {
144 outcome,
145 via: via.map(Cow::Borrowed),
146 provided_by,
147 criticality,
148 note: note.map(Cow::Borrowed),
149 }
150}
151
152fn headless_profile() -> CapabilityProfile {
153 use CapabilityOutcome::*;
154 use Criticality::*;
155 use ProvidedBy::{Runtime, Warble};
156 profile([
157 (
158 "sql_execution:read_only",
159 entry(Native, Some("bash-wren"), Runtime, Required, None),
160 ),
161 (
162 "genbi_build",
163 entry(Native, Some("bash-wren"), Runtime, Required, None),
164 ),
165 (
166 "semantic_introspection",
167 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
168 ),
169 (
172 "schema_introspection",
173 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
174 ),
175 ("llm:strong", entry(Native, None, Runtime, Required, None)),
176 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
177 (
178 "llm:per_step_tier",
179 entry(RealizeVia, Some("subagents"), Runtime, Required, None),
180 ),
181 (
188 "llm:per_step_provider",
189 entry(
190 RealizeVia,
191 Some("bash-script|mcp-server"),
192 Warble,
193 Required,
194 Some("local step via a bash-script script or an mcp-server (warble mcp-serve); cloud steps are the driver's own wren work"),
195 ),
196 ),
197 (
198 "render_contract",
199 entry(RealizeVia, Some("html-file"), Runtime, BestEffort, None),
200 ),
201 (
202 "structured_output_capture",
203 entry(Native, Some("stream-json"), Runtime, Required, None),
204 ),
205 (
206 "human_approval",
207 entry(
208 Fail,
209 None,
210 ProvidedBy::None,
211 SafetyCritical,
212 Some("no human in the loop in headless mode"),
213 ),
214 ),
215 (
216 "write_authz",
217 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
218 ),
219 (
223 "context_write_authz",
224 entry(RealizeVia, Some("scoped-fs"), Runtime, SafetyCritical, None),
225 ),
226 (
227 "artifact_write",
228 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
229 ),
230 (
235 CONTEXT_ISOLATION_CAPABILITY,
236 entry(RealizeVia, Some("subagent"), Runtime, Required, None),
237 ),
238 (
243 "scheduler",
244 entry(RealizeVia, Some("os-cron"), Runtime, Required, None),
245 ),
246 (
247 "event_bus",
248 entry(RealizeVia, Some("pub-sub"), Runtime, Required, None),
249 ),
250 (
251 "notify_channel",
252 entry(RealizeVia, Some("mcp-notify"), Runtime, Required, None),
253 ),
254 (
255 "blast_radius",
256 entry(
257 Fail,
258 None,
259 Warble,
260 SafetyCritical,
261 Some("requires fine_grained_binding"),
262 ),
263 ),
264 (
267 "version_control",
268 entry(RealizeVia, Some("git"), Runtime, Required, None),
269 ),
270 ])
271}
272
273fn interactive_profile() -> CapabilityProfile {
274 use CapabilityOutcome::*;
275 use Criticality::*;
276 use ProvidedBy::{Runtime, Warble};
277 profile([
278 (
279 "sql_execution:read_only",
280 entry(Native, Some("bash-wren"), Runtime, Required, None),
281 ),
282 (
283 "genbi_build",
284 entry(Native, Some("bash-wren"), Runtime, Required, None),
285 ),
286 (
287 "semantic_introspection",
288 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
289 ),
290 (
291 "source_connect",
292 entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None),
293 ),
294 (
296 "schema_introspection",
297 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
298 ),
299 ("raw_material_read", entry(Native, Some("Read"), Runtime, Required, Some("Claude Code's cwd-scoped Read tool; prompts forbid credentials and raw excerpts"))),
300 ("context_validate", entry(RealizeVia, Some("Bash(wren:*)"), Runtime, Required, None)),
301 ("context_build", entry(RealizeVia, Some("Bash(wren:*)"), Runtime, Required, None)),
302 ("enrichment_apply:deterministic", entry(RealizeVia, Some("native-interactive-approved-tool"), Runtime, SafetyCritical, Some("only after the native human-approval gate; Warble does not run it"))),
303 ("llm:strong", entry(Native, None, Runtime, Required, None)),
304 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
305 (
306 "llm:per_step_tier",
307 entry(RealizeVia, Some("subagents"), Runtime, Required, None),
308 ),
309 (
312 "llm:per_step_provider",
313 entry(
314 RealizeVia,
315 Some("bash-script|mcp-server"),
316 Warble,
317 Required,
318 Some("local step via an emitted local-inference script (Bash); cloud steps stay native subagents"),
319 ),
320 ),
321 (
322 "render_contract",
323 entry(
324 Degrade,
325 Some("terminal-markdown"),
326 Runtime,
327 BestEffort,
328 None,
329 ),
330 ),
331 (
332 "structured_output_capture",
333 entry(
334 Degrade,
335 None,
336 ProvidedBy::None,
337 BestEffort,
338 Some("not captured in the interactive TUI"),
339 ),
340 ),
341 (
342 "human_approval",
343 entry(Native, None, Runtime, SafetyCritical, None),
344 ),
345 (
346 "write_authz",
347 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
348 ),
349 (
352 "context_write_authz",
353 entry(RealizeVia, Some("scoped-fs"), Runtime, SafetyCritical, None),
354 ),
355 (
356 "artifact_write",
357 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
358 ),
359 (
364 CONTEXT_ISOLATION_CAPABILITY,
365 entry(RealizeVia, Some("subagent"), Runtime, Required, None),
366 ),
367 (
371 "scheduler",
372 entry(RealizeVia, Some("os-cron"), Runtime, Required, None),
373 ),
374 (
375 "event_bus",
376 entry(RealizeVia, Some("pub-sub"), Runtime, Required, None),
377 ),
378 (
379 "notify_channel",
380 entry(RealizeVia, Some("mcp-notify"), Runtime, Required, None),
381 ),
382 (
383 "blast_radius",
384 entry(
385 Fail,
386 None,
387 Warble,
388 SafetyCritical,
389 Some("requires fine_grained_binding"),
390 ),
391 ),
392 (
395 "version_control",
396 entry(RealizeVia, Some("git"), Runtime, Required, None),
397 ),
398 ])
399}
400
401fn codex_interactive_profile() -> CapabilityProfile {
404 use CapabilityOutcome::*;
405 use Criticality::*;
406 use ProvidedBy::{Runtime, Warble};
407 profile([
408 ("sql_execution:read_only", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
409 ("genbi_build", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
410 ("source_connect", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
411 ("semantic_introspection", entry(RealizeVia, Some("codex-repository-read"), Runtime, Required, None)),
412 ("raw_material_read", entry(RealizeVia, Some("codex-repository-read"), Runtime, Required, Some("repository-scoped native TUI reads only; prompts forbid credentials and raw excerpts"))),
413 ("llm:strong", entry(Native, None, Runtime, Required, None)),
414 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
415 ("llm:per_step_tier", entry(RealizeVia, Some("native-interactive-subagents"), Runtime, Required, None)),
416 ("context_write_authz", entry(RealizeVia, Some("native-interactive-human-approval"), Runtime, SafetyCritical, None)),
417 ("context_validate", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
418 ("context_build", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
419 ("version_control", entry(RealizeVia, Some("git"), Runtime, Required, None)),
420 ("human_approval", entry(Native, Some("native-interactive-human"), Runtime, SafetyCritical, None)),
421 ("enrichment_apply:deterministic", entry(RealizeVia, Some("native-interactive-approved-tool"), Runtime, SafetyCritical, Some("only after the native human-approval gate; Warble does not run it"))),
422 ("render_contract", entry(Degrade, Some("terminal-markdown"), Runtime, BestEffort, None)),
423 ("artifact_write", entry(RealizeVia, Some("session-scoped-artifact-api"), Runtime, SafetyCritical, Some("the host owns the artifact API and persistence"))),
424 ("blast_radius", entry(Fail, None, Warble, SafetyCritical, Some("requires fine_grained_binding"))),
425 ])
426}