Skip to content

add copilot dashboard session - #960

Open
charan-vadd wants to merge 1 commit into
juspay:releasefrom
charan-vadd:feat/bzn-40680-copilot-dashboard-session
Open

add copilot dashboard session#960
charan-vadd wants to merge 1 commit into
juspay:releasefrom
charan-vadd:feat/bzn-40680-copilot-dashboard-session

Conversation

@charan-vadd

@charan-vadd charan-vadd commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added optional Copilot data scoping when creating chat sessions.
    • Supports merchant, template, timezone, and date-range selections.
    • Automatically applies authorized data access and default seven-day date windows.
    • Persists resolved Copilot scope information with the chat session.
  • Bug Fixes

    • Prevented clients from overriding server-managed session metadata.
    • Added clear validation errors for unauthorized merchants, templates, permissions, and invalid date ranges.
    • Preserved existing behavior for standard chat sessions.

Copilot AI review requested due to automatic review settings July 28, 2026 08:50
@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 719fbe52-721a-4578-85c3-9b052311b878

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This change adds Copilot scope request schemas and resolution, verifies merchant and template authorization, computes date windows, and injects server-owned scope metadata into chat sessions while rejecting client attempts to override reserved metadata.

Changes

Buddy Copilot scope flow

Layer / File(s) Summary
Copilot scope contracts
app/schemas/breeze_buddy/copilot.py, app/schemas/breeze_buddy/chat.py
Adds validated request and immutable resolved-scope models, capability/date-range enums, metadata serialization, and the optional chat-session copilot_scope field.
Scope authorization and normalization
app/services/breeze_buddy/copilot/*, app/database/queries/breeze_buddy/template.py, app/database/accessor/breeze_buddy/template.py, tests/test_copilot_scope.py
Resolves permissions, merchants, templates, and date windows; adds template ownership lookup; and tests authorization, validation, metadata contents, and date behavior.
Chat-session metadata integration
app/api/routers/breeze_buddy/chat/handlers.py, app/services/breeze_buddy/*, tests/test_copilot_session.py
Protects reserved metadata keys, injects resolved Copilot metadata, maps scope errors to HTTP responses, and tests normal-session compatibility and rejection paths.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant ChatSessionHandler
  participant ScopeResolver
  participant TemplateAccessor
  participant SessionPersistence
  Client->>ChatSessionHandler: Create session with copilot_scope
  ChatSessionHandler->>ScopeResolver: resolve_copilot_scope(request, current_user)
  ScopeResolver->>TemplateAccessor: get_template_merchant_id(template_id)
  TemplateAccessor-->>ScopeResolver: template merchant_id
  ScopeResolver-->>ChatSessionHandler: resolved CopilotScope
  ChatSessionHandler->>SessionPersistence: persist merged session metadata
  SessionPersistence-->>ChatSessionHandler: created chat session
Loading

Possibly related PRs

  • juspay/clairvoyance#957: Adds the Copilot scope foundation used by chat-session resolution and template ownership checks.

Suggested reviewers: copilot

Poem

A bunny bounds through scopes today,
With guarded metadata tucked away.
Merchants align, dates softly flow,
Templates prove what they may show.
“No client keys!” the rabbit sings,
While Copilot earns its wings.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change: adding Copilot dashboard session support to chat session creation and persistence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces a “Buddy Copilot” data-scope contract that can be requested by the dashboard when creating a normal Breeze Buddy chat session, with server-side validation and persistence into chat_session.metadata for downstream tool/prompt use.

Changes:

  • Adds Copilot scope request/response schemas and a scope resolver service (permissions + merchant/template authorization + timezone-aware date window).
  • Extends chat session creation to (a) protect server-owned metadata namespaces and (b) resolve/persist Copilot scope under metadata.copilot.
  • Adds a lightweight “template → merchant_id” DB query/accessor and comprehensive unit tests for scope resolution + session metadata behavior.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
app/api/routers/breeze_buddy/chat/handlers.py Validates reserved metadata keys and optionally resolves/persists Copilot scope during chat session creation.
app/schemas/breeze_buddy/chat.py Extends CreateChatSessionRequest with optional copilot_scope.
app/schemas/breeze_buddy/copilot.py Introduces Copilot scope schemas and session-metadata serialization helper.
app/services/breeze_buddy/copilot/scope.py Implements server-authoritative scope resolution + validation logic.
app/services/breeze_buddy/copilot/__init__.py Exposes Copilot scope resolver symbols for import convenience.
app/database/queries/breeze_buddy/template.py Adds a query to fetch only merchant_id for a template.
app/database/accessor/breeze_buddy/template.py Adds accessor get_template_merchant_id() used by scope validation to avoid loading full templates.
tests/test_copilot_session.py Tests session creation behavior, server-owned metadata protection, and Copilot metadata persistence.
tests/test_copilot_scope.py Tests scope resolver behavior (permissions, merchant/template authorization, date window normalization, timezone errors).
app/services/breeze_buddy/__init__.py Adds package docstring for Breeze Buddy services namespace.

Comment thread app/schemas/breeze_buddy/copilot.py Outdated
@charan-vadd
charan-vadd force-pushed the feat/bzn-40680-copilot-dashboard-session branch from a15cc9f to 9da33e0 Compare July 28, 2026 08:58

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/schemas/breeze_buddy/copilot.py (2)

38-44: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add a type hint for info.

info in validate_order lacks a type annotation; use pydantic.ValidationInfo.

✏️ Proposed fix
-from pydantic import BaseModel, ConfigDict, Field, computed_field, field_validator
+from pydantic import (
+    BaseModel,
+    ConfigDict,
+    Field,
+    ValidationInfo,
+    computed_field,
+    field_validator,
+)
@@
-    def validate_order(cls, value: date, info) -> date:
+    def validate_order(cls, value: date, info: ValidationInfo) -> date:
As per coding guidelines, "Include required type hints on all function signatures."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/schemas/breeze_buddy/copilot.py` around lines 38 - 44, Update the
validate_order method signature to annotate info with pydantic.ValidationInfo,
importing ValidationInfo through the project’s existing Pydantic import style.
Preserve the validator’s current date-order validation behavior.

Source: Coding guidelines


106-122: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

label hardcodes "Last 7 days", decoupled from the actual window size.

The DEFAULT-source label is a fixed string rather than derived from date_from/date_to. If the default window size in _resolve_date_window (scope.py) ever changes, this label will silently go stale.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/schemas/breeze_buddy/copilot.py` around lines 106 - 122, The
CopilotDateWindow.label property hardcodes the DEFAULT label instead of
reflecting the resolved dates. Update label to derive its DEFAULT-source text
from date_from and date_to (or the shared default-window configuration), so it
remains accurate if _resolve_date_window changes; preserve the existing
explicit-date label format.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/schemas/breeze_buddy/copilot.py`:
- Around line 38-44: Update the validate_order method signature to annotate info
with pydantic.ValidationInfo, importing ValidationInfo through the project’s
existing Pydantic import style. Preserve the validator’s current date-order
validation behavior.
- Around line 106-122: The CopilotDateWindow.label property hardcodes the
DEFAULT label instead of reflecting the resolved dates. Update label to derive
its DEFAULT-source text from date_from and date_to (or the shared default-window
configuration), so it remains accurate if _resolve_date_window changes; preserve
the existing explicit-date label format.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 78ec55e8-46ce-4703-b000-90d16be0b19b

📥 Commits

Reviewing files that changed from the base of the PR and between 8bff1d8 and a15cc9f.

📒 Files selected for processing (10)
  • app/api/routers/breeze_buddy/chat/handlers.py
  • app/database/accessor/breeze_buddy/template.py
  • app/database/queries/breeze_buddy/template.py
  • app/schemas/breeze_buddy/chat.py
  • app/schemas/breeze_buddy/copilot.py
  • app/services/breeze_buddy/__init__.py
  • app/services/breeze_buddy/copilot/__init__.py
  • app/services/breeze_buddy/copilot/scope.py
  • tests/test_copilot_scope.py
  • tests/test_copilot_session.py

@charan-vadd
charan-vadd force-pushed the feat/bzn-40680-copilot-dashboard-session branch 2 times, most recently from 868228f to 93ea20c Compare July 31, 2026 05:39
@charan-vadd
charan-vadd requested a review from Copilot July 31, 2026 06:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@charan-vadd
charan-vadd force-pushed the feat/bzn-40680-copilot-dashboard-session branch 4 times, most recently from dbd5844 to 9166288 Compare July 31, 2026 07:19
@murdore

murdore commented Aug 2, 2026

Copy link
Copy Markdown
Contributor

Reviewed at head 9166288 (single commit, no other open threads found via full history mining, not just the unresolved view).

This directly fixes the actor-info-leak the Copilot review flagged inline on app/schemas/breeze_buddy/copilot.py (session_metadata() was persisting the full CopilotScope, including actor.username/permissions/reseller_ids/merchant_ids, into chat_session.metadata — retrievable verbatim via GET /chat/session/{id} to anyone with session access, not just the creator). At current head, session_metadata() now excludes actor from the dump and re-adds only {"user_id": ...} (copilot.py:148-151), and it's backed by a non-vacuous test: the _user() fixture sets real username/role/permissions/reseller_ids, and both test_copilot_scope.py::test_scope_metadata_does_not_include_runtime_identity and test_copilot_session.py::test_copilot_scope_is_resolved_and_persisted_on_normal_chat_session assert the persisted actor collapses to {"user_id": ...}.

Also checked: _validate_client_metadata/_build_session_metadata correctly reject client-supplied metadata.copilot / metadata.template_vars with 422 before the session row is persisted (test_client_cannot_set_server_owned_metadata), and the new copilot_scope field's lack of a camelCase alias is consistent with every sibling field in CreateChatSessionRequest (none of template_id/template_vars/metadata use Field(alias=...) either), so that's not a deviation.

No blockers from me — worth clicking the Copilot thread resolved since the fix it asked for is already in this diff.

Comment thread app/api/routers/breeze_buddy/chat/handlers.py
@charan-vadd
charan-vadd force-pushed the feat/bzn-40680-copilot-dashboard-session branch from 9166288 to 48f452e Compare August 17, 2026 06:08
@charan-vadd
charan-vadd force-pushed the feat/bzn-40680-copilot-dashboard-session branch from 48f452e to acfb8c4 Compare August 18, 2026 05:49

@murdore murdore left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed at acfb8c42c6fd. Traced the authorization path end to end rather than reading the summary — this is a data-scoping change on merchant data, so the question I cared about was whether a caller can bind a session to a merchant they don't own, or keep access after losing it.

No findings. This is a careful piece of work, and a few of the choices are ones I see missed far more often than not:

Re-validation happens on every access, not just at creation. validate_persisted_copilot_scope_access is wired into all six session operations — get_session, send_message, approve_tool, cancel_turn, end_session, get_transcript — via _validate_chat_and_copilot_session_access. Nothing is left on the old validate_chat_session_access-only path. That means a user whose merchant access is revoked loses access to sessions they previously created, which is the property that gets skipped when scope is authorized once and then trusted forever.

The 403/404 split is deliberate and correct. At creation, _validate_data_template and _resolve_data_merchant_id return 403 — the caller named the merchant, so telling them it's denied leaks nothing. On resume, every path in validate_persisted_copilot_scope_access passes status_code=404, and _hidden_scope_error collapses that to a bare "Chat session not found". So an attacker holding a guessed session_id can't distinguish "exists but you lack merchant access" from "doesn't exist". That asymmetry looks like an inconsistency at a glance and is actually the point.

Clients can no longer plant a scope. _validate_client_metadata rejects template_vars and COPILOT_SCOPE_METADATA_KEY outright. Without that, the re-validation above would be defeated by a caller supplying their own metadata.copilot — the two changes only work as a pair, and both are here.

Persisted metadata is treated as untrusted on read. _load_persisted_data_scope and the merchant/template extraction check isinstance, strip, and reject empty strings before use, raising invalid_persisted_scope rather than propagating a malformed value into an authorization decision.

The one thing I wanted to confirm was allowed_merchant_ids is None meaning "unrestricted", since a resolver that returns None on an unexpected path would be a total bypass at scope.py:283-286. It holds: resolve_merchant_ids (app/core/security/scope.py:111) returns None only for UserRole.ADMIN and for an owner chain terminating at an admin owner; a non-wildcard user gets a concrete list (and [] when empty, which fails closed since x not in [] is always true); and resellers-with-wildcard are explicitly excluded, with a comment saying why:

    # Reseller with wildcard → resolve to merchants they OWN.
    # Never return None for resellers — they should only see their own merchants,
    # not every merchant in the system.

Both call sites treat None identically, so there's no asymmetry between the create path and the resume path either.


One compatibility note, not a defect. _validate_client_metadata changes behaviour for existing clients. Previously create_chat_session_handler did:

    persisted_metadata = {
        **(req.metadata or {}),
        "template_vars": transformed_template_vars,
    }

— a client sending metadata.template_vars had it silently overwritten. Now it gets a 422 metadata.template_vars is server-owned. Failing loudly is the right call and I'd keep it, but it is a breaking change for any caller currently sending that key and getting away with it. Worth a line in the release note, or a quick check of whether anything in Loom/dashboard does.

Approving.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants