agent-sdk-core 0.1.0-alpha.4

Product-neutral primitive kernel and contracts for a Rust-first Agent SDK.
Documentation
use agent_sdk_core::{
    AgentId, CapabilityCatalogSnapshot, CapabilityId, CapabilityKind, CapabilitySourceKind,
    CapabilitySpec, CapabilityVisibility, ExecutorRef, PackageDelta, PackageSidecarRef,
    PackageSidecarSnapshot, PolicyKind, PolicyRef, ProjectionMode, ProviderRouteSnapshot,
    RuntimePackage, RuntimePackageConformanceReport, RuntimePackageId, SourceKind, SourceRef,
    TrustClass, VolatileRuntimeFields,
};
use serde_json::{Value, json};

fn fixture(path: &str) -> Value {
    serde_json::from_str(path).expect("fixture parses")
}

fn package_policy(id: &str) -> PolicyRef {
    PolicyRef::with_kind(PolicyKind::RuntimePackage, id)
}

fn approval_policy(id: &str) -> PolicyRef {
    PolicyRef::with_kind(PolicyKind::Approval, id)
}

fn source(id: &str) -> SourceRef {
    SourceRef::with_kind(SourceKind::Sdk, id)
}

fn schema_ref(version: &str) -> PackageSidecarRef {
    let mut sidecar =
        PackageSidecarRef::new("sidecar.schema.workspace_read", "tool_schema", version);
    sidecar.content_hash = Some(format!("sha256:schema.{version}"));
    sidecar
}

fn named_sidecar_ref(sidecar_id: &str) -> PackageSidecarRef {
    let mut sidecar = PackageSidecarRef::new(sidecar_id, "tool_schema", "v1");
    sidecar.content_hash = Some(format!("sha256:{sidecar_id}"));
    sidecar
}

fn workspace_read_tool(schema_version: &str, executor_ref: &str) -> CapabilitySpec {
    CapabilitySpec::fake_tool(
        "cap.workspace_read",
        "workspace_read",
        schema_ref(schema_version),
        ExecutorRef::new(executor_ref),
        approval_policy("policy.approval.workspace_read"),
        source("source.sdk.toolpack"),
    )
}

fn package_with_tool() -> RuntimePackage {
    RuntimePackage::builder(RuntimePackageId::new("package.contract"))
        .agent(agent_sdk_core::AgentSnapshot {
            agent_id: AgentId::new("agent.contract"),
            name: "contract agent".to_string(),
            default_behavior_refs: vec![package_policy("policy.agent.default")],
        })
        .provider_route(ProviderRouteSnapshot::new("provider.fake", "model.fake.p0"))
        .sidecar(PackageSidecarSnapshot {
            sidecar_id: "sidecar.schema.workspace_read".to_string(),
            kind: "tool_schema".to_string(),
            version: "v1".to_string(),
            refs: vec![schema_ref("v1")],
            policy_refs: vec![approval_policy("policy.approval.workspace_read")],
            content_hash: "sha256:schema.v1".to_string(),
            redacted_payload: None,
        })
        .capability(workspace_read_tool("v1", "executor.workspace_read.v1"))
        .policy(package_policy("policy.package.default"))
        .build()
        .expect("package builds")
}

#[test]
fn runtime_package_fingerprint_is_deterministic_and_fixture_backed() {
    let package = package_with_tool();
    let first = package.fingerprint().expect("fingerprint");
    let second = package.fingerprint().expect("fingerprint");
    assert_eq!(first, second);
    assert!(
        first
            .as_str()
            .starts_with("sha256:runtime-package-canonical-v1:")
    );

    let snapshot = serde_json::to_value(package.canonical_snapshot().expect("snapshot")).unwrap();
    let expected_snapshot = fixture(include_str!(
        "../fixtures/package/runtime-package-canonical-v1.json"
    ));
    assert_eq!(
        agent_sdk_core::testing::normalize_json_value(snapshot),
        agent_sdk_core::testing::normalize_json_value(expected_snapshot)
    );

    let fingerprint_fixture = fixture(include_str!(
        "../fixtures/package/runtime-package-fingerprint.json"
    ));
    assert_eq!(
        json!({
            "schema_version": 1,
            "fingerprint": first.as_str(),
        }),
        fingerprint_fixture
    );
}

