agent_os_kernel 3.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.
- agent_control_plane/__init__.py +662 -0
- agent_control_plane/a2a_adapter.py +543 -0
- agent_control_plane/adapter.py +417 -0
- agent_control_plane/agent_hibernation.py +394 -0
- agent_control_plane/agent_kernel.py +470 -0
- agent_control_plane/compliance.py +720 -0
- agent_control_plane/constraint_graphs.py +478 -0
- agent_control_plane/control_plane.py +854 -0
- agent_control_plane/example_executors.py +195 -0
- agent_control_plane/execution_engine.py +231 -0
- agent_control_plane/flight_recorder.py +846 -0
- agent_control_plane/governance_layer.py +435 -0
- agent_control_plane/hf_utils.py +563 -0
- agent_control_plane/interfaces/__init__.py +55 -0
- agent_control_plane/interfaces/kernel_interface.py +361 -0
- agent_control_plane/interfaces/plugin_interface.py +497 -0
- agent_control_plane/interfaces/protocol_interfaces.py +387 -0
- agent_control_plane/kernel_space.py +1009 -0
- agent_control_plane/langchain_adapter.py +424 -0
- agent_control_plane/lifecycle.py +3113 -0
- agent_control_plane/mcp_adapter.py +653 -0
- agent_control_plane/ml_safety.py +563 -0
- agent_control_plane/multimodal.py +727 -0
- agent_control_plane/mute_agent.py +422 -0
- agent_control_plane/observability.py +787 -0
- agent_control_plane/orchestrator.py +482 -0
- agent_control_plane/plugin_registry.py +750 -0
- agent_control_plane/policy_engine.py +954 -0
- agent_control_plane/process_isolation.py +777 -0
- agent_control_plane/shadow_mode.py +310 -0
- agent_control_plane/signals.py +493 -0
- agent_control_plane/supervisor_agents.py +430 -0
- agent_control_plane/time_travel_debugger.py +557 -0
- agent_control_plane/tool_registry.py +452 -0
- agent_control_plane/vfs.py +697 -0
- agent_kernel/__init__.py +69 -0
- agent_kernel/analyzer.py +435 -0
- agent_kernel/auditor.py +36 -0
- agent_kernel/completeness_auditor.py +237 -0
- agent_kernel/detector.py +203 -0
- agent_kernel/kernel.py +744 -0
- agent_kernel/memory_manager.py +85 -0
- agent_kernel/models.py +374 -0
- agent_kernel/nudge_mechanism.py +263 -0
- agent_kernel/outcome_analyzer.py +338 -0
- agent_kernel/patcher.py +582 -0
- agent_kernel/semantic_analyzer.py +316 -0
- agent_kernel/semantic_purge.py +349 -0
- agent_kernel/simulator.py +449 -0
- agent_kernel/teacher.py +85 -0
- agent_kernel/triage.py +152 -0
- agent_os/__init__.py +409 -0
- agent_os/_adversarial_impl.py +200 -0
- agent_os/_circuit_breaker_impl.py +232 -0
- agent_os/_mcp_metrics.py +193 -0
- agent_os/adversarial.py +20 -0
- agent_os/agents_compat.py +490 -0
- agent_os/audit_logger.py +135 -0
- agent_os/base_agent.py +651 -0
- agent_os/circuit_breaker.py +34 -0
- agent_os/cli/__init__.py +659 -0
- agent_os/cli/cmd_audit.py +128 -0
- agent_os/cli/cmd_init.py +152 -0
- agent_os/cli/cmd_policy.py +41 -0
- agent_os/cli/cmd_policy_gen.py +180 -0
- agent_os/cli/cmd_validate.py +258 -0
- agent_os/cli/mcp_scan.py +265 -0
- agent_os/cli/output.py +192 -0
- agent_os/cli/policy_checker.py +330 -0
- agent_os/compat.py +74 -0
- agent_os/constraint_graph.py +234 -0
- agent_os/content_governance.py +140 -0
- agent_os/context_budget.py +305 -0
- agent_os/credential_redactor.py +224 -0
- agent_os/diff_policy.py +89 -0
- agent_os/egress_policy.py +159 -0
- agent_os/escalation.py +276 -0
- agent_os/event_bus.py +124 -0
- agent_os/exceptions.py +180 -0
- agent_os/execution_context_policy.py +141 -0
- agent_os/github_enterprise.py +96 -0
- agent_os/health.py +20 -0
- agent_os/integrations/__init__.py +279 -0
- agent_os/integrations/a2a_adapter.py +279 -0
- agent_os/integrations/agent_lightning/__init__.py +30 -0
- agent_os/integrations/anthropic_adapter.py +420 -0
- agent_os/integrations/autogen_adapter.py +620 -0
- agent_os/integrations/base.py +1137 -0
- agent_os/integrations/compat.py +229 -0
- agent_os/integrations/config.py +98 -0
- agent_os/integrations/conversation_guardian.py +957 -0
- agent_os/integrations/crewai_adapter.py +467 -0
- agent_os/integrations/drift_detector.py +425 -0
- agent_os/integrations/dry_run.py +124 -0
- agent_os/integrations/escalation.py +582 -0
- agent_os/integrations/gemini_adapter.py +364 -0
- agent_os/integrations/google_adk_adapter.py +633 -0
- agent_os/integrations/guardrails_adapter.py +394 -0
- agent_os/integrations/health.py +197 -0
- agent_os/integrations/langchain_adapter.py +654 -0
- agent_os/integrations/llamafirewall.py +343 -0
- agent_os/integrations/llamaindex_adapter.py +188 -0
- agent_os/integrations/logging.py +191 -0
- agent_os/integrations/maf_adapter.py +631 -0
- agent_os/integrations/mistral_adapter.py +365 -0
- agent_os/integrations/openai_adapter.py +816 -0
- agent_os/integrations/openai_agents_sdk.py +406 -0
- agent_os/integrations/policy_compose.py +171 -0
- agent_os/integrations/profiling.py +144 -0
- agent_os/integrations/pydantic_ai_adapter.py +420 -0
- agent_os/integrations/rate_limiter.py +130 -0
- agent_os/integrations/rbac.py +143 -0
- agent_os/integrations/registry.py +113 -0
- agent_os/integrations/scope_guard.py +303 -0
- agent_os/integrations/semantic_kernel_adapter.py +769 -0
- agent_os/integrations/smolagents_adapter.py +629 -0
- agent_os/integrations/templates.py +178 -0
- agent_os/integrations/token_budget.py +134 -0
- agent_os/integrations/tool_aliases.py +190 -0
- agent_os/integrations/webhooks.py +177 -0
- agent_os/lite.py +208 -0
- agent_os/mcp_gateway.py +385 -0
- agent_os/mcp_message_signer.py +273 -0
- agent_os/mcp_protocols.py +161 -0
- agent_os/mcp_response_scanner.py +232 -0
- agent_os/mcp_security.py +924 -0
- agent_os/mcp_session_auth.py +231 -0
- agent_os/mcp_sliding_rate_limiter.py +184 -0
- agent_os/memory_guard.py +409 -0
- agent_os/metrics.py +134 -0
- agent_os/mute.py +428 -0
- agent_os/mute_agent.py +209 -0
- agent_os/policies/__init__.py +77 -0
- agent_os/policies/async_evaluator.py +275 -0
- agent_os/policies/backends.py +670 -0
- agent_os/policies/bridge.py +169 -0
- agent_os/policies/budget.py +85 -0
- agent_os/policies/cli.py +294 -0
- agent_os/policies/conflict_resolution.py +270 -0
- agent_os/policies/data_classification.py +252 -0
- agent_os/policies/evaluator.py +239 -0
- agent_os/policies/policy_schema.json +228 -0
- agent_os/policies/rate_limiting.py +145 -0
- agent_os/policies/schema.py +115 -0
- agent_os/policies/shared.py +331 -0
- agent_os/prompt_injection.py +694 -0
- agent_os/providers.py +182 -0
- agent_os/py.typed +0 -0
- agent_os/retry.py +81 -0
- agent_os/reversibility.py +251 -0
- agent_os/sandbox.py +432 -0
- agent_os/sandbox_provider.py +140 -0
- agent_os/secure_codegen.py +525 -0
- agent_os/security_skills.py +538 -0
- agent_os/semantic_policy.py +422 -0
- agent_os/server/__init__.py +15 -0
- agent_os/server/__main__.py +25 -0
- agent_os/server/app.py +277 -0
- agent_os/server/models.py +104 -0
- agent_os/shift_left_metrics.py +130 -0
- agent_os/stateless.py +742 -0
- agent_os/supervisor.py +148 -0
- agent_os/task_outcome.py +148 -0
- agent_os/transparency.py +181 -0
- agent_os/trust_root.py +128 -0
- agent_os_kernel-3.1.0.dist-info/METADATA +1269 -0
- agent_os_kernel-3.1.0.dist-info/RECORD +337 -0
- agent_os_kernel-3.1.0.dist-info/WHEEL +4 -0
- agent_os_kernel-3.1.0.dist-info/entry_points.txt +2 -0
- agent_os_kernel-3.1.0.dist-info/licenses/LICENSE +21 -0
- agent_os_observability/__init__.py +27 -0
- agent_os_observability/dashboards.py +898 -0
- agent_os_observability/metrics.py +398 -0
- agent_os_observability/server.py +223 -0
- agent_os_observability/tracer.py +232 -0
- agent_primitives/__init__.py +24 -0
- agent_primitives/failures.py +84 -0
- agent_primitives/py.typed +0 -0
- amb_core/__init__.py +177 -0
- amb_core/adapters/__init__.py +57 -0
- amb_core/adapters/aws_sqs_broker.py +376 -0
- amb_core/adapters/azure_servicebus_broker.py +340 -0
- amb_core/adapters/kafka_broker.py +260 -0
- amb_core/adapters/nats_broker.py +285 -0
- amb_core/adapters/rabbitmq_broker.py +235 -0
- amb_core/adapters/redis_broker.py +262 -0
- amb_core/broker.py +145 -0
- amb_core/bus.py +481 -0
- amb_core/cloudevents.py +509 -0
- amb_core/dlq.py +345 -0
- amb_core/hf_utils.py +536 -0
- amb_core/memory_broker.py +410 -0
- amb_core/models.py +141 -0
- amb_core/persistence.py +529 -0
- amb_core/schema.py +294 -0
- amb_core/tracing.py +358 -0
- atr/__init__.py +640 -0
- atr/access.py +348 -0
- atr/composition.py +645 -0
- atr/decorator.py +357 -0
- atr/executor.py +384 -0
- atr/health.py +557 -0
- atr/hf_utils.py +449 -0
- atr/injection.py +422 -0
- atr/metrics.py +440 -0
- atr/policies.py +403 -0
- atr/py.typed +2 -0
- atr/registry.py +452 -0
- atr/schema.py +480 -0
- atr/tools/safe/__init__.py +75 -0
- atr/tools/safe/calculator.py +467 -0
- atr/tools/safe/datetime_tool.py +443 -0
- atr/tools/safe/file_reader.py +402 -0
- atr/tools/safe/http_client.py +316 -0
- atr/tools/safe/json_parser.py +374 -0
- atr/tools/safe/text_tool.py +537 -0
- atr/tools/safe/toolkit.py +175 -0
- caas/__init__.py +162 -0
- caas/api/__init__.py +7 -0
- caas/api/server.py +1328 -0
- caas/caching.py +834 -0
- caas/cli.py +210 -0
- caas/conversation.py +223 -0
- caas/decay.py +72 -0
- caas/detection/__init__.py +9 -0
- caas/detection/detector.py +238 -0
- caas/enrichment.py +130 -0
- caas/gateway/__init__.py +27 -0
- caas/gateway/trust_gateway.py +474 -0
- caas/hf_utils.py +479 -0
- caas/ingestion/__init__.py +23 -0
- caas/ingestion/processors.py +253 -0
- caas/ingestion/structure_parser.py +188 -0
- caas/models.py +356 -0
- caas/pragmatic_truth.py +444 -0
- caas/routing/__init__.py +10 -0
- caas/routing/heuristic_router.py +58 -0
- caas/storage/__init__.py +9 -0
- caas/storage/store.py +389 -0
- caas/triad.py +213 -0
- caas/tuning/__init__.py +9 -0
- caas/tuning/tuner.py +329 -0
- caas/vfs/__init__.py +14 -0
- caas/vfs/filesystem.py +452 -0
- cmvk/__init__.py +218 -0
- cmvk/audit.py +402 -0
- cmvk/benchmarks.py +478 -0
- cmvk/constitutional.py +904 -0
- cmvk/hf_utils.py +301 -0
- cmvk/metrics.py +473 -0
- cmvk/profiles.py +300 -0
- cmvk/py.typed +0 -0
- cmvk/types.py +12 -0
- cmvk/verification.py +956 -0
- emk/__init__.py +89 -0
- emk/causal.py +352 -0
- emk/hf_utils.py +421 -0
- emk/indexer.py +83 -0
- emk/py.typed +0 -0
- emk/schema.py +204 -0
- emk/sleep_cycle.py +347 -0
- emk/store.py +281 -0
- iatp/__init__.py +166 -0
- iatp/attestation.py +461 -0
- iatp/cli.py +317 -0
- iatp/hf_utils.py +472 -0
- iatp/ipc_pipes.py +580 -0
- iatp/main.py +412 -0
- iatp/models/__init__.py +447 -0
- iatp/policy_engine.py +337 -0
- iatp/py.typed +2 -0
- iatp/recovery.py +321 -0
- iatp/security/__init__.py +270 -0
- iatp/sidecar/__init__.py +519 -0
- iatp/telemetry/__init__.py +164 -0
- iatp/tests/__init__.py +1 -0
- iatp/tests/test_attestation.py +370 -0
- iatp/tests/test_cli.py +131 -0
- iatp/tests/test_ed25519_attestation.py +211 -0
- iatp/tests/test_models.py +130 -0
- iatp/tests/test_policy_engine.py +347 -0
- iatp/tests/test_recovery.py +281 -0
- iatp/tests/test_security.py +222 -0
- iatp/tests/test_sidecar.py +167 -0
- iatp/tests/test_telemetry.py +175 -0
- mcp_kernel_server/__init__.py +28 -0
- mcp_kernel_server/cli.py +274 -0
- mcp_kernel_server/resources.py +217 -0
- mcp_kernel_server/server.py +564 -0
- mcp_kernel_server/tools.py +1174 -0
- mute_agent/__init__.py +68 -0
- mute_agent/core/__init__.py +1 -0
- mute_agent/core/execution_agent.py +166 -0
- mute_agent/core/handshake_protocol.py +201 -0
- mute_agent/core/reasoning_agent.py +238 -0
- mute_agent/knowledge_graph/__init__.py +1 -0
- mute_agent/knowledge_graph/graph_elements.py +65 -0
- mute_agent/knowledge_graph/multidimensional_graph.py +170 -0
- mute_agent/knowledge_graph/subgraph.py +224 -0
- mute_agent/listener/__init__.py +43 -0
- mute_agent/listener/adapters/__init__.py +31 -0
- mute_agent/listener/adapters/base_adapter.py +189 -0
- mute_agent/listener/adapters/caas_adapter.py +344 -0
- mute_agent/listener/adapters/control_plane_adapter.py +436 -0
- mute_agent/listener/adapters/iatp_adapter.py +332 -0
- mute_agent/listener/adapters/scak_adapter.py +251 -0
- mute_agent/listener/listener.py +610 -0
- mute_agent/listener/state_observer.py +436 -0
- mute_agent/listener/threshold_config.py +313 -0
- mute_agent/super_system/__init__.py +1 -0
- mute_agent/super_system/router.py +204 -0
- mute_agent/visualization/__init__.py +10 -0
- mute_agent/visualization/graph_debugger.py +502 -0
- nexus/README.md +60 -0
- nexus/__init__.py +51 -0
- nexus/arbiter.py +359 -0
- nexus/client.py +466 -0
- nexus/dmz.py +444 -0
- nexus/escrow.py +430 -0
- nexus/exceptions.py +286 -0
- nexus/pyproject.toml +36 -0
- nexus/registry.py +393 -0
- nexus/reputation.py +425 -0
- nexus/schemas/__init__.py +51 -0
- nexus/schemas/compliance.py +276 -0
- nexus/schemas/escrow.py +251 -0
- nexus/schemas/manifest.py +225 -0
- nexus/schemas/receipt.py +208 -0
- nexus/tests/__init__.py +0 -0
- nexus/tests/conftest.py +146 -0
- nexus/tests/test_arbiter.py +192 -0
- nexus/tests/test_dmz.py +194 -0
- nexus/tests/test_escrow.py +276 -0
- nexus/tests/test_exceptions.py +225 -0
- nexus/tests/test_registry.py +232 -0
- nexus/tests/test_reputation.py +328 -0
- nexus/tests/test_schemas.py +295 -0
nexus/escrow.py
ADDED
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
"""
|
|
4
|
+
Proof of Outcome / Escrow Manager
|
|
5
|
+
|
|
6
|
+
Implements the "Reward" mechanism where agents bet on task outcomes.
|
|
7
|
+
Credits are escrowed and released based on SCAK validation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from datetime import datetime, timedelta, timezone
|
|
11
|
+
from typing import Optional, Literal
|
|
12
|
+
import hashlib
|
|
13
|
+
import uuid
|
|
14
|
+
import asyncio
|
|
15
|
+
|
|
16
|
+
from .schemas.escrow import (
|
|
17
|
+
EscrowRequest,
|
|
18
|
+
EscrowReceipt,
|
|
19
|
+
EscrowStatus,
|
|
20
|
+
EscrowRelease,
|
|
21
|
+
EscrowResolution,
|
|
22
|
+
)
|
|
23
|
+
from .schemas.receipt import JobCompletionReceipt, SignedReceipt
|
|
24
|
+
from .reputation import ReputationEngine
|
|
25
|
+
from .exceptions import (
|
|
26
|
+
EscrowNotFoundError,
|
|
27
|
+
EscrowExpiredError,
|
|
28
|
+
EscrowAlreadyResolvedError,
|
|
29
|
+
InsufficientCreditsError,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class EscrowManager:
|
|
34
|
+
"""
|
|
35
|
+
Manages escrow lifecycle for inter-agent tasks.
|
|
36
|
+
|
|
37
|
+
The "Reward" OS - agents don't just talk, they bet on outcomes.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self, reputation_engine: Optional[ReputationEngine] = None):
|
|
41
|
+
self.reputation_engine = reputation_engine or ReputationEngine()
|
|
42
|
+
|
|
43
|
+
# In-memory storage (would be database in production)
|
|
44
|
+
self._escrows: dict[str, EscrowReceipt] = {}
|
|
45
|
+
self._agent_credits: dict[str, int] = {} # DID -> credits
|
|
46
|
+
|
|
47
|
+
# Configuration
|
|
48
|
+
self.default_timeout = 3600 # 1 hour
|
|
49
|
+
self.max_credits_per_escrow = 10000
|
|
50
|
+
|
|
51
|
+
async def create_escrow(
|
|
52
|
+
self,
|
|
53
|
+
request: EscrowRequest,
|
|
54
|
+
requester_signature: str,
|
|
55
|
+
) -> EscrowReceipt:
|
|
56
|
+
"""
|
|
57
|
+
Create an escrow for a task.
|
|
58
|
+
|
|
59
|
+
Locks credits from requester until task completion.
|
|
60
|
+
"""
|
|
61
|
+
# Check requester has enough credits
|
|
62
|
+
available = self._agent_credits.get(request.requester_did, 0)
|
|
63
|
+
if available < request.credits:
|
|
64
|
+
raise InsufficientCreditsError(
|
|
65
|
+
request.requester_did,
|
|
66
|
+
required=request.credits,
|
|
67
|
+
available=available,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Generate escrow ID
|
|
71
|
+
escrow_id = f"escrow_{uuid.uuid4().hex[:16]}"
|
|
72
|
+
|
|
73
|
+
# Create receipt
|
|
74
|
+
receipt = EscrowReceipt.from_request(
|
|
75
|
+
escrow_id=escrow_id,
|
|
76
|
+
request=request,
|
|
77
|
+
requester_signature=requester_signature,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Lock credits
|
|
81
|
+
self._agent_credits[request.requester_did] -= request.credits
|
|
82
|
+
|
|
83
|
+
# Sign from Nexus
|
|
84
|
+
receipt.nexus_signature = self._sign_escrow(escrow_id, request)
|
|
85
|
+
|
|
86
|
+
# Store
|
|
87
|
+
self._escrows[escrow_id] = receipt
|
|
88
|
+
|
|
89
|
+
return receipt
|
|
90
|
+
|
|
91
|
+
async def activate_escrow(self, escrow_id: str) -> EscrowReceipt:
|
|
92
|
+
"""Mark escrow as active (task in progress)."""
|
|
93
|
+
receipt = self._get_escrow(escrow_id)
|
|
94
|
+
|
|
95
|
+
if receipt.status != EscrowStatus.PENDING:
|
|
96
|
+
raise EscrowAlreadyResolvedError(escrow_id, receipt.status.value)
|
|
97
|
+
|
|
98
|
+
receipt.status = EscrowStatus.ACTIVE
|
|
99
|
+
receipt.activated_at = datetime.now(timezone.utc)
|
|
100
|
+
|
|
101
|
+
return receipt
|
|
102
|
+
|
|
103
|
+
async def complete_task(
|
|
104
|
+
self,
|
|
105
|
+
escrow_id: str,
|
|
106
|
+
output_hash: str,
|
|
107
|
+
duration_ms: int,
|
|
108
|
+
provider_signature: str,
|
|
109
|
+
) -> EscrowReceipt:
|
|
110
|
+
"""Mark task as complete, awaiting validation."""
|
|
111
|
+
receipt = self._get_escrow(escrow_id)
|
|
112
|
+
|
|
113
|
+
if receipt.status != EscrowStatus.ACTIVE:
|
|
114
|
+
raise EscrowAlreadyResolvedError(escrow_id, receipt.status.value)
|
|
115
|
+
|
|
116
|
+
if receipt.is_expired():
|
|
117
|
+
receipt.status = EscrowStatus.EXPIRED
|
|
118
|
+
raise EscrowExpiredError(escrow_id, str(receipt.expires_at))
|
|
119
|
+
|
|
120
|
+
receipt.status = EscrowStatus.AWAITING_VALIDATION
|
|
121
|
+
receipt.completed_at = datetime.now(timezone.utc)
|
|
122
|
+
|
|
123
|
+
return receipt
|
|
124
|
+
|
|
125
|
+
async def release_escrow(
|
|
126
|
+
self,
|
|
127
|
+
release: EscrowRelease,
|
|
128
|
+
) -> EscrowResolution:
|
|
129
|
+
"""
|
|
130
|
+
Release escrow based on outcome.
|
|
131
|
+
|
|
132
|
+
- success: Credits go to provider, provider reputation +2
|
|
133
|
+
- failure: Credits return to requester, provider reputation -10
|
|
134
|
+
- dispute: Escalate to Arbiter
|
|
135
|
+
"""
|
|
136
|
+
receipt = self._get_escrow(release.escrow_id)
|
|
137
|
+
|
|
138
|
+
if not receipt.is_active():
|
|
139
|
+
raise EscrowAlreadyResolvedError(release.escrow_id, receipt.status.value)
|
|
140
|
+
|
|
141
|
+
request = receipt.request
|
|
142
|
+
credits = request.credits
|
|
143
|
+
|
|
144
|
+
# Determine resolution based on outcome
|
|
145
|
+
if release.outcome == "success":
|
|
146
|
+
# Validate with SCAK if required
|
|
147
|
+
if request.require_scak_validation:
|
|
148
|
+
if not release.scak_validated:
|
|
149
|
+
raise ValueError("SCAK validation required but not performed")
|
|
150
|
+
if not release.scak_passed:
|
|
151
|
+
# SCAK failed - treat as failure
|
|
152
|
+
return await self._resolve_failure(receipt, release)
|
|
153
|
+
|
|
154
|
+
return await self._resolve_success(receipt, release)
|
|
155
|
+
|
|
156
|
+
elif release.outcome == "failure":
|
|
157
|
+
return await self._resolve_failure(receipt, release)
|
|
158
|
+
|
|
159
|
+
else: # dispute
|
|
160
|
+
return await self._resolve_dispute(receipt, release)
|
|
161
|
+
|
|
162
|
+
async def _resolve_success(
|
|
163
|
+
self,
|
|
164
|
+
receipt: EscrowReceipt,
|
|
165
|
+
release: EscrowRelease,
|
|
166
|
+
) -> EscrowResolution:
|
|
167
|
+
"""Resolve escrow as success - credits to provider."""
|
|
168
|
+
request = receipt.request
|
|
169
|
+
|
|
170
|
+
# Transfer credits to provider
|
|
171
|
+
self._agent_credits[request.provider_did] = (
|
|
172
|
+
self._agent_credits.get(request.provider_did, 0) + request.credits
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Update reputation
|
|
176
|
+
self.reputation_engine.record_task_outcome(request.provider_did, "success")
|
|
177
|
+
|
|
178
|
+
# Update receipt
|
|
179
|
+
receipt.status = EscrowStatus.RELEASED
|
|
180
|
+
receipt.resolved_at = datetime.now(timezone.utc)
|
|
181
|
+
|
|
182
|
+
return EscrowResolution(
|
|
183
|
+
escrow_id=receipt.escrow_id,
|
|
184
|
+
final_status=EscrowStatus.RELEASED,
|
|
185
|
+
credits_to_provider=request.credits,
|
|
186
|
+
credits_to_requester=0,
|
|
187
|
+
provider_reputation_change=2,
|
|
188
|
+
requester_reputation_change=0,
|
|
189
|
+
resolution_reason="Task completed successfully",
|
|
190
|
+
resolved_by="automatic",
|
|
191
|
+
nexus_signature=self._sign_resolution(receipt.escrow_id, "success"),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
async def _resolve_failure(
|
|
195
|
+
self,
|
|
196
|
+
receipt: EscrowReceipt,
|
|
197
|
+
release: EscrowRelease,
|
|
198
|
+
) -> EscrowResolution:
|
|
199
|
+
"""Resolve escrow as failure - credits returned to requester."""
|
|
200
|
+
request = receipt.request
|
|
201
|
+
|
|
202
|
+
# Return credits to requester
|
|
203
|
+
self._agent_credits[request.requester_did] = (
|
|
204
|
+
self._agent_credits.get(request.requester_did, 0) + request.credits
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
# Update reputation
|
|
208
|
+
self.reputation_engine.record_task_outcome(request.provider_did, "failure")
|
|
209
|
+
|
|
210
|
+
# Update receipt
|
|
211
|
+
receipt.status = EscrowStatus.REFUNDED
|
|
212
|
+
receipt.resolved_at = datetime.now(timezone.utc)
|
|
213
|
+
|
|
214
|
+
return EscrowResolution(
|
|
215
|
+
escrow_id=receipt.escrow_id,
|
|
216
|
+
final_status=EscrowStatus.REFUNDED,
|
|
217
|
+
credits_to_provider=0,
|
|
218
|
+
credits_to_requester=request.credits,
|
|
219
|
+
provider_reputation_change=-10,
|
|
220
|
+
requester_reputation_change=0,
|
|
221
|
+
resolution_reason="Task failed",
|
|
222
|
+
resolved_by="automatic",
|
|
223
|
+
nexus_signature=self._sign_resolution(receipt.escrow_id, "failure"),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
async def _resolve_dispute(
|
|
227
|
+
self,
|
|
228
|
+
receipt: EscrowReceipt,
|
|
229
|
+
release: EscrowRelease,
|
|
230
|
+
) -> EscrowResolution:
|
|
231
|
+
"""Mark escrow as disputed - to be resolved by Arbiter."""
|
|
232
|
+
receipt.status = EscrowStatus.DISPUTED
|
|
233
|
+
|
|
234
|
+
# Credits remain locked until Arbiter decides
|
|
235
|
+
return EscrowResolution(
|
|
236
|
+
escrow_id=receipt.escrow_id,
|
|
237
|
+
final_status=EscrowStatus.DISPUTED,
|
|
238
|
+
credits_to_provider=0,
|
|
239
|
+
credits_to_requester=0,
|
|
240
|
+
provider_reputation_change=0,
|
|
241
|
+
requester_reputation_change=0,
|
|
242
|
+
resolution_reason=f"Dispute raised: {release.dispute_reason}",
|
|
243
|
+
resolved_by="arbiter",
|
|
244
|
+
nexus_signature=self._sign_resolution(receipt.escrow_id, "dispute"),
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
async def expire_escrow(self, escrow_id: str) -> EscrowResolution:
|
|
248
|
+
"""Handle escrow expiration - credits returned to requester."""
|
|
249
|
+
receipt = self._get_escrow(escrow_id)
|
|
250
|
+
request = receipt.request
|
|
251
|
+
|
|
252
|
+
# Return credits
|
|
253
|
+
self._agent_credits[request.requester_did] = (
|
|
254
|
+
self._agent_credits.get(request.requester_did, 0) + request.credits
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
# Small reputation penalty for provider
|
|
258
|
+
self.reputation_engine.record_task_outcome(request.provider_did, "failure")
|
|
259
|
+
|
|
260
|
+
receipt.status = EscrowStatus.EXPIRED
|
|
261
|
+
receipt.resolved_at = datetime.now(timezone.utc)
|
|
262
|
+
|
|
263
|
+
return EscrowResolution(
|
|
264
|
+
escrow_id=escrow_id,
|
|
265
|
+
final_status=EscrowStatus.EXPIRED,
|
|
266
|
+
credits_to_provider=0,
|
|
267
|
+
credits_to_requester=request.credits,
|
|
268
|
+
provider_reputation_change=-5,
|
|
269
|
+
requester_reputation_change=0,
|
|
270
|
+
resolution_reason="Escrow expired without completion",
|
|
271
|
+
resolved_by="timeout",
|
|
272
|
+
nexus_signature=self._sign_resolution(escrow_id, "expired"),
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
def get_escrow(self, escrow_id: str) -> EscrowReceipt:
|
|
276
|
+
"""Get escrow by ID (public method)."""
|
|
277
|
+
return self._get_escrow(escrow_id)
|
|
278
|
+
|
|
279
|
+
def get_agent_credits(self, agent_did: str) -> int:
|
|
280
|
+
"""Get credit balance for an agent."""
|
|
281
|
+
return self._agent_credits.get(agent_did, 0)
|
|
282
|
+
|
|
283
|
+
def add_credits(self, agent_did: str, amount: int) -> int:
|
|
284
|
+
"""Add credits to an agent's balance."""
|
|
285
|
+
self._agent_credits[agent_did] = self._agent_credits.get(agent_did, 0) + amount
|
|
286
|
+
return self._agent_credits[agent_did]
|
|
287
|
+
|
|
288
|
+
def list_escrows(
|
|
289
|
+
self,
|
|
290
|
+
agent_did: Optional[str] = None,
|
|
291
|
+
status: Optional[EscrowStatus] = None,
|
|
292
|
+
) -> list[EscrowReceipt]:
|
|
293
|
+
"""List escrows with optional filtering."""
|
|
294
|
+
results = list(self._escrows.values())
|
|
295
|
+
|
|
296
|
+
if agent_did:
|
|
297
|
+
results = [
|
|
298
|
+
e for e in results
|
|
299
|
+
if e.request.requester_did == agent_did or e.request.provider_did == agent_did
|
|
300
|
+
]
|
|
301
|
+
|
|
302
|
+
if status:
|
|
303
|
+
results = [e for e in results if e.status == status]
|
|
304
|
+
|
|
305
|
+
return results
|
|
306
|
+
|
|
307
|
+
def _get_escrow(self, escrow_id: str) -> EscrowReceipt:
|
|
308
|
+
"""Get escrow by ID or raise error."""
|
|
309
|
+
if escrow_id not in self._escrows:
|
|
310
|
+
raise EscrowNotFoundError(escrow_id)
|
|
311
|
+
return self._escrows[escrow_id]
|
|
312
|
+
|
|
313
|
+
def _sign_escrow(self, escrow_id: str, request: EscrowRequest) -> str:
|
|
314
|
+
"""Generate Nexus signature for escrow."""
|
|
315
|
+
data = f"{escrow_id}:{request.task_hash}:{request.credits}"
|
|
316
|
+
return f"nexus_escrow_{hashlib.sha256(data.encode()).hexdigest()[:32]}"
|
|
317
|
+
|
|
318
|
+
def _sign_resolution(self, escrow_id: str, outcome: str) -> str:
|
|
319
|
+
"""Generate Nexus signature for resolution."""
|
|
320
|
+
data = f"{escrow_id}:{outcome}:{datetime.now(timezone.utc).isoformat()}"
|
|
321
|
+
return f"nexus_res_{hashlib.sha256(data.encode()).hexdigest()[:32]}"
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
class ProofOfOutcome:
|
|
325
|
+
"""
|
|
326
|
+
High-level API for the Proof-of-Outcome mechanism.
|
|
327
|
+
|
|
328
|
+
Wraps EscrowManager with SCAK validation integration.
|
|
329
|
+
"""
|
|
330
|
+
|
|
331
|
+
def __init__(
|
|
332
|
+
self,
|
|
333
|
+
escrow_manager: Optional[EscrowManager] = None,
|
|
334
|
+
scak_validator: Optional[any] = None, # Would be SCAK validator
|
|
335
|
+
):
|
|
336
|
+
self.escrow_manager = escrow_manager or EscrowManager()
|
|
337
|
+
self.scak_validator = scak_validator
|
|
338
|
+
|
|
339
|
+
async def create_escrow(
|
|
340
|
+
self,
|
|
341
|
+
requester_did: str,
|
|
342
|
+
provider_did: str,
|
|
343
|
+
task_hash: str,
|
|
344
|
+
credits: int,
|
|
345
|
+
timeout_seconds: int = 3600,
|
|
346
|
+
require_scak: bool = True,
|
|
347
|
+
drift_threshold: float = 0.15,
|
|
348
|
+
) -> EscrowReceipt:
|
|
349
|
+
"""Create an escrow for a task."""
|
|
350
|
+
request = EscrowRequest(
|
|
351
|
+
requester_did=requester_did,
|
|
352
|
+
provider_did=provider_did,
|
|
353
|
+
task_hash=task_hash,
|
|
354
|
+
credits=credits,
|
|
355
|
+
timeout_seconds=timeout_seconds,
|
|
356
|
+
require_scak_validation=require_scak,
|
|
357
|
+
scak_drift_threshold=drift_threshold,
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# TODO: Generate actual signature
|
|
361
|
+
signature = f"sig_{requester_did}_{task_hash[:8]}"
|
|
362
|
+
|
|
363
|
+
return await self.escrow_manager.create_escrow(request, signature)
|
|
364
|
+
|
|
365
|
+
async def validate_outcome(
|
|
366
|
+
self,
|
|
367
|
+
escrow_id: str,
|
|
368
|
+
flight_recorder_log: bytes,
|
|
369
|
+
claimed_outcome: Literal["success", "failure"],
|
|
370
|
+
) -> tuple[bool, Optional[float]]:
|
|
371
|
+
"""
|
|
372
|
+
Validate task outcome using SCAK.
|
|
373
|
+
|
|
374
|
+
Replays the flight recorder log against the Control Plane
|
|
375
|
+
to deterministically verify the claimed outcome.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Tuple of (passed, drift_score)
|
|
379
|
+
"""
|
|
380
|
+
receipt = self.escrow_manager.get_escrow(escrow_id)
|
|
381
|
+
|
|
382
|
+
if self.scak_validator is None:
|
|
383
|
+
# No SCAK validator - assume success
|
|
384
|
+
return True, 0.0
|
|
385
|
+
|
|
386
|
+
# Validate with SCAK
|
|
387
|
+
# drift_score = await self.scak_validator.validate(flight_recorder_log)
|
|
388
|
+
# For now, simulate
|
|
389
|
+
drift_score = 0.05
|
|
390
|
+
|
|
391
|
+
threshold = receipt.request.scak_drift_threshold
|
|
392
|
+
passed = drift_score <= threshold
|
|
393
|
+
|
|
394
|
+
return passed, drift_score
|
|
395
|
+
|
|
396
|
+
async def release_escrow(
|
|
397
|
+
self,
|
|
398
|
+
escrow_id: str,
|
|
399
|
+
outcome: Literal["success", "failure", "dispute"],
|
|
400
|
+
output_hash: Optional[str] = None,
|
|
401
|
+
duration_ms: Optional[int] = None,
|
|
402
|
+
scak_drift_score: Optional[float] = None,
|
|
403
|
+
dispute_reason: Optional[str] = None,
|
|
404
|
+
) -> EscrowResolution:
|
|
405
|
+
"""
|
|
406
|
+
Release escrow based on outcome.
|
|
407
|
+
|
|
408
|
+
- success: Credits go to provider, provider reputation +2
|
|
409
|
+
- failure: Credits return to requester, provider reputation -10
|
|
410
|
+
- dispute: Escalate to Arbiter
|
|
411
|
+
"""
|
|
412
|
+
receipt = self.escrow_manager.get_escrow(escrow_id)
|
|
413
|
+
|
|
414
|
+
# Determine if SCAK passed
|
|
415
|
+
scak_passed = None
|
|
416
|
+
if scak_drift_score is not None:
|
|
417
|
+
scak_passed = scak_drift_score <= receipt.request.scak_drift_threshold
|
|
418
|
+
|
|
419
|
+
release = EscrowRelease(
|
|
420
|
+
escrow_id=escrow_id,
|
|
421
|
+
outcome=outcome,
|
|
422
|
+
output_hash=output_hash,
|
|
423
|
+
duration_ms=duration_ms,
|
|
424
|
+
scak_validated=scak_drift_score is not None,
|
|
425
|
+
scak_drift_score=scak_drift_score,
|
|
426
|
+
scak_passed=scak_passed,
|
|
427
|
+
dispute_reason=dispute_reason,
|
|
428
|
+
)
|
|
429
|
+
|
|
430
|
+
return await self.escrow_manager.release_escrow(release)
|