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 "component_invocation",
159 entry(
160 Fail,
161 None,
162 ProvidedBy::None,
163 Required,
164 Some("no trusted component-invocation handler is installed for this file target"),
165 ),
166 ),
167 (
168 "sql_execution:read_only",
169 entry(Native, Some("bash-wren"), Runtime, Required, None),
170 ),
171 (
172 "genbi_build",
173 entry(Native, Some("bash-wren"), Runtime, Required, None),
174 ),
175 (
176 "semantic_introspection",
177 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
178 ),
179 (
182 "schema_introspection",
183 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
184 ),
185 ("llm:strong", entry(Native, None, Runtime, Required, None)),
186 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
187 (
188 "llm:per_step_tier",
189 entry(RealizeVia, Some("subagents"), Runtime, Required, None),
190 ),
191 (
198 "llm:per_step_provider",
199 entry(
200 RealizeVia,
201 Some("bash-script|mcp-server"),
202 Warble,
203 Required,
204 Some("local step via a bash-script script or an mcp-server (warble mcp-serve); cloud steps are the driver's own wren work"),
205 ),
206 ),
207 (
208 "render_contract",
209 entry(RealizeVia, Some("html-file"), Runtime, BestEffort, None),
210 ),
211 (
212 "structured_output_capture",
213 entry(Native, Some("stream-json"), Runtime, Required, None),
214 ),
215 (
216 "human_approval",
217 entry(
218 Fail,
219 None,
220 ProvidedBy::None,
221 SafetyCritical,
222 Some("no human in the loop in headless mode"),
223 ),
224 ),
225 (
226 "write_authz",
227 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
228 ),
229 (
233 "context_write_authz",
234 entry(RealizeVia, Some("scoped-fs"), Runtime, SafetyCritical, None),
235 ),
236 (
237 "artifact_write",
238 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
239 ),
240 (
245 CONTEXT_ISOLATION_CAPABILITY,
246 entry(RealizeVia, Some("subagent"), Runtime, Required, None),
247 ),
248 (
253 "scheduler",
254 entry(RealizeVia, Some("os-cron"), Runtime, Required, None),
255 ),
256 (
257 "event_bus",
258 entry(RealizeVia, Some("pub-sub"), Runtime, Required, None),
259 ),
260 (
261 "notify_channel",
262 entry(RealizeVia, Some("mcp-notify"), Runtime, Required, None),
263 ),
264 (
265 "blast_radius",
266 entry(
267 Fail,
268 None,
269 Warble,
270 SafetyCritical,
271 Some("requires fine_grained_binding"),
272 ),
273 ),
274 (
277 "version_control",
278 entry(RealizeVia, Some("git"), Runtime, Required, None),
279 ),
280 ])
281}
282
283fn interactive_profile() -> CapabilityProfile {
284 use CapabilityOutcome::*;
285 use Criticality::*;
286 use ProvidedBy::{Runtime, Warble};
287 profile([
288 (
289 "component_invocation",
290 entry(
291 Fail,
292 None,
293 ProvidedBy::None,
294 Required,
295 Some("no trusted component-invocation handler is installed for this file target"),
296 ),
297 ),
298 (
299 "sql_execution:read_only",
300 entry(Native, Some("bash-wren"), Runtime, Required, None),
301 ),
302 (
303 "genbi_build",
304 entry(Native, Some("bash-wren"), Runtime, Required, None),
305 ),
306 (
307 "semantic_introspection",
308 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
309 ),
310 (
311 "source_connect",
312 entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None),
313 ),
314 (
316 "schema_introspection",
317 entry(RealizeVia, Some("bash-wren"), Runtime, Required, None),
318 ),
319 ("raw_material_read", entry(Native, Some("Read"), Runtime, Required, Some("Claude Code's cwd-scoped Read tool; prompts forbid credentials and raw excerpts"))),
320 ("context_validate", entry(RealizeVia, Some("Bash(wren:*)"), Runtime, Required, None)),
321 ("context_build", entry(RealizeVia, Some("Bash(wren:*)"), Runtime, Required, None)),
322 ("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"))),
323 ("llm:strong", entry(Native, None, Runtime, Required, None)),
324 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
325 (
326 "llm:per_step_tier",
327 entry(RealizeVia, Some("subagents"), Runtime, Required, None),
328 ),
329 (
332 "llm:per_step_provider",
333 entry(
334 RealizeVia,
335 Some("bash-script|mcp-server"),
336 Warble,
337 Required,
338 Some("local step via an emitted local-inference script (Bash); cloud steps stay native subagents"),
339 ),
340 ),
341 (
342 "render_contract",
343 entry(
344 Degrade,
345 Some("terminal-markdown"),
346 Runtime,
347 BestEffort,
348 None,
349 ),
350 ),
351 (
352 "structured_output_capture",
353 entry(
354 Degrade,
355 None,
356 ProvidedBy::None,
357 BestEffort,
358 Some("not captured in the interactive TUI"),
359 ),
360 ),
361 (
362 "human_approval",
363 entry(Native, None, Runtime, SafetyCritical, None),
364 ),
365 (
366 "write_authz",
367 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
368 ),
369 (
372 "context_write_authz",
373 entry(RealizeVia, Some("scoped-fs"), Runtime, SafetyCritical, None),
374 ),
375 (
376 "artifact_write",
377 entry(RealizeVia, Some("fs"), Runtime, SafetyCritical, None),
378 ),
379 (
384 CONTEXT_ISOLATION_CAPABILITY,
385 entry(RealizeVia, Some("subagent"), Runtime, Required, None),
386 ),
387 (
391 "scheduler",
392 entry(RealizeVia, Some("os-cron"), Runtime, Required, None),
393 ),
394 (
395 "event_bus",
396 entry(RealizeVia, Some("pub-sub"), Runtime, Required, None),
397 ),
398 (
399 "notify_channel",
400 entry(RealizeVia, Some("mcp-notify"), Runtime, Required, None),
401 ),
402 (
403 "blast_radius",
404 entry(
405 Fail,
406 None,
407 Warble,
408 SafetyCritical,
409 Some("requires fine_grained_binding"),
410 ),
411 ),
412 (
415 "version_control",
416 entry(RealizeVia, Some("git"), Runtime, Required, None),
417 ),
418 ])
419}
420
421fn codex_interactive_profile() -> CapabilityProfile {
424 use CapabilityOutcome::*;
425 use Criticality::*;
426 use ProvidedBy::{Runtime, Warble};
427 profile([
428 (
429 "component_invocation",
430 entry(
431 Fail,
432 None,
433 ProvidedBy::None,
434 Required,
435 Some("codex interactive emits discovery artifacts and has no invocation runtime"),
436 ),
437 ),
438 ("sql_execution:read_only", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
439 ("genbi_build", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
440 ("source_connect", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
441 ("semantic_introspection", entry(RealizeVia, Some("codex-repository-read"), Runtime, Required, None)),
442 ("raw_material_read", entry(RealizeVia, Some("codex-repository-read"), Runtime, Required, Some("repository-scoped native TUI reads only; prompts forbid credentials and raw excerpts"))),
443 ("llm:strong", entry(Native, None, Runtime, Required, None)),
444 ("llm:cheap", entry(Native, None, Runtime, Required, None)),
445 ("llm:per_step_tier", entry(RealizeVia, Some("native-interactive-subagents"), Runtime, Required, None)),
446 ("context_write_authz", entry(RealizeVia, Some("native-interactive-human-approval"), Runtime, SafetyCritical, None)),
447 ("context_validate", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
448 ("context_build", entry(RealizeVia, Some("native-interactive-command"), Runtime, Required, None)),
449 ("version_control", entry(RealizeVia, Some("git"), Runtime, Required, None)),
450 ("human_approval", entry(Native, Some("native-interactive-human"), Runtime, SafetyCritical, None)),
451 ("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"))),
452 ("render_contract", entry(Degrade, Some("terminal-markdown"), Runtime, BestEffort, None)),
453 ("artifact_write", entry(RealizeVia, Some("session-scoped-artifact-api"), Runtime, SafetyCritical, Some("the host owns the artifact API and persistence"))),
454 ("blast_radius", entry(Fail, None, Warble, SafetyCritical, Some("requires fine_grained_binding"))),
455 ])
456}