feat(adapters): add Factory controls and host integrations - #64
Conversation
Add official Factory lifecycle hooks plus on-demand JSON-RPC interrupt and model-aware effort control. Attribute Synara and Conductor sessions through existing journals, open T3 threads through its published deep link, and bundle the hook helper in release apps.
Build both daemon sidecars, bundle a real debug Tauri app, and expose run, verify, debug, and log modes through one project-local command and Codex Run action.
Align Rust, Tauri, and npm package versions and document the adapter capability boundaries and verification evidence for the 0.3.0 release.
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 52 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughMicrobridge 0.3.0 adds Factory hooks, lifecycle ingestion, Droid JSON-RPC control, expanded Conductor and Synara attribution, T3 thread deep-link focus, updated UI integration management, and macOS build packaging for both native sidecars. ChangesFactory and release integration
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Factory
participant microbridgectl
participant microbridged
participant Droid
Factory->>microbridgectl: Send lifecycle hook JSON
microbridgectl->>microbridged: Ingest Factory session status
microbridged->>Droid: Issue JSON-RPC action
Droid-->>microbridged: Return streamed acknowledgement
Possibly related PRs
Suggested reviewers: Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR expands Microbridge’s first-party host support and release tooling by adding Factory lifecycle + JSON-RPC controls, attributing Synara/Conductor sessions via existing watchers, enabling semantic deep-link focus for T3 Code, bundling microbridgectl into app releases, and bumping the project to v0.3.0.
Changes:
- Add Factory integration (managed hooks installer + on-demand Droid JSON-RPC interrupt and reasoning-effort controls).
- Improve host attribution and focus handling (Synara/Conductor attribution; T3 Code thread deep-link focus).
- Prepare v0.3.0 release assets and developer workflow (bundled
microbridgectl, new build/run script, docs/version bumps).
Reviewed changes
Copilot reviewed 33 out of 36 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| script/build_and_run.sh | Adds a repeatable macOS dev build/run/verify entrypoint that bundles both sidecars. |
| README.md | Updates supported hosts/status to v0.3.x and documents Factory/Synara/Conductor behavior. |
| INSTALL.md | Documents Factory hook installation/removal paths and prerequisites. |
| docs/releases/v0.3.0.md | Adds v0.3.0 release notes covering new integrations and verification. |
| docs/protocol.md | Updates focused-app key source documentation to include Conductor/Factory. |
| docs/architecture.md | Updates architecture/footprint notes to include Factory + host attribution watchers. |
| docs/adapters.md | Updates adapter strategy guidance to include Factory hooks + JSON-RPC. |
| crates/microbridged/src/t3code.rs | Adds environment id tracking and semantic deep-link focus-open support. |
| crates/microbridged/src/state.rs | Enhances lifecycle ingestion to associate internal owners/capabilities; adds Factory diagnostics and new adapter cards. |
| crates/microbridged/src/main.rs | Installs/spawns the new internal Factory adapter action loop. |
| crates/microbridged/src/lib.rs | Exposes the new factory module. |
| crates/microbridged/src/factory.rs | Implements on-demand Droid JSON-RPC control + settings discovery for Factory sessions. |
| crates/microbridged/src/app_match.rs | Adds app-family matching for Conductor and Factory. |
| crates/microbridgectl/src/main.rs | Adds factory-event hook entrypoint for ingesting Factory lifecycle events. |
| crates/mb-protocol/src/lib.rs | Adds Factory to default adapter preferences and consent documentation. |
| crates/mb-adapters/src/hosts.rs | Adds Conductor workspace path → host attribution mapping. |
| crates/mb-adapters/src/codex.rs | Attributes Codex sessions to Conductor/Factory via host metadata and cwd mapping. |
| crates/mb-adapters/src/claude.rs | Attributes Claude sessions to Conductor via cwd mapping. |
| Cargo.toml | Bumps workspace to 0.3.0 and enables Tokio process feature. |
| Cargo.lock | Updates crate versions to 0.3.0. |
| apps/microbridge-ui/src/surfaces/surfaces.test.tsx | Updates UI tests for Factory adapter preference and copy changes. |
| apps/microbridge-ui/src/surfaces/Settings.tsx | Updates adapter UX copy and button labels for Factory/Synara/Conductor/T3. |
| apps/microbridge-ui/src/lib/bus.ts | Updates demo snapshot to include Conductor/Synara/Factory cards. |
| apps/microbridge-ui/src-tauri/tauri.conf.json | Bumps to 0.3.0 and bundles microbridgectl as an external binary. |
| apps/microbridge-ui/src-tauri/src/lib.rs | Implements Factory hook merge/install/remove + enable/forget wiring in the UI backend. |
| apps/microbridge-ui/src-tauri/Cargo.toml | Bumps UI crate version to 0.3.0. |
| apps/microbridge-ui/src-tauri/Cargo.lock | Updates UI lockfile versions to 0.3.0. |
| apps/microbridge-ui/package.json | Bumps UI package version to 0.3.0. |
| apps/microbridge-ui/package-lock.json | Updates UI lockfile version to 0.3.0. |
| adapters/t3code/README.md | Documents T3 network access requirement and new deep-link focus behavior. |
| adapters/synara/README.md | Adds Synara integration documentation (host attribution via watchers). |
| adapters/README.md | Updates adapter matrix/statuses to include Factory/Synara/Conductor and Rust-owned T3 adapter. |
| adapters/factory/README.md | Adds Factory integration documentation (hooks + JSON-RPC controls). |
| adapters/conductor/README.md | Adds Conductor integration documentation (watcher-based attribution only). |
| .github/workflows/release.yml | Bundles microbridgectl into release builds and UI artifacts. |
| .codex/environments/environment.toml | Adds a Codex environment action to run the new build/run script. |
Files not reviewed (1)
- apps/microbridge-ui/package-lock.json: Generated file
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for event in FACTORY_HOOK_EVENTS { | ||
| let groups = hooks | ||
| .entry((*event).to_string()) | ||
| .or_insert_with(|| serde_json::json!([])); | ||
| let groups = groups.as_array_mut().ok_or_else(|| { | ||
| format!("Factory {event} hooks are not an array; preserving them unchanged.") | ||
| })?; |
| let method_and_params = match action { | ||
| Action::Interrupt => ("droid.interrupt_session", json!({})), | ||
| Action::ReasoningEffortUp | Action::ReasoningEffortDown => { | ||
| let settings = read_session_settings(session_id)?; |
| fn read_session_settings(session_id: &str) -> Result<Value, String> { | ||
| let home = std::env::var_os("HOME").ok_or_else(|| "HOME is unavailable".to_string())?; | ||
| let root = PathBuf::from(home).join(".factory/sessions"); | ||
| let direct = root.join(format!("{session_id}.settings.json")); |
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/microbridged/src/app_match.rs (1)
110-116: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winMake these assertions exercise the canonical mapping.
same_appreturns immediately for identical strings, so these tests never callapp_familyorcanonical_family. Compare the display labels with their stable keys instead.Proposed test adjustment
- assert!(same_app("Conductor", "Conductor")); - assert!(same_app("Factory", "Factory")); + assert!(same_app("Conductor", "conductor")); + assert!(same_app("Factory", "factory"));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/microbridged/src/app_match.rs` around lines 110 - 116, Update the test function cursor_and_synara_match_themselves so assertions for identical applications compare each display label with its stable key rather than the same string, ensuring same_app invokes app_family and canonical_family. Preserve the existing negative comparisons and canonical self-match assertions for Conductor and Factory unless they likewise require display-label-to-key pairs.
🧹 Nitpick comments (1)
crates/mb-adapters/src/codex.rs (1)
192-193: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the explicit Conductor originator path.
The new
("conductor", "Conductor")mapping is not directly tested; the added Conductor case usescodex_sdk_tsand exerciseshost_from_cwdfallback instead. Add aconductor_desktopassertion alongside the Factory assertion.Suggested assertion
assert_eq!(codex_app_label(Some("synara_desktop"), None), "Synara"); assert_eq!(codex_app_label(Some("t3code_desktop"), None), "T3 Code"); + assert_eq!(codex_app_label(Some("conductor_desktop"), None), "Conductor"); assert_eq!(codex_app_label(Some("factory_desktop"), None), "Factory");🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mb-adapters/src/codex.rs` around lines 192 - 193, Add a direct test assertion for the ("conductor", "Conductor") mapping in the originator mapping tests, using the conductor_desktop fixture or input alongside the existing Factory assertion. Ensure the assertion exercises the explicit Conductor path rather than the codex_sdk_ts host_from_cwd fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/microbridge-ui/src-tauri/tauri.conf.json`:
- Line 75: Update the install flow in scripts/install.sh so
target/release/microbridgectl is copied or staged into
apps/microbridge-ui/src-tauri/binaries/ before npm run tauri build executes.
Ensure the staged filename matches the microbridgectl entry in the
tauri.conf.json externalBin configuration, while preserving the existing
microbridged staging behavior.
In `@crates/microbridged/src/factory.rs`:
- Around line 207-239: Validate session_id at the boundary of
read_session_settings before constructing any filesystem paths, rejecting empty
values and identifiers containing path separators, "..", or other path
components that could escape the sessions directory. Return a descriptive error
for invalid IDs, and only invoke find_settings or direct path construction after
validation.
- Around line 330-333: Update the assertion in
discovers_a_factory_droid_binary_outside_gui_path to avoid comparing binary with
an allocated PathBuf; compare against the borrowed "droid" path representation
instead, while preserving the existing is_file() alternative and test behavior.
- Around line 241-286: Replace the help-text parsing in parse_reasoning_levels
with the documented Microbridge contract for --reasoning-effort, using the
factory’s stable model/reasoning metadata rather than matching headings and
per-model line wording. Preserve the function’s Option<(Vec<String>, String)>
result and return the supported levels plus default for the requested model_id.
In `@crates/microbridged/src/t3code.rs`:
- Around line 91-94: Update the capabilities configuration containing focus_open
so it is true only on macOS by gating it with cfg!(target_os = "macos"). Keep
the existing focus behavior unchanged on macOS and ensure non-macOS targets no
longer advertise unsupported focus support.
- Around line 765-774: Extract the t3 thread deep-link construction from
open_thread into a reusable helper, preserving the existing Url path-segment
encoding behavior for environment and thread identifiers. Update open_thread and
constructs_the_official_t3_thread_deep_link to call this helper so the test
exercises production URL assembly.
---
Outside diff comments:
In `@crates/microbridged/src/app_match.rs`:
- Around line 110-116: Update the test function
cursor_and_synara_match_themselves so assertions for identical applications
compare each display label with its stable key rather than the same string,
ensuring same_app invokes app_family and canonical_family. Preserve the existing
negative comparisons and canonical self-match assertions for Conductor and
Factory unless they likewise require display-label-to-key pairs.
---
Nitpick comments:
In `@crates/mb-adapters/src/codex.rs`:
- Around line 192-193: Add a direct test assertion for the ("conductor",
"Conductor") mapping in the originator mapping tests, using the
conductor_desktop fixture or input alongside the existing Factory assertion.
Ensure the assertion exercises the explicit Conductor path rather than the
codex_sdk_ts host_from_cwd fallback.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: b287932f-6988-48ab-b87d-86f69910d78b
⛔ Files ignored due to path filters (3)
Cargo.lockis excluded by!**/*.lockapps/microbridge-ui/package-lock.jsonis excluded by!**/package-lock.jsonapps/microbridge-ui/src-tauri/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (33)
.codex/environments/environment.toml.github/workflows/release.ymlCargo.tomlINSTALL.mdREADME.mdadapters/README.mdadapters/conductor/README.mdadapters/factory/README.mdadapters/synara/README.mdadapters/t3code/README.mdapps/microbridge-ui/package.jsonapps/microbridge-ui/src-tauri/Cargo.tomlapps/microbridge-ui/src-tauri/src/lib.rsapps/microbridge-ui/src-tauri/tauri.conf.jsonapps/microbridge-ui/src/lib/bus.tsapps/microbridge-ui/src/surfaces/Settings.tsxapps/microbridge-ui/src/surfaces/surfaces.test.tsxcrates/mb-adapters/src/claude.rscrates/mb-adapters/src/codex.rscrates/mb-adapters/src/hosts.rscrates/mb-protocol/src/lib.rscrates/microbridgectl/src/main.rscrates/microbridged/src/app_match.rscrates/microbridged/src/factory.rscrates/microbridged/src/lib.rscrates/microbridged/src/main.rscrates/microbridged/src/state.rscrates/microbridged/src/t3code.rsdocs/adapters.mddocs/architecture.mddocs/protocol.mddocs/releases/v0.3.0.mdscript/build_and_run.sh
| "active": true, | ||
| "targets": "all", | ||
| "externalBin": ["binaries/microbridged"], | ||
| "externalBin": ["binaries/microbridged", "binaries/microbridgectl"], |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all tauri build invocations and check each is preceded by a microbridgectl copy.
rg -nP 'tauri\s+build|npm run tauri' -g '*.yml' -g '*.yaml' -g '*.sh' -C15
rg -n 'microbridgectl' -g '*.yml' -g '*.yaml' -g '*.sh'Repository: DevVig/microbridge
Length of output: 156
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== candidate files ==\n'
git ls-files | rg '(\.github/workflows/.*\.ya?ml$|script/.*\.sh$|tauri\.conf\.json$|package\.json$|Cargo\.toml$)'
printf '\n== tauri build invocations ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'tauri(\s+build|[^[:alnum:]_:-])|npm run tauri|cargo tauri' .
printf '\n== microbridgectl references ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'microbridgectl' .Repository: DevVig/microbridge
Length of output: 12994
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== files under apps/microbridge-ui ==\n'
git ls-files 'apps/microbridge-ui/**'
printf '\n== package scripts ==\n'
sed -n '1,220p' apps/microbridge-ui/package.json
printf '\n== tauri config ==\n'
sed -n '1,180p' apps/microbridge-ui/src-tauri/tauri.conf.json
printf '\n== workflow files mentioning microbridge-ui ==\n'
rg -n --hidden --glob '.github/workflows/*.yml' --glob '.github/workflows/*.yaml' 'microbridge-ui|tauri|microbridgectl|build_and_run|externalBin' .github/workflows
printf '\n== shell scripts mentioning microbridge-ui ==\n'
rg -n --hidden --glob '*.sh' 'microbridge-ui|tauri|microbridgectl|externalBin' script .Repository: DevVig/microbridge
Length of output: 14694
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,240p' apps/microbridge-ui/package.json
printf '\n== workflows ==\n'
for f in .github/workflows/*.{yml,yaml}; do
[ -f "$f" ] || continue
echo "--- $f ---"
rg -n 'microbridge-ui|tauri|microbridgectl|externalBin|build_and_run|copy' "$f" || true
done
printf '\n== repo files containing microbridge-ui build references ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'microbridge-ui|tauri build|cargo tauri|build_and_run|externalBin|microbridgectl' .Repository: DevVig/microbridge
Length of output: 18424
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== scripts/install.sh around the Tauri path ==\n'
sed -n '120,170p' scripts/install.sh
printf '\n== any microbridgectl staging near tauri build ==\n'
rg -n 'microbridgectl|tauri build|tauri dev|web build|copy' scripts/install.sh script/build_and_run.sh .github/workflows/release.ymlRepository: DevVig/microbridge
Length of output: 4345
Stage microbridgectl before npm run tauri build in scripts/install.sh.
scripts/install.sh:135-139 builds the UI bundle without copying target/release/microbridgectl into apps/microbridge-ui/src-tauri/binaries/, so this install path will trip over the new externalBin entry.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@apps/microbridge-ui/src-tauri/tauri.conf.json` at line 75, Update the install
flow in scripts/install.sh so target/release/microbridgectl is copied or staged
into apps/microbridge-ui/src-tauri/binaries/ before npm run tauri build
executes. Ensure the staged filename matches the microbridgectl entry in the
tauri.conf.json externalBin configuration, while preserving the existing
microbridged staging behavior.
| fn read_session_settings(session_id: &str) -> Result<Value, String> { | ||
| let home = std::env::var_os("HOME").ok_or_else(|| "HOME is unavailable".to_string())?; | ||
| let root = PathBuf::from(home).join(".factory/sessions"); | ||
| let direct = root.join(format!("{session_id}.settings.json")); | ||
| let path = if direct.is_file() { | ||
| direct | ||
| } else { | ||
| find_settings(&root, session_id, 3) | ||
| .ok_or_else(|| format!("Factory settings for session {session_id} were not found."))? | ||
| }; | ||
| let content = std::fs::read_to_string(&path) | ||
| .map_err(|error| format!("read {}: {error}", path.display()))?; | ||
| serde_json::from_str(&content).map_err(|error| format!("parse {}: {error}", path.display())) | ||
| } | ||
|
|
||
| fn find_settings(root: &Path, session_id: &str, depth: usize) -> Option<PathBuf> { | ||
| if depth == 0 { | ||
| return None; | ||
| } | ||
| let wanted = format!("{session_id}.settings.json"); | ||
| for entry in std::fs::read_dir(root).ok()?.flatten() { | ||
| let path = entry.path(); | ||
| if path.file_name().and_then(|name| name.to_str()) == Some(wanted.as_str()) { | ||
| return Some(path); | ||
| } | ||
| if path.is_dir() { | ||
| if let Some(found) = find_settings(&path, session_id, depth - 1) { | ||
| return Some(found); | ||
| } | ||
| } | ||
| } | ||
| None | ||
| } |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win
Unsanitized session_id used to build a filesystem path.
read_session_settings/find_settings join session_id directly into a path under ~/.factory/sessions (root.join(format!("{session_id}.settings.json"))) with no check that it's free of //.. components. session_id originates from Factory's own hook payload today, but nothing here enforces that invariant at this boundary, so a malformed or unexpected session id could cause reads outside the intended directory.
🛡️ Proposed guard
fn read_session_settings(session_id: &str) -> Result<Value, String> {
+ if session_id.is_empty() || session_id.contains(['/', '\\']) || session_id.contains("..") {
+ return Err(format!("invalid Factory session id: {session_id}"));
+ }
let home = std::env::var_os("HOME").ok_or_else(|| "HOME is unavailable".to_string())?;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn read_session_settings(session_id: &str) -> Result<Value, String> { | |
| let home = std::env::var_os("HOME").ok_or_else(|| "HOME is unavailable".to_string())?; | |
| let root = PathBuf::from(home).join(".factory/sessions"); | |
| let direct = root.join(format!("{session_id}.settings.json")); | |
| let path = if direct.is_file() { | |
| direct | |
| } else { | |
| find_settings(&root, session_id, 3) | |
| .ok_or_else(|| format!("Factory settings for session {session_id} were not found."))? | |
| }; | |
| let content = std::fs::read_to_string(&path) | |
| .map_err(|error| format!("read {}: {error}", path.display()))?; | |
| serde_json::from_str(&content).map_err(|error| format!("parse {}: {error}", path.display())) | |
| } | |
| fn find_settings(root: &Path, session_id: &str, depth: usize) -> Option<PathBuf> { | |
| if depth == 0 { | |
| return None; | |
| } | |
| let wanted = format!("{session_id}.settings.json"); | |
| for entry in std::fs::read_dir(root).ok()?.flatten() { | |
| let path = entry.path(); | |
| if path.file_name().and_then(|name| name.to_str()) == Some(wanted.as_str()) { | |
| return Some(path); | |
| } | |
| if path.is_dir() { | |
| if let Some(found) = find_settings(&path, session_id, depth - 1) { | |
| return Some(found); | |
| } | |
| } | |
| } | |
| None | |
| } | |
| fn read_session_settings(session_id: &str) -> Result<Value, String> { | |
| if session_id.is_empty() || session_id.contains(['/', '\\']) || session_id.contains("..") { | |
| return Err(format!("invalid Factory session id: {session_id}")); | |
| } | |
| let home = std::env::var_os("HOME").ok_or_else(|| "HOME is unavailable".to_string())?; | |
| let root = PathBuf::from(home).join(".factory/sessions"); | |
| let direct = root.join(format!("{session_id}.settings.json")); | |
| let path = if direct.is_file() { | |
| direct | |
| } else { | |
| find_settings(&root, session_id, 3) | |
| .ok_or_else(|| format!("Factory settings for session {session_id} were not found."))? | |
| }; | |
| let content = std::fs::read_to_string(&path) | |
| .map_err(|error| format!("read {}: {error}", path.display()))?; | |
| serde_json::from_str(&content).map_err(|error| format!("parse {}: {error}", path.display())) | |
| } | |
| fn find_settings(root: &Path, session_id: &str, depth: usize) -> Option<PathBuf> { | |
| if depth == 0 { | |
| return None; | |
| } | |
| let wanted = format!("{session_id}.settings.json"); | |
| for entry in std::fs::read_dir(root).ok()?.flatten() { | |
| let path = entry.path(); | |
| if path.file_name().and_then(|name| name.to_str()) == Some(wanted.as_str()) { | |
| return Some(path); | |
| } | |
| if path.is_dir() { | |
| if let Some(found) = find_settings(&path, session_id, depth - 1) { | |
| return Some(found); | |
| } | |
| } | |
| } | |
| None | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/microbridged/src/factory.rs` around lines 207 - 239, Validate
session_id at the boundary of read_session_settings before constructing any
filesystem paths, rejecting empty values and identifiers containing path
separators, "..", or other path components that could escape the sessions
directory. Return a descriptive error for invalid IDs, and only invoke
find_settings or direct path construction after validation.
| fn parse_reasoning_levels(help: &str, model_id: &str) -> Option<(Vec<String>, String)> { | ||
| let mut in_models = false; | ||
| let mut display = None; | ||
| for line in help.lines() { | ||
| let trimmed = line.trim(); | ||
| if trimmed == "Available Models:" { | ||
| in_models = true; | ||
| continue; | ||
| } | ||
| if trimmed == "Custom Models:" || trimmed == "Model details:" { | ||
| in_models = false; | ||
| } | ||
| if in_models { | ||
| let mut fields = trimmed.split_whitespace(); | ||
| if fields.next() == Some(model_id) { | ||
| display = Some( | ||
| fields | ||
| .collect::<Vec<_>>() | ||
| .join(" ") | ||
| .trim_end_matches(" (default)") | ||
| .trim_end_matches(" [Deprecated]") | ||
| .to_string(), | ||
| ); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| let display = display?; | ||
| let prefix = format!("- {display}: supports reasoning: Yes; supported: ["); | ||
| let line = help | ||
| .lines() | ||
| .map(str::trim) | ||
| .find(|line| line.starts_with(&prefix))?; | ||
| let levels_raw = line.strip_prefix(&prefix)?.split(']').next()?; | ||
| let levels: Vec<String> = levels_raw | ||
| .split(',') | ||
| .map(|item| item.trim().to_string()) | ||
| .collect(); | ||
| let default = line | ||
| .split("default: ") | ||
| .nth(1) | ||
| .map(str::trim) | ||
| .unwrap_or_else(|| levels.first().map(String::as_str).unwrap_or("none")) | ||
| .to_string(); | ||
| Some((levels, default)) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Factory AI droid CLI exec --help stable output format reasoning effort JSON-RPC
💡 Result:
The Factory AI droid exec CLI is designed for headless, non-interactive automation and integration [1][2]. It supports configurable reasoning and structured output, making it suitable for CI/CD pipelines and programmatic agent interactions [2][3]. ### Reasoning Effort The reasoning effort level controls the model's deliberation depth [2]. You can configure this via the command-line flag -r or --reasoning-effort [4][5]. * Supported values: off, none, low, medium, high [2][6]. * Effect: off / none disables structured reasoning for maximum speed, while medium and high increase deliberation time for complex tasks [2][6]. * Usage example: droid exec "your task" --reasoning-effort high [4]. ### JSON-RPC and Output Formats For programmatic integration, droid exec supports several output formats, selectable via the -o or --output-format flag [3][5]. * stream-jsonrpc: Specifically designed for SDKs and persistent integrations, this format provides structured, machine-readable communication [7][5]. * stream-json: Useful for real-time monitoring of agent progress [2][5]. * json: Provides the final output only [2]. * text: Standard text output [5]. The Factory Droid SDK (e.g., droid-sdk-python) utilizes stream-jsonrpc over a subprocess, allowing you to manage session lifecycles, stream tool usage, and receive thinking process updates programmatically [7]. ### Summary of CLI Execution To view all available configuration options, use the built-in help command: droid exec --help [4][1] Key flags for automation include: * --auto <low|medium|high>: Sets the agent's autonomy level (default is read-only) [4][3]. * -m, --model <id>: Specifies the model ID to use for the execution [4][5]. * -o, --output-format <format>: Controls the data structure of the output [3][5]. * -s, --session-id <id>: Resumes an existing session [1][3].
Citations:
- 1: https://docs.factory.ai/reference/cli-reference
- 2: https://docs.factory.ai/guides/building/droid-exec-tutorial
- 3: https://toolsbase.dev/en/reference/droid-commands
- 4: https://docs.factory.ai/cli/droid-exec/overview
- 5: https://agent-safehouse.dev/docs/agent-investigations/droid
- 6: https://docs.factory.ai/cli/configuration/settings
- 7: https://github.com/Factory-AI/droid-sdk-python/blob/main/README.md
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant file and nearby references
git ls-files crates/microbridged/src/factory.rs
echo '--- outline ---'
ast-grep outline crates/microbridged/src/factory.rs --view expanded || true
echo '--- search for parse_reasoning_levels usage ---'
rg -n "parse_reasoning_levels|reasoning-effort|Available Models:|Model details:|supports reasoning" crates/microbridged/src/factory.rs crates/microbridged -S || true
echo '--- line count ---'
wc -l crates/microbridged/src/factory.rsRepository: DevVig/microbridge
Length of output: 3261
🌐 Web query:
"Available Models:" "Model details:" "supports reasoning: Yes; supported:" Factory droid exec help
💡 Result:
Yes, Factory Droid supports reasoning across its command-line interface, including within droid exec [1][2][3]. The droid exec command supports reasoning through the --reasoning-effort flag (or -r shorthand) [1][2]. This flag allows users to control the depth of the AI's analysis, with available levels typically including off, low (default), medium, and high [2][3]. Increasing the effort level generally leads to deeper analysis at the cost of slower execution [3]. Key technical details regarding reasoning support include: - Flag Usage: You can specify reasoning effort directly in your command, for example: droid exec -r high "your prompt here" [2]. - Automation Contexts: The reasoning effort can also be configured in automated environments, such as within GitHub Actions (via the reasoning_effort input) [4] or when building interactive apps via droid exec [3]. - Limitations: While core support is widespread, it is noted in some documentation that reasoning effort may not yet be supported for certain custom models, though this is subject to change [1]. - Interaction: In the interactive TUI mode of the Droid CLI, you can cycle through reasoning effort levels by pressing Tab [2]. For further assistance, you can use the droid exec --help command directly in your terminal to see the most up-to-date configuration options for your specific version of the Droid CLI [1][5].
Citations:
- 1: https://docs.factory.ai/cli/droid-exec/overview
- 2: https://factory.mintlify.app/reference/cli-reference
- 3: https://docs.factory.ai/guides/building/droid-exec-tutorial
- 4: https://github.com/Factory-AI/droid-action
- 5: https://docs.factory.ai/welcome
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '40,75p' crates/microbridged/src/factory.rs
echo '--- tests ---'
sed -n '299,334p' crates/microbridged/src/factory.rsRepository: DevVig/microbridge
Length of output: 3045
Parse a documented contract instead of --help text Factory’s docs expose --reasoning-effort, but this parser depends on the exact Available Models: / Model details: layout and per-model line wording. Any CLI text change will disable reasoning-effort adjustment for Microbridge users and surface as a user-facing error.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/microbridged/src/factory.rs` around lines 241 - 286, Replace the
help-text parsing in parse_reasoning_levels with the documented Microbridge
contract for --reasoning-effort, using the factory’s stable model/reasoning
metadata rather than matching headings and per-model line wording. Preserve the
function’s Option<(Vec<String>, String)> result and return the supported levels
plus default for the requested model_id.
| new_session: false, | ||
| focus_open: false, | ||
| // T3 publishes a stable, semantic deep link for a specific environment | ||
| // and thread. This deliberately avoids synthesizing UI keybindings. | ||
| focus_open: true, |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Gate focus_open by platform.
capabilities() advertises focus support on every target, but open_thread explicitly fails on non-macOS. This lets AdapterCapabilities::supports expose an action that cannot succeed. Use cfg!(target_os = "macos") for focus_open or restrict this adapter capability to macOS builds.
Also applies to: 610-613
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/microbridged/src/t3code.rs` around lines 91 - 94, Update the
capabilities configuration containing focus_open so it is true only on macOS by
gating it with cfg!(target_os = "macos"). Keep the existing focus behavior
unchanged on macOS and ensure non-macOS targets no longer advertise unsupported
focus support.
|
|
||
| #[test] | ||
| fn constructs_the_official_t3_thread_deep_link() { | ||
| let mut url = Url::parse("t3code://threads/").unwrap(); | ||
| url.path_segments_mut() | ||
| .unwrap() | ||
| .push("environment 1") | ||
| .push("thread/1"); | ||
| assert_eq!(url.as_str(), "t3code://threads/environment%201/thread%2F1"); | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
ast-grep outline crates/microbridged/src/t3code.rs --view expanded
sed -n '700,820p' crates/microbridged/src/t3code.rs
rg -n "open_thread|path_segments_mut|thread%2F1|constructs_the_official_t3_thread_deep_link|environment 1" crates/microbridged/src/t3code.rsRepository: DevVig/microbridge
Length of output: 5474
🏁 Script executed:
sed -n '586,610p' crates/microbridged/src/t3code.rsRepository: DevVig/microbridge
Length of output: 1133
Extract the deep-link builder. This test only checks Url segment encoding; it won’t catch regressions in open_thread while the production URL assembly stays inline. Move that construction into a helper shared by open_thread and the test.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/microbridged/src/t3code.rs` around lines 765 - 774, Extract the t3
thread deep-link construction from open_thread into a reusable helper,
preserving the existing Url path-segment encoding behavior for environment and
thread identifiers. Update open_thread and
constructs_the_official_t3_thread_deep_link to call this helper so the test
exercises production URL assembly.
Summary
Capability boundaries
Verification
Release
After merge, tag v0.3.0 to build the signed/notarized DMGs, updater artifacts, daemon archives, and Homebrew bump.
Summary by CodeRabbit