Skip to content

Feature: Collaborative Assistant Editing via Share Permissions #113

@DerrickF

Description

@DerrickF

Summary

Extend assistant sharing to support permission levels so that assistant creators can grant edit access to specific people. This enables collaborative assistant building by teams, not just individuals.

Motivation

Currently, sharing an assistant only grants read/chat access. If a team wants to co-manage an assistant — updating instructions, uploading documents, tweaking settings — only the original creator can do it. This is a bottleneck for departments, research groups, and course teams that want to build and maintain assistants together.

Current Sharing Model

Share records in DynamoDB are flat with no permission level:

PK: AST#{assistantId}
SK: SHARE#{email}
GSI3_PK: SHARE#{email}
GSI3_SK: AST#{assistantId}
assistantId, email, createdAt, firstInteracted
  • All write operations (update_assistant, delete_assistant, share_assistant, unshare_assistant) require owner_id match
  • get_assistant_with_access_check grants shared users read access only
  • Share/unshare endpoints only allow the owner to manage shares

Proposed Changes

Permission Levels

Permission Can Chat Can View Config Can Edit Can Manage Shares Can Delete
viewer (default)
editor
owner (implicit)

Editor can:

  • View and update assistant settings (name, description, instructions, tags, starters, emoji, visibility, image, strictness, citation toggles)
  • Upload, delete, and manage knowledge base documents
  • Use the test chat
  • View the share list (read-only — cannot add/remove shares)

Editor cannot:

  • Delete the assistant
  • Add or remove shared users
  • Transfer ownership

Data Model Changes

DynamoDB Share Record

Add a permission attribute to share records:

PK: AST#{assistantId}
SK: SHARE#{email}
GSI3_PK: SHARE#{email}
GSI3_SK: AST#{assistantId}
assistantId, email, createdAt, firstInteracted
permission: "viewer" | "editor"    ← NEW (default: "viewer")

Existing share records without a permission attribute are treated as viewer (backward compatible).

Backend Models

ShareAssistantRequest — add optional permission field:

class ShareAssistantRequest(BaseModel):
    emails: List[str] = Field(..., min_length=1)
    permission: Literal["viewer", "editor"] = Field("viewer", description="Permission level for shared users")

AssistantSharesResponse — return permission per user:

class ShareEntry(BaseModel):
    email: str
    permission: Literal["viewer", "editor"] = "viewer"

class AssistantSharesResponse(BaseModel):
    assistant_id: str = Field(..., alias="assistantId")
    shared_with: List[ShareEntry] = Field(..., alias="sharedWith")

AssistantResponse — add the requesting user's permission level:

class AssistantResponse(BaseModel):
    # ... existing fields ...
    user_permission: Optional[Literal["owner", "editor", "viewer"]] = Field(
        None, alias="userPermission",
        description="Requesting user's permission level on this assistant"
    )

Backend Changes

Access Check Updates (service.py)

get_assistant_with_access_check — return the permission level alongside the assistant so callers know what the user can do. Currently returns Optional[Assistant]; extend to also populate user_permission on the response.

check_share_access — currently returns bool. Change to return the permission level (or None if no share):

async def check_share_access(assistant_id: str, user_email: str) -> Optional[str]:
    # Returns "viewer", "editor", or None

Write Endpoint Authorization

update_assistant (routes.py) — currently checks owner_id match. Extend to also allow users with editor permission:

# Current: only owner can update
updated_assistant = await update_assistant(assistant_id=assistant_id, owner_id=user_id, ...)

# New: owner OR editor can update
assistant = await get_assistant_by_id(assistant_id)  # no owner check
permission = resolve_permission(assistant, user_id, user_email)
if permission not in ("owner", "editor"):
    raise HTTPException(403, "Edit access required")

Apply the same pattern to:

  • PUT /assistants/{id} — allow editor
  • POST /assistants/{id}/test-chat — allow editor
  • Document upload/delete endpoints for the assistant — allow editor
  • DELETE /assistants/{id} — owner only (no change)
  • POST/DELETE /assistants/{id}/shares — owner only (no change)
  • GET /assistants/{id}/shares — owner and editor (read-only for editor)

Share Management

share_assistant — write the permission field to the share record:

table.put_item(Item={
    "PK": f"AST#{assistant_id}",
    "SK": f"SHARE#{email}",
    # ... existing fields ...
    "permission": permission,  # "viewer" or "editor"
})

New: Update share permission — allow the owner to change an existing share's permission level without removing and re-adding:

PATCH /assistants/{assistant_id}/shares
Body: { "email": "user@example.com", "permission": "editor" }

Frontend Changes

Share Dialog

  • When sharing, add a permission dropdown/toggle next to each email: Can view | Can edit
  • Default: "Can view"
  • In the share list, show the current permission level per user with the ability to change it
  • Owner can upgrade viewer → editor or downgrade editor → viewer inline

Assistant Edit Page

  • Editors see the full edit form (same as owner)
  • Hide the "Delete Assistant" button for editors
  • Hide the share management section for editors, or show it read-only
  • Show a subtle indicator that the user is editing as a collaborator, not the owner (e.g., "Shared by {ownerName}" badge)

Assistant List

  • Shared assistants with edit permission could show a small "Editor" badge to distinguish from view-only shares

Migration

  • Existing share records have no permission attribute
  • Backend treats missing permission as "viewer" — fully backward compatible, no data migration needed
  • All existing behavior is preserved; this is purely additive

Out of Scope

  • Ownership transfer
  • Group/team-based sharing (share with a role or department)
  • Granular per-field edit permissions
  • Edit history / audit log of who changed what
  • Real-time collaborative editing (conflict resolution)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions