Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
a10b65b
Add the workflow proposal setting-off golden and bump to 0.261.200
paullizer Sep 29, 2026
6287080
Author personal File Sync in the V2 workflow editor
paullizer Sep 29, 2026
1b622d6
Add workflow planning context and browser time zone to orchestration
paullizer Sep 29, 2026
01a0209
Move Phase 4 to reserved version 0.261.204
paullizer Sep 29, 2026
d57b01e
Add the workflow deliverable kind to orchestration
paullizer Sep 29, 2026
d3445f2
Merge V2 (7900ab1e4, 0.261.203) into Phase 4
paullizer Sep 29, 2026
337d53c
Add the workflow_propose orchestration capability
paullizer Sep 29, 2026
84b6896
Add workflow proposal routes and decisions
paullizer Sep 29, 2026
87715e6
Add the workflow proposal card under orchestration answers
paullizer Sep 29, 2026
48af639
Answer a workflow preview against the request's local time
paullizer Sep 29, 2026
09668df
Move Phase 4 to reserved version 0.261.206
paullizer Sep 29, 2026
4489812
Merge V2 (2f8dab475, 0.261.204) into Phase 4
paullizer Sep 29, 2026
c636924
Move Phase 4 to reserved version 0.261.207
paullizer Sep 29, 2026
6951401
Offer the workflow planner only documents the user named
paullizer Sep 30, 2026
20f9257
Add the Propose Workflows From Chat admin switch and document proposals
paullizer Sep 30, 2026
3718cca
Merge V2 18dac67d5 into Phase 4
paullizer Sep 30, 2026
e72baec
Approve workflowProposalLink as a same-origin URL builder
paullizer Sep 30, 2026
8cd926f
Test personal File Sync on structured drafts and in the Changes tab
paullizer Sep 30, 2026
f502c97
Describe no_suitable_agent as a card reason, not a plan failure
paullizer Sep 30, 2026
177e0bc
Stub log_workflow_creation for tests that import the orchestration ro…
paullizer Sep 30, 2026
5933c07
Address CodeQL notes on the Phase 4 changes
paullizer Sep 30, 2026
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
23 changes: 22 additions & 1 deletion application/single_app/admin_settings_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -3760,6 +3760,25 @@
{"key": "enable_semantic_kernel", "equals": True},
],
},
{
"key": "enable_chat_orchestration_workflows",
"type": "switch",
"label": "Propose Workflows From Chat",
"help": (
"When a user asks for recurring or automated work, such as a weekly "
"summary, a plan can propose a personal workflow as a card in the user's "
"own private conversation. Nothing is created until the user approves the "

Check warning on line 3770 in application/single_app/admin_settings_fields.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.
"card, and the workflow limits under Limits apply. Off by default, because "
"an approved proposal becomes a standing personal workflow that keeps "
"running on the user's behalf. Requires Chat Orchestration and Enable "
"Personal Workflows."
),
"default": False,
"depends_on": [
{"key": "enable_chat_orchestration", "equals": True},
{"key": "allow_user_workflows", "equals": True},
],
},
{
"key": "chat_orchestration_enabled_capabilities",
"type": "checkbox_set",
Expand All @@ -3770,7 +3789,8 @@
"those capabilities. Plans need Prepare content to write a chat answer or "
"the content of a file, and Create a file to deliver a downloadable file. "
"Generate images also requires Image Generation. Use an action also "
"requires Enable Action Access."
"requires Enable Action Access. Propose workflows also requires Propose "
"Workflows From Chat and personal workflows."
),
"default": [],
"options": [
Expand All @@ -3786,6 +3806,7 @@
{"value": "compose", "label": "Prepare content"},
{"value": "render_file", "label": "Create a file"},
{"value": "generate_image", "label": "Generate images"},
{"value": "workflow_propose", "label": "Propose workflows"},
],
"depends_on": {"key": "enable_chat_orchestration", "equals": True},
},
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 @@ -100,7 +100,7 @@
EXECUTOR_TYPE = 'thread'
EXECUTOR_MAX_WORKERS = 30
SESSION_TYPE = 'filesystem'
VERSION = "0.261.205"
VERSION = "0.261.207"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
15 changes: 15 additions & 0 deletions application/single_app/functions_orchestration_adapters.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
CAPABILITY_TABULAR_ANALYZE,
CAPABILITY_URL_FETCH,
CAPABILITY_WEB_SEARCH,
CAPABILITY_WORKFLOW_PROPOSE,
DEPENDENCY_PLAN_CONTRACT_VERSION,
DOCUMENT_ACTION_TYPE_COMPARISON,
VISUAL_CHART,
Expand Down Expand Up @@ -1841,6 +1842,19 @@ def run_agent_invoke(step, context, *, settings, user_id, emit, cancel_requested
# Registry
# --------------------------------------------------------------------------------------

def run_workflow_propose(step, context, *, settings, user_id, emit=None, cancel_requested=None):
"""Prepare a workflow proposal for the user's approval; nothing is created.

The workflow draft service loads only when a plan proposes a workflow, so resolving this
adapter imports nothing beyond this module.
"""
from functions_orchestration_workflows import adapter_workflow_propose

return adapter_workflow_propose(
step, context, settings=settings, user_id=user_id, emit=emit, cancel_requested=cancel_requested,
)


# Keyed by capability id, which is also each descriptor's declared ``adapter`` name, so the
# executor can look an adapter up straight from the step's capability without a second map.
ADAPTER_REGISTRY = {
Expand All @@ -1853,6 +1867,7 @@ def run_agent_invoke(step, context, *, settings, user_id, emit, cancel_requested
CAPABILITY_DEEP_RESEARCH: run_deep_research,
CAPABILITY_AGENT_INVOKE: run_agent_invoke,
CAPABILITY_ACTION_INVOKE: run_action_invoke,
CAPABILITY_WORKFLOW_PROPOSE: run_workflow_propose,
}


Expand Down
10 changes: 9 additions & 1 deletion application/single_app/functions_orchestration_composition.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
Answer-writing steps receive saved memory, the resolved conversation references, the
knowledge basis the planner declared, a disclosure of optional inputs that could not be
gathered, and guidance for the visuals the planner named (charts, Mermaid diagrams, image
proposal cards).
proposal cards). In a turn that may propose a workflow they also receive the user's local
time, worded as the workflow's scheduled runs will receive theirs.

JSON preparation states each declared output's exact shape, asks the endpoint for a JSON
object, and makes one corrective call when a reply breaks a declared rule. Neither reply is
Expand Down Expand Up @@ -48,6 +49,7 @@
build_answer_visual_guidance, build_existing_charts_note, collect_run_charts,
image_proposals_available, place_chart_blocks,
)
from functions_orchestration_workflow_context import workflow_answer_time_line


