Skip to content

feat: Agent-backend migration and v0.0.6 features - #75

Merged
fazxes merged 56 commits into
mainfrom
feat/migration
Mar 17, 2026
Merged

fazxes merged 56 commits into
mainfrom
feat/migration

Conversation

@fazxes

@fazxes fazxes commented Mar 10, 2026

Copy link
Copy Markdown
Member

Summary

  • Agent-backend: Introduces the Agent-backend codebase (forked from opencode) as the replacement for agent-bridge, using HTTP + SSE architecture with multi-provider support
  • Migration plan: docs/plans/due/OPENCODE-MIGRATION-PLAN.md documents the full migration strategy
  • v0.0.6 features: Runtime app icon picker, word-by-word streaming, thinking phase persistence, browser runtime injection, skills marketplace, iOS runtime backend, liquid glass redesign, color system migration, and more

What's included

  • Agent-backend/ — Full opencode fork with TypeScript backend (HTTP + SSE), multi-provider AI support, and Bun-based tooling
  • Thinking phase persistence with interleaved rendering and duration tracking
  • Data-layer word-by-word streaming reveal
  • Browser MCP improvements and screenshot capture
  • Skills marketplace with trending/top browse
  • iOS runtime backend completion
  • Color system migration from Radix scales to Orbit semantic tokens
  • Tool widget and chat UI redesign

Test plan

  • All 294+ Rust tests pass (verified by pre-push hook)
  • All 13 Vitest tests pass
  • TypeScript type checks pass
  • ESLint passes with zero warnings
  • Agent-backend builds with cd Agent-backend && bun install && bun run build
  • Existing agent-bridge functionality unaffected

@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

