Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .beads/beads.jsonl
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,7 @@
{"id":"bd-critical-019","title":"[SECURITY] Auth Session Timeout Too Long","description":"5-minute OAuth session timeout creates large attack window.\n\n## Vulnerability\nIn cli-auth.ts:159:\n```typescript\nconst OAUTH_COMPLETION_TIMEOUT = 5 * 60 * 1000; // 5 minutes\n```\n\n## Risk\n- Long window for session hijacking\n- Active sessions can be enumerated\n- Stale PTY processes consume resources\n- Race conditions in token capture\n\n## Fix\n1. Reduce timeout to 2 minutes (plenty for OAuth)\n2. Add session invalidation on suspicious activity\n3. Implement one-time-use session tokens\n4. Clean up PTY immediately on error\n\n## Files\n- src/daemon/cli-auth.ts:159","priority":128,"status":"open","created_at":"2026-01-05T12:00:00Z","tags":["security","p00"],"depends_on":[]}
{"id":"bd-critical-020","title":"[SECURITY] Force Device Flow in Cloud Mode","description":"Standard OAuth redirect to localhost doesn't work in cloud - must force device flow.\n\n## Problem\nCodex OAuth redirects to localhost:1455, which:\n- Doesn't exist on user's machine when using cloud\n- CLI runs in container, not user's computer\n- Callback never reaches the CLI\n\n## Current Behavior\nDevice flow is opt-in via checkbox, defaults to OFF.\n\n## Fix\n1. Auto-detect cloud mode in frontend\n2. Force useDeviceFlow=true for providers that support it\n3. Hide the checkbox in cloud mode (always on)\n4. Show clear instructions for device flow\n\n## Files\n- src/dashboard/react-components/settings/WorkspaceSettingsPanel.tsx\n- src/cloud/api/onboarding.ts","priority":135,"status":"open","created_at":"2026-01-05T12:00:00Z","tags":["security","ux","launch-blocker","p00"],"depends_on":[]}
{"id":"bd-critical-021","title":"[ARCHITECTURE] Per-User Credentials in Shared Workspaces","description":"Multi-user workspaces share single owner's credentials - breaks team usage.\n\n## Current Problem\n- Credentials stored at USER level (credentials table: userId, provider, tokens)\n- Workspace provisioning injects owner's tokens as CONTAINER-LEVEL env vars\n- entrypoint.sh writes owner's creds to `${HOME}/.claude/` at container start\n- CLI tools (claude, codex) read from `$HOME/.{cli}/` - NOT env vars\n\n## Why Env Var Override Won't Work\nCLI tools are NOT SDKs. They have their own auth:\n- `claude` CLI reads `$HOME/.claude/.credentials.json`\n- `codex` CLI reads `$HOME/.codex/auth.json`\n- They IGNORE `ANTHROPIC_API_KEY` / `OPENAI_API_KEY` env vars\n\n## Solution: Per-User HOME Directories\n\nSet `HOME` env var to user-specific directory when spawning.\n\n### Impact on Git Operations\n\n**Git is SAFE** - uses credential helper with env vars:\n- `git-credential-relay` reads `CLOUD_API_URL`, `WORKSPACE_ID`, `WORKSPACE_TOKEN`\n- Does NOT read files from HOME\n- Git clone/push/pull will continue working\n\n**gh CLI needs config copied:**\n- Config at `${HOME}/.config/gh/hosts.yml`\n- Copy from container HOME to user HOME\n- Or rely on `GH_TOKEN` env var (already set)\n\n### prepareUserHome() Function\n\n```typescript\nasync function prepareUserHome(userId: string, provider: string): Promise<string> {\n const userHome = `/home/workspace-users/${userId}`;\n const containerHome = process.env.HOME || '/home/workspace';\n \n // 1. Fetch user's credentials from cloud\n const creds = await fetchUserCredentials(userId, provider);\n \n // 2. Write provider-specific credential file\n if (provider === 'anthropic') {\n await fs.mkdir(`${userHome}/.claude`, { recursive: true });\n await fs.writeFile(\n `${userHome}/.claude/.credentials.json`,\n JSON.stringify({ claudeAiOauth: creds })\n );\n } else if (provider === 'openai') {\n await fs.mkdir(`${userHome}/.codex`, { recursive: true });\n await fs.writeFile(\n `${userHome}/.codex/auth.json`,\n JSON.stringify({ tokens: { access_token: creds.accessToken, refresh_token: creds.refreshToken } })\n );\n }\n \n // 3. Copy gh CLI config (for `gh pr create` etc.)\n await fs.cp(`${containerHome}/.config/gh`, `${userHome}/.config/gh`, { recursive: true });\n \n return userHome;\n}\n```\n\n### Credential Format Reference\n\n**Claude** (`$HOME/.claude/.credentials.json`):\n```json\n{ \"claudeAiOauth\": { \"accessToken\": \"...\", \"refreshToken\": \"...\", \"expiresAt\": \"...\" } }\n```\n\n**Codex** (`$HOME/.codex/auth.json`):\n```json\n{ \"tokens\": { \"access_token\": \"...\", \"refresh_token\": \"...\" } }\n```\n\n### Database Changes\n```sql\nCREATE TABLE workspace_credentials (\n workspace_id UUID, user_id UUID, provider TEXT,\n credential_id UUID REFERENCES credentials(id),\n is_default BOOLEAN, UNIQUE(workspace_id, user_id, provider)\n);\n```\n\n### Credential Resolution Order\n1. User's own credential (if connected)\n2. Workspace default (if set by admin)\n3. Owner's credential (fallback to container HOME)\n\n## Files to Modify\n- src/daemon/agent-manager.ts - call prepareUserHome before spawn\n- src/daemon/credential-writer.ts (new) - per-provider credential format\n- src/cloud/api/workspaces.ts - credential resolution endpoint\n- src/cloud/db/schema.ts - workspace_credentials table\n- deploy/workspace/entrypoint.sh - already writes owner creds (reference)\n\n## Security: Daemon Must Auth to Cloud\nDepends on bd-critical-016 (daemon auth). Without auth, any process could request any user's credentials.","priority":155,"status":"open","created_at":"2026-01-05T13:00:00Z","tags":["critical","architecture","team","credentials","launch-blocker","p00"],"depends_on":["bd-critical-016"]}
{"id":"agent-relay-322","title":"Agent spawner doesn't apply model from agent profile","description":"When agents are spawned via dashboard API (POST /api/spawn), the spawner does NOT read agent config files or apply --model flags.\n\n## Root Cause\n- `buildClaudeArgs()` in `src/utils/agent-config.ts:133-157` correctly adds `--model` flag from agent profiles\n- This is only called for agents started via CLI wrapper (`agent-relay claude ...`)\n- Spawn API path: `server.ts:3003` → `spawner.spawn()` → NO agent config lookup\n\n## Expected Behavior\nAgent profile specifies `model: haiku`, agent should run on haiku model.\n\n## Actual Behavior\nAgent runs on default model (opus/sonnet) regardless of profile setting.\n\n## Fix\nAdd agent config lookup to `spawner.ts spawn()` method:\n1. Import `findAgentConfig` from `../utils/agent-config.js`\n2. Look up agent config by name\n3. If config.model exists, add `--model` to args array\n\n## Files\n- src/bridge/spawner.ts:248-275 (spawn method, CLI parsing)\n- src/utils/agent-config.ts (reference implementation)","priority":80,"status":"open","created_at":"2026-01-05T22:58:00Z","tags":["bug","spawner","agent-config"],"depends_on":[]}
{"id":"agent-relay-323","title":"gh CLI authentication not working in workspace containers","description":"The gh CLI fails with 401 Unauthorized when trying to create PRs or interact with GitHub API.\n\n## Error\n```\nfailed to migrate config: cowardly refusing to continue with multi account migration: couldn't get user name for \"github.com\"\nstatus code: 401 Unauthorized body: \"Bad credentials\"\n```\n\n## Context\n- There was a previous trajectory that supposedly resolved this issue\n- The fix is not working in practice\n- Affects agent ability to create PRs, issues, etc.\n\n## Expected Behavior\ngh CLI should authenticate using workspace credentials and allow GitHub operations.\n\n## Actual Behavior\n401 Unauthorized on any gh API call.\n\n## Investigation Needed\n1. Check previous trajectory for what was implemented\n2. Verify git-credential-relay is properly configured\n3. Check if GH_TOKEN env var is set correctly\n4. Verify gh CLI config in container\n\n## Files\n- deploy/workspace/entrypoint.sh (credential setup)\n- Git credential helper configuration","priority":90,"status":"open","created_at":"2026-01-05T23:00:00Z","tags":["bug","gh-cli","auth","regression"],"depends_on":[]}
{"id":"agent-relay-324","title":"Agent memory metrics showing 0 B for all agents","description":"The Agent Memory & Resources section on the metrics page shows 0 B memory usage, 0% CPU, Unknown trend, and 0 B peak for all agents.\n\n## Observed Behavior\n- Memory Usage: 0 B (all agents)\n- CPU: 0.0% (all agents)\n- TREND: ? Unknown (all agents)\n- PEAK: 0 B (all agents)\n- Lead agent shows PID: 829, others show PID: N/A\n\n## Expected Behavior\nActual memory and CPU metrics should be displayed for running agents.\n\n## Investigation Needed\n1. Check `/api/metrics/agents` endpoint response\n2. Verify PtyWrapper/spawner is collecting process metrics\n3. Check if `pidusage` or similar library is working\n4. Verify PID tracking for spawned agents\n\n## Files\n- src/dashboard-server/server.ts (metrics endpoint)\n- src/bridge/spawner.ts (agent tracking)\n- src/dashboard/app/metrics/page.tsx (display)","priority":75,"status":"open","created_at":"2026-01-05T23:05:00Z","tags":["bug","metrics","dashboard"],"depends_on":[]}
{"id":"agent-relay-325","title":"Mobile header not sticky when test input open and scrolling","description":"On mobile, the header isn't truly sticky. When user opens the test input and scrolls down, the header disappears.\n\n## Steps to Reproduce\n1. Open dashboard on mobile\n2. Open the test input\n3. Scroll down\n4. Header disappears (should stay sticky)\n\n## Expected Behavior\nHeader should remain sticky/fixed at top even when test input is open and user scrolls.\n\n## Investigation Needed\n1. Check header z-index vs test input z-index\n2. Check if test input container creates new stacking context\n3. Verify sticky positioning works with whatever container wraps the scrollable area\n4. May need position: fixed instead of sticky, or adjust container overflow\n\n## Files\n- src/dashboard/react-components/layout/Header.tsx\n- Related input/modal components","priority":70,"status":"open","created_at":"2026-01-05T23:15:00Z","tags":["bug","mobile","ui","header"],"depends_on":[]}
33 changes: 33 additions & 0 deletions .trajectories/active/traj_avqeghu6pz5a.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"id": "traj_avqeghu6pz5a",
"version": 1,
"task": {
"title": "Fix gh CLI authentication in workspace containers",
"source": {
"system": "plain",
"id": "agent-relay-323"
}
},
"status": "active",
"startedAt": "2026-01-05T23:14:00.755Z",
"agents": [
{
"name": "Lead",
"role": "lead",
"joinedAt": "2026-01-05T23:14:00.911Z"
}
],
"chapters": [
{
"id": "chap_1hosy42mvh0j",
"title": "Initial work",
"agentName": "Lead",
"startedAt": "2026-01-05T23:14:00.911Z",
"events": []
}
],
"commits": [],
"filesChanged": [],
"projectId": "84085b56a3fa",
"tags": []
}
6 changes: 6 additions & 0 deletions .trajectories/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@
"startedAt": "2026-01-05T23:10:52.953Z",
"completedAt": "2026-01-05T23:15:32.933Z",
"path": "/Users/khaliqgant/Projects/agent-workforce/relay/.trajectories/completed/2026-01/traj_fhx9irlckht6.json"
},
"traj_avqeghu6pz5a": {
"title": "Fix gh CLI authentication in workspace containers",
"status": "active",
"startedAt": "2026-01-05T23:14:00.755Z",
"path": "/workspace/relay/.trajectories/active/traj_avqeghu6pz5a.json"
}
}
}
2 changes: 1 addition & 1 deletion src/dashboard/app/metrics/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export default function MetricsPage() {
<div className="max-w-[1400px] mx-auto flex items-center justify-between">
<div className="flex items-center gap-4">
<Link
href="/"
href="/app"
className="flex items-center gap-2 text-text-muted text-sm font-medium px-3 py-2 rounded-md transition-all hover:text-accent hover:bg-accent/10"
>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
Expand Down
6 changes: 4 additions & 2 deletions src/dashboard/react-components/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -942,8 +942,8 @@ export function App({ wsUrl, orchestratorUrl }: AppProps) {

{/* Main Content */}
<main className="flex-1 flex flex-col min-w-0 bg-bg-secondary/50 overflow-hidden">
{/* Header - sticky on mobile */}
<div className="sticky top-0 z-50 flex-shrink-0">
{/* Header - fixed on mobile for keyboard-safe positioning, sticky on desktop */}
<div className="fixed top-0 left-0 right-0 z-50 md:sticky md:top-0 md:left-auto md:right-auto bg-bg-secondary">
<Header
currentChannel={currentChannel}
selectedAgent={selectedAgent}
Expand Down Expand Up @@ -973,6 +973,8 @@ export function App({ wsUrl, orchestratorUrl }: AppProps) {
</div>
)}
</div>
{/* Spacer for fixed header on mobile - matches header height (52px) */}
<div className="h-[52px] flex-shrink-0 md:hidden" />

{/* Content Area */}
<div className="flex-1 flex overflow-hidden min-h-0">
Expand Down
Loading