feat(tui): run bang commands through the gateway
This commit is contained in:
+60
-6
@@ -44,7 +44,7 @@ from nanobot.agent.turn_delivery import (
|
||||
)
|
||||
from nanobot.agent.turn_delivery import TurnRoute as TurnRoute
|
||||
from nanobot.agent.turn_hooks import AgentTurnHookSpec, build_agent_turn_hook
|
||||
from nanobot.bus.events import InboundMessage, OutboundMessage
|
||||
from nanobot.bus.events import INBOUND_META_USER_SHELL, InboundMessage, OutboundMessage
|
||||
from nanobot.bus.outbound_events import StreamedResponseEvent
|
||||
from nanobot.bus.queue import MessageBus
|
||||
from nanobot.bus.runtime_events import RuntimeEventBus
|
||||
@@ -802,12 +802,66 @@ class AgentLoop:
|
||||
dispatch_fn: Callable[[CommandContext], Awaitable[OutboundMessage | None]],
|
||||
) -> None:
|
||||
"""Dispatch a command directly from the run() loop and publish the result."""
|
||||
ctx = CommandContext(msg=msg, session=None, key=key, raw=raw, loop=self)
|
||||
result = await dispatch_fn(ctx)
|
||||
if result:
|
||||
await self.bus.publish_outbound(result)
|
||||
async def dispatch_and_publish() -> None:
|
||||
ctx = CommandContext(msg=msg, session=None, key=key, raw=raw, loop=self)
|
||||
result = await dispatch_fn(ctx)
|
||||
if result:
|
||||
await self.bus.publish_outbound(result)
|
||||
else:
|
||||
logger.warning("Command '{}' matched but dispatch returned None", raw)
|
||||
|
||||
# A shell command may run for up to the configured exec timeout. Keep
|
||||
# the inbound consumer responsive when it runs beside an active turn.
|
||||
if (msg.metadata or {}).get(INBOUND_META_USER_SHELL) is True:
|
||||
self.schedule_background(dispatch_and_publish())
|
||||
return
|
||||
await dispatch_and_publish()
|
||||
|
||||
async def execute_user_shell_command(self, ctx: CommandContext) -> OutboundMessage:
|
||||
"""Execute one trusted user command with the active workspace policy."""
|
||||
metadata = dict(ctx.msg.metadata or {})
|
||||
tool = self.tools.get("exec")
|
||||
if tool is None:
|
||||
content = "Shell execution is disabled in this nanobot configuration."
|
||||
else:
|
||||
logger.warning("Command '{}' matched but dispatch returned None", raw)
|
||||
session = ctx.session or self.sessions.get_or_create(ctx.key)
|
||||
scope = self.workspace_scopes.for_turn(
|
||||
channel=ctx.msg.channel,
|
||||
message_metadata=metadata,
|
||||
session_metadata=session.metadata,
|
||||
)
|
||||
request_token = bind_request_context(RequestContext(
|
||||
channel=ctx.msg.channel,
|
||||
chat_id=ctx.msg.chat_id,
|
||||
message_id=metadata.get("message_id"),
|
||||
session_key=ctx.key,
|
||||
original_user_text=f"!{ctx.args.strip()}",
|
||||
runtime=ctx.runtime,
|
||||
metadata=metadata,
|
||||
sender_id=ctx.msg.sender_id,
|
||||
turn_id=metadata.get("webui_turn_id"),
|
||||
workspace=scope.project_path,
|
||||
))
|
||||
workspace_token = bind_workspace_scope(scope)
|
||||
turn_scope_stack = ExitStack()
|
||||
try:
|
||||
for turn_scope in ctx.turn_scopes:
|
||||
turn_scope_stack.enter_context(turn_scope)
|
||||
result = await tool.execute(
|
||||
command=ctx.args.strip(),
|
||||
working_dir=str(scope.project_path),
|
||||
)
|
||||
content = str(result)
|
||||
finally:
|
||||
turn_scope_stack.close()
|
||||
reset_workspace_scope(workspace_token)
|
||||
reset_request_context(request_token)
|
||||
return OutboundMessage(
|
||||
channel=ctx.msg.channel,
|
||||
chat_id=ctx.msg.chat_id,
|
||||
content=content,
|
||||
metadata={**metadata, "render_as": "text"},
|
||||
)
|
||||
|
||||
async def _cancel_active_tasks(self, key: str) -> int:
|
||||
"""Cancel and await all active work for *key*.
|
||||
|
||||
Reference in New Issue
Block a user