mirror of
https://github.com/browseros-ai/BrowserOS.git
synced 2026-05-19 19:41:06 +00:00
fix/patch-cli-sync
42 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
ba60bf466f |
feat(agents): rich command-center rows + home grid + dead-code sweep (#879)
* feat(agents): rich-info command center rows + pin/PATCH/adapter-health backbone
Splits AgentRowCard from a 271-line monolith into a shallow tree of
single-responsibility sub-components under `agent-row/`:
AgentTile, AdapterHealthDot, PinToggle, AgentTitleRow,
AgentSparkline, AgentSummaryChips, AgentLastMessage, CwdChip,
AgentTokenSummary, AgentMetaRow, AgentErrorPanel, AgentActions
Adds the data each row consumes:
- pinned: boolean field on AgentDefinition + FileAgentStore.update
+ new PATCH /agents/:id route. useUpdateHarnessAgent mutation
optimistically updates the listing cache so the star flips
instantly; rolls back on error.
- Listing payload extended with lastUserMessage, cwd, tokens
(cumulative + last7d shape — last7d zero-filled until the
activity ledger lands), turnsByDay/failedByDay (zero-filled),
lastError/lastErrorAt, activeTurnId. AcpxRuntime grows a
getRowSnapshot() that reads cwd + cumulative tokens + last user
message from the session record in one pass.
- Adapter health: in-memory AdapterHealthChecker probes
`claude --version` / `codex --version` with a 2s timeout and
caches results for 5 min. /adapters response carries
{ healthy, reason?, checkedAt }. Tile-corner dot exposes the
state via HoverCard; openclaw inherits health from the gateway
snapshot already on the page.
Sub-components are pure: card itself owns no state. Sort order
becomes pinned-first, then recency. HoverCard is the workhorse for
keeping rows compact while exposing depth (full message, token
breakdown, daily turn list, error stack, adapter reason).
* refactor(agents): tighten command-center row design + cut redundant affordances
User feedback round 1:
1) Two green dots on the tile (health + liveness) was confusing. Health
moves out of the tile entirely and surfaces as an inline 'Unavailable'
chip in the model line — silent when the adapter is healthy, with a
warning amber chip + HoverCard reason when not. The tile now shows
one signal: liveness.
2) The last-user-message HoverCard wasn't telegraphing intent. Drop the
HoverCard. The line is informational, italic, with a leading quote
glyph so the row reads like a conversation snippet. To see the full
message the user opens the chat (which is the action they want next
anyway).
3) Resume + Chat were duplicate CTAs. Single primary action per row:
Resume (filled, accent-orange, with a pulsing dot) replaces Chat
when there's an active turn. Both navigate to /agents/:id but the
row tells the user which action they're taking.
4) Tokens weren't visible because the row gated on last7d.requestCount,
which is zero until the activity ledger ships. Switch to lifetime
tokens (which we have today). Drop the '7d stats:' framing — talking
about a window we can't compute would be misleading. The HoverCard
surfaces input/output split + a footnote that per-window stats land
in a follow-up.
5) CWD was rendering the server's own running directory, which is
meaningless to users. Hide it from the row entirely. The cwd field
still rides in the listing payload for future surfaces (chat panel,
debug view) — only the row stops rendering it.
Aesthetic refinements while we're here:
- Whole card carries state, not just the tile: working rows get an
accent-orange tinted border with a soft glow, error rows tint
destructive, idle rows lift on hover.
- Pin star fades in on hover (group-hover) when unpinned and stays
solid amber when pinned — keeps the rail calm by default.
- Tabular-nums on token figures so columns visually align across rows.
- Drop CwdChip and AdapterHealthDot files: no callers left.
* fix(agents): align row title flush-left whether pinned or not
Pin star moved from leading the title to trailing the badges, and
hidden from layout entirely (`hidden group-hover:inline-flex`) when
unpinned. The previous `opacity-0` rule kept the star reserving its
`size-6` slot, which left every unpinned title indented relative to
the model / preview / meta lines underneath it. Title now flushes
left in both states; pinned star stays solid amber so the signal
isn't hidden, and unpinned reveals an outline star on row hover for
the toggle affordance.
* fix(agents): keep pin-toggle slot reserved so row height is constant
Switching the unpinned star from `hidden group-hover:inline-flex`
to `opacity-0 group-hover:opacity-100`. The hidden/show variant was
collapsing the title row's height when the star wasn't rendered,
which made every card below visibly shift on hover. Always rendering
the button (with opacity-only visibility) keeps the row's vertical
metrics constant; the title still flushes left because the slot is
trailing, not leading.
Card hover effect (-translate-y + shadow-md) restored — the layout
shift wasn't coming from the card hover; it was the pin slot
appearing and disappearing.
* fix(agents): quieten row hover — border-tint only, no lift, no shadow
Drop the `-translate-y-px` and `hover:shadow-md` from the row card
plus the working-state inner ring. The translate + shadow grow
combination was visibly noisy as the cursor moved through the rail —
each row 'lifted' as you passed over it. Hover now just tints the
border in accent-orange/30; working and error states keep their
distinct border colours but no inner ring. Card height and shadow
stay constant in every state, so the rail reads as a calm vertical
list of cards.
* feat(home): rich Recent Agents grid + dead-code sweep
The /home Recent Agents grid was a placeholder shell. Every 'rich'
field on the card (lastMessage, lastMessageTimestamp, activitySummary,
currentTool, costUsd) was wired to undefined because AgentCommandHome
called `buildAgentCardData(agents, status?.status, undefined)` — the
dashboard arg has been hard-coded undefined since the harness
migration. Repointing the grid at `useHarnessAgents` + `useAgentAdapters`
gives every card the same enriched data the rail uses.
What the new card shows per agent:
• Adapter glyph tile + liveness dot (working pulses; asleep is
hollow; error is red)
• Name + Working pill (when active)
• Adapter · model · reasoning summary line, with an inline
Unavailable chip + HoverCard reason when the adapter binary
isn't on $PATH
• Italic last-user-message preview (line-clamp-2, leading quote
glyph) — same visual language as the rail
• Footer: 'X ago' + state chip (Asleep / Attention) OR a Resume
button (orange, with pulsing dot) when activeTurnId is non-null
Sort on the home grid is active-turn → recency. Pinning is NOT a
sort key here (and there's no pin indicator on the card) — pinning
belongs to the rail at /agents; the home page is action-oriented
and trusts active-turn + recency to surface the right agent.
Dead code removed:
• useAgentDashboard.ts (96 lines, no callers; subscribed to the
dead /claw/dashboard/stream from the OpenClaw-only era)
• useAgentCardData.ts (the dashboard-merge shim; passed undefined
every call so all enriched fields landed as undefined)
• AgentCard.tsx (AgentCardExpanded replaced by HomeAgentCard;
AgentCardCompact had no callers — the dock's compact mode was
never used)
• AgentCardData interface dropped from lib/agent-conversations/
types.ts; the new card consumes HarnessAgent directly
Visual language stays continuous between rail and grid: same
<AgentTile>, same <LivenessDot>, same italic-quote message
preview, same orange Resume button with a pulsing dot.
|
||
|
|
a228c278c6 |
feat(agents): background-resilient chat — turns survive tab disconnect (#863)
* feat(agents): decouple chat turn lifecycle from SSE response
Introduce a per-process ActiveTurnRegistry that owns each agent turn's
lifecycle and a ring-buffered event stream, so chat tabs that close,
refresh, or navigate away no longer cancel the in-flight turn. New
endpoints:
POST /agents/:id/chat starts a turn (now returns 409 when
one is already running, with the
active turnId for attaching)
GET /agents/:id/chat/active reports the running turn for a UI
that just mounted
GET /agents/:id/chat/stream subscribes to a turn; supports
Last-Event-ID resume via per-event
seq ids
POST /agents/:id/chat/cancel explicit cancel — fetch abort no
longer affects the underlying turn
The chat hook now captures X-Turn-Id, tracks lastSeq from SSE id lines,
re-attaches on mount when the server still has an active turn, and
routes Stop through the cancel endpoint. The runtime call uses the
registry's per-turn AbortController instead of the HTTP request signal,
which is the core decoupling that lets turns outlive their initiator.
* feat(agents): add ActiveTurnRegistry primitive backing the new chat lifecycle
The previous commit referenced these files in tests and the harness
service but global gitignore swallowed them on the first add.
The registry owns the per-turn ring buffer (drop-oldest, terminal frame
preserved), the per-turn AbortController, and subscriber fan-out used
by /chat/stream resume.
|
||
|
|
91d3285aa0 |
feat: add ACP agent harness (#849)
* feat: add acp agent runtime spike * feat: add agent harness catalog * feat: persist harness agents in json * feat: persist agent transcripts * feat: route harness service through agent records * feat: expose generic agent harness routes * feat: add harness agent frontend api * feat: create harness agents from agents page * feat: chat with persisted harness agents * chore: remove obsolete agent profile spike * chore: self-review fixes * fix: combine openclaw and harness agents UI * refactor: split agents page components * fix: hide persisted harness turns |
||
|
|
ddbb2cf492 |
feat(agent): composer attachments + server-side outbound message queue (#826)
* feat(agent): attach images and text files to chat messages
Adds end-to-end support for image and text file attachments in the chat
composer, with the staged files round-tripping through the OpenClaw
gateway as OpenAI-compatible content blocks and persisting in the JSONL
so they show up in the historical view.
Server
- HTTP client: new OpenClawChatContentPart union and a buildUserContent
helper that emits multimodal content arrays when messageParts is
supplied, falls back to the legacy string content otherwise.
- Service: chatStream takes an optional messageParts array and forwards
it; BrowserOSChatHistoryItem gains an attachments field.
- JSONL reader: PiContentBlock learns the OpenAI image_url and Anthropic
image source/data shapes; user messages now emit user.attachment
events that the history mapper accumulates onto the next user item.
- Route: validates an inbound attachments[] (kind/mime/size/count),
inlines text-shaped files as <attachment> blocks in the message body,
attaches images via image_url parts. Replaces the immediate 409 on
active monitoring session with a 30s waitForSessionFree(agentId) wait
(registry now exposes onSessionEnd) so cron/hook contention does not
reject a user-chat send outright. Returns 503 if the wait times out.
Client
- New lib/attachments.ts: validateAttachment / compressImageIfNeeded
(canvas downscale to 2048px long edge, JPEG 0.85 re-encode for >1.5
MB inputs) / stageAttachment / stageAttachments that produces the
staged-attachment shape the composer renders and the payload the
server accepts.
- ConversationInput: drag-and-drop, paperclip button, clipboard paste,
staged attachment chip strip with thumbnails for images and a
paperclip+name chip for text files. Send button enables on either
text or attachments. Drop-zone overlay during drag.
- chatWithAgent forwards attachments[]; useAgentConversation.send
accepts a SendInput shape and renders user attachments on the
optimistic streaming turn via MessageAttachments / MessageAttachment.
- ClawChatMessage groups historical attachment parts into a single
MessageAttachments strip, ordered before reasoning/tools/text.
- claw-chat-types adds an attachment ClawChatMessagePart variant; the
history mapper emits attachment parts first and skips the text part
when the user only sent media.
- AgentCommandHome forwards the new SendInput shape — home composer
drops attachments at the boundary in v1 (the conversation page is
where staging is most useful; carrying bytes through the URL bar
is not sensible).
Limits: 10 attachments per message, 5 MB per image (post compression),
1 MB per text file, mime types png/jpeg/webp/gif and text/* +
application/json. PDFs and other binaries are deferred to v2.
* feat(agent): outbound message queue for chats while agent is mid-turn
Lets users keep typing and submitting messages while the agent is still
streaming a previous turn. Each press is appended to a single-flight
queue and dispatched as soon as `streaming` flips false; the queued
state renders as a strip above the composer so the user sees what's
pending vs. what's already sending.
- New `useOutboundQueue` hook owns the queue, the worker effect, and
cancel/retry actions. Single-flight by design — a re-entrancy ref
guard prevents two simultaneous dispatches when `streaming` flickers.
- Composer (`ConversationInput`) accepts optional `outboundQueue`,
`onCancelQueued`, `onRetryQueued` props. When the queue is provided
the send-button gate stops blocking on `streaming`; the spinner stays
as the visual cue that the agent is still busy. Legacy direct-send
callers keep the old streaming-blocks-send semantic.
- Renders an OutboundQueueStrip above the staged-attachment strip with
per-item status (queued / sending / failed), a cancel button on
queued items, and retry + discard on failed items.
- AgentCommandConversation wires `onSend` to `queue.enqueue` and routes
the home composer's `?q=` initial-message handoff through the queue
too, so it inherits the same single-flight serialization.
The server-side `waitForSessionFree` (added with attachments) and this
client-side queue together cover both contention sources: cron / hook
turns and back-to-back user sends. Persistence across reloads is
intentionally out of scope for v1 — losing the queue on extension
reload is documented as a known limitation.
* feat(server): server-side outbound message queue
Replaces the client-only React-state queue from
|
||
|
|
711934555d |
feat(agent): enrich chat UI with tool activity, reasoning duration, and cost (#825)
* feat: pass per-turn cost and token data through chat history items
- Add costUsd, tokensIn, tokensOut to BrowserOSChatHistoryItem (server)
- Pass through from JSONL agent.message events in jsonlEventsToHistoryItems()
- Add same fields to client-side BrowserOSChatHistoryItem and ClawChatMessage
- Map cost/token data in mapHistoryItemToClawMessage()
Data flows: JSONL message.usage → server history item → API response →
client ClawChatMessage. Available for rendering in ClawChatMessage
component (message toolbar, cost badges).
* feat: add message toolbar with copy button and per-turn cost display
Add MessageToolbar to historical assistant messages in ClawChatMessage:
- Copy button copies message text to clipboard via MessageAction
- Per-turn token count (22.7K → 238) and cost ($0.003) shown as muted
tabular-nums text on the right side of the toolbar
- Toolbar appears on hover (opacity transition via group-hover)
- Only shown when the message has text content
- Cost/token display only shown when data is available from JSONL
* fix: toolbar only on assistant messages, always visible, cost only
- Only render toolbar on assistant messages (not user messages)
- Remove hover-only opacity — toolbar is always visible
- Remove token counts (22.7K → 238 is meaningless to users)
- Show only cost as a budget signal ($0.003)
* feat: group all tool activity into single Task collapsible per turn
Replace flat tool rows with a single ai-elements Task collapsible per
assistant turn that lists every tool/MCP call in sequence.
Live streaming (ConversationMessage):
- Aggregate all tool-batch parts into one Task
- Title: "Working… (N actions)" while running, "Agent activity (N actions)" when done
- Default open while turn is in progress
- Wrench icon in trigger
Historical (ClawChatMessage):
- Group all tool-call parts into one Task
- Title includes failed count if any tools errored
- Default collapsed — expandable on click
- Tool name + status icon + error text per row
Both views show one clean collapsible per turn instead of N individual
tool cards. Collapsed reads "5 actions"; expanded shows the timeline.
* feat: include tool calls in chat history responses
Server: jsonlEventsToHistoryItems() now walks ALL events (not just
messages) and pairs agent.tool_use with agent.tool_result by toolCallId.
The resulting tool call list is attached to the next assistant text
message as toolCalls[]. Each entry includes status, input arguments,
output text, error string, and duration computed from event timestamps.
Client:
- BrowserOSChatHistoryItem gets optional toolCalls field
- Tool-call message part type gets durationMs field
- mapHistoryItemToClawMessage() emits tool-call parts BEFORE the text
part (the order the agent produced them)
- ClawChatMessage Task view now shows tool duration in seconds
Result: historical messages now display the full tool activity
timeline grouped into the single Task collapsible per turn (designed
in step 3), instead of showing only the final text response.
* feat: render activity rows as human verbs sourced from tool registry
Tool calls in the chat activity view now read as sentences:
"Opened tab · news.ycombinator.com" instead of "browseros__new_page".
Server (tool-label-registry.ts):
- Curated verb override map for ~70 BrowserOS first-party tools
- Per-tool subject extractors that pull the meaningful argument from
input (URL → host, query → quoted, element → ID, etc.)
- Generic fallback humanizes snake_case for any unmapped tool
- Strips MCP namespace prefixes (browseros__, mcp_)
Server (openclaw-service.ts):
- jsonlEventsToHistoryItems calls buildToolLabel for each tool_use,
attaches label and subject to the BrowserOSChatHistoryToolCall
Client:
- Mirrored label module at lib/tool-labels.ts
- useAgentConversation tool-start handler computes label/subject
from the SSE tool args
- ClawChatMessage and ConversationMessage render label · subject
with foreground/muted styling, no font-mono
- ToolEntry, BrowserOSChatHistoryToolCall, and tool-call message
part types all carry label and optional subject
* fix: drop meaningless tab N subject from page-read tool rows
Page IDs are internal numbers, not URLs. 'Took screenshot · tab 4'
tells the user nothing. Removed subject extractors for take_snapshot,
take_enhanced_snapshot, get_page_content, get_page_links, get_dom,
and take_screenshot. The verb alone is the right signal.
* fix: gate initial loading on historyQuery.isFetched not isLoading
The session and history queries are sequential: the history query is
disabled until session resolves. After session resolves, there's a render
frame where historyQuery.isLoading is still false (the query hasn't
been kicked off yet). isInitialLoading flipped to false during that
window, exposing an empty chat shell with just Task collapsibles and
copy buttons before the messages filled in.
Switching the guard to isFetched closes that window — the loading state
stays true until the first history fetch actually completes.
* fix: render historical messages immediately instead of through Streamdown's idle-callback debounce
Streamdown defaults to mode="streaming" which uses requestIdleCallback (300ms
debounce, 500ms idle timeout) and lazy/Suspense to optimize for token-by-token
live streams. For finalized historical messages this caused tool collapsibles
and copy buttons to paint while text bodies stayed blank for ~300-500ms after
load. Pass mode="static" + parseIncompleteMarkdown=false on the historical
MessageResponse so completed text paints in the same frame as the surrounding
chrome. Live streaming turns still use the default streaming mode.
Also collapse the redundant /agents/:id/session round-trip into the existing
/history endpoint (server already resolves the most recent user-chat session
when sessionKey is omitted) and tighten the initial-loading gate to stay true
across the render frame where the query is enabled but hasn't started fetching.
* feat: surface thinking duration on historical reasoning collapsibles
Server accumulates agent.thinking events per turn from JSONL and attaches a
single reasoning block (joined text + durationMs from first thinking event
to the closing agent.message) on each assistant history item. Reasoning
buffer resets on user.message alongside the tool-call buffer.
Client mirrors the type, emits the reasoning part before tool calls in
mapHistoryItemToClawMessage (chronological: think → act → answer), and
passes duration in seconds to <Reasoning> so the trigger reads "Thought
for N seconds" instead of just "Thinking" on collapsed historical turns.
* fix: read thinking blocks from the correct JSONL field name
OpenClaw stores reasoning blocks as {type:'thinking', thinking:'...'} but
the JSONL parser was reading block.text, so every thinking event was
silently dropped before it ever reached jsonlEventsToHistoryItems. As a
result the reasoning field on history items was always empty even though
the new accumulator was wired up correctly.
Also guard the client mapping: when durationMs is 0 (think + answer
emitted in the same JSONL line, no real elapsed wall-clock) pass
undefined to <Reasoning> so it renders the static "Thinking" trigger
instead of the streaming shimmer / "Thought for 0 seconds".
* fix: reset reasoning buffer on discarded turns and drop dead session hook
Two cleanups from PR review:
1. jsonlEventsToHistoryItems: when an agent.message is discarded (the
"[Chat messages since your last reply" wrapper without a current-message
marker) the tool buffers were already reset but the reasoning buffer
was not. Accumulated thinking from the discarded turn would bleed onto
the next assistant message. Reset pendingReasoningTexts and
pendingReasoningFirstAt alongside the tool buffers.
2. useClawAgentSession, the AgentSessionResponse type, and the unused
session entry in CLAW_CHAT_QUERY_KEYS became dead code after the
session round-trip was folded into the history endpoint. Removed.
|
||
|
|
0035893f33 |
feat: dashboard API, JSONL reader, and OpenClaw observer for enriched home page (#810)
* feat: draft agent chat ui exploration * feat: refine agent chat ui draft * feat: remove outer frame from agent chat workspace * fix: offset agent chat for app sidebar * fix: simplify agent conversation shell * fix: remove redundant chat header actions * fix: unify agent conversation headers * fix: tighten agent chat spacing * fix: bound agent chat composer height * fix: remove agent chat page inset * fix: align agent header height with sidepanel * fix: center agent composer resting state * fix: anchor multiline composer controls * fix: remove focus grid from agent home * fix: remove redundant agent home header * fix: constrain home agent composer * fix: match home composer default posture * feat: add openclaw chat history APIs * feat: add claw chat history hydration * fix: stabilize claw chat viewport layout * fix: use conversation scroll base for claw chat * refactor: split claw chat controller responsibilities * fix: keep active agent turns in memory * fix: normalize openclaw chat sessions * refactor: use HTTP client for agent history instead of CLI client Replace the CLI-based getChatHistory() call in getAgentHistoryPage() with the HTTP client's getSessionHistory() from PR #795. This uses the direct HTTP transport to OpenClaw's /sessions/<key>/history endpoint instead of shelling out through the CLI. - Add filterHttpSessionHistoryMessages() for flat-string content format - Add normalizeHttpHistoryMessages() for OpenClawSessionHistoryMessage shape - Update getAgentHistoryPage() to call getSessionHistory() via httpClient - Remove unused getChatHistory(), filterOpenClawSystemMessages(), normalizeChatHistoryMessages(), and getTextContent() - Update test mocks from cliClient.getChatHistory to httpClient.getSessionHistory - Update MutableOpenClawService type: chatClient -> httpClient * fix: fetch all session messages by iterating OpenClaw pagination OpenClaw's HTTP history endpoint returns a limited page by default. When called without a limit, only the first ~27 messages were returned, causing all newer conversation messages to be silently dropped. Add fetchAllSessionMessages() that iterates through OpenClaw's cursor- based pagination (200 messages per page) until hasMore is false, then feeds the complete message list into the existing BrowserOS normalization and in-memory pagination layer. * refactor: migrate chat history from HTTP gateway to direct JSONL file reads Replace the HTTP-based chat history pipeline (BrowserOS server → OpenClaw gateway /sessions/:key/history pagination loop) with direct JSONL file reads from the host filesystem via Lima's virtiofs mount. - Add OpenClawJsonlReader that reads session JSONL files directly from ~/.browseros/vm/openclaw/.openclaw/agents/<id>/sessions/ - Replace fetchAllSessionMessages() HTTP pagination with single file read - Replace CLI-based listSessions() with sessions.json file reads - Make listSessions, resolveAgentSession, getAgentHistoryPage synchronous - Remove unused toBrowserOSSession, filterHttpSessionHistoryMessages, normalizeHttpHistoryMessages helpers - Update route handlers to drop unnecessary async/await - Update tests to use temp JSONL files instead of mocked HTTP/CLI clients * fix: restore async route handlers for test compatibility with mocked service * fix: address review feedback — path traversal guard, lazy reader, exists flag - Add safePath() to OpenClawJsonlReader that validates resolved paths stay within stateRoot, preventing path traversal via crafted agentId values - Use lazy initialization for jsonlReader (nulled on rebuildRuntimeClients) instead of creating a new instance per property access - Return exists: false from resolveSpecificAgentSession when no session matches instead of fabricating a ghost session with sessionId: '' * feat: add dashboard API and enrich home page agent cards Server: - Add summarizeToolActivity() that converts tool events into natural language descriptions ("Browsed 3 pages, took 2 screenshots") - Add getDashboard() to OpenClawService that aggregates per-agent stats from JSONL: latest message, activity summary, cost, session count - Add GET /claw/dashboard endpoint Client: - Add useAgentDashboard() React Query hook (10s refetch, 5s stale) - Rewrite useAgentCardData from async IndexedDB hook to pure buildAgentCardData() function merging agent entries with dashboard data - Add activity summary and cost to AgentCardExpanded footer - Add activitySummary and costUsd fields to AgentCardData type - Remove IndexedDB dependency from the home page * feat: add OpenClawObserver for real-time per-agent status via gateway WS - Add OpenClawObserver that connects to the OpenClaw gateway WebSocket control plane and subscribes to chat broadcast events - Track per-agent status in real time: working (streaming), idle (turn complete), error (run failed), with current tool name - Auto-connect when gateway control plane becomes available, auto- reconnect on disconnect with 5s backoff - Disconnect observer on stop/shutdown - Wire live status + currentTool into getDashboard() response - Update client: AgentOverview includes status + currentTool, card shows spinning loader + tool name when agent is working - Status resolution: per-agent WS status takes precedence over gateway- level status for working/error states * feat: add SSE dashboard stream for real-time agent status on home page Server: - Add GET /claw/dashboard/stream SSE endpoint that sends an initial snapshot then pushes per-agent status events as they arrive from the OpenClaw observer - Add onAgentStatusChange() to OpenClawService exposing the observer's listener for the route layer - Heartbeat every 15s to keep connections alive Client: - useAgentDashboard() now subscribes to EventSource at /claw/dashboard/stream - SSE snapshot event hydrates the React Query cache immediately - SSE status events patch individual agent status + currentTool in the cache without refetching — agent cards update instantly - Polling fallback raised to 30s since SSE handles real-time * fix: observer WS handshake — wait for challenge before sending connect The OpenClaw gateway sends a connect.challenge event before accepting the connect request. The observer was sending the connect request on ws.open which raced with the challenge. Now waits for the challenge event before sending the handshake. Also add dangerouslyDisableDeviceAuth to the gateway setup config batch so the observer can connect without device identity on new installs. * fix: JSONL reader falls back to most recent file when sessions.json is stale OpenClaw's sessions.json can record a Pi session ID that doesn't match the actual JSONL filename on disk. This happens after context compaction or session restart — the JSONL file gets a new UUID but sessions.json keeps the old one. Previously this caused history to silently disappear (the reader tried to open a non-existent file and returned empty). Now resolveJsonlPath() checks if the mapped file exists and, when it doesn't, scans the sessions directory for the most recently modified .jsonl file as a fallback. * feat: add ClawSession state machine for reliable per-agent status The OpenClawObserver only knows about status changes it witnesses via WS events. If an agent was already running when the observer connected, or after a reconnect, statuses were stuck at "unknown". ClawSession is an in-memory state machine that solves this: 1. Seeds from JSONL on first control plane call — reads the latest events for each agent and infers working/idle. A session is "working" if the last event is a user.message with no subsequent agent.message, or an agent.tool_use with no matching agent.tool_result. 2. Receives live transitions from the WS observer — the observer now delegates all state management to ClawSession instead of maintaining its own status map. 3. Applies a 5-minute staleness threshold — if the last JSONL event is older than 5 minutes, assume idle (handles agent crashes). Consumers (SSE stream, dashboard endpoint) read from ClawSession and get correct state from the first call — no "unknown" period. * fix: remove staleTime so dashboard refetches on every mount * fix: reset stale working status on WS disconnect, eliminate redundant JSONL reads - Observer resets all "working" agents to "unknown" when the WS closes, preventing agents from appearing stuck as Working indefinitely after a gateway restart. ClawSession re-seeds correct state on reconnect. - getDashboard() now derives latestAgentMessage and cost from the already-loaded events array for the latest session instead of calling latestAgentMessage() and getSessionStats() which each re-read the same JSONL file. Reduces file reads from 3x to 1x per agent. |
||
|
|
2f86020b30 |
feat: gate agent alpha UI behind capabilities (#716)
* feat: gate agent alpha UI behind capabilities * fix: provide chat session for non-alpha home * fix: gate agents page behind alpha * fix: enable alpha capabilities in development |
||
|
|
f1c108b2ed |
feat: mcp acl guard (#710)
* feat: guard MCP actions with persisted ACL rules * chore: add safe OpenClaw lifecycle logging |
||
|
|
ce7c209ba6 |
feat: add OpenClaw agent command center and terminal (#692)
* feat: agent command center new tab with OpenClaw conversation history * feat: add web terminal for Podman container shell access * feat: align agent command center with new tab * fix: simplify agent command center styling * style: polish agent terminal layout and theming * style: simplify agent terminal styling * fix: address PR review comments for OpenClaw routes * fix: handle OpenClaw client start and error states * fix: resolve remaining OpenClaw review comments |
||
|
|
3c629c5929 |
feat: tool approvals, governance dashboard, and execution history
- Add tool approval system with per-category approval configuration - Build unified Governance dashboard (renamed from Admin) with pending approvals view and execution audit log - Move execution history tracking into the app shell - Extract buildChatRequestBody helper and add newtab system prompt - Add approval config change detection for mid-conversation rebuilds |
||
|
|
77dcd37000 |
feat: ACLs and support enforcing (#583)
* feat: add ACL rules for per-site element-level agent restrictions Implement Access Control List (ACL) rules that let users block the agent from interacting with specific elements on specific websites. Rules are defined in a new Settings > ACL Rules page and enforced server-side in executeTool() before any input tool handler runs. - Shared ACL types and site pattern matching (packages/shared) - Extension storage, settings UI with rule cards and add dialog - Server-side guard in executeTool() checking tool+page+element - Browser class extensions for element property resolution via CDP - Visual overlay injection (red "BLOCKED" mask) via Runtime.evaluate - Rules transported in chat request body alongside declinedApps * fix: address review comments for ACL rules - Add selector-to-property matching in matchesElement (tag, id, class) - Remove scroll from guarded tools set (read-like action) * fix: ACL site pattern matching fails on multi-segment URL paths The glob-to-regex conversion used [^/]* for wildcard (*) which only matches a single path segment. "*.amazon.com/*" failed to match "www.amazon.com/cart/smart-wagon" because the trailing * couldn't cross the slash between "cart" and "smart-wagon". Fix: Split URL matching into hostname vs path parts. Path wildcards now use .* to match across slashes. Also add simple domain matching so users can just type "amazon.com" instead of "*.amazon.com/*". * fix: wire up ACL overlay injection after take_snapshot applyAclOverlays was defined but never called. Now triggers after take_snapshot completes on pages matching ACL rules, so the agent sees red "BLOCKED" overlays on restricted elements. * refactor: rework 0326-acl_rules based on feedback |
||
|
|
df7873562d |
Revert Kimi partnership UI, restore daily limit survey (#663)
* docs: add uBlock Origin install info to getting started and ad-blocking pages Chrome dropped support for the full uBlock Origin extension — highlight that BrowserOS brings it back and make it easy to install from both the getting started guide and the dedicated ad-blocking page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: revert Kimi partnership UI, restore daily limit survey Remove Kimi/Moonshot AI partnership branding from the rate limit banner, provider card, provider templates, and LLM hub. Restore the original survey CTA on daily limit errors. Moonshot AI remains as a regular provider template without the "Recommended" badge. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address Greptile review comments - Guard survey CTA with !isCreditsExhausted to avoid showing it for credits-exhausted users who already see "View Usage & Billing" - Remove dead kimi-launch feature flag files (kimi-launch.ts, useKimiLaunch.ts) - Remove unused KIMI_RATE_LIMIT analytics events - Remove VITE_PUBLIC_KIMI_LAUNCH from env schema and .env.example Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
94540d9e87 | chore(agent): remove workflows feature (#656) | ||
|
|
81350c0d7f |
feat: replace model picker with shadcn Combobox + fuse.js fuzzy search (#617)
The model picker in NewProviderDialog rendered inline, causing dialog resizing and lacked keyboard navigation. Replace it with a Popover + Command (shadcn Combobox) pattern and add fuse.js for fuzzy search. - Replace custom ModelPickerList with Popover + Command dropdown - Add fuse.js for fuzzy model search (replaces string.includes) - Add MODEL_SELECTED_EVENT and AI_PROVIDER_UPDATED_EVENT analytics - Enrich PROVIDER_SELECTED_EVENT with model_id in chat sessions |
||
|
|
cee318a40b |
fix: improve chat history freshness and reduce query payload (#598)
* fix: add refresh indicator to chat history when fetching latest conversations Show a non-blocking "Fetching latest conversations" indicator at the top of the history list while the cached data is being refreshed. Users can still interact with the cached conversation list during the refresh. * perf: reduce chat history query payload — fetch last 2 messages instead of 5 The conversation list only displays the last user message as a preview. Fetching 5 messages per conversation was wasteful — each message contains the full UIMessage object (tool calls, reasoning, etc.) multiplied by 50 conversations per page. Reduced to last 2 which is sufficient to find the last user message in a user→assistant exchange. * perf: use first+DESC instead of last+ASC to push LIMIT down to SQL PostGraphile's `last: N` doesn't map to SQL LIMIT — it uses a padded LIMIT 10 and slices in application code. Changing to `first: 2` with ORDER_INDEX_DESC generates a true SQL LIMIT 2, reducing rows scanned from 500 to 100 per page (50 conversations × 2 vs 10 messages each). No UX impact — extractLastUserMessage() filters by role regardless of message order. * chore: update react query packages * feat: replace localforage with idb-keyval |
||
|
|
dde35ccbd5 |
feat: integrate models.dev for dynamic LLM provider/model data (#547)
* feat: integrate models.dev for dynamic LLM provider/model data (#TKT-657) Replace hardcoded model lists with data sourced from models.dev so new providers and models appear automatically when the community adds them. - Add build script (scripts/generate-models.ts) that fetches models.dev/api.json and outputs a compact JSON with 10 providers and 520 models - Replace hardcoded MODELS_DATA (50 models) with dynamic models.dev lookups - Add searchable model combobox (Popover + Command) replacing plain Select dropdown - Enrich provider templates with models.dev metadata (context window, image support) - Keep chatgpt-pro, qwen-code, browseros, openai-compatible as hardcoded providers * fix: address review — remove ollama-cloud mapping, fix default models, remove dead code - Remove ollama from PROVIDER_MAP (ollama-cloud has cloud models, not local) - Add ollama to CUSTOM_PROVIDER_MODELS with empty list (users type custom IDs) - Update defaultModelIds to ones that exist in models.dev data: openrouter → anthropic/claude-sonnet-4.5 lmstudio → openai/gpt-oss-20b bedrock → anthropic.claude-sonnet-4-6 - Remove dead isCustomModel export - Regenerate models-dev-data.json (9 providers, 486 models) * fix: model suggestion list focus/dismiss behavior - List only opens when input is focused or user types - Clicking a model selects it and closes the list - Clicking outside (blur) dismisses the list - onMouseDown preventDefault on list items prevents blur race condition * refactor: extract ModelPickerList component with proper open/close UX - Collapsed state: Select-like trigger showing selected model + chevron - Expanded state: search input + scrollable filtered list, inline - Click outside or Escape to close, Enter to submit custom model - Extracted as separate component (reduces dialog nesting, testable) - No more setTimeout hacks for blur handling * chore: remove plan doc from repo |
||
|
|
fb5143b563 |
feat: UI improvements for OAuth dialog, provider badges, and events docs (#543)
* feat: UI improvements for OAuth dialog, provider badges, and events docs - Replace OAuth device code toast with a proper Dialog showing the code prominently with a copy button (GitHub Copilot, Qwen Code, ChatGPT Pro) - Add "New" badge on provider template cards for ChatGPT Plus/Pro, GitHub Copilot, and Qwen Code with orange border highlight - Add events.md documenting all analytics events across the platform * fix: add verificationUri to DeviceCodeDialog for popup-blocked fallback Add verificationUri to PendingDeviceCode interface and pass it from both handleClientAuth and handleServerAuth. Render a fallback "Open verification page" link in DeviceCodeDialog so users can navigate to the auth page if the popup was blocked. |
||
|
|
890d3406dd |
feat: promote BrowserOS as MCP with UI improvements (#541)
- Add MCP promo banner on AI providers page with "New" badge and "66+ tools" highlight, linking to /settings/mcp - Add Quick Setup section on MCP settings page with copy-paste commands for Claude Code, Gemini CLI, Codex, Claude Desktop, OpenClaw - Consolidate MCP settings: move restart button inline with server URL, remove separate MCP Server Settings card - Add analytics event for promo banner clicks |
||
|
|
86ec88ed80 |
feat: sentry improvements (#532)
* feat: process request record from sentry locally * feat: added analytics for logged in users |
||
|
|
4928b7e84b |
fix: no current window and sentry context (#531)
* fix: error reporting and better breadcrumbs * fix: lint issues |
||
|
|
2b53daf641 |
fix: prevent deleted scheduled tasks from reappearing after sync (#518)
* fix: prevent deleted scheduled tasks from reappearing after sync When a scheduled task was deleted, the sync function would see the remote job missing locally and re-add it, undoing the delete. Fix by tracking pending deletions in storage so the sync function deletes them from the backend instead of re-adding them locally. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: use read-modify-write for pending deletions to prevent concurrent clobber Re-read pendingDeletionStorage before write-back and only remove resolved IDs, preserving any new entries added by concurrent removeJob calls during the sync's network I/O. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
2271277b4d |
feat: add voice input to new tab search bar (#509)
* feat: add voice recording UI with waveform overlay to new tab search bar Add a microphone button to the NewTab search bar that opens a fullscreen recording overlay powered by react-voice-visualizer. The overlay shows a real-time waveform visualization during recording, recording time, and a stop button. On completion, the audio is transcribed via the existing gateway endpoint and the transcript auto-navigates to inline chat. Changes: - Extract transcribeAudio() to shared lib/voice/transcribe-audio.ts - Add VoiceRecordingOverlay component with react-voice-visualizer - Add Mic button to NewTab search bar - Track analytics via existing NEWTAB_VOICE_* events - Handle cancel (backdrop click) vs submit (stop button) correctly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: address PR review comments for voice recording overlay - Reset processingRef on transcription error to prevent stuck state - Use stable callback refs to prevent useEffect re-runs from inline arrow function props (fixes timer reset and unnecessary re-processing) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: replace voice overlay with inline sidepanel-style voice UI Remove react-voice-visualizer dependency and VoiceRecordingOverlay. Instead use the same inline voice pattern as the sidepanel ChatInput: - Waveform bars replace the search input during recording - Mic/stop/loading button states in the search bar - Transcript populates the search input on completion - Voice error shown inline below the search bar Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
8548bcf50a |
feat: credit-based tracking for BrowserOS provider (#489)
* feat: add credit-based tracking for BrowserOS provider Send X-BrowserOS-ID header on all LLM requests through the BrowserOS gateway for per-installation credit tracking. Handle 429 CREDITS_EXHAUSTED as non-retryable. Add GET/PUT /credits endpoints to check and manage credit balance. * docs: add credits tracking UI design Design for showing credit balance in side panel chat header (color-coded badge) and a dedicated Usage & Billing settings page. Credits refresh after each completed message turn or on exhaustion error. * docs: add credits tracking UI implementation plan 8-task plan covering useCredits hook, CreditBadge component, ChatHeader integration, message completion refresh, ChatError CREDITS_EXHAUSTED handling, Usage & Billing settings page, and route/sidebar registration. * feat: add useCredits React Query hook * feat: add CreditBadge component with color thresholds * feat: show credit badge in chat header for BrowserOS provider * feat: refresh credits after chat message completion and on error * feat: handle CREDITS_EXHAUSTED error in chat * feat: add Usage & Billing settings page * feat: register usage page route and sidebar entry * fix: lint and formatting fixes for credit tracking UI * fix: separate credits exhausted from Kimi rate limit in ChatError, redesign Usage page * chore: remove PUT /credits endpoint and setCredits function * fix: extract shared credit colors, add error state to UsagePage, use dailyLimit from gateway * fix: make dailyLimit required in CreditsInfo (gateway always returns it) * feat: gate credits UI behind CREDITS_SUPPORT feature flag (server >= 0.0.78) |
||
|
|
e3601bfdc1 | feat: gate Qwen Code behind server version 0.0.77 (#508) | ||
|
|
11d15d079f |
feat: alibaba qwen oauth (#506)
* feat: add Qwen Code as OAuth LLM provider with refactored OAuth hooks Add Alibaba Qwen Code as a third OAuth provider using Device Code flow with PKCE. Free tier: 2,000 requests/day, up to 1M token context. Refactoring: - Extract useOAuthProviderFlow hook (eliminates ~180 lines of duplicated OAuth logic from AISettingsPage for ChatGPT Pro + Copilot + Qwen) - Extract resolveOAuthConfig in config.ts (shared resolver for all OAuth providers, parameterized by provider name, default model, refresh flag) - Generalize token-manager device code flow to support PKCE (code_challenge/code_verifier) and form-urlencoded content type New code: - Qwen Code provider config with PKCE + form encoding flags - Provider factories (both provider.ts and provider-factory.ts) - Extension UI (template card, models, analytics, dialog) * fix: use portal.qwen.ai as API base URL for OAuth tokens DashScope (dashscope.aliyuncs.com) expects Alibaba Cloud API keys, not OAuth tokens from chat.qwen.ai. The correct endpoint for OAuth Bearer tokens is portal.qwen.ai/v1. * fix: correct Qwen Code model IDs and context windows - coder-model (1M context): virtual alias that routes to best model - qwen3-coder-plus (1M): was incorrectly 131K - qwen3-coder-flash (1M): new, speed-optimized variant - qwen3.5-plus (1M): was incorrectly 1048576 (power-of-two vs decimal) - Removed qwen3-coder-next (local/self-hosted, not available via OAuth) - Default model changed to coder-model (auto-routes server-side) * fix: move Qwen device code request to extension (bypasses WAF) Alibaba WAF blocks server-side requests to chat.qwen.ai. Move the initial device code request to the extension (browser context with cookies), then hand off the deviceCode + codeVerifier to the server for background polling via new POST /oauth/:provider/poll endpoint. * fix: persist OAuth flow-started flag in sessionStorage The flowStartedRef was lost when the component remounted (e.g. user navigated to onboarding then back to settings). Use sessionStorage to persist the flag so auto-create works after navigation. * revert: remove sessionStorage for OAuth flow flag Revert to simple useRef pattern matching the original ChatGPT Pro implementation. The auto-create works when the user stays on the AI settings page during auth. * revert: move Qwen back to server-side device code flow WAF block was temporary (rate-limiting), not permanent. Server-side fetch to chat.qwen.ai now works. Reverted client-side device code approach — Qwen now uses the same clean server-side flow as Copilot. Removed: clientSideDeviceCode config, startClientSideDeviceCode(), POST /oauth/:provider/poll endpoint, startDeviceCodePolling(). * feat: add WAF detection, rate-limit protection, and token storage endpoint - Detect WAF captcha responses (HTML instead of JSON) in device code request and token polling, with user-friendly error messages - Add 30s cooldown on "USE" button to prevent rapid clicks triggering WAF - WAF-blocked poll requests silently retry instead of aborting - Add POST /oauth/:provider/token endpoint for storing externally-provided tokens (useful for future fallback flows) - Add storeTokens() method to OAuthTokenManager - Pass server error messages through to extension toast notifications * refactor: remove 30s cooldown, simplify OAuth hook The hook is now identical for all providers — server handles retries via activeDeviceFlows.delete(). Removed flowStartedAtRef cooldown that was blocking legitimate retries. * feat: client-side OAuth for Copilot and Qwen Code Move device code OAuth flow to the extension for GitHub Copilot and Qwen Code. The extension makes requests using Chrome's network stack, which bypasses Alibaba WAF TLS fingerprint detection that blocks server-side Bun/Node.js fetch. New files: - client-oauth.ts: Client-side device code + PKCE + token polling Changes: - useOAuthProviderFlow: handleClientAuth() for providers with clientAuth config, handleServerAuth() for others (ChatGPT Pro) - AISettingsPage: clientAuth config for Copilot and Qwen Code - WAF detection: opens provider site for captcha solving on block Server-side device code flow preserved as fallback (token-manager.ts, providers.ts). Token storage via POST /oauth/:provider/token endpoint. * fix: export OAuthProviderFlowConfig type, fix typecheck errors - Export OAuthProviderFlowConfig interface so AISettingsPage can use it instead of duplicating the type inline - Fix string | null → string | undefined for agentServerUrl parameter |
||
|
|
9257832acf |
feat: gate ChatGPT Pro and GitHub Copilot behind server version 0.0.77 (#503)
Add CHATGPT_PRO_SUPPORT and GITHUB_COPILOT_SUPPORT feature flags gated on minServerVersion 0.0.77. Hide template cards and provider type dropdown options when the server doesn't support the OAuth endpoints. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> |
||
|
|
720baaed3e |
feat: add GitHub Copilot as OAuth LLM provider (#500)
* feat: add GitHub Copilot as OAuth-based LLM provider Add GitHub Copilot as a second OAuth provider using the Device Code flow (RFC 8628). Users authenticate via github.com/login/device, and the server polls for token completion. Supports 25+ models through a single Copilot subscription. Key changes: - Device Code OAuth flow in token manager (poll with safety margin) - Custom fetch wrapper injecting Copilot headers + vision detection - Provider factory using createOpenAICompatible for Chat Completions API - Extension UI with template card, auto-create on auth, and disconnect * fix: address PR review comments for GitHub Copilot OAuth - Validate device code response for error fields (GitHub can return 200 with error payload) - Store empty refreshToken instead of access token for GitHub tokens - Add closeButton to Toaster for dismissing device code toast * fix: add github-copilot to agent provider factory The chat route uses a separate provider-factory.ts (agent layer) from the test-provider route (llm/provider.ts). Added createGitHubCopilotFactory to the agent factory so chat works with GitHub Copilot. * fix: add github-copilot to provider icons, models, and dialog - Add Github icon from lucide-react to providerIcons map - Add 8 Copilot models (GPT-4o, Claude, Gemini, Grok) to models.ts - Add github-copilot to NewProviderDialog zod enum, validation skip, canTest check, and OAuth credential message * fix: reorder copilot models with free-tier models first Put models available on Copilot Free at the top (gpt-4o, gpt-4.1, gpt-5-mini, claude-haiku-4.5, grok-code-fast-1), followed by premium models that require paid Copilot subscription. * fix: set correct 64K context window for Copilot models Copilot API enforces a 64K input token limit regardless of the underlying model's native context window. Updated all model entries and the default template to 64000 so compaction triggers correctly. * fix: use actual per-model prompt limits from Copilot /models API Queried api.githubcopilot.com/models for real max_prompt_tokens values. GPT-4o/4.1 have 64K, Claude/gpt-5-mini have 128K, GPT-5.x have 272K. Also updated model list to match what's actually available on the API (e.g. claude-sonnet-4.6 instead of 4.5, added gpt-5.4/5.2-codex). * feat: resize images for Copilot using VS Code's algorithm Large screenshots cause 413 errors on Copilot's API. Resize images following VS Code's approach: max 2048px longest side, 768px shortest side, re-encode as JPEG at 75% quality. Uses sharp for server-side image processing. * fix: address all Greptile P1 review comments - Add .catch() on fire-and-forget pollDeviceCode to prevent unhandled rejection crashes (Node 15+) - Add deduplication guard (activeDeviceFlows Set) to prevent concurrent device code flows for the same provider - Add runtime validation of server response in frontend before calling window.open() and showing toast - Remove dead GITHUB_DEVICE_VERIFICATION constant from urls.ts * fix: upgrade biome to 2.4.8, fix all lint errors, and address review bugs - Upgrade biome from 2.4.5 to 2.4.8 (matches CI) and migrate configs - Fix image resize: only re-encode when dimensions actually change - Fix device code polling: retry on transient network errors instead of aborting - Allow restarting device code flow (clear old flow instead of throwing 500) - Fix pre-existing noNonNullAssertion and noExplicitAny lint errors globally * fix: address Greptile P2 review — image resize and config guard - Fix early-return guard: check max/min sides against their respective limits (MAX_LONG_SIDE/MAX_SHORT_SIDE) instead of both against SHORT - Preserve PNG alpha: detect hasAlpha and keep PNG format instead of unconditionally converting to lossy JPEG - Keep browserosId guard in resolveGitHubCopilotConfig consistent with ChatGPT Pro pattern (safety check that caller context is valid) * feat: update Copilot models to full list from pricing page, default to gpt-5-mini Added all 23 models from GitHub Copilot pricing page. Ordered with free-tier models first (gpt-5-mini, claude-haiku-4.5), then premium. Changed default from gpt-4o to gpt-5-mini since it's unlimited on Pro plan and has 128K context (vs gpt-4o's 64K limit). |
||
|
|
7bdeeb85d5 |
fix: revert: convert settings to popup dialog (#477) (#498)
* Revert "feat: convert settings to popup dialog (#477)"
This reverts commit
|
||
|
|
5bb6143373 |
feat: display selected text from page in sidepanel (#496)
* feat: select text and pass to sidepanel * fix: lint issues * fix: persist selection across tabs * fix: review comments * fix: change when the selection is cleared * feat: sanitize url |
||
|
|
d965698905 |
fix: biome & tsc setup across repo (#493)
* fix: biome lint issues * fix: code quality workflow * fix: all lint issues * chore: test lefthook pre-commit hook * chore: test lefthook with agent file * chore: revert test comment from lefthook verification * feat: setup tsgo for typechecking agent * fix: typecheck cli command * fix: early return to prevent errors |
||
|
|
1b88ade021 |
feat: updated homepage chat (#481)
* feat: updated chat ui from homepage * fix: vertical scroll * fix: horizontal scroll issue * fix: lint issues * fix: header width * fix: message input from home to chat * feat: created sidebar header support in new tab chat * fix: remove history from new tab chat * fix: remove the shared element transition * fix: lint issues * fix: review comments * fix: defer the sendMessage callback * fix: all code concerns * fix: preserve state of chat on homepage * fix: review comments |
||
|
|
42aa0ff1ef |
feat: convert settings to popup dialog (#477)
* feat: convert settings page to popup dialog, move workflows to main nav Replace the dedicated settings page layout (SettingsSidebarLayout) with a modal dialog (SettingsDialog) that opens on top of the current page. Settings are now accessible via a dialog triggered from the main sidebar, eliminating the confusing dual-sidebar navigation pattern. - Create SettingsDialog with tabbed left panel and content area - Move Workflows into main sidebar navigation (feature-gated) - Remove /settings/* routes (except /settings/survey) - Delete SettingsSidebarLayout and SettingsSidebar components - Update backward compatibility redirects Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: setup new urls for the dialog box * fix: dialog close button * fix: settings analytics * fix: address review comments --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Dani Akash <DaniAkash@users.noreply.github.com> |
||
|
|
4000f094f6 |
Feat/chatgpt pro polish (#484)
* fix: ChatGPT Pro UI polish — fix undefined display and add icon - Fix "gpt-5.3-codex · undefined" — hide baseUrl when not set - Add OpenAI icon for chatgpt-pro provider in icon map * chore: rename ChatGPT Pro to ChatGPT Plus/Pro (supports both plans) * chore: remove accidentally committed files |
||
|
|
151be81cee |
fix: ChatGPT Pro UI polish — fix undefined display and add icon (#483)
- Fix "gpt-5.3-codex · undefined" — hide baseUrl when not set - Add OpenAI icon for chatgpt-pro provider in icon map |
||
|
|
46a8326140 |
feat: add ChatGPT Pro OAuth as LLM provider (#476)
* feat: add ChatGPT Pro OAuth as LLM provider
Adds OAuth 2.0 (Authorization Code + PKCE) flow so users can authenticate
with their ChatGPT Pro subscription to power BrowserOS's agent, matching
the pattern used by Codex CLI, OpenCode, and Pi.
Server:
- OAuth token lifecycle (PKCE, exchange, refresh, SQLite storage)
- Dedicated callback server on port 1455 (Codex client ID registration)
- Codex fetch wrapper routing API calls to chatgpt.com/backend-api
- Config resolution + provider factories for all code paths (chat, test, refine)
Extension:
- ChatGPT Pro template card with OAuth flow trigger
- Status polling hook + auto-create provider on auth success
- Model list with Codex-supported models (gpt-5.x-codex family)
* fix: address Greptile PR review comments
- Wire OAuth callback server stop handle into onShutdown (P1: port 1455 leak)
- Guard against missing refresh token + clear stale tokens on failed refresh (P1)
- Add logger.warn to silent catch in codex-fetch body mutation
- Document JWT trust assumption in parseAccessTokenClaims
- Source model ID from provider template instead of hard-coding
* simplify: remove unnecessary OAuth shutdown wiring and useCallback
- Revert OAuthHandle interface — callback server port releases on process exit
- Remove stopCallbackServer from shutdown flow (dead code)
- Remove all useCallback from useOAuthStatus per CLAUDE.md guidance
* style: add readonly modifiers and braces per TS style guide
* docs: add E2E test screenshots for ChatGPT Pro OAuth
* fix: strip item IDs from Codex requests to fix multi-turn conversations
* fix: preserve function_call_output IDs in Codex requests
* fix: resolve Codex store=false + tool-use incompatibility
- Pass providerOptions { openai: { store: false } } to ToolLoopAgent
so the AI SDK inlines content instead of using item_reference
- Strip item IDs and previous_response_id in codex-fetch (safety net)
- Use .responses() model (Codex only speaks Responses API format)
* fix: remove non-Codex model gpt-5.2 from chatgpt-pro model list
* fix: strip unsupported Codex params and update model list
- Strip temperature, max_tokens, top_p from Codex requests (unsupported)
- Add all available Codex models including gpt-5.4, gpt-5.2, gpt-5.1
* chore: remove screenshots containing email
* feat: enable reasoning events for ChatGPT Pro Codex models
* chore: set reasoning effort to high for ChatGPT Pro
* feat: add configurable reasoning effort and summary for ChatGPT Pro
- Add reasoningEffort (none/low/medium/high) and reasoningSummary
(auto/concise/detailed) dropdowns in the Edit Provider dialog
- Pass through extension → chat request → agent config → providerOptions
- Defaults: effort=high, summary=auto
* fix: strip max_output_tokens from Codex requests (fixes compaction)
* fix: address Greptile P1 issues
- Fix default model fallback: gpt-4o → gpt-5.3-codex (Codex endpoint)
- Clear stale tokens on refresh failure (prevents infinite retry loop)
- Only auto-create provider after explicit OAuth flow, not on page load
- Add catch block to auto-create effect with error toast
|
||
|
|
2597cdbc70 |
feat: add Rewrite with AI for scheduled task prompts (#465)
* feat: add "Rewrite with AI" prompt refinement for scheduled tasks Add a lightweight /refine-prompt endpoint that uses generateText to rewrite rough scheduled task prompts into clear, actionable instructions. The UI adds a sparkle-icon button next to the Prompt label in the NewScheduledTaskDialog with loading state, undo support, and disabled state when the textarea is empty. * fix: clear stale undo ref on dialog re-open and pass providerId to refinePrompt - Reset originalPromptRef when dialog opens and on form submit to prevent stale "Undo rewrite" button on re-open - Accept optional providerId in refinePrompt() so the form's selected provider is used for refinement instead of always the system default * fix: hide undo rewrite link while refinement is in flight * fix: reset isRefining state on dialog re-open * fix: ignore stale refine-prompt responses after dialog re-open Use a request generation counter so that if the dialog is closed and re-opened while a rewrite is in flight, the stale response is silently discarded instead of overwriting the fresh form state. * fix: invalidate stale refine requests on dialog reopen and rename to kebab-case - Increment refineRequestIdRef on dialog open so in-flight requests from a previous session are discarded when they complete - Rename refinePrompt.ts to refine-prompt.ts per CLAUDE.md file naming |
||
|
|
2a6848bc1d |
feat: improved system prompt (#466)
* feat: added ai-sdk dev tools * feat: new system prompt section * feat: tests to maintain prompt integrity * feat: update mcp sync to use react query * fix: refetch logic for sync * chore: remove limits on fetching integrations * fix: refetch integrations on delete * fix: review comment * chore: update tests * fix: improved memory classification * fix: lint issues * fix: core memory prompts * fix: handle scenario where soul file is empty |
||
|
|
e67c17a0f8 |
feat: add voice input to agent chat sidebar (#467)
* feat: add voice input to agent chat sidebar Allow users to record voice and transcribe to text in the chat input. Mic button shows when input is empty, waveform visualizer during recording, transcription via OpenAI (llm.browseros.com/api/transcribe). - Extract shared useVoiceInput hook to lib/voice/ - Time-domain waveform bars that bounce per-frequency-band - Bar height capped to fit input container - Analytics events for recording lifecycle * fix: address review — add fetch timeout, await stopRecording, deduplicate VoiceInputState - Add AbortSignal.timeout(30s) to transcription fetch - Await stopRecording() and track analytics after completion - Export VoiceInputState from useVoiceInput, import in consumers * fix: await startRecording before tracking, narrow SurveyChat effect deps - Await startRecording() so analytics only fires after mic permission granted - Narrow SurveyChat useEffect dependency from [voice] to [voice.transcript, voice.isTranscribing] * fix: analytics only tracks on success, clean up stream on failure, type API response - startRecording returns boolean; track(RECORDING_STARTED) only fires on success - Catch block cleans up MediaStream tracks and AudioContext on partial failure - Type transcription API response with TranscribeResponse interface * fix: keep mic button always visible alongside send button Mic and send are now separate buttons, both always visible. Mic is disabled while AI is streaming. Send is disabled during recording/transcribing. Buttons are no longer absolutely positioned inside the textarea — they sit beside it in the flex row. * fix: keep mic button always visible inside input alongside send Both mic and send buttons are always visible inside the input field, positioned on the right side (ChatGPT-style). Mic is disabled while AI is streaming. Send is disabled during recording/transcribing. * fix: remove unreachable CSS branch in recording waveform div |
||
|
|
41c9b1547c |
feat: add per-task LLM provider selection for scheduled tasks (#450)
* feat: add per-task LLM provider selection for scheduled tasks Allow users to choose which AI provider a scheduled task runs with, using the same ChatProviderSelector component from the new-tab page. Falls back to the global default provider when none is selected or if the selected provider has been deleted. * fix: lint issues * chore: updated to latest schema.graphql file --------- Co-authored-by: Dani Akash <DaniAkash@users.noreply.github.com> |
||
|
|
4bee76253d |
fix: prevent undefined provider in chat requests on fresh install (#442)
* fix: fallback to default BrowserOS provider when provider is null When the extension first loads, provider config is loaded async from storage. If a chat request fires before loading completes (race condition), provider is null and the server receives provider: undefined, causing a Zod validation error. This adds a fallback to createDefaultBrowserOSProvider() in both chat paths (sidepanel and scheduled tasks) so provider.type is always defined. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: fallback to first provider when default provider ID is stale When defaultProviderId in storage doesn't match any loaded provider (e.g. after Kimi/Moonshot rollout), selectedProvider was null causing provider: undefined in chat requests. Now falls back to providers[0]. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: repair stale defaultProviderId in storage on load When the stored default provider ID doesn't match any loaded provider, write back the corrected ID (providers[0].id) to storage so it doesn't silently persist across sessions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
95c855a091 |
feat: replace rate limit CTAs with Kimi/Moonshot partnership links (#437)
* feat: replace rate limit CTAs with Kimi/Moonshot partnership links Comment out old "Learn more" and "take a quick survey" links on the daily limit error banner. Replace with Kimi API key docs link and direct Moonshot AI platform link for conversion tracking. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: remove partnership tagline from rate limit banner Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
290ee91a8b |
Add 'packages/browseros-agent/' from commit '90bd4be3008285bf3825aad3702aff98f872671a'
git-subtree-dir: packages/browseros-agent git-subtree-mainline: |