Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions application/single_app/agent_execution_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,13 +171,14 @@


def capture_execution_identity(user_id, conversation_id=None):
"""Capture only server-authenticated identity, never parent prompt or workspace state.
"""Capture authenticated identity and admitted retrieval limits, never workspace preferences.

Flask is imported here because workers use the context types without a Flask
dependency. The closure creates a distinct app/request context for every
invocation; concurrent children never share ``g`` or a mutable session.
"""
from flask import current_app, g, has_request_context, request, session
from public_chat_scope_state import current_public_chat_scope, public_chat_scope_context

if not has_request_context():
return ExecutionIdentity(user_id, conversation_id)
Expand All @@ -187,6 +188,7 @@
app = current_app._get_current_object()
origin = request.host_url
identity_session = {"user": deepcopy(authenticated_user)}
public_scope = deepcopy(current_public_chat_scope(user_id))
root_session = session._get_current_object()
if session.get("token_cache"):
identity_session["token_cache"] = session["token_cache"]
Expand Down Expand Up @@ -223,7 +225,15 @@
}
if group_id:
g.conversation_group_id = group_id
yield
scope_context = (
public_chat_scope_context(
user_id, public_scope["selection"], public_scope["workspace_ids"],

Check warning on line 230 in application/single_app/agent_execution_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
public_scope.get("resolve_public_chat_workspace_ids"),

Check warning on line 231 in application/single_app/agent_execution_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
)
if public_scope else nullcontext()
)
with scope_context:
yield

return ExecutionIdentity(
user_id, conversation_id, bridge,
Expand Down
2 changes: 1 addition & 1 deletion application/single_app/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.309"
VERSION = "0.261.311"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
42 changes: 24 additions & 18 deletions application/single_app/functions_orchestration_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,8 @@


def _document_scope(context, arguments):
if (_ctx(context, 'seeds', {}) or {}).get('public_workspace_selection'):

Check warning on line 309 in application/single_app/functions_orchestration_adapters.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
return 'public'
standing_scope = _ctx(context, 'doc_scope', 'all')
if _ctx(context, 'elicitation_references', None) and standing_scope in ('personal', 'group', 'public'):
return standing_scope
Expand Down Expand Up @@ -512,24 +514,28 @@
if not ids:
return []

resolver = _ctx(context, 'resolve_source_manifest', None)
selection = selection_mode or _selection_mode(context)
if callable(resolver):
return list(resolver(ids) or [])

from functions_analysis_access import resolve_analysis_source_manifest

return list(resolve_analysis_source_manifest(
ids,
user_id or _ctx(context, 'user_id', None),
selection_mode=selection,
conversation_id=_ctx(context, 'conversation_id', None),
active_group_ids=_ctx(context, 'active_group_ids', None),
active_public_workspace_ids=_public_workspace_ids(context),
doc_scope=_ctx(context, 'doc_scope', 'all'),
cancel_requested=cancel_requested,
request_correlation_id=_ctx(context, 'request_correlation_id', None),
) or [])
# Source readers run both before execution and in independent worker threads.
from public_chat_scope import public_chat_execution_scope

with public_chat_execution_scope(context, user_id or _ctx(context, 'user_id', None), settings):
resolver = _ctx(context, 'resolve_source_manifest', None)
selection = selection_mode or _selection_mode(context)
if callable(resolver):
return list(resolver(ids) or [])

from functions_analysis_access import resolve_analysis_source_manifest

return list(resolve_analysis_source_manifest(
ids,
user_id or _ctx(context, 'user_id', None),
selection_mode=selection,
conversation_id=_ctx(context, 'conversation_id', None),

Check warning on line 532 in application/single_app/functions_orchestration_adapters.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
active_group_ids=_ctx(context, 'active_group_ids', None),
active_public_workspace_ids=_public_workspace_ids(context),

Check warning on line 534 in application/single_app/functions_orchestration_adapters.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
doc_scope=_document_scope(context, {}),
cancel_requested=cancel_requested,
request_correlation_id=_ctx(context, 'request_correlation_id', None),
) or [])


# --------------------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
'conversation_context', 'context_message_ids', 'allowed_user_urls',
'chat_type', 'selection_mode', 'doc_scope', 'tags', 'document_filter_mode',
'active_group_ids', 'active_group_id', 'active_public_workspace_ids',
'gpt_model',
'gpt_model', 'public_workspace_selection',

Check warning on line 54 in application/single_app/functions_orchestration_checkpoints.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
)


Expand Down
13 changes: 12 additions & 1 deletion application/single_app/functions_orchestration_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
from datetime import datetime, timezone

from functions_appinsights import log_event, workflow_log_context
from public_chat_scope_state import (
PublicChatScopeError, aggregate_public_workspace_ids, current_public_chat_scope,

Check warning on line 42 in application/single_app/functions_orchestration_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
normalize_public_workspace_selection,

Check warning on line 43 in application/single_app/functions_orchestration_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
)
from functions_action_catalog import build_action_planner_projection
from functions_assist_references import sanitize_reference_label
from functions_message_block_revisions import resolve_block_sources_in_content
Expand Down Expand Up @@ -188,6 +192,7 @@

return {
**({'model_routing': 'auto'} if routing == 'auto' else {}),
'public_workspace_selection': normalize_public_workspace_selection(request_data.get('public_workspace_selection')),

Check warning on line 195 in application/single_app/functions_orchestration_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains AI, plugin, agent, or workspace boundary marker. Recommendation%3A Check whether prompts, chat history, uploaded documents, embeddings, citations, settings, or identity can cross a new boundary.
'document_ids': document_ids,
'document_labels': document_labels,
'doc_scope': _text(request_data.get('doc_scope')) or 'all',
Expand Down Expand Up @@ -575,6 +580,9 @@
the browser, or an active workspace, is never evidence of access.
"""
kind, scope_id = scope['kind'], scope['id']
aggregate = current_public_chat_scope(user_id)
if aggregate and kind != 'public':
raise ElicitationContextError('Public search cannot use personal or group sources.', reason='workspace_unavailable')

