Project Info
π§
Inspiration
In todayβs remote and hybrid world, collaboration often feels fragmented β scattered across chat tools, notes, and task managers. We wanted to reimagine what it means to βwork togetherβ when one of your teammates is an intelligent agent. Doriyo (γγͺγ¨, Japanese for coworker) was born from a simple question: βWhat if your conversations could think, remember, and act with you?β We envisioned a shared workspace where people can chat naturally, brainstorm ideas, and have an AI coworker that listens, remembers, and takes action when needed. Whether itβs creating a GitHub repo, generating a Notion itinerary, or summarizing a discussion, Doriyo acts as a quiet, capable teammate. π€
What it does
Doriyo is a real-time conversational workspace that brings humans and AI together. π£οΈ Collaborative chat: Create or join workspaces and collaborate in real time. π§βπ» AI coworker (@Doriyo): Mention @ask to query context or @act to perform an action. πͺ Integrated actions with Composio: Connect tools like GitHub, Notion, and Google Drive. Example: β@act create a Next.js template repo for our project.β β Doriyo commits the scaffold to your GitHub. Example: β@act create a Next.js template repo for our project.β β Doriyo commits the scaffold to your GitHub. π§ Memory: Doriyo remembers previous discussions to provide contextually aware responses. Think of it as Slack + Notion + an AI coworker combined into a single workspace. π§©
How we built it
Frontend: React + Vite for a fast, modular interface. Firebase Firestore for real-time message synchronization and presence tracking. Firebase Auth for secure login, signup, and workspace invites. Backend: FastAPI (Python) for handling requests, orchestrating actions, and managing session logic. Letta to define and manage each userβs personal AI agent, maintaining context and persistence. Composio for secure integration with third-party tools like GitHub, Notion, and Google Drive. Claude for language understanding, reasoning, and conversational context generation. The result is a seamless full-stack system where multiple users can chat, mention the agent, and see actions happen live β all backed by real-time data sync. π§±
Challenges we ran into
Maintaining real-time consistency for multi-user chat sessions using Firestore transactions. Designing a safe and permissioned workflow for actions so only workspace hosts can approve executions. Handling long-term memory and summarization efficiently without overloading the context window. Creating a cohesive UX where the agent feels like part of the team, not just another bot. π
Accomplishments we're proud of
Built a functional, multi-user AI workspace with persistent context and real-time collaboration. Integrated Letta and Composio to allow the AI to act directly on connected user tools. Achieved smooth, low-latency updates using Firebase Firestore listeners. Designed a clean, modern interface that makes AI feel like a true collaborator. π‘
What we learned
Building multi-user, multi-agent systems is as much a design problem as a technical one. Clear permission boundaries and confirmations improve user trust and safety. Contextual memory dramatically improves AI usefulness in collaborative settings. Real-time Firestore updates simplify sync logic compared to maintaining custom WebSocket servers. π
What's next
for Doriyo π§ Voice support: Integrate SFU (LiveKit) for voice-based interaction. π§© Multi-agent collaboration: Let personal agents cooperate on shared goals. π Automatic summaries: Generate meeting notes and documents from conversations. π’ Enterprise support: Role-based access control and organization-level workspaces. π Public sharing: Export or share interactive workspace transcripts. π§ In short Doriyo is your AI coworker β listening, thinking, and acting with your team in real time. Collaboration shouldn't just be about talking; it should be about doing, together.
Conversational Workspace β Project Template Documentation A voice/text-first collaborative workspace where multiple users interact with an AI Agent that can listen, answer (@ask), and act (@act) using connected tools (GitHub, Notion, Google Drive/Sheets) via Composio and Letta.
- MVP Scope User flows
Auth: Firebase (Email/Password or Google).
Workspace: Create, invite via link, join.
Session/Party: Real-time text chat (WebSockets); (Optional later) voice via SFU.
Agent: @ask (answer with memory), @act (host-only; confirm; execute via Composio).
Settings (post-MVP): Link/unlink external apps; permission delegation.
Nonβgoals (MVP)
Full voice stack w/ diarization.
Complex role hierarchies beyond host/member.
Mobile apps.
- System Architecture ββββββββββββββββ HTTPS / WSS ββββββββββββββββββββ β Next.js UI βββββββββββββββββββββββββΊβ FastAPI Core β β (Firebase) β β (AuthZ, REST, β β β WebSocket (chat) β WS Gateway) β ββββββββ¬ββββββββ βββββ¬βββββββββββββββ β β β βPub/Sub ββββββββββββββ β ββββββββββββββΊβ Redis β β β ββββββββββββββ β β β² β β β β ββββββββββββ΄ββββββββββ β β β Worker(s): Agent βββββββββ β β (Letta + Composio β Subscribes β β tool executors) β to session β ββββββββββββ¬ββββββββββ channels β β β ββββββββ΄ββββββββ β β Postgres + β ββββββββββββββββββββββββββββββΊβ pgvector β ββββββββββββββββ
(Optional Voice) Next.js ββWebRTCβββΊ SFU (LiveKit/Daily) βββΊ Bot Subscriber βββΊ ASR β same WS/Agent pipeline 3) Tech Stack Frontend: Next.js (App Router), React, Tailwind, TanStack Query.
Auth: Firebase Auth (ID token verified by backend).
Backend: FastAPI (Python 3.11+), uvicorn, SQLAlchemy/SQLModel.
DB: Postgres (Neon/Supabase), pgvector for semantic memory.
Realtime: WebSockets (FastAPI) + Redis pub/sub (Upstash/Elasticache).
Agent: Letta (LLM + tool calling) with retrieval; Composio for integrations.
Workers: RQ or Celery (Redis) for @ask/@act jobs and βsleeperβ tasks.
(Optional Voice): LiveKit/Daily SFU; ASR (Whisper server or managed).
- Repository Layout root/ apps/ web/ # Next.js app api/ # FastAPI service infra/ # IaC / deploy scripts (optional) docs/ # Design docs, OpenAPI, sequences Frontend (apps/web)
src/ app/ (auth)/ dashboard/ workspaces/[id]/ sessions/[id]/ components/ lib/ styles/ Backend (apps/api)
app/ main.py auth/firebase.py db/base.py db/models.py api/routes/ workspaces.py invites.py sessions.py messages.py actions.py composio.py services/ agent.py # Letta orchestration actions.py # Composio executors memory.py # embeddings + retrieval ws_gateway.py # Redis + WS fanout workers/ jobs.py # ask/act jobs schemas/ dto.py # pydantic models 5) Environment & Config Create two .env files: one per app.
apps/web/.env.local
NEXT_PUBLIC_API_URL=https://api.example.com NEXT_PUBLIC_WS_URL=wss://api.example.com NEXT_PUBLIC_FIREBASE_API_KEY=... NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=... NEXT_PUBLIC_FIREBASE_PROJECT_ID=... NEXT_PUBLIC_FIREBASE_APP_ID=... apps/api/.env
PORT=8080 DATABASE_URL=postgresql+psycopg://user:pass@host/db REDIS_URL=redis://:pass@host:6379/0 FIREBASE_PROJECT_ID=... COMPOSIO_API_KEY=... LETTA_API_KEY=... EMBEDDINGS_PROVIDER=openai OPENAI_API_KEY=... JWT_AUDIENCE=your-firebase-project ALLOW_ORIGINS=https://app.example.com,https://localhost:3000 Store provider keys in your secrets manager (e.g., Fly.io secrets, Vercel envs).
- Database Schema (MVP) -- Users are identified by Firebase UID create table users ( id text primary key, email text not null, display_name text, created_at timestamptz default now() );
create table workspaces ( id uuid primary key default gen_random_uuid(), host_user_id text references users(id) on delete cascade, name text not null, created_at timestamptz default now() );
create table workspace_members ( workspace_id uuid references workspaces(id) on delete cascade, user_id text references users(id) on delete cascade, role text check (role in ('host','member')) not null, primary key (workspace_id, user_id) );
create table sessions ( id uuid primary key default gen_random_uuid(), workspace_id uuid references workspaces(id) on delete cascade, is_active boolean default true, created_at timestamptz default now() );
create table messages ( id bigserial primary key, session_id uuid references sessions(id) on delete cascade, sender_user_id text null references users(id), -- null for agent/system type text check (type in ('user','agent','system','transcript')) not null, text text not null, seq bigint not null, ts timestamptz default now() ); create index on messages(session_id, seq);
create table actions ( id uuid primary key default gen_random_uuid(), workspace_id uuid references workspaces(id) on delete cascade, session_id uuid references sessions(id) on delete cascade, initiator_user_id text references users(id), kind text not null, -- e.g. github_template, notion_itinerary, sheet_budget status text check (status in ('pending','awaiting_confirm','running','done','error')) not null, payload_json jsonb not null, result_json jsonb, created_at timestamptz default now() );
-- Composio mapping (we store only IDs, not tokens) create table connections ( id uuid primary key default gen_random_uuid(), workspace_id uuid references workspaces(id) on delete cascade, user_id text references users(id), provider text not null, -- github|notion|google account_id text not null, -- composio connected account id label text, created_at timestamptz default now() );
-- Semantic memory create table memory ( id bigserial primary key, workspace_id uuid references workspaces(id) on delete cascade, text text not null, embedding vector(1536), source_message_id bigint references messages(id), ts timestamptz default now() ); 7) Backend Endpoints (OpenAPI sketch) Auth
GET /me β current Firebase user (after token verify).
Workspaces
POST /workspaces { name } β { id } (host = caller)
GET /workspaces β list memberships
POST /workspaces/{id}/invites β { code, url }
POST /invites/{code}/accept β join
Sessions (Parties)
POST /workspaces/{id}/sessions β { sessionId }
POST /sessions/{id}/end β is_active=false
Messages
GET /sessions/{id}/messages?after_seq=N&limit=100
WS /ws/sessions/{id} (see protocol below)
Actions (@act)
POST /actions { workspace_id, session_id, tool, args, connected_account_id, dry_run } β { action_id }
POST /actions/{id}/confirm (host only)
GET /actions/{id} β status/result
Composio
POST /composio/users (idempotent create; maps Firebase uid β Composio user)
POST /composio/connect-link { provider, user_id } β { url }
GET /composio/accounts?user_id=... β list connected accounts
Webhooks: /composio/webhook (optional) to update connection status
- WebSocket Protocol (Sessions) Connect: wss://api/ws/sessions/:id with header Authorization: Bearer
Client β Server events
{ "type": "user_msg", "client_event_id": "uuid", "text": "@ask what did we decide?" } { "type": "typing", "is_typing": true } Server β Client events
{ "type": "message", "seq": 123, "sender": {"id":"U1","name":"Reet"}, "role":"user|agent|system|transcript", "text":"β¦", "ts": 1730.12 } { "type": "presence", "users": [{"id":"U1","name":"Reet"}, ...] } { "type": "action_update", "id":"A1", "status":"awaiting_confirm|running|done|error", "result": { ... } } Ordering: Messages carry a server-assigned, monotonically increasing seq; the gateway broadcasts in seq order.
Idempotency: Server de-duplicates client_event_id within a short TTL.
- Agent Orchestration (Letta) System prompt (compact)
Identity, guardrails, role rules (host-only for @act), JSON tool schema expectations.
Context building
Window: recent chat (time/seq-based) + topβk from memory via pgvector.
Include speaker tags and workspace summary (rolled per session hourly).
Tools (examples)
create_github_template(repo_name, template)
create_notion_itinerary(trip_name, days, cities[], prefs{})
create_budget_sheet(sheet_name, items[])
Flow
Detect @ask vs @act in router.
@ask β Letta β stream back text; persist & optionally TTS later.
@act β Letta returns tool_call + args β server validates (Pydantic) β create Action(status=awaiting_confirm) β host confirms β run Composio β update via WS.
- Composio Integration (Multiβuser) On first login, create Composio User mapped to Firebase UID.
To link a tool, request Connect Link for that user & provider.
After OAuth, Composio stores tokens; you store only Connected Account IDs per user (optionally bind to workspace).
When executing, choose which connected_account_id to use (default: hostβs for workspaceβscoped actions).
Maintain audit log (action id β external IDs: repo URL, notion page ID, sheet ID).
- Frontend Notes (Next.js) Use Firebase SDK; persist ID token; attach to every REST/WS call.
Chat UI: list by seq; show presence & typing; host-only confirm button on pending actions.
βLink appsβ screen: list Composio accounts; connect flow opens link in new tab.
Error toasts on WS disconnects; auto-retry with backoff.
WS client helper
const ws = new WebSocket(${WS_URL}/ws/sessions/${sid});
ws.onopen = () => ws.send(JSON.stringify({ type: 'hello' }));
ws.onmessage = (e) => dispatch(JSON.parse(e.data));
function sendText(text: string) {
ws.send(JSON.stringify({ type: 'user_msg', client_event_id: crypto.randomUUID(), text }));
}
12) Local Dev β Quickstart
Requirements: Python 3.11, Node 18+, Docker, Postgres, Redis.
Backend
cd apps/api python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt alembic upgrade head # run migrations uvicorn app.main:app --reload --port 8080
Frontend
cd apps/web pnpm i pnpm dev Services (Docker Compose)
version: "3.9" services: db: image: pgvector/pgvector:pg16 environment: POSTGRES_PASSWORD: postgres ports: ["5432:5432"] redis: image: redis:7 ports: ["6379:6379"] 13) Deployment Web: Vercel (set env vars; proxy API URL).
API: Fly.io/Render/Heroku; scale one web + one worker.
DB: Neon/Supabase (enable pgvector).
Redis: Upstash.
Domain & CORS: lock to app.yourdomain.com.
Zero-downtime notes
Use sticky WS or stateless fanout via Redis (recommended).
Run Alembic migrations on deploy.
- Security & Compliance Verify Firebase ID token on every HTTP/WS request (backend).
Role checks server-side (host-only @act & confirm).
Store only Composio account IDs, never raw OAuth tokens.
Encrypt secrets at rest (KMS); rotate keys regularly.
PII: redact emails/phones in memory/doc logs if needed.
- Observability & Testing Logs: structured JSON; correlate by workspace_id, session_id, action_id.
Metrics: latency (ask/act), WS connections, tool success rate.
Tracing: OpenTelemetry for request β agent β action path.
Testing:
Unit: tool arg validators, router logic, role checks.
Integration: mock Letta, mock Composio; run against test Postgres/Redis.
E2E: Playwright for UI; WebSocket harness for message ordering/idempotency.
- Roadmap (stretch) Voice rooms (LiveKit); bot participant; streaming ASR/diarization β type: "transcript" events.
Delegated permissions; per-user tool accounts and policy engine.
Multi-agent parallel tasks; planner/worker agents with human-in-the-loop.
Summaries & knowledge base per workspace; vector memory compaction.
SSO (Org); billing; rate limits & quotas; export data.
- Appendix β Minimal Snippets FastAPI Firebase verify
from fastapi import Depends, Header, HTTPException import firebase_admin from firebase_admin import auth firebase_admin.initialize_app()
def require_user(authorization: str = Header(None)): if not authorization: raise HTTPException(401, "Missing token") try: scheme, token = authorization.split() assert scheme.lower() == "bearer" decoded = auth.verify_id_token(token) return {"uid": decoded["uid"], "email": decoded.get("email")} except Exception: raise HTTPException(401, "Invalid token") WS gateway skeleton
from fastapi import WebSocket from redis.asyncio import Redis
redis = Redis.from_url(os.getenv("REDIS_URL"))
async def ws_session(ws: WebSocket, sid: str, user: dict): await ws.accept() pubsub = redis.pubsub() await pubsub.subscribe(f"session:{sid}") try: async for msg in pubsub.listen(): if msg["type"] == "message": await ws.send_text(msg["data"].decode()) finally: await pubsub.unsubscribe(f"session:{sid}") Action dryβrun β confirm flow
POST /actions
action = Actions.create(..., status="awaiting_confirm")
host clicks confirm -> /actions/{id}/confirm
Actions.update(id, status="running") result = run_executor(tool, args, connected_account_id) Actions.update(id, status="done", result_json=result)
broadcast via WS
Analysis
View
Metric
- 26
- 15
- 2
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- FirebaseIn code
- HTMLIn code
- JavaScriptIn code
- ReactIn code
- FastAPIClaimed
- PythonClaimed
5 of 7 appear in the indexed code. 2 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
92 KB
Source files
16
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
decoder3064/Doryo
30 files Β· 2.3 MB Β· @ 6fd4545
Structure
Interface
4 files Β· 13%Screens, components and styles rendered to the user.
Application logic
7 files Β· 23%Domain rules, services and shared utilities.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here β open the file browser to check anything the diagram implies.
Languages
- JavaScript44%
- CSS35%
- Markdown19%
- HTML2%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
frontend/package.json
npm Β· 14- @composio/core
- buffer
- cra-template
- crypto-browserify
- firebase
- path-browserify
- process
- react
- react-dom
- react-router-dom
- stream-browserify
- vm-browserify
- +2 more
Declared in the repositoryβs manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This projectβs features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.