feat: Sora v2 — fleet mesh, spawn_agents, placement, ops Gate 1#20
Conversation
…latform Reliability: make worker.js the sole scheduler (in-process gated behind LOCALMIND_IN_PROCESS_SCHEDULER), persist file-monitor mtime, and tighten internal + loopback auth so local requests are validated by forwarded-header values (fixes settings/providers 401/500) rather than mere presence. Wiring: model routing rules in the engine, human-approval pause/resume for workflows (v15 migration + approve/reject/pending APIs), and a seeded morning-briefing automation. Assistant depth: Reminders, Contacts and web_research tools; calendar from/to date fix; AppleScript injection hardening for mac-automation. Platform: task-graph chat path, functional critic + refuter LLM calls, and channel-based tool confirmations. Adds tests and updates README. Co-authored-by: Cursor <[email protected]>
Add the Browse surface and read_secure_webpage alias backed by Secure Browser plus a three-layer web-guard (kill switch, site grants, sensitive domains). Improve chat chrome (fixed header/composer), app-wide font scaling, personal- assistant prompting, spawn drill-down, and subagent tool-access handling for small models. Document the stack in README and the Secure Browser MCP guide. Co-authored-by: Cursor <[email protected]>
…e, scheduling
Implements the free/local features from docs/PRD-sora-v2.md, each tested:
- B Browse: collapsible/summonable Sora panel (⌘/, persisted, orb affordance)
- D schedule_task tool: NL→cron with confirmation, owner-scoped, on the
always-confirm floor (commits future autonomy)
- A Ops Kanban /ops: agent_processes migration (pillar/parent/progress),
?board=1 aggregation folding in workflow approvals + proposals, live board
- G.3 Context Broker: local-embed retrieval, token-budget packing, dedupe,
coverage-gap note; wired into the async system-prompt assembler
- C1 you.com web-search backend (opt-in, keyed) + provider setting; safe
brave/duckduckgo fallbacks
- C2 Brain foundation: brain_edges table, ~/.localmind/brain vault, zero-LLM
[[wikilink]] edge extraction
- F Pillars: engine pillar tagging via pure classifier; Strategist (Ideate)
persona + ideation prompt block
- G.1/G.2 Idle self-improvement: improvement_proposals + self_checks tables,
two-gate approval (propose → owner-approve → build; human-merge only),
idle detection + window, worker idle-tick, Proposals lane on the board
- H User Context Graph /context (phase 1): graph assembler over goals/memory/
brain, REST endpoint, reusable ForceGraph, page + nav (GraphQL is a later phase)
- E renovate.json (majors never auto-merge; next/react/electron/better-sqlite3/
playwright pinned); supersedes dependabot
Config schema → v25 (all migrations idempotent on fresh DB). Full suite green
(32 files, 221 tests); typecheck + lint clean.
Co-authored-by: Cursor <[email protected]>
- README: Ops board, Brain, Context Broker, User Context Graph, idle self-improvement, pillars/Ideate, hide-Sora-on-browse, you.com backend, new env vars (LM_SELF_IMPROVE, LM_IDLE_RUN_TESTS, LOCALMIND_EMBED_MODEL), Renovate/CI, and updated architecture table + key paths - docs/sora-v2-implementation.md: feature→code→test status map + deferred paid-cloud items and integration caveats - Browse: replace the faint corner toggle with a clear "Hide" control in the Sora panel header (onHide prop); floating orb summons it back - Rail: promote /ops and /context to their own top-level glyphs (Columns3, Waypoints); drop them from Work/Knowledge match groups Co-authored-by: Cursor <[email protected]>
…surfaces - Toaster: glassy, animated, orb-accented cards with a lifetime bar; coalesce repeated messages into one card with a ×N count (kills the "moved to trash" spam), cap the stack at 4, graceful enter/exit; respects reduced-motion - SettingsSidebar: add /browse, /ops, /context to HIDDEN_ROUTES so those full-bleed workspace surfaces aren't crowded by the config sidebar — the Sora panel's own Hide control is the only hide affordance on Browse Co-authored-by: Cursor <[email protected]>
The Ops board is a task board — ordinary chat turns complete within the chat window and belong in /orchestration, not here. bucketLanes now filters out process_type "chat" (via isBoardTask); only work that runs outside the chat window or can't finish within it shows: scheduled tasks, monitors, long-running jobs, workflows, subagents, and self-improvement proposals. Added a subtitle on /ops and a regression test. Co-authored-by: Cursor <[email protected]>
- Context Broker: budget fraction, floor tokens, and top-k are no longer hard-coded magic numbers — each derives from the live context-window setting and is overridable via LM_CONTEXT_BUDGET_FRACTION / _FLOOR_TOKENS / _TOPK, read at call time - self_checks: add GET /api/ops/self-checks so idle-cycle results are queryable instead of write-only (route-map: authenticated) - README: document the new configurable env vars Co-authored-by: Cursor <[email protected]>
The contracts stay fixed in code (guarantees intact) but are now discoverable at
runtime so nothing is blocked by not knowing the taxonomy:
- GET /api/ops/meta — pillars (id/label/description/color), board lanes,
proposal statuses, and the transition state machine
- GET /api/context/meta — context-graph node kinds, edge kinds, sources
- GET /api/brain/edges — query the Brain entity graph (?entity=) + distinct
edge types in use
Taxonomies are now single-source runtime constants (PILLARS/PILLAR_INFO,
BOARD_LANES, PROPOSAL_STATUSES/PROPOSAL_TRANSITIONS, NODE_KINDS/EDGE_KINDS/
SOURCES) with types derived from them. Pillar taxonomy extracted to a pure
client-safe module (src/lib/pillars.ts) to keep the DB layer out of the client
bundle. Added contract-consistency tests. route-map: all new reads authenticated.
Co-authored-by: Cursor <[email protected]>
Fixes "remind me to drink water in 4 minutes" failing silently:
- One-shot reminders: new run_at column (migration v26). Relative ("in 4
minutes") and near-term absolute ("tomorrow at 9") schedules become one-shot
tasks the worker/scheduler fire once when due, then disable — instead of being
mis-converted to a recurring daily cron.
- Tool robustness: normalizeOperation recovers from small-model mistakes (e.g.
"set_enabled" with a schedule+prompt → create), auto-derives a name from the
prompt, and coerces enabled string→bool.
- Channel fallback: an unconnected delivery channel (e.g. telegram) falls back
to in-app delivery with a note rather than hard-failing the reminder.
- Output shows the human-readable time, not raw cron.
worker.js + scheduler.ts fire due one-shots every tick and disable them;
recurring cron tasks (run_at NULL) unchanged.
Co-authored-by: Cursor <[email protected]>
- "What Sora knows" and "what Sora knows about you" now share one page: the Context Graph is the "About you" tab inside /knowledge (extracted into ContextGraphView). /context redirects to /knowledge?tab=context; the separate Context rail glyph is removed and folded back into the Knowledge cluster; ⌘K points at the tab. - New Settings → "Reach" section consolidating everything Sora can reach: its tools (permission tiers), channels, web access (kill switch + site grants), and MCP/plugin integration links — composed from the existing surfaces. - README updated for both. Co-authored-by: Cursor <[email protected]>
Small local models frequently narrate a tool call as JSON in their text reply instead of emitting a structured call — so the action never runs (the "spawn 15 agents" that didn't spawn) and the JSON leaks to the user. The engine now: - parses a text-only reply that's really a tool call (bare object, prose+JSON, code-fenced, arrays, stringified args) and runs it through the SAME permission guard + confirmation + audit path — nothing executes unsupervised - resolves common name confusions via a small alias map (scheduler→schedule_task, research→web_research, spawn→spawn_subagent, …) - emits tool_text_recovered so the chat + browse panel drop the leaked JSON bubble Parser is pure and unit-tested (src/lib/agent/text-tool-calls.ts). Co-authored-by: Cursor <[email protected]>
…g summary + recall) Stop dumping the whole conversation into context. History is now three layers: - Recent verbatim window (LM_HISTORY_RECENT_MESSAGES, default 16) kept exactly; the cut never orphans a tool result from its assistant turn. - Rolling summary of everything older, maintained *incrementally* (only summarizes turns not yet covered, tracked by covered_count) in a new conversation_summaries table, and folded into the system prompt — replacing the old "re-summarize the oldest 60% on every overflow" behavior. - On-demand retrieval: a new `recall` tool the agent calls to pull specific earlier messages by keyword instead of them all sitting in context. Short conversations are untouched. Summary/budget steps are best-effort with fallback to full history. Pure pieces (splitHistory, recall ranking) unit-tested. Note: idle-time precompute of summaries (curate-while-idle) is a clean follow-up but needs a shared message-builder so covered_count stays consistent with the live path — deferred to avoid drift. Co-authored-by: Cursor <[email protected]>
…ge builder - Extract buildConversationMessages(conversationId) — the single source for how a conversation's messages are fed to the model. Both the engine and the idle cycle use it, so the rolling-summary covered_count watermark stays consistent. - Idle cycle now precomputes/refreshes the rolling summary for the most recently-active conversations (refreshConversationSummaries), so live turns pay no summary latency. Bounded and best-effort; reports summaries:N. Co-authored-by: Cursor <[email protected]>
The broker was only wired into the async prompt assembler, which the engine doesn't call — so per-turn memory/brain/knowledge retrieval never happened. Now each main-chat turn retrieves the relevant slice for the user's query and folds a cited brief (within a token budget) into the system prompt: "only what's needed", not a full dump. Local (Ollama embeddings), best-effort, and silent when nothing relevant is found (no noise on empty knowledge bases). Subagents don't run it yet. Co-authored-by: Cursor <[email protected]>
- Ops + Orchestration trace panels: fixed 420px side panel became a full-screen overlay below sm (was squeezing the board/list on narrow windows); static 420px panel on sm+. - Knowledge notes: 220px/1fr grid now stacks to one column under md; reduced page padding on small screens. - Chat: narrow the conversations strip and tighten header/scroll/composer padding under 680px so the thread keeps room (left rail is a bottom bar there). Desktop appearance unchanged. Note: verified via build/typecheck (the in-IDE browser wasn't available to visually iterate) — conservative, reversible. Co-authored-by: Cursor <[email protected]>
Add images to a chat turn (composer paperclip). Images are downscaled client-side (max 1024px), previewed with remove, sent to the engine, persisted on the message (new messages.attachments column, migration conv v4), rehydrated on reload, and rendered in the user bubble. The shared message builder attaches them as base64 `images` on the user message; the Ollama provider passes them to vision models via the images field. Count/size capped in the API. Video is intentionally not wired — local vision models handle still images, not video (noted in the UI + README). Co-authored-by: Cursor <[email protected]>
The message box was stuck at one line with an internal scroll, so pasting a page of context was hard to see. The composer textarea now auto-resizes to fit its content (up to ~50vh, then scrolls) on typing and on programmatic changes (prefill, clear-after-send). Textarea now forwards a ref to support this. Co-authored-by: Cursor <[email protected]>
Two bugs made memory "saved" but invisible:
- API/scoping: /api/memory returned listMemory(user.id) which filtered
WHERE user_id=?, but the memory tool writes rows with the conversation owner
— null on single-operator/localhost setups. listMemory(userId) now also
includes unowned (user_id IS NULL) rows, so owner-visible memory shows while
per-user isolation is preserved.
- UI shape: the Memory tab rendered m.content, but rows are { key, value } — so
entries showed blank. It now renders key + value (and filters over both).
Co-authored-by: Cursor <[email protected]>
Persist agent_mode and related settings, close share-policy/guest auth holes, and harden browse, chat, ops, and provider paths so the UI matches what the APIs actually enforce. Co-authored-by: Cursor <[email protected]>
Make multi-agent work reliable on small models (sequential batch + text-call recovery), default agent_mode to auto, and close nav/deep-link gaps so every primary workflow stays mapped in rail, ⌘K, and docs. Co-authored-by: Cursor <[email protected]>
Remove the redirect that made Today unreachable after it was remapped into nav, and expand/scroll the audit row Analytics links to via ?id=. Co-authored-by: Cursor <[email protected]>
Paired devices share conversation history with per-device attribution, and chat can run on the least-loaded peer so the mesh feels like one machine. Co-authored-by: Cursor <[email protected]>
Ship the five follow-ups: attachment sync + relay status SSE + Auto default, unified spawn_agents with intent compiler, graph placement runner, Gate-1 idle proposal cards, and cleanup (orb icon, dead nav, Sora grants, PRD). Co-authored-by: Cursor <[email protected]>
Align shipped docs with fleet polish, unified spawn, placement, and idle proposal cards now on mainline feat/sora-v2. Co-authored-by: Cursor <[email protected]>
| const text = html | ||
| .replace(/<script[\s\S]*?<\/script>/gi, "") | ||
| .replace(/<style[\s\S]*?<\/style>/gi, "") |
| const text = html | ||
| .replace(/<script[\s\S]*?<\/script>/gi, "") |
| }); | ||
| const html = await r.text(); | ||
| const text = html | ||
| .replace(/<script[\s\S]*?<\/script>/gi, "") |
| const linkRe = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g; | ||
| const snipRe = /<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g; | ||
| const strip = (s: string) => | ||
| s.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/'/g, "'") |
| const linkRe = /<a[^>]+class="result__a"[^>]+href="([^"]+)"[^>]*>([\s\S]*?)<\/a>/g; | ||
| const snipRe = /<a[^>]+class="result__snippet"[^>]*>([\s\S]*?)<\/a>/g; | ||
| const strip = (s: string) => | ||
| s.replace(/<[^>]+>/g, "").replace(/&/g, "&").replace(/'/g, "'") |
| @@ -0,0 +1,35 @@ | |||
| import { describe, it, expect, beforeEach } from "vitest"; | |||
| import { | ||
| createProposal, | ||
| getProposal, | ||
| listProposals, | ||
| setProposalStatus, | ||
| canTransition, | ||
| PROPOSAL_STATUSES, | ||
| PROPOSAL_TRANSITIONS, | ||
| } from "../src/lib/db/proposals"; |
|
|
||
| const STEP_TIMEOUT_MS = 60_000; | ||
| const RUN_TIMEOUT_MS = 10 * 60_000; | ||
| const APPROVAL_TIMEOUT_MS = 24 * 60 * 60_000; |
There was a problem hiding this comment.
Pull request overview
This PR advances LocalMind “Sora v2” by expanding LAN fleet capabilities (chat placement + conversation sync), tightening localhost/internal auth hardening, and adding new agent tooling/surfaces (Browse, Ops board, idle self-improvement Gate 1, workflow approvals).
Changes:
- Security hardening: loopback detection for localhost-owner behavior; internal API token becomes per-install secret + loopback-only verification; web-guard enforcement expanded.
- Fleet + placement: new chat placement API and conversation-sync envelope/handler; capabilities advertise chat-relay acceptance.
- Agent UX + ops: Ops board meta/proposals/self-checks, workflow approvals UI + APIs, Browse sessions/tooling, multimodal image plumbing across providers.
Reviewed changes
Copilot reviewed 194 out of 197 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| worker.js | Background worker: per-install internal token + one-shot reminders + critic/idle ticks. |
| src/middleware.ts | Uses isLoopbackRequest for safe localhost-owner fallback. |
| src/lib/tools/web-research.ts | Adds combined web search + top-page reads (Secure Browser–backed). |
| src/lib/tools/time.ts | Tightens tool usage guidance to reduce unnecessary calls. |
| src/lib/tools/request-tool-access.ts | More tolerant parsing of small-model tool lists; clearer error. |
| src/lib/tools/reminders.ts | Adds macOS Reminders read/create tool (AppleScript). |
| src/lib/tools/recall.ts | Adds conversation-local keyword recall tool (non-persistent). |
| src/lib/tools/read-secure-webpage.ts | Adds first-class alias for Secure Browser MCP tool with guardrails. |
| src/lib/tools/pi-code.ts | Reclassifies pi_code run as destructive_shell for confirmations. |
| src/lib/tools/memory.ts | Adds “list” alias + normalizes invented ops. |
| src/lib/tools/mcp.ts | Web-guard interception + longer Secure Browser timeout. |
| src/lib/tools/mac-automation.ts | Hardens AppleScript escaping; http(s)-only URLs; escalates destructive payloads. |
| src/lib/tools/index.ts | Registers new tools; hides raw Secure Browser MCP name in tool list. |
| src/lib/tools/filesystem.ts | Rejects http(s) paths; points callers to read_secure_webpage/web_research. |
| src/lib/tools/contacts.ts | Adds macOS Contacts search/get tool (AppleScript). |
| src/lib/tools/check-resources.ts | Guidance to prefer sequential subagents when speedup isn’t needed. |
| src/lib/tools/calendar.ts | AppleScript escaping + optional date range parsing for reads and creates. |
| src/lib/tools/browser.ts | Ensures Playwright browsers path fix; adds web-guard checks + auditing. |
| src/lib/tools/browse-session.ts | Tool to drive an interactive /browse session linked to a conversation. |
| src/lib/scheduler.ts | Adds one-shot due reminders + disables after firing; filters recurring tasks. |
| src/lib/providers/types.ts | Extends user messages to support optional images[] for multimodal. |
| src/lib/providers/openai-compatible.ts | Adds LM Studio provider + multimodal shaping; apiKeyOptional support. |
| src/lib/providers/ollama.ts | Adds Ollama vision images shaping. |
| src/lib/providers/index.ts | Adds lmstudio provider selection. |
| src/lib/providers/anthropic.ts | Adds multimodal shaping; exports toAnthropic for tests. |
| src/lib/playwright-path.ts | Fixes PLAYWRIGHT_BROWSERS_PATH resolution in Cursor/sandbox environments. |
| src/lib/pillars.ts | Introduces pillar taxonomy + metadata as a dependency-free contract module. |
| src/lib/paths.ts | Adds BRAIN_DIR and ensures it is created. |
| src/lib/internal-auth.ts | Removes guessable internal token; persists random secret + loopback requirement. |
| src/lib/graph/runner-default.ts | Passes allowedTools/modelPreference/fromGraph; supports system_prefix input. |
| src/lib/graph/refute.ts | Removes stub sentinel; expects structured JSON verdict parsing. |
| src/lib/fleet/server.ts | Registers conversation-sync handler. |
| src/lib/fleet/envelope.ts | Adds conversation-sync envelope kinds. |
| src/lib/fleet/chat-placement.ts | New placement decision logic for chat execution across peers. |
| src/lib/fleet/capabilities.ts | Adds accepts_chat_relay capability derived from trusted peer policy. |
| src/lib/default-automations.ts | Seeds a default Morning Briefing scheduled task idempotently. |
| src/lib/db/self-checks.ts | Adds persistence/query for idle-cycle self-check records. |
| src/lib/db/proposals.ts | Adds improvement proposal lifecycle + guarded status transitions. |
| src/lib/db/fleet.ts | Adds sync_conversations default policy toggle. |
| src/lib/db/conversation-summary.ts | Adds rolling conversation summaries with covered_count watermark. |
| src/lib/context-graph.ts | Adds User Context Graph taxonomy + assembly (pure + DB-backed). |
| src/lib/channels/index.ts | Adds YES/NO confirmations for channel inbound messages. |
| src/lib/browse/app-bridge.ts | Bridges Electron app tab via CDP → browse session with web-guard gating. |
| src/lib/automations/monitor.ts | Persists file_change mtime baseline each check; returns mtimeMs. |
| src/lib/auth/session.ts | Uses isLoopbackRequest for no-login owner fallback. |
| src/lib/auth/loopback.ts | Implements robust loopback detection (resists Host spoofing/tunnels). |
| src/lib/agent/pillar-classify.ts | Adds pillar classification heuristics and persona mapping. |
| src/lib/agent/permission-guard.ts | Makes agent_mode default “auto”; adds schedule_write destructive floor. |
| src/lib/agent/idle.ts | Adds idle eligibility logic + LM_SELF_IMPROVE propose/off gating. |
| src/lib/agent/history-summary.ts | Adds incremental rolling summary generation + persistence. |
| src/lib/agent/history-context.ts | Adds splitHistory logic (keeps recent window tool-safe) + env-config window size. |
| src/lib/agent/graph-chat.ts | Routes runAgentCollect through single-node task graph + placement runner. |
| src/lib/agent/critic.ts | Wires critic judging to provider + active_model; parses JSON rating output. |
| src/lib/agent/conversation-messages.ts | Centralizes conversation → provider message shaping (incl. attachments→images). |
| src/lib/agent/confirmations.ts | Adds channel-scoped confirmation resolution (YES/NO) + previews. |
| src/instrumentation-node.ts | Disables in-process scheduler by default; seeds default automations; imports PW path fix. |
| src/components/ui.tsx | Makes Textarea forwardRef-compatible. |
| src/components/sidebar.tsx | Removes legacy MainNav sidebar component. |
| src/components/settings-sidebar.tsx | Adds “Reach” section; hides sidebar on new full-bleed routes; improves active-link logic. |
| src/components/rail.tsx | Updates primary destinations (adds Ops/Browse; adjusts matching). |
| src/components/ops/pillars.tsx | Client-side icon mapping for pillar taxonomy (uses shared PILLAR_INFO). |
| src/components/mobile-nav.tsx | Removes legacy mobile nav sheet. |
| src/components/font-scale.tsx | Applies settings-driven root font scale on client. |
| src/components/command-palette.tsx | Adds Ops/Browse/Today/Settings deep links. |
| src/components/chat/voice.tsx | Dictation now appends text (continuous mode) via onText opts. |
| src/app/permissions/page.tsx | Adds robust fetch error handling + fallback active profile selection. |
| src/app/models/page.tsx | Improves error handling; sets provider when selecting model; safer stream handling. |
| src/app/memory/page.tsx | Adds legacy /memory redirect to /knowledge?tab=memory. |
| src/app/mcp/page.tsx | Adds “warming-scanner” bootstrap phase display. |
| src/app/layout.tsx | Adds suppressHydrationWarning, min-h-0 main, and mounts FontScale. |
| src/app/icon.svg | Adds new orb favicon SVG. |
| src/app/graphs/page.tsx | Makes graphs list load resilient to JSON/errors. |
| src/app/graphs/[id]/page.tsx | Ensures loaded state is set on error. |
| src/app/fleet/page.tsx | Adds manual “Sync chats” + per-peer policy toggles for sync/relay. |
| src/app/data/page.tsx | Removes Node Buffer usage in client; adds browser-safe base64url encoder. |
| src/app/context/page.tsx | Adds legacy /context redirect to /knowledge?tab=context. |
| src/app/automations/page.tsx | Adds workflow pending approvals UI and approve/reject actions. |
| src/app/audit/page.tsx | Adds deep-link expansion via ?id=; wraps in Suspense for searchParams. |
| src/app/api/workflows/runs/[id]/reject/route.ts | Adds workflow run rejection endpoint. |
| src/app/api/workflows/runs/[id]/approve/route.ts | Adds workflow run approval endpoint. |
| src/app/api/workflows/approvals/pending/route.ts | Adds pending approvals listing endpoint. |
| src/app/api/web-guard/grants/route.ts | Adds CRUD API for site grants. |
| src/app/api/settings/route.ts | Expands allow-list so UI settings actually persist (agent_mode/idle/web). |
| src/app/api/providers/route.ts | Adds lmstudio provider; wraps GET in error handling. |
| src/app/api/orchestration/processes/route.ts | Adds board=1 aggregation with synthetic cards for approvals/proposals. |
| src/app/api/ops/self-checks/route.ts | Adds read-only self-checks listing endpoint. |
| src/app/api/ops/proposals/route.ts | Adds proposals list/create endpoints + audit logging. |
| src/app/api/ops/proposals/[id]/reject/route.ts | Adds proposal rejection endpoint + audit logging. |
| src/app/api/ops/proposals/[id]/merge/route.ts | Adds proposal merged marking endpoint + audit logging. |
| src/app/api/ops/proposals/[id]/approve/route.ts | Adds proposal approval endpoint + audit logging. |
| src/app/api/ops/meta/route.ts | Adds queryable ops taxonomy endpoint (pillars/lanes/proposal transitions). |
| src/app/api/internal/process-critic/route.ts | Adds internal critic processing endpoint (internal-auth gated). |
| src/app/api/internal/idle-tick/route.ts | Adds internal idle tick endpoint (internal-auth gated). |
| src/app/api/fleet/sync/route.ts | Adds manual peer conversation sync trigger endpoint. |
| src/app/api/fleet/peers/[id]/route.ts | Adds sync_conversations patch; stops forcing DEFAULT_PEER_POLICY merge. |
| src/app/api/fleet/chat-placement/route.ts | Adds chat placement decision endpoint. |
| src/app/api/conversations/[id]/export/route.ts | Adds authz check to prevent exporting other users’ conversations. |
| src/app/api/context/meta/route.ts | Adds context-graph taxonomy endpoint. |
| src/app/api/context/graph/route.ts | Adds context-graph fetch endpoint scoped to requester. |
| src/app/api/chat/route.ts | Adds browse session linking + multimodal image attachments support. |
| src/app/api/browse/session/route.ts | Adds browse session creation/validation endpoint with Playwright guidance. |
| src/app/api/browse/session/[id]/route.ts | Adds browse snapshot + close endpoints. |
| src/app/api/browse/session/[id]/navigate/route.ts | Adds browse navigate endpoint with blocked/expired handling. |
| src/app/api/browse/session/[id]/action/route.ts | Adds browse action endpoint (click/type/scroll/etc.). |
| src/app/api/browse/route.ts | Adds Browse backend that routes URLs through Secure Browser MCP. |
| src/app/api/browse/app-tab/route.ts | Adds Electron app-tab grant/revoke endpoint. |
| src/app/api/brain/edges/route.ts | Adds Brain entity edge listing endpoint. |
| src/app/api/audit/route.ts | Adds ?id= support to load a specific audit row. |
| src/app/analytics/page.tsx | Improves analytics load validation/error handling. |
| src/app/agents/page.tsx | Copy updates for sequential/parallel orchestration; minor UI cleanup. |
| renovate.json | Adds Renovate configuration; removes Dependabot config. |
| package.json | Adds Electron app scripts; adds electron dev dependency. |
| next.config.js | Removes obsolete experimental.nodeMiddleware; keeps redirect adjustments. |
| mcp-servers/secure-browser/tests/test_sanitizer.py | Updates sanitizer tests to include sensitive-context detection. |
| mcp-servers/secure-browser/src/secure_browser/server.py | Adds allow_sensitive gating for password-field pages. |
| mcp-servers/secure-browser/src/secure_browser/browser.py | Adds password-field detection + decomposed-node safety in sanitizer. |
| mcp-servers/secure-browser/run.sh | Adds PLAYWRIGHT_BROWSERS_PATH fallback logic. |
| mcp-servers/secure-browser/README.md | Documents SSRF/sensitive-context behavior + bootstrap flow. |
| mcp-servers/secure-browser/bootstrap.sh | Warms prompt-injection scanner model during bootstrap. |
| electron/preload.js | Adds narrow lmBrowser bridge API for /browse app-tab control. |
| .github/dependabot.yml | Removes Dependabot configuration (Renovate replaces it). |
| tests/workflow-resume.test.ts | Adds unit tests for resume step index logic. |
| tests/web-research.test.ts | Adds URL extraction tests for web_research. |
| tests/web-guard.test.ts | Adds contract tests for web-guard layering and helpers. |
| tests/spawn-intent.test.ts | Adds tests for spawn intent compilation + sequential defaulting. |
| tests/settings-allowlist.test.ts | Adds tests ensuring settings PATCH allow-list includes critical keys. |
| tests/routing.test.ts | Adds routing heuristic tests (URLs → secure readers; persona routing). |
| tests/route-map.test.ts | Adds route-map regression tests for auth requirements. |
| tests/request-tool-access.test.ts | Adds tests for tolerant request_tool_access parsing. |
| tests/refute.test.ts | Adds tests for refute verdict parsing behavior. |
| tests/recall-tool.test.ts | Adds recall tool tests (ranking, chronology, empty query). |
| tests/provider-images.test.ts | Adds tests for multimodal message shaping across providers. |
| tests/pillar-classify.test.ts | Adds tests for pillar classification + strategist persona seed. |
| tests/permission-guard.test.ts | Adds tests for schedule_write destructive floor + auto default behavior. |
| tests/ops-board.test.ts | Adds tests for board lane bucketing + ops migration columns. |
| tests/monitor-file-change.test.ts | Adds tests for file_change baseline/trigger semantics. |
| tests/memory-scope.test.ts | Adds tests for memory scoping (unowned memory visibility). |
| tests/medium-fixes.test.ts | Adds tests for migration runner + destructive floor in tools. |
| tests/mac-automation.test.ts | Adds tests for AppleScript escaping + URL scheme rejection. |
| tests/loopback.test.ts | Adds tests for robust loopback detection vs proxy/tunnel/host spoofing. |
| tests/internal-auth.test.ts | Adds tests for internal-auth token + loopback enforcement. |
| tests/idle.test.ts | Adds tests for idle window + eligibility + LM_SELF_IMPROVE mode. |
| tests/history-context.test.ts | Adds tests for history splitting + conversation_summaries migration. |
| tests/filesystem-http.test.ts | Adds test ensuring filesystem rejects http(s) paths. |
| tests/default-automations.test.ts | Adds tests for default automations seeding idempotency. |
| tests/conversation-messages.test.ts | Adds tests for attachments→images rehydration and tool message parsing. |
| tests/context-graph.test.ts | Adds tests for context graph assembly and taxonomy adherence. |
| tests/channel-confirmations.test.ts | Adds tests for YES/NO channel confirmation resolution. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| for (const t of dueOneShots) { | ||
| console.log(`[worker] running one-shot reminder: ${t.name}`); | ||
| await post("/api/internal/run-task", { taskId: t.id }); | ||
| d.prepare("UPDATE scheduled_tasks SET enabled=0 WHERE id=?").run(t.id); | ||
| } |
| if (secureReader) { | ||
| const page = await secureReader.execute({ url }, ctx); | ||
| sections.push(page.ok ? page.output.slice(0, 4000) : `(could not read: ${page.output})`); | ||
| } else { | ||
| try { | ||
| const r = await fetch(url, { | ||
| signal: AbortSignal.timeout(10000), | ||
| headers: { "User-Agent": "LocalMind/1.0 (research)" }, | ||
| }); | ||
| const html = await r.text(); | ||
| const text = html | ||
| .replace(/<script[\s\S]*?<\/script>/gi, "") | ||
| .replace(/<style[\s\S]*?<\/style>/gi, "") | ||
| .replace(/<[^>]+>/g, " ") | ||
| .replace(/\s+/g, " ") | ||
| .trim() | ||
| .slice(0, 3000); | ||
| sections.push(text || "(empty page)"); | ||
| } catch (e: any) { | ||
| sections.push(`(fetch failed: ${e?.message || "unknown"})`); | ||
| } | ||
| } |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 6 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| if (item.kind === "user") | ||
| return ( | ||
| <div key={i} className="lm-turn lm-turn--user"> | ||
| <div className="lm-bubble">{item.content}</div> |
There was a problem hiding this comment.
Relay chat never persists
High Severity
When send() targets a peer (including Auto), user and assistant messages are only updated in the UI's React state. The local conversations.db is not updated because the /api/chat endpoint is bypassed, causing conversation history to appear empty after a reload or re-opening.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| const res = await fetch(`/api/fleet/peers/${peerTarget}/chat`, { | ||
| method: "POST", | ||
| headers: { "content-type": "application/json" }, | ||
| headers: { "content-type": "application/json", Accept: "text/event-stream" }, |
There was a problem hiding this comment.
Relay drops image attachments
Medium Severity
Multimodal messages sent via Fleet relay (to a peer or 'Auto' mode) omit image attachments from the request body. This causes images to be dropped, or image-only turns to fail, when routed through a peer, unlike local chat which correctly includes them.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| const outcome = await executeGraph(graph.graph_id, { | ||
| runner: createPlacementRunner(), | ||
| skip_refute: true, | ||
| }); |
There was a problem hiding this comment.
Graph collect ignores conversation
High Severity
runCollectViaGraph accepts a conversationId but never passes it into buildSingleNode, executeGraph, or the runner context. Inbound chat relay creates a twin conversation id, yet the default graph collect path runs in a freshly created graph conversation instead, so relay threads do not accumulate history on the executor twin.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| const outcome = await executeGraph(graph.graph_id, { | ||
| runner: createPlacementRunner(), | ||
| skip_refute: true, | ||
| }); |
There was a problem hiding this comment.
Relay re-delegates via placement
High Severity
Inbound chat relay is meant to run on the executor that accepted the relay, but runCollectViaGraph always uses createPlacementRunner(), which can forward the single-node graph to another peer with lower load. A turn intended for one machine may execute elsewhere or fail unpredictably in a multi-peer mesh.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| ); | ||
| } catch { | ||
| acceptsChatRelay = false; | ||
| } |
There was a problem hiding this comment.
Wrong relay capability signal
Medium Severity
Heartbeat sets accepts_chat_relay when any trusted peer has accept_chat_relay in this node’s outbound policy, not when this node accepts inbound relay from the peer driving chat. Auto placement can pick that peer; the executor then rejects the initiator unless policy matches per peer.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.
| }, | ||
| async execute(input, ctx) { | ||
| const { compileSpawnIntent } = await import("../agent/spawn-intent"); | ||
| const intent = compileSpawnIntent(input); |
There was a problem hiding this comment.
spawn_agents ignores user prose
Medium Severity
spawn_agents calls compileSpawnIntent(input) without the user’s chat text. Prose-based parallel vs sequential inference in compileSpawnIntent only runs when opts.userText or input.intent/reason is set, so a structured tool call with batch and no mode always defaults multi-unit work to sequential even when the user asked for parallel execution.
Reviewed by Cursor Bugbot for commit fe1ea16. Configure here.


Summary
spawn_agents+ intent compiler (sequential default for multi-unit); triad kept/opsproposal cards whenLM_SELF_IMPROVE=proposeTest plan
npx vitest run(293+ tests)npx tsc --noEmit/fleet, enable sync + accept chat relay, confirm Auto run-on and origin labelsLM_SELF_IMPROVE=propose, confirm failing idle checks create proposal cards on/opsMade with Cursor