Check warning on line 585 in application/single_app/functions_orchestration_context.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains secret or sensitive data source marker. Recommendation%3A Pair this source with any nearby network, logging, serialization, or process execution sink before approving.
if not settings.get(WORKSPACE_SCOPE_SETTINGS[kind], False):
raise ElicitationContextError(
'That workspace capability is currently disabled.', reason='workspace_disabled',
Expand Down Expand Up @@ -608,7 +616,8 @@
get_user_visible_public_workspace_ids_from_settings,
)

if scope_id not in get_user_visible_public_workspace_ids_from_settings(user_id):
public_ids = aggregate_public_workspace_ids(user_id) if aggregate else get_user_visible_public_workspace_ids_from_settings(user_id)
if scope_id not in public_ids:
raise ElicitationContextError(
'That public workspace is not available.', reason='workspace_unavailable',
)
Expand Down Expand Up @@ -1361,6 +1370,8 @@
tags_filter=seeds.get('tags') or None,
document_filter_mode=seeds.get('document_filter_mode') or 'intersection',
)
except PublicChatScopeError:
raise
except Exception as exc:
log_event(
f"[ORCHESTRATION_CONTEXT] Candidate document probe failed; planning without "
Expand Down
13 changes: 13 additions & 0 deletions application/single_app/functions_orchestration_execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,17 @@ def _read_conversation(self):
self.record["user_id"], self.record["conversation_id"],
)

def _bind_public_chat_scope(self, context, conversation_id):
from functions_public_workspaces import resolve_public_chat_workspace_ids

def read_conversation(requested_conversation_id):
if requested_conversation_id != conversation_id:
raise PermissionError("That conversation is not available.")
return self._read_conversation()

context.resolve_public_chat_workspace_ids = resolve_public_chat_workspace_ids
context.read_conversation_for_public_chat = read_conversation