this.revealController.beginDrain(sid, message.message_id, () => {
useChatStore.getState().updateMessage(sid, message.message_id, (m) => ({
...m,
isStreaming: false,
}));

P1 Badge Defer drain callback until after completion persistence

Calling beginDrain here can synchronously run the callback (see StreamingRevealController.beginDrain when no reveal loop exists), which flips isStreaming to false before the completion logic reaches if (lastMsg?.role === 'assistant' && lastMsg.isStreaming). This happens on turns with no text chunks (e.g., tool/thinking-only), causing the persistence block to be skipped and dropping assistant/tool metadata from conversation history after reload.


// Streaming: keep looping — more content may arrive
this.scheduleNext(key, state);
return;

P2 Badge Avoid RAF busy-loop when no new text is available

When displayedContent has already caught up and the state is still streaming, this path immediately schedules another requestAnimationFrame, creating a continuous ~60fps polling loop until drain starts. During long pauses between chunks (common in tool-heavy turns), this repeatedly scans session/message state and burns CPU without advancing output.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

fazxes added 29 commits March 17, 2026 18:45
Introduces the Agent-backend codebase (forked from opencode) as the replacement
for agent-bridge, using HTTP + SSE architecture with multi-provider support.
Rewrite README to match Orbit's brand positioning:
- "AI-native development environment" (not "AI-powered code editor")
- Document all V1 surfaces: Agent, Editor, Browser, Terminal, Vault
- Add honest "What V1 Does Not Have" section
- Remove Canvas from V1 features (moved to roadmap)
- Fix platform claims to macOS-only (not cross-platform)
- Fix GitHub URL typo (Recusive → Recursive)
- Remove "agentic" terminology (Warp's positioning)
- Add orbit-primary icon centered at top of README
- Fix CI badge: Recusive/Snowflake-V0 → Recusive/Orbit (matches remote)
- Center hero text and badges for better presentation
…opic>

Restructured docs/plans/ from a flat directory into a categorized system:
- tracked/todo/ for queued plans, tracked/done/ for shipped plans
- others/ with 14 topic subfolders (browser, canvas, vault, etc.)
- Added plan tracking policy to root CLAUDE.md and docs/CLAUDE.md
Introduce OpenCode as an alternative AI backend alongside the existing Claude agent-bridge.
Adds OpenCode SDK package, SSE event streaming, Rust process lifecycle management,
dedicated Zustand stores (session, message, permission, provider), chat UI components,
conversation repository abstraction layer, and backend-switching settings UI.
Refactors sidebar and chat area to support backend-agnostic conversation handling.
Add comprehensive debug/info logging to the OpenCode backend prompt loop
(session routes, permission evaluation, tool resolution, LLM calls) and
frontend services (SSE manager, event coordinator, session service, stores,
chat hook, UI bridge) to provide full pipeline observability. Update
opencode-providers cache and add two new tracked plans (structured logging,
unified chat UI). Refresh learnings.md with migration findings.
Delete the separate opencode/ component folder (11 files) and migrate
OpenCode chat rendering into the shared chat pipeline. Adds OcAgentSurface,
OcQuestionCard, oc-control-bar, generic-tool-widget, streaming reveal hook,
and chat adapter. Updates input controls, message rendering, tool widgets,
permission modal, provider settings, and session service to support the
unified dual-backend architecture. Includes new tests and updated plans.
…t flash

Migrate OcModelSelector and OcThinkingSelector from DropdownMenu to
Command/Popover with search and smooth scroll. Redesign OcProviderDialog
with scroll masks and refined layout. Refactor ProvidersSettings and
BackendSettings with liquid-glass styling and inline auth flows. Polish
source control with pill toggle tabs, rounded action buttons, and stable
scrollbar gutter. Fix TUI flash-of-invisible-text by removing reactive
`last` prop from Dynamic call and patching CodeRenderable to skip
ensureVisibleTextBeforeHighlight when text is already visible. Add plan
docs for settings-to-page migration and full-bleed TUI layout.
…d polish

Replace the modal settings dialog with an inline page rendered at the shell
level (App.tsx), matching the vault pattern. Settings content is centered
with the same max-width as the chat area and respects the fullWidth toggle.

Key changes:
- UIStore: rename settingsDialog* → settings*, add closeSecondarySurface()
- SettingsPage: inline page with mx-auto width, scrollbarGutter, Suspense key
- SettingsNavList: grouped sections (General, Agent, Features) with headings
- PrimarySidebar: conditional settings nav, pinned Feedback button at bottom
- ContentTopBar: shows "Settings | Page" breadcrumb, back arrow wires up
- AccountSettings: pulsing skeleton replaces spinner flash on credential check
- SubagentsStore: persistent Zustand store (mirrors commands-store pattern)
- App.tsx: use null fallback for outer Suspense (SettingsPage module is
  tiny, inner Suspense handles the real skeleton with proper max-width)
- PrimarySidebar: hide Settings/Feedback bar on welcome page
- Move SETTINGS-DIALOG-TO-PAGE plan to tracked/done
…pdate plans and changelog

Rebrand internal "Backend" terminology to user-facing "Engine" (v1/v2)
across settings sidebar, backend page, and health section. Move app
icon picker inline as a settings row with dots inside the dock shelf.
Move 4 completed plans to done/, add OpenCode hooks system plan, and
expand v0.0.6 changelog.
… and add pending-send message count

Predicts when the backend will generate a title (2nd real user message) and
shows a skeleton loader until a non-default title arrives via SSE. Adds
pendingSendSessions to bump sidebar message count immediately on send.
Normalizes OpenCode's default "Session - <date>" titles to "Untitled".
Adds thinking-selector icon, updates provider model cache, and moves
completed plan to done/.
Cloned Ghostty and stripped upstream git history, contributions, and license files.
Kept AGENTS.md, HACKING.md, and PACKAGING.md as development references.
…al, polish file explorer skeleton

- Remove 4200+ unused Ghostty files (nix, flatpak, snap, fuzz corpus, examples, images, po translations, pkg, test harnesses)
- Rebrand macOS Swift layer: Ghostty → Orbit Terminal across AppDelegate, features, helpers, and tests
- Delete Ghostty-specific Swift modules (Ghostty.App, Ghostty.Config, Ghostty.Surface, SurfaceView, etc.)
- Add file explorer skeleton with minimum display time to prevent flash
- Update OpenCode provider models and chat adapter improvements
…odule to OrbitTerminalKit, optimize file explorer blur-reveal

Terminal-app: The previous commit over-eagerly renamed C API constants
(GHOSTTY_ACTION_*, GHOSTTY_COLOR_SCHEME_*, env vars) to ORBIT_TERMINAL_*.
These symbols come from the untouched libghostty C headers and must keep
their original names. Swift module renamed GhosttyKit → OrbitTerminalKit.

Agent app: Moved blur-reveal animation from per-item filter:blur() (dozens
of elements choking the browser) to a single container-level animate-title-in
class. Added entrance animation to SettingsNavList.
Key the header wrapper on settingsOpen so animate-title-in re-triggers
when toggling settings. Apply the same animation to sidebar conversation
actions and bottom utilities via a composite key.
…idebar/CLI, update providers

- Browser hide/show now uses NSWindow alpha transparency instead of offscreen
  positioning (fixes Mission Control glitches and z-order drift)
- Dialog overlays coordinate with browser panel visibility via new
  browser-overlay-coordination module (hide browser when dialog opens)
- Add decorum set_ns_window_alpha helper for NSWindow transparency control
- Terminal-app: add orbitctl CLI, IPC server (Unix socket), sidebar views,
  and expanded config options
- Update opencode-providers.json with latest model data
- Move browser-overlay-dialog-fix plan to done
…minal-app window and sidebar

TodoBar: add updateToolInput action to tool-store for real-time streaming,
fix flicker on consecutive TodoWrite calls with payload-aware selection,
update OC chat adapter to emit tool input updates. Move completed plans
to done/.

Terminal-app: enhance window styling, sidebar view, and controller setup.
…akeFromNib, add CLAUDE.md and browser resize plan

Move fullSizeContentView and titlebar hiding from configureSidebarTitlebar() into
awakeFromNib so the style is applied before AppKit computes initial layout — eliminates
the brief titlebar flash on window open. Block all titlebar accessories when sidebar is
active instead of hiding them after the fact. Extract hideTitlebarContainer() helper with
deferred second pass for reliability.
…owser animation plans

Remove unused IPC server, notification/tab metadata stores, sidebar view/manager,
and orbitctl CLI binary. Streamline window and terminal controller code.
Add browser atomic setframe and native NSWindow animation plan docs.
Remove OrbitLogo + separator from browser and file viewer empty states,
center the contextual icon (Globe, FileCode) on its own. Remove unused
Terminal-app translation files and update provider config.
…ontext limits

- Add CodeSearchToolWidget for OpenCode codesearch tool visualization
- Map OpenCode 'question' tool to AskUserQuestionWidget with answer extraction
- Remove step-finish token count noise from chat adapter
- Wire model context window limits from provider store to OcAgentSurface
- Add text-md (12px) size tier for control bar pills
- Unify button shapes to rounded-full, improve hover states with tinted backgrounds
- Add per-agent icons (Hammer, Map, Telescope) to agent mode picker
- Reorder controls: model picker before mode picker
- Update opencode-providers.json with expanded model data
- Fix OcThinkingSelector only showing for OpenCode backend
- Update Terminal-app CLAUDE.md
Improve title generation by stripping quotes, "Title:" prefix, and
trailing periods from LLM output, with smart 50-char word-boundary
truncation. Add implementation plan for OpenCode session restore
optimization (pre-warm sidecar, parallel data load, skeleton on mount).
… during restore

- Pre-warm sidecar on welcome page (saves 800ms-2s startup)
- Parallel listSessions + loadProviders, skip validateSession on happy path
- Skeleton on mount during restore, hide ChatContent to prevent input flash
- Error-path fallback distinguishes deleted vs transient failures
- Move plan to tracked/done
…l + add provider API key flow plan

Swap SlashCommandPopover from Radix Popover (portal-based, JS position
recalc) to an absolutely-positioned div inside the input container.
Eliminates frame lag on resize, adds integrated search bar with smooth
scrolling, and refines item styling. Also adds the provider API key →
model selection end-to-end flow plan.
…add upstream sync docs

- Add State.reset() and Provider.reset() to invalidate cached provider state after auth mutations (API key save, OAuth, removal)
- Call Provider.reset() in auth.ts, server.ts routes so frontend sees updated provider status immediately
- Improve ProvidersSettings with loading spinner, inline errors, toast notifications, and proper async handling
- Add upstream sync framework (CLAUDE.md, process docs, versioned diffs/plans for 1.2.26 and 1.2.27)
- Add ProvidersSettings unit tests
- Move completed plans to done, add rename-opencode-to-orbit plan
fazxes added 27 commits March 17, 2026 18:46
…o npm

- Rename ~800 imports across ~230 files from @opencode-ai/* to @orbit.build/*
- Rename packages/opencode-sdk → packages/orbit-sdk
- Update tsconfig, vite, vitest path aliases
- Fix config.ts type errors (empty object literal lacks index signature)
- Add legacy @opencode-ai/plugin migration in installDependencies()
- Publish @orbit.build/sdk@0.0.5 and @orbit.build/plugin@0.0.5 to npm (UNLICENSED)
- Set package versions to 0.0.5, license to UNLICENSED
- Rename remaining .opencode/ config references
- Add docs/development/NPM-PUBLISH-GUIDE.md
- Add npm_packages section to root CLAUDE.md
- Move rename plan to tracked/done
Incorporates audit findings across 5 review rounds:
- Add pure text-edit helpers (applyTextEdit, remapCommandStartIndex)
- Replace flat sibling DOM traversal with recursive findBadgeLocation
- Enforce full-token leading-only invariant via isDelimitedLeadingCommand
- Add collapsed-selection guard on Backspace/Delete handlers
- Add badge demotion in handleInputChange for runtime invariant repair
- Add comprehensive test plan (helpers, DOM, hook, component levels)
Replace broken inline badge approach (contentEditable="false" fails in
WKWebView) with a text overlay system: contentEditable stays plain text,
a positioned overlay renders known /commands in blue (text-git-untracked).

- Multi-command support: all known commands highlighted anywhere in text
- leadingCommand state set on popover selection (flicker-free)
- parseCommandSegments tokenizer for overlay rendering
- Scroll sync between contentEditable and overlay
- Remove ~300 lines of dead badge DOM code (rebuildInputDOM, findBadgeLocation,
  remapCommandStartIndex, applyTextEdit, etc.)
- Revert chip approach changes (context-chips.tsx, context.ts)
- Move 3 slash command plans to done
Wire OpenCode /compact through CompactIndicator instead of raw markdown,
settle compaction after loadMessages to prevent flash, and rewrite token
calculation to use last-assistant snapshot (matches reference app).
Add "How to Execute" sections to all 13 phase plans with concrete
commands for copying files from upstream, applying patches, and
deleting obsolete modules.
Major upstream migration bringing Effect dependency, branded ID system,
account management, schema modules, and new database migrations. Removes
deprecated .opencode config directory and old control module.
Introduce Lexical editor scaffolding for chat input, refactor
use-chat-input to reduce complexity, and update tests to match
new structure. Adds Lexical dependencies and Vite config aliases.
RAF-batch per-character SSE deltas into single Immer transactions per
animation frame, reducing Zustand set() calls from ~400/sec to ~60/sec.
Time-gate streaming reveal renders to ~80ms intervals and stabilize
ThinkingBlock array references to prevent unnecessary re-renders.
CSP treated localhost and 127.0.0.1 as different origins, blocking all
OpenCode API calls in production builds while Rust health checks passed.
Adds contract test, updates CSP docs, marks plan as done.
…nter

Extract visual/interaction logic from AskUserQuestionModal into a
backend-agnostic QuestionPrompt component. Both Claude and OpenCode
backends now render identical paginated question overlays while
preserving their native answer contracts.

- Create QuestionPrompt shared presenter with multi-select and
  allowCustom support
- Refactor AskUserQuestionModal as thin Claude wrapper
- Add OcQuestionModal as thin OpenCode wrapper (positional string[][])
- Fix keyboard shortcut conflict with isQuestionOverlayActive guard
- Delete OcQuestionCard (replaced by shared presenter)
- Add component tests for presenter, wrappers, and keyboard behavior
Move the "Workspaces" heading and worktree/workspace rows out of the
scrollable conversation list into a shrink-0 section above the scroll
container. Conversations now scroll independently while the workspace
chrome stays visible — matching the existing pattern used by the search
bar, tab heading, and action items.

- Simplify ConversationList to conversation items only (removed 7 props)
- Add capped worktree list (max-h-[160px]) with auto-scroll for active row
- useLayoutEffect ensures active worktree visible on tab switch/hydration
Thinking blocks and content no longer stream in parallel. A single
ordered frontier in useOcStreamingReveal sequences reveal per phase:
thinking₁ → content₁ → thinking₂ → content₂. Later thinking blocks
stay hidden until displayedContent reaches their contentOffset boundary.
Preserves multi-phase interleaving, tool visibility, and legacy fallback.
…ted models

- Wire ImageAttachment → FilePartInput through the OC adapter layer so
  images reach the OpenCode backend (was silently dropped at the adapter boundary)
- Widen SDK overlay promptAsync.parts to accept FilePartInput union
- Derive supportsImageInput from modalities.input/attachment per model
- Disable image button with tooltip when selected model lacks vision support
- Auto-clear image chips on model switch to unsupported model
- Add send-time guard in OC adapter to strip images for non-vision models
- Gate all shared-component logic with activeBackend === 'opencode'
Implement image attachment display as thumbnail tiles below chat messages,
with fullscreen lightbox viewer, navigation, and broken-image fallback.
SafeImage component converts data: URLs to opaque blob: URLs across all
four rendering surfaces (tiles, lightbox, context chips, queued bubble)
to prevent base64 data from leaking into the DOM. Includes Rust image
cache, OpenCode adapter image support, and image-only message send path.
Migrate from flat model fields (attachment, modalities, reasoning) to
nested capabilities object (capabilities.input.image, capabilities.reasoning).
Extract resolveOcModelSupportsImageInput to shared function, make
supportsImageInput required on OcProviderModel, and update SDK client
types to match upstream API changes.
Comprehensive 6-phase plan to remove ~185 lint suppressions and upgrade
~83 Rust #[allow] to #[expect]. Passed 3 audit rounds covering platform-
conditional lints, hashbrown sensitivity, and TUI override scoping.
Phase 1 — Config-level consolidation:
- Add no-unsafe-return config block for TUI .tsx files (~150 comments removed)
- Consolidate stress-test console suppressions to file-level (12→2)

Phase 2 — Rust #[allow] → #[expect] conversion:
- Convert ~24 allows to expects across crates/ and src-tauri/
- Discovered and removed 6 dead suppressions (needless_pass_by_value,
  too_many_lines no longer triggered)
- Add reason strings to 12 decorum platform-conditional allows
- Keep hashbrown-sensitive allows (terminal, LSP iter_over_hash_type)

Phase 3 — LSP crate audit:
- Convert 17 of 18 module-level allows to expects (all compile clean)
- Keep iter_over_hash_type as allow (hashbrown-sensitive)

Phase 4 — Orbit main TS fixes:
- CanvasInputArea: replace console.log with structured logger, use
  params in stub handlers (removes 3 eslint-disable comments)
- oc-message-store: replace 8 dynamic deletes with Reflect.deleteProperty
  (removes file-level eslint-disable)

Phase 5 — Agent-backend targeted:
- Create ambient declaration for @parcel/watcher/wrapper (eliminates
  @ts-ignore — the only banned suppression remaining)
- Remove 3 eslint-disable comments from watcher.ts

Phase 6 — Rust test standardization: already done via Phase 2 conversion.

All checks pass: 1454 Vitest tests, 310 Rust tests, zero clippy warnings,
zero ESLint warnings.
The .gitignore entry `SDK/` (meant for the top-level SDK docs folder)
was case-insensitively matching `Agent-backend/packages/sdk/` on macOS
(core.ignorecase=true), silently preventing those files from being
committed. In CI (Linux, case-sensitive) the files simply didn't exist,
breaking all tests that transitively import from @orbit.build/sdk.

- Change `SDK/` to `/SDK/` so it only matches the root docs directory
- Track Agent-backend/packages/sdk/ (SDK client library, 51 files)
- Track Agent-backend/packages/opencode/src/provider/sdk/ (Copilot
  provider, 31 files) — also hidden by the same rule
- Fix sdk .gitignore glob for .tgz files (/*.tgz → *.tgz)
The re-export from Agent-backend caused Vite to walk into the SDK's
tsconfig.json which extends @tsconfig/node22 — a dependency not
installed in the root workspace. Replace the re-export with a
bun-bundled version (13 modules flattened into one file, zero imports)
matching the existing self-contained client.d.ts pattern.
Tauri's build script requires all externalBin entries to exist.
The orbit-server sidecar was added to tauri.conf.json but CI
placeholder binaries were not updated to include it.
On Linux CI, macOS-only functions are compiled out so
clippy::needless_pass_by_value never fires — causing
unfulfilled-lint-expectations to error.
@fazxes
fazxes merged commit ff09e13 into main Mar 17, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant