arbora 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- arbora/__init__.py +29 -0
- arbora/_version.py +6 -0
- arbora/a2a/__init__.py +74 -0
- arbora/a2a/admission.py +67 -0
- arbora/a2a/application.py +498 -0
- arbora/a2a/approval.py +152 -0
- arbora/a2a/authority.py +613 -0
- arbora/a2a/composition.py +41 -0
- arbora/a2a/mcp.py +118 -0
- arbora/a2a/models.py +249 -0
- arbora/a2a/routing.py +70 -0
- arbora/a2a/sdk_app.py +124 -0
- arbora/a2a/service.py +95 -0
- arbora/a2a/terminal_event.py +140 -0
- arbora/a2a/tool_task.py +318 -0
- arbora/a2a/work_items.py +98 -0
- arbora/access/__init__.py +1 -0
- arbora/access/action_policy.py +1265 -0
- arbora/access/approval.py +53 -0
- arbora/access/assurance.py +247 -0
- arbora/access/external_policy_sync.py +754 -0
- arbora/access/priority.py +88 -0
- arbora/access/service.py +176 -0
- arbora/access/task_guard.py +21 -0
- arbora/acp/__init__.py +31 -0
- arbora/acp/coding_session.py +123 -0
- arbora/acp/session_policy.py +139 -0
- arbora/agent_host/__init__.py +336 -0
- arbora/agent_host/log_access.py +297 -0
- arbora/agent_host/log_replay.py +174 -0
- arbora/agent_host/log_routes.py +118 -0
- arbora/agent_host/log_runtime.py +82 -0
- arbora/agent_host/log_sink.py +284 -0
- arbora/agent_host/start_admission.py +226 -0
- arbora/agent_host/start_configuration.py +84 -0
- arbora/agent_runs.py +168 -0
- arbora/api/__init__.py +19 -0
- arbora/api/contracts/__init__.py +133 -0
- arbora/api/contracts/agent_control_contracts.py +294 -0
- arbora/api/contracts/agent_control_plane.py +505 -0
- arbora/api/contracts/catalog.py +388 -0
- arbora/api/contracts/messaging.py +42 -0
- arbora/api/contracts/provisioning.py +250 -0
- arbora/api/contracts/session.py +569 -0
- arbora/api/errors.py +414 -0
- arbora/api/evidence/__init__.py +5 -0
- arbora/api/evidence/bundle.py +1077 -0
- arbora/api/evidence/contradiction.py +353 -0
- arbora/api/generated/__init__.py +5 -0
- arbora/api/generated/descriptors.json +5946 -0
- arbora/api/generated/engine_errors.py +279 -0
- arbora/api/generated/registry.json +24709 -0
- arbora/api/generated/schemas/method.request.json +1 -0
- arbora/api/generated/schemas/result.cluster.json +1 -0
- arbora/api/generated/schemas/result.compute.json +1 -0
- arbora/api/generated/schemas/result.coordination.json +1 -0
- arbora/api/generated/schemas/result.graph.json +1 -0
- arbora/api/generated/schemas/result.ingestion.json +1 -0
- arbora/api/generated/schemas/result.messaging.json +1 -0
- arbora/api/generated/schemas/result.query.json +1 -0
- arbora/api/generated/schemas/result.reasoning.json +1 -0
- arbora/api/generated/schemas/result.security.json +1 -0
- arbora/api/generated/schemas/result.storage.json +1 -0
- arbora/api/generated/schemas/result.transactions.json +1 -0
- arbora/api/http/__init__.py +6 -0
- arbora/api/http/app.py +197 -0
- arbora/api/http/auth.py +230 -0
- arbora/api/http/openapi.py +57 -0
- arbora/api/http/protocol_routes.py +34 -0
- arbora/api/http/routes.py +139 -0
- arbora/api/invoke/__init__.py +13 -0
- arbora/api/invoke/audit.py +67 -0
- arbora/api/invoke/bootstrap.py +285 -0
- arbora/api/invoke/eg_audit.py +199 -0
- arbora/api/invoke/executor.py +292 -0
- arbora/api/invoke/pipeline.py +873 -0
- arbora/api/invoke/plan.py +232 -0
- arbora/api/invoke/steps.py +221 -0
- arbora/api/mcp/__init__.py +5 -0
- arbora/api/mcp/caller.py +125 -0
- arbora/api/mcp/discovery.py +166 -0
- arbora/api/mcp/registration.py +250 -0
- arbora/api/mcp/resolve.py +336 -0
- arbora/api/mcp/resources.py +58 -0
- arbora/api/mcp/serving.py +75 -0
- arbora/api/mcp/verbs.py +417 -0
- arbora/api/ops/__init__.py +8 -0
- arbora/api/ops/_common.py +147 -0
- arbora/api/ops/access.py +232 -0
- arbora/api/ops/agents.py +172 -0
- arbora/api/ops/browser.py +76 -0
- arbora/api/ops/capacity.py +147 -0
- arbora/api/ops/decide.py +82 -0
- arbora/api/ops/fleet.py +230 -0
- arbora/api/ops/fleet_deploy_watch.py +94 -0
- arbora/api/ops/ingest.py +177 -0
- arbora/api/ops/ops.py +273 -0
- arbora/api/ops/policy.py +71 -0
- arbora/api/ops/query.py +43 -0
- arbora/api/ops/registry_factory.py +82 -0
- arbora/api/ops/retrieval.py +151 -0
- arbora/api/ops/runs.py +163 -0
- arbora/api/ops/security.py +71 -0
- arbora/api/ops/swarm.py +70 -0
- arbora/api/ops/telemetry.py +70 -0
- arbora/api/ops/usage.py +70 -0
- arbora/api/ops/work.py +128 -0
- arbora/api/ops/write_back.py +79 -0
- arbora/api/ops/write_back_models.py +118 -0
- arbora/api/policy/__init__.py +17 -0
- arbora/api/policy/cache.py +46 -0
- arbora/api/policy/defaults.py +25 -0
- arbora/api/policy/domain_scopes.py +84 -0
- arbora/api/policy/eunomia.py +333 -0
- arbora/api/policy/pdp_remote.py +266 -0
- arbora/api/registry/__init__.py +73 -0
- arbora/api/registry/contract_admission.py +167 -0
- arbora/api/registry/digest.py +106 -0
- arbora/api/registry/eg_binding.py +403 -0
- arbora/api/registry/registry.py +110 -0
- arbora/api/registry/scopes.py +48 -0
- arbora/api/registry/spec.py +197 -0
- arbora/api/registry/surface_gate.py +145 -0
- arbora/architecture/__init__.py +1 -0
- arbora/architecture/boundary_census.py +153 -0
- arbora/architecture/capability_layers.py +88 -0
- arbora/browser_control/__init__.py +5 -0
- arbora/browser_control/browser_control_api.py +239 -0
- arbora/browser_control/browser_control_attendance.py +114 -0
- arbora/browser_control/browser_control_attendance_api.py +112 -0
- arbora/browser_control/browser_control_attended.py +212 -0
- arbora/browser_control/browser_control_binding.py +144 -0
- arbora/browser_control/browser_control_cancellation.py +166 -0
- arbora/browser_control/browser_control_channel.py +307 -0
- arbora/browser_control/browser_control_client.py +97 -0
- arbora/browser_control/browser_control_common.py +210 -0
- arbora/browser_control/browser_control_descriptor.py +81 -0
- arbora/browser_control/browser_control_dispatch.py +255 -0
- arbora/browser_control/browser_control_durability.py +336 -0
- arbora/browser_control/browser_control_lease.py +245 -0
- arbora/browser_control/browser_control_outcome.py +302 -0
- arbora/browser_control/browser_control_policy.py +159 -0
- arbora/browser_control/browser_control_port.py +21 -0
- arbora/browser_control/browser_control_provenance.py +266 -0
- arbora/browser_control/browser_control_registration.py +324 -0
- arbora/browser_control/browser_control_runtime.py +127 -0
- arbora/browser_control/browser_control_server.py +80 -0
- arbora/browser_control/browser_control_service.py +156 -0
- arbora/browser_control/browser_control_state.py +191 -0
- arbora/browser_control/browser_control_validation.py +148 -0
- arbora/browser_control/mcp.py +42 -0
- arbora/browser_control/trace_bundle.py +268 -0
- arbora/client/__init__.py +1 -0
- arbora/client/_generated_models.py +532 -0
- arbora/client/_schema.py +74 -0
- arbora/client/invoke.py +26 -0
- arbora/connector_runner.py +166 -0
- arbora/content.py +22 -0
- arbora/control_plane/__init__.py +7 -0
- arbora/control_plane/_model.py +11 -0
- arbora/control_plane/admission/__init__.py +6 -0
- arbora/control_plane/admission/models.py +57 -0
- arbora/control_plane/admission/service.py +69 -0
- arbora/control_plane/agents/__init__.py +93 -0
- arbora/control_plane/agents/models.py +804 -0
- arbora/control_plane/agents/repository.py +94 -0
- arbora/control_plane/agents/service.py +547 -0
- arbora/control_plane/connectors/__init__.py +103 -0
- arbora/control_plane/connectors/models.py +665 -0
- arbora/control_plane/connectors/repository.py +115 -0
- arbora/control_plane/connectors/service.py +477 -0
- arbora/control_plane/economics/__init__.py +104 -0
- arbora/control_plane/economics/models.py +1149 -0
- arbora/control_plane/economics/repository.py +341 -0
- arbora/control_plane/economics/service.py +518 -0
- arbora/control_plane/fleet_watch/__init__.py +26 -0
- arbora/control_plane/fleet_watch/service.py +86 -0
- arbora/control_plane/foundation/__init__.py +121 -0
- arbora/control_plane/foundation/models.py +829 -0
- arbora/control_plane/foundation/repository.py +161 -0
- arbora/control_plane/foundation/service.py +916 -0
- arbora/control_plane/migrations/__init__.py +89 -0
- arbora/control_plane/migrations/authority.py +918 -0
- arbora/control_plane/migrations/errors.py +53 -0
- arbora/control_plane/migrations/models.py +656 -0
- arbora/control_plane/policy/__init__.py +63 -0
- arbora/control_plane/policy/models.py +376 -0
- arbora/control_plane/policy/repository.py +445 -0
- arbora/control_plane/projection/__init__.py +95 -0
- arbora/control_plane/projection/authority.py +28 -0
- arbora/control_plane/projection/models.py +870 -0
- arbora/control_plane/projection/observability.py +15 -0
- arbora/control_plane/projection/projector.py +751 -0
- arbora/control_plane/projection/protocols.py +86 -0
- arbora/control_plane/projection/repository.py +164 -0
- arbora/control_plane/retrieval/__init__.py +85 -0
- arbora/control_plane/retrieval/errors.py +45 -0
- arbora/control_plane/retrieval/models.py +682 -0
- arbora/control_plane/retrieval/protocols.py +97 -0
- arbora/control_plane/retrieval/repository.py +176 -0
- arbora/control_plane/retrieval/service.py +321 -0
- arbora/control_plane/runs/__init__.py +71 -0
- arbora/control_plane/runs/admission.py +232 -0
- arbora/control_plane/runs/audit.py +134 -0
- arbora/control_plane/runs/capacity.py +129 -0
- arbora/control_plane/runs/models.py +381 -0
- arbora/control_plane/runs/observations.py +88 -0
- arbora/control_plane/sources/__init__.py +84 -0
- arbora/control_plane/sources/models.py +871 -0
- arbora/control_plane/sources/repository.py +84 -0
- arbora/control_plane/sources/service.py +502 -0
- arbora/control_plane/webui/__init__.py +102 -0
- arbora/control_plane/webui/errors.py +60 -0
- arbora/control_plane/webui/models.py +517 -0
- arbora/control_plane/webui/protocols.py +60 -0
- arbora/control_plane/webui/repository.py +559 -0
- arbora/control_plane/webui/service.py +442 -0
- arbora/control_plane/workflows/__init__.py +83 -0
- arbora/control_plane/workflows/domain.py +528 -0
- arbora/control_plane/workflows/repository.py +363 -0
- arbora/core/__init__.py +9 -0
- arbora/core/agent_config_admin.py +556 -0
- arbora/core/capability_contract.py +158 -0
- arbora/core/config_admin.py +97 -0
- arbora/core/event_loop.py +164 -0
- arbora/core/provider_materialization.py +711 -0
- arbora/core/providers.py +720 -0
- arbora/core/settings.py +1079 -0
- arbora/core/transport.py +224 -0
- arbora/deployment/__init__.py +151 -0
- arbora/deployment/certification_assets.py +394 -0
- arbora/deployment/cli.py +239 -0
- arbora/deployment/codex_registration.py +157 -0
- arbora/deployment/config_generator.py +1242 -0
- arbora/deployment/doctor.py +2433 -0
- arbora/deployment/doctor_certification.py +413 -0
- arbora/deployment/doctor_coverage.py +226 -0
- arbora/deployment/doctor_graph.py +305 -0
- arbora/deployment/doctor_lakehouse.py +660 -0
- arbora/deployment/doctor_observability.py +570 -0
- arbora/deployment/doctor_support.py +47 -0
- arbora/deployment/engine_lock.py +155 -0
- arbora/deployment/exit_criteria.py +69 -0
- arbora/deployment/file_lock.py +266 -0
- arbora/deployment/genesis_environments.py +1150 -0
- arbora/deployment/host_config.py +76 -0
- arbora/deployment/host_lock.py +250 -0
- arbora/deployment/kubernetes_first_boot.py +827 -0
- arbora/deployment/live_mount_guard.py +296 -0
- arbora/deployment/local_gates.py +83 -0
- arbora/deployment/preflight.py +462 -0
- arbora/deployment/production_ops.py +517 -0
- arbora/deployment/profile_guard.py +589 -0
- arbora/deployment/public_ingress_identity.py +208 -0
- arbora/deployment/release_canary.py +125 -0
- arbora/deployment/release_candidate.py +332 -0
- arbora/deployment/repo_templates.py +570 -0
- arbora/deployment/self_deploy.py +130 -0
- arbora/deployment/semantic_provisioning.py +533 -0
- arbora/deployment/venv_autosync.py +973 -0
- arbora/deployment/venv_sync.py +2918 -0
- arbora/deployment/venv_sync_types.py +257 -0
- arbora/deployment/webui_reference_model.py +101 -0
- arbora/ecosystem/__init__.py +12 -0
- arbora/ecosystem/agent_manager_dashboard.py +350 -0
- arbora/ecosystem/ard_federation.py +191 -0
- arbora/ecosystem/ard_registry.py +331 -0
- arbora/ecosystem/ard_signing.py +157 -0
- arbora/ecosystem/hook_installer.py +368 -0
- arbora/epistemic.py +447 -0
- arbora/evolution/__init__.py +5 -0
- arbora/evolution/wiring_sweep.py +918 -0
- arbora/finance/__init__.py +70 -0
- arbora/finance/alerts.py +79 -0
- arbora/finance/dca.py +104 -0
- arbora/finance/markets.py +116 -0
- arbora/finance/schedule.py +84 -0
- arbora/fleet/__init__.py +90 -0
- arbora/fleet/access_contracts.py +164 -0
- arbora/fleet/catalog_composition.py +86 -0
- arbora/fleet/catalog_items.py +316 -0
- arbora/fleet/catalog_reader.py +624 -0
- arbora/fleet/catalog_sources.py +212 -0
- arbora/fleet/child_resilience.py +1373 -0
- arbora/fleet/client_toolsets.py +175 -0
- arbora/fleet/connector_count_pins.py +121 -0
- arbora/fleet/discovery_authority.py +78 -0
- arbora/fleet/epistemic_adapter.py +243 -0
- arbora/fleet/error_budget.py +232 -0
- arbora/fleet/feeds.py +98 -0
- arbora/fleet/gateway_ops.py +511 -0
- arbora/fleet/health.py +548 -0
- arbora/fleet/local_skill_catalog.py +207 -0
- arbora/fleet/mcp_resource_reconciliation.py +147 -0
- arbora/fleet/multiplexer.py +7408 -0
- arbora/fleet/multiplexer_ops.py +388 -0
- arbora/fleet/onboarding.py +521 -0
- arbora/fleet/protocol_compat.py +487 -0
- arbora/fleet/scaling/__init__.py +26 -0
- arbora/fleet/scaling/_model.py +11 -0
- arbora/fleet/scaling/development_lane.py +200 -0
- arbora/fleet/scaling/resource_pool_authority.py +1149 -0
- arbora/fleet/scaling/resource_reservation.py +151 -0
- arbora/fleet/scaling/scaling_authority.py +1086 -0
- arbora/fleet/scaling/scaling_intent_reconciler.py +1501 -0
- arbora/fleet/scaling/scaling_signal_authority.py +1018 -0
- arbora/fleet/scaling/service_scale_units.py +1850 -0
- arbora/fleet/service_child.py +352 -0
- arbora/fleet/session_loads.py +278 -0
- arbora/fleet/session_notifications.py +37 -0
- arbora/fleet/shared_multiplexer.py +160 -0
- arbora/fleet/throttle_service.py +84 -0
- arbora/fleet_tool_call.py +100 -0
- arbora/gateway/__init__.py +45 -0
- arbora/gateway/aggregator.py +241 -0
- arbora/gateway/config.py +595 -0
- arbora/gateway/daemon.py +344 -0
- arbora/gateway/dashboard_api.py +407 -0
- arbora/gateway/enhanced_catalog_api.py +320 -0
- arbora/gateway/fleet.py +567 -0
- arbora/gateway/fleet_events.py +500 -0
- arbora/gateway/graph_api.py +325 -0
- arbora/gateway/models.py +232 -0
- arbora/gateway/ontology_api.py +495 -0
- arbora/gateway/ports.py +61 -0
- arbora/gateway/rate_limit.py +169 -0
- arbora/gateway/registry.py +206 -0
- arbora/gateway/registry_api.py +1682 -0
- arbora/gateway/remote_oauth_api.py +435 -0
- arbora/gateway/schema_context_api.py +81 -0
- arbora/gateway/schema_context_service.py +53 -0
- arbora/gateway/schemas/__init__.py +7 -0
- arbora/gateway/schemas/connector.py +67 -0
- arbora/gateway/schemas/graph_core.py +790 -0
- arbora/gateway/schemas/schema_context.py +121 -0
- arbora/gateway/widgets/__init__.py +5 -0
- arbora/gateway/widgets/ansible_tower.py +59 -0
- arbora/gateway/widgets/archivebox.py +46 -0
- arbora/gateway/widgets/aris.py +47 -0
- arbora/gateway/widgets/arr.py +40 -0
- arbora/gateway/widgets/atlassian.py +44 -0
- arbora/gateway/widgets/audio_transcriber.py +35 -0
- arbora/gateway/widgets/audiobookshelf.py +47 -0
- arbora/gateway/widgets/base.py +374 -0
- arbora/gateway/widgets/caddy.py +96 -0
- arbora/gateway/widgets/camunda.py +58 -0
- arbora/gateway/widgets/ciso_assistant.py +62 -0
- arbora/gateway/widgets/clarity.py +45 -0
- arbora/gateway/widgets/container_manager.py +52 -0
- arbora/gateway/widgets/data_science.py +33 -0
- arbora/gateway/widgets/dockerhub.py +50 -0
- arbora/gateway/widgets/documentdb.py +48 -0
- arbora/gateway/widgets/ear.py +57 -0
- arbora/gateway/widgets/egeria.py +54 -0
- arbora/gateway/widgets/emerald_exchange.py +32 -0
- arbora/gateway/widgets/erpnext.py +52 -0
- arbora/gateway/widgets/fan_manager.py +59 -0
- arbora/gateway/widgets/firefly_iii.py +47 -0
- arbora/gateway/widgets/fleet_client.py +260 -0
- arbora/gateway/widgets/freshrss.py +69 -0
- arbora/gateway/widgets/github.py +45 -0
- arbora/gateway/widgets/gitlab.py +74 -0
- arbora/gateway/widgets/google_workspace.py +29 -0
- arbora/gateway/widgets/gramps.py +54 -0
- arbora/gateway/widgets/hdhomerun.py +46 -0
- arbora/gateway/widgets/home_assistant.py +61 -0
- arbora/gateway/widgets/jellyfin.py +63 -0
- arbora/gateway/widgets/jena.py +42 -0
- arbora/gateway/widgets/kafka.py +57 -0
- arbora/gateway/widgets/keycloak.py +56 -0
- arbora/gateway/widgets/langfuse.py +59 -0
- arbora/gateway/widgets/leanix.py +58 -0
- arbora/gateway/widgets/legal_peripherals.py +30 -0
- arbora/gateway/widgets/lgtm.py +65 -0
- arbora/gateway/widgets/listmonk.py +53 -0
- arbora/gateway/widgets/mattermost.py +55 -0
- arbora/gateway/widgets/mealie.py +56 -0
- arbora/gateway/widgets/media_downloader.py +48 -0
- arbora/gateway/widgets/microsoft.py +45 -0
- arbora/gateway/widgets/nextcloud.py +59 -0
- arbora/gateway/widgets/okta.py +47 -0
- arbora/gateway/widgets/ollama.py +54 -0
- arbora/gateway/widgets/onetrust.py +46 -0
- arbora/gateway/widgets/openbao.py +49 -0
- arbora/gateway/widgets/orchestrator.py +34 -0
- arbora/gateway/widgets/owncast.py +48 -0
- arbora/gateway/widgets/paperless_ngx.py +46 -0
- arbora/gateway/widgets/plane.py +56 -0
- arbora/gateway/widgets/portainer.py +99 -0
- arbora/gateway/widgets/postiz.py +50 -0
- arbora/gateway/widgets/pulselink.py +46 -0
- arbora/gateway/widgets/qbittorrent.py +55 -0
- arbora/gateway/widgets/repository_manager.py +44 -0
- arbora/gateway/widgets/rom_manager.py +63 -0
- arbora/gateway/widgets/scholarx.py +36 -0
- arbora/gateway/widgets/searxng.py +49 -0
- arbora/gateway/widgets/sentry.py +58 -0
- arbora/gateway/widgets/servicenow.py +54 -0
- arbora/gateway/widgets/stirlingpdf.py +45 -0
- arbora/gateway/widgets/systems_manager.py +36 -0
- arbora/gateway/widgets/technitium.py +73 -0
- arbora/gateway/widgets/teleport.py +28 -0
- arbora/gateway/widgets/tunnel_manager.py +44 -0
- arbora/gateway/widgets/twenty.py +44 -0
- arbora/gateway/widgets/uptime_kuma.py +71 -0
- arbora/gateway/widgets/vector_db.py +64 -0
- arbora/gateway/widgets/wger.py +50 -0
- arbora/gateway/widgets/zulip.py +58 -0
- arbora/host_bindings.py +268 -0
- arbora/identity/__init__.py +28 -0
- arbora/identity/admin_console.py +54 -0
- arbora/identity/admission.py +82 -0
- arbora/identity/api_keys.py +54 -0
- arbora/identity/bootstrap.py +76 -0
- arbora/identity/broker.py +304 -0
- arbora/identity/browser.py +261 -0
- arbora/identity/client_entry_points.py +70 -0
- arbora/identity/composition.py +58 -0
- arbora/identity/engine.py +168 -0
- arbora/identity/engine_ports.py +14 -0
- arbora/identity/issuer.py +180 -0
- arbora/identity/ldap.py +140 -0
- arbora/identity/mail_capability.py +88 -0
- arbora/identity/mfa.py +77 -0
- arbora/identity/oidc.py +142 -0
- arbora/identity/ports.py +83 -0
- arbora/identity/saml.py +173 -0
- arbora/identity/scim.py +132 -0
- arbora/ingest/__init__.py +32 -0
- arbora/ingest/service.py +192 -0
- arbora/ingest/source_catalog.py +1002 -0
- arbora/mcp_server/__init__.py +15 -0
- arbora/mcp_server/agent_control.py +48 -0
- arbora/mcp_server/arbora_operations.json +1675 -0
- arbora/mcp_server/backing/__init__.py +20 -0
- arbora/mcp_server/backing/_arbora_action_manifest.py +2242 -0
- arbora/mcp_server/backing/action_dispatch.py +256 -0
- arbora/mcp_server/backing/arbora_capabilities.json +23321 -0
- arbora/mcp_server/backing/arbora_surface.py +294 -0
- arbora/mcp_server/backing/client_credentials.py +767 -0
- arbora/mcp_server/backing/concurrency.py +127 -0
- arbora/mcp_server/backing/connector_surface.py +244 -0
- arbora/mcp_server/backing/content_resources.py +64 -0
- arbora/mcp_server/backing/context_helpers.py +402 -0
- arbora/mcp_server/backing/delegated_auth.py +580 -0
- arbora/mcp_server/backing/engine_routing.py +1064 -0
- arbora/mcp_server/backing/engine_surface_types.py +67 -0
- arbora/mcp_server/backing/eunomia_principal.py +807 -0
- arbora/mcp_server/backing/httpx_boundary.py +157 -0
- arbora/mcp_server/backing/intent_contract.py +221 -0
- arbora/mcp_server/backing/middlewares.py +243 -0
- arbora/mcp_server/backing/oauth_log_hygiene.py +100 -0
- arbora/mcp_server/backing/ontology_types.py +30 -0
- arbora/mcp_server/backing/optional_tool_features.py +44 -0
- arbora/mcp_server/backing/remote_oauth_broker.py +1521 -0
- arbora/mcp_server/backing/server_factory.py +2745 -0
- arbora/mcp_server/backing/skill_coverage.py +553 -0
- arbora/mcp_server/backing/tasks_extension.py +1400 -0
- arbora/mcp_server/backing/tool_specs.py +349 -0
- arbora/mcp_server/backing/tools/__init__.py +51 -0
- arbora/mcp_server/backing/tools/_surface_selection.py +37 -0
- arbora/mcp_server/backing/tools/analysis_tools.py +2656 -0
- arbora/mcp_server/backing/tools/analyze_suite.py +220 -0
- arbora/mcp_server/backing/tools/audit_tools.py +106 -0
- arbora/mcp_server/backing/tools/candidate_claim_tools.py +173 -0
- arbora/mcp_server/backing/tools/compliance_tools.py +396 -0
- arbora/mcp_server/backing/tools/config_tools.py +106 -0
- arbora/mcp_server/backing/tools/domain_ops_tools.py +114 -0
- arbora/mcp_server/backing/tools/engine_surface_tools.py +3618 -0
- arbora/mcp_server/backing/tools/engine_tools.py +715 -0
- arbora/mcp_server/backing/tools/epistemic_tools.py +265 -0
- arbora/mcp_server/backing/tools/governance_tools.py +275 -0
- arbora/mcp_server/backing/tools/graph_tools.py +352 -0
- arbora/mcp_server/backing/tools/intent_tools.py +2821 -0
- arbora/mcp_server/backing/tools/mcp_apps.py +434 -0
- arbora/mcp_server/backing/tools/ontology_tools.py +1771 -0
- arbora/mcp_server/backing/tools/query_tools.py +3505 -0
- arbora/mcp_server/backing/tools/reach_tools.py +98 -0
- arbora/mcp_server/backing/tools/secret_tools.py +110 -0
- arbora/mcp_server/backing/tools/state_tools.py +245 -0
- arbora/mcp_server/backing/tools/write_ingest_tools.py +1684 -0
- arbora/mcp_server/backing/verbose_tools.py +808 -0
- arbora/mcp_server/backing/write_ingest_types.py +41 -0
- arbora/mcp_server/bootstrap.py +738 -0
- arbora/mcp_server/catalog_composition.py +127 -0
- arbora/mcp_server/composition.py +293 -0
- arbora/mcp_server/decide_wiring.py +236 -0
- arbora/mcp_server/effect_journal.py +244 -0
- arbora/mcp_server/fleet_composition.py +152 -0
- arbora/mcp_server/harness_host.py +125 -0
- arbora/mcp_server/hosted_invocation.py +58 -0
- arbora/mcp_server/legacy_action_mapping.py +87 -0
- arbora/mcp_server/operation_registry.py +107 -0
- arbora/mcp_server/routes.py +26 -0
- arbora/mcp_server/runtime.py +942 -0
- arbora/mcp_server/served_engine.py +304 -0
- arbora/mcp_server/served_invocation.py +293 -0
- arbora/mcp_server/server.py +409 -0
- arbora/messaging/__init__.py +5 -0
- arbora/messaging/action_dispatch.py +119 -0
- arbora/messaging/alert_intake.py +215 -0
- arbora/messaging/backends/__init__.py +21 -0
- arbora/messaging/backends/discord.py +344 -0
- arbora/messaging/backends/googlechat.py +160 -0
- arbora/messaging/backends/googlemeet.py +113 -0
- arbora/messaging/backends/imessage.py +147 -0
- arbora/messaging/backends/irc.py +139 -0
- arbora/messaging/backends/line.py +121 -0
- arbora/messaging/backends/matrix.py +149 -0
- arbora/messaging/backends/mattermost.py +440 -0
- arbora/messaging/backends/nextcloud.py +165 -0
- arbora/messaging/backends/signal.py +113 -0
- arbora/messaging/backends/slack.py +276 -0
- arbora/messaging/backends/synology.py +110 -0
- arbora/messaging/backends/teams.py +185 -0
- arbora/messaging/backends/telegram.py +475 -0
- arbora/messaging/backends/twitch.py +128 -0
- arbora/messaging/backends/voicecall.py +121 -0
- arbora/messaging/backends/whatsapp.py +211 -0
- arbora/messaging/base.py +420 -0
- arbora/messaging/capabilities.py +444 -0
- arbora/messaging/coalescer.py +101 -0
- arbora/messaging/commands.py +177 -0
- arbora/messaging/governed_send.py +46 -0
- arbora/messaging/host_ports.py +63 -0
- arbora/messaging/inbox.py +182 -0
- arbora/messaging/intake.py +76 -0
- arbora/messaging/lease.py +467 -0
- arbora/messaging/models.py +303 -0
- arbora/messaging/planner_handler.py +111 -0
- arbora/messaging/polling.py +171 -0
- arbora/messaging/reach.py +19 -0
- arbora/messaging/registry.py +413 -0
- arbora/messaging/render.py +85 -0
- arbora/messaging/router.py +474 -0
- arbora/messaging/service.py +565 -0
- arbora/messaging/supervision.py +122 -0
- arbora/messaging/transport.py +40 -0
- arbora/observability/__init__.py +36 -0
- arbora/observability/correlation.py +127 -0
- arbora/observability/custom_observability.py +1102 -0
- arbora/observability/error_detail_sink.py +151 -0
- arbora/observability/gateway_health.py +164 -0
- arbora/observability/gateway_metrics.py +1338 -0
- arbora/observability/health.py +348 -0
- arbora/observability/langfuse_exporter.py +385 -0
- arbora/observability/langfuse_trust.py +1246 -0
- arbora/observability/readiness.py +115 -0
- arbora/observability/runtime_health.py +750 -0
- arbora/observability/runtime_signals.py +593 -0
- arbora/observability/telemetry.py +90 -0
- arbora/observability/trace_ontology.py +488 -0
- arbora/ontology/shapes/runtime.shapes.ttl +51 -0
- arbora/security/__init__.py +13 -0
- arbora/security/actor_context.py +184 -0
- arbora/security/auth.py +286 -0
- arbora/security/config_sensitivity.py +131 -0
- arbora/security/conformance/__init__.py +72 -0
- arbora/security/conformance/surface_inventory.py +183 -0
- arbora/security/conformance/surface_manifest.py +363 -0
- arbora/security/delegation.py +459 -0
- arbora/security/elevation.py +266 -0
- arbora/security/error_surface.py +561 -0
- arbora/security/http_boundary.py +441 -0
- arbora/security/identifiers.py +120 -0
- arbora/security/identity.py +218 -0
- arbora/security/log_redaction.py +38 -0
- arbora/security/oidc_discovery.py +152 -0
- arbora/security/operation_result.py +41 -0
- arbora/security/prompt_injection.py +512 -0
- arbora/security/request_identity.py +1483 -0
- arbora/security/run_token.py +190 -0
- arbora/security/scope_registry.py +223 -0
- arbora/security/secrets.py +663 -0
- arbora/security/security_policy_middleware.py +56 -0
- arbora/security/settings.py +112 -0
- arbora/semantic_content.py +200 -0
- arbora/skills/__init__.py +15 -0
- arbora/skills/agent-utilities-source-integration/SKILL.md +181 -0
- arbora/skills/agent-utilities-source-integration/agents/arbora.yaml +5 -0
- arbora/skills/agent-utilities-source-integration/agents/openai.yaml +4 -0
- arbora/skills/arbora-deployment/SKILL.md +203 -0
- arbora/skills/arbora-deployment/agents/arbora.yaml +5 -0
- arbora/skills/arbora-deployment/agents/openai.yaml +4 -0
- arbora/skills/arbora-deployment/references/first-boot-verification.md +80 -0
- arbora/skills/arbora-deployment/references/fleet-and-control-plane.md +85 -0
- arbora/skills/arbora-deployment/references/identity-and-access.md +147 -0
- arbora/skills/arbora-deployment/references/secrets-and-federation.md +67 -0
- arbora/skills/arbora-deployment/references/telemetry.md +29 -0
- arbora/skills/arbora-development/SKILL.md +144 -0
- arbora/skills/arbora-development/references/bootstrap.md +88 -0
- arbora/skills/arbora-evolution/SKILL.md +542 -0
- arbora/skills/arbora-evolution/agents/arbora.yaml +5 -0
- arbora/skills/arbora-evolution/agents/openai.yaml +4 -0
- arbora/skills/arbora-genesis/SKILL.md +347 -0
- arbora/skills/arbora-genesis/agents/arbora.yaml +5 -0
- arbora/skills/arbora-genesis/agents/openai.yaml +4 -0
- arbora/skills/arbora-genesis/references/TROUBLESHOOTING.md +39 -0
- arbora/skills/arbora-genesis/references/bare-metal.md +45 -0
- arbora/skills/arbora-genesis/references/compose-and-podman.md +83 -0
- arbora/skills/arbora-genesis/references/data-plane-substrate.md +246 -0
- arbora/skills/arbora-genesis/references/deployment-contract.md +99 -0
- arbora/skills/arbora-genesis/references/development-workspace.md +62 -0
- arbora/skills/arbora-genesis/references/docker-swarm.md +63 -0
- arbora/skills/arbora-genesis/references/engine-identity-admission.md +218 -0
- arbora/skills/arbora-genesis/references/kubernetes-and-helm.md +111 -0
- arbora/skills/arbora-genesis/references/orchestrator-migration-cutover.md +68 -0
- arbora/skills/arbora-genesis/references/runtime-topology.md +90 -0
- arbora/skills/arbora-genesis/references/security-and-operations.md +128 -0
- arbora/skills/arbora-repository-development/SKILL.md +286 -0
- arbora/skills/autonomous-contribution/SKILL.md +42 -0
- arbora/skills/autonomous-contribution/agents/arbora.yaml +5 -0
- arbora/skills/autonomous-contribution/agents/openai.yaml +4 -0
- arbora/skills/core-pack.txt +15 -0
- arbora/skills/graph-engine-and-modalities/SKILL.md +127 -0
- arbora/skills/graph-engine-and-modalities/agents/arbora.yaml +37 -0
- arbora/skills/graph-engine-and-modalities/agents/openai.yaml +4 -0
- arbora/skills/graph-ingestion-and-integration/SKILL.md +191 -0
- arbora/skills/graph-ingestion-and-integration/agents/arbora.yaml +15 -0
- arbora/skills/graph-ingestion-and-integration/agents/openai.yaml +4 -0
- arbora/skills/graph-ingestion-and-integration/references/ingest-connector-reference.md +261 -0
- arbora/skills/graph-ingestion-and-integration/references/ingest_manifest.yaml +77 -0
- arbora/skills/graph-modeling-and-mutation/SKILL.md +136 -0
- arbora/skills/graph-modeling-and-mutation/agents/arbora.yaml +22 -0
- arbora/skills/graph-modeling-and-mutation/agents/openai.yaml +4 -0
- arbora/skills/graph-orchestration-and-automation/SKILL.md +128 -0
- arbora/skills/graph-orchestration-and-automation/agents/arbora.yaml +12 -0
- arbora/skills/graph-orchestration-and-automation/agents/openai.yaml +4 -0
- arbora/skills/graph-query-and-explanation/SKILL.md +166 -0
- arbora/skills/graph-query-and-explanation/agents/arbora.yaml +28 -0
- arbora/skills/graph-query-and-explanation/agents/openai.yaml +4 -0
- arbora/skills/graph-query-and-explanation/references/uql-reference.md +432 -0
- arbora/skills/graph-research-and-analysis/SKILL.md +160 -0
- arbora/skills/graph-research-and-analysis/agents/arbora.yaml +11 -0
- arbora/skills/graph-research-and-analysis/agents/openai.yaml +4 -0
- arbora/skills/graph-runtime-and-governance/SKILL.md +165 -0
- arbora/skills/graph-runtime-and-governance/agents/arbora.yaml +17 -0
- arbora/skills/graph-runtime-and-governance/agents/openai.yaml +4 -0
- arbora/skills/using-arbora/SKILL.md +114 -0
- arbora/webui_host/__init__.py +10 -0
- arbora/webui_host/contact_delivery.py +259 -0
- arbora/webui_host/contact_governance.py +282 -0
- arbora/webui_host/mcp_delegation.py +136 -0
- arbora/webui_host/package_layout_expectation.py +70 -0
- arbora/webui_host/voice_delegation.py +74 -0
- arbora/webui_host/webui_co_service.py +251 -0
- arbora/work_items.py +251 -0
- arbora-0.1.0.dist-info/METADATA +223 -0
- arbora-0.1.0.dist-info/RECORD +653 -0
- arbora-0.1.0.dist-info/WHEEL +5 -0
- arbora-0.1.0.dist-info/entry_points.txt +39 -0
- arbora-0.1.0.dist-info/licenses/LICENSE +21 -0
- arbora-0.1.0.dist-info/top_level.txt +1 -0
arbora/__init__.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Arbora: the deployable composition for the Knuckles agent platform.
|
|
2
|
+
|
|
3
|
+
RF-ADR-009 (`plans/refactor/RF-ADR-009-connector-sdk-and-arbora.md`) §2 assigns
|
|
4
|
+
this repository phase 5: **the deployable composition** — the MCP server, REST
|
|
5
|
+
gateway, control plane, fleet gateway, agent-webui hosting, and deployment
|
|
6
|
+
tooling. It must never own business logic that belongs to
|
|
7
|
+
epistemic-graph, agent-connector-sdk, or the agent-utilities agent plane.
|
|
8
|
+
|
|
9
|
+
The live package layout is:
|
|
10
|
+
|
|
11
|
+
- `arbora.mcp_server` — native MCP/REST composition and serving lifecycle.
|
|
12
|
+
- `arbora.fleet` — in-process MCP fleet gateway and child lifecycle.
|
|
13
|
+
- `arbora.gateway` — REST gateway and dashboard projections.
|
|
14
|
+
- `arbora.control_plane` — reconciliation and action policy.
|
|
15
|
+
- `arbora.webui_host` — optional Agent WebUI co-service hosting.
|
|
16
|
+
- `arbora.deployment` — configuration, diagnostics, canaries, and operations.
|
|
17
|
+
|
|
18
|
+
The ``arbora`` console command serves
|
|
19
|
+
``arbora.mcp_server.server:mcp_server`` over stdio or authenticated
|
|
20
|
+
streamable HTTP. Capability limits that depend on unfinished upstream
|
|
21
|
+
contracts are documented in ``docs/status.md`` and fail closed; no legacy
|
|
22
|
+
fallback is implied by this package description.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from arbora._version import __version__
|
|
26
|
+
|
|
27
|
+
from . import epistemic
|
|
28
|
+
|
|
29
|
+
__all__ = ["__version__", "epistemic"]
|
arbora/_version.py
ADDED
arbora/a2a/__init__.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""First-party Arbora unary A2A facade."""
|
|
2
|
+
|
|
3
|
+
from .application import (
|
|
4
|
+
A2AAuthenticator,
|
|
5
|
+
AmbientA2AAuthenticator,
|
|
6
|
+
create_a2a_application,
|
|
7
|
+
create_a2a_handlers,
|
|
8
|
+
)
|
|
9
|
+
from .authority import (
|
|
10
|
+
A2AIdempotencyConflict,
|
|
11
|
+
A2ATaskAuthority,
|
|
12
|
+
A2ATaskNotCancelable,
|
|
13
|
+
WorkItemA2AAuthority,
|
|
14
|
+
)
|
|
15
|
+
from .composition import A2AComposition, compose_a2a
|
|
16
|
+
from .models import (
|
|
17
|
+
A2AAgentCard,
|
|
18
|
+
A2AListResult,
|
|
19
|
+
A2AMessage,
|
|
20
|
+
A2ARouteDecision,
|
|
21
|
+
A2ATask,
|
|
22
|
+
)
|
|
23
|
+
from .routing import A2AAssemblyUnavailable, A2ARouter, OrchestratorA2ARouter
|
|
24
|
+
from .sdk_app import AdmittedA2AApp, create_admitted_a2a_app
|
|
25
|
+
from .service import A2ACardMetadata, A2AService
|
|
26
|
+
from .tool_task import (
|
|
27
|
+
ToolTaskCarrier,
|
|
28
|
+
ToolTaskDispatcher,
|
|
29
|
+
ToolTaskRequest,
|
|
30
|
+
ToolTaskResult,
|
|
31
|
+
WorkItemFence,
|
|
32
|
+
run_tool_task,
|
|
33
|
+
)
|
|
34
|
+
from .work_items import (
|
|
35
|
+
WorkItemView,
|
|
36
|
+
list_work_item_views,
|
|
37
|
+
parse_work_item_view,
|
|
38
|
+
work_item_view,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"AdmittedA2AApp",
|
|
43
|
+
"create_admitted_a2a_app",
|
|
44
|
+
"A2AAgentCard",
|
|
45
|
+
"A2AAssemblyUnavailable",
|
|
46
|
+
"A2AAuthenticator",
|
|
47
|
+
"A2ACardMetadata",
|
|
48
|
+
"A2AComposition",
|
|
49
|
+
"A2AIdempotencyConflict",
|
|
50
|
+
"A2AListResult",
|
|
51
|
+
"A2AMessage",
|
|
52
|
+
"A2ARouteDecision",
|
|
53
|
+
"A2ARouter",
|
|
54
|
+
"A2AService",
|
|
55
|
+
"A2ATask",
|
|
56
|
+
"A2ATaskAuthority",
|
|
57
|
+
"A2ATaskNotCancelable",
|
|
58
|
+
"AmbientA2AAuthenticator",
|
|
59
|
+
"OrchestratorA2ARouter",
|
|
60
|
+
"ToolTaskCarrier",
|
|
61
|
+
"ToolTaskDispatcher",
|
|
62
|
+
"ToolTaskRequest",
|
|
63
|
+
"ToolTaskResult",
|
|
64
|
+
"WorkItemA2AAuthority",
|
|
65
|
+
"WorkItemFence",
|
|
66
|
+
"WorkItemView",
|
|
67
|
+
"compose_a2a",
|
|
68
|
+
"create_a2a_application",
|
|
69
|
+
"create_a2a_handlers",
|
|
70
|
+
"list_work_item_views",
|
|
71
|
+
"parse_work_item_view",
|
|
72
|
+
"run_tool_task",
|
|
73
|
+
"work_item_view",
|
|
74
|
+
]
|
arbora/a2a/admission.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Typed context-budget tool-subset admission for the A2A door.
|
|
2
|
+
|
|
3
|
+
Owned by ``GRAPHOS-A2A-002``: admission, refusal, and provenance for the
|
|
4
|
+
smallest authorized tool subset that covers a selected agent graph within a
|
|
5
|
+
caller's context budget (see ``docs/a2a.md``, "Tool-subset assembly
|
|
6
|
+
status"). This fails closed today because epistemic-graph's
|
|
7
|
+
``AgentAssemble`` operation returns unavailable and the signed AU
|
|
8
|
+
``AgentTurnEnvelope`` carries no allowed-tool-subset field
|
|
9
|
+
(agent-utilities ``AU-CONTROL-R035``). Neither upstream gap is bridged with a
|
|
10
|
+
local heuristic, a cached prior subset, or an advisory best-effort answer.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from typing import Literal
|
|
16
|
+
|
|
17
|
+
from pydantic import Field
|
|
18
|
+
|
|
19
|
+
from .models import A2AContextBudget, _WireModel
|
|
20
|
+
|
|
21
|
+
__all__ = [
|
|
22
|
+
"ToolSubsetAdmissionDecision",
|
|
23
|
+
"ToolSubsetAdmissionRefusalReason",
|
|
24
|
+
"ToolSubsetAdmissionRequest",
|
|
25
|
+
"admit_tool_subset",
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
ToolSubsetAdmissionRefusalReason = Literal[
|
|
29
|
+
"EG_ASSEMBLE_UNAVAILABLE",
|
|
30
|
+
"ENVELOPE_LACKS_ALLOWED_TOOL_SUBSET",
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ToolSubsetAdmissionRequest(_WireModel):
|
|
35
|
+
"""A caller's request to admit the smallest authorized tool subset."""
|
|
36
|
+
|
|
37
|
+
agent_graph_ref: str = Field(min_length=1, max_length=512)
|
|
38
|
+
context_budget: A2AContextBudget
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ToolSubsetAdmissionDecision(_WireModel):
|
|
42
|
+
"""The door's admit/refuse result. Never advisory; never partial."""
|
|
43
|
+
|
|
44
|
+
admitted: bool
|
|
45
|
+
admitted_tools: tuple[str, ...] = Field(default=(), max_length=64)
|
|
46
|
+
refusal_reason: ToolSubsetAdmissionRefusalReason | None = None
|
|
47
|
+
provenance: str = Field(min_length=1, max_length=256)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def admit_tool_subset(
|
|
51
|
+
request: ToolSubsetAdmissionRequest,
|
|
52
|
+
) -> ToolSubsetAdmissionDecision:
|
|
53
|
+
"""Admit the request, or fail closed with a typed, provenanced refusal.
|
|
54
|
+
|
|
55
|
+
``GRAPHOS-A2A-R017.1``: epistemic-graph's ``AgentAssemble`` operation
|
|
56
|
+
returns unavailable today, so no context-budget-bound tool subset can be
|
|
57
|
+
derived or cryptographically carried. This refuses closed rather than
|
|
58
|
+
returning an advisory, cached, or partial subset.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
del request
|
|
62
|
+
return ToolSubsetAdmissionDecision(
|
|
63
|
+
admitted=False,
|
|
64
|
+
admitted_tools=(),
|
|
65
|
+
refusal_reason="EG_ASSEMBLE_UNAVAILABLE",
|
|
66
|
+
provenance="eg.AgentAssemble:unavailable",
|
|
67
|
+
)
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
"""Authenticated Agent Card and JSON-RPC unary A2A transport."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable, Iterator
|
|
6
|
+
from contextlib import contextmanager
|
|
7
|
+
from secrets import token_hex
|
|
8
|
+
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
9
|
+
|
|
10
|
+
from fastapi import FastAPI, Request
|
|
11
|
+
from fastapi.encoders import jsonable_encoder
|
|
12
|
+
from fastapi.responses import JSONResponse
|
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field, ValidationError
|
|
14
|
+
|
|
15
|
+
from arbora.api.http.auth import HTTPAuthenticationError
|
|
16
|
+
|
|
17
|
+
from .authority import (
|
|
18
|
+
A2AIdempotencyConflict,
|
|
19
|
+
A2AStreamingUnavailable,
|
|
20
|
+
A2ATaskNotCancelable,
|
|
21
|
+
A2ATransitionHistoryUnavailable,
|
|
22
|
+
)
|
|
23
|
+
from .models import A2AMessage, A2AOperationInvokeParams, A2APlanConfirmParams
|
|
24
|
+
from .routing import A2AAssemblyUnavailable, A2AOperationBridgeUnavailable
|
|
25
|
+
from .service import A2AService
|
|
26
|
+
from .terminal_event import A2ATerminalEventUnavailable
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from arbora.api.http.auth import AmbientHTTPAuthenticator
|
|
30
|
+
from arbora.api.invoke.pipeline import InvokeServices
|
|
31
|
+
from arbora.api.invoke.steps import VerifiedCaller
|
|
32
|
+
|
|
33
|
+
__all__ = [
|
|
34
|
+
"A2AAuthenticator",
|
|
35
|
+
"AmbientA2AAuthenticator",
|
|
36
|
+
"create_a2a_application",
|
|
37
|
+
"create_a2a_handlers",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
@runtime_checkable
|
|
42
|
+
class A2AAuthenticator(Protocol):
|
|
43
|
+
async def authenticate(
|
|
44
|
+
self, request: Request, *, scope: str
|
|
45
|
+
) -> VerifiedCaller | None:
|
|
46
|
+
"""Verify authority; an injected adapter may own its dispatch binding."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class AmbientA2AAuthenticator:
|
|
50
|
+
"""Resolve each HTTP request through AU's verified bearer/session flow."""
|
|
51
|
+
|
|
52
|
+
async def authenticate(self, request: Request, *, scope: str) -> VerifiedCaller:
|
|
53
|
+
caller = await _request_caller(request)
|
|
54
|
+
return _require_caller_scope(caller, scope)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def _request_caller(request: Request) -> VerifiedCaller:
|
|
58
|
+
from arbora.api.http.auth import (
|
|
59
|
+
AmbientHTTPAuthenticator,
|
|
60
|
+
verified_bearer_session_for_request,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return await AmbientHTTPAuthenticator(
|
|
64
|
+
session_for_request=verified_bearer_session_for_request
|
|
65
|
+
).authenticate(request)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _require_caller_scope(caller: VerifiedCaller, scope: str) -> VerifiedCaller:
|
|
69
|
+
if scope not in caller.effective_scopes:
|
|
70
|
+
raise PermissionError("Required A2A scope unavailable")
|
|
71
|
+
return caller
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def _refresh_unary_caller(caller: VerifiedCaller | None) -> None:
|
|
75
|
+
"""Refuse changed authority before entering the task service's session."""
|
|
76
|
+
if caller is None:
|
|
77
|
+
return
|
|
78
|
+
from arbora.api.invoke.bootstrap import (
|
|
79
|
+
SessionRequiredError,
|
|
80
|
+
verify_current_from_session,
|
|
81
|
+
)
|
|
82
|
+
from arbora.api.invoke.steps import authenticate
|
|
83
|
+
|
|
84
|
+
try:
|
|
85
|
+
current = await verify_current_from_session(caller)
|
|
86
|
+
except SessionRequiredError:
|
|
87
|
+
raise HTTPAuthenticationError("Current A2A authority required") from None
|
|
88
|
+
if current != caller or authenticate(current) is not None:
|
|
89
|
+
raise HTTPAuthenticationError("Current A2A authority required")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@contextmanager
|
|
93
|
+
def _caller_session_scope(caller: VerifiedCaller | None) -> Iterator[None]:
|
|
94
|
+
"""Bind a returned caller through legacy dispatch; never resolve a fallback."""
|
|
95
|
+
if caller is None:
|
|
96
|
+
# Explicit injected authenticators already own their admission/binding.
|
|
97
|
+
yield
|
|
98
|
+
return
|
|
99
|
+
from arbora.api.contracts.session import use_session
|
|
100
|
+
from arbora.security.actor_context import use_actor
|
|
101
|
+
|
|
102
|
+
with use_actor(caller.session.actor), use_session(caller.session):
|
|
103
|
+
yield
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _authentication_error(request_id: Any, error: PermissionError) -> JSONResponse:
|
|
107
|
+
status = 401 if isinstance(error, HTTPAuthenticationError) else 403
|
|
108
|
+
return _error(request_id, -32001, "A2A authority refused", status)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class _Params(BaseModel):
|
|
112
|
+
model_config = ConfigDict(populate_by_name=True, extra="forbid")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class _SendParams(_Params):
|
|
116
|
+
message: A2AMessage
|
|
117
|
+
context_budget_tokens: int | None = Field(
|
|
118
|
+
default=None, alias="contextBudgetTokens", ge=256, le=1_000_000
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class _TaskParams(_Params):
|
|
123
|
+
id: str = Field(min_length=1, max_length=80)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class _GetTaskParams(_TaskParams):
|
|
127
|
+
include_committed_event: bool = Field(
|
|
128
|
+
default=False, alias="includeCommittedEvent", strict=True
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
class _ResubscribeParams(_Params):
|
|
133
|
+
id: str = Field(min_length=1, max_length=80)
|
|
134
|
+
cursor: str | None = Field(default=None, max_length=1024)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class _ListParams(_Params):
|
|
138
|
+
cursor: str | None = Field(default=None, max_length=1024)
|
|
139
|
+
limit: int = Field(default=50, ge=1, le=100)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _error(request_id: Any, code: int, message: str, status: int = 400) -> JSONResponse:
|
|
143
|
+
return JSONResponse(
|
|
144
|
+
{
|
|
145
|
+
"jsonrpc": "2.0",
|
|
146
|
+
"id": request_id,
|
|
147
|
+
"error": {"code": code, "message": message},
|
|
148
|
+
},
|
|
149
|
+
status_code=status,
|
|
150
|
+
headers={"Cache-Control": "no-store"},
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def _request_envelope(
|
|
155
|
+
request: Request,
|
|
156
|
+
) -> tuple[Any, str, dict[str, Any]] | JSONResponse:
|
|
157
|
+
try:
|
|
158
|
+
body = await request.json()
|
|
159
|
+
except ValueError:
|
|
160
|
+
return _error(None, -32700, "Parse error")
|
|
161
|
+
if not isinstance(body, dict) or body.get("jsonrpc") != "2.0":
|
|
162
|
+
request_id = body.get("id") if isinstance(body, dict) else None
|
|
163
|
+
return _error(request_id, -32600, "Invalid Request")
|
|
164
|
+
request_id = body.get("id")
|
|
165
|
+
method = body.get("method")
|
|
166
|
+
params = body.get("params")
|
|
167
|
+
if not isinstance(method, str) or not isinstance(params, dict):
|
|
168
|
+
return _error(request_id, -32602, "Invalid params")
|
|
169
|
+
return request_id, method, params
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
async def _send_message(service: A2AService, request: Request, raw_params: Any) -> Any:
|
|
173
|
+
send_params = _SendParams.model_validate(raw_params)
|
|
174
|
+
key = request.headers.get("Idempotency-Key", "")
|
|
175
|
+
if not key:
|
|
176
|
+
raise ValueError("Idempotency-Key is required")
|
|
177
|
+
return await service.send_message(
|
|
178
|
+
message=send_params.message,
|
|
179
|
+
idempotency_key=key,
|
|
180
|
+
context_budget_tokens=send_params.context_budget_tokens,
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
async def _get_task(service: A2AService, raw_params: Any, request_id: Any) -> Any:
|
|
185
|
+
task_params = _GetTaskParams.model_validate(raw_params)
|
|
186
|
+
result = await service.get_task(
|
|
187
|
+
task_params.id, include_committed_event=task_params.include_committed_event
|
|
188
|
+
)
|
|
189
|
+
return result or _error(request_id, -32001, "Task not found", 404)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def _resubscribe_stub(raw_params: Any) -> Any:
|
|
193
|
+
# Registered in the method table (GRAPHOS-A2A-R001) but fail-closed:
|
|
194
|
+
# durable streaming has no bounded, restart-safe event-cursor backing
|
|
195
|
+
# yet. Params are still validated so a malformed call is rejected as
|
|
196
|
+
# invalid, not treated as an unreachable method.
|
|
197
|
+
_ResubscribeParams.model_validate(raw_params)
|
|
198
|
+
raise A2AStreamingUnavailable(
|
|
199
|
+
"durable task streaming and resubscribe are not available yet"
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _op_invoke_stub(raw_params: Any) -> Any:
|
|
204
|
+
# Registered in the method table (GRAPHOS-A2A-R006) but fail-closed:
|
|
205
|
+
# the shared hosted-operation registry's invoke path is not bridged in
|
|
206
|
+
# yet. Params are still validated so a malformed call is rejected as
|
|
207
|
+
# invalid, not treated as an unreachable method.
|
|
208
|
+
A2AOperationInvokeParams.model_validate(raw_params)
|
|
209
|
+
raise A2AOperationBridgeUnavailable(
|
|
210
|
+
"arbora.op/invoke is not bridged to the shared operation registry yet"
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _plan_confirm_stub(raw_params: Any) -> Any:
|
|
215
|
+
# Registered in the method table (GRAPHOS-A2A-R005/R006) but
|
|
216
|
+
# fail-closed: the signed human approval exchange is not implemented
|
|
217
|
+
# end to end yet, so no approval attempt may succeed.
|
|
218
|
+
from .approval import verify_pending_signature
|
|
219
|
+
|
|
220
|
+
confirmation = A2APlanConfirmParams.model_validate(raw_params)
|
|
221
|
+
# No trusted human-key resolver or durable pending/grant authority is wired.
|
|
222
|
+
# A parsed payload or valid detached signature cannot supply that authority.
|
|
223
|
+
check = verify_pending_signature(confirmation, evidence=None)
|
|
224
|
+
raise A2AOperationBridgeUnavailable(
|
|
225
|
+
f"arbora.plan/confirm authority unavailable: {check.value}"
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
_SYNC_METHODS: dict[str, Callable[[Any], Any]] = {
|
|
230
|
+
"tasks/resubscribe": _resubscribe_stub,
|
|
231
|
+
"arbora.op/invoke": _op_invoke_stub,
|
|
232
|
+
"arbora.plan/confirm": _plan_confirm_stub,
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
async def _invoke_method(
|
|
237
|
+
service: A2AService,
|
|
238
|
+
request: Request,
|
|
239
|
+
method: str,
|
|
240
|
+
raw_params: dict[str, Any],
|
|
241
|
+
request_id: Any,
|
|
242
|
+
) -> Any:
|
|
243
|
+
"""Validate and invoke one unary method on the shared service."""
|
|
244
|
+
if method == "message/send":
|
|
245
|
+
return await _send_message(service, request, raw_params)
|
|
246
|
+
if method == "tasks/get":
|
|
247
|
+
return await _get_task(service, raw_params, request_id)
|
|
248
|
+
if method == "tasks/list":
|
|
249
|
+
list_params = _ListParams.model_validate(raw_params)
|
|
250
|
+
return await service.list_tasks(
|
|
251
|
+
cursor=list_params.cursor, limit=list_params.limit
|
|
252
|
+
)
|
|
253
|
+
if method == "tasks/cancel":
|
|
254
|
+
cancel_params = _TaskParams.model_validate(raw_params)
|
|
255
|
+
return await service.cancel_task(cancel_params.id)
|
|
256
|
+
sync_handler = _SYNC_METHODS.get(method)
|
|
257
|
+
if sync_handler is not None:
|
|
258
|
+
return sync_handler(raw_params)
|
|
259
|
+
return _error(request_id, -32601, "Method not found", 404)
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
_ERROR_CODES: dict[type[Exception], tuple[int, int]] = {
|
|
263
|
+
A2ATerminalEventUnavailable: (-32013, 503),
|
|
264
|
+
A2AIdempotencyConflict: (-32009, 409),
|
|
265
|
+
A2AAssemblyUnavailable: (-32003, 503),
|
|
266
|
+
A2ATaskNotCancelable: (-32002, 409),
|
|
267
|
+
A2AStreamingUnavailable: (-32010, 501),
|
|
268
|
+
A2AOperationBridgeUnavailable: (-32011, 501),
|
|
269
|
+
A2ATransitionHistoryUnavailable: (-32012, 501),
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def _application_error(request_id: Any, error: Exception) -> JSONResponse:
|
|
274
|
+
"""Translate known service failures without echoing caller input."""
|
|
275
|
+
mapped = _ERROR_CODES.get(type(error))
|
|
276
|
+
if mapped is not None:
|
|
277
|
+
code, status = mapped
|
|
278
|
+
return _error(request_id, code, str(error), status)
|
|
279
|
+
# Pydantic errors may echo caller text in ``input_value``. Keep the wire
|
|
280
|
+
# error stable and privacy-safe; details belong in local logs.
|
|
281
|
+
return _error(request_id, -32602, "Invalid params")
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _operation_response(
|
|
285
|
+
outcome: Any, *, op: str, request_id: Any, caller_id: str, digest: str
|
|
286
|
+
) -> JSONResponse:
|
|
287
|
+
"""Project shared operation outcomes without reclassifying refusals."""
|
|
288
|
+
from arbora.api.errors import a2a_error_status, to_envelope
|
|
289
|
+
from arbora.api.invoke.steps import OpError
|
|
290
|
+
|
|
291
|
+
status = 200
|
|
292
|
+
if outcome.code == "OK":
|
|
293
|
+
payload = {
|
|
294
|
+
"ok": True,
|
|
295
|
+
"result": jsonable_encoder(outcome.value),
|
|
296
|
+
"meta": {"registry_digest": digest, "api_version": "v1"},
|
|
297
|
+
}
|
|
298
|
+
else:
|
|
299
|
+
status, payload = to_envelope(
|
|
300
|
+
outcome,
|
|
301
|
+
op=op,
|
|
302
|
+
request_id=caller_id or token_hex(12),
|
|
303
|
+
registry_digest=digest,
|
|
304
|
+
)
|
|
305
|
+
body: dict[str, Any] = {"jsonrpc": "2.0", "id": request_id}
|
|
306
|
+
if isinstance(outcome, OpError):
|
|
307
|
+
code, status = a2a_error_status(outcome.code, source=outcome.source)
|
|
308
|
+
body["error"] = {
|
|
309
|
+
"code": code,
|
|
310
|
+
"message": payload["error"]["message"],
|
|
311
|
+
"data": payload,
|
|
312
|
+
}
|
|
313
|
+
else:
|
|
314
|
+
body["result"] = payload
|
|
315
|
+
return JSONResponse(body, status_code=status, headers={"Cache-Control": "no-store"})
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
async def _invoke_operation(
|
|
319
|
+
request: Request,
|
|
320
|
+
raw_params: dict[str, Any],
|
|
321
|
+
request_id: Any,
|
|
322
|
+
services: InvokeServices,
|
|
323
|
+
authenticator: AmbientHTTPAuthenticator,
|
|
324
|
+
) -> JSONResponse:
|
|
325
|
+
from arbora.api.http.auth import HTTPAuthenticationError
|
|
326
|
+
from arbora.api.invoke.pipeline import invoke
|
|
327
|
+
from arbora.api.invoke.steps import OpError, OpResult
|
|
328
|
+
from arbora.api.registry import Surface
|
|
329
|
+
|
|
330
|
+
params = A2AOperationInvokeParams.model_validate(raw_params)
|
|
331
|
+
caller_id = ""
|
|
332
|
+
outcome: OpError | OpResult
|
|
333
|
+
try:
|
|
334
|
+
caller = await authenticator.authenticate(request)
|
|
335
|
+
except HTTPAuthenticationError:
|
|
336
|
+
outcome = OpError("UNAUTHENTICATED")
|
|
337
|
+
else:
|
|
338
|
+
# A plan reference cannot substitute for the missing signed A2A exchange.
|
|
339
|
+
if params.plan_ref is not None:
|
|
340
|
+
raise A2AOperationBridgeUnavailable(
|
|
341
|
+
"A2A plan consumption requires the signed approval exchange"
|
|
342
|
+
)
|
|
343
|
+
caller_id = caller.request_id
|
|
344
|
+
outcome = await invoke(
|
|
345
|
+
params.op,
|
|
346
|
+
params.params,
|
|
347
|
+
caller,
|
|
348
|
+
Surface.A2A,
|
|
349
|
+
services=services,
|
|
350
|
+
idempotency_key=params.idempotency_key,
|
|
351
|
+
)
|
|
352
|
+
return _operation_response(
|
|
353
|
+
outcome,
|
|
354
|
+
op=params.op,
|
|
355
|
+
request_id=request_id,
|
|
356
|
+
caller_id=caller_id,
|
|
357
|
+
digest=services.registry.digest,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
def _validate_operation_composition(
|
|
362
|
+
services: InvokeServices | None,
|
|
363
|
+
authenticator: AmbientHTTPAuthenticator | None,
|
|
364
|
+
) -> None:
|
|
365
|
+
if (services is None) != (authenticator is None):
|
|
366
|
+
raise ValueError(
|
|
367
|
+
"operation services and authenticator must be composed together"
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
async def _operation_request(
|
|
372
|
+
request: Request,
|
|
373
|
+
raw_params: dict[str, Any],
|
|
374
|
+
request_id: Any,
|
|
375
|
+
services: InvokeServices,
|
|
376
|
+
authenticator: AmbientHTTPAuthenticator,
|
|
377
|
+
) -> JSONResponse:
|
|
378
|
+
try:
|
|
379
|
+
return await _invoke_operation(
|
|
380
|
+
request, raw_params, request_id, services, authenticator
|
|
381
|
+
)
|
|
382
|
+
except (A2AOperationBridgeUnavailable, ValidationError) as error:
|
|
383
|
+
return _application_error(request_id, error)
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
async def _unary_request(
|
|
387
|
+
service: A2AService,
|
|
388
|
+
authenticator: A2AAuthenticator,
|
|
389
|
+
request: Request,
|
|
390
|
+
method: str,
|
|
391
|
+
raw_params: dict[str, Any],
|
|
392
|
+
request_id: Any,
|
|
393
|
+
) -> JSONResponse:
|
|
394
|
+
write_methods = {"message/send", "tasks/cancel", "arbora.plan/confirm"}
|
|
395
|
+
scope = "kg:write" if method in write_methods else "kg:read"
|
|
396
|
+
try:
|
|
397
|
+
caller = await authenticator.authenticate(request, scope=scope)
|
|
398
|
+
await _refresh_unary_caller(caller)
|
|
399
|
+
with _caller_session_scope(caller):
|
|
400
|
+
result = await _invoke_method(
|
|
401
|
+
service, request, method, raw_params, request_id
|
|
402
|
+
)
|
|
403
|
+
except PermissionError as error:
|
|
404
|
+
return _authentication_error(request_id, error)
|
|
405
|
+
except (
|
|
406
|
+
A2ATerminalEventUnavailable,
|
|
407
|
+
A2AIdempotencyConflict,
|
|
408
|
+
A2AAssemblyUnavailable,
|
|
409
|
+
A2AOperationBridgeUnavailable,
|
|
410
|
+
A2AStreamingUnavailable,
|
|
411
|
+
A2ATransitionHistoryUnavailable,
|
|
412
|
+
A2ATaskNotCancelable,
|
|
413
|
+
ValidationError,
|
|
414
|
+
TypeError,
|
|
415
|
+
ValueError,
|
|
416
|
+
) as error:
|
|
417
|
+
return _application_error(request_id, error)
|
|
418
|
+
return _unary_response(result, request_id)
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _unary_response(result: Any, request_id: Any) -> JSONResponse:
|
|
422
|
+
if isinstance(result, JSONResponse):
|
|
423
|
+
return result
|
|
424
|
+
payload = result.model_dump(mode="json", by_alias=True)
|
|
425
|
+
return JSONResponse(
|
|
426
|
+
{"jsonrpc": "2.0", "id": request_id, "result": payload},
|
|
427
|
+
headers={"Cache-Control": "no-store"},
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
async def _agent_card_request(
|
|
432
|
+
service: A2AService, authenticator: A2AAuthenticator, request: Request
|
|
433
|
+
) -> JSONResponse:
|
|
434
|
+
try:
|
|
435
|
+
await authenticator.authenticate(request, scope="kg:read")
|
|
436
|
+
except PermissionError as error:
|
|
437
|
+
return _authentication_error(None, error)
|
|
438
|
+
endpoint = str(request.url.replace(path="/a2a", query=""))
|
|
439
|
+
return JSONResponse(
|
|
440
|
+
service.agent_card(endpoint).model_dump(mode="json", by_alias=True),
|
|
441
|
+
headers={"Cache-Control": "no-store"},
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def create_a2a_handlers(
|
|
446
|
+
*,
|
|
447
|
+
service: A2AService,
|
|
448
|
+
authenticator: A2AAuthenticator,
|
|
449
|
+
operation_services: InvokeServices | None = None,
|
|
450
|
+
operation_authenticator: AmbientHTTPAuthenticator | None = None,
|
|
451
|
+
) -> tuple[Any, Any]:
|
|
452
|
+
"""Create route handlers reusable by standalone FastAPI and FastMCP."""
|
|
453
|
+
if not isinstance(authenticator, A2AAuthenticator):
|
|
454
|
+
raise TypeError("authenticator does not implement A2AAuthenticator")
|
|
455
|
+
_validate_operation_composition(operation_services, operation_authenticator)
|
|
456
|
+
|
|
457
|
+
async def agent_card(request: Request) -> JSONResponse:
|
|
458
|
+
return await _agent_card_request(service, authenticator, request)
|
|
459
|
+
|
|
460
|
+
async def json_rpc(request: Request) -> JSONResponse:
|
|
461
|
+
envelope = await _request_envelope(request)
|
|
462
|
+
if isinstance(envelope, JSONResponse):
|
|
463
|
+
return envelope
|
|
464
|
+
request_id, method, raw_params = envelope
|
|
465
|
+
if method == "arbora.op/invoke" and operation_services is not None:
|
|
466
|
+
assert operation_authenticator is not None
|
|
467
|
+
return await _operation_request(
|
|
468
|
+
request,
|
|
469
|
+
raw_params,
|
|
470
|
+
request_id,
|
|
471
|
+
operation_services,
|
|
472
|
+
operation_authenticator,
|
|
473
|
+
)
|
|
474
|
+
return await _unary_request(
|
|
475
|
+
service, authenticator, request, method, raw_params, request_id
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
return agent_card, json_rpc
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def create_a2a_application(
|
|
482
|
+
*,
|
|
483
|
+
service: A2AService,
|
|
484
|
+
authenticator: A2AAuthenticator,
|
|
485
|
+
operation_services: InvokeServices | None = None,
|
|
486
|
+
operation_authenticator: AmbientHTTPAuthenticator | None = None,
|
|
487
|
+
) -> FastAPI:
|
|
488
|
+
metadata = service.card_metadata
|
|
489
|
+
app = FastAPI(title=f"{metadata.name} A2A", version=metadata.version)
|
|
490
|
+
card_handler, rpc_handler = create_a2a_handlers(
|
|
491
|
+
service=service,
|
|
492
|
+
authenticator=authenticator,
|
|
493
|
+
operation_services=operation_services,
|
|
494
|
+
operation_authenticator=operation_authenticator,
|
|
495
|
+
)
|
|
496
|
+
app.add_api_route("/.well-known/agent-card.json", card_handler, methods=["GET"])
|
|
497
|
+
app.add_api_route("/a2a", rpc_handler, methods=["POST"])
|
|
498
|
+
return app
|