Request Flows

TeamWork is a full web UI with chat channels, file browser, terminal, browser screencast, and content panel. Messages flow through a webhook with rich view context so Prax knows exactly what the user is looking at.

Synced from Prax at f62d7985 View source ↗

← Architecture

Request Flow — SMS Message

sequenceDiagram
    participant U as User (Phone)
    participant T as Twilio
    participant F as Flask /sms
    participant S as SmsService
    participant C as ConversationService
    participant A as ReAct Agent
    participant DB as SQLite
    participant W as Workspace

    U->>T: Send SMS
    T->>F: POST /sms (webhook)
    F->>S: process(request)
    S->>S: Authorize (phone_to_name_map)

    alt PDF attachment
        S->>S: Extract markdown (opendataloader-pdf)
        S->>W: Save .md to active/, .pdf to archive/
        S->>U: Summary via SMS
    else Text message
        S->>C: reply(from_number, text)
        C->>DB: Retrieve conversation history
        C->>W: get_workspace_context()
        C->>A: Invoke agent (history + input + context)
        A->>A: ReAct loop (reason → act → observe)
        A-->>W: Workspace tool calls (optional)
        A-->>A: Search / PDF / NPR tools (optional)
        A->>C: Final response
        C->>DB: Save user + assistant messages
        C->>S: Return response text
        S->>U: Send SMS (chunked if >1600 chars)
    end

Request Flow — Discord Message

sequenceDiagram
    participant U as User (Discord)
    participant D as Discord Bot
    participant DS as DiscordService
    participant C as ConversationService
    participant A as ReAct Agent
    participant DB as SQLite

    U->>D: Send message (DM or channel)
    D->>DS: on_message(message)
    DS->>DS: Authorize (discord_allowed_users)

    alt File attachment
        DS->>DS: Download attachment
        DS->>C: reply(discord_user_id, text + attachment context)
    else Text message
        DS->>C: reply(discord_user_id, text)
    end

    C->>DB: Retrieve conversation history
    C->>A: Invoke agent (history + input + workspace context)
    A->>A: ReAct loop (reason → act → observe)
    A->>C: Final response
    C->>DB: Save user + assistant messages
    C->>DS: Return response text
    DS->>U: Send Discord message (chunked if >2000 chars)

Request Flow — TeamWork Web UI

TeamWork is a full web UI with chat channels, file browser, terminal, browser screencast, and content panel. Messages flow through a webhook with rich view context so Prax knows exactly what the user is looking at.

sequenceDiagram
    participant U as User (Browser)
    participant TW as TeamWork (FastAPI)
    participant WH as Prax /teamwork/webhook
    participant C as ConversationService
    participant A as ReAct Agent
    participant DB as SQLite

    U->>TW: Send message (chat, side chat, or DM)
    Note over U: Message includes:
content, channel_id,
active_view, extra_data TW->>TW: Persist message to DB TW->>TW: Broadcast via WebSocket TW->>WH: POST webhook (content, channel_id,
active_view, extra_data) WH->>WH: Build view context Note over WH: View-specific behavior:
browser → "PAIRING in shared browser"
terminal → "PAIRING in shared terminal"
content → "browsing the Library"
+ fetch screen state if applicable alt active_view = "browser" WH->>TW: GET /api/browser/info TW-->>WH: Browser screencast status Note over WH: Prepend: "[LIVE BROWSER — user watching]" else active_view = "terminal" WH->>TW: GET /api/terminal/{project}/recent TW-->>WH: Last ~50 terminal lines Note over WH: Prepend: "[TERMINAL SCREEN — last 50 lines]" else active_view = "library" Note over WH: Extract extra_data.content_context
{project, notebook, slug, title} WH->>WH: library_service.get_note(...) Note over WH: Prepend: "[LIBRARY ITEM — viewing this note]
Title + full markdown content" else active_view = "home" Note over WH: No extra context needed — Prax knows
the user is on the project dashboard end WH->>WH: Set ContextVars (channel_id, active_view) WH->>C: reply(user_id, prefixed_content) C->>DB: Retrieve conversation history C->>A: Invoke agent (history + input + view context) A->>A: ReAct loop (reason → act → observe) A->>C: Final response C->>DB: Save messages WH->>TW: POST /api/external/reply (response + trace metadata) TW->>U: WebSocket broadcast → message appears in UI

View Context System

Every message from TeamWork includes an active_view field indicating which panel the user is on. Prax uses this to tailor both the system prompt and its behavior:

active_view What Prax Sees Behavior
"browser" Live browser info + screencast status Uses delegate_browser exclusively — user watches the browser in real-time
"terminal" Last ~50 terminal output lines Uses sandbox_shell — executes commands immediately, no confirmation
"library" Full library item content injected (when viewing a specific note) + item metadata Uses library_* tools; discusses the item immediately without re-reading
"home" Active project dashboard visible Uses library_projects_list + library_tasks_list when asked for status
"chat" No special context Default behavior with all tools available
other No special context View label shown but no behavior change

Library item context tracking

When the user is viewing a specific note in the Library, the frontend passes the selected item’s metadata through the full stack:

LibraryPanel (note selection)
  → ProjectWorkspace (holds state)
  → BrowserChatSidebar (contentContext prop)
  → POST /api/messages { extra_data: { content_context: {project, notebook, slug, title} } }
  → TeamWork forwards extra_data in webhook payload
  → Prax extracts content_context, injects into tool_guidance:
      "The user is currently viewing a library item: personal/health/sleep-tips — 'Sleep tips'"