COMPOSE_POLICY = (
Expand Down Expand Up @@ -360,6 +362,12 @@
if history:
messages.append({'role': 'system', 'content': CONVERSATION_POLICY})
messages.extend({'role': message['role'], 'content': message['content']} for message in history)
# A turn that may propose a workflow answers against the local time its runs will be given.
time_line = workflow_answer_time_line(
getattr(context, 'workflow_planning', None), getattr(context, 'time_zone', None),

Check warning on line 367 in application/single_app/functions_orchestration_composition.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Moderate - Changed line contains obfuscation, dynamic loading, or hidden payload marker. Recommendation%3A Confirm the changed code is not hiding behavior, decoding payloads, or bypassing normal review.
)
if time_line:
messages.append({'role': 'system', 'content': time_line})

Check warning on line 370 in application/single_app/functions_orchestration_composition.py

View workflow job for this annotation

GitHub Actions / malicious-pr-security-review

Important - Changed line contains security control, sanitization, or audit marker. Recommendation%3A Confirm the change does not weaken auth, CSRF, CSP, XSS defenses, settings sanitization, redaction, audit logging, or tests.
messages.append({
'role': 'user',
'content': json.dumps({
Expand Down
28 changes: 24 additions & 4 deletions application/single_app/functions_orchestration_context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1381,8 +1381,14 @@
self.code = code


def enrich_planner_candidates(candidates, user_id, *, conversation_id, seeds=None):
"""Use authorized source metadata, never display labels, to describe source kinds."""
def enrich_planner_candidates(candidates, user_id, *, conversation_id, seeds=None, source_scopes=None):

Check warning on line 1384 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.
"""Use authorized source metadata, never display labels, to describe source kinds.

When ``source_scopes`` is a dict, it also receives each authorized document's owning scope
and file name (``{document_id: {'scope', 'scope_id', 'file_name'}}``) from the same current
authority check, for callers that need to name the document's workspace. The returned
candidates are unchanged either way.
"""
seeds = seeds or {}
selected = set(seeds.get('document_ids') or [])
if not candidates and not selected:
Expand Down Expand Up @@ -1483,6 +1489,13 @@
'A selected document could not be opened. Review your document selection.',
code='selected_source_unavailable',
)
if isinstance(source_scopes, dict):
for document_id, source in available.items():
source_scopes[document_id] = {
'scope': source.get('scope'),
'scope_id': source.get('scope_id'),
'file_name': source.get('file_name'),
}
return [
{
**candidate,
Expand Down Expand Up @@ -1963,9 +1976,14 @@
user_id, identity, user_message, agent_catalog, action_catalog=None, *, allowed_user_urls=None,
native_bridge_for_step=None, rendering_service=None,
external_source_admission=None, external_source_authorizer=None, external_source_preflight=None,
capture_external_source_configuration=None,
capture_external_source_configuration=None, workflow_planning=None,
):
"""Apply the same caller-specific capability gates to planning, revisions, and execution."""
"""Apply the same caller-specific capability gates to planning, revisions, and execution.

``workflow_planning`` is the server-built workflow proposal context stored with the turn.
It is added only when the caller has one, so a request without workflow proposals keeps
exactly the context it had before they existed.
"""
if native_bridge_for_step is not None and not callable(native_bridge_for_step):
raise ValueError('A server native bridge factory is required.')
if rendering_service is not None:
Expand Down Expand Up @@ -2007,6 +2025,8 @@
context['rendering_service'] = rendering_service
if has_external_bindings:
context.update(external_bindings)
if workflow_planning is not None:
context['workflow_planning'] = deepcopy(workflow_planning)
return context


Expand Down
Loading
Loading