1use serde::{Deserialize, Serialize};
16
17pub const SDK_CAPABILITIES_SCHEMA_V1: &str = "a3s-code/sdk-capabilities/v1";
19
20pub const SDK_CAPABILITIES_SCHEMA_V2: &str = "a3s-code/sdk-capabilities/v2";
22
23#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
25#[serde(rename_all = "snake_case")]
26pub enum CapabilityTier {
27 Baseline,
29 Advanced,
31}
32
33impl CapabilityTier {
34 pub const fn as_str(self) -> &'static str {
35 match self {
36 Self::Baseline => "baseline",
37 Self::Advanced => "advanced",
38 }
39 }
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
49pub struct SdkCapability {
50 pub id: String,
52 pub category: String,
54 pub description: String,
56 pub operations: Vec<String>,
59 pub host_owned: bool,
61 pub tier: CapabilityTier,
63}
64
65struct CapabilitySpec {
66 id: &'static str,
67 category: &'static str,
68 description: &'static str,
69 operations: &'static [&'static str],
70 host_owned: bool,
71 tier: CapabilityTier,
72}
73
74const CAPABILITY_SPECS: &[CapabilitySpec] = &[
75 CapabilitySpec {
76 id: "agent_runtime",
77 category: "runtime",
78 description: "Create agents, bind workspaces, resume, replace, and close sessions.",
79 operations: &["agent.create", "agent.session", "agent.resume_session", "agent.close"],
80 host_owned: false,
81 tier: CapabilityTier::Baseline,
82 },
83 CapabilitySpec {
84 id: "conversation",
85 category: "runtime",
86 description: "Send, run, stream, attach content, inspect history, and cancel transcript-affecting turns.",
87 operations: &[
88 "session.send",
89 "session.run",
90 "session.stream",
91 "session.send_with_attachments",
92 "session.history",
93 "session.cancel",
94 ],
95 host_owned: false,
96 tier: CapabilityTier::Baseline,
97 },
98 CapabilitySpec {
99 id: "run_control",
100 category: "runtime",
101 description: "Steer or cooperatively interrupt an active run with idempotent receipts and optimistic turn guards.",
102 operations: &["session.steer", "session.interrupt", "session.run_control_snapshot"],
103 host_owned: true,
104 tier: CapabilityTier::Baseline,
105 },
106 CapabilitySpec {
107 id: "governed_tools",
108 category: "execution",
109 description: "Invoke built-in and MCP tools through validation, policy, confirmation, hooks, budgets, and tracing.",
110 operations: &["session.tool", "session.governed_tool", "session.tool_definitions"],
111 host_owned: true,
112 tier: CapabilityTier::Baseline,
113 },
114 CapabilitySpec {
115 id: "workspace_tools",
116 category: "workspace",
117 description: "Read, write, list, edit, patch, shell, glob, and grep through the governed workspace boundary.",
118 operations: &[
119 "session.read_file",
120 "session.write_file",
121 "session.ls",
122 "session.edit_file",
123 "session.patch_file",
124 "session.bash",
125 "session.glob",
126 "session.grep",
127 "session.git",
128 "session.tool:download",
129 "session.tool:batch",
130 ],
131 host_owned: true,
132 tier: CapabilityTier::Baseline,
133 },
134 CapabilitySpec {
135 id: "workspace_retrieval",
136 category: "workspace",
137 description: "Run exact, lexical, symbol, semantic, and hybrid retrieval with bounded session-owned state.",
138 operations: &[
139 "session.workspace_retrieval_status",
140 "session.semantic_search",
141 "session.hybrid_search",
142 ],
143 host_owned: true,
144 tier: CapabilityTier::Baseline,
145 },
146 CapabilitySpec {
147 id: "model_adapters",
148 category: "model",
149 description: "Resolve configured Anthropic, OpenAI-compatible, and custom host model adapters.",
150 operations: &["agent.create", "session.send", "session.stream"],
151 host_owned: true,
152 tier: CapabilityTier::Baseline,
153 },
154 CapabilitySpec {
155 id: "structured_output",
156 category: "model",
157 description: "Request schema-constrained model output with validation and bounded repair.",
158 operations: &["session.send", "session.run", "session.task"],
159 host_owned: true,
160 tier: CapabilityTier::Baseline,
161 },
162 CapabilitySpec {
163 id: "mcp_and_skills",
164 category: "extension",
165 description: "Discover and mutate isolated MCP servers and filesystem or inline Skills.",
166 operations: &[
167 "session.add_mcp",
168 "session.remove_mcp",
169 "session.mcps",
170 "session.add_skill",
171 "session.skill_names",
172 ],
173 host_owned: true,
174 tier: CapabilityTier::Baseline,
175 },
176 CapabilitySpec {
177 id: "planning_delegation",
178 category: "orchestration",
179 description: "Run plans, worker agents, and unified `task` delegation with bounded fan-out and cancellation. Model-visible `parallel_task` is removed (`HARNESS-CONV4`); use `session.tasks` / multi-item `task`. Host `session.parallel` / Flow workflows are Advanced `programmable_workflows`, not this baseline surface.",
180 operations: &["session.task", "session.tasks", "session.register_worker_agent"],
181 host_owned: true,
182 tier: CapabilityTier::Baseline,
183 },
184 CapabilitySpec {
185 id: "priority_scheduling",
186 category: "orchestration",
187 description: "Host capacity plumbing: share bounded priority/FIFO admission and observe scheduler occupancy, fairness, and lifecycle counters. Not required for ordinary coding-loop correctness.",
188 operations: &[
189 "session.task_scheduler_stats",
190 "session.task_scheduler_health",
191 "session.model_generation_pool_health",
192 "session.model_middleware_health",
193 "session.queue_stats",
194 "session.set_lane_handler",
195 ],
196 host_owned: false,
197 tier: CapabilityTier::Baseline,
198 },
199 CapabilitySpec {
200 id: "persistence",
201 category: "state",
202 description: "Atomically save and restore session state, runs, artifacts, traces, and verification evidence.",
203 operations: &["session.save", "agent.resume_session", "session.get_artifact"],
204 host_owned: true,
205 tier: CapabilityTier::Baseline,
206 },
207 CapabilitySpec {
208 id: "governance",
209 category: "security",
210 description: "Configure permissions, confirmations, hooks, budgets, verification, sanitization, and sandbox policy.",
211 operations: &[
212 "session.pending_confirmations",
213 "session.confirm_tool_use",
214 "session.register_hook",
215 "session.set_budget_guard",
216 "session.verify_commands",
217 ],
218 host_owned: true,
219 tier: CapabilityTier::Baseline,
220 },
221 CapabilitySpec {
222 id: "run_observability",
223 category: "observability",
224 description: "Inspect durable run snapshots, event pages, active tools, traces, and child-task state.",
225 operations: &[
226 "session.runs",
227 "session.run_snapshot",
228 "session.run_events",
229 "session.run_event_page",
230 "session.active_tools",
231 "session.subagent_tasks",
232 ],
233 host_owned: false,
234 tier: CapabilityTier::Baseline,
235 },
236 CapabilitySpec {
237 id: "context_memory",
238 category: "context",
239 description: "Use working, short-term, durable, and semantic memory with typed health and recall APIs.",
240 operations: &[
241 "session.memory",
242 "session.remember",
243 "session.recall",
244 "session.memory_stats",
245 ],
246 host_owned: true,
247 tier: CapabilityTier::Baseline,
248 },
249 CapabilitySpec {
250 id: "web_search",
251 category: "web",
252 description: "Baseline web search through a3s-search v3.1.4 over HTTP, native API, and RSS engines. JavaScript/headless engines require the Advanced `moli_runtime` / Cargo `headless-search` profile. Billed providers stay opt-in.",
253 operations: &["session.web_search", "session.tool:web_search"],
254 host_owned: true,
255 tier: CapabilityTier::Baseline,
256 },
257 CapabilitySpec {
258 id: "web_fetch",
259 category: "web",
260 description: "Fetch a public HTTP(S) URL into text or markdown through the governed `web_fetch` tool (size and redirect bounded).",
261 operations: &["session.tool:web_fetch"],
262 host_owned: true,
263 tier: CapabilityTier::Baseline,
264 },
265 CapabilitySpec {
266 id: "code_intelligence",
267 category: "workspace",
268 description: "Query saved-file symbols, navigation, and diagnostics when a language service is available.",
269 operations: &[
270 "session.tool:code_symbols",
271 "session.tool:code_navigation",
272 "session.tool:code_diagnostics",
273 ],
274 host_owned: true,
275 tier: CapabilityTier::Advanced,
276 },
277 CapabilitySpec {
278 id: "cognitive_packages",
279 category: "context",
280 description: "Bind exact cited cognitive knowledge generations supplied by an authoritative host.",
281 operations: &[
282 "session.cognitive_package_binding",
283 "session.current_cognitive_package_binding",
284 ],
285 host_owned: true,
286 tier: CapabilityTier::Advanced,
287 },
288 CapabilitySpec {
289 id: "use_runtime_tasks",
290 category: "integration",
291 description: "Project host-owned A3S Use runtime tasks as governed model and direct-tool capabilities.",
292 operations: &["session.tool:use_runtime_task"],
293 host_owned: true,
294 tier: CapabilityTier::Advanced,
295 },
296 CapabilitySpec {
297 id: "program",
298 category: "composition",
299 description: "Execute a bounded in-process QuickJS `program` tool as part of the coding harness. Prefer unified `task` for model-driven fan-out; this is an escape hatch, not a second workflow engine.",
300 operations: &["session.program", "session.tool:program"],
301 host_owned: false,
302 tier: CapabilityTier::Baseline,
303 },
304 CapabilitySpec {
305 id: "programmable_workflows",
306 category: "orchestration",
307 description: "Host-authored resumable parallel Workflow APIs and Flow-backed dynamic workflows (`advanced-harness`). Prefer unified `task` for model-driven coding fan-out. Does not include the baseline `program` tool.",
308 operations: &[
309 "session.parallel",
310 "session.parallel_resumable",
311 "session.workflow_step",
312 ],
313 host_owned: true,
314 tier: CapabilityTier::Advanced,
315 },
316 CapabilitySpec {
317 id: "state_graph",
318 category: "state",
319 description: "Maintain hash-linked state graph events, patches, forks, and deterministic diffs.",
320 operations: &[
321 "state_graph.create",
322 "state_graph.restore",
323 "state_graph.propose_patch",
324 "state_graph.diff",
325 ],
326 host_owned: true,
327 tier: CapabilityTier::Advanced,
328 },
329 CapabilitySpec {
330 id: "agent_release_contract",
331 category: "deployment",
332 description: "Validate versioned asset manifests, provenance, and compatibility before activation.",
333 operations: &["release.admit", "release.bind_publication", "release.verify"],
334 host_owned: true,
335 tier: CapabilityTier::Advanced,
336 },
337 CapabilitySpec {
338 id: "agent_protocol",
339 category: "transport",
340 description: "Serve versioned session/run start, cancellation, recovery, and event-page protocols.",
341 operations: &[
342 "agent_protocol.start",
343 "agent_protocol.cancel",
344 "agent_protocol.recover",
345 "agent_protocol.events",
346 ],
347 host_owned: true,
348 tier: CapabilityTier::Advanced,
349 },
350 CapabilitySpec {
351 id: "evaluation_substrate",
352 category: "evaluation",
353 description: "Project bounded evidence, isolated auxiliary lifecycle, restart-safe dispatch claims, and immutable evaluation records through versioned boundaries.",
354 operations: &[
355 "evaluation.evidence",
356 "evaluation.auxiliary",
357 "evaluation.dispatch_ledger",
358 "evaluation.result",
359 "evaluation.result_store",
360 "evaluation.wire_v1",
361 ],
362 host_owned: true,
363 tier: CapabilityTier::Advanced,
364 },
365 CapabilitySpec {
366 id: "typed_decisions",
367 category: "decision",
368 description: "In-process typed System-1 decisions via Apofasi (`choice` / `score` / `noul`) with host GatePolicy auto/escalate and digest-bound receipts. Requires Cargo feature `apofasi` (optional `apofasi-infer` / `apofasi-metal`). When enabled, Code refuses to replace planning pre-analysis and goal achievement: each returns more than one typed answer, and the call site refuses to skip the generation. Code does not add a keyword classifier and does not lower GatePolicy. The escalate prompt includes the task state. Not injected into the system prompt, not a generative model path, and not a Use-projected capability kind.",
369 operations: &[
370 "typed_decision.decide",
371 "typed_decision.decide_and_gate",
372 "typed_decision.receipt_v1",
373 "typed_decision.route",
374 ],
375 host_owned: true,
376 tier: CapabilityTier::Advanced,
377 },
378 CapabilitySpec {
379 id: "moli_runtime",
380 category: "web",
381 description: "Use a verified packaged or shared-cache Moli runtime with cross-process installation locking.",
382 operations: &["moli.default", "moli.ensure", "moli.packaged"],
383 host_owned: true,
384 tier: CapabilityTier::Advanced,
385 },
386 CapabilitySpec {
387 id: "s3_workspace",
388 category: "workspace",
389 description: "Use an S3-compatible workspace backend with bounded reads and search. Requires the SDK `s3` Cargo feature.",
390 operations: &[
391 "session.workspace_backend:s3",
392 "session.read_file",
393 "session.write_file",
394 ],
395 host_owned: true,
396 tier: CapabilityTier::Advanced,
397 },
398 CapabilitySpec {
399 id: "opentelemetry",
400 category: "observability",
401 description: "Export redacted runtime traces and metrics through the optional OTLP integration. Requires the Core `telemetry` Cargo feature.",
402 operations: &["telemetry.init", "session.trace_events"],
403 host_owned: true,
404 tier: CapabilityTier::Advanced,
405 },
406];
407
408pub fn sdk_capabilities() -> Vec<SdkCapability> {
410 CAPABILITY_SPECS
411 .iter()
412 .map(|spec| SdkCapability {
413 id: spec.id.to_owned(),
414 category: spec.category.to_owned(),
415 description: spec.description.to_owned(),
416 operations: spec
417 .operations
418 .iter()
419 .map(|value| (*value).to_owned())
420 .collect(),
421 host_owned: spec.host_owned,
422 tier: spec.tier,
423 })
424 .collect()
425}
426
427pub fn sdk_baseline_capabilities() -> Vec<SdkCapability> {
429 sdk_capabilities()
430 .into_iter()
431 .filter(|capability| capability.tier == CapabilityTier::Baseline)
432 .collect()
433}
434
435pub const fn sdk_capabilities_schema() -> &'static str {
437 SDK_CAPABILITIES_SCHEMA_V2
438}
439
440#[cfg(test)]
441mod tests {
442 use super::*;
443 use std::collections::HashSet;
444
445 #[test]
446 fn inventory_is_stable_and_complete() {
447 let capabilities = sdk_capabilities();
448 assert!(capabilities.len() >= 20);
449 let ids = capabilities
450 .iter()
451 .map(|item| item.id.as_str())
452 .collect::<Vec<_>>();
453 assert_eq!(ids.len(), ids.iter().collect::<HashSet<_>>().len());
454 for capability in &capabilities {
455 assert!(!capability.category.is_empty());
456 assert!(!capability.description.is_empty());
457 assert!(!capability.operations.is_empty());
458 }
459 let required_baseline = [
460 "agent_runtime",
461 "conversation",
462 "governed_tools",
463 "workspace_tools",
464 "workspace_retrieval",
465 "planning_delegation",
466 "persistence",
467 "governance",
468 "run_control",
469 "web_search",
470 "web_fetch",
471 "program",
472 ];
473 for id in required_baseline {
474 let capability = capabilities
475 .iter()
476 .find(|item| item.id == id)
477 .unwrap_or_else(|| panic!("missing capability {id}"));
478 assert_eq!(
479 capability.tier,
480 CapabilityTier::Baseline,
481 "{id} must be baseline"
482 );
483 }
484 let required_advanced = [
485 "evaluation_substrate",
486 "typed_decisions",
487 "state_graph",
488 "programmable_workflows",
489 "s3_workspace",
490 "opentelemetry",
491 "moli_runtime",
492 ];
493 for id in required_advanced {
494 let capability = capabilities
495 .iter()
496 .find(|item| item.id == id)
497 .unwrap_or_else(|| panic!("missing capability {id}"));
498 assert_eq!(
499 capability.tier,
500 CapabilityTier::Advanced,
501 "{id} must be advanced"
502 );
503 }
504 let programmable = capabilities
505 .iter()
506 .find(|item| item.id == "programmable_workflows")
507 .expect("programmable_workflows");
508 assert!(
509 !programmable
510 .operations
511 .iter()
512 .any(|operation| operation.contains("program")),
513 "baseline program must not be mixed into Advanced programmable_workflows"
514 );
515 let web_search = capabilities
516 .iter()
517 .find(|item| item.id == "web_search")
518 .expect("web_search");
519 assert!(
520 !web_search
521 .description
522 .to_ascii_lowercase()
523 .contains("javascript-rendered")
524 || web_search.description.contains("headless-search"),
525 "baseline web_search must not claim JS engines without naming the Advanced gate"
526 );
527 }
528
529 #[test]
530 fn planning_delegation_omits_deprecated_parallel_task() {
531 let planning = sdk_capabilities()
532 .into_iter()
533 .find(|item| item.id == "planning_delegation")
534 .expect("planning_delegation");
535 assert!(planning
536 .operations
537 .iter()
538 .all(|operation| operation != "session.parallel_task"));
539 assert!(
540 planning.description.contains("removed")
541 || planning.description.contains("HARNESS-CONV4"),
542 "planning_delegation must document parallel_task removal"
543 );
544 }
545
546 #[test]
547 fn baseline_filter_excludes_advanced_surfaces() {
548 let baseline = sdk_baseline_capabilities();
549 assert!(baseline
550 .iter()
551 .all(|capability| capability.tier == CapabilityTier::Baseline));
552 assert!(!baseline.iter().any(|capability| {
553 matches!(
554 capability.id.as_str(),
555 "evaluation_substrate" | "state_graph" | "s3_workspace" | "programmable_workflows"
556 )
557 }));
558 assert!(baseline.len() >= 12);
559 assert!(baseline.len() < sdk_capabilities().len());
560 }
561
562 #[test]
563 fn inventory_serializes_with_schema() {
564 let value = serde_json::json!({
565 "schema": sdk_capabilities_schema(),
566 "capabilities": sdk_capabilities(),
567 });
568 assert_eq!(value["schema"], SDK_CAPABILITIES_SCHEMA_V2);
569 let first = &value["capabilities"][0];
570 assert!(first.get("tier").is_some());
571 assert!(value["capabilities"]
572 .as_array()
573 .is_some_and(|items| !items.is_empty()));
574 }
575
576 #[test]
577 fn tier_display_strings_are_stable() {
578 assert_eq!(CapabilityTier::Baseline.as_str(), "baseline");
579 assert_eq!(CapabilityTier::Advanced.as_str(), "advanced");
580 }
581}