diff --git a/agent-node/VERSION b/agent-node/VERSION index 5257575..f9ef507 100644 --- a/agent-node/VERSION +++ b/agent-node/VERSION @@ -1 +1 @@ -1.0.77 +1.0.78 diff --git a/agent-node/src/agent_node/node.py b/agent-node/src/agent_node/node.py index b2f6825..492d27b 100644 --- a/agent-node/src/agent_node/node.py +++ b/agent-node/src/agent_node/node.py @@ -18,7 +18,7 @@ from agent_node.core.sandbox import SandboxEngine from agent_node.core.sync import NodeSyncManager from agent_node.core.watcher import WorkspaceWatcher -from agent_node.utils.auth import verify_task_signature +from agent_node.utils.auth import verify_task_signature, verify_server_message_signature from agent_node.utils.network import get_secure_stub import agent_node.config as config from agent_node.utils.watchdog import watchdog @@ -331,6 +331,10 @@ except: pass def _process_server_message(self, msg): + if not verify_server_message_signature(msg): + print(f"[❌] Invalid signature on ServerTaskMessage! Dropping message.", flush=True) + return + kind = msg.WhichOneof('payload') if config.DEBUG_GRPC or True: # Force logging for now to debug Mac if kind == 'file_sync' and msg.file_sync.HasField('control'): @@ -599,13 +603,35 @@ )) return - self.task_queue.put(agent_pb2.ClientTaskMessage( - file_sync=agent_pb2.FileSyncMessage( - session_id=session_id, - task_id=task_id, - manifest=agent_pb2.DirectoryManifest(root_path=rel_path, files=files) - ) - )) + chunk_size = 1000 + total_files = len(files) + + if total_files == 0: + self.task_queue.put(agent_pb2.ClientTaskMessage( + file_sync=agent_pb2.FileSyncMessage( + session_id=session_id, + task_id=task_id, + manifest=agent_pb2.DirectoryManifest(root_path=rel_path, files=[], chunk_index=0, is_final=True) + ) + )) + else: + for i in range(0, total_files, chunk_size): + chunk = files[i:i+chunk_size] + is_final = (i + chunk_size) >= total_files + chunk_index = i // chunk_size + + self.task_queue.put(agent_pb2.ClientTaskMessage( + file_sync=agent_pb2.FileSyncMessage( + session_id=session_id, + task_id=task_id, + manifest=agent_pb2.DirectoryManifest( + root_path=rel_path, + files=chunk, + chunk_index=chunk_index, + is_final=is_final + ) + ) + )) def _handle_fs_write(self, session_id, rel_path, content, is_dir, task_id=""): """Modular FS Write/Create.""" diff --git a/agent-node/src/agent_node/utils/auth.py b/agent-node/src/agent_node/utils/auth.py index 52ebe19..b06710c 100644 --- a/agent-node/src/agent_node/utils/auth.py +++ b/agent-node/src/agent_node/utils/auth.py @@ -20,3 +20,13 @@ expected_sig = hmac.new(secret.encode(), sign_base.encode(), hashlib.sha256).hexdigest() return hmac.compare_digest(task.signature, expected_sig) + +def verify_server_message_signature(msg: agent_pb2.ServerTaskMessage, secret=SECRET_KEY) -> bool: + """Verifies HMAC signature for ServerTaskMessage.""" + sig = msg.signature + msg.signature = "" + msg_bytes = msg.SerializeToString() + msg.signature = sig # Restore it + + expected_sig = hmac.new(secret.encode(), msg_bytes, hashlib.sha256).hexdigest() + return hmac.compare_digest(sig, expected_sig) diff --git a/agent-node/src/agent_pb2.py b/agent-node/src/agent_pb2.py index 981686e..450935e 100644 --- a/agent-node/src/agent_pb2.py +++ b/agent-node/src/agent_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE # source: agent.proto -# Protobuf Python Version: 4.25.1 +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'agent.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,16 +24,16 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61gent.proto\x12\x05\x61gent\"\xde\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x12\n\nauth_token\x18\x03 \x01(\t\x12\x18\n\x10node_description\x18\x04 \x01(\t\x12\x42\n\x0c\x63\x61pabilities\x18\x05 \x03(\x0b\x32,.agent.RegistrationRequest.CapabilitiesEntry\x1a\x33\n\x11\x43\x61pabilitiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe0\x01\n\rSandboxPolicy\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.agent.SandboxPolicy.Mode\x12\x18\n\x10\x61llowed_commands\x18\x02 \x03(\t\x12\x17\n\x0f\x64\x65nied_commands\x18\x03 \x03(\t\x12\x1a\n\x12sensitive_commands\x18\x04 \x03(\t\x12\x18\n\x10working_dir_jail\x18\x05 \x01(\t\x12\x19\n\x11skill_config_json\x18\x06 \x01(\t\"\"\n\x04Mode\x12\n\n\x06STRICT\x10\x00\x12\x0e\n\nPERMISSIVE\x10\x01\"x\n\x14RegistrationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t\x12$\n\x06policy\x18\x04 \x01(\x0b\x32\x14.agent.SandboxPolicy\"\xfb\x01\n\x11\x43lientTaskMessage\x12,\n\rtask_response\x18\x01 \x01(\x0b\x32\x13.agent.TaskResponseH\x00\x12-\n\ntask_claim\x18\x02 \x01(\x0b\x32\x17.agent.TaskClaimRequestH\x00\x12\'\n\x08\x61nnounce\x18\x04 \x01(\x0b\x32\x13.agent.NodeAnnounceH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12(\n\x0bskill_event\x18\x06 \x01(\x0b\x32\x11.agent.SkillEventH\x00\x42\t\n\x07payload\"y\n\nSkillEvent\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0f\n\x07task_id\x18\x02 \x01(\t\x12\x16\n\x0cterminal_out\x18\x03 \x01(\tH\x00\x12\x10\n\x06prompt\x18\x04 \x01(\tH\x00\x12\x14\n\nkeep_alive\x18\x05 \x01(\x08H\x00\x42\x06\n\x04\x64\x61ta\"\x1f\n\x0cNodeAnnounce\x12\x0f\n\x07node_id\x18\x01 \x01(\t\"\xbc\x02\n\x11ServerTaskMessage\x12*\n\x0ctask_request\x18\x01 \x01(\x0b\x32\x12.agent.TaskRequestH\x00\x12\x31\n\x10work_pool_update\x18\x02 \x01(\x0b\x32\x15.agent.WorkPoolUpdateH\x00\x12\x30\n\x0c\x63laim_status\x18\x03 \x01(\x0b\x32\x18.agent.TaskClaimResponseH\x00\x12/\n\x0btask_cancel\x18\x04 \x01(\x0b\x32\x18.agent.TaskCancelRequestH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12-\n\rpolicy_update\x18\x06 \x01(\x0b\x32\x14.agent.SandboxPolicyH\x00\x42\t\n\x07payload\"$\n\x11TaskCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\xa1\x01\n\x0bTaskRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x11\n\ttask_type\x18\x02 \x01(\t\x12\x16\n\x0cpayload_json\x18\x03 \x01(\tH\x00\x12\x12\n\ntimeout_ms\x18\x04 \x01(\x05\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x11\n\tsignature\x18\x06 \x01(\t\x12\x12\n\nsession_id\x18\x08 \x01(\tB\t\n\x07payload\"\xa4\x02\n\x0cTaskResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.agent.TaskResponse.Status\x12\x0e\n\x06stdout\x18\x03 \x01(\t\x12\x0e\n\x06stderr\x18\x04 \x01(\t\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x35\n\tartifacts\x18\x06 \x03(\x0b\x32\".agent.TaskResponse.ArtifactsEntry\x1a\x30\n\x0e\x41rtifactsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"<\n\x06Status\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x0b\n\x07TIMEOUT\x10\x02\x12\r\n\tCANCELLED\x10\x03\",\n\x0eWorkPoolUpdate\x12\x1a\n\x12\x61vailable_task_ids\x18\x01 \x03(\t\"4\n\x10TaskClaimRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\t\"E\n\x11TaskClaimResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07granted\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\xe6\x02\n\tHeartbeat\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x19\n\x11\x63pu_usage_percent\x18\x02 \x01(\x02\x12\x1c\n\x14memory_usage_percent\x18\x03 \x01(\x02\x12\x1b\n\x13\x61\x63tive_worker_count\x18\x04 \x01(\x05\x12\x1b\n\x13max_worker_capacity\x18\x05 \x01(\x05\x12\x16\n\x0estatus_message\x18\x06 \x01(\t\x12\x18\n\x10running_task_ids\x18\x07 \x03(\t\x12\x11\n\tcpu_count\x18\x08 \x01(\x05\x12\x16\n\x0ememory_used_gb\x18\t \x01(\x02\x12\x17\n\x0fmemory_total_gb\x18\n \x01(\x02\x12\x1a\n\x12\x63pu_usage_per_core\x18\x0b \x03(\x02\x12\x14\n\x0c\x63pu_freq_mhz\x18\x0c \x01(\x02\x12\x1b\n\x13memory_available_gb\x18\r \x01(\x02\x12\x10\n\x08load_avg\x18\x0e \x03(\x02\"-\n\x13HealthCheckResponse\x12\x16\n\x0eserver_time_ms\x18\x01 \x01(\x03\"\xe4\x01\n\x0f\x46ileSyncMessage\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12,\n\x08manifest\x18\x02 \x01(\x0b\x32\x18.agent.DirectoryManifestH\x00\x12\'\n\tfile_data\x18\x03 \x01(\x0b\x32\x12.agent.FilePayloadH\x00\x12#\n\x06status\x18\x04 \x01(\x0b\x32\x11.agent.SyncStatusH\x00\x12%\n\x07\x63ontrol\x18\x05 \x01(\x0b\x32\x12.agent.SyncControlH\x00\x12\x0f\n\x07task_id\x18\x06 \x01(\tB\t\n\x07payload\"\xab\x02\n\x0bSyncControl\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.agent.SyncControl.Action\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x15\n\rrequest_paths\x18\x03 \x03(\t\x12\x0f\n\x07\x63ontent\x18\x04 \x01(\x0c\x12\x0e\n\x06is_dir\x18\x05 \x01(\x08\"\xaa\x01\n\x06\x41\x63tion\x12\x12\n\x0eSTART_WATCHING\x10\x00\x12\x11\n\rSTOP_WATCHING\x10\x01\x12\x08\n\x04LOCK\x10\x02\x12\n\n\x06UNLOCK\x10\x03\x12\x14\n\x10REFRESH_MANIFEST\x10\x04\x12\n\n\x06RESYNC\x10\x05\x12\x08\n\x04LIST\x10\x06\x12\x08\n\x04READ\x10\x07\x12\t\n\x05WRITE\x10\x08\x12\n\n\x06\x44\x45LETE\x10\t\x12\t\n\x05PURGE\x10\n\x12\x0b\n\x07\x43LEANUP\x10\x0b\"F\n\x11\x44irectoryManifest\x12\x11\n\troot_path\x18\x01 \x01(\t\x12\x1e\n\x05\x66iles\x18\x02 \x03(\x0b\x32\x0f.agent.FileInfo\"D\n\x08\x46ileInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x03\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\x0e\n\x06is_dir\x18\x04 \x01(\x08\"\xad\x01\n\x0b\x46ilePayload\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\r\n\x05\x63hunk\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\x12\x0c\n\x04hash\x18\x05 \x01(\t\x12\x0e\n\x06offset\x18\x06 \x01(\x03\x12\x12\n\ncompressed\x18\x07 \x01(\x08\x12\x14\n\x0ctotal_chunks\x18\x08 \x01(\x05\x12\x12\n\ntotal_size\x18\t \x01(\x03\"\xa0\x01\n\nSyncStatus\x12$\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x16.agent.SyncStatus.Code\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x17\n\x0freconcile_paths\x18\x03 \x03(\t\"B\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x16\n\x12RECONCILE_REQUIRED\x10\x02\x12\x0f\n\x0bIN_PROGRESS\x10\x03\x32\xe9\x01\n\x11\x41gentOrchestrator\x12L\n\x11SyncConfiguration\x12\x1a.agent.RegistrationRequest\x1a\x1b.agent.RegistrationResponse\x12\x44\n\nTaskStream\x12\x18.agent.ClientTaskMessage\x1a\x18.agent.ServerTaskMessage(\x01\x30\x01\x12@\n\x0cReportHealth\x12\x10.agent.Heartbeat\x1a\x1a.agent.HealthCheckResponse(\x01\x30\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61gent.proto\x12\x05\x61gent\"\xde\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x12\n\nauth_token\x18\x03 \x01(\t\x12\x18\n\x10node_description\x18\x04 \x01(\t\x12\x42\n\x0c\x63\x61pabilities\x18\x05 \x03(\x0b\x32,.agent.RegistrationRequest.CapabilitiesEntry\x1a\x33\n\x11\x43\x61pabilitiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe0\x01\n\rSandboxPolicy\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.agent.SandboxPolicy.Mode\x12\x18\n\x10\x61llowed_commands\x18\x02 \x03(\t\x12\x17\n\x0f\x64\x65nied_commands\x18\x03 \x03(\t\x12\x1a\n\x12sensitive_commands\x18\x04 \x03(\t\x12\x18\n\x10working_dir_jail\x18\x05 \x01(\t\x12\x19\n\x11skill_config_json\x18\x06 \x01(\t\"\"\n\x04Mode\x12\n\n\x06STRICT\x10\x00\x12\x0e\n\nPERMISSIVE\x10\x01\"x\n\x14RegistrationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t\x12$\n\x06policy\x18\x04 \x01(\x0b\x32\x14.agent.SandboxPolicy\"\xfb\x01\n\x11\x43lientTaskMessage\x12,\n\rtask_response\x18\x01 \x01(\x0b\x32\x13.agent.TaskResponseH\x00\x12-\n\ntask_claim\x18\x02 \x01(\x0b\x32\x17.agent.TaskClaimRequestH\x00\x12\'\n\x08\x61nnounce\x18\x04 \x01(\x0b\x32\x13.agent.NodeAnnounceH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12(\n\x0bskill_event\x18\x06 \x01(\x0b\x32\x11.agent.SkillEventH\x00\x42\t\n\x07payload\"y\n\nSkillEvent\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0f\n\x07task_id\x18\x02 \x01(\t\x12\x16\n\x0cterminal_out\x18\x03 \x01(\tH\x00\x12\x10\n\x06prompt\x18\x04 \x01(\tH\x00\x12\x14\n\nkeep_alive\x18\x05 \x01(\x08H\x00\x42\x06\n\x04\x64\x61ta\"\x1f\n\x0cNodeAnnounce\x12\x0f\n\x07node_id\x18\x01 \x01(\t\"\xcf\x02\n\x11ServerTaskMessage\x12*\n\x0ctask_request\x18\x01 \x01(\x0b\x32\x12.agent.TaskRequestH\x00\x12\x31\n\x10work_pool_update\x18\x02 \x01(\x0b\x32\x15.agent.WorkPoolUpdateH\x00\x12\x30\n\x0c\x63laim_status\x18\x03 \x01(\x0b\x32\x18.agent.TaskClaimResponseH\x00\x12/\n\x0btask_cancel\x18\x04 \x01(\x0b\x32\x18.agent.TaskCancelRequestH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12-\n\rpolicy_update\x18\x06 \x01(\x0b\x32\x14.agent.SandboxPolicyH\x00\x12\x11\n\tsignature\x18\x07 \x01(\tB\t\n\x07payload\"$\n\x11TaskCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\xa1\x01\n\x0bTaskRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x11\n\ttask_type\x18\x02 \x01(\t\x12\x16\n\x0cpayload_json\x18\x03 \x01(\tH\x00\x12\x12\n\ntimeout_ms\x18\x04 \x01(\x05\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x11\n\tsignature\x18\x06 \x01(\t\x12\x12\n\nsession_id\x18\x08 \x01(\tB\t\n\x07payload\"\xa4\x02\n\x0cTaskResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.agent.TaskResponse.Status\x12\x0e\n\x06stdout\x18\x03 \x01(\t\x12\x0e\n\x06stderr\x18\x04 \x01(\t\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x35\n\tartifacts\x18\x06 \x03(\x0b\x32\".agent.TaskResponse.ArtifactsEntry\x1a\x30\n\x0e\x41rtifactsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"<\n\x06Status\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x0b\n\x07TIMEOUT\x10\x02\x12\r\n\tCANCELLED\x10\x03\",\n\x0eWorkPoolUpdate\x12\x1a\n\x12\x61vailable_task_ids\x18\x01 \x03(\t\"4\n\x10TaskClaimRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\t\"E\n\x11TaskClaimResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07granted\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\xe6\x02\n\tHeartbeat\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x19\n\x11\x63pu_usage_percent\x18\x02 \x01(\x02\x12\x1c\n\x14memory_usage_percent\x18\x03 \x01(\x02\x12\x1b\n\x13\x61\x63tive_worker_count\x18\x04 \x01(\x05\x12\x1b\n\x13max_worker_capacity\x18\x05 \x01(\x05\x12\x16\n\x0estatus_message\x18\x06 \x01(\t\x12\x18\n\x10running_task_ids\x18\x07 \x03(\t\x12\x11\n\tcpu_count\x18\x08 \x01(\x05\x12\x16\n\x0ememory_used_gb\x18\t \x01(\x02\x12\x17\n\x0fmemory_total_gb\x18\n \x01(\x02\x12\x1a\n\x12\x63pu_usage_per_core\x18\x0b \x03(\x02\x12\x14\n\x0c\x63pu_freq_mhz\x18\x0c \x01(\x02\x12\x1b\n\x13memory_available_gb\x18\r \x01(\x02\x12\x10\n\x08load_avg\x18\x0e \x03(\x02\"-\n\x13HealthCheckResponse\x12\x16\n\x0eserver_time_ms\x18\x01 \x01(\x03\"\xe4\x01\n\x0f\x46ileSyncMessage\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12,\n\x08manifest\x18\x02 \x01(\x0b\x32\x18.agent.DirectoryManifestH\x00\x12\'\n\tfile_data\x18\x03 \x01(\x0b\x32\x12.agent.FilePayloadH\x00\x12#\n\x06status\x18\x04 \x01(\x0b\x32\x11.agent.SyncStatusH\x00\x12%\n\x07\x63ontrol\x18\x05 \x01(\x0b\x32\x12.agent.SyncControlH\x00\x12\x0f\n\x07task_id\x18\x06 \x01(\tB\t\n\x07payload\"\xab\x02\n\x0bSyncControl\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.agent.SyncControl.Action\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x15\n\rrequest_paths\x18\x03 \x03(\t\x12\x0f\n\x07\x63ontent\x18\x04 \x01(\x0c\x12\x0e\n\x06is_dir\x18\x05 \x01(\x08\"\xaa\x01\n\x06\x41\x63tion\x12\x12\n\x0eSTART_WATCHING\x10\x00\x12\x11\n\rSTOP_WATCHING\x10\x01\x12\x08\n\x04LOCK\x10\x02\x12\n\n\x06UNLOCK\x10\x03\x12\x14\n\x10REFRESH_MANIFEST\x10\x04\x12\n\n\x06RESYNC\x10\x05\x12\x08\n\x04LIST\x10\x06\x12\x08\n\x04READ\x10\x07\x12\t\n\x05WRITE\x10\x08\x12\n\n\x06\x44\x45LETE\x10\t\x12\t\n\x05PURGE\x10\n\x12\x0b\n\x07\x43LEANUP\x10\x0b\"m\n\x11\x44irectoryManifest\x12\x11\n\troot_path\x18\x01 \x01(\t\x12\x1e\n\x05\x66iles\x18\x02 \x03(\x0b\x32\x0f.agent.FileInfo\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\"D\n\x08\x46ileInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x03\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\x0e\n\x06is_dir\x18\x04 \x01(\x08\"\xad\x01\n\x0b\x46ilePayload\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\r\n\x05\x63hunk\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\x12\x0c\n\x04hash\x18\x05 \x01(\t\x12\x0e\n\x06offset\x18\x06 \x01(\x03\x12\x12\n\ncompressed\x18\x07 \x01(\x08\x12\x14\n\x0ctotal_chunks\x18\x08 \x01(\x05\x12\x12\n\ntotal_size\x18\t \x01(\x03\"\xa0\x01\n\nSyncStatus\x12$\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x16.agent.SyncStatus.Code\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x17\n\x0freconcile_paths\x18\x03 \x03(\t\"B\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x16\n\x12RECONCILE_REQUIRED\x10\x02\x12\x0f\n\x0bIN_PROGRESS\x10\x03\x32\xe9\x01\n\x11\x41gentOrchestrator\x12L\n\x11SyncConfiguration\x12\x1a.agent.RegistrationRequest\x1a\x1b.agent.RegistrationResponse\x12\x44\n\nTaskStream\x12\x18.agent.ClientTaskMessage\x1a\x18.agent.ServerTaskMessage(\x01\x30\x01\x12@\n\x0cReportHealth\x12\x10.agent.Heartbeat\x1a\x1a.agent.HealthCheckResponse(\x01\x30\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) _builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._options = None +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._loaded_options = None _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_options = b'8\001' - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._options = None + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._loaded_options = None _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_options = b'8\001' _globals['_REGISTRATIONREQUEST']._serialized_start=23 _globals['_REGISTRATIONREQUEST']._serialized_end=245 @@ -42,43 +52,43 @@ _globals['_NODEANNOUNCE']._serialized_start=973 _globals['_NODEANNOUNCE']._serialized_end=1004 _globals['_SERVERTASKMESSAGE']._serialized_start=1007 - _globals['_SERVERTASKMESSAGE']._serialized_end=1323 - _globals['_TASKCANCELREQUEST']._serialized_start=1325 - _globals['_TASKCANCELREQUEST']._serialized_end=1361 - _globals['_TASKREQUEST']._serialized_start=1364 - _globals['_TASKREQUEST']._serialized_end=1525 - _globals['_TASKRESPONSE']._serialized_start=1528 - _globals['_TASKRESPONSE']._serialized_end=1820 - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_start=1710 - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_end=1758 - _globals['_TASKRESPONSE_STATUS']._serialized_start=1760 - _globals['_TASKRESPONSE_STATUS']._serialized_end=1820 - _globals['_WORKPOOLUPDATE']._serialized_start=1822 - _globals['_WORKPOOLUPDATE']._serialized_end=1866 - _globals['_TASKCLAIMREQUEST']._serialized_start=1868 - _globals['_TASKCLAIMREQUEST']._serialized_end=1920 - _globals['_TASKCLAIMRESPONSE']._serialized_start=1922 - _globals['_TASKCLAIMRESPONSE']._serialized_end=1991 - _globals['_HEARTBEAT']._serialized_start=1994 - _globals['_HEARTBEAT']._serialized_end=2352 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=2354 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=2399 - _globals['_FILESYNCMESSAGE']._serialized_start=2402 - _globals['_FILESYNCMESSAGE']._serialized_end=2630 - _globals['_SYNCCONTROL']._serialized_start=2633 - _globals['_SYNCCONTROL']._serialized_end=2932 - _globals['_SYNCCONTROL_ACTION']._serialized_start=2762 - _globals['_SYNCCONTROL_ACTION']._serialized_end=2932 - _globals['_DIRECTORYMANIFEST']._serialized_start=2934 - _globals['_DIRECTORYMANIFEST']._serialized_end=3004 - _globals['_FILEINFO']._serialized_start=3006 - _globals['_FILEINFO']._serialized_end=3074 - _globals['_FILEPAYLOAD']._serialized_start=3077 - _globals['_FILEPAYLOAD']._serialized_end=3250 - _globals['_SYNCSTATUS']._serialized_start=3253 - _globals['_SYNCSTATUS']._serialized_end=3413 - _globals['_SYNCSTATUS_CODE']._serialized_start=3347 - _globals['_SYNCSTATUS_CODE']._serialized_end=3413 - _globals['_AGENTORCHESTRATOR']._serialized_start=3416 - _globals['_AGENTORCHESTRATOR']._serialized_end=3649 + _globals['_SERVERTASKMESSAGE']._serialized_end=1342 + _globals['_TASKCANCELREQUEST']._serialized_start=1344 + _globals['_TASKCANCELREQUEST']._serialized_end=1380 + _globals['_TASKREQUEST']._serialized_start=1383 + _globals['_TASKREQUEST']._serialized_end=1544 + _globals['_TASKRESPONSE']._serialized_start=1547 + _globals['_TASKRESPONSE']._serialized_end=1839 + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_start=1729 + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_end=1777 + _globals['_TASKRESPONSE_STATUS']._serialized_start=1779 + _globals['_TASKRESPONSE_STATUS']._serialized_end=1839 + _globals['_WORKPOOLUPDATE']._serialized_start=1841 + _globals['_WORKPOOLUPDATE']._serialized_end=1885 + _globals['_TASKCLAIMREQUEST']._serialized_start=1887 + _globals['_TASKCLAIMREQUEST']._serialized_end=1939 + _globals['_TASKCLAIMRESPONSE']._serialized_start=1941 + _globals['_TASKCLAIMRESPONSE']._serialized_end=2010 + _globals['_HEARTBEAT']._serialized_start=2013 + _globals['_HEARTBEAT']._serialized_end=2371 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=2373 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=2418 + _globals['_FILESYNCMESSAGE']._serialized_start=2421 + _globals['_FILESYNCMESSAGE']._serialized_end=2649 + _globals['_SYNCCONTROL']._serialized_start=2652 + _globals['_SYNCCONTROL']._serialized_end=2951 + _globals['_SYNCCONTROL_ACTION']._serialized_start=2781 + _globals['_SYNCCONTROL_ACTION']._serialized_end=2951 + _globals['_DIRECTORYMANIFEST']._serialized_start=2953 + _globals['_DIRECTORYMANIFEST']._serialized_end=3062 + _globals['_FILEINFO']._serialized_start=3064 + _globals['_FILEINFO']._serialized_end=3132 + _globals['_FILEPAYLOAD']._serialized_start=3135 + _globals['_FILEPAYLOAD']._serialized_end=3308 + _globals['_SYNCSTATUS']._serialized_start=3311 + _globals['_SYNCSTATUS']._serialized_end=3471 + _globals['_SYNCSTATUS_CODE']._serialized_start=3405 + _globals['_SYNCSTATUS_CODE']._serialized_end=3471 + _globals['_AGENTORCHESTRATOR']._serialized_start=3474 + _globals['_AGENTORCHESTRATOR']._serialized_end=3707 # @@protoc_insertion_point(module_scope) diff --git a/agent-node/src/agent_pb2_grpc.py b/agent-node/src/agent_pb2_grpc.py index 932d45e..e972a62 100644 --- a/agent-node/src/agent_pb2_grpc.py +++ b/agent-node/src/agent_pb2_grpc.py @@ -1,9 +1,29 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings import agent_pb2 as agent__pb2 +GRPC_GENERATED_VERSION = '1.71.2' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in agent_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) + class AgentOrchestratorStub(object): """The Cortex Server exposes this service @@ -19,17 +39,17 @@ '/agent.AgentOrchestrator/SyncConfiguration', request_serializer=agent__pb2.RegistrationRequest.SerializeToString, response_deserializer=agent__pb2.RegistrationResponse.FromString, - ) + _registered_method=True) self.TaskStream = channel.stream_stream( '/agent.AgentOrchestrator/TaskStream', request_serializer=agent__pb2.ClientTaskMessage.SerializeToString, response_deserializer=agent__pb2.ServerTaskMessage.FromString, - ) + _registered_method=True) self.ReportHealth = channel.stream_stream( '/agent.AgentOrchestrator/ReportHealth', request_serializer=agent__pb2.Heartbeat.SerializeToString, response_deserializer=agent__pb2.HealthCheckResponse.FromString, - ) + _registered_method=True) class AgentOrchestratorServicer(object): @@ -79,6 +99,7 @@ generic_handler = grpc.method_handlers_generic_handler( 'agent.AgentOrchestrator', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('agent.AgentOrchestrator', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -97,11 +118,21 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/agent.AgentOrchestrator/SyncConfiguration', + return grpc.experimental.unary_unary( + request, + target, + '/agent.AgentOrchestrator/SyncConfiguration', agent__pb2.RegistrationRequest.SerializeToString, agent__pb2.RegistrationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def TaskStream(request_iterator, @@ -114,11 +145,21 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/agent.AgentOrchestrator/TaskStream', + return grpc.experimental.stream_stream( + request_iterator, + target, + '/agent.AgentOrchestrator/TaskStream', agent__pb2.ClientTaskMessage.SerializeToString, agent__pb2.ServerTaskMessage.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def ReportHealth(request_iterator, @@ -131,8 +172,18 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/agent.AgentOrchestrator/ReportHealth', + return grpc.experimental.stream_stream( + request_iterator, + target, + '/agent.AgentOrchestrator/ReportHealth', agent__pb2.Heartbeat.SerializeToString, agent__pb2.HealthCheckResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/agent-node/src/protos/agent.proto b/agent-node/src/protos/agent.proto index 0f14b03..6db4b01 100644 --- a/agent-node/src/protos/agent.proto +++ b/agent-node/src/protos/agent.proto @@ -77,6 +77,7 @@ FileSyncMessage file_sync = 5; // NEW: Ghost Mirror Sync SandboxPolicy policy_update = 6; // NEW: Live Policy Update } + string signature = 7; // NEW: Unified Signature } message TaskCancelRequest { @@ -189,6 +190,8 @@ message DirectoryManifest { string root_path = 1; repeated FileInfo files = 2; + int32 chunk_index = 3; // NEW: For paginated manifest + bool is_final = 4; // NEW: For paginated manifest } message FileInfo { diff --git a/ai-hub/app/core/grpc/services/grpc_server.py b/ai-hub/app/core/grpc/services/grpc_server.py index 265cbfc..e42d1e5 100644 --- a/ai-hub/app/core/grpc/services/grpc_server.py +++ b/ai-hub/app/core/grpc/services/grpc_server.py @@ -33,6 +33,8 @@ self.io_locks_lock = threading.Lock() self.assistant = TaskAssistant(self.registry, self.journal, self.pool, self.mirror) self.pool.on_new_work = self._broadcast_work + self.manifest_accumulators = {} # NEW: For paginated manifests + self.manifest_accumulators_lock = threading.Lock() # 4. Mesh Observation (Aggregated Health Dashboard) threading.Thread(target=self._monitor_mesh, daemon=True, name="MeshMonitor").start() @@ -395,40 +397,59 @@ self.registry.emit(node_id, "sync_progress", {"path": fs.file_data.path, "chunk": fs.file_data.chunk_index}) elif fs.HasField("manifest"): - logger.debug(f"[📁📥] Received Manifest from {node_id} for {fs.session_id}") + logger.debug(f"[📁📥] Received Manifest from {node_id} for {fs.session_id} (chunk: {fs.manifest.chunk_index}, final: {fs.manifest.is_final})") - # M6: Handle interactive 'ls' result correlation - if task_id and task_id.startswith("fs-ls-"): - files = [ - {"path": f.path, "name": os.path.basename(f.path) or f.path, "is_dir": f.is_dir, "size": f.size} - for f in fs.manifest.files - ] - self.journal.fulfill(task_id, {"files": files, "path": fs.manifest.root_path}) + key = (node_id, fs.session_id) + with self.manifest_accumulators_lock: + if key not in self.manifest_accumulators: + self.manifest_accumulators[key] = [] + self.manifest_accumulators[key].extend(fs.manifest.files) + + is_final = fs.manifest.is_final + root_path = fs.manifest.root_path + + if is_final: + accumulated_files = self.manifest_accumulators.pop(key) - # M6: Only reconcile for real user sessions, not for the modular explorer. - if fs.session_id != "__fs_explorer__": - drifts = None - # Do not reconcile on shallow manifests triggered by interactive FS tools - if task_id and any(task_id.startswith(p) for p in ("fs-ls-", "fs-write-", "fs-rm-")): - pass - else: - drifts = self.mirror.reconcile(fs.session_id, fs.manifest) - if drifts: - logger.info(f"[📁🏃] Drift Detected (Node -> Server): Requesting {len(drifts)} files") - # Request node to push these specific files - # Priority 1: Drift Reconciliation Request - node.send_message(agent_pb2.ServerTaskMessage( - file_sync=agent_pb2.FileSyncMessage( - session_id=fs.session_id, - control=agent_pb2.SyncControl( - action=agent_pb2.SyncControl.REFRESH_MANIFEST, - path=fs.manifest.root_path, - request_paths=drifts + if is_final: + # Create a complete manifest object for downstream processing + full_manifest = agent_pb2.DirectoryManifest( + root_path=root_path, + files=accumulated_files + ) + + # M6: Handle interactive 'ls' result correlation + if task_id and task_id.startswith("fs-ls-"): + files = [ + {"path": f.path, "name": os.path.basename(f.path) or f.path, "is_dir": f.is_dir, "size": f.size} + for f in accumulated_files + ] + self.journal.fulfill(task_id, {"files": files, "path": root_path}) + + # M6: Only reconcile for real user sessions, not for the modular explorer. + if fs.session_id != "__fs_explorer__": + drifts = None + # Do not reconcile on shallow manifests triggered by interactive FS tools + if task_id and any(task_id.startswith(p) for p in ("fs-ls-", "fs-write-", "fs-rm-")): + pass + else: + drifts = self.mirror.reconcile(fs.session_id, full_manifest) + if drifts: + logger.info(f"[📁🏃] Drift Detected (Node -> Server): Requesting {len(drifts)} files") + # Request node to push these specific files + # Priority 1: Drift Reconciliation Request + node.send_message(agent_pb2.ServerTaskMessage( + file_sync=agent_pb2.FileSyncMessage( + session_id=fs.session_id, + control=agent_pb2.SyncControl( + action=agent_pb2.SyncControl.REFRESH_MANIFEST, + path=root_path, + request_paths=drifts + ) ) - ) - ), priority=1) - else: - self.registry.emit(node_id, "sync_status", {"message": "Synchronized (Node -> Server)", "code": 0}) + ), priority=1) + else: + self.registry.emit(node_id, "sync_status", {"message": "Synchronized (Node -> Server)", "code": 0}) elif fs.HasField("status"): logger.debug(f"[📁] Sync Status from {node_id}: {fs.status.message}") diff --git a/ai-hub/app/core/grpc/utils/crypto.py b/ai-hub/app/core/grpc/utils/crypto.py index 4ea9485..b973bac 100644 --- a/ai-hub/app/core/grpc/utils/crypto.py +++ b/ai-hub/app/core/grpc/utils/crypto.py @@ -1,13 +1,23 @@ import hmac import hashlib from app.config import settings + SECRET_KEY = settings.SECRET_KEY def sign_payload(payload: str) -> str: """Signs a string payload using HMAC-SHA256.""" return hmac.new(SECRET_KEY.encode(), payload.encode(), hashlib.sha256).hexdigest() +def sign_bytes(data: bytes) -> str: + """Signs bytes using HMAC-SHA256.""" + return hmac.new(SECRET_KEY.encode(), data, hashlib.sha256).hexdigest() + def verify_signature(payload: str, signature: str) -> bool: """Verifies a signature against a payload using HMAC-SHA256.""" expected = sign_payload(payload) return hmac.compare_digest(signature, expected) + +def verify_bytes_signature(data: bytes, signature: str) -> bool: + """Verifies a signature against bytes using HMAC-SHA256.""" + expected = sign_bytes(data) + return hmac.compare_digest(signature, expected) diff --git a/ai-hub/app/core/services/node_registry.py b/ai-hub/app/core/services/node_registry.py index 60bb6e9..7d178cd 100644 --- a/ai-hub/app/core/services/node_registry.py +++ b/ai-hub/app/core/services/node_registry.py @@ -85,6 +85,14 @@ Thread-safe and Async-safe message dispatcher. priority: 0 (Admin/Control), 1 (Terminal/Interactive), 2 (File Sync) """ + from app.protos import agent_pb2 + from app.core.grpc.utils.crypto import sign_bytes + + if isinstance(msg, agent_pb2.ServerTaskMessage): + msg.signature = "" + msg_bytes = msg.SerializeToString() + msg.signature = sign_bytes(msg_bytes) + item = (priority, time.time(), msg) def _blocking_put(): diff --git a/ai-hub/app/protos/agent.proto b/ai-hub/app/protos/agent.proto index 0f14b03..6db4b01 100644 --- a/ai-hub/app/protos/agent.proto +++ b/ai-hub/app/protos/agent.proto @@ -77,6 +77,7 @@ FileSyncMessage file_sync = 5; // NEW: Ghost Mirror Sync SandboxPolicy policy_update = 6; // NEW: Live Policy Update } + string signature = 7; // NEW: Unified Signature } message TaskCancelRequest { @@ -189,6 +190,8 @@ message DirectoryManifest { string root_path = 1; repeated FileInfo files = 2; + int32 chunk_index = 3; // NEW: For paginated manifest + bool is_final = 4; // NEW: For paginated manifest } message FileInfo { diff --git a/ai-hub/app/protos/agent_pb2.py b/ai-hub/app/protos/agent_pb2.py index 506ec2c..450935e 100644 --- a/ai-hub/app/protos/agent_pb2.py +++ b/ai-hub/app/protos/agent_pb2.py @@ -1,12 +1,22 @@ # -*- coding: utf-8 -*- # Generated by the protocol buffer compiler. DO NOT EDIT! -# source: app/protos/agent.proto -# Protobuf Python Version: 4.25.1 +# NO CHECKED-IN PROTOBUF GENCODE +# source: agent.proto +# Protobuf Python Version: 5.29.0 """Generated protocol buffer code.""" from google.protobuf import descriptor as _descriptor from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version from google.protobuf import symbol_database as _symbol_database from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 5, + 29, + 0, + '', + 'agent.proto' +) # @@protoc_insertion_point(imports) _sym_db = _symbol_database.Default() @@ -14,71 +24,71 @@ -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x16\x61pp/protos/agent.proto\x12\x05\x61gent\"\xde\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x12\n\nauth_token\x18\x03 \x01(\t\x12\x18\n\x10node_description\x18\x04 \x01(\t\x12\x42\n\x0c\x63\x61pabilities\x18\x05 \x03(\x0b\x32,.agent.RegistrationRequest.CapabilitiesEntry\x1a\x33\n\x11\x43\x61pabilitiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe0\x01\n\rSandboxPolicy\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.agent.SandboxPolicy.Mode\x12\x18\n\x10\x61llowed_commands\x18\x02 \x03(\t\x12\x17\n\x0f\x64\x65nied_commands\x18\x03 \x03(\t\x12\x1a\n\x12sensitive_commands\x18\x04 \x03(\t\x12\x18\n\x10working_dir_jail\x18\x05 \x01(\t\x12\x19\n\x11skill_config_json\x18\x06 \x01(\t\"\"\n\x04Mode\x12\n\n\x06STRICT\x10\x00\x12\x0e\n\nPERMISSIVE\x10\x01\"x\n\x14RegistrationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t\x12$\n\x06policy\x18\x04 \x01(\x0b\x32\x14.agent.SandboxPolicy\"\xfb\x01\n\x11\x43lientTaskMessage\x12,\n\rtask_response\x18\x01 \x01(\x0b\x32\x13.agent.TaskResponseH\x00\x12-\n\ntask_claim\x18\x02 \x01(\x0b\x32\x17.agent.TaskClaimRequestH\x00\x12\'\n\x08\x61nnounce\x18\x04 \x01(\x0b\x32\x13.agent.NodeAnnounceH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12(\n\x0bskill_event\x18\x06 \x01(\x0b\x32\x11.agent.SkillEventH\x00\x42\t\n\x07payload\"y\n\nSkillEvent\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0f\n\x07task_id\x18\x02 \x01(\t\x12\x16\n\x0cterminal_out\x18\x03 \x01(\tH\x00\x12\x10\n\x06prompt\x18\x04 \x01(\tH\x00\x12\x14\n\nkeep_alive\x18\x05 \x01(\x08H\x00\x42\x06\n\x04\x64\x61ta\"\x1f\n\x0cNodeAnnounce\x12\x0f\n\x07node_id\x18\x01 \x01(\t\"\xbc\x02\n\x11ServerTaskMessage\x12*\n\x0ctask_request\x18\x01 \x01(\x0b\x32\x12.agent.TaskRequestH\x00\x12\x31\n\x10work_pool_update\x18\x02 \x01(\x0b\x32\x15.agent.WorkPoolUpdateH\x00\x12\x30\n\x0c\x63laim_status\x18\x03 \x01(\x0b\x32\x18.agent.TaskClaimResponseH\x00\x12/\n\x0btask_cancel\x18\x04 \x01(\x0b\x32\x18.agent.TaskCancelRequestH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12-\n\rpolicy_update\x18\x06 \x01(\x0b\x32\x14.agent.SandboxPolicyH\x00\x42\t\n\x07payload\"$\n\x11TaskCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\xa1\x01\n\x0bTaskRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x11\n\ttask_type\x18\x02 \x01(\t\x12\x16\n\x0cpayload_json\x18\x03 \x01(\tH\x00\x12\x12\n\ntimeout_ms\x18\x04 \x01(\x05\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x11\n\tsignature\x18\x06 \x01(\t\x12\x12\n\nsession_id\x18\x08 \x01(\tB\t\n\x07payload\"\xa4\x02\n\x0cTaskResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.agent.TaskResponse.Status\x12\x0e\n\x06stdout\x18\x03 \x01(\t\x12\x0e\n\x06stderr\x18\x04 \x01(\t\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x35\n\tartifacts\x18\x06 \x03(\x0b\x32\".agent.TaskResponse.ArtifactsEntry\x1a\x30\n\x0e\x41rtifactsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"<\n\x06Status\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x0b\n\x07TIMEOUT\x10\x02\x12\r\n\tCANCELLED\x10\x03\",\n\x0eWorkPoolUpdate\x12\x1a\n\x12\x61vailable_task_ids\x18\x01 \x03(\t\"4\n\x10TaskClaimRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\t\"E\n\x11TaskClaimResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07granted\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\xe6\x02\n\tHeartbeat\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x19\n\x11\x63pu_usage_percent\x18\x02 \x01(\x02\x12\x1c\n\x14memory_usage_percent\x18\x03 \x01(\x02\x12\x1b\n\x13\x61\x63tive_worker_count\x18\x04 \x01(\x05\x12\x1b\n\x13max_worker_capacity\x18\x05 \x01(\x05\x12\x16\n\x0estatus_message\x18\x06 \x01(\t\x12\x18\n\x10running_task_ids\x18\x07 \x03(\t\x12\x11\n\tcpu_count\x18\x08 \x01(\x05\x12\x16\n\x0ememory_used_gb\x18\t \x01(\x02\x12\x17\n\x0fmemory_total_gb\x18\n \x01(\x02\x12\x1a\n\x12\x63pu_usage_per_core\x18\x0b \x03(\x02\x12\x14\n\x0c\x63pu_freq_mhz\x18\x0c \x01(\x02\x12\x1b\n\x13memory_available_gb\x18\r \x01(\x02\x12\x10\n\x08load_avg\x18\x0e \x03(\x02\"-\n\x13HealthCheckResponse\x12\x16\n\x0eserver_time_ms\x18\x01 \x01(\x03\"\xe4\x01\n\x0f\x46ileSyncMessage\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12,\n\x08manifest\x18\x02 \x01(\x0b\x32\x18.agent.DirectoryManifestH\x00\x12\'\n\tfile_data\x18\x03 \x01(\x0b\x32\x12.agent.FilePayloadH\x00\x12#\n\x06status\x18\x04 \x01(\x0b\x32\x11.agent.SyncStatusH\x00\x12%\n\x07\x63ontrol\x18\x05 \x01(\x0b\x32\x12.agent.SyncControlH\x00\x12\x0f\n\x07task_id\x18\x06 \x01(\tB\t\n\x07payload\"\xab\x02\n\x0bSyncControl\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.agent.SyncControl.Action\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x15\n\rrequest_paths\x18\x03 \x03(\t\x12\x0f\n\x07\x63ontent\x18\x04 \x01(\x0c\x12\x0e\n\x06is_dir\x18\x05 \x01(\x08\"\xaa\x01\n\x06\x41\x63tion\x12\x12\n\x0eSTART_WATCHING\x10\x00\x12\x11\n\rSTOP_WATCHING\x10\x01\x12\x08\n\x04LOCK\x10\x02\x12\n\n\x06UNLOCK\x10\x03\x12\x14\n\x10REFRESH_MANIFEST\x10\x04\x12\n\n\x06RESYNC\x10\x05\x12\x08\n\x04LIST\x10\x06\x12\x08\n\x04READ\x10\x07\x12\t\n\x05WRITE\x10\x08\x12\n\n\x06\x44\x45LETE\x10\t\x12\t\n\x05PURGE\x10\n\x12\x0b\n\x07\x43LEANUP\x10\x0b\"F\n\x11\x44irectoryManifest\x12\x11\n\troot_path\x18\x01 \x01(\t\x12\x1e\n\x05\x66iles\x18\x02 \x03(\x0b\x32\x0f.agent.FileInfo\"D\n\x08\x46ileInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x03\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\x0e\n\x06is_dir\x18\x04 \x01(\x08\"\xad\x01\n\x0b\x46ilePayload\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\r\n\x05\x63hunk\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\x12\x0c\n\x04hash\x18\x05 \x01(\t\x12\x0e\n\x06offset\x18\x06 \x01(\x03\x12\x12\n\ncompressed\x18\x07 \x01(\x08\x12\x14\n\x0ctotal_chunks\x18\x08 \x01(\x05\x12\x12\n\ntotal_size\x18\t \x01(\x03\"\xa0\x01\n\nSyncStatus\x12$\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x16.agent.SyncStatus.Code\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x17\n\x0freconcile_paths\x18\x03 \x03(\t\"B\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x16\n\x12RECONCILE_REQUIRED\x10\x02\x12\x0f\n\x0bIN_PROGRESS\x10\x03\x32\xe9\x01\n\x11\x41gentOrchestrator\x12L\n\x11SyncConfiguration\x12\x1a.agent.RegistrationRequest\x1a\x1b.agent.RegistrationResponse\x12\x44\n\nTaskStream\x12\x18.agent.ClientTaskMessage\x1a\x18.agent.ServerTaskMessage(\x01\x30\x01\x12@\n\x0cReportHealth\x12\x10.agent.Heartbeat\x1a\x1a.agent.HealthCheckResponse(\x01\x30\x01\x62\x06proto3') +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x0b\x61gent.proto\x12\x05\x61gent\"\xde\x01\n\x13RegistrationRequest\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x0f\n\x07version\x18\x02 \x01(\t\x12\x12\n\nauth_token\x18\x03 \x01(\t\x12\x18\n\x10node_description\x18\x04 \x01(\t\x12\x42\n\x0c\x63\x61pabilities\x18\x05 \x03(\x0b\x32,.agent.RegistrationRequest.CapabilitiesEntry\x1a\x33\n\x11\x43\x61pabilitiesEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xe0\x01\n\rSandboxPolicy\x12\'\n\x04mode\x18\x01 \x01(\x0e\x32\x19.agent.SandboxPolicy.Mode\x12\x18\n\x10\x61llowed_commands\x18\x02 \x03(\t\x12\x17\n\x0f\x64\x65nied_commands\x18\x03 \x03(\t\x12\x1a\n\x12sensitive_commands\x18\x04 \x03(\t\x12\x18\n\x10working_dir_jail\x18\x05 \x01(\t\x12\x19\n\x11skill_config_json\x18\x06 \x01(\t\"\"\n\x04Mode\x12\n\n\x06STRICT\x10\x00\x12\x0e\n\nPERMISSIVE\x10\x01\"x\n\x14RegistrationResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x15\n\rerror_message\x18\x02 \x01(\t\x12\x12\n\nsession_id\x18\x03 \x01(\t\x12$\n\x06policy\x18\x04 \x01(\x0b\x32\x14.agent.SandboxPolicy\"\xfb\x01\n\x11\x43lientTaskMessage\x12,\n\rtask_response\x18\x01 \x01(\x0b\x32\x13.agent.TaskResponseH\x00\x12-\n\ntask_claim\x18\x02 \x01(\x0b\x32\x17.agent.TaskClaimRequestH\x00\x12\'\n\x08\x61nnounce\x18\x04 \x01(\x0b\x32\x13.agent.NodeAnnounceH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12(\n\x0bskill_event\x18\x06 \x01(\x0b\x32\x11.agent.SkillEventH\x00\x42\t\n\x07payload\"y\n\nSkillEvent\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12\x0f\n\x07task_id\x18\x02 \x01(\t\x12\x16\n\x0cterminal_out\x18\x03 \x01(\tH\x00\x12\x10\n\x06prompt\x18\x04 \x01(\tH\x00\x12\x14\n\nkeep_alive\x18\x05 \x01(\x08H\x00\x42\x06\n\x04\x64\x61ta\"\x1f\n\x0cNodeAnnounce\x12\x0f\n\x07node_id\x18\x01 \x01(\t\"\xcf\x02\n\x11ServerTaskMessage\x12*\n\x0ctask_request\x18\x01 \x01(\x0b\x32\x12.agent.TaskRequestH\x00\x12\x31\n\x10work_pool_update\x18\x02 \x01(\x0b\x32\x15.agent.WorkPoolUpdateH\x00\x12\x30\n\x0c\x63laim_status\x18\x03 \x01(\x0b\x32\x18.agent.TaskClaimResponseH\x00\x12/\n\x0btask_cancel\x18\x04 \x01(\x0b\x32\x18.agent.TaskCancelRequestH\x00\x12+\n\tfile_sync\x18\x05 \x01(\x0b\x32\x16.agent.FileSyncMessageH\x00\x12-\n\rpolicy_update\x18\x06 \x01(\x0b\x32\x14.agent.SandboxPolicyH\x00\x12\x11\n\tsignature\x18\x07 \x01(\tB\t\n\x07payload\"$\n\x11TaskCancelRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\"\xa1\x01\n\x0bTaskRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x11\n\ttask_type\x18\x02 \x01(\t\x12\x16\n\x0cpayload_json\x18\x03 \x01(\tH\x00\x12\x12\n\ntimeout_ms\x18\x04 \x01(\x05\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x11\n\tsignature\x18\x06 \x01(\t\x12\x12\n\nsession_id\x18\x08 \x01(\tB\t\n\x07payload\"\xa4\x02\n\x0cTaskResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12*\n\x06status\x18\x02 \x01(\x0e\x32\x1a.agent.TaskResponse.Status\x12\x0e\n\x06stdout\x18\x03 \x01(\t\x12\x0e\n\x06stderr\x18\x04 \x01(\t\x12\x10\n\x08trace_id\x18\x05 \x01(\t\x12\x35\n\tartifacts\x18\x06 \x03(\x0b\x32\".agent.TaskResponse.ArtifactsEntry\x1a\x30\n\x0e\x41rtifactsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c:\x02\x38\x01\"<\n\x06Status\x12\x0b\n\x07SUCCESS\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x0b\n\x07TIMEOUT\x10\x02\x12\r\n\tCANCELLED\x10\x03\",\n\x0eWorkPoolUpdate\x12\x1a\n\x12\x61vailable_task_ids\x18\x01 \x03(\t\"4\n\x10TaskClaimRequest\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07node_id\x18\x02 \x01(\t\"E\n\x11TaskClaimResponse\x12\x0f\n\x07task_id\x18\x01 \x01(\t\x12\x0f\n\x07granted\x18\x02 \x01(\x08\x12\x0e\n\x06reason\x18\x03 \x01(\t\"\xe6\x02\n\tHeartbeat\x12\x0f\n\x07node_id\x18\x01 \x01(\t\x12\x19\n\x11\x63pu_usage_percent\x18\x02 \x01(\x02\x12\x1c\n\x14memory_usage_percent\x18\x03 \x01(\x02\x12\x1b\n\x13\x61\x63tive_worker_count\x18\x04 \x01(\x05\x12\x1b\n\x13max_worker_capacity\x18\x05 \x01(\x05\x12\x16\n\x0estatus_message\x18\x06 \x01(\t\x12\x18\n\x10running_task_ids\x18\x07 \x03(\t\x12\x11\n\tcpu_count\x18\x08 \x01(\x05\x12\x16\n\x0ememory_used_gb\x18\t \x01(\x02\x12\x17\n\x0fmemory_total_gb\x18\n \x01(\x02\x12\x1a\n\x12\x63pu_usage_per_core\x18\x0b \x03(\x02\x12\x14\n\x0c\x63pu_freq_mhz\x18\x0c \x01(\x02\x12\x1b\n\x13memory_available_gb\x18\r \x01(\x02\x12\x10\n\x08load_avg\x18\x0e \x03(\x02\"-\n\x13HealthCheckResponse\x12\x16\n\x0eserver_time_ms\x18\x01 \x01(\x03\"\xe4\x01\n\x0f\x46ileSyncMessage\x12\x12\n\nsession_id\x18\x01 \x01(\t\x12,\n\x08manifest\x18\x02 \x01(\x0b\x32\x18.agent.DirectoryManifestH\x00\x12\'\n\tfile_data\x18\x03 \x01(\x0b\x32\x12.agent.FilePayloadH\x00\x12#\n\x06status\x18\x04 \x01(\x0b\x32\x11.agent.SyncStatusH\x00\x12%\n\x07\x63ontrol\x18\x05 \x01(\x0b\x32\x12.agent.SyncControlH\x00\x12\x0f\n\x07task_id\x18\x06 \x01(\tB\t\n\x07payload\"\xab\x02\n\x0bSyncControl\x12)\n\x06\x61\x63tion\x18\x01 \x01(\x0e\x32\x19.agent.SyncControl.Action\x12\x0c\n\x04path\x18\x02 \x01(\t\x12\x15\n\rrequest_paths\x18\x03 \x03(\t\x12\x0f\n\x07\x63ontent\x18\x04 \x01(\x0c\x12\x0e\n\x06is_dir\x18\x05 \x01(\x08\"\xaa\x01\n\x06\x41\x63tion\x12\x12\n\x0eSTART_WATCHING\x10\x00\x12\x11\n\rSTOP_WATCHING\x10\x01\x12\x08\n\x04LOCK\x10\x02\x12\n\n\x06UNLOCK\x10\x03\x12\x14\n\x10REFRESH_MANIFEST\x10\x04\x12\n\n\x06RESYNC\x10\x05\x12\x08\n\x04LIST\x10\x06\x12\x08\n\x04READ\x10\x07\x12\t\n\x05WRITE\x10\x08\x12\n\n\x06\x44\x45LETE\x10\t\x12\t\n\x05PURGE\x10\n\x12\x0b\n\x07\x43LEANUP\x10\x0b\"m\n\x11\x44irectoryManifest\x12\x11\n\troot_path\x18\x01 \x01(\t\x12\x1e\n\x05\x66iles\x18\x02 \x03(\x0b\x32\x0f.agent.FileInfo\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\"D\n\x08\x46ileInfo\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\x0c\n\x04size\x18\x02 \x01(\x03\x12\x0c\n\x04hash\x18\x03 \x01(\t\x12\x0e\n\x06is_dir\x18\x04 \x01(\x08\"\xad\x01\n\x0b\x46ilePayload\x12\x0c\n\x04path\x18\x01 \x01(\t\x12\r\n\x05\x63hunk\x18\x02 \x01(\x0c\x12\x13\n\x0b\x63hunk_index\x18\x03 \x01(\x05\x12\x10\n\x08is_final\x18\x04 \x01(\x08\x12\x0c\n\x04hash\x18\x05 \x01(\t\x12\x0e\n\x06offset\x18\x06 \x01(\x03\x12\x12\n\ncompressed\x18\x07 \x01(\x08\x12\x14\n\x0ctotal_chunks\x18\x08 \x01(\x05\x12\x12\n\ntotal_size\x18\t \x01(\x03\"\xa0\x01\n\nSyncStatus\x12$\n\x04\x63ode\x18\x01 \x01(\x0e\x32\x16.agent.SyncStatus.Code\x12\x0f\n\x07message\x18\x02 \x01(\t\x12\x17\n\x0freconcile_paths\x18\x03 \x03(\t\"B\n\x04\x43ode\x12\x06\n\x02OK\x10\x00\x12\t\n\x05\x45RROR\x10\x01\x12\x16\n\x12RECONCILE_REQUIRED\x10\x02\x12\x0f\n\x0bIN_PROGRESS\x10\x03\x32\xe9\x01\n\x11\x41gentOrchestrator\x12L\n\x11SyncConfiguration\x12\x1a.agent.RegistrationRequest\x1a\x1b.agent.RegistrationResponse\x12\x44\n\nTaskStream\x12\x18.agent.ClientTaskMessage\x1a\x18.agent.ServerTaskMessage(\x01\x30\x01\x12@\n\x0cReportHealth\x12\x10.agent.Heartbeat\x1a\x1a.agent.HealthCheckResponse(\x01\x30\x01\x62\x06proto3') _globals = globals() _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'app.protos.agent_pb2', _globals) -if _descriptor._USE_C_DESCRIPTORS == False: - DESCRIPTOR._options = None - _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._options = None +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'agent_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._loaded_options = None _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_options = b'8\001' - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._options = None + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._loaded_options = None _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_options = b'8\001' - _globals['_REGISTRATIONREQUEST']._serialized_start=34 - _globals['_REGISTRATIONREQUEST']._serialized_end=256 - _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_start=205 - _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_end=256 - _globals['_SANDBOXPOLICY']._serialized_start=259 - _globals['_SANDBOXPOLICY']._serialized_end=483 - _globals['_SANDBOXPOLICY_MODE']._serialized_start=449 - _globals['_SANDBOXPOLICY_MODE']._serialized_end=483 - _globals['_REGISTRATIONRESPONSE']._serialized_start=485 - _globals['_REGISTRATIONRESPONSE']._serialized_end=605 - _globals['_CLIENTTASKMESSAGE']._serialized_start=608 - _globals['_CLIENTTASKMESSAGE']._serialized_end=859 - _globals['_SKILLEVENT']._serialized_start=861 - _globals['_SKILLEVENT']._serialized_end=982 - _globals['_NODEANNOUNCE']._serialized_start=984 - _globals['_NODEANNOUNCE']._serialized_end=1015 - _globals['_SERVERTASKMESSAGE']._serialized_start=1018 - _globals['_SERVERTASKMESSAGE']._serialized_end=1334 - _globals['_TASKCANCELREQUEST']._serialized_start=1336 - _globals['_TASKCANCELREQUEST']._serialized_end=1372 - _globals['_TASKREQUEST']._serialized_start=1375 - _globals['_TASKREQUEST']._serialized_end=1536 - _globals['_TASKRESPONSE']._serialized_start=1539 - _globals['_TASKRESPONSE']._serialized_end=1831 - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_start=1721 - _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_end=1769 - _globals['_TASKRESPONSE_STATUS']._serialized_start=1771 - _globals['_TASKRESPONSE_STATUS']._serialized_end=1831 - _globals['_WORKPOOLUPDATE']._serialized_start=1833 - _globals['_WORKPOOLUPDATE']._serialized_end=1877 - _globals['_TASKCLAIMREQUEST']._serialized_start=1879 - _globals['_TASKCLAIMREQUEST']._serialized_end=1931 - _globals['_TASKCLAIMRESPONSE']._serialized_start=1933 - _globals['_TASKCLAIMRESPONSE']._serialized_end=2002 - _globals['_HEARTBEAT']._serialized_start=2005 - _globals['_HEARTBEAT']._serialized_end=2363 - _globals['_HEALTHCHECKRESPONSE']._serialized_start=2365 - _globals['_HEALTHCHECKRESPONSE']._serialized_end=2410 - _globals['_FILESYNCMESSAGE']._serialized_start=2413 - _globals['_FILESYNCMESSAGE']._serialized_end=2641 - _globals['_SYNCCONTROL']._serialized_start=2644 - _globals['_SYNCCONTROL']._serialized_end=2943 - _globals['_SYNCCONTROL_ACTION']._serialized_start=2773 - _globals['_SYNCCONTROL_ACTION']._serialized_end=2943 - _globals['_DIRECTORYMANIFEST']._serialized_start=2945 - _globals['_DIRECTORYMANIFEST']._serialized_end=3015 - _globals['_FILEINFO']._serialized_start=3017 - _globals['_FILEINFO']._serialized_end=3085 - _globals['_FILEPAYLOAD']._serialized_start=3088 - _globals['_FILEPAYLOAD']._serialized_end=3261 - _globals['_SYNCSTATUS']._serialized_start=3264 - _globals['_SYNCSTATUS']._serialized_end=3424 - _globals['_SYNCSTATUS_CODE']._serialized_start=3358 - _globals['_SYNCSTATUS_CODE']._serialized_end=3424 - _globals['_AGENTORCHESTRATOR']._serialized_start=3427 - _globals['_AGENTORCHESTRATOR']._serialized_end=3660 + _globals['_REGISTRATIONREQUEST']._serialized_start=23 + _globals['_REGISTRATIONREQUEST']._serialized_end=245 + _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_start=194 + _globals['_REGISTRATIONREQUEST_CAPABILITIESENTRY']._serialized_end=245 + _globals['_SANDBOXPOLICY']._serialized_start=248 + _globals['_SANDBOXPOLICY']._serialized_end=472 + _globals['_SANDBOXPOLICY_MODE']._serialized_start=438 + _globals['_SANDBOXPOLICY_MODE']._serialized_end=472 + _globals['_REGISTRATIONRESPONSE']._serialized_start=474 + _globals['_REGISTRATIONRESPONSE']._serialized_end=594 + _globals['_CLIENTTASKMESSAGE']._serialized_start=597 + _globals['_CLIENTTASKMESSAGE']._serialized_end=848 + _globals['_SKILLEVENT']._serialized_start=850 + _globals['_SKILLEVENT']._serialized_end=971 + _globals['_NODEANNOUNCE']._serialized_start=973 + _globals['_NODEANNOUNCE']._serialized_end=1004 + _globals['_SERVERTASKMESSAGE']._serialized_start=1007 + _globals['_SERVERTASKMESSAGE']._serialized_end=1342 + _globals['_TASKCANCELREQUEST']._serialized_start=1344 + _globals['_TASKCANCELREQUEST']._serialized_end=1380 + _globals['_TASKREQUEST']._serialized_start=1383 + _globals['_TASKREQUEST']._serialized_end=1544 + _globals['_TASKRESPONSE']._serialized_start=1547 + _globals['_TASKRESPONSE']._serialized_end=1839 + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_start=1729 + _globals['_TASKRESPONSE_ARTIFACTSENTRY']._serialized_end=1777 + _globals['_TASKRESPONSE_STATUS']._serialized_start=1779 + _globals['_TASKRESPONSE_STATUS']._serialized_end=1839 + _globals['_WORKPOOLUPDATE']._serialized_start=1841 + _globals['_WORKPOOLUPDATE']._serialized_end=1885 + _globals['_TASKCLAIMREQUEST']._serialized_start=1887 + _globals['_TASKCLAIMREQUEST']._serialized_end=1939 + _globals['_TASKCLAIMRESPONSE']._serialized_start=1941 + _globals['_TASKCLAIMRESPONSE']._serialized_end=2010 + _globals['_HEARTBEAT']._serialized_start=2013 + _globals['_HEARTBEAT']._serialized_end=2371 + _globals['_HEALTHCHECKRESPONSE']._serialized_start=2373 + _globals['_HEALTHCHECKRESPONSE']._serialized_end=2418 + _globals['_FILESYNCMESSAGE']._serialized_start=2421 + _globals['_FILESYNCMESSAGE']._serialized_end=2649 + _globals['_SYNCCONTROL']._serialized_start=2652 + _globals['_SYNCCONTROL']._serialized_end=2951 + _globals['_SYNCCONTROL_ACTION']._serialized_start=2781 + _globals['_SYNCCONTROL_ACTION']._serialized_end=2951 + _globals['_DIRECTORYMANIFEST']._serialized_start=2953 + _globals['_DIRECTORYMANIFEST']._serialized_end=3062 + _globals['_FILEINFO']._serialized_start=3064 + _globals['_FILEINFO']._serialized_end=3132 + _globals['_FILEPAYLOAD']._serialized_start=3135 + _globals['_FILEPAYLOAD']._serialized_end=3308 + _globals['_SYNCSTATUS']._serialized_start=3311 + _globals['_SYNCSTATUS']._serialized_end=3471 + _globals['_SYNCSTATUS_CODE']._serialized_start=3405 + _globals['_SYNCSTATUS_CODE']._serialized_end=3471 + _globals['_AGENTORCHESTRATOR']._serialized_start=3474 + _globals['_AGENTORCHESTRATOR']._serialized_end=3707 # @@protoc_insertion_point(module_scope) diff --git a/ai-hub/app/protos/agent_pb2_grpc.py b/ai-hub/app/protos/agent_pb2_grpc.py index 90b55ee..e972a62 100644 --- a/ai-hub/app/protos/agent_pb2_grpc.py +++ b/ai-hub/app/protos/agent_pb2_grpc.py @@ -1,8 +1,28 @@ # Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! """Client and server classes corresponding to protobuf-defined services.""" import grpc +import warnings -from app.protos import agent_pb2 as app_dot_protos_dot_agent__pb2 +import agent_pb2 as agent__pb2 + +GRPC_GENERATED_VERSION = '1.71.2' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in agent_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) class AgentOrchestratorStub(object): @@ -17,19 +37,19 @@ """ self.SyncConfiguration = channel.unary_unary( '/agent.AgentOrchestrator/SyncConfiguration', - request_serializer=app_dot_protos_dot_agent__pb2.RegistrationRequest.SerializeToString, - response_deserializer=app_dot_protos_dot_agent__pb2.RegistrationResponse.FromString, - ) + request_serializer=agent__pb2.RegistrationRequest.SerializeToString, + response_deserializer=agent__pb2.RegistrationResponse.FromString, + _registered_method=True) self.TaskStream = channel.stream_stream( '/agent.AgentOrchestrator/TaskStream', - request_serializer=app_dot_protos_dot_agent__pb2.ClientTaskMessage.SerializeToString, - response_deserializer=app_dot_protos_dot_agent__pb2.ServerTaskMessage.FromString, - ) + request_serializer=agent__pb2.ClientTaskMessage.SerializeToString, + response_deserializer=agent__pb2.ServerTaskMessage.FromString, + _registered_method=True) self.ReportHealth = channel.stream_stream( '/agent.AgentOrchestrator/ReportHealth', - request_serializer=app_dot_protos_dot_agent__pb2.Heartbeat.SerializeToString, - response_deserializer=app_dot_protos_dot_agent__pb2.HealthCheckResponse.FromString, - ) + request_serializer=agent__pb2.Heartbeat.SerializeToString, + response_deserializer=agent__pb2.HealthCheckResponse.FromString, + _registered_method=True) class AgentOrchestratorServicer(object): @@ -62,23 +82,24 @@ rpc_method_handlers = { 'SyncConfiguration': grpc.unary_unary_rpc_method_handler( servicer.SyncConfiguration, - request_deserializer=app_dot_protos_dot_agent__pb2.RegistrationRequest.FromString, - response_serializer=app_dot_protos_dot_agent__pb2.RegistrationResponse.SerializeToString, + request_deserializer=agent__pb2.RegistrationRequest.FromString, + response_serializer=agent__pb2.RegistrationResponse.SerializeToString, ), 'TaskStream': grpc.stream_stream_rpc_method_handler( servicer.TaskStream, - request_deserializer=app_dot_protos_dot_agent__pb2.ClientTaskMessage.FromString, - response_serializer=app_dot_protos_dot_agent__pb2.ServerTaskMessage.SerializeToString, + request_deserializer=agent__pb2.ClientTaskMessage.FromString, + response_serializer=agent__pb2.ServerTaskMessage.SerializeToString, ), 'ReportHealth': grpc.stream_stream_rpc_method_handler( servicer.ReportHealth, - request_deserializer=app_dot_protos_dot_agent__pb2.Heartbeat.FromString, - response_serializer=app_dot_protos_dot_agent__pb2.HealthCheckResponse.SerializeToString, + request_deserializer=agent__pb2.Heartbeat.FromString, + response_serializer=agent__pb2.HealthCheckResponse.SerializeToString, ), } generic_handler = grpc.method_handlers_generic_handler( 'agent.AgentOrchestrator', rpc_method_handlers) server.add_generic_rpc_handlers((generic_handler,)) + server.add_registered_method_handlers('agent.AgentOrchestrator', rpc_method_handlers) # This class is part of an EXPERIMENTAL API. @@ -97,11 +118,21 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.unary_unary(request, target, '/agent.AgentOrchestrator/SyncConfiguration', - app_dot_protos_dot_agent__pb2.RegistrationRequest.SerializeToString, - app_dot_protos_dot_agent__pb2.RegistrationResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.unary_unary( + request, + target, + '/agent.AgentOrchestrator/SyncConfiguration', + agent__pb2.RegistrationRequest.SerializeToString, + agent__pb2.RegistrationResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def TaskStream(request_iterator, @@ -114,11 +145,21 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/agent.AgentOrchestrator/TaskStream', - app_dot_protos_dot_agent__pb2.ClientTaskMessage.SerializeToString, - app_dot_protos_dot_agent__pb2.ServerTaskMessage.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.stream_stream( + request_iterator, + target, + '/agent.AgentOrchestrator/TaskStream', + agent__pb2.ClientTaskMessage.SerializeToString, + agent__pb2.ServerTaskMessage.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) @staticmethod def ReportHealth(request_iterator, @@ -131,8 +172,18 @@ wait_for_ready=None, timeout=None, metadata=None): - return grpc.experimental.stream_stream(request_iterator, target, '/agent.AgentOrchestrator/ReportHealth', - app_dot_protos_dot_agent__pb2.Heartbeat.SerializeToString, - app_dot_protos_dot_agent__pb2.HealthCheckResponse.FromString, - options, channel_credentials, - insecure, call_credentials, compression, wait_for_ready, timeout, metadata) + return grpc.experimental.stream_stream( + request_iterator, + target, + '/agent.AgentOrchestrator/ReportHealth', + agent__pb2.Heartbeat.SerializeToString, + agent__pb2.HealthCheckResponse.FromString, + options, + channel_credentials, + insecure, + call_credentials, + compression, + wait_for_ready, + timeout, + metadata, + _registered_method=True) diff --git a/ai-hub/tests/core/grpc/test_journal.py b/ai-hub/tests/core/grpc/test_journal.py new file mode 100644 index 0000000..afbbdd5 --- /dev/null +++ b/ai-hub/tests/core/grpc/test_journal.py @@ -0,0 +1,68 @@ +import pytest +import threading +from unittest.mock import MagicMock, patch +from app.core.grpc.core.journal import TaskJournal + +@pytest.fixture +def mock_settings(): + with patch('app.core.grpc.core.journal.settings') as mock: + mock.STREAM_HEAD_CHARS = 10 + mock.STREAM_TAIL_CHARS = 10 + mock.THOUGHT_HEAD_COUNT = 2 + mock.THOUGHT_TAIL_COUNT = 2 + yield mock + +@pytest.fixture +def journal(mock_settings): + # Prevent cleanup thread from starting + with patch('threading.Thread'): + return TaskJournal() + +def test_stream_limits_from_config(journal): + assert journal.STREAM_HEAD_CHARS == 10 + assert journal.STREAM_TAIL_CHARS == 10 + assert journal.STREAM_MAX_CHARS == 20 + +def test_trim_stream(journal): + journal.STREAM_HEAD_CHARS = 5 + journal.STREAM_TAIL_CHARS = 5 + journal.STREAM_MAX_CHARS = 10 + + # Within limit + buf = "abc" + chunk = "def" + result = journal._trim_stream(buf, chunk) + assert result == "abcdef" + + # Over limit + buf = "123456" + chunk = "789012" # Total 12 + result = journal._trim_stream(buf, chunk) + # Head 5: "12345" + # Tail 5: "89012" + # Omitted: 12 - 5 - 5 = 2 + assert result.startswith("12345") + assert result.endswith("89012") + assert "2 bytes omitted" in result + +def test_sharded_locking_structure(journal): + assert journal.NUM_SHARDS == 16 + assert len(journal.shards) == 16 + for shard in journal.shards: + assert "tasks" in shard + assert "lock" in shard + assert hasattr(shard["lock"], "acquire") + +def test_get_shard(journal): + shard1 = journal._get_shard("task1") + assert shard1 in journal.shards + +def test_register_acquires_lock(journal): + shard = journal._get_shard("task1") + mock_lock = MagicMock() + shard["lock"] = mock_lock + + journal.register("task1") + + mock_lock.__enter__.assert_called_once() + mock_lock.__exit__.assert_called_once()