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
agent_kernel/patcher.py
ADDED
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
# Copyright (c) Microsoft Corporation.
|
|
2
|
+
# Licensed under the MIT License.
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Agent patcher that applies corrections to agents.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
import uuid
|
|
10
|
+
from typing import Dict, Any, Optional, List
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
|
|
13
|
+
from .models import (
|
|
14
|
+
FailureAnalysis, SimulationResult, CorrectionPatch, AgentState,
|
|
15
|
+
DiagnosisJSON, ShadowAgentResult, PatchStrategy, CognitiveGlitch,
|
|
16
|
+
AgentFailure
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
logger = logging.getLogger(__name__)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class AgentPatcher:
|
|
23
|
+
"""
|
|
24
|
+
Patches agents to prevent future failures.
|
|
25
|
+
|
|
26
|
+
This is "The Patcher" (The Optimizer) - applies fixes permanently.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self.patches: Dict[str, CorrectionPatch] = {}
|
|
31
|
+
self.agent_states: Dict[str, AgentState] = {}
|
|
32
|
+
self.system_prompts: Dict[str, str] = {} # Store system prompts
|
|
33
|
+
self.rag_memories: List[Dict[str, Any]] = [] # RAG memory store
|
|
34
|
+
|
|
35
|
+
def create_patch(
|
|
36
|
+
self,
|
|
37
|
+
agent_id: str,
|
|
38
|
+
analysis: FailureAnalysis,
|
|
39
|
+
simulation: SimulationResult,
|
|
40
|
+
diagnosis: Optional[DiagnosisJSON] = None,
|
|
41
|
+
shadow_result: Optional[ShadowAgentResult] = None
|
|
42
|
+
) -> CorrectionPatch:
|
|
43
|
+
"""
|
|
44
|
+
Create a correction patch for an agent.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
agent_id: ID of the agent to patch
|
|
48
|
+
analysis: Failure analysis
|
|
49
|
+
simulation: Successful simulation result
|
|
50
|
+
diagnosis: Cognitive glitch diagnosis if available
|
|
51
|
+
shadow_result: Shadow agent verification result
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
CorrectionPatch object
|
|
55
|
+
"""
|
|
56
|
+
logger.info(f"Creating patch for agent {agent_id}")
|
|
57
|
+
|
|
58
|
+
# Generate patch ID
|
|
59
|
+
patch_id = f"patch-{uuid.uuid4().hex[:8]}"
|
|
60
|
+
|
|
61
|
+
# Determine patch strategy (easy vs hard fix)
|
|
62
|
+
strategy = self._determine_patch_strategy(analysis, diagnosis)
|
|
63
|
+
|
|
64
|
+
# Determine patch type based on failure
|
|
65
|
+
patch_type = self._determine_patch_type(analysis, strategy)
|
|
66
|
+
|
|
67
|
+
# Generate patch content based on strategy
|
|
68
|
+
patch_content = self._generate_patch_content(
|
|
69
|
+
analysis, simulation, strategy, diagnosis, shadow_result
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
patch = CorrectionPatch(
|
|
73
|
+
patch_id=patch_id,
|
|
74
|
+
agent_id=agent_id,
|
|
75
|
+
failure_analysis=analysis,
|
|
76
|
+
simulation_result=simulation,
|
|
77
|
+
patch_type=patch_type,
|
|
78
|
+
patch_content=patch_content,
|
|
79
|
+
applied=False,
|
|
80
|
+
rollback_available=True,
|
|
81
|
+
diagnosis=diagnosis,
|
|
82
|
+
shadow_result=shadow_result
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
self.patches[patch_id] = patch
|
|
86
|
+
logger.info(f"Created {patch_type} patch {patch_id} with strategy {strategy}")
|
|
87
|
+
|
|
88
|
+
return patch
|
|
89
|
+
|
|
90
|
+
def apply_patch(self, patch: CorrectionPatch) -> bool:
|
|
91
|
+
"""
|
|
92
|
+
Apply a correction patch to an agent.
|
|
93
|
+
|
|
94
|
+
This is "The Patcher" in action - applying the fix permanently.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
patch: The patch to apply
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
True if patch was applied successfully
|
|
101
|
+
"""
|
|
102
|
+
logger.info(f"Applying patch {patch.patch_id} to agent {patch.agent_id}")
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
# Apply patch based on type
|
|
106
|
+
if patch.patch_type in [PatchStrategy.SYSTEM_PROMPT.value, "system_prompt"]:
|
|
107
|
+
self._apply_system_prompt_patch(patch)
|
|
108
|
+
elif patch.patch_type in [PatchStrategy.RAG_MEMORY.value, "rag_memory"]:
|
|
109
|
+
self._apply_rag_memory_patch(patch)
|
|
110
|
+
elif patch.patch_type == "code":
|
|
111
|
+
self._apply_code_patch(patch)
|
|
112
|
+
elif patch.patch_type == "config":
|
|
113
|
+
self._apply_config_patch(patch)
|
|
114
|
+
else:
|
|
115
|
+
logger.warning(f"Unknown patch type: {patch.patch_type}, applying generically")
|
|
116
|
+
|
|
117
|
+
# Mark as applied
|
|
118
|
+
patch.applied = True
|
|
119
|
+
patch.applied_at = datetime.utcnow()
|
|
120
|
+
|
|
121
|
+
# Update agent state
|
|
122
|
+
self._update_agent_state(patch.agent_id, patch)
|
|
123
|
+
|
|
124
|
+
logger.info(f"Successfully applied patch {patch.patch_id}")
|
|
125
|
+
return True
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error(f"Failed to apply patch {patch.patch_id}: {e}")
|
|
129
|
+
return False
|
|
130
|
+
|
|
131
|
+
def _apply_system_prompt_patch(self, patch: CorrectionPatch):
|
|
132
|
+
"""
|
|
133
|
+
Apply an easy fix: Update the system_prompt with a new rule.
|
|
134
|
+
|
|
135
|
+
Example: "Always check schema before querying"
|
|
136
|
+
"""
|
|
137
|
+
agent_id = patch.agent_id
|
|
138
|
+
rule = patch.patch_content.get("rule", "")
|
|
139
|
+
|
|
140
|
+
# Get or create system prompt
|
|
141
|
+
if agent_id not in self.system_prompts:
|
|
142
|
+
self.system_prompts[agent_id] = "You are a helpful assistant."
|
|
143
|
+
|
|
144
|
+
# Append the new rule
|
|
145
|
+
self.system_prompts[agent_id] += f"\n\nIMPORTANT RULE: {rule}"
|
|
146
|
+
|
|
147
|
+
logger.info(f"Updated system prompt for agent {agent_id} with new rule")
|
|
148
|
+
|
|
149
|
+
def _apply_rag_memory_patch(self, patch: CorrectionPatch):
|
|
150
|
+
"""
|
|
151
|
+
Apply a hard fix: Inject a "Memory" into the vector store.
|
|
152
|
+
|
|
153
|
+
Example: "In 2025, user asked X, and we failed. The correct logic is Y."
|
|
154
|
+
"""
|
|
155
|
+
memory = {
|
|
156
|
+
"agent_id": patch.agent_id,
|
|
157
|
+
"timestamp": datetime.utcnow(),
|
|
158
|
+
"failure_context": patch.patch_content.get("failure_context", ""),
|
|
159
|
+
"correct_logic": patch.patch_content.get("correct_logic", ""),
|
|
160
|
+
"patch_id": patch.patch_id,
|
|
161
|
+
"embeddings_ready": False # Would compute embeddings in real system
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
self.rag_memories.append(memory)
|
|
165
|
+
|
|
166
|
+
logger.info(f"Injected RAG memory for agent {patch.agent_id}: {memory['correct_logic'][:50]}...")
|
|
167
|
+
|
|
168
|
+
def _apply_code_patch(self, patch: CorrectionPatch):
|
|
169
|
+
"""Apply code changes patch."""
|
|
170
|
+
# In real system, would modify actual code
|
|
171
|
+
logger.info(f"Code patch applied (simulated) for agent {patch.agent_id}")
|
|
172
|
+
|
|
173
|
+
def _apply_config_patch(self, patch: CorrectionPatch):
|
|
174
|
+
"""Apply configuration changes patch."""
|
|
175
|
+
# In real system, would update configuration
|
|
176
|
+
logger.info(f"Config patch applied (simulated) for agent {patch.agent_id}")
|
|
177
|
+
|
|
178
|
+
def rollback_patch(self, patch_id: str) -> bool:
|
|
179
|
+
"""
|
|
180
|
+
Rollback a previously applied patch.
|
|
181
|
+
|
|
182
|
+
Args:
|
|
183
|
+
patch_id: ID of the patch to rollback
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
True if rollback was successful
|
|
187
|
+
"""
|
|
188
|
+
if patch_id not in self.patches:
|
|
189
|
+
logger.error(f"Patch {patch_id} not found")
|
|
190
|
+
return False
|
|
191
|
+
|
|
192
|
+
patch = self.patches[patch_id]
|
|
193
|
+
|
|
194
|
+
if not patch.applied:
|
|
195
|
+
logger.warning(f"Patch {patch_id} is not applied, cannot rollback")
|
|
196
|
+
return False
|
|
197
|
+
|
|
198
|
+
if not patch.rollback_available:
|
|
199
|
+
logger.error(f"Patch {patch_id} does not support rollback")
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
logger.info(f"Rolling back patch {patch_id}")
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
# Rollback based on patch type
|
|
206
|
+
if patch.patch_type in [PatchStrategy.SYSTEM_PROMPT.value, "system_prompt"]:
|
|
207
|
+
self._rollback_system_prompt(patch)
|
|
208
|
+
elif patch.patch_type in [PatchStrategy.RAG_MEMORY.value, "rag_memory"]:
|
|
209
|
+
self._rollback_rag_memory(patch)
|
|
210
|
+
|
|
211
|
+
# Mark as not applied
|
|
212
|
+
patch.applied = False
|
|
213
|
+
patch.applied_at = None
|
|
214
|
+
|
|
215
|
+
# Update agent state
|
|
216
|
+
if patch.agent_id in self.agent_states:
|
|
217
|
+
state = self.agent_states[patch.agent_id]
|
|
218
|
+
if patch_id in state.patches_applied:
|
|
219
|
+
state.patches_applied.remove(patch_id)
|
|
220
|
+
|
|
221
|
+
logger.info(f"Successfully rolled back patch {patch_id}")
|
|
222
|
+
return True
|
|
223
|
+
|
|
224
|
+
except Exception as e:
|
|
225
|
+
logger.error(f"Failed to rollback patch {patch_id}: {e}")
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
def _rollback_system_prompt(self, patch: CorrectionPatch):
|
|
229
|
+
"""Rollback system prompt changes."""
|
|
230
|
+
# In real system, would restore previous version
|
|
231
|
+
logger.info(f"Rolled back system prompt for agent {patch.agent_id}")
|
|
232
|
+
|
|
233
|
+
def _rollback_rag_memory(self, patch: CorrectionPatch):
|
|
234
|
+
"""Rollback RAG memory injection."""
|
|
235
|
+
# Remove the memory from RAG store
|
|
236
|
+
self.rag_memories = [
|
|
237
|
+
m for m in self.rag_memories if m.get("patch_id") != patch.patch_id
|
|
238
|
+
]
|
|
239
|
+
logger.info(f"Removed RAG memory for patch {patch.patch_id}")
|
|
240
|
+
|
|
241
|
+
def get_agent_state(self, agent_id: str) -> AgentState:
|
|
242
|
+
"""Get the current state of an agent."""
|
|
243
|
+
if agent_id not in self.agent_states:
|
|
244
|
+
self.agent_states[agent_id] = AgentState(
|
|
245
|
+
agent_id=agent_id,
|
|
246
|
+
status="unknown"
|
|
247
|
+
)
|
|
248
|
+
return self.agent_states[agent_id]
|
|
249
|
+
|
|
250
|
+
def _determine_patch_strategy(
|
|
251
|
+
self,
|
|
252
|
+
analysis: FailureAnalysis,
|
|
253
|
+
diagnosis: Optional[DiagnosisJSON]
|
|
254
|
+
) -> PatchStrategy:
|
|
255
|
+
"""
|
|
256
|
+
Determine patch strategy based on the problem statement requirements:
|
|
257
|
+
|
|
258
|
+
- Tool Misuse → Schema Injection (update tool definition in prompt)
|
|
259
|
+
- Hallucination → RAG Patch (add negative constraint to context)
|
|
260
|
+
- Policy Violation → Constitutional Update (prepend refusal rule to system prompt)
|
|
261
|
+
"""
|
|
262
|
+
if not diagnosis:
|
|
263
|
+
return PatchStrategy.CODE_CHANGE
|
|
264
|
+
|
|
265
|
+
# Tool Misuse: Schema Injection - update tool definition in the prompt
|
|
266
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.TOOL_MISUSE:
|
|
267
|
+
return PatchStrategy.SYSTEM_PROMPT
|
|
268
|
+
|
|
269
|
+
# Policy Violation: Constitutional Update - prepend refusal rule to system prompt
|
|
270
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.POLICY_VIOLATION:
|
|
271
|
+
return PatchStrategy.SYSTEM_PROMPT
|
|
272
|
+
|
|
273
|
+
# Hallucination: RAG Patch - add negative constraint to memory
|
|
274
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.HALLUCINATION:
|
|
275
|
+
return PatchStrategy.RAG_MEMORY
|
|
276
|
+
|
|
277
|
+
# Easy fixes: Simple cognitive glitches that can be addressed with rules
|
|
278
|
+
if diagnosis.cognitive_glitch in [
|
|
279
|
+
CognitiveGlitch.PERMISSION_ERROR,
|
|
280
|
+
CognitiveGlitch.CONTEXT_GAP
|
|
281
|
+
]:
|
|
282
|
+
if diagnosis.confidence > 0.8:
|
|
283
|
+
return PatchStrategy.SYSTEM_PROMPT
|
|
284
|
+
|
|
285
|
+
# Hard fixes: Complex patterns requiring historical context
|
|
286
|
+
if diagnosis.cognitive_glitch in [
|
|
287
|
+
CognitiveGlitch.SCHEMA_MISMATCH,
|
|
288
|
+
CognitiveGlitch.LOGIC_ERROR
|
|
289
|
+
]:
|
|
290
|
+
return PatchStrategy.RAG_MEMORY
|
|
291
|
+
|
|
292
|
+
return PatchStrategy.CODE_CHANGE
|
|
293
|
+
|
|
294
|
+
def _determine_patch_type(
|
|
295
|
+
self,
|
|
296
|
+
analysis: FailureAnalysis,
|
|
297
|
+
strategy: PatchStrategy
|
|
298
|
+
) -> str:
|
|
299
|
+
"""Determine the type of patch needed."""
|
|
300
|
+
# Use strategy as primary determinant
|
|
301
|
+
if strategy == PatchStrategy.SYSTEM_PROMPT:
|
|
302
|
+
return "system_prompt"
|
|
303
|
+
elif strategy == PatchStrategy.RAG_MEMORY:
|
|
304
|
+
return "rag_memory"
|
|
305
|
+
elif strategy == PatchStrategy.CONFIG_UPDATE:
|
|
306
|
+
return "config"
|
|
307
|
+
elif strategy == PatchStrategy.RULE_UPDATE:
|
|
308
|
+
return "rule"
|
|
309
|
+
|
|
310
|
+
# Fall back to failure type analysis
|
|
311
|
+
failure_type = analysis.failure.failure_type.value
|
|
312
|
+
|
|
313
|
+
if failure_type == "blocked_by_control_plane":
|
|
314
|
+
return "code" # Code changes to add permission checks
|
|
315
|
+
elif failure_type == "timeout":
|
|
316
|
+
return "config" # Configuration changes for timeouts
|
|
317
|
+
elif failure_type == "invalid_action":
|
|
318
|
+
return "rule" # Rule changes to validate actions
|
|
319
|
+
else:
|
|
320
|
+
return "code" # Default to code patches
|
|
321
|
+
|
|
322
|
+
def _generate_patch_content(
|
|
323
|
+
self,
|
|
324
|
+
analysis: FailureAnalysis,
|
|
325
|
+
simulation: SimulationResult,
|
|
326
|
+
strategy: PatchStrategy,
|
|
327
|
+
diagnosis: Optional[DiagnosisJSON] = None,
|
|
328
|
+
shadow_result: Optional[ShadowAgentResult] = None
|
|
329
|
+
) -> Dict[str, Any]:
|
|
330
|
+
"""Generate the actual patch content based on strategy."""
|
|
331
|
+
|
|
332
|
+
# EASY FIX: System Prompt Update
|
|
333
|
+
if strategy == PatchStrategy.SYSTEM_PROMPT:
|
|
334
|
+
rule = self._generate_system_prompt_rule(diagnosis, analysis)
|
|
335
|
+
return {
|
|
336
|
+
"type": "system_prompt_update",
|
|
337
|
+
"rule": rule,
|
|
338
|
+
"diagnosis": diagnosis.cognitive_glitch.value if diagnosis else "unknown",
|
|
339
|
+
"hint_applied": diagnosis.hint if diagnosis else ""
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
# HARD FIX: RAG Memory Injection
|
|
343
|
+
elif strategy == PatchStrategy.RAG_MEMORY:
|
|
344
|
+
return self._generate_rag_memory_content(diagnosis, analysis, shadow_result)
|
|
345
|
+
|
|
346
|
+
# CODE CHANGE
|
|
347
|
+
elif strategy == PatchStrategy.CODE_CHANGE:
|
|
348
|
+
return self._generate_code_change_content(analysis, simulation)
|
|
349
|
+
|
|
350
|
+
# CONFIG UPDATE
|
|
351
|
+
elif strategy == PatchStrategy.CONFIG_UPDATE:
|
|
352
|
+
return self._generate_config_content(analysis, simulation)
|
|
353
|
+
|
|
354
|
+
# Default fallback
|
|
355
|
+
return {
|
|
356
|
+
"type": "generic_fix",
|
|
357
|
+
"suggested_fixes": analysis.suggested_fixes,
|
|
358
|
+
"simulation_steps": simulation.alternative_path
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
def _generate_system_prompt_rule(
|
|
362
|
+
self,
|
|
363
|
+
diagnosis: Optional[DiagnosisJSON],
|
|
364
|
+
analysis: FailureAnalysis
|
|
365
|
+
) -> str:
|
|
366
|
+
"""
|
|
367
|
+
Generate a rule to add to system prompt.
|
|
368
|
+
|
|
369
|
+
Implements:
|
|
370
|
+
- Tool Misuse → Schema Injection (stricter tool definition)
|
|
371
|
+
- Policy Violation → Constitutional Update (refusal rule)
|
|
372
|
+
"""
|
|
373
|
+
if not diagnosis:
|
|
374
|
+
return f"Always validate before: {analysis.suggested_fixes[0] if analysis.suggested_fixes else 'executing actions'}"
|
|
375
|
+
|
|
376
|
+
# Tool Misuse: Schema Injection - Make tool definition stricter
|
|
377
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.TOOL_MISUSE:
|
|
378
|
+
failure = analysis.failure
|
|
379
|
+
# Extract specific tool information from the failure
|
|
380
|
+
tool_info = ""
|
|
381
|
+
if failure.failure_trace and failure.failure_trace.failed_action:
|
|
382
|
+
action_name = failure.failure_trace.failed_action.get("action", "tool")
|
|
383
|
+
tool_info = f" for {action_name}"
|
|
384
|
+
return f"SCHEMA INJECTION: Tool definitions{tool_info} require strict parameter type checking. Always verify parameter types match the schema exactly (e.g., UUID format for id parameters, not names or strings)."
|
|
385
|
+
|
|
386
|
+
# Policy Violation: Constitutional Update - Prepend refusal rule
|
|
387
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.POLICY_VIOLATION:
|
|
388
|
+
# Determine the specific policy domain from the failure
|
|
389
|
+
failure = analysis.failure
|
|
390
|
+
domain = "restricted topics"
|
|
391
|
+
|
|
392
|
+
# Check error message first
|
|
393
|
+
error_lower = failure.error_message.lower()
|
|
394
|
+
if any(kw in error_lower for kw in ["medical", "health"]):
|
|
395
|
+
domain = "medical issues"
|
|
396
|
+
elif any(kw in error_lower for kw in ["legal", "law"]):
|
|
397
|
+
domain = "legal matters"
|
|
398
|
+
elif any(kw in error_lower for kw in ["investment", "financial"]):
|
|
399
|
+
domain = "investment advice"
|
|
400
|
+
# Then check failure trace if available
|
|
401
|
+
elif failure.failure_trace and failure.failure_trace.user_prompt:
|
|
402
|
+
prompt_lower = failure.failure_trace.user_prompt.lower()
|
|
403
|
+
if any(kw in prompt_lower for kw in ["medical", "health", "diagnosis", "treatment", "medicine"]):
|
|
404
|
+
domain = "medical issues"
|
|
405
|
+
elif any(kw in prompt_lower for kw in ["legal", "law", "attorney", "sue", "lawsuit"]):
|
|
406
|
+
domain = "legal matters"
|
|
407
|
+
elif any(kw in prompt_lower for kw in ["investment", "financial", "stock", "invest"]):
|
|
408
|
+
domain = "investment advice"
|
|
409
|
+
|
|
410
|
+
return f"CONSTITUTIONAL REFUSAL RULE: You must refuse to provide advice on {domain}. Politely decline and explain that you are not qualified to advise on such matters."
|
|
411
|
+
|
|
412
|
+
# Convert hint into a permanent rule for other glitches
|
|
413
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.PERMISSION_ERROR:
|
|
414
|
+
return "Always check permissions before attempting any action. Use validate_permissions() first."
|
|
415
|
+
elif diagnosis.cognitive_glitch == CognitiveGlitch.CONTEXT_GAP:
|
|
416
|
+
return "Before executing actions, ensure you have: 1) Complete schema information, 2) Permission requirements, 3) Clear action scope."
|
|
417
|
+
elif diagnosis.cognitive_glitch == CognitiveGlitch.HALLUCINATION:
|
|
418
|
+
return "Always verify entity names against the provided schema before using them. Never invent or assume entity names."
|
|
419
|
+
elif diagnosis.cognitive_glitch == CognitiveGlitch.SCHEMA_MISMATCH:
|
|
420
|
+
return "Verify all table and column names against the schema before use. Do not assume schema structure."
|
|
421
|
+
elif diagnosis.cognitive_glitch == CognitiveGlitch.LOGIC_ERROR:
|
|
422
|
+
return "When interpreting ambiguous terms like 'recent', 'delete', 'modify', ask for clarification before proceeding."
|
|
423
|
+
|
|
424
|
+
return "Proceed with caution and verify all assumptions before actions."
|
|
425
|
+
|
|
426
|
+
def _generate_rag_memory_content(
|
|
427
|
+
self,
|
|
428
|
+
diagnosis: Optional[DiagnosisJSON],
|
|
429
|
+
analysis: FailureAnalysis,
|
|
430
|
+
shadow_result: Optional[ShadowAgentResult]
|
|
431
|
+
) -> Dict[str, Any]:
|
|
432
|
+
"""
|
|
433
|
+
Generate RAG memory content (Hard Fix).
|
|
434
|
+
|
|
435
|
+
For Hallucination: Add negative constraint to the context.
|
|
436
|
+
Example: "Project_Alpha is deprecated" or "Entity X does not exist"
|
|
437
|
+
"""
|
|
438
|
+
failure = analysis.failure
|
|
439
|
+
|
|
440
|
+
# Create a memory entry
|
|
441
|
+
memory_text = f"In {failure.timestamp.year}, "
|
|
442
|
+
negative_constraint = None
|
|
443
|
+
|
|
444
|
+
if failure.failure_trace:
|
|
445
|
+
memory_text += f"user asked: '{failure.failure_trace.user_prompt}', "
|
|
446
|
+
memory_text += f"and we failed with: {failure.error_message}. "
|
|
447
|
+
|
|
448
|
+
if diagnosis:
|
|
449
|
+
memory_text += f"The problem was {diagnosis.cognitive_glitch.value}: {diagnosis.deep_problem}. "
|
|
450
|
+
|
|
451
|
+
# For hallucinations, extract the hallucinated entity and create negative constraint
|
|
452
|
+
if diagnosis.cognitive_glitch == CognitiveGlitch.HALLUCINATION:
|
|
453
|
+
# Extract hallucinated entity from error message or failed action
|
|
454
|
+
hallucinated_entity = self._extract_hallucinated_entity(failure)
|
|
455
|
+
if hallucinated_entity:
|
|
456
|
+
negative_constraint = f"{hallucinated_entity} does not exist and is deprecated. Do not reference it."
|
|
457
|
+
memory_text += f"NEGATIVE CONSTRAINT: {negative_constraint} "
|
|
458
|
+
|
|
459
|
+
if shadow_result and shadow_result.verified:
|
|
460
|
+
memory_text += f"The correct approach is: {shadow_result.output}. "
|
|
461
|
+
if shadow_result.action_taken:
|
|
462
|
+
memory_text += f"Correct action: {shadow_result.action_taken}"
|
|
463
|
+
else:
|
|
464
|
+
memory_text += f"we encountered: {failure.error_message}. "
|
|
465
|
+
memory_text += f"The correct approach is: {analysis.suggested_fixes[0] if analysis.suggested_fixes else 'validate before action'}"
|
|
466
|
+
|
|
467
|
+
return {
|
|
468
|
+
"type": "rag_memory",
|
|
469
|
+
"failure_context": memory_text,
|
|
470
|
+
"correct_logic": shadow_result.output if shadow_result else analysis.suggested_fixes[0] if analysis.suggested_fixes else "Unknown",
|
|
471
|
+
"cognitive_glitch": diagnosis.cognitive_glitch.value if diagnosis else "unknown",
|
|
472
|
+
"negative_constraint": negative_constraint, # New field for hallucinations
|
|
473
|
+
"timestamp": datetime.utcnow().isoformat(),
|
|
474
|
+
"verified_by_shadow": shadow_result.verified if shadow_result else False
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
def _extract_hallucinated_entity(self, failure: AgentFailure) -> Optional[str]:
|
|
478
|
+
"""Extract the hallucinated entity name from failure context."""
|
|
479
|
+
error_lower = failure.error_message.lower()
|
|
480
|
+
|
|
481
|
+
# Common patterns for hallucinations
|
|
482
|
+
# "Project_Alpha does not exist" -> "Project_Alpha"
|
|
483
|
+
# "Table 'recent_users' not found" -> "recent_users"
|
|
484
|
+
# "Unknown entity: XYZ" -> "XYZ"
|
|
485
|
+
|
|
486
|
+
import re
|
|
487
|
+
|
|
488
|
+
# Pattern: 'entity_name' or "entity_name" in error message
|
|
489
|
+
quoted_match = re.search(r"['\"]([^'\"]+)['\"]", failure.error_message)
|
|
490
|
+
if quoted_match:
|
|
491
|
+
return quoted_match.group(1)
|
|
492
|
+
|
|
493
|
+
# Pattern: Entity_Name or Project_Alpha (CamelCase or Snake_Case with underscores)
|
|
494
|
+
if failure.failure_trace and failure.failure_trace.failed_action:
|
|
495
|
+
action_str = str(failure.failure_trace.failed_action)
|
|
496
|
+
# Look for CamelCase or underscore-separated capitalized words
|
|
497
|
+
camel_match = re.search(r'\b([A-Z][a-z]*(?:_[A-Z][a-z]*)+)\b', action_str)
|
|
498
|
+
if camel_match:
|
|
499
|
+
return camel_match.group(1)
|
|
500
|
+
# Also try looking for simple CamelCase
|
|
501
|
+
camel_match2 = re.search(r'\b([A-Z][a-z]+[A-Z][a-z]+(?:[A-Z][a-z]+)*)\b', action_str)
|
|
502
|
+
if camel_match2:
|
|
503
|
+
return camel_match2.group(1)
|
|
504
|
+
|
|
505
|
+
# Try to find in error message
|
|
506
|
+
camel_match3 = re.search(r'\b([A-Z][a-z]*(?:_[A-Z][a-z]*)+)\b', failure.error_message)
|
|
507
|
+
if camel_match3:
|
|
508
|
+
return camel_match3.group(1)
|
|
509
|
+
|
|
510
|
+
return None
|
|
511
|
+
|
|
512
|
+
def _generate_code_change_content(
|
|
513
|
+
self,
|
|
514
|
+
analysis: FailureAnalysis,
|
|
515
|
+
simulation: SimulationResult
|
|
516
|
+
) -> Dict[str, Any]:
|
|
517
|
+
"""Generate code change content."""
|
|
518
|
+
failure_type = analysis.failure.failure_type.value
|
|
519
|
+
|
|
520
|
+
if failure_type == "blocked_by_control_plane":
|
|
521
|
+
return {
|
|
522
|
+
"type": "permission_check",
|
|
523
|
+
"changes": [
|
|
524
|
+
{
|
|
525
|
+
"location": "before_action",
|
|
526
|
+
"code": "if not validate_permissions(action, resource): raise PermissionError()",
|
|
527
|
+
"description": "Add permission validation"
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
"location": "action_handler",
|
|
531
|
+
"code": "with safe_context(): execute_action()",
|
|
532
|
+
"description": "Wrap action in safe context"
|
|
533
|
+
}
|
|
534
|
+
],
|
|
535
|
+
"simulation_steps": simulation.alternative_path
|
|
536
|
+
}
|
|
537
|
+
else:
|
|
538
|
+
return {
|
|
539
|
+
"type": "generic_code_fix",
|
|
540
|
+
"suggested_fixes": analysis.suggested_fixes,
|
|
541
|
+
"simulation_steps": simulation.alternative_path
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
def _generate_config_content(
|
|
545
|
+
self,
|
|
546
|
+
analysis: FailureAnalysis,
|
|
547
|
+
simulation: SimulationResult
|
|
548
|
+
) -> Dict[str, Any]:
|
|
549
|
+
"""Generate configuration update content."""
|
|
550
|
+
return {
|
|
551
|
+
"type": "timeout_handling",
|
|
552
|
+
"config": {
|
|
553
|
+
"timeout_seconds": 30,
|
|
554
|
+
"enable_progress_monitoring": True,
|
|
555
|
+
"allow_partial_results": True
|
|
556
|
+
},
|
|
557
|
+
"simulation_steps": simulation.alternative_path
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
def _update_agent_state(self, agent_id: str, patch: CorrectionPatch):
|
|
561
|
+
"""Update the state of an agent after patching."""
|
|
562
|
+
if agent_id not in self.agent_states:
|
|
563
|
+
self.agent_states[agent_id] = AgentState(
|
|
564
|
+
agent_id=agent_id,
|
|
565
|
+
status="running"
|
|
566
|
+
)
|
|
567
|
+
|
|
568
|
+
state = self.agent_states[agent_id]
|
|
569
|
+
state.status = "patched"
|
|
570
|
+
state.last_failure = patch.failure_analysis.failure
|
|
571
|
+
|
|
572
|
+
if patch.patch_id not in state.patches_applied:
|
|
573
|
+
state.patches_applied.append(patch.patch_id)
|
|
574
|
+
|
|
575
|
+
def get_patch_history(self, agent_id: Optional[str] = None) -> List[CorrectionPatch]:
|
|
576
|
+
"""Get patch history, optionally filtered by agent_id."""
|
|
577
|
+
patches = list(self.patches.values())
|
|
578
|
+
|
|
579
|
+
if agent_id:
|
|
580
|
+
patches = [p for p in patches if p.agent_id == agent_id]
|
|
581
|
+
|
|
582
|
+
return sorted(patches, key=lambda p: p.applied_at or datetime.min, reverse=True)
|