feat: I-WORK-HANDOFF + I-WORK-PROACTIVE — approval requests posted to communication with approval_request block, proactive suggestions posted to communication with action_card block
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
This commit is contained in:
+41
-6
@@ -390,16 +390,51 @@ async def run_react_loop(
|
|||||||
requested_by_type="agent",
|
requested_by_type="agent",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Post approval request to workstream
|
# Post approval request to Communication (I-WORK-HANDOFF)
|
||||||
if agent_run_id:
|
if agent_run_id:
|
||||||
await post_approval_request(
|
try:
|
||||||
|
from app.plugins.builtins.contracts import get_contract_registry
|
||||||
|
from app.plugins.builtins.kommunikation.models import CommConversation
|
||||||
|
from sqlalchemy import select as sa_select
|
||||||
|
komm = get_contract_registry().get("kommunikation")
|
||||||
|
if komm:
|
||||||
|
agent_id = getattr(agent_definition, "id", uuid.uuid4())
|
||||||
|
room_title = f"Agent: {getattr(agent_definition, 'name', 'Agent')}"
|
||||||
|
existing = await db.execute(
|
||||||
|
sa_select(CommConversation).where(
|
||||||
|
CommConversation.tenant_id == tenant_id,
|
||||||
|
CommConversation.title == room_title,
|
||||||
|
CommConversation.is_locked.is_(True),
|
||||||
|
CommConversation.locked_by == "automation",
|
||||||
|
CommConversation.deleted_at.is_(None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
conv = existing.scalar_one_or_none()
|
||||||
|
if conv:
|
||||||
|
await komm.send_message(
|
||||||
db=db,
|
db=db,
|
||||||
tenant_id=tenant_id,
|
tenant_id=tenant_id,
|
||||||
agent_id=getattr(agent_definition, "id", uuid.uuid4()),
|
conversation_id=conv.id,
|
||||||
approval_id=approval.id,
|
sender_id=agent_id,
|
||||||
action=f"Tool '{tool_name}' requires approval",
|
sender_type="agent",
|
||||||
details={"tool_name": tool_name, "arguments": args},
|
content=f"Approval required for tool '{tool_name}'",
|
||||||
|
content_format="text",
|
||||||
|
blocks=[
|
||||||
|
{
|
||||||
|
"block_type": "approval_request",
|
||||||
|
"block_data": {
|
||||||
|
n "title": f"Approval: {tool_name}",
|
||||||
|
"description": f"Agent wants to execute tool '{tool_name}' with arguments: {json.dumps(args)[:300]}",
|
||||||
|
"approval_id": str(approval.id),
|
||||||
|
"status": "pending",
|
||||||
|
},
|
||||||
|
"sort_order": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
metadata={"approval_id": str(approval.id), "agent_run_id": str(agent_run_id)},
|
||||||
)
|
)
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Failed to post approval request to communication", exc_info=True)
|
||||||
|
|
||||||
# Pause the loop — return with waiting_for_approval status
|
# Pause the loop — return with waiting_for_approval status
|
||||||
result.status = "waiting_for_approval"
|
result.status = "waiting_for_approval"
|
||||||
|
|||||||
@@ -62,10 +62,77 @@ def get_sse_queue(user_id: str) -> asyncio.Queue[dict[str, Any]]:
|
|||||||
|
|
||||||
|
|
||||||
async def push_suggestion(user_id: str, suggestion: dict[str, Any]) -> None:
|
async def push_suggestion(user_id: str, suggestion: dict[str, Any]) -> None:
|
||||||
"""Push suggestion to user's SSE queue."""
|
"""Push suggestion to user's SSE queue and post to Communication."""
|
||||||
queue = get_sse_queue(user_id)
|
queue = get_sse_queue(user_id)
|
||||||
await queue.put(suggestion)
|
await queue.put(suggestion)
|
||||||
|
|
||||||
|
# Post suggestion to Communication (I-WORK-PROACTIVE)
|
||||||
|
try:
|
||||||
|
import uuid as uuid_mod
|
||||||
|
from app.plugins.builtins.contracts import get_contract_registry
|
||||||
|
from app.plugins.builtins.kommunikation.models import CommConversation
|
||||||
|
from sqlalchemy import select as sa_select
|
||||||
|
from app.core.db import get_worker_session_factory
|
||||||
|
komm = get_contract_registry().get("kommunikation")
|
||||||
|
if komm:
|
||||||
|
factory = get_worker_session_factory()
|
||||||
|
async with factory() as db:
|
||||||
|
# Find or create AI suggestions room
|
||||||
|
room_title = "KI Vorschläge"
|
||||||
|
# Get tenant_id from suggestion or user
|
||||||
|
tenant_id = suggestion.get("tenant_id")
|
||||||
|
if not tenant_id:
|
||||||
|
return
|
||||||
|
existing = await db.execute(
|
||||||
|
sa_select(CommConversation).where(
|
||||||
|
CommConversation.tenant_id == uuid_mod.UUID(str(tenant_id)),
|
||||||
|
CommConversation.title == room_title,
|
||||||
|
CommConversation.is_locked.is_(True),
|
||||||
|
CommConversation.locked_by == "ai_proactive",
|
||||||
|
CommConversation.deleted_at.is_(None),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
conv = existing.scalar_one_or_none()
|
||||||
|
if not conv:
|
||||||
|
room = await komm.create_plugin_room(
|
||||||
|
db=db,
|
||||||
|
tenant_id=uuid_mod.UUID(str(tenant_id)),
|
||||||
|
user_id=uuid_mod.UUID(str(user_id)),
|
||||||
|
plugin_name="ai_proactive",
|
||||||
|
title=room_title,
|
||||||
|
participant_type="ai",
|
||||||
|
)
|
||||||
|
conv_id = uuid_mod.UUID(room["conversation_id"])
|
||||||
|
else:
|
||||||
|
conv_id = conv.id
|
||||||
|
await komm.send_message(
|
||||||
|
db=db,
|
||||||
|
tenant_id=uuid_mod.UUID(str(tenant_id)),
|
||||||
|
conversation_id=conv_id,
|
||||||
|
sender_id=None,
|
||||||
|
sender_type="ai",
|
||||||
|
content=suggestion.get("title", "KI Vorschlag"),
|
||||||
|
content_format="text",
|
||||||
|
blocks=[
|
||||||
|
{
|
||||||
|
"block_type": "action_card",
|
||||||
|
"block_data": {
|
||||||
|
"title": suggestion.get("title", "Vorschlag"),
|
||||||
|
"description": suggestion.get("description", ""),
|
||||||
|
"actions": [
|
||||||
|
{"label": "Annehmen", "action": "accept_suggestion", "data": {"suggestion_id": suggestion.get("id", "")}},
|
||||||
|
{"label": "Ablehnen", "action": "dismiss_suggestion", "data": {"suggestion_id": suggestion.get("id", "")}},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"sort_order": 0,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
metadata={"suggestion_id": suggestion.get("id", ""), "type": "proactive_suggestion"},
|
||||||
|
)
|
||||||
|
await db.commit()
|
||||||
|
except Exception:
|
||||||
|
logger.warning("Failed to post suggestion to communication", exc_info=True)
|
||||||
|
|
||||||
|
|
||||||
# ─── Rate Limiting ───
|
# ─── Rate Limiting ───
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user