def _validate_memory(self):
conversation = self._read_conversation()
validate_memory_context(
Expand Down Expand Up @@ -803,6 +814,7 @@ def resolve_step_model(step_seeds, current_settings):
workflow_planning, self.record.get("time_zone") if workflow_configured else None,
),
)
self._bind_public_chat_scope(self.context, conversation_id)
self.context.prompt_token_usage = self.prompt_token_usage
if auto_routing:
def guarded_planner_client(model):
Expand Down Expand Up @@ -1025,6 +1037,7 @@ def _initialize_delivery(self, settings, services):
task_results={name: TaskResult.from_dict(value) for name, value in tasks.items()},
execution_deadline_at=self.record["execution_deadline_at"],
)
self._bind_public_chat_scope(self.context, conversation_id)
self.context.pending_results = deepcopy(pending)
response = None
if self.record["plan"].get("final_response"):
Expand Down
15 changes: 9 additions & 6 deletions application/single_app/functions_orchestration_executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
from content_screening.access import assert_current_request_sources_available
from content_screening.contracts import ScreeningError
from functions_appinsights import log_event, workflow_log_context
from public_chat_scope import public_chat_execution_scope
from functions_mixed_source_orchestration import (
AUTHORIZATION_STATUS_AUTHORIZED,
MixedSourceCancellationError,
Expand Down Expand Up @@ -431,6 +432,7 @@ def __init__(
self.elicitation_references = list(elicitation_references or [])
self.selected_document_ids = list(selected_document_ids or [])
self.seeds = dict(seeds or {})
self.public_workspace_selection = self.seeds.get('public_workspace_selection')
self.original_seeds = dict(original_seeds or {})
self.user_message_id = user_message_id
self.conversation_context = deepcopy(conversation_context or {})
Expand Down Expand Up @@ -774,11 +776,12 @@ def execute_plan(
version = plan_contract_version(plan)
if version != getattr(context, 'plan_contract_version', None):
raise ResultContractError('result_version_unsupported')
return _execute_dependency_plan(
plan, context, settings=settings, user_id=user_id, emit=emit,
cancel_requested=cancel_requested, persist=persist, get_adapter=get_adapter,
checkpoints=checkpoints,
)
with public_chat_execution_scope(context, user_id, settings):
return _execute_dependency_plan(
plan, context, settings=settings, user_id=user_id, emit=emit,
cancel_requested=cancel_requested, persist=persist, get_adapter=get_adapter,
checkpoints=checkpoints,
)


def _dependency_request_context(context):
Expand Down Expand Up @@ -939,7 +942,7 @@ def _run_dependency_step(
# to create. Every other Gather or Reason step stays unable to publish anything.
capability = get_capability(step['capability_id'], contract_version=DEPENDENCY_PLAN_CONTRACT_VERSION)
publishes = step['role'] == 'render' or (capability or {}).get('publishes_generated_images') is True
with orchestration_file_policy(allow_generated_files=publishes), model_scope:
with public_chat_execution_scope(scoped, user_id, settings), orchestration_file_policy(allow_generated_files=publishes), model_scope:
if step['capability_id'] == 'render_file':
result = _render_dependency_step(
runtime_step, scoped, settings=settings, user_id=user_id, cancel_requested=cancel_probe,
Expand Down
29 changes: 29 additions & 0 deletions application/single_app/functions_public_workspaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
import functions_settings
from functions_group import *
from typing import Iterable
from public_chat_scope_state import (
PublicChatScopeError,
normalize_public_workspace_selection,
)

from functions_chat_bootstrap_cache import bump_chat_bootstrap_global_cache_version
from functions_workspace_branding import (
Expand Down Expand Up @@ -513,6 +517,31 @@ def set_user_visible_public_workspaces(user_id: str, workspace_ids: list) -> Non
update_user_settings(user_id, {"visiblePublicWorkspaceIds": workspace_ids})


def resolve_public_chat_workspace_ids(user_id, selection, *, settings=None):
"""Resolve aggregate curation against existing, chat-available public workspaces."""
selection = normalize_public_workspace_selection(selection)
if not selection:
raise PublicChatScopeError("Choose All or Visible public workspaces.")
settings = functions_settings.get_settings() if settings is None else settings
if not settings.get("enable_public_workspaces", False):
raise PublicChatScopeError("Public workspaces are currently disabled.", "public_chat_disabled", 403)
workspaces = get_all_public_workspaces()
visible_ids = None
if selection == "visible":
user_settings = functions_settings.get_user_settings(user_id)
visible_ids = set(visible_public_workspace_ids_from_user_settings(
user_settings, list_public_workspaces=lambda: workspaces,
))
resolved = []
for workspace in workspaces:
workspace_id = workspace.get("id")
allowed, _reason = check_public_workspace_status_allows_operation(workspace, "chat")
if workspace_id and allowed and (visible_ids is None or workspace_id in visible_ids):
if workspace_id not in resolved:
resolved.append(workspace_id)
return resolved


def add_visible_public_workspace(user_id: str, ws_id: str) -> None:
"""
Add a workspace to the user's visible list using publicDirectorySettings.
Expand Down
14 changes: 13 additions & 1 deletion application/single_app/functions_search.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import hashlib
import logging
from public_chat_scope_state import aggregate_public_workspace_ids, current_public_chat_scope
from typing import List, Dict, Any
from content_screening.access import (
PROVENANCE_FIELD,
Expand All @@ -20,7 +21,10 @@
read_embedding_settings,
search_with_embedding_profile,
)
from functions_public_workspaces import get_user_visible_public_workspace_docs, get_user_visible_public_workspace_ids_from_settings
from functions_public_workspaces import (
get_user_visible_public_workspace_docs,
Comment thread
paullizer marked this conversation as resolved.
get_user_visible_public_workspace_ids_from_settings,
Comment thread
paullizer marked this conversation as resolved.
)
Comment thread
paullizer marked this conversation as resolved.
from utils_cache import (
generate_search_cache_key,
get_cached_search_results,
Expand Down Expand Up @@ -121,6 +125,10 @@
active_public_workspace_id=None,
enforce_public_workspace_visibility=True,
):
aggregate_ids = aggregate_public_workspace_ids(user_id)
if aggregate_ids is not None:
requested_ids = normalize_search_id_list(active_public_workspace_id)
return [value for value in aggregate_ids if not requested_ids or value in requested_ids]
requested_workspace_ids = normalize_search_id_list(active_public_workspace_id)
if requested_workspace_ids and not enforce_public_workspace_visibility:
return requested_workspace_ids
Expand Down Expand Up @@ -414,6 +422,10 @@

top_n = normalize_search_top_n(top_n)
doc_scope = normalize_search_scope(doc_scope)
if current_public_chat_scope(user_id):
doc_scope = "public"
if not aggregate_public_workspace_ids(user_id):
return []
document_ids = normalize_search_id_list(document_ids)
document_filter_mode = _normalize_document_filter_mode(document_filter_mode)

Expand Down
Loading
Loading