Merge remote-tracking branch 'preview-upstream/lab' into feature/runtime-containment

This commit is contained in:
Alexandre Teixeira
2026-10-02 00:27:20 +01:00
11 changed files with 1891 additions and 18 deletions
+13 -1
View File
@@ -1027,11 +1027,15 @@ async def build_chat_context(
persist_user_message: bool = True,
interaction_mode: str | None = None,
auto_escalated: bool = False,
context_resolution=None,
) -> ChatContext:
"""Build the full context (preface + messages) for an LLM call.
This is the shared logic between /chat and /chat_stream — preset extraction,
message preprocessing, memory/RAG/web injection, compaction, normalization.
``context_resolution`` is the turn's already resolved context window. When
supplied, history shaping sizes against it instead of probing the endpoint.
"""
# Preset
preset = extract_preset(chat_handler, preset_id)
@@ -1225,12 +1229,20 @@ async def build_chat_context(
# for every candidate. Running selected-model compaction here would mutate
# session history before we know which route can answer and would make a
# later larger-context candidate unable to recover discarded history.
if context_resolution is not None:
prepared_window = {"context_length": context_resolution.shaping_window}
else:
prepared_window = {}
if defer_context_shaping:
context_length = get_context_length(sess.endpoint_url, sess.model)
context_length = (
prepared_window.get("context_length")
or get_context_length(sess.endpoint_url, sess.model)
)
was_compacted = False
else:
messages, context_length, was_compacted = await maybe_compact(
sess, sess.endpoint_url, sess.model, messages, sess.headers, owner=user,
**prepared_window,
)
_before_trim_messages = len(messages)
_before_trim_tokens = estimate_tokens(messages)
+54 -4
View File
@@ -87,6 +87,7 @@ from src.model_profiles import (
)
from src.tool_execution import AgentExecutionBridge, bind_execution_bridge
from src.agent_runtime.authority import is_internal_tool_request, request_authority_for_http
from src.agent_runtime.runtime_selection import uses_compact_preview_runtime
from src.turn_contract import (
FAMILY_TOOLS, bind_turn_contract, preserve_bound_editor_selected_tools,
requested_capabilities, resolve_turn_contract,
@@ -145,6 +146,19 @@ def _turn_contract_enabled(*, exact_tool_approval, runtime_surface,
)
def _request_privileges(request, user) -> Dict[str, Any]:
"""Per-user privileges from the app's auth manager; empty when unmanaged."""
try:
app = getattr(request, "app", None)
except (AttributeError, KeyError):
app = None
state = getattr(app, "state", None) if app is not None else None
auth_manager = getattr(state, "auth_manager", None) if state is not None else None
if not user or not auth_manager:
return {}
return auth_manager.get_privileges(user) or {}
def _native_runtime_requires_local_browser(client_runtime_context):
"""Use the private browser to verify declared local HTML artifacts."""
context = client_runtime_context if isinstance(client_runtime_context, dict) else {}
@@ -2893,6 +2907,38 @@ def setup_chat_routes(
allowed_models=_allowed_models_for_request(request),
)
# Decide once whether this turn runs on the compact (clean v3)
# runtime. Every input is final here; the native workspace term of
# the contract policy cannot veto a requested clean route. This one
# value prepares the turn below and stamps its contract later, and
# the agent loop dispatches on that stamp.
_compact_preview_turn = uses_compact_preview_runtime(
clean_route_requested=_clean_v3_route_requested,
turn_contract_enabled=_turn_contract_enabled(
exact_tool_approval=exact_tool_approval,
runtime_surface=str((client_runtime_context or {}).get("surface") or ""),
native_workspace_contract=False,
clean_v3_route=_clean_v3_route_requested,
full_schema_route=(_effective_tool_schema_mode == "full"),
),
agent_mode=(chat_mode == "agent"),
agent_permitted=_request_privileges(
request, effective_user(request),
).get("can_use_agent", True),
image_generation=image_generation_session,
)
# A compact turn resolves its typed context window once, here, with
# the session's provider credentials. History shaping below and the
# compact runtime both reuse this exact object, so the turn neither
# probes twice nor mixes the legacy untyped lookup into it.
_compact_context_resolution = None
if _compact_preview_turn:
from src.agent_runtime.context_resolution import resolve_effective_context
_compact_context_resolution = await resolve_effective_context(
sess.endpoint_url, sess.model, headers=sess.headers,
client_runtime_context=client_runtime_context,
)
# Build shared context (stream path uses enhanced_message for context preface)
ctx = await build_chat_context(
sess, request, chat_handler, chat_processor,
@@ -2923,6 +2969,7 @@ def setup_chat_routes(
else None
),
persist_user_message=not tool_approval_continuation and not is_internal_tool_request(request),
context_resolution=_compact_context_resolution,
interaction_mode=chat_mode,
auto_escalated=auto_escalated,
)
@@ -3122,7 +3169,9 @@ def setup_chat_routes(
# pasted revision request. This only offers permitted schemas;
# it never requires or performs a document mutation.
_turn_capabilities = _turn_capabilities | {"documents"}
_clean_v3_preview = bool(_use_turn_contract and _clean_v3_route_requested)
# Same decision that prepared the turn; it only stamps the contract
# inside the agent-contract branch below.
_clean_v3_preview = _compact_preview_turn
# requested_capabilities already inherits a typed, recently executed
# family for referential follow-ups. Do not additionally union stale
# families into an explicit new request: that inflated regular-model
@@ -3267,13 +3316,11 @@ def setup_chat_routes(
})
# Enforce per-user privileges
_privs = {}
# Bearer clients enter the agent loop as the sandboxed ``api`` user,
# but their token is owned by the real account. Use that owner here so
# a permitted TUI/WebUI client does not inherit api's default denial.
_user = effective_user(request)
if _user and hasattr(request.app.state, 'auth_manager') and request.app.state.auth_manager:
_privs = request.app.state.auth_manager.get_privileges(_user)
_privs = _request_privileges(request, _user)
if _privs:
if not _privs.get("can_use_bash", True):
disabled_tools.update(FAMILY_TOOLS["shell_files"])
@@ -4459,6 +4506,9 @@ def setup_chat_routes(
client_runtime_context=client_runtime_context,
thinking_mode=thinking_mode,
reasoning_effort=reasoning_effort,
context_resolution=(
_compact_context_resolution if _clean_v3_preview else None
),
):
if chunk.startswith("data: ") and not chunk.startswith("data: [DONE]"):
try: