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
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.214"
VERSION = "0.261.215"
IS_DEVELOPMENT = is_development_env_enabled()

# Opt-out for deployments where App Service Easy Auth is active but the platform
Expand Down
12 changes: 11 additions & 1 deletion application/single_app/route_backend_workflows.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@
workflow_execution_records_page, workflow_execution_provenance_page, workflow_loop_items_page,
)
from functions_workflow_repeat_history import workflow_repeat_iterations_page, workflow_repeat_state_page
from functions_settings import workflow_assistant_required
from functions_settings import workflow_assistant_required, is_workflow_assistant_enabled_for_user
from functions_workflow_assist import ASSIST_MAX_BODY_BYTES, WorkflowAssistError, parse_assist_body, run_workflow_assist
from functions_workflow_assist_runtime import build_workflow_assist_services
from werkzeug.exceptions import BadRequest, RequestEntityTooLarge
Expand Down Expand Up @@ -1618,6 +1618,16 @@
_resolve_active_group_for_workflow_management(user_id)
elif workflow_scope == 'personal':
_assert_personal_workflow_draft_access(settings)
# Runs after the personal access check so its 400 and role 403 keep precedence;

Check warning on line 1621 in application/single_app/route_backend_workflows.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.
# the assistant helper also returns False when personal workflows are unavailable.
if not is_workflow_assistant_enabled_for_user(
settings,
user_roles=(session.get('user') or {}).get('roles', []),

Check warning on line 1625 in application/single_app/route_backend_workflows.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.
):
return jsonify({
'error': 'The AI workflow assistant is not available.',
'code': 'workflow_assistant_disabled',

Check warning on line 1629 in application/single_app/route_backend_workflows.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.
}), 403
else:
return jsonify({'error': 'Invalid workflow scope.'}), 400
except ValueError as exc:
Expand Down
4 changes: 4 additions & 0 deletions application/single_app/route_frontend_workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@
settings,
user_roles=current_user_roles,
)
public_settings['enable_workflow_ai_assistant'] = is_workflow_assistant_enabled_for_user(
settings,
user_roles=current_user_roles,

Check warning on line 43 in application/single_app/route_frontend_workspace.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.
)
if not user_id:
print("User not authenticated.")
return redirect(url_for('frontend_authentication.login'))
Expand Down
2 changes: 2 additions & 0 deletions application/single_app/templates/workspace.html
Original file line number Diff line number Diff line change
Expand Up @@ -1521,6 +1521,7 @@ <h6 class="mb-1">Task sequence</h6>
</div>
</div>
</div>
{% if settings.enable_workflow_ai_assistant %}
<div class="mb-3 workflow-step-block" data-workflow-step="tasks">
<label for="workflow-task-brief" class="form-label">Task Brief</label>
<textarea class="form-control" id="workflow-task-brief" rows="3" placeholder="Briefly describe what this workflow should accomplish."></textarea>
Expand All @@ -1531,6 +1532,7 @@ <h6 class="mb-1">Task sequence</h6>
<span class="text-muted small" id="workflow-draft-instructions-status" aria-live="polite"></span>
</div>
</div>
{% endif %}
<div class="mb-3 workflow-step-block" data-workflow-step="tasks">
<label for="workflow-task-prompt" class="form-label">Task Instructions</label>
<textarea class="form-control" id="workflow-task-prompt" rows="7" placeholder="Describe exactly what this task should do each time it runs."></textarea>
Expand Down
9 changes: 6 additions & 3 deletions docs/admin/workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,12 @@

The assistant is on by default wherever personal workflows are on, and it
follows the same `WorkflowUser` role rule. Turning it off also hides each task's
**Draft with AI** button in the V2 editor. The classic workspace's **Draft
Workflow Instructions** button doesn't follow this setting. Things to know
before relying on it:
**Draft with AI** button in the V2 editor and, from version **0.261.215**, the
**Task Brief** box and **Draft Workflow Instructions** button in the classic

Check warning on line 107 in docs/admin/workflow.md

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.
workspace's personal workflow editor. The server refuses personal draft requests
too, so calling the API directly doesn't get around the setting. Group workflows
aren't affected: their **Draft Workflow Instructions** button follows the group

Check warning on line 110 in docs/admin/workflow.md

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.
workflow settings. Things to know before relying on it:

