Feat/claude oauth login#58
Open
jeewoo-lee wants to merge 254 commits into
Open
Conversation
- Add VERIFY_MAX_CONCURRENT_JOBS env var (default: 1, preserves current behavior) - Add VERIFY_DB_POOL_MIN/MAX env vars with auto-sizing from concurrency - When concurrency > 1, N worker coroutines each claim jobs independently via FOR UPDATE SKIP LOCKED; each creates its own AsyncDaytona client - Single-worker mode (default) keeps shared client for zero overhead - Coordinator coroutine handles stale-job recovery separately - Add duration logging and worker ID prefixes to all log lines - Fix daytona_sdk import to try daytona_sdk before daytona - Add deployment docs: services, env vars, scaling modes - Add 7 tests: pool sizing, _run_one_job, daytona crash recovery, concurrent claim independence Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Transient Daytona errors (CPU quota, rate limits) now retry up to VERIFY_SANDBOX_MAX_RETRIES (default 3) with exponential backoff (VERIFY_SANDBOX_RETRY_BACKOFF * attempt, default 30s base). Non-retryable errors (no fork, disabled) still fail immediately. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Instead of giving up after N retries and marking runs as error, the verifier now retries indefinitely until Daytona has capacity. Backoff caps at SANDBOX_RETRY_BACKOFF * SANDBOX_MAX_RETRIES (default 90s). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Daytona free tier doesn't allow programmatic sandbox deletion. Now falls back to stopping the sandbox, which frees CPU immediately. Stopped sandboxes auto-delete via Daytona's auto_delete_interval. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
These were lost during a linter revert: - git fetch all refs + checkout specific commit for agent fork - HIVE_SETUP_CMD sandbox setup step before prepare.sh Also change default VERIFY_MAX_CONCURRENT_JOBS from 1 to 3. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep both sides: deployment's auth/private-tasks/items changes and verify-runs' verification system. All conflict markers resolved. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: server-side verification via Daytona sandboxes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: restore /verify-old endpoint
…se_sha Old forks created before SHA tracking have base_sha=NULL. The admin can now pass task_repo_sha in the request body as a fallback. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: restore /verify-old endpoint + base_sha fallback
- API: GET /tasks accepts ?type=public|private query param - CLI: hive task list --public and --private flags - UI: main page and Public Tasks use type=public, profile uses /tasks/mine
Backend: - Add verification_enabled, leaderboard_verified, leaderboard_unverified to /context endpoint - Add section=all query support for mixed verified+unverified leaderboard UI: - All/Verified toggle on graph and leaderboard - Green checkmark on verified runs, pending/failed badges - Effective score display (verified_score if available, else self-reported) - Non-verified tasks show default UI unchanged Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: verification UI + context endpoint
- Graph tooltip shows checkmark (verified), pending/running/error badges - Graph endpoint returns verification_status for each node - useGraph maps verification_status to Run objects - Verified leaderboard uses /runs?section=verified (paginated) instead of context endpoint (was limited to 5) - Leaderboard uses verified_score for section=verified Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: graph tooltip badges + verified leaderboard pagination
Graph was filtering by verifiedRunIds from context (limited to 5). Now filters by r.verified on each graph node directly, showing all verified runs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: graph shows all verified runs instead of 5
Users can create one sandbox per task via POST /api/tasks/{task_id}/sandbox.
The sandbox uses an existing Daytona snapshot, bootstraps Node.js + Claude Code
at startup, clones the task repo, and returns SSH credentials for the browser
terminal. GET refreshes expired SSH tokens, DELETE cleans up both Daytona and DB.
Access control: sandboxes are scoped to (task_id, user_id) — users can only
see and manage their own sandboxes.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add auth section (16 endpoints: signup, login, OAuth, API keys) - Fix agent token: UUID, not equal to agent_id - Add POST /tasks (archive upload), POST /tasks/private, GET /tasks/mine - Add DELETE /tasks, DELETE /runs, POST /verify-old endpoints - Add verification fields to run responses and Task Verification Config - Add search type filter, agent/since params - Add context verification_enabled, split leaderboards - Add Deployment section with server/verifier env vars - Update CLI: auth login/claim/switch, task --public/--private - Update CLI: feed --run, comment --parent-type, search inline filters - Update CLI: swarm --dangerously-skip-permissions, config structure - Remove outdated Items/kanban references per prior discussion
- Add auth section (16 endpoints: signup, login, OAuth, API keys) - Fix agent token: UUID, not equal to agent_id - Add POST /tasks (archive upload), POST /tasks/private, GET /tasks/mine - Add DELETE /tasks, DELETE /runs, POST /verify-old endpoints - Add verification fields to run responses and Task Verification Config - Add search type filter, agent/since params - Add context verification_enabled, split leaderboards - Add Deployment section with server/verifier env vars - Update CLI: auth login/claim/switch, task --public/--private - Update CLI: feed --run, comment --parent-type, search inline filters - Update CLI: swarm --dangerously-skip-permissions, config structure - Remove outdated Items/kanban references per prior discussion
Replace TEXT task IDs with SERIAL integer PKs and owner-scoped slugs,
following GitHub's owner/repo URL model. Public tasks are owned by
the platform namespace ('hive'); private tasks are owned by the
creating user's UUID. Slugs are unique per owner, allowing duplicates
across owners.
- db: tasks.id TEXT -> SERIAL, add slug + owner columns with
UNIQUE(owner, slug); migrate FK columns on forks/runs/posts/claims/
skills/items to INTEGER; one-shot migration backfills existing rows
- server: all 24+ task-scoped routes change from /tasks/{task_id}/...
to /tasks/{owner}/{slug}/...; resolve owner+slug to integer task_id
internally; fork naming uses task['slug']
- cli: helpers expose _task_ref()/split_task_ref(); .hive/task stores
owner/slug; bare slugs auto-prefix with 'hive/' for legacy compat
- ui: Task type gains slug/owner; routes restructured to
/task/[owner]/[slug]; profile page links to /task/{uuid}/{slug}
instead of /me/{uuid}/{slug}
- tests: update fixtures and assertions for new schema and routes
claude-agent-acp prefixes MCP tool names with mcp__{server}__. Use
endsWith("ask_user") instead of exact match.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Tool input may be JSON string, nested under arguments/input, or a direct object. Try all formats. Also extract input from classified.data .input and .arguments in addition to .rawInput. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move the AskUserWidget from inline message rendering to above the text input field — matching Cursor's UX where interactive questions appear at the bottom near the input, not buried in the chat history. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When user clicks an "Other..." option in the ask_user widget, it expands to show a text input inline. Detects options starting with "other" (case-insensitive). Enter submits the custom text. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Reduce container padding (px-4/py-4 → px-3/py-3), option padding (px-3/py-2.5 → px-2.5/py-1.5), letter badge size (w-6/h-6 → w-5/h-5), and spacing (space-y-2 → space-y-1.5) for a more compact widget. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ging Streamable HTTP transport may use GET for SSE stream or hit sub-paths. Added handlers to log what claude-agent-acp actually requests. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Other option renders as always-visible inline text field - Questions header at top of card - Skip and Continue buttons at bottom right - Compact paddings throughout
Streamable HTTP transport requires: 1. Long-lived GET SSE stream (was ending immediately, causing reconnect loop and blocking POST requests) 2. mcp-session-id header on all responses for session tracking
Remove the aggressive reorder that moved all thinking parts to the top of each assistant message. Log events are already chronological — just use them as-is.
Log stores reasoning after text, but runtime shows thinking before text. Swap only adjacent (text, thinking) pairs — not all thinking to top (which broke multi-cycle turns).
ACP uses SSE transport which requires an 'endpoint' event to know where to POST tool calls. Without it, the client connects via GET but never sends POSTs.
SSE transport checks that endpoint origin matches connection origin. Using the host header gave the internal backend URL, causing origin mismatch. Use relative path /api/mcp instead — resolves correctly against the client's connection URL.
tool_call arrives with empty input, but tool_result has rawInput with question/options/mode. Save rawInput as input on the tool part during tool_call_update. Show widget whenever input has question data, regardless of tool status.
MCP blocking doesn't work, so the widget now sends the user's answer as a regular chat message via sendMessage. The agent receives it in the next turn. Removed apiFetch/apiPostJson dependency from the widget.
All modes now use the same visual language: - select: A/B/C badges, click to submit - confirm: A) Yes, B) No badges, click to submit - multi_select: A/B/C badges, toggle multiple, Submit button - text: badge + inline text field (like Other...) Extracted OptionButton helper for reuse across modes.
MCP server simplified to just initialize + tools/list + immediate tools/call response. No blocking, no questions endpoints, no asyncio. Widget now tracks answered tool IDs — disappears after user submits. Answer sent as chat message to the agent.
Remove font-medium from question text so it matches the regular chat message weight.
Update tool schema from single question to questions array. Each question has its own mode, options, and question text. Agent can ask multiple questions at once in one tool call.
- Paginated view with "1 of N" navigation and progress dots - Selecting an answer auto-advances to next question - Can go back and change previous answers via arrows or dots - Submit button only on last question, sends all answers at once - Supports both questions array and single question format - All modes (select, confirm, multi_select, text) use consistent lettered badge style
- Skip button styled as grey button matching Submit - Remove progress dots - Add Next button for multi_select and text modes - Text mode uses plain input without letter badge - Click same option to unselect in select/confirm modes - Auto-advance only on select, not deselect - Multi-select supports Other... inline text field - Fix React setState-during-render warning in multi_select
Submit enabled when at least one question is answered. Skipped questions send empty string instead of blocking submission.
Sandbox lifecycle is now tied to the workspace, not the first agent. Creating a cloud workspace provisions the agent-sdk sandbox synchronously and rolls back (502) on failure. Agents create/delete only flip their session on the existing sandbox, which stays alive when there are zero agents and is destroyed only when the workspace is deleted. Also fixes the display bug where cloud-workspace agents were stored as type='local' and rendered as "Local" in the UI. Agent type now inherits from the workspace type. Reserves the "persistent" type slot for a future admin-owned long-lived sandbox. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
POST /workspaces returns immediately and fires a background task to
provision the sandbox. The workspace page navigates instantly and
shows a loading overlay while polling GET /workspaces/{id} every 2s;
the overlay clears once sdk_sandbox_id is populated.
_workspace_sdk_connect still has the NULL-sandbox fallback so an agent
created before the background task finishes gets provisioned on-demand.
Both paths are idempotent (check-before-provision; UPDATE ... WHERE
sdk_sandbox_id IS NULL).
On provision failure, the workspace stays in pending state (sdk_sandbox_id
remains null) rather than rolling back — the user can delete it or retry
creating an agent to re-trigger provisioning.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cloud workspaces created before async provisioning landed — or whose
provisioning errored and was silently swallowed by the previous
implementation — had type='cloud' + sdk_sandbox_id=NULL with no
background task running. The new loading overlay revealed these
permanently, spinning forever.
GET /workspaces/{id} now nudges _provision_workspace_sandbox when it
sees a cloud workspace with no sandbox, so visiting the page revives
the provision. Added a module-level in-flight set so concurrent polls
coalesce into one provision call; the early sdk_sandbox_id check and
UPDATE ... WHERE sdk_sandbox_id IS NULL remain as second-line guards.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Users connect their Claude account once from the profile settings page;
hive runs `claude setup-token` under a PTY on the server, surfaces the
OAuth URL to the UI, accepts the pasted code, and persists the resulting
token — encrypted with the existing Fernet helper — in a new
claude_oauth_tokens table keyed to user_id.
Subsequent cloud workspaces (and their agents) are authenticated with
that user's Claude subscription instead of the server's shared
ANTHROPIC_API_KEY, via the oauth_token field already wired into the
agent-sdk /sessions call.
If a user tries to create a cloud workspace without having connected
Claude yet, the backend returns 402 + {auth_required: true} and the
CreateWorkspaceModal embeds the connect flow inline, then retries.
Changes:
- db.py: new table claude_oauth_tokens (INTEGER PK REFERENCES users(id),
token_encrypted, created_at, expires_at). Strictly additive; zero
ALTER TABLE on existing tables.
- claude_oauth.py (new): PTY broker around `claude setup-token`. Spawns
via pty.fork(), scrapes the OAuth URL from the Ink TUI hyperlink
escape, accepts a user-pasted code via a second RPC, captures the
final sk-ant-oat01-… token. Per-user supersede on concurrent starts;
10-min TTL.
- main.py: POST /auth/claude/start, POST /auth/claude/code,
GET /auth/claude/status, DELETE /auth/claude — modeled on the
existing github OAuth routes, reusing _encrypt/_decrypt. Workspace
creation short-circuits to 402 + {auth_required: true} when the
user has no token. _workspace_sdk_connect now decrypts + passes
oauth_token to agent-sdk.
- Dockerfile.server: adds nodejs + `npm i -g @anthropic-ai/claude-code`
so the PTY broker has the CLI available.
- ui: four new hooks in auth.tsx (claudeStatus / Start / SubmitCode /
Disconnect); new ClaudeConnectModal; ProfilePanel shows a "Claude
Account" row next to GitHub with Connect / Reconnect / Disconnect
buttons. CreateWorkspaceModal handles 402 by opening the connect
modal inline and retrying on success.
- tests/server/test_claude_oauth.py: URL + token regex coverage and
the session-store guards (TTL reap, per-user isolation).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.