diff --git a/ai-hub/app/api/routes/agents.py b/ai-hub/app/api/routes/agents.py index 5192d93..b7a454e 100644 --- a/ai-hub/app/api/routes/agents.py +++ b/ai-hub/app/api/routes/agents.py @@ -11,6 +11,8 @@ ) import uuid import json +import os +import logging from app.core.orchestration.agent_loop import AgentExecutor from sqlalchemy.orm import joinedload @@ -18,9 +20,64 @@ def create_agents_router(services: ServiceContainer) -> APIRouter: router = APIRouter() + def _workspace_id_from_jail(jail_path: str | None, fallback_session_id: int | None = None) -> str: + """Derive a stable workspace ID from jail path when possible.""" + if jail_path: + normalized = jail_path.rstrip("/") + base = os.path.basename(normalized) + if base: + return base + if fallback_session_id is not None: + return f"session-{fallback_session_id}" + return f"agent-{uuid.uuid4().hex[:8]}" + + def _ensure_agent_workspace_binding(instance: AgentInstance, db: Session): + """ + Keep session sync workspace and agent jail aligned. + This heals legacy records where sync_workspace_id is null/mismatched. + """ + if not instance or not instance.session: + return + + desired_workspace_id = _workspace_id_from_jail(instance.current_workspace_jail, instance.session_id) + desired_jail = f"/tmp/cortex/{desired_workspace_id}/" + + changed = False + if instance.session.sync_workspace_id != desired_workspace_id: + instance.session.sync_workspace_id = desired_workspace_id + changed = True + + if instance.current_workspace_jail != desired_jail: + instance.current_workspace_jail = desired_jail + changed = True + + if changed: + db.flush() + try: + orchestrator = getattr(services, "orchestrator", None) + if orchestrator and instance.mesh_node_id: + orchestrator.assistant.push_workspace(instance.mesh_node_id, desired_workspace_id) + orchestrator.assistant.control_sync(instance.mesh_node_id, desired_workspace_id, action="START") + orchestrator.assistant.control_sync(instance.mesh_node_id, desired_workspace_id, action="UNLOCK") + except Exception as e: + logging.error(f"Failed to heal workspace binding for agent {instance.id}: {e}") + @router.get("", response_model=List[AgentInstanceResponse]) def get_agents(db: Session = Depends(get_db)): - return db.query(AgentInstance).options(joinedload(AgentInstance.template)).all() + agents = db.query(AgentInstance).options(joinedload(AgentInstance.template), joinedload(AgentInstance.session)).all() + changed = False + for instance in agents: + before_sync = instance.session.sync_workspace_id if instance.session else None + before_jail = instance.current_workspace_jail + _ensure_agent_workspace_binding(instance, db) + after_sync = instance.session.sync_workspace_id if instance.session else None + after_jail = instance.current_workspace_jail + if before_sync != after_sync or before_jail != after_jail: + changed = True + + if changed: + db.commit() + return agents @router.post("/templates", response_model=AgentTemplateResponse) def create_template(request: AgentTemplateCreate, db: Session = Depends(get_db)): @@ -190,13 +247,30 @@ provider_name=resolved_provider, feature_name="agent_harness", is_locked=True, - system_prompt_override=request.system_prompt + system_prompt_override=request.system_prompt, + attached_node_ids=[request.mesh_node_id] if getattr(request, "mesh_node_id", None) else [] ) db.add(new_session) db.flush() + workspace_id = f"agent_{template.id[:8]}" + workspace_jail = f"/tmp/cortex/{workspace_id}/" + new_session.sync_workspace_id = workspace_id + db.flush() + + # 2.5: Inject node into Orchestrator to ensure mirror works locally & remotely + try: + orchestrator = getattr(services, "orchestrator", None) + if orchestrator and request.mesh_node_id: + # Same logic as session attach_nodes config.source="empty" + orchestrator.assistant.push_workspace(request.mesh_node_id, new_session.sync_workspace_id) + orchestrator.assistant.control_sync(request.mesh_node_id, new_session.sync_workspace_id, action="START") + orchestrator.assistant.control_sync(request.mesh_node_id, new_session.sync_workspace_id, action="UNLOCK") + except Exception as e: + import logging + logging.error(f"Failed to bootstrap Orchestrator Sync for Agent Deploy: {e}") + # 3. Create AgentInstance - workspace_jail = f"/tmp/cortex/agent_{template.id[:8]}/" instance = AgentInstance( template_id=template.id, session_id=new_session.id, diff --git a/ai-hub/app/api/schemas.py b/ai-hub/app/api/schemas.py index 0a10522..3023c8c 100644 --- a/ai-hub/app/api/schemas.py +++ b/ai-hub/app/api/schemas.py @@ -568,6 +568,7 @@ id: str last_heartbeat: Optional[datetime] = None template: Optional[AgentTemplateResponse] = None + session: Optional[Session] = None model_config = ConfigDict(from_attributes=True) diff --git a/ai-hub/app/core/grpc/services/assistant.py b/ai-hub/app/core/grpc/services/assistant.py index 0f4aa86..a6b99a6 100644 --- a/ai-hub/app/core/grpc/services/assistant.py +++ b/ai-hub/app/core/grpc/services/assistant.py @@ -326,9 +326,14 @@ return {"files": files, "path": path} except Exception as e: logger.error(f"[📁📂] Local ls error for {session_id}/{path}: {e}") + elif node_id in ["hub", "server", "local"]: + return {"files": [], "path": path} node = self.registry.get_node(node_id) - if not node: return {"error": "Offline"} + if not node: + if node_id in ["hub", "server", "local"]: + return {"files": [], "path": path} + return {"error": "Offline"} tid = f"fs-ls-{int(time.time()*1000)}" event = self.journal.register(tid, node_id) @@ -383,7 +388,10 @@ return {"error": "File not found"} node = self.registry.get_node(node_id) - if not node: return {"error": "Offline"} + if not node: + if node_id in ["hub", "server", "local"]: + return {"error": "File not found"} + return {"error": "Offline"} # For 'cat', we might get multiple chunks, but TaskJournal fulfill # usually happens on the final chunk. We'll handle chunking in server. diff --git a/ai-hub/app/core/orchestration/memory.py b/ai-hub/app/core/orchestration/memory.py index 517e958..b25f9aa 100644 --- a/ai-hub/app/core/orchestration/memory.py +++ b/ai-hub/app/core/orchestration/memory.py @@ -54,7 +54,7 @@ if sync_workspace_id: mesh_info += f"\nActive Ghost Mirror Session ID: {sync_workspace_id}\n" - mesh_info += f"Instruction: When using `mesh_file_explorer` or `mesh_sync_control`, you MUST use this exactly as the `session_id` (or leave it blank/use 'current' which I will resolve for you).\n" + mesh_info += f"Instruction: Your `session_id` is automatically injected into tools like `mesh_file_explorer` or `mesh_sync_control`. You do not need to provide it.\n" # List of actual tools for strict enforcement available_skills = [t["function"]["name"] for t in (tools or [])] diff --git a/ai-hub/app/core/services/node_registry.py b/ai-hub/app/core/services/node_registry.py index 9a265d7..5a2e64a 100644 --- a/ai-hub/app/core/services/node_registry.py +++ b/ai-hub/app/core/services/node_registry.py @@ -184,8 +184,19 @@ record.last_seen_at = datetime.utcnow() db.commit() else: - # Node not pre-registered by admin — log warning but don't crash - print(f"[NodeRegistry] WARNING: Node '{node_id}' connected but has no DB record. Admin must register it first.") + # Auto-register node if missing + logger.info(f"[NodeRegistry] Node '{node_id}' connected but has no DB record. Auto-registering.") + new_node = AgentNode( + node_id=node_id, + display_name=node_id, + registered_by=user_id, + capabilities=metadata.get("caps", {}), + last_status="online", + last_seen_at=datetime.utcnow(), + is_active=True + ) + db.add(new_node) + db.commit() except Exception as e: print(f"[NodeRegistry] DB upsert failed for {node_id}: {e}") diff --git a/ai-hub/app/core/services/rag.py b/ai-hub/app/core/services/rag.py index cdce720..0d4bd93 100644 --- a/ai-hub/app/core/services/rag.py +++ b/ai-hub/app/core/services/rag.py @@ -200,7 +200,7 @@ mesh_context = mesh_context, db = db, user_id = user_id or session.user_id, - sync_workspace_id = session.sync_workspace_id, + sync_workspace_id = session.sync_workspace_id or str(session_id), session_id = session_id, feature_name = session.feature_name, prompt_slug = profile.default_prompt_slug, diff --git a/ai-hub/app/core/services/tool.py b/ai-hub/app/core/services/tool.py index a347cf8..2c41d99 100644 --- a/ai-hub/app/core/services/tool.py +++ b/ai-hub/app/core/services/tool.py @@ -177,7 +177,7 @@ if "node_id" not in parameters["properties"]: parameters["properties"]["node_id"] = { "type": "string", - "description": "Optional specific mesh node ID to execute this on. Leave empty to auto-use the session's first attached node." + "description": "Optional specific mesh node ID to execute this on. Leave empty to auto-use the session's first attached node. DO NOT invent or guess node IDs (e.g., node1), they must match the actual Node IDs in the context exactly." } tools.append({ @@ -195,6 +195,34 @@ """ Executes a registered skill. """ + # --- Node Auto-Resolution & Security Guard --- + node_id = arguments.get("node_id") + node_ids = arguments.get("node_ids") + if db and session_db_id: + session_meta = db.query(models.Session).filter(models.Session.id == session_db_id).first() + if session_meta: + attached = session_meta.attached_node_ids or [] + if not node_id and not node_ids and attached: + node_id = attached[0] + arguments["node_id"] = node_id + + allowed_ids = attached + ["hub", "server", "local"] + if node_id and node_id not in allowed_ids: + # Fuzzy match fallback (e.g. 'test node1' -> 'test-node-1') + fuzzy_nid = str(node_id).lower().replace(" ", "").replace("-", "").replace("_", "") + for aid in allowed_ids: + if aid.lower().replace("-", "").replace("_", "") == fuzzy_nid: + node_id = aid + arguments["node_id"] = aid + break + if node_id not in allowed_ids: + return {"success": False, "error": f"Node '{node_id}' is NOT attached or doesn't exist. Available nodes: {attached}"} + + if node_ids: + illegal = [nid for nid in node_ids if nid not in allowed_ids] + if illegal: + return {"success": False, "error": f"Nodes {illegal} are NOT attached to this session. Available nodes: {attached}"} + # 1. Try local/native skill first if tool_name in self._local_skills: skill = self._local_skills[tool_name] @@ -210,9 +238,9 @@ context = { "db": db, "user_id": user_id, - "session_id": session_id, - "node_id": arguments.get("node_id"), - "node_ids": arguments.get("node_ids"), + "session_id": session_id or "__fs_explorer__", + "node_id": node_id, + "node_ids": node_ids, "services": self._services, "orchestrator": orchestrator, "assistant": orchestrator.assistant if orchestrator else None, @@ -257,36 +285,9 @@ from app.core.services.sub_agent import SubAgent from app.core.providers.factory import get_llm_provider - # --- Programmatic Access Control (M3/M6) --- - # If targeting a mesh node, we MUST ensure it's actually attached to this session in the DB. - # This prevents AI from 'guessing' node IDs and executing on unauthorized infrastructure. + # Node IDs are already validated and resolved from step 0 in call_tool. node_id = args.get("node_id") node_ids = args.get("node_ids") - - if db and session_db_id: - session = db.query(models.Session).filter(models.Session.id == session_db_id).first() - if session: - attached = session.attached_node_ids or [] - - # Implicit fallback to first attached node if no target was specified - if not node_id and not node_ids and attached: - node_id = attached[0] - args["node_id"] = node_id - - # Allow virtual node IDs for system maintenance - allowed_ids = attached + ["hub", "server", "local"] - - # Check single node target - if node_id and node_id not in allowed_ids: - logger.warning(f"[Security] AI attempted to access unattached node '{node_id}' in session {session_db_id}") - return {"success": False, "error": f"Node '{node_id}' is NOT attached to this session. Access denied."} - - # Check swarm target - if node_ids: - illegal = [nid for nid in node_ids if nid not in allowed_ids] - if illegal: - logger.warning(f"[Security] AI attempted to access unattached nodes {illegal} in swarm call") - return {"success": False, "error": f"Nodes {illegal} are NOT attached to this session. Access denied."} # --- Standard Preparation --- llm_provider = None diff --git a/ai-hub/app/core/tools/definitions/mesh_file_explorer.py b/ai-hub/app/core/tools/definitions/mesh_file_explorer.py index 406ef24..90ec3f8 100644 --- a/ai-hub/app/core/tools/definitions/mesh_file_explorer.py +++ b/ai-hub/app/core/tools/definitions/mesh_file_explorer.py @@ -12,9 +12,8 @@ action = args.get("action") path = args.get("path") content = args.get("content", "") - - node_id = context.get("node_id") - resolved_sid = context.get("session_id") + node_id = args.get("node_id") or context.get("node_id") + resolved_sid = args.get("session_id") or context.get("session_id") assistant = context.get("assistant") services = context.get("services") @@ -47,7 +46,7 @@ return {"success": False, "error": "Ghost Mirror not available"} base = orchestrator.mirror.get_workspace_path(session_id) - target = os.path.normpath(os.path.join(base, str(path).lstrip("/"))) + target = os.path.normpath(os.path.join(base, str(path or "").lstrip("/"))) if not target.startswith(base): return {"success": False, "error": "Path traversal attempt blocked"} diff --git a/ai-hub/app/db/models/agent.py b/ai-hub/app/db/models/agent.py index e2779e9..150f185 100644 --- a/ai-hub/app/db/models/agent.py +++ b/ai-hub/app/db/models/agent.py @@ -27,7 +27,8 @@ last_heartbeat = Column(DateTime, default=datetime.datetime.utcnow) template = relationship("AgentTemplate", back_populates="instances") - triggers = relationship("AgentTrigger", back_populates="instance") + session = relationship("Session", primaryjoin="AgentInstance.session_id == Session.id") + triggers = relationship("AgentTrigger", back_populates="instance", cascade="all, delete-orphan") class AgentTrigger(Base): __tablename__ = 'agent_triggers' diff --git a/ai-hub/docs/api_reference/agent-nodes.md b/ai-hub/docs/api_reference/agent-nodes.md index 10db238..f08a24c 100644 --- a/ai-hub/docs/api_reference/agent-nodes.md +++ b/ai-hub/docs/api_reference/agent-nodes.md @@ -1,6 +1,6 @@ # API Reference: Agent Nodes -## POST `/api/v1/nodes/admin` +## POST `/nodes/admin` **Summary:** [Admin] Register New Node @@ -32,7 +32,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin?admin_id=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -40,7 +40,7 @@ --- -## GET `/api/v1/nodes/admin` +## GET `/nodes/admin` **Summary:** [Admin] List All Nodes @@ -63,13 +63,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin?admin_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/admin/{node_id}` +## GET `/nodes/admin/{node_id}` **Summary:** [Admin] Get Node Detail @@ -93,13 +93,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}?admin_id=' \ -H 'accept: application/json' ``` --- -## PATCH `/api/v1/nodes/admin/{node_id}` +## PATCH `/nodes/admin/{node_id}` **Summary:** [Admin] Update Node Config @@ -130,7 +130,7 @@ ```bash curl -X 'PATCH' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}?admin_id=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -138,7 +138,7 @@ --- -## DELETE `/api/v1/nodes/admin/{node_id}` +## DELETE `/nodes/admin/{node_id}` **Summary:** [Admin] Deregister Node @@ -162,13 +162,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}?admin_id=' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/nodes/admin/{node_id}/access` +## POST `/nodes/admin/{node_id}/access` **Summary:** [Admin] Grant Group Access @@ -199,7 +199,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}/access?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}/access?admin_id=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -207,7 +207,7 @@ --- -## DELETE `/api/v1/nodes/admin/{node_id}/access/{group_id}` +## DELETE `/nodes/admin/{node_id}/access/{group_id}` **Summary:** [Admin] Revoke Group Access @@ -232,13 +232,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}/access/{group_id}?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}/access/{group_id}?admin_id=' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/nodes/admin/mesh/reset` +## POST `/nodes/admin/mesh/reset` **Summary:** [Admin] Emergency Mesh Reset @@ -262,13 +262,13 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/mesh/reset?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/mesh/reset?admin_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/` +## GET `/nodes/` **Summary:** List Accessible Nodes @@ -292,13 +292,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/?user_id=' \ + 'http://localhost:8000/api/v1/nodes/?user_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/{node_id}/status` +## GET `/nodes/{node_id}/status` **Summary:** Quick Node Online Check @@ -322,14 +322,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/status' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/status' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## GET `/api/v1/nodes/{node_id}/terminal` +## GET `/nodes/{node_id}/terminal` **Summary:** Read Node Terminal History (AI Use Case) @@ -354,14 +354,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/terminal' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/terminal' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/nodes/{node_id}/dispatch` +## POST `/nodes/{node_id}/dispatch` **Summary:** Dispatch Task to Node @@ -393,7 +393,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/dispatch?user_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/dispatch?user_id=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -401,7 +401,7 @@ --- -## POST `/api/v1/nodes/{node_id}/cancel` +## POST `/nodes/{node_id}/cancel` **Summary:** Cancel/Interrupt Task on Node @@ -427,14 +427,14 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/cancel?task_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/cancel?task_id=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## PATCH `/api/v1/nodes/preferences` +## PATCH `/nodes/preferences` **Summary:** Update User Node Preferences @@ -465,7 +465,7 @@ ```bash curl -X 'PATCH' \ - 'http://localhost:8000/api/v1/api/v1/nodes/preferences?user_id=' \ + 'http://localhost:8000/api/v1/nodes/preferences?user_id=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -473,7 +473,7 @@ --- -## GET `/api/v1/nodes/preferences` +## GET `/nodes/preferences` **Summary:** Get User Node Preferences @@ -496,13 +496,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/preferences?user_id=' \ + 'http://localhost:8000/api/v1/nodes/preferences?user_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/admin/{node_id}/config.yaml` +## GET `/nodes/admin/{node_id}/config.yaml` **Summary:** [Admin] Download Node Config YAML @@ -527,13 +527,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}/config.yaml?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}/config.yaml?admin_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/provision/{node_id}` +## GET `/nodes/provision/{node_id}` **Summary:** Headless Provisioning Script @@ -560,13 +560,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/provision/{node_id}?token=' \ + 'http://localhost:8000/api/v1/nodes/provision/{node_id}?token=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/admin/{node_id}/download` +## GET `/nodes/admin/{node_id}/download` **Summary:** [Admin] Download Agent Node Bundle (ZIP) @@ -591,13 +591,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/admin/{node_id}/download?admin_id=' \ + 'http://localhost:8000/api/v1/nodes/admin/{node_id}/download?admin_id=' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/nodes/validate-token` +## POST `/nodes/validate-token` **Summary:** [Internal] Validate Node Invite Token @@ -629,13 +629,13 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/validate-token?token=&node_id=' \ + 'http://localhost:8000/api/v1/nodes/validate-token?token=&node_id=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/nodes/{node_id}/fs/ls` +## GET `/nodes/{node_id}/fs/ls` **Summary:** List Directory Content @@ -662,14 +662,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/ls?path=&session_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/ls?path=&session_id=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## GET `/api/v1/nodes/{node_id}/fs/cat` +## GET `/nodes/{node_id}/fs/cat` **Summary:** Read File Content @@ -695,14 +695,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/cat?path=&session_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/cat?path=&session_id=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/nodes/{node_id}/fs/touch` +## POST `/nodes/{node_id}/fs/touch` **Summary:** Create File or Directory @@ -733,7 +733,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/touch' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/touch' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -742,7 +742,7 @@ --- -## GET `/api/v1/nodes/{node_id}/fs/download` +## GET `/nodes/{node_id}/fs/download` **Summary:** Download File @@ -769,14 +769,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/download?path=&session_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/download?path=&session_id=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/nodes/{node_id}/fs/upload` +## POST `/nodes/{node_id}/fs/upload` **Summary:** Upload File @@ -796,7 +796,7 @@ **Required:** Yes - **Media Type:** `multipart/form-data` -- **Schema:** `Body_fs_upload_api_v1_nodes__node_id__fs_upload_post` (Define in Models) +- **Schema:** `Body_fs_upload_nodes__node_id__fs_upload_post` (Define in Models) #### Responses @@ -809,7 +809,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/upload?path=&session_id=' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/upload?path=&session_id=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: multipart/form-data' \ @@ -818,7 +818,7 @@ --- -## POST `/api/v1/nodes/{node_id}/fs/rm` +## POST `/nodes/{node_id}/fs/rm` **Summary:** Delete File/Directory @@ -849,7 +849,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/nodes/{node_id}/fs/rm' \ + 'http://localhost:8000/api/v1/nodes/{node_id}/fs/rm' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ diff --git a/ai-hub/docs/api_reference/agent-update.md b/ai-hub/docs/api_reference/agent-update.md index 678fe81..9c7a29c 100644 --- a/ai-hub/docs/api_reference/agent-update.md +++ b/ai-hub/docs/api_reference/agent-update.md @@ -1,6 +1,6 @@ # API Reference: Agent Update -## GET `/api/v1/agent/version` +## GET `/agent/version` **Summary:** Get current agent version @@ -17,13 +17,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agent/version' \ + 'http://localhost:8000/api/v1/agent/version' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/agent/download` +## GET `/agent/download` **Summary:** Download agent node tarball @@ -40,13 +40,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agent/download' \ + 'http://localhost:8000/api/v1/agent/download' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/agent/installer` +## GET `/agent/installer` **Summary:** Download bootstrap_installer.py @@ -63,7 +63,7 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agent/installer' \ + 'http://localhost:8000/api/v1/agent/installer' \ -H 'accept: application/json' ``` diff --git a/ai-hub/docs/api_reference/agents.md b/ai-hub/docs/api_reference/agents.md index 1453e28..7e6e5af 100644 --- a/ai-hub/docs/api_reference/agents.md +++ b/ai-hub/docs/api_reference/agents.md @@ -1,6 +1,6 @@ # API Reference: Agents -## GET `/api/v1/agents` +## GET `/agents` **Summary:** Get Agents @@ -16,13 +16,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agents' \ + 'http://localhost:8000/api/v1/agents' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/agents/templates` +## POST `/agents/templates` **Summary:** Create Template @@ -46,7 +46,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/agents/templates' \ + 'http://localhost:8000/api/v1/agents/templates' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -54,7 +54,7 @@ --- -## POST `/api/v1/agents/instances` +## POST `/agents/instances` **Summary:** Create Instance @@ -78,7 +78,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/agents/instances' \ + 'http://localhost:8000/api/v1/agents/instances' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -86,7 +86,7 @@ --- -## PATCH `/api/v1/agents/{id}/status` +## PATCH `/agents/{id}/status` **Summary:** Update Status @@ -116,7 +116,7 @@ ```bash curl -X 'PATCH' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/status' \ + 'http://localhost:8000/api/v1/agents/{id}/status' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -124,7 +124,7 @@ --- -## PATCH `/api/v1/agents/{id}/config` +## PATCH `/agents/{id}/config` **Summary:** Update Config @@ -154,7 +154,7 @@ ```bash curl -X 'PATCH' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/config' \ + 'http://localhost:8000/api/v1/agents/{id}/config' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -162,7 +162,7 @@ --- -## POST `/api/v1/agents/{id}/webhook` +## POST `/agents/{id}/webhook` **Summary:** Webhook Receiver @@ -192,7 +192,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/webhook?token=' \ + 'http://localhost:8000/api/v1/agents/{id}/webhook?token=' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -200,7 +200,7 @@ --- -## GET `/api/v1/agents/{id}/triggers` +## GET `/agents/{id}/triggers` **Summary:** Get Agent Triggers @@ -223,13 +223,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/triggers' \ + 'http://localhost:8000/api/v1/agents/{id}/triggers' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/agents/{id}/triggers` +## POST `/agents/{id}/triggers` **Summary:** Create Agent Trigger @@ -259,7 +259,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/triggers' \ + 'http://localhost:8000/api/v1/agents/{id}/triggers' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -267,7 +267,7 @@ --- -## DELETE `/api/v1/agents/triggers/{trigger_id}` +## DELETE `/agents/triggers/{trigger_id}` **Summary:** Delete Agent Trigger @@ -290,13 +290,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/agents/triggers/{trigger_id}' \ + 'http://localhost:8000/api/v1/agents/triggers/{trigger_id}' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/agents/{id}/telemetry` +## GET `/agents/{id}/telemetry` **Summary:** Get Telemetry @@ -319,13 +319,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/telemetry' \ + 'http://localhost:8000/api/v1/agents/{id}/telemetry' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/agents/{id}/dependencies` +## GET `/agents/{id}/dependencies` **Summary:** Get Dependencies @@ -348,13 +348,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}/dependencies' \ + 'http://localhost:8000/api/v1/agents/{id}/dependencies' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/agents/deploy` +## POST `/agents/deploy` **Summary:** Deploy Agent @@ -379,7 +379,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/agents/deploy' \ + 'http://localhost:8000/api/v1/agents/deploy' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -387,7 +387,7 @@ --- -## DELETE `/api/v1/agents/{id}` +## DELETE `/agents/{id}` **Summary:** Delete Agent @@ -410,7 +410,7 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/agents/{id}' \ + 'http://localhost:8000/api/v1/agents/{id}' \ -H 'accept: application/json' ``` diff --git a/ai-hub/docs/api_reference/documents.md b/ai-hub/docs/api_reference/documents.md index 1ed6ad0..015a72a 100644 --- a/ai-hub/docs/api_reference/documents.md +++ b/ai-hub/docs/api_reference/documents.md @@ -1,6 +1,6 @@ # API Reference: Documents -## GET `/api/v1/documents/` +## GET `/documents/` **Summary:** List All Documents @@ -16,13 +16,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/documents/' \ + 'http://localhost:8000/api/v1/documents/' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/documents/` +## POST `/documents/` **Summary:** Add a New Document @@ -46,7 +46,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/documents/' \ + 'http://localhost:8000/api/v1/documents/' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -54,7 +54,7 @@ --- -## DELETE `/api/v1/documents/{document_id}` +## DELETE `/documents/{document_id}` **Summary:** Delete a Document @@ -77,7 +77,7 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/documents/{document_id}' \ + 'http://localhost:8000/api/v1/documents/{document_id}' \ -H 'accept: application/json' ``` diff --git a/ai-hub/docs/api_reference/general.md b/ai-hub/docs/api_reference/general.md index 7979207..69c7cb1 100644 --- a/ai-hub/docs/api_reference/general.md +++ b/ai-hub/docs/api_reference/general.md @@ -1,6 +1,6 @@ # API Reference: General -## GET `/api/v1/` +## GET `/` **Summary:** Check Service Status @@ -16,13 +16,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/' \ + 'http://localhost:8000/api/v1/' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/status` +## GET `/status` **Summary:** Get Full System Status @@ -38,7 +38,7 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/status' \ + 'http://localhost:8000/api/v1/status' \ -H 'accept: application/json' ``` diff --git a/ai-hub/docs/api_reference/index.md b/ai-hub/docs/api_reference/index.md index 348baab..d98fe2c 100644 --- a/ai-hub/docs/api_reference/index.md +++ b/ai-hub/docs/api_reference/index.md @@ -11,6 +11,6 @@ - [Agent Nodes](./agent-nodes.md) - [Skills](./skills.md) - [Agent Update](./agent-update.md) -- [Api](./api.md) +- [Admin](./admin.md) - [Agents](./agents.md) - [Models (Schemas)](./models.md) diff --git a/ai-hub/docs/api_reference/models.md b/ai-hub/docs/api_reference/models.md index def9156..442c9b1 100644 --- a/ai-hub/docs/api_reference/models.md +++ b/ai-hub/docs/api_reference/models.md @@ -40,6 +40,7 @@ | `id*` | `string` | | | `last_heartbeat` | `anyOf` | | | `template` | `anyOf` | | +| `session` | `anyOf` | | ## `AgentInstanceStatusUpdate` @@ -192,7 +193,7 @@ |----------|------|-------------| | `allow_password_login` | `anyOf` | | -## `Body_fs_upload_api_v1_nodes__node_id__fs_upload_post` +## `Body_fs_upload_nodes__node_id__fs_upload_post` **Type:** `object` @@ -200,7 +201,7 @@ |----------|------|-------------| | `file*` | `string` | | -## `Body_import_user_config_yaml_api_v1_users_me_config_import_post` +## `Body_import_user_config_yaml_users_me_config_import_post` **Type:** `object` @@ -208,7 +209,7 @@ |----------|------|-------------| | `file*` | `string` | | -## `Body_transcribe_audio_to_text_api_v1_stt_transcribe_post` +## `Body_transcribe_audio_to_text_stt_transcribe_post` **Type:** `object` @@ -216,7 +217,7 @@ |----------|------|-------------| | `audio_file*` | `string` | | -## `Body_upload_message_audio_api_v1_sessions_messages__message_id__audio_post` +## `Body_upload_message_audio_sessions_messages__message_id__audio_post` **Type:** `object` diff --git a/ai-hub/docs/api_reference/sessions.md b/ai-hub/docs/api_reference/sessions.md index 9ad55af..1155d9c 100644 --- a/ai-hub/docs/api_reference/sessions.md +++ b/ai-hub/docs/api_reference/sessions.md @@ -1,6 +1,6 @@ # API Reference: Sessions -## POST `/api/v1/sessions/` +## POST `/sessions/` **Summary:** Create a New Chat Session @@ -24,7 +24,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/' \ + 'http://localhost:8000/api/v1/sessions/' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -32,7 +32,7 @@ --- -## GET `/api/v1/sessions/` +## GET `/sessions/` **Summary:** Get All Chat Sessions @@ -56,13 +56,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/?user_id=&feature_name=' \ + 'http://localhost:8000/api/v1/sessions/?user_id=&feature_name=' \ -H 'accept: application/json' ``` --- -## DELETE `/api/v1/sessions/` +## DELETE `/sessions/` **Summary:** Delete All Sessions for Feature @@ -86,13 +86,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/sessions/?user_id=&feature_name=' \ + 'http://localhost:8000/api/v1/sessions/?user_id=&feature_name=' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/sessions/{session_id}/chat` +## POST `/sessions/{session_id}/chat` **Summary:** Send a Message in a Session (Streaming) @@ -123,7 +123,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/chat' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/chat' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -131,7 +131,7 @@ --- -## GET `/api/v1/sessions/{session_id}/messages` +## GET `/sessions/{session_id}/messages` **Summary:** Get Session Chat History @@ -154,13 +154,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/messages' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/messages' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/sessions/{session_id}/clear-history` +## POST `/sessions/{session_id}/clear-history` **Summary:** Clear Chat History (Preserve Session) @@ -185,13 +185,13 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/clear-history' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/clear-history' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/sessions/{session_id}/tokens` +## GET `/sessions/{session_id}/tokens` **Summary:** Get Session Token Usage @@ -214,13 +214,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/tokens' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/tokens' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/sessions/{session_id}` +## GET `/sessions/{session_id}` **Summary:** Get a Single Session @@ -243,13 +243,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}' \ + 'http://localhost:8000/api/v1/sessions/{session_id}' \ -H 'accept: application/json' ``` --- -## PATCH `/api/v1/sessions/{session_id}` +## PATCH `/sessions/{session_id}` **Summary:** Update a Chat Session @@ -279,7 +279,7 @@ ```bash curl -X 'PATCH' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}' \ + 'http://localhost:8000/api/v1/sessions/{session_id}' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -287,7 +287,7 @@ --- -## DELETE `/api/v1/sessions/{session_id}` +## DELETE `/sessions/{session_id}` **Summary:** Delete a Chat Session @@ -310,13 +310,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}' \ + 'http://localhost:8000/api/v1/sessions/{session_id}' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/sessions/messages/{message_id}/audio` +## POST `/sessions/messages/{message_id}/audio` **Summary:** Upload audio for a specific message @@ -333,7 +333,7 @@ **Required:** Yes - **Media Type:** `multipart/form-data` -- **Schema:** `Body_upload_message_audio_api_v1_sessions_messages__message_id__audio_post` (Define in Models) +- **Schema:** `Body_upload_message_audio_sessions_messages__message_id__audio_post` (Define in Models) #### Responses @@ -346,7 +346,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/messages/{message_id}/audio' \ + 'http://localhost:8000/api/v1/sessions/messages/{message_id}/audio' \ -H 'accept: application/json' \ -H 'Content-Type: multipart/form-data' \ -F 'file=@/path/to/file' @@ -354,7 +354,7 @@ --- -## GET `/api/v1/sessions/messages/{message_id}/audio` +## GET `/sessions/messages/{message_id}/audio` **Summary:** Get audio for a specific message @@ -377,13 +377,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/messages/{message_id}/audio' \ + 'http://localhost:8000/api/v1/sessions/messages/{message_id}/audio' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/sessions/{session_id}/nodes` +## POST `/sessions/{session_id}/nodes` **Summary:** Attach Nodes to Session @@ -413,7 +413,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/nodes' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/nodes' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -421,7 +421,7 @@ --- -## GET `/api/v1/sessions/{session_id}/nodes` +## GET `/sessions/{session_id}/nodes` **Summary:** Get Session Node Status @@ -445,13 +445,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/nodes' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/nodes' \ -H 'accept: application/json' ``` --- -## DELETE `/api/v1/sessions/{session_id}/nodes/{node_id}` +## DELETE `/sessions/{session_id}/nodes/{node_id}` **Summary:** Detach Node from Session @@ -475,13 +475,13 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/nodes/{node_id}' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/nodes/{node_id}' \ -H 'accept: application/json' ``` --- -## POST `/api/v1/sessions/{session_id}/cancel` +## POST `/sessions/{session_id}/cancel` **Summary:** Cancel Running AI Task @@ -504,7 +504,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/sessions/{session_id}/cancel' \ + 'http://localhost:8000/api/v1/sessions/{session_id}/cancel' \ -H 'accept: application/json' ``` diff --git a/ai-hub/docs/api_reference/skills.md b/ai-hub/docs/api_reference/skills.md index c00a49c..5bd5862 100644 --- a/ai-hub/docs/api_reference/skills.md +++ b/ai-hub/docs/api_reference/skills.md @@ -1,6 +1,6 @@ # API Reference: Skills -## GET `/api/v1/skills/` +## GET `/skills/` **Summary:** List Skills @@ -24,14 +24,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/skills/?feature=' \ + 'http://localhost:8000/api/v1/skills/?feature=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/skills/` +## POST `/skills/` **Summary:** Create Skill @@ -61,7 +61,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/skills/' \ + 'http://localhost:8000/api/v1/skills/' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -70,7 +70,7 @@ --- -## PUT `/api/v1/skills/{skill_id}` +## PUT `/skills/{skill_id}` **Summary:** Update Skill @@ -101,7 +101,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}' \ + 'http://localhost:8000/api/v1/skills/{skill_id}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -110,7 +110,7 @@ --- -## DELETE `/api/v1/skills/{skill_id}` +## DELETE `/skills/{skill_id}` **Summary:** Delete Skill @@ -134,14 +134,14 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}' \ + 'http://localhost:8000/api/v1/skills/{skill_id}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## GET `/api/v1/skills/{skill_id}/files` +## GET `/skills/{skill_id}/files` **Summary:** List Skill Files @@ -165,14 +165,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}/files' \ + 'http://localhost:8000/api/v1/skills/{skill_id}/files' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## GET `/api/v1/skills/{skill_id}/files/{path}` +## GET `/skills/{skill_id}/files/{path}` **Summary:** Read Skill File @@ -197,14 +197,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}/files/{path}' \ + 'http://localhost:8000/api/v1/skills/{skill_id}/files/{path}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/skills/{skill_id}/files/{path}` +## POST `/skills/{skill_id}/files/{path}` **Summary:** Create Or Update Skill File @@ -236,7 +236,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}/files/{path}' \ + 'http://localhost:8000/api/v1/skills/{skill_id}/files/{path}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -245,7 +245,7 @@ --- -## DELETE `/api/v1/skills/{skill_id}/files/{path}` +## DELETE `/skills/{skill_id}/files/{path}` **Summary:** Delete Skill File @@ -270,7 +270,7 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/skills/{skill_id}/files/{path}' \ + 'http://localhost:8000/api/v1/skills/{skill_id}/files/{path}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` diff --git a/ai-hub/docs/api_reference/stt.md b/ai-hub/docs/api_reference/stt.md index c7e8404..85af7b8 100644 --- a/ai-hub/docs/api_reference/stt.md +++ b/ai-hub/docs/api_reference/stt.md @@ -1,6 +1,6 @@ # API Reference: STT -## POST `/api/v1/stt/transcribe` +## POST `/stt/transcribe` **Summary:** Transcribe audio to text @@ -21,7 +21,7 @@ **Required:** Yes - **Media Type:** `multipart/form-data` -- **Schema:** `Body_transcribe_audio_to_text_api_v1_stt_transcribe_post` (Define in Models) +- **Schema:** `Body_transcribe_audio_to_text_stt_transcribe_post` (Define in Models) #### Responses @@ -34,7 +34,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/stt/transcribe?provider_name=' \ + 'http://localhost:8000/api/v1/stt/transcribe?provider_name=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: multipart/form-data' \ diff --git a/ai-hub/docs/api_reference/tts.md b/ai-hub/docs/api_reference/tts.md index de890ce..d223116 100644 --- a/ai-hub/docs/api_reference/tts.md +++ b/ai-hub/docs/api_reference/tts.md @@ -1,6 +1,6 @@ # API Reference: TTS -## POST `/api/v1/speech` +## POST `/speech` **Summary:** Generate speech from text @@ -33,7 +33,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/speech?stream=&as_wav=&provider_name=' \ + 'http://localhost:8000/api/v1/speech?stream=&as_wav=&provider_name=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -42,7 +42,7 @@ --- -## GET `/api/v1/speech/voices` +## GET `/speech/voices` **Summary:** List available TTS voices @@ -67,7 +67,7 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/speech/voices?provider=&api_key=' \ + 'http://localhost:8000/api/v1/speech/voices?provider=&api_key=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` diff --git a/ai-hub/docs/api_reference/users.md b/ai-hub/docs/api_reference/users.md index 0bdddb8..deb8d8d 100644 --- a/ai-hub/docs/api_reference/users.md +++ b/ai-hub/docs/api_reference/users.md @@ -1,6 +1,6 @@ # API Reference: Users -## GET `/api/v1/users/login` +## GET `/users/login` **Summary:** Initiate OIDC Login Flow @@ -23,13 +23,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/login?frontend_callback_uri=' \ + 'http://localhost:8000/api/v1/users/login?frontend_callback_uri=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/users/login/callback` +## GET `/users/login/callback` **Summary:** Handle OIDC Login Callback @@ -53,13 +53,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/login/callback?code=&state=' \ + 'http://localhost:8000/api/v1/users/login/callback?code=&state=' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/users/me` +## GET `/users/me` **Summary:** Get Current User Status @@ -83,14 +83,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me' \ + 'http://localhost:8000/api/v1/users/me' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/users/login/local` +## POST `/users/login/local` **Summary:** Local Authentication Fallback @@ -114,7 +114,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/login/local' \ + 'http://localhost:8000/api/v1/users/login/local' \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ -d '{}' @@ -122,7 +122,7 @@ --- -## PUT `/api/v1/users/password` +## PUT `/users/password` **Summary:** Update User Password @@ -152,7 +152,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/password' \ + 'http://localhost:8000/api/v1/users/password' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -161,7 +161,7 @@ --- -## GET `/api/v1/users/me/profile` +## GET `/users/me/profile` **Summary:** Get Current User Profile @@ -184,14 +184,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me/profile' \ + 'http://localhost:8000/api/v1/users/me/profile' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## PUT `/api/v1/users/me/profile` +## PUT `/users/me/profile` **Summary:** Update User Profile @@ -221,7 +221,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/me/profile' \ + 'http://localhost:8000/api/v1/users/me/profile' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -230,7 +230,7 @@ --- -## GET `/api/v1/users/me/config` +## GET `/users/me/config` **Summary:** Get Current User Preferences @@ -253,14 +253,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config' \ + 'http://localhost:8000/api/v1/users/me/config' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## PUT `/api/v1/users/me/config` +## PUT `/users/me/config` **Summary:** Update Current User Preferences @@ -290,7 +290,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config' \ + 'http://localhost:8000/api/v1/users/me/config' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -299,7 +299,7 @@ --- -## GET `/api/v1/users/me/config/models` +## GET `/users/me/config/models` **Summary:** Get Models for Provider @@ -323,13 +323,13 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/models?provider_name=§ion=
' \ + 'http://localhost:8000/api/v1/users/me/config/models?provider_name=§ion=
' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/users/me/config/providers` +## GET `/users/me/config/providers` **Summary:** Get All Valid Providers per Section @@ -354,14 +354,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/providers?section=
&configured_only=' \ + 'http://localhost:8000/api/v1/users/me/config/providers?section=
&configured_only=' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/users/me/config/verify_llm` +## POST `/users/me/config/verify_llm` **Summary:** Verify Llm @@ -391,7 +391,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/verify_llm' \ + 'http://localhost:8000/api/v1/users/me/config/verify_llm' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -400,7 +400,7 @@ --- -## POST `/api/v1/users/me/config/verify_tts` +## POST `/users/me/config/verify_tts` **Summary:** Verify Tts @@ -430,7 +430,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/verify_tts' \ + 'http://localhost:8000/api/v1/users/me/config/verify_tts' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -439,7 +439,7 @@ --- -## POST `/api/v1/users/me/config/verify_stt` +## POST `/users/me/config/verify_stt` **Summary:** Verify Stt @@ -469,7 +469,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/verify_stt' \ + 'http://localhost:8000/api/v1/users/me/config/verify_stt' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -478,7 +478,7 @@ --- -## POST `/api/v1/users/logout` +## POST `/users/logout` **Summary:** Log Out the Current User @@ -494,13 +494,13 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/logout' \ + 'http://localhost:8000/api/v1/users/logout' \ -H 'accept: application/json' ``` --- -## GET `/api/v1/users/me/config/export` +## GET `/users/me/config/export` **Summary:** Export Configurations to YAML @@ -523,14 +523,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/export' \ + 'http://localhost:8000/api/v1/users/me/config/export' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/users/me/config/import` +## POST `/users/me/config/import` **Summary:** Import Configurations from YAML @@ -547,7 +547,7 @@ **Required:** Yes - **Media Type:** `multipart/form-data` -- **Schema:** `Body_import_user_config_yaml_api_v1_users_me_config_import_post` (Define in Models) +- **Schema:** `Body_import_user_config_yaml_users_me_config_import_post` (Define in Models) #### Responses @@ -560,7 +560,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/me/config/import' \ + 'http://localhost:8000/api/v1/users/me/config/import' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: multipart/form-data' \ @@ -569,7 +569,7 @@ --- -## GET `/api/v1/users/admin/users` +## GET `/users/admin/users` **Summary:** List All Users (Admin Only) @@ -592,14 +592,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/users' \ + 'http://localhost:8000/api/v1/users/admin/users' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## PUT `/api/v1/users/admin/users/{uid}/role` +## PUT `/users/admin/users/{uid}/role` **Summary:** Update User Role (Admin Only) @@ -630,7 +630,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/users/{uid}/role' \ + 'http://localhost:8000/api/v1/users/admin/users/{uid}/role' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -639,7 +639,7 @@ --- -## PUT `/api/v1/users/admin/users/{uid}/group` +## PUT `/users/admin/users/{uid}/group` **Summary:** Update User Group (Admin Only) @@ -670,7 +670,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/users/{uid}/group' \ + 'http://localhost:8000/api/v1/users/admin/users/{uid}/group' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -679,7 +679,7 @@ --- -## GET `/api/v1/users/admin/groups` +## GET `/users/admin/groups` **Summary:** List All Groups (Admin Only) @@ -702,14 +702,14 @@ ```bash curl -X 'GET' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/groups' \ + 'http://localhost:8000/api/v1/users/admin/groups' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` --- -## POST `/api/v1/users/admin/groups` +## POST `/users/admin/groups` **Summary:** Create Group (Admin Only) @@ -739,7 +739,7 @@ ```bash curl -X 'POST' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/groups' \ + 'http://localhost:8000/api/v1/users/admin/groups' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -748,7 +748,7 @@ --- -## PUT `/api/v1/users/admin/groups/{gid}` +## PUT `/users/admin/groups/{gid}` **Summary:** Update Group (Admin Only) @@ -779,7 +779,7 @@ ```bash curl -X 'PUT' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/groups/{gid}' \ + 'http://localhost:8000/api/v1/users/admin/groups/{gid}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' \ -H 'Content-Type: application/json' \ @@ -788,7 +788,7 @@ --- -## DELETE `/api/v1/users/admin/groups/{gid}` +## DELETE `/users/admin/groups/{gid}` **Summary:** Delete Group (Admin Only) @@ -812,7 +812,7 @@ ```bash curl -X 'DELETE' \ - 'http://localhost:8000/api/v1/api/v1/users/admin/groups/{gid}' \ + 'http://localhost:8000/api/v1/users/admin/groups/{gid}' \ -H 'accept: application/json' \ -H 'X-User-ID: ' ``` diff --git a/ai-hub/integration_tests/test_agents.py b/ai-hub/integration_tests/test_agents.py index 62e1727..0d6c89d 100644 --- a/ai-hub/integration_tests/test_agents.py +++ b/ai-hub/integration_tests/test_agents.py @@ -39,48 +39,36 @@ r_node = client.post(f"{BASE_URL}/nodes/admin", params={"admin_id": admin_id}, json=node_payload) assert r_node.status_code == 200, f"Node registration failed: {r_node.text}" - # 2. Register a basic Agent Template - tmpl_payload = { + # 2. Deploy Agent using the unified endpoint (matching UI behavior) + deploy_payload = { "name": "Cron Print Agent", "description": "Periodically prints to the node console", - "system_prompt_path": "You are a cron agent. Run shell tasks periodically.", - "max_loop_iterations": 1 - } - r_tmpl = client.post(f"{BASE_URL}/agents/templates", json=tmpl_payload, headers=_headers()) - assert r_tmpl.status_code == 200, f"Template registration failed: {r_tmpl.text}" - template_id = r_tmpl.json()["id"] - - # 3. Register a Session for the Agent - session_payload = { - "title": "Agent Test Session", - "feature_name": "agent_harness", - "user_id": admin_id, - "provider_name": "gemini" - } - r_sess = client.post(f"{BASE_URL}/sessions/", json=session_payload, headers=_headers()) - assert r_sess.status_code == 200, f"Session registration failed: {r_sess.text}" - session_id = r_sess.json()["id"] - - # 4. Register a simple Agent Instance linked to the Session - inst_payload = { - "template_id": template_id, + "system_prompt": "You are a cron agent. Run shell tasks periodically.", + "max_loop_iterations": 1, "mesh_node_id": node_id, - "session_id": session_id, - "status": "idle" # Let the Scheduler wake it up! - } - r_inst = client.post(f"{BASE_URL}/agents/instances", json=inst_payload, headers=_headers()) - assert r_inst.status_code == 200, f"Instance registration failed: {r_inst.text}" - instance_id = r_inst.json()["id"] - - # 5. Choose a basic trigger method (interval) with 5 second wakeup - trig_payload = { + "provider_name": "gemini", "trigger_type": "interval", "interval_seconds": 5, - "default_prompt": "Hello test agent! Just reply the word 'Acknowledged' and nothing else." + "default_prompt": "Hello test agent! Just reply the word 'Acknowledged' and nothing else.", + "initial_prompt": None } - r_trig = client.post(f"{BASE_URL}/agents/{instance_id}/triggers", json=trig_payload, headers=_headers()) - assert r_trig.status_code == 200, f"Trigger logic failed: {r_trig.text}" - trigger_id = r_trig.json()["id"] + r_deploy = client.post(f"{BASE_URL}/agents/deploy", json=deploy_payload, headers=_headers()) + assert r_deploy.status_code == 200, f"Deploy unified endpoint failed: {r_deploy.text}" + deploy_res = r_deploy.json() + + instance_id = deploy_res["instance_id"] + session_id = deploy_res["session_id"] + template_id = deploy_res["template_id"] + + # 3. VERIFY NODE BINDING (Fixing the exact edge case) + r_sess_check = client.get(f"{BASE_URL}/sessions/{session_id}", headers=_headers()) + assert r_sess_check.status_code == 200, "Could not fetch agent session" + assert node_id in (r_sess_check.json().get("attached_node_ids") or []), "Node ID was NOT attached to the session during deployment!" + + # We need to fetch the trigger ID for later checks + r_trig_get = client.get(f"{BASE_URL}/agents/{instance_id}/triggers", headers=_headers()) + trigger_id = r_trig_get.json()[0]["id"] + # 6. Verify Agent Periodical Execution print("\\n[test] Waiting 15 seconds to allow background interval scheduler to wake the agent...") @@ -113,12 +101,9 @@ r_cfg = client.patch(f"{BASE_URL}/agents/{instance_id}/config", json={"name": "Updated Cron Agent", "mesh_node_id": node_id}, headers=_headers()) assert r_cfg.status_code == 200 - # 9. Test Remove (delete trigger first, then agent) - r_del_trig = client.delete(f"{BASE_URL}/agents/triggers/{trigger_id}", headers=_headers()) - assert r_del_trig.status_code == 200 - + # 9. Test Remove (delete agent directly, verifying cascading trigger deletion) r_del_agent = client.delete(f"{BASE_URL}/agents/{instance_id}", headers=_headers()) - assert r_del_agent.status_code == 200 + assert r_del_agent.status_code == 200, f"Cascading delete failed: {r_del_agent.text}" # Cleanup Node client.delete(f"{BASE_URL}/nodes/admin/{node_id}", params={"admin_id": admin_id}) diff --git a/ai-hub/integration_tests/test_file_sync.py b/ai-hub/integration_tests/test_file_sync.py index 1a4cdbf..b00fa69 100644 --- a/ai-hub/integration_tests/test_file_sync.py +++ b/ai-hub/integration_tests/test_file_sync.py @@ -445,6 +445,45 @@ ) print(f"[Case 9] ✅ {NODE_2} cat returned in {latency_node2:.2f}s (file absent, fast-fail).") + # ── Case 11: Agent "hub" pseudo-node write visibility ─────────────────── + def test_case11_hub_pseudo_node_write_visibility( + self, sync_client, swarm_session + ): + """ + Regression test for AI agents writing to node='hub'. + When an AI uses mesh_file_explorer with node='hub', it directly modifies + the local mirror without broadcasting to an agent node immediately if it's + just "hub". Wait, if session_id is provided, it DOES broadcast! + Let's ensure that writing to 'hub' with a valid session scope returns success, + is immediately visible in the mirror, and is retrievable via fs/cat! + """ + filename = _unique("case11_hub") + content = f"Case 11 — Dummy file from hub — {uuid.uuid4()}" + workspace = swarm_session + + print(f"\n[Case 11] Writing {filename!r} to pseudonode 'hub' in workspace {workspace}") + result = _touch(sync_client, "hub", filename, content, workspace) + assert result.get("success"), f"Write to 'hub' failed: {result}" + + # Mirror cat (since we read from node-1, the Hub mirror resolves it instantly if it exists) + # Or better yet, read from "hub" pseudonode! + def _cat_hub(): + r = sync_client.get( + f"{NODES_PATH}/hub/fs/cat", + params={"path": filename, "session_id": workspace}, + headers=_headers(), + ) + if r.status_code == 200: + return r.json().get("content", "") + return None + + # Verify on Hub mirror + hub_content = _poll_until(_cat_hub, timeout=SMALL_FILE_TIMEOUT) + assert hub_content is not None, f"[Case 11] File '{filename}' not found on 'hub' pseudonode." + assert content in hub_content, f"[Case 11] Content mismatch on Hub loopback." + print(f"[Case 11] ✅ 'hub' pseudonode successfully returned the file.") + + # ══════════════════════════════════════════════════════════════════════════════ # NODE RECONNECT / RESYNC TESTS diff --git a/ai-hub/integration_tests/test_tools.py b/ai-hub/integration_tests/test_tools.py new file mode 100644 index 0000000..96ad0d6 --- /dev/null +++ b/ai-hub/integration_tests/test_tools.py @@ -0,0 +1,71 @@ +import pytest +from app.core.services.tool import ToolService + +@pytest.mark.asyncio +async def test_mesh_file_explorer_none_path_and_session(): + """Test that UI tools don't crash when passing None for paths or unauthenticated session IDs.""" + from app.core.tools.definitions.mesh_file_explorer import MeshFileExplorerTool + import app.core.services.tool as ts + from unittest.mock import MagicMock + + # Mock services + services_mock = MagicMock() + # Mock orchestrator and mirror + mirror_mock = MagicMock() + mirror_mock.get_workspace_path.return_value = "/tmp/fake_workspace/__fs_explorer__" + orchestrator_mock = MagicMock() + orchestrator_mock.mirror = mirror_mock + services_mock.orchestrator = orchestrator_mock + + # Init tool service wrapper purely to mimic the router context + tool = MeshFileExplorerTool() + + # 1. Provide an empty context (simulating no active session attached to the UI call) + context = { + "services": services_mock, + "session_id": "__fs_explorer__", + "node_id": "hub" # Execute on hub to hit the _hub_fs branch + } + + # Arguments where path is None + args = { + "action": "list", + "path": None + } + + task_fn, task_args = tool.prepare_task(args, context) + assert task_fn is not None + + # Execute the synchronous task returned by the plugin + result = task_fn(**task_args) + + # It shouldn't crash with TypeError. It might error with "Path not found" which is handled. + assert "error" in result or "files" in result + +@pytest.mark.asyncio +async def test_tool_service_node_id_validation(): + """Test that ToolService rejects unauthorized/hallucinated node IDs.""" + from unittest.mock import MagicMock + + service = ToolService() + + # Mock DB session and Session object + db_mock = MagicMock() + session_obj = MagicMock() + session_obj.attached_node_ids = ["test-node-1"] + db_mock.query().filter().first.return_value = session_obj + + # We don't even need the dummy_skill, call_tool will block it early! + + # The AI guessed "node1" + args = {"node_id": "node1"} + + res = await service.call_tool( + tool_name="dummy_skill", + arguments=args, + db=db_mock, + session_db_id=1 + ) + + assert res.get("success") is False + assert "NOT attached or doesn't exist" in res.get("error") diff --git a/frontend/src/features/agents/components/AgentDrillDown.js b/frontend/src/features/agents/components/AgentDrillDown.js index fc0b17a..cc3d827 100644 --- a/frontend/src/features/agents/components/AgentDrillDown.js +++ b/frontend/src/features/agents/components/AgentDrillDown.js @@ -218,7 +218,8 @@
Node: {nodes.find(n => n.id === agent?.mesh_node_id)?.name || agent?.mesh_node_id || 'unassigned'} - Jail: {agent?.current_workspace_jail || '/tmp'} + Jail Path: {agent?.current_workspace_jail || '/tmp'} + Synced Workspace: {agent?.session?.sync_workspace_id || agent?.session_id || 'not-bound'}
@@ -292,9 +293,9 @@ {activeTab === 'workspace' && ( agent ? ( ) : ( diff --git a/frontend/src/features/agents/components/AgentHarnessPage.js b/frontend/src/features/agents/components/AgentHarnessPage.js index 3d9adf9..84812a2 100644 --- a/frontend/src/features/agents/components/AgentHarnessPage.js +++ b/frontend/src/features/agents/components/AgentHarnessPage.js @@ -456,13 +456,23 @@
-
+
+ {/* Tooltip */} +
+
Execution Stats
+
+ Max Iterations: {agent.template?.max_loop_iterations || 20} + Run Iterations: 0 (N/A) + Success Rate: 100% +
+
+

onNavigate(`/agents/drilldown/${agent.id}`)} title={agent.id}> - {agent.id.split('-')[0]}... + {agent.template?.name || 'Agent'}

- Instance ID + ID: {agent.id.split('-')[0]}
{agent.status} @@ -511,21 +521,32 @@ > - - + +
+ + + + + +
); diff --git a/run_integration_tests.sh b/run_integration_tests.sh index 74eebaa..8b6fced 100755 --- a/run_integration_tests.sh +++ b/run_integration_tests.sh @@ -83,7 +83,7 @@ elif [ -d "/app/ai-hub" ]; then cd /app/ai-hub fi - DATA_DIR=./data DATABASE_URL=sqlite:///./test.db AGENT_NODE_SRC_DIR=../agent-node SKILLS_SRC_DIR=../skills /tmp/venv/bin/uvicorn app.main:app --host 0.0.0.0 --port 8000 > native_hub.log 2>&1 & + DATA_DIR=./data DATABASE_URL=sqlite:///./test.db AGENT_NODE_SRC_DIR=../agent-node SKILLS_SRC_DIR=../skills uvicorn app.main:app --host 0.0.0.0 --port 8000 > native_hub.log 2>&1 & HUB_PID=$! cd - > /dev/null diff --git a/skills/mesh-file-explorer/SKILL.md b/skills/mesh-file-explorer/SKILL.md index b9864f2..06fccea 100644 --- a/skills/mesh-file-explorer/SKILL.md +++ b/skills/mesh-file-explorer/SKILL.md @@ -57,7 +57,7 @@ - **AVOID**: **DO NOT** use `mesh_terminal_control` to execute native shell commands (`rm`, `echo`, `cp`, `mv`, `mkdir`) for files within the synchronized workspace on the `hub` node. Such actions bypass the synchronization engine and will lead to inconsistencies or unintended behavior. - **WHEN**: You are working on project files intended to sync across all nodes. - **PATH**: Use a **RELATIVE** path (e.g., `src/main.py`). NEVER use absolute paths starting with `/tmp/cortex-sync/`. -- **SESSION**: You MUST provide the `session_id` (usually your current Ghost Mirror ID). +- **SESSION**: The system will automatically inject your current autonomous workspace session ID. You DO NOT need to provide it manually for standard operations. - **BENEFIT**: Zero-latency write to the Hub mirror + instantaneous broadcast to nodes, ensuring consistent state across the mesh. ### 2. 🖥️ Physical Node Maintenance