#[test]
fn execution_affecting_changes_update_fingerprint_but_volatile_fields_do_not() {
    let package = package_with_tool();
    let baseline = package.fingerprint().expect("fingerprint");

    let schema_changed = RuntimePackage::builder(RuntimePackageId::new("package.contract"))
        .agent(package.agent.clone())
        .provider_route(package.provider_route.clone())
        .capability(workspace_read_tool("v2", "executor.workspace_read.v1"))
        .policy(package_policy("policy.package.default"))
        .build()
        .expect("schema package builds")
        .fingerprint()
        .expect("schema fingerprint");
    assert_ne!(baseline, schema_changed);

    let route_changed = RuntimePackage::builder(RuntimePackageId::new("package.contract"))
        .agent(package.agent.clone())
        .provider_route(ProviderRouteSnapshot::new(
            "provider.fake",
            "model.fake.other",
        ))
        .capability(workspace_read_tool("v1", "executor.workspace_read.v2"))
        .policy(package_policy("policy.package.default"))
        .build()
        .expect("route package builds")
        .fingerprint()
        .expect("route fingerprint");
    assert_ne!(baseline, route_changed);

    let mut volatile_changed = package.clone();
    volatile_changed.volatile = VolatileRuntimeFields {
        run_id: Some("run.dynamic".to_string()),
        event_id: Some("event.dynamic".to_string()),
        timestamp_ms: Some(123),
        adapter_health: Some("healthy-now".to_string()),
        temporary_path: Some("/tmp/dynamic".to_string()),
    };
    assert_eq!(
        baseline,
        volatile_changed
            .fingerprint()
            .expect("volatile fingerprint")
    );
}

#[test]
fn runtime_package_fingerprint_canonicalizes_nested_execution_lists() {
    let mut first = package_with_tool();
    let mut second = package_with_tool();

    first.capabilities[0].sidecar_refs = vec![
        named_sidecar_ref("sidecar.z"),
        named_sidecar_ref("sidecar.a"),
    ];
    second.capabilities[0].sidecar_refs = vec![
        named_sidecar_ref("sidecar.a"),
        named_sidecar_ref("sidecar.z"),
    ];

    first.sidecars[0].refs = vec![
        named_sidecar_ref("sidecar.ref.z"),
        named_sidecar_ref("sidecar.ref.a"),
    ];
    second.sidecars[0].refs = vec![
        named_sidecar_ref("sidecar.ref.a"),
        named_sidecar_ref("sidecar.ref.z"),
    ];

    first.sidecars[0].policy_refs = vec![
        approval_policy("policy.approval.z"),
        approval_policy("policy.approval.a"),
    ];
    second.sidecars[0].policy_refs = vec![
        approval_policy("policy.approval.a"),
        approval_policy("policy.approval.z"),
    ];

    let mut first_catalog = CapabilityCatalogSnapshot {
        catalog_id: "catalog.discovery.1".to_string(),
        source_kind: CapabilitySourceKind::DiscoveryIndex,
        source_ref: source("source.sdk.discovery"),
        version: Some("v1".to_string()),
        content_hash: Some("sha256:catalog.discovery.1".to_string()),
        trust_state: TrustClass::SdkGenerated,
        activation_policy_ref: package_policy("policy.activation.discovery"),
        candidates: vec![CapabilityId::new("cap.z"), CapabilityId::new("cap.a")],
    };
    let mut second_catalog = first_catalog.clone();
    second_catalog.candidates.reverse();

    first.catalogs = vec![first_catalog.clone()];
    second.catalogs = vec![second_catalog.clone()];

    assert_eq!(
        first.fingerprint().expect("first fingerprint"),
        second.fingerprint().expect("second fingerprint")
    );

    first_catalog
        .candidates
        .sort_by_key(|id| id.as_str().to_string());
    assert_eq!(
        first
            .canonical_snapshot()
            .expect("canonical snapshot")
            .catalogs[0]
            .candidates,
        first_catalog.candidates
    );
}

#[test]
fn projection_and_execution_routes_derive_from_one_package_authority() {
    let package = package_with_tool();
    let projected = package.provider_tool_specs().expect("provider specs");
    let executable = package.executable_routes().expect("executable routes");

    assert_eq!(projected.len(), 1);
    assert_eq!(executable.len(), 1);
    assert_eq!(projected[0].capability_id, executable[0].capability_id);
    assert_eq!(projected[0].policy_ref, executable[0].policy_ref);

    let mut broken = package.clone();
    broken.capabilities[0].executor_ref = None;
    let error = broken
        .validate()
        .expect_err("projected tool without executor fails");
    assert_eq!(error.kind(), agent_sdk_core::AgentErrorKind::InvalidPackage);
}