- **It proposes; it never saves.** Nothing changes until the person reviews the
edit and saves it through the normal save, with the usual checks and the Run
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -947,8 +947,9 @@
pending block focus request, so the Flow canvas can't take focus back.
- **Follow-ups** from #1593:
- A Latest Feature card, once `docs/images/guides/create-a-workflow-ask-ai.png` exists.
- A product decision: should `/api/workflows/draft-instructions` check `enable_workflow_ai_assistant` on the
server? It checks workflow access only, and the classic button ignores the setting.
- Settled for personal scope in v0.261.215: `/api/workflows/draft-instructions` refuses personal requests with

Check warning on line 950 in docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md

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.
403 `workflow_assistant_disabled` when `enable_workflow_ai_assistant` is off for the person, and the classic

Check warning on line 951 in docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md

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.
personal workspace hides its draft controls. Group-scope AI drafting is to be revisited with Phase 7 (#1550).
- Structural flow edits (For each and If blocks, adding or moving tasks), File Sync operations, and the rest of
#1577's list.
- Disable **Reload workflow** during a save, check focus after a reloaded editor closes, and add browser tests
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
- `POST /api/workflows/draft-instructions`
- Request: `workflow_scope`, `name`, `description`, `brief`, `existing_instructions`
- Response: `success`, `instructions`
- Access: authenticated users only; personal workflow app-role gates and group workflow management role gates are enforced.
- Access: authenticated users only; personal workflow app-role gates and group workflow management role gates are enforced. From version **0.261.215**, personal requests also need the AI workflow assistant (**Enable AI Workflow Assistant**, `enable_workflow_ai_assistant`) to be available to the user, and are refused with 403 `workflow_assistant_disabled` otherwise. Group requests don't follow that setting.

Check warning on line 30 in docs/explanation/features/VOICE_ASSISTED_WORKFLOW_ACTION_CREATION.md

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.

### Configuration Options

Expand All @@ -50,3 +50,4 @@

- Voice controls only render when speech-to-text is enabled and the browser supports local audio recording APIs.
- Drafted workflow instructions still require user review before save.
- From version **0.261.215**, the personal workflow modal renders the task brief and **Draft Workflow Instructions** controls only when the AI workflow assistant is available to the user. The group workflow modal is unaffected.

Check warning on line 53 in docs/explanation/features/VOICE_ASSISTED_WORKFLOW_ACTION_CREATION.md

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.
22 changes: 15 additions & 7 deletions docs/explanation/features/WORKFLOW_AI_ASSISTANT.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ setting on top:

The V2 bootstrap route (`/api/v2/bootstrap`) reports the per-user result as
`features.enable_workflow_ai_assistant`, and the editor hides the **Ask AI** tab and **Draft with
AI** when it's false. See [When the tab is offered](#when-the-tab-is-offered).
AI** when it's false. See [When the tab is offered](#when-the-tab-is-offered). From 0.261.215 the
same result gates personal requests to `POST /api/workflows/draft-instructions`, which answers 403
`workflow_assistant_disabled` when it's false, and the classic personal workspace hides its
**Task Brief** and **Draft Workflow Instructions** controls. Group drafting doesn't follow this
setting.

### Request

Expand Down Expand Up @@ -702,8 +706,9 @@ as one undoable AI change labeled "Draft with AI: <task>", and focus moves to th
- It doesn't lock the editor. When the task gained instructions or was removed while it worked,
nothing is added, and the message says so.
- Instructions longer than the editor's 12,000-character limit aren't added; the message says so.
- It's offered only where Ask AI is. The route checks personal workflow access but not
`enable_workflow_ai_assistant`, so hiding it with Ask AI keeps the setting's meaning simple.
- It's offered only where Ask AI is. From 0.261.215 the route also refuses a personal request
with 403 `workflow_assistant_disabled` when `enable_workflow_ai_assistant` is off for the person,
so hiding the button isn't the only control.

### Accessibility and plain text

Expand Down Expand Up @@ -826,10 +831,13 @@ python -u -m pytest ui_tests/test_v2_workflow_ask_ai.py -q
from an earlier session can't be undone. Its card says **Made in an earlier editing session.**
and **Undo this change** reports "This turn can no longer be undone." Replay tells the assistant
those changes may not have been saved.
- **Draft with AI isn't gated by the assistant setting on the server.**
`POST /api/workflows/draft-instructions` checks workflow access, not
`enable_workflow_ai_assistant`. The V2 editor hides **Draft with AI** whenever Ask AI is hidden,
but the classic personal and group workspace pages still offer **Draft Workflow Instructions**.
- **Group drafting doesn't follow the assistant setting.** From 0.261.215,
`POST /api/workflows/draft-instructions` refuses personal requests with 403
`workflow_assistant_disabled` when `enable_workflow_ai_assistant` is off for the person, and the
classic personal workspace hides its **Task Brief** and **Draft Workflow Instructions** controls.
The setting covers personal workflows only, so the classic group workspace still offers
**Draft Workflow Instructions**, governed by the group workflow settings. Group-scope AI drafting
is to be revisited with Phase 7 (#1550).
- **Hidden by an active run or lost access.** These hide the tab through the same read-only check
as a reader's editor, which the browser tests cover, but neither has a browser case of its own.
- **File Sync operations.** Phase 4 (#1547) made personal File Sync editable, and the Python
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Workflow Draft Instructions Assistant Gate Fix

Fixed in version: **0.261.215**

The application version is recorded in `application/single_app/config.py`.

Related: #1548 (Phase 3 of the [chat orchestration workflows roadmap](../features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md)), part of #1543.
Found while reviewing Phase 3c (#1593).

## Issue

The admin setting **Enable AI Workflow Assistant** (`enable_workflow_ai_assistant`, on by default, personal
workflows only) didn't stop personal AI drafting of workflow task instructions.

`POST /api/workflows/draft-instructions` calls the draft-instructions model to write a task's instructions. It
checked personal workflow access and the `WorkflowUser` role, but not the assistant setting. With the assistant
turned off:

- The V2 editor hid **Draft with AI**, but that only hid a button.
- The classic workspace's personal workflow modal still offered **Task Brief** and **Draft Workflow
Instructions**, and the button still drafted instructions.
- Anyone who could edit personal workflows could call the route directly and get a model-written draft.

## Root cause

The setting was enforced only by the `workflow_assistant_required` decorator, which wraps the V2 editor's
**Ask AI** route (`POST /api/user/workflows/assist`). The draft route predates the assistant and serves both
personal and group scopes, so it wasn't given the decorator, and nothing else checked the setting for it. The
V2 editor followed the setting by hiding its button, and the classic page didn't look at it at all.

## Resolution

### Server

In `draft_workflow_instructions`, the personal branch now checks the assistant after the existing personal
access check:

1. `_assert_personal_workflow_draft_access(settings)` runs first, unchanged. Personal workflows that are off
still get 400 `Personal workflows are disabled.`, and a missing `WorkflowUser` role still gets 403
`Personal workflows require the WorkflowUser app role.`
2. `is_workflow_assistant_enabled_for_user(settings, user_roles=...)` then decides whether the assistant is
available to the signed-in user, using the roles in the session. When it isn't, the route returns 403 with
the decorator's body:

```json
{"error": "The AI workflow assistant is not available.", "code": "workflow_assistant_disabled"}
```

No model client is created and no model call is made.

The order matters. The helper also returns False when personal workflows aren't available, so checking it
first would replace the personal-workflow 400 and the role 403 with the assistant's 403.

The check runs before the route's "Provide a task brief..." 400, so an empty personal request also gets the
assistant's 403 when the assistant is off.

The route isn't wrapped in `workflow_assistant_required`, because that would refuse group requests too.
`_assert_personal_workflow_draft_access` isn't changed, because other workflow routes use it.

### Group scope

Group drafting is unchanged. The setting is documented as personal-only, and group drafting stays governed by
the group workflow settings and the caller's group role. Whether group workflows should get an AI drafting
setting of their own is to be revisited with Phase 7 (#1550).

### Classic workspace

`/workspace` now overrides `enable_workflow_ai_assistant` in the sanitized settings it gives the template with
the same role-aware result, as it already does for `allow_user_workflows` and `enable_url_access`. The value is
a boolean computed on the server, and no raw setting is passed through.

`workspace.html` renders the personal **Task Brief** box, the **Draft Workflow Instructions** button and its
status line only when that value is true. The brief only feeds the draft request and isn't saved, so hiding the
button alone would have left an input that does nothing. `workspace_workflows.js` already treats all three
elements as optional, so nothing else changes. **Task Instructions** stays, so people can still write
instructions themselves.

The group workspace's controls in `group_workspaces.html` are unchanged.

### V2 editor

No change. The editor already offers **Draft with AI** only where it offers **Ask AI**, which needs the
bootstrap's role-aware `features.enable_workflow_ai_assistant`.

## Files modified

| File | Change |
| --- | --- |
| `application/single_app/route_backend_workflows.py` | Imports `is_workflow_assistant_enabled_for_user` and refuses personal drafting when the assistant isn't available |
| `application/single_app/route_frontend_workspace.py` | Passes a role-aware `enable_workflow_ai_assistant` to the classic workspace template |
| `application/single_app/templates/workspace.html` | Renders the personal Task Brief and Draft Workflow Instructions controls only when the assistant is available |
| `application/single_app/config.py` | `VERSION = "0.261.215"` |
| `functional_tests/test_workflow_draft_instructions_assistant_gate.py` | New regression test |

Documentation updated: `docs/admin/workflow.md`, `docs/explanation/features/WORKFLOW_AI_ASSISTANT.md`,
`docs/explanation/features/VOICE_ASSISTED_WORKFLOW_ACTION_CREATION.md` and the Phase 3c follow-up in
`docs/explanation/features/CHAT_ORCHESTRATION_WORKFLOWS_ROADMAP.md`.

## Impact

- **Admins** who turn the assistant off now get what the setting says: no personal AI drafting from the V2
editor, the classic workspace or the API.
- **Users** with the assistant available see no change.
- **Users** without it no longer see the classic personal **Task Brief** and **Draft Workflow Instructions**
controls, and they can still write task instructions themselves.
- **Group workflows** are unaffected.
- **API callers** that drafted personal instructions with the assistant off now get 403
`workflow_assistant_disabled`, the same answer the **Ask AI** route gives.

## Validation

`functional_tests/test_workflow_draft_instructions_assistant_gate.py` runs the real route bodies, the real gate
helpers from `functions_settings.py` and the real modal markup on closed Flask and Jinja environments, with a fake
model client, group resolver and Cosmos. It covers:

- personal drafting with the assistant on reaching the model
- personal drafting with the assistant off refused with the decorator's body, before any model client is created
- a `WorkflowUser` who still needs the assistant when the role is required
- the personal-workflow 400 and the role 403, unchanged and taking precedence over the assistant's answer
- group drafting with the assistant off, still allowed, and group refusals unchanged
- an invalid scope, still a 400
- the route not being wrapped in the assistant decorator
- the classic workspace getting a role-aware flag, never the raw setting
- the personal modal hiding its draft controls when the flag is off, and the group modal keeping them

The test passes as a script and under pytest, each with and without `python -O`.

Each of these mutations fails at least one test:

- removing the server check
- also gating group scope
- inverting the flag
- checking before the personal access check
- reading the raw setting instead of the role-aware helper
- decorating the route
- removing the template condition or the route's override
- gating the group template

The route policy tests under `functional_tests/route_tests/` are unchanged and pass, because the route, its
decorators and its auth policy are the same.

### Before and after

| Request or page, assistant off | Before | After |
| --- | --- | --- |
| Personal `POST /api/workflows/draft-instructions` | 200, with a drafted answer | 403 `workflow_assistant_disabled`, no model call |
| Group `POST /api/workflows/draft-instructions` | 200 | 200, unchanged |
| Classic personal workflow modal | Shows Task Brief and Draft Workflow Instructions | Hides them, and Task Instructions stays |
| Classic group workflow modal | Shows them | Shows them, unchanged |
| V2 editor | Hides Draft with AI | Hides Draft with AI, unchanged |
11 changes: 11 additions & 0 deletions docs/explanation/release_notes.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

For feature-focused and fix-focused drill-downs by version, see [Features by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/features) and [Fixes by Version](https://github.com/microsoft/simplechat/tree/main/docs/explanation/fixes).

### **(v0.261.215)**

#### Bug Fixes

* **Draft Workflow Instructions Follows The AI Workflow Assistant Setting**
* Turning off **Enable AI Workflow Assistant** now stops personal AI drafting of workflow task instructions everywhere, not just in the V2 editor. Before, only the V2 editor's **Draft with AI** button was hidden; the classic workspace's **Draft Workflow Instructions** button and direct calls to `/api/workflows/draft-instructions` still drafted instructions.
* The route now refuses a personal request with 403 `workflow_assistant_disabled`, the same answer the **Ask AI** route gives, before any model call. Personal workflows that are off and a missing `WorkflowUser` role keep their existing answers, which still come first.
* The classic workspace's personal workflow modal hides its **Task Brief** box and **Draft Workflow Instructions** button when the assistant isn't available to the signed-in user, using the same role-aware check as the V2 editor. **Task Instructions** stays, so instructions can still be written by hand.
* Group workflows aren't affected. Their **Draft Workflow Instructions** button follows the group workflow settings.
* (Ref: #1548, #1543, `route_backend_workflows.py`, `route_frontend_workspace.py`, `workspace.html`, `is_workflow_assistant_enabled_for_user`, [Workflow Draft Instructions Assistant Gate Fix](fixes/WORKFLOW_DRAFT_INSTRUCTIONS_ASSISTANT_GATE_FIX.md))

### **(v0.261.214)**

#### New Features
Expand Down
Loading
Loading