This means when the user says “refine this” or “add a section about X”, Prax knows exactly which note they mean without the user needing to name it. For very long notes, content is truncated with a hint to use library_note_read for the full version.

Direct-edit acknowledgment

When the user edits a note directly in the Library panel, Prax’s library view tool guidance tells the agent to treat bracketed meta notes like [I just edited this] as completed actions rather than instructions — the user is informing Prax of a state change, not asking for one.

Sandbox Code Execution Flow (direct execution)

Direct code execution — no coding sessions. The multi-round OpenCode coding-session tools (sandbox_start / sandbox_message / sandbox_review / sandbox_finish / sandbox_abort / sandbox_search / sandbox_execute) were removed (2026-07): the sandbox image no longer ships a coding-agent server. delegate_sandbox is now a headless sub-agent that writes and runs code directly in the container via sandbox_shell — no session lifecycle, no rounds, no archive/replay. Prax also codes natively on the host (run_python, workspace_save/workspace_patch, source_read/source_grep). Available whenever SANDBOX_ENABLED. See sandbox-execution-boundary.

sequenceDiagram
    participant A as Main Agent
    participant SB as Sandbox Spoke
    participant SS as Sandbox Service
    participant D as Docker
    participant W as Workspace Git

    Note over A: User asks: "Turn this PDF into a beamer presentation with voiceover"
    A->>SB: delegate_sandbox("Build beamer deck from PDF")
    Note over SB: Headless sub-agent plans and codes directly
    SB->>SS: sandbox_shell("cat main.tex > ...")
    SS->>D: docker exec (write file)
    D-->>SS: ok
    SS-->>SB: stdout / exit code

    SB->>SS: sandbox_shell("pdflatex main.tex && ...")
    SS->>D: docker exec (build)
    D-->>SS: build output
    SS-->>SB: stdout / exit code

    Note over SB: Inspect a file with the line-numbered viewer
    SB->>SS: sandbox_view("build.sh")
    SS->>D: docker exec (read)
    D-->>SS: file contents
    SS-->>SB: numbered lines

    Note over SB: Copy the artifact back to the user's workspace
    SB->>W: write artifact + git commit
    SB-->>A: Summary of what was built + artifact path
    A->>A: Respond to user via SMS

Scheduled Messages Flow

sequenceDiagram
    participant U as User
    participant A as Main Agent
    participant SchS as Scheduler Service
    participant YAML as schedules.yaml
    participant APS as APScheduler
    participant C as ConversationService
    participant SMS as SMS Gateway

    Note over U: "Send me French words every 2h weekdays 9-5, I'm in LA"
    U->>A: (via SMS)
    A->>SchS: schedule_set_timezone("America/Los_Angeles")
    SchS->>YAML: Write timezone
    A->>SchS: schedule_create("French vocab", prompt, "0 9,11,13,15,17 * * 1-5")
    SchS->>YAML: Append schedule entry
    SchS->>APS: Register CronTrigger (tz=America/Los_Angeles)
    SchS-->>A: Schedule created (id: french-vocab-a1b2c3)
    A->>U: "Done! I'll send French words at 9am, 11am, 1pm, 3pm, 5pm PT on weekdays."

    Note over APS: Monday 9:00 AM Pacific
    APS->>SchS: _on_fire(user, schedule_id, prompt)
    SchS->>C: reply(user, "[Scheduled task] Send me 5 French words...")
    C->>A: Agent generates fresh vocabulary
    A-->>C: "Here are 5 French words: ..."
    C-->>SchS: Response text
    SchS->>SMS: send_sms(response, user)
    SMS->>U: French vocabulary arrives as SMS
    SchS->>YAML: Update last_run timestamp

schedules.yaml Format

Each user has a schedules.yaml in their git workspace that both the agent and the user can edit manually:

timezone: America/Los_Angeles
schedules:
- id: french-vocab-a1b2c3
  description: French vocabulary practice
  prompt: >
    Send me 5 new French words with their English translations,
    pronunciation guides, and example sentences. Vary the difficulty
    and topic each time. Remember what you sent before.
  cron: '0 9,11,13,15,17 * * 1-5'
  timezone: America/Los_Angeles
  enabled: true
  created_at: '2026-03-20T09:00:00-07:00'
  last_run: '2026-03-20T15:00:00-07:00'

- id: daily-briefing-d4e5f6
  description: Morning news briefing
  prompt: >
    Give me a brief morning briefing: top 3 news headlines,
    weather summary, and one interesting fact.
  cron: '30 7 * * 1-5'
  timezone: America/Los_Angeles
  enabled: true
  created_at: '2026-03-20T09:05:00-07:00'
  last_run: null

Cron field reference (5 fields: minute hour day month weekday):

Pattern Meaning
0 9,11,13,15,17 * * 1-5 9am, 11am, 1pm, 3pm, 5pm on weekdays
30 7 * * * Daily at 7:30am
0 */3 * * 1-5 Every 3 hours on weekdays
0 8 * * 1 Every Monday at 8am
0 20 1,15 * * 8pm on the 1st and 15th of each month

Manual editing: Edit the YAML directly in the workspace, then tell the agent “I edited the schedules file” and it will call schedule_reload to pick up changes. All changes are git-committed automatically.