#[test]
fn reserved_variants_are_inactive_and_cannot_project_or_execute() {
    let inactive = CapabilitySpec::reserved_inactive(
        "cap.stream.reserved",
        CapabilityKind::StreamControl,
        package_policy("policy.stream.reserved"),
        source("source.sdk.reserved"),
    );
    let package = RuntimePackage::builder(RuntimePackageId::new("package.reserved"))
        .agent(agent_sdk_core::AgentSnapshot {
            agent_id: AgentId::new("agent.reserved"),
            name: "reserved agent".to_string(),
            default_behavior_refs: Vec::new(),
        })
        .provider_route(ProviderRouteSnapshot::new("provider.fake", "model.fake.p0"))
        .capability(inactive)
        .build()
        .expect("inactive reserved package builds");

    assert_eq!(package.provider_tool_specs().unwrap().len(), 0);
    assert_eq!(package.executable_routes().unwrap().len(), 0);
    assert_eq!(
        package
            .canonical_snapshot()
            .unwrap()
            .fingerprint_manifest
            .reserved_feature_status[0]
            .owner_role,
        "05-streaming-realtime-rules"
    );

    let mut projected_reserved = package.capabilities[0].clone();
    projected_reserved.visibility = CapabilityVisibility::Active;
    projected_reserved.projection = ProjectionMode::DescriptorOnly;
    let error = projected_reserved
        .project_for_provider()
        .expect_err("reserved projection fails");
    assert_eq!(
        error.kind(),
        agent_sdk_core::AgentErrorKind::InvalidStateTransition
    );

    let mut executable_reserved = package.capabilities[0].clone();
    executable_reserved.executor_ref = Some(ExecutorRef::new("executor.reserved"));
    let error = executable_reserved
        .executable_route()
        .expect_err("reserved execution fails");
    assert_eq!(
        error.kind(),
        agent_sdk_core::AgentErrorKind::InvalidStateTransition
    );
}

#[test]
fn catalog_snapshot_and_package_delta_create_next_snapshot() {
    let package = package_with_tool();
    let hidden_tool = CapabilitySpec::fake_tool(
        "cap.hidden_search",
        "hidden_search",
        schema_ref("v1"),
        ExecutorRef::new("executor.hidden_search.v1"),
        approval_policy("policy.approval.hidden_search"),
        source("source.sdk.discovery"),
    );
    let catalog = CapabilityCatalogSnapshot {
        catalog_id: "catalog.discovery.1".to_string(),
        source_kind: CapabilitySourceKind::DiscoveryIndex,
        source_ref: source("source.sdk.discovery"),
        version: Some("v1".to_string()),
        content_hash: Some("sha256:catalog.discovery.1".to_string()),
        trust_state: TrustClass::SdkGenerated,
        activation_policy_ref: package_policy("policy.activation.discovery"),
        candidates: vec![CapabilityId::new("cap.hidden_search")],
    };
    let delta = PackageDelta {
        previous_fingerprint: package.fingerprint().expect("fingerprint"),
        requested_by: source("source.sdk.discovery"),
        reason: "activate hidden discovery candidate for next package".to_string(),
        activated_capabilities: vec![hidden_tool],
        deactivated_capability_ids: Vec::new(),
        catalogs: vec![catalog],
        sidecars: Vec::new(),
    };

    let next = package.apply_delta(delta).expect("delta applies");
    assert_eq!(next.capabilities.len(), 2);
    assert_eq!(next.catalogs.len(), 1);
    assert_ne!(
        package.fingerprint().expect("old fingerprint"),
        next.fingerprint().expect("next fingerprint")
    );
}

#[test]
fn sdk_consumer_conformance_report_is_fake_friendly() {
    let package = package_with_tool();
    let report = package
        .conformance_report()
        .expect("conformance report is deterministic");
    assert_eq!(
        report,
        RuntimePackageConformanceReport {
            fingerprint: package.fingerprint().unwrap(),
            provider_projection_count: 1,
            executable_route_count: 1,
            reserved_inactive_count: 0,
            catalog_count: 0,
            sidecar_count: 1,
        }
    );
}