Status: Draft
Author: @bdougie
Date: 2026-02-15
Add team-based session sharing to Tapes, enabling collaborative observability across engineering teams. This includes search capabilities to discover sessions worth sharing, bulk export by day/project, and a dedicated shared sessions view in the deck.
Tapes captures valuable context about how teams interact with AI agents—prompts that worked, debugging sessions, cost patterns. Currently this knowledge is siloed per-developer. Teams need to:
- Learn from each other: See how teammates prompt effectively
- Debug collaboratively: Share problematic sessions for review
- Track team usage: Aggregate costs and model preferences
- Export for analysis: Pull sessions by day or project for reporting
Goal: Find and export sessions before sharing them.
Add search to the deck TUI and web UI, mirroring CLI capabilities:
┌─────────────────────────────────────────────────────────────────┐
│ 🔍 Search: contributor.info refactor │
├─────────────────────────────────────────────────────────────────┤
│ Filters: [Model ▾] [Project ▾] [Date Range ▾] [Status ▾] │
├─────────────────────────────────────────────────────────────────┤
│ ● Feb 15 contributor.info claude-sonnet $0.42 "refactor..." │
│ ○ Feb 14 contributor.info gpt-4o $0.18 "help me..." │
│ ○ Feb 12 tapes claude-sonnet $0.31 "add search" │
└─────────────────────────────────────────────────────────────────┘
[Export Selected] [Share Selected] [Cancel]
Search capabilities:
- Full-text search across message content
- Filter by model, project, date range, status, cost range
- Sort by date, cost, duration, tokens
- Multi-select for bulk operations
CLI equivalent:
# Search sessions
tapes search "refactor" --project contributor.info --since 7d
# Interactive search with fuzzy finder
tapes search -iExport sessions by day, project, or custom filter:
# Export all sessions from a specific day
tapes export --date 2026-02-14 -o feb-14-sessions.tape
# Export all sessions for a project
tapes export --project contributor.info -o contributor-sessions.tape
# Export with filters
tapes export --model claude-sonnet --since 30d -o sonnet-sessions.tape
# Export search results
tapes search "debugging" | tapes export -o debug-sessions.tapeExport formats:
.tape- Native bundle (gzipped JSON, includes merkle nodes).json- Plain JSON for analysis.csv- Spreadsheet-friendly (metadata only)
Bundle structure:
{
"version": 1,
"format": "tape-bundle",
"exported_at": "2026-02-15T18:00:00Z",
"exported_by": "bdougie",
"filter": {
"project": "contributor.info",
"date": "2026-02-14"
},
"sessions": [
{
"id": "abc123",
"project": "contributor.info",
"model": "claude-sonnet-4-20250514",
"start_time": "2026-02-14T09:30:00Z",
"end_time": "2026-02-14T09:45:00Z",
"total_cost": 0.42,
"input_tokens": 12500,
"output_tokens": 3200,
"nodes": [/* merkle nodes with content */],
"facets": {/* extracted facets */}
}
],
"checksum": "sha256:..."
}Goal: Share sessions with teammates and track what's been shared.
-- Users (synced from auth provider)
CREATE TABLE users (
id TEXT PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
name TEXT,
avatar_url TEXT,
github_username TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Teams
CREATE TABLE teams (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
slug TEXT UNIQUE NOT NULL,
owner_id TEXT NOT NULL REFERENCES users(id),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Team membership
CREATE TABLE team_members (
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
role TEXT NOT NULL DEFAULT 'member', -- 'owner', 'admin', 'member'
joined_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (team_id, user_id)
);
-- Team invites
CREATE TABLE team_invites (
code TEXT PRIMARY KEY,
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
created_by TEXT NOT NULL REFERENCES users(id),
email TEXT, -- optional: restrict to specific email
expires_at TIMESTAMP NOT NULL,
max_uses INTEGER DEFAULT 1,
uses INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Shared sessions (tracks what's been shared)
CREATE TABLE shared_sessions (
id TEXT PRIMARY KEY,
session_id TEXT NOT NULL, -- reference to local session root hash
team_id TEXT NOT NULL REFERENCES teams(id) ON DELETE CASCADE,
shared_by TEXT NOT NULL REFERENCES users(id),
title TEXT, -- optional title/description
note TEXT, -- optional sharing note
visibility TEXT NOT NULL DEFAULT 'team', -- 'team', 'public'
shared_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at TIMESTAMP, -- optional expiry
view_count INTEGER DEFAULT 0,
UNIQUE(session_id, team_id)
);
-- Session sync state (for pulling shared sessions locally)
CREATE TABLE session_sync (
session_id TEXT PRIMARY KEY,
team_id TEXT NOT NULL,
shared_by TEXT NOT NULL,
synced_at TIMESTAMP,
local_path TEXT -- path to local .tape file if downloaded
);Share a session:
# Share to your team
tapes share <session-id>
# → Shared to "Paper Compute" team
# → https://tapes.dev/t/papercompute/s/Xk9mZ2
# Share with a note
tapes share <session-id> --note "Good example of multi-file refactor"
# Share publicly (anyone with link)
tapes share <session-id> --public
# Share with expiry
tapes share <session-id> --expires 7d
# Bulk share from search
tapes search "debugging" --since 7d | tapes share --note "Debug sessions this week"In Deck UI:
┌─ Session: abc123 ─────────────────────────────────────────────┐
│ contributor.info • claude-sonnet • $0.42 • 15 min │
├───────────────────────────────────────────────────────────────┤
│ [Share to Team] [Export] [Copy Link] [Delete] │
│ │
│ Share to: [Paper Compute ▾] │
│ Note: Good example of multi-file refactor___________ │
│ Visibility: ○ Team only ● Anyone with link │
│ Expires: [Never ▾] │
│ │
│ [Cancel] [Share] │
└───────────────────────────────────────────────────────────────┘
New tab/section in deck showing sessions shared with you:
┌─ Deck ──────────────────────────────────────────────────────────┐
│ [My Sessions] [Shared with Me] [Team Activity] │
├─────────────────────────────────────────────────────────────────┤
│ 🔍 Search shared sessions... │
├─────────────────────────────────────────────────────────────────┤
│ TODAY │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 alice shared "Debug session for issue #423" │ │
│ │ tapes • claude-sonnet • $0.31 • 2h ago │ │
│ │ "Check out how I traced the race condition" │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 bob shared "Cost optimization experiment" │ │
│ │ api-service • gpt-4o-mini • $0.08 • 5h ago │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │
│ THIS WEEK │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 👤 alice shared 3 sessions from contributor.info │ │
│ │ Feb 12 • Total: $1.24 │ │
│ └─────────────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ [Import All] [Mark as Read] Showing 5 of 12 │
└─────────────────────────────────────────────────────────────────┘
Features:
- Chronological feed of shared sessions
- Inline notes from sharer
- One-click import to local deck
- Filter by teammate, project, date
- Unread indicators
- Search within shared sessions
# Create a team
tapes team create "Paper Compute"
# → Team created! Invite link: https://tapes.dev/join/abc123
# Generate invite link
tapes team invite
# → https://tapes.dev/join/xyz789 (expires in 7 days)
# Invite by email
tapes team invite alice@example.com
# Join a team
tapes team join <code>
# List teams
tapes team list
# List members
tapes team members
# Leave a team
tapes team leave
# Remove member (admin/owner only)
tapes team remove alice@example.comVisibility levels:
| Level | Who can view |
|---|---|
private |
Only you |
team |
Team members only |
public |
Anyone with the link |
Auto-visibility (optional, configurable):
# Set default visibility for a project
tapes config set default-visibility.contributor.info team
tapes config set default-visibility.personal-notes privateAccess control logic:
func canViewSession(viewer User, session SharedSession) bool {
// Owner can always view
if session.SharedBy == viewer.ID {
return true
}
// Check visibility
switch session.Visibility {
case "public":
return true
case "team":
return isTeamMember(viewer.ID, session.TeamID)
default:
return false
}
}Goal: Aggregate insights across the team.
┌─ Team: Paper Compute ───────────────────────────────────────────┐
│ [Overview] [Members] [Shared Sessions] [Settings] │
├─────────────────────────────────────────────────────────────────┤
│ 📊 Last 30 Days [▾ 30 days] │
├──────────────┬──────────────┬──────────────┬───────────────────┤
│ Sessions │ Total Cost │ Avg/Session │ Total Tokens │
│ 247 │ $142.50 │ $0.58 │ 2.4M │
│ ↑ 23% │ ↑ 15% │ ↓ 8% │ ↑ 31% │
├──────────────┴──────────────┴──────────────┴───────────────────┤
│ │
│ Activity by Member │ Model Usage │
│ ████████████ alice 42% │ ████████ claude-sonnet 58% │
│ ████████ bob 31% │ ████ gpt-4o 24% │
│ ████ charlie 18% │ ██ gpt-4o-mini 12% │
│ ██ you 9% │ █ claude-haiku 6% │
│ │ │
├───────────────────────────────┴─────────────────────────────────┤
│ 📈 Daily Activity │
│ ▂▃▅▇█▆▄▃▂▃▄▅▆▇█▇▅▄▃▂▁▂▃▄▅▆▇█▆ │
│ |-------- Feb --------| │
├─────────────────────────────────────────────────────────────────┤
│ Top Projects │ Recent Shared │
│ 1. contributor.info $45.20 │ • alice: "Debug #423" 2h ago │
│ 2. tapes $38.90 │ • bob: "Cost exp" 5h ago │
│ 3. api-service $22.15 │ • alice: 3 sessions Feb 12 │
└─────────────────────────────────────────────────────────────────┘
type TeamStats struct {
TotalSessions int `json:"total_sessions"`
TotalCost float64 `json:"total_cost"`
TotalTokens int64 `json:"total_tokens"`
AvgSessionCost float64 `json:"avg_session_cost"`
PreviousPeriod *TeamStats `json:"previous_period,omitempty"`
}
type MemberStats struct {
UserID string `json:"user_id"`
Name string `json:"name"`
Sessions int `json:"sessions"`
TotalCost float64 `json:"total_cost"`
TopModel string `json:"top_model"`
TopProject string `json:"top_project"`
SharedCount int `json:"shared_count"`
}
func (q *Query) TeamDashboard(ctx context.Context, teamID string, period time.Duration) (*TeamDashboard, error) {
// Parallel queries for efficiency
var stats TeamStats
var members []MemberStats
var modelUsage []ModelUsage
var activity []DayActivity
var recentShared []SharedSession
// ... aggregate from shared_sessions joined with session data
}- Add search to deck TUI (filter bar, fuzzy search)
- Add search to deck web UI
- Implement bulk export command (
--date,--project,--since) - Support export formats:
.tape,.json,.csv - Pipe support:
tapes search | tapes export
- User accounts with GitHub OAuth
- Teams CRUD (create, invite, join, leave)
-
shared_sessionstable and sync -
tapes sharecommand - Share modal in deck UI
- "Shared with Me" view in deck
- Web viewer at tapes.dev/t//s/
- Team dashboard queries
- Dashboard UI in deck (TUI + web)
- Activity charts by member
- Model/project breakdowns
- Export team reports
# Auth
POST /auth/github GitHub OAuth callback
GET /auth/me Current user info
# Teams
POST /teams Create team
GET /teams List user's teams
GET /teams/:id Get team details
PATCH /teams/:id Update team
DELETE /teams/:id Delete team
# Members
GET /teams/:id/members List members
POST /teams/:id/invite Create invite
DELETE /teams/:id/members/:uid Remove member
# Sharing
POST /sessions/share Share a session
GET /shared List sessions shared with me
GET /shared/:id Get shared session details
DELETE /shared/:id Unshare a session
# Dashboard
GET /teams/:id/stats Team statistics
GET /teams/:id/activity Activity timeline
- Sync model: Pull-on-demand vs. background sync for shared sessions?
- Storage: Shared sessions stored on tapes.dev, or P2P between team members?
- Content privacy: Share full message content or metadata only by default?
- Notifications: How to notify when someone shares a session? (email, in-app, webhook)
- Offline: How do shared sessions work when offline?
- Sessions may contain sensitive data (API keys, internal URLs, proprietary code)
- Sharing should require explicit action, never automatic
- Team admins can audit shared sessions
- Support for redacting sensitive content before sharing
- Shared sessions should be deletable by sharer