feat: deployment-level no_shell policy (agents.yaml no_shell: true)
#3
@@ -474,3 +474,36 @@ def install() -> None:
|
|||||||
_patch_create_session_factory()
|
_patch_create_session_factory()
|
||||||
_patch_aggregator_call_tool()
|
_patch_aggregator_call_tool()
|
||||||
|
|
||||||
|
|
||||||
|
def install_no_shell() -> None:
|
||||||
|
"""Disable fast-agent's shell runtime for every agent in this process.
|
||||||
|
|
||||||
|
fast-agent auto-activates its ``execute`` shell tool on any agent that
|
||||||
|
has skills configured (``MCPAgent._ensure_shell_runtime_for_skills``);
|
||||||
|
the only opt-out is ``Context.no_shell``, which fast-agent's own CLI
|
||||||
|
sets by direct assignment but which has no ``FastAgent``-constructor or
|
||||||
|
config-file knob. The context is created inside ``run()`` before agents
|
||||||
|
are constructed, so we wrap ``initialize_context`` to stamp the flag on
|
||||||
|
every context it returns. Skill loading itself is unaffected — the
|
||||||
|
``read_skill`` tool registers independently of the shell.
|
||||||
|
|
||||||
|
NOT installed by ``install()``: this is deployment policy, not a runtime
|
||||||
|
fix. ``server.main()`` calls it when the deployment's ``agents.yaml``
|
||||||
|
sets a truthy top-level ``no_shell:`` key.
|
||||||
|
"""
|
||||||
|
from fast_agent import context as _fa_context
|
||||||
|
|
||||||
|
if getattr(_fa_context.initialize_context, "_pallas_no_shell_patched", False):
|
||||||
|
return
|
||||||
|
|
||||||
|
_original_initialize_context = _fa_context.initialize_context
|
||||||
|
|
||||||
|
async def _initialize_context_no_shell(*args: Any, **kwargs: Any):
|
||||||
|
ctx = await _original_initialize_context(*args, **kwargs)
|
||||||
|
ctx.no_shell = True
|
||||||
|
return ctx
|
||||||
|
|
||||||
|
_initialize_context_no_shell._pallas_no_shell_patched = True # type: ignore[attr-defined]
|
||||||
|
_fa_context.initialize_context = _initialize_context_no_shell
|
||||||
|
logger.info("no_shell patch installed — agents will not get the shell tool")
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
|
from pallas import _fastagent_patch
|
||||||
from pallas.log import set_agent_component, set_project, setup_logging
|
from pallas.log import set_agent_component, set_project, setup_logging
|
||||||
from pallas.multimodal_server import MultimodalAgentMCPServer
|
from pallas.multimodal_server import MultimodalAgentMCPServer
|
||||||
|
|
||||||
@@ -390,6 +391,13 @@ def main() -> None:
|
|||||||
|
|
||||||
setup_logging()
|
setup_logging()
|
||||||
|
|
||||||
|
# Team-level policy: a truthy top-level ``no_shell:`` in agents.yaml
|
||||||
|
# keeps fast-agent's shell tool off every agent in this deployment
|
||||||
|
# (it otherwise auto-activates on agents with skills configured).
|
||||||
|
# After setup_logging() so the install log record isn't lost.
|
||||||
|
if config.get("no_shell"):
|
||||||
|
_fastagent_patch.install_no_shell()
|
||||||
|
|
||||||
if args.agent:
|
if args.agent:
|
||||||
port = agents[args.agent]["port"]
|
port = agents[args.agent]["port"]
|
||||||
logger.info("Starting %s agent on port %d", args.agent, port)
|
logger.info("Starting %s agent on port %d", args.agent, port)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "pallas-mcp"
|
name = "pallas-mcp"
|
||||||
version = "0.5.1"
|
version = "0.5.2"
|
||||||
description = "FastAgent MCP Bridge — generic runtime for serving FastAgent agents over StreamableHTTP"
|
description = "FastAgent MCP Bridge — generic runtime for serving FastAgent agents over StreamableHTTP"
|
||||||
requires-python = ">=3.13.5"
|
requires-python = ">=3.13.5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|||||||
Reference in New Issue
Block a user