feat(provider): add Factory Droid as first-class provider#1
Open
0xSero wants to merge 19 commits into
Open
Conversation
- Add factoryDroid provider adapter using droid CLI stream-jsonrpc protocol - Implement delta coalescing (50ms) and delayed turn completion (200ms) to handle CLI's burst-mode token output without data loss - Map droid tool calls (create_message/tool_result) to item lifecycle events - Map session_title_updated and token usage notifications - Wire provider through contracts, shared model, server layers, and web UI - Add provider picker, model catalog, settings, health check, and traits picker - Add incremental domain event application in web store for streaming UX - Allow explicit provider switching in ProviderCommandReactor - Allow stateless session recovery for factoryDroid in ProviderService
…ctions Fixes conflict markers in ChatView.tsx, adds back getModelCapabilities/getDefaultEffort/hasEffortLevel/trimOrNull that were lost during rebase, updates composerProviderRegistry to use unified TraitsPicker, and fixes test expectations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…se modelSelection - Add FactoryDroidModelSelection to ModelSelection union so server can decode droid threads - Add PROVIDER_DISPLAY_NAMES constant for UI display - Fix store.syncServerReadModel to map modelSelection and defaultModelSelection instead of removed model field - Fix ProviderCommandReactor to read thread.modelSelection instead of thread.model - Add missing resolveModelSlugForProvider import to ChatView Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Removed the section listing supported providers from the README.
- Deduplicate effort API in shared/model.ts: getModelCapabilities now delegates to getReasoningEffortOptions instead of maintaining parallel logic - Remove dead exports (getEffectiveClaudeCodeEffort, unused constant re-exports) - Inline model slug constants into supportsClaudeX helpers - Refactor FactoryDroidRuntimeEvents to improve event mapping coverage - Update FactoryDroidAdapter, ChatView, and store for consistency
…treaming - Migrate TraitsPicker, ClaudeAdapter, ChatView off ModelCapabilities facade to use typed effort API directly (getReasoningEffortOptions, resolveReasoningEffortForProvider, supportsClaudeX helpers) - Fix codex fast mode support in TraitsPicker (was broken by earlier refactor) - Deduplicate EFFORT_LABELS: single export from shared/model.ts - Collapse supportsClaudeMaxEffort/supportsClaudeUltrathinkKeyword into aliases - Remove unused contract imports from model.ts - Fix doubled droid output: set sawAssistantTextDelta after fallback text - Fix premature turn completion: cancel idle timer on non-idle state - Split assistant text across tool use boundaries via segment-based itemIds so text renders around tool activity cards instead of one merged block
…nd event mapper - Delete unused FactoryDroidTraitsPicker.tsx (Droid uses generic TraitsPicker) - Rewrite FactoryDroidAdapter.ts: compact session context, remove 1.5s startup sleep (replaced with spawn-readiness await), consolidate JSON-RPC helpers - Shrink FactoryDroidRuntimeEvents.ts: deduplicate helpers, inline token usage mapper - Net reduction: ~365 lines removed across Droid integration files
- Remove getModelCapabilities, getDefaultEffort, hasEffortLevel, EffortLevel, ModelCapabilities (only used by tests, not production code) - Remove dead inferProviderForThreadModel and unused imports from store.ts - Rewrite facade tests to use direct typed API (getReasoningEffortOptions, supportsClaudeFastMode, etc.) - Net reduction: ~127 lines
Critical fixes: - Fix 'reasoning' -> 'reasoning_text' (invalid RuntimeContentStreamKind) - Wrap raw Error throws in typed ProviderAdapterError subtypes - Fix misleading authStatus:'authenticated' in health check (now 'unknown') - Deduplicate asObj helper between adapter and runtime events Dynamic model discovery: - Add models field to ServerProviderStatus schema - Parse available models from 'droid exec --help' at startup - Merge discovered models into web model picker via provider statuses - Trim hardcoded fallback to 3 core models (was 35) - Remove stale model aliases
The Droid CLI validates model IDs server-side. Don't reject dynamically discovered or custom model slugs that aren't in the static fallback list.
Map task_started, task_progress, task_completed, task_notification notifications from the Droid CLI to task.started/progress/completed ProviderRuntimeEvents. Recognize Task/subagent tool calls as collab_agent_tool_call items. Fix missing brace in model parser.
The 4s default timeout was too tight for 'droid exec --help' which takes ~2s+ to run. Split into separate probes with dedicated timeouts.
…racking
- Handle droid.ask_user RPC by emitting user-input.requested events,
rendering the questionnaire in the web UI, and responding with the
Droid CLI's expected format ({cancelled, answers: [{index, question, answer}]})
- Track toolUseId -> itemType in a registry so tool_result events
emit item.completed with the correct itemType (e.g. collab_agent_tool_call)
instead of always defaulting to dynamic_tool_call
- Clean up pendingUserInputs and toolUseRegistry on session stop
- Route droid.request_permission through the approval UI flow instead of auto-approving. In full-access mode, still auto-approve. In approval-required mode, emit request.opened, wait for user decision via respondToRequest, then forward the mapped selectedOption to the CLI. - Implement respondToRequest to resolve pending approval deferreds. - Hydrate turn items from create_message notifications so readThread returns authoritative per-turn item state. - Replace JSON.stringify equality in sameModelOptions with a sorted-key deep compare to avoid order-sensitive mismatches. - Add clarifying comment that ProviderHealth discovery is cached via Fiber (runs once at startup, not on every getStatuses call).
103997c to
b37e2cc
Compare
|
Will this also integrate missions? Would you welcome me to test it and provide feedback? :) |
…apter Fix two critical bugs in the Factory Droid adapter: 1. Segment-tracking race: incrementSegment() was called BEFORE mapFactoryDroidNotification evaluated sawDelta, resetting the flag to false. This caused create_message text blocks to be emitted as fallbackText even though the same text was already streamed via assistant_text_delta, producing visible duplicate/repeated content in the chat UI. Especially noticeable around subagent boundaries. 2. Idle timer during user interaction: droid_working_state_changed with idle state would schedule the turn-completion timer even while ask_user or request_permission was pending. The 200ms idle timer would fire and complete the turn prematurely, leaving the session stuck in a ready state while droid was still waiting for input. After answering, droid would continue generating into a completed turn, causing the never-responds / pending hang. Also refactors the adapter into separate JsonRpcProcess and TokenCoalescer modules for maintainability.
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.
Summary
This PR adds Factory Droid as a first-class provider
What changed
Diff size
Branch diff against
main...feature/droid-support:Top-level breakdown:
apps/server/src/provider: 15 files, +1308 / -116apps/server/src/persistence: 10 files, +830 / -112apps/server/src/orchestration: 15 files, +497 / -230apps/web/src/components: 22 files, +1414 / -1067apps/web/src/composerDraftStore.ts: 1 file, +584 / -181packages/contracts/src: 5 files, +242 / -65packages/shared/src: 2 files, +229 / -98Top refactor commit only (
4648f1da):README.md: 999 bytesapps/server/src/provider/Layers/FactoryDroidAdapter.ts: 22,352 bytesapps/server/src/provider/Layers/FactoryDroidRuntimeEvents.ts: 7,022 bytesapps/server/src/provider/Layers/FactoryDroidRuntimeEvents.test.ts: 1,658 bytesWhy these files/areas exist
apps/server/src/provider/*: provider lifecycle, runtime events, adapter capabilities, registry wiring.apps/server/src/orchestration/*: provider command/runtime integration and projection flow.apps/server/src/persistence/*: migration and projection updates for provider-aware model/session state.apps/web/src/components/*,composerDraftStore.ts,store.ts: provider-aware composer/model selection UX and Droid traits UI.packages/contracts/src/*,packages/shared/src/*: schema and model resolution updates required by both server and web.Validation
Focused validation for the top refactor commit:
bun fmt: passedbun lint: passed with preexisting warnings in web filesbun run --cwd apps/server test src/provider/Layers/FactoryDroidRuntimeEvents.test.ts: passedKnown blockers on current branch:
bun typecheckfails in preexisting branch files underapps/webandapps/server, includingapps/web/src/components/chat/FactoryDroidTraitsPicker.tsx,apps/web/src/components/ChatView.tsx,apps/web/src/composerDraftStore.ts,apps/server/src/orchestration/Layers/ProviderCommandReactor.ts, andapps/server/src/provider/Layers/ClaudeAdapter.ts.