mirror of
https://github.com/nexu-io/open-design.git
synced 2026-10-27 09:51:12 +01:00
b1fd529eea3cb5ebdb55265e2889f049588fe607
198
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
b1fd529eea |
Feat/aihubmix byok provider (#3583)
* feat(media): integrate AIHubMix as full-stack BYOK provider Add AIHubMix (https://aihubmix.com) as a BYOK provider at the same layer as SenseAudio — both a chat protocol and a media-generation provider — so users can paste one key and get chat + image + speech, with the same key auto-mirrored into media-config for the `od media` CLI path. AIHubMix is an OpenAI-wire-compatible aggregator. Its distinguishing trait is the fixed APP-Code attribution header (discount), funnelled through a single aihubmixHeaders() helper so every outbound call point (chat proxy, connection test, model discovery, media renderers, memory extractor) injects it in one place. Chat protocol layer: - Extend ApiProtocol / ConnectionTestProtocol / MemoryExtractionProvider unions. - New web provider client + dispatch; mirror across apiProtocols, config, utils, Settings/ChatComposer/HomeHero UI surfaces. - Refactor the SenseAudio tool-loop chat proxy into a shared registerByokToolChatProxy() factory; register senseaudio + aihubmix through it (senseaudio behaviour byte-preserved). connectionTest + providerModels treat aihubmix as OpenAI-shaped with APP-Code headers. Media layer: - Register provider + image (aihubmix-gpt-image-1 / -dall-e-3) and speech (aihubmix-tts-1) models; prefixed ids stay unique and map to wire names. - OpenAI-compatible renderers (/v1/images/generations, /v1/audio/speech) and chat tool executors; ENV_KEYS + media-picker allowlists updated. Note: AIHUBMIX_APP_CODE in apps/daemon/src/aihubmix.ts is an empty placeholder — fill it with the code from https://aihubmix.com/appstore to enable the discount header (the integration works without it). Tests: media-aihubmix.test.ts (wire-name mapping, APP-Code invariant, image + speech renderers) and an AIHubMix proxy-route case; verify:media-models, guard, and typecheck all green. * feat(media): source AIHubMix model lists from live catalog endpoints Chat LLM models and media image models now come from AIHubMix's live catalogue (GET /api/v1/models?type=llm | image_generation, public, returns model_id / model_name) instead of only the hand-curated static lists. Chat: the existing /api/provider/models discovery, when protocol=aihubmix, now hits the type=llm catalogue and parses the AIHubMix envelope (342 models live). The static SUGGESTED_MODELS remain as the pre-fetch default. Media image: new GET /api/media/providers/aihubmix/models endpoint proxies the type=image_generation catalogue (53 models), prefixes ids `aihubmix-` for uniqueness, and caches for 5 min with stale-on-error fallback. generateMedia synthesizes a def for any unregistered `aihubmix-*` id (like the fal-ai/* path) so every catalogue model renders through the OpenAI-compatible image endpoint. Helpers (aihubmixCatalogUrl / parseAIHubMixCatalog) + tests for url building, envelope parsing, and rendering a dynamic catalogue id. The web media pickers still read the static seed list; wiring them to the live endpoint is a follow-up UI change. * feat(web): show live AIHubMix image catalog in all media pickers Wire the AIHubMix image-generation catalogue (53 models, fetched from the new daemon endpoint) into every image-model picker. A shared hook (useAIHubMixImageModels) fetches once per session and caches; mergeAihubmixImageModels replaces the two static aihubmix seeds with the live list (and falls back to the seeds when the fetch hasn't resolved or fails, so the picker is never empty). Surfaces: - NewProjectPanel image picker — merged list passed to supportedModels('image'). - Home media composer — buildHomeMediaComposer / fieldsForSurface / normalizeHomeMediaInputs now accept an injected imageModels list; HomeView fetches and threads it through all three composer call sites. Saved/template image-model ids starting with `aihubmix-` are accepted even though they're absent from the static IMAGE_MODELS, since they render dynamically daemon-side. * feat(byok): load AIHubMix chat model list without an API key The BYOK chat model dropdown already merges live-fetched models with the static suggestions, but the fetch was gated on a non-empty API key (every provider hits /v1/models behind its key). AIHubMix's catalogue is public, so its 342-model list now loads as soon as the AIHubMix tab is selected — no key required. - Daemon /api/provider/models accepts an empty apiKey when protocol=aihubmix (baseUrl still required); providerModelsHeaders sends no Bearer when the key is blank. - Web: missingByokModelFetchFields treats apiKey as optional for aihubmix, and the auto-fetch effect skips the commit-on-blur gate for aihubmix (base URL is prefilled and there's no key to debounce), so the list populates on tab select. * feat(byok): default AIHubMix chat model to claude-opus-4-8 * feat(byok): memory model picker uses live searchable model list The Memory model picker was a plain <select> hard-wired to the 8 static SUGGESTED_MODELS. Swap it to the same SearchableModelSelect the chat picker above uses, fed by the parent's merged fetched+suggested model options (apiModelOptions). For AIHubMix that means the full live 342-model catalogue shows as a searchable dropdown (keyless), matching the chat picker; other protocols get their live /v1/models list once the chat key is set. 'Same as chat' leads the list and 'Custom…' is pinned last. * feat(byok): BYOK chat image-model picker lists live AIHubMix catalog The Settings BYOK 'image generation model' dropdown (byokImageModel, used by the chat-injected generate_image tool) listed only the 2 static aihubmix seeds. Feed it the live image catalogue (useAIHubMixImageModels) for aihubmix so it shows all ~53 models; SenseAudio keeps its static registry list. Relax the daemon's isAIHubMixImageModel to accept any aihubmix-* id (they all render via the OpenAI-compatible endpoint with prefix-strip), so a dynamically-chosen default passes validation instead of being dropped back to the seed default. * refactor(byok): share one hook for BYOK image-model pickers; wire composer The chat composer's inline 'image generation model' picker still listed only the static aihubmix seeds. Rather than re-derive the merge+filter a third time, extract useByokImageModelOptions(provider) into the shared aihubmix-image-models module (live AIHubMix catalogue for aihubmix, static registry otherwise) and use it from both the Settings image picker and the composer picker. One cached fetch feeds all sites. * feat(byok): route AIHubMix chat by model name to native protocol wire Per the AIHubMix integration guide §4.3, route the BYOK chat by model name to each family's native endpoint on the AIHubMix origin instead of forcing every model through /v1/chat/completions: claude* → Anthropic /v1/messages (x-api-key + anthropic-version) gemini*/imagen* → Gemini :streamGenerateContent (x-goog-api-key) everything else → OpenAI /v1/chat/completions (keeps the tool loop) All three carry the APP-Code attribution header. The claude/gemini routes run base chat only (no in-chat generate_image yet) — each protocol has a pluggable tool slot for a later Anthropic tool_use / Gemini functionCall adapter. The OpenAI family keeps in-chat image/speech generation. Reuse: extracted runAnthropicChatStream / runGeminiChatStream (+ payload builders) from the existing /api/proxy/{anthropic,google}/stream handlers; those routes now delegate to the same functions (behaviour preserved, 79 proxy tests green). New helpers classifyAIHubMixModel / aihubmixOriginFromBase / aihubmixAppCodeHeader in aihubmix.ts. No SDK added. Default AIHubMix chat model is now gpt-5.5 (an OpenAI-family model) so the default keeps in-chat generate_image working after routing. Tests: claude→/v1/messages and gemini→/gemini/.../streamGenerateContent routing assertions in proxy-routes.test.ts. * feat(byok): AIHubMix in-chat image/video/audio model selection + media-adapters Bring the BYOK chat media surface to parity across image, video and audio: - Composer + Settings dropdowns for image/video/speech models and TTS voice, threaded as byokVideoModel / byokSpeechModel / byokSpeechVoice. The user's explicit pick is authoritative over the LLM's tool `model` arg (regression the LLM kept overriding the composer choice). "Default" resolves to the first live-catalogue model (honest label) with the daemon const as final fallback. - New apps/daemon/src/media-adapters: injection-style capability registry + family-aware video request builder (seedance content[] array vs generic input_reference), capability shape aligned with OpenRouter /videos/models. The chat video executor now builds requests through it. - Per-family request shaping: * video: duration snapped to each family's allowed set (veo 4/6/8, sora 4/8/12, wan 5/10); i2v reference resolved to a data URL. * image: gemini/nano-banana models routed to gemini-native generateContent (the /v1/images/generations shape 400s for them); others stay OpenAI-shaped. * speech: gemini TTS via generateContent with PCM->WAV wrapping and Gemini voice fallback; others use /v1/audio/speech (mp3). * download: same-origin AIHubMix asset URLs re-send Bearer + APP-Code. - Clear, actionable errors for unsupported i2v / "params dropped" models, plus per-call submit logging (model + endpoint) for multi-model verification. - i18n keys across 19 locales for the new pickers and the neutral default label. Tests: byok-tools + media-adapters specs (85 passing); guard + daemon/web typecheck green. * feat(byok): media tool loops on claude/gemini chat + wan i2v wire + seedance resolution fix Make AIHubMix in-chat media generation work regardless of which chat model is selected, and fix two image-to-video wire-shape bugs surfaced by live testing. chat-routes: the aihubmix routeByModel divert sent claude -> Anthropic /v1/messages and gemini -> Gemini generateContent BEFORE the OpenAI tool loop, so those chat models had no generate_image/video/speech tools ("没有音频工具"). Add per-protocol tool loops inside registerByokToolChatProxy (reusing the shared executeOneTool): runAnthropicToolTurn/Chat parse tool_use content blocks + input_json_delta; runGeminiToolTurn/Chat parse functionCall parts. The shared single-pass runAnthropicChatStream/runGeminiChatStream and the direct /api/proxy/{anthropic,google}/stream routes are untouched. Factor the per-kind tool-result hint into buildToolResultContent (shared by all three protocols) and add openaiToolsToAnthropic / openaiToolsToGemini schema converters (Gemini schema sanitized to the OpenAPI subset). Gemini 3.x attaches a thoughtSignature to each functionCall part that must be echoed back verbatim or the follow-up 400s, so replay the raw functionCall part instead of reconstructing {name,args}. media-adapters: seedance accepts resolution only as a token (480p/720p/1080p), never a WxH pixel string — passing the aspect-derived 1280x720 made it 400 ("resolution ... not valid ... in i2v"). Add snapResolutionToken to normalise. Add a third request family `wan` for wan*/happyhorse*: verified against a working happyhorse-1.0-i2v call, these are DashScope/wanx-backed and use { input:{ prompt, media:[{type:first_frame,url}] }, parameters:{ resolution, duration, prompt_extend, watermark } }, NOT the flat input_reference shape we were sending. Update the i2v failure-advice message accordingly (the remaining risk is wanx needing a public image URL while we send an inline data URL; seedance accepts the inline image). Tests: anthropic+gemini in-chat tool loops (incl. thoughtSignature round-trip), snapResolutionToken, seedance token resolution, and the wan input.media shape. * feat(byok): add veo video family + default to seedance-2.0-fast veo* models go through the gateway's OpenAI→Gemini predictLongRunning shim, which wants `seconds` as a NUMBER and only honours `size` (no aspect_ratio) — distinct enough from the generic flat shape to warrant its own `veo` family in the media-adapters builder. Verified against a working veo-3.1-lite call. Also default the BYOK chat video model to doubao-seedance-2-0-fast-260128 (the broadly-available fast i2v/t2v path) and extend the media-adapters tests to cover the veo family routing + duration snapping (4/6/8). * feat(byok): hide account-source model labels for AIHubMix AIHubMix's live model fetch returns its full account catalogue, so every picker option carried an identical "from your account" badge that added noise without distinguishing anything. Introduce a reusable ACCOUNT_MODEL_SOURCE_LABEL_HIDDEN set + hidesAccountModelSourceLabel() predicate: for listed protocols the per-option source label is dropped and the loaded-models hint shows a plain count (new settings.modelsLoadedCount key) instead of the account wording. Other providers are unaffected; add a protocol to the set to opt it in. * fix(byok): close AIHubMix catalogue SSRF + share gemini image branch Two review fixes on the AIHubMix BYOK provider: - SSRF: the public `/api/media/providers/aihubmix/models` GET took a caller-controlled `baseUrl` and fetched it server-side, so `?baseUrl=http://169.254.169.254/` could reach a private/metadata host. The catalogue lives at one fixed origin, so the route now ignores any `baseUrl` query and hard-codes the official AIHubMix origin — no attacker-chooseable fetch target remains. A focused route test asserts a malicious `baseUrl` is ignored and only aihubmix.com is contacted. - Gemini image parity: the media renderer always sent AIHubMix image models to the OpenAI `/images/generations` endpoint, which 400s for gemini/imagen-family models. Extract the chat tool's Gemini-native branch into a shared `aihubmixGeminiImageBytes` helper and route those models through it from both the chat tool and the Home/New Project/CLI media path, so the catalogue the picker exposes is fully renderable. Added a media-path test that asserts a gemini-family id hits the generateContent wire. * feat(byok): hide redundant gateway preset for single-origin AIHubMix The "gateway preset" dropdown only ever offered AIHubMix itself (plus the generic custom-endpoint option) because AIHubMix is a fixed-origin gateway with exactly one preset — redundant UI. Gate it behind a reusable GATEWAY_PRESET_HIDDEN set + hidesGatewayPreset() predicate (mirroring the existing ACCOUNT_MODEL_SOURCE_LABEL_HIDDEN pattern) so another single-origin gateway can opt in by adding its protocol, rather than hardcoding a check. The Base URL field still shows the resolved origin; multi-preset and custom-endpoint-reliant protocols (azure/google/senseaudio) are unaffected. * feat(byok): hide Base URL field too for fixed-origin AIHubMix gateway AIHubMix resolves its Base URL automatically, so the editable field was as redundant as the gateway preset. Generalize the just-added GATEWAY_PRESET_HIDDEN into one semantic concept — FIXED_ORIGIN_GATEWAYS + isFixedOriginGateway() — and drive BOTH the preset dropdown and the Base URL field off that single predicate. Adding a fixed-origin gateway protocol now hides both surfaces in one place; the user only supplies an API key. azure/google/senseaudio are unaffected. * fix(byok): close AIHubMix asset-download SSRF redirect bypass The AIHubMix asset downloads validated the literal URL via assertExternalAssetUrl but then fetched with the default redirect:'follow', so a validated public URL could 302 the daemon into 169.254.169.254 / RFC1918 space (and leak Bearer/APP-Code to the hop target). Add a shared assertAndFetchExternalAsset(url, init) helper that validates and pins redirect:'error', and route every external asset download through it: chat video/image (byok-tools.ts) and media image/video + SenseAudio image (media.ts). Also add the missing isLocalSameOrigin guard to the AIHubMix catalogue route so it matches its sibling media routes. Regression tests lock the invariant: helper-level (blocks literal metadata host, forces redirect:'error') plus chat video/image executors asserting the download fetch carries redirect:'error', and a cross-origin rejection test on the catalogue route. Also point the AIHubMix API-key console link at the referral URL. * feat(byok): carry New Project media-model pick into BYOK conversation The model chosen in New Project → Media was stored on project.metadata but dropped for BYOK providers (AIHubMix): media is produced by the generate_* chat tools whose default comes from the per-request byok*Model field, which ProjectView only seeded from Settings. Seed those overrides from the project's creation-time pick instead, gated to the active BYOK provider so a cross-provider model (e.g. a SenseAudio run whose image project defaulted to gpt-image-2) can't clobber the user's Settings default. mediaModelProviderId resolves AIHubMix's `aihubmix-` live ids by prefix so the seed survives before the async catalogue loads. * refactor(byok): remove in-chat media model selector pending unified picker Drop the in-conversation BYOK image/video/speech model dropdowns from the chat composer. Media model selection now comes from the project's creation-time pick (seeded into the byok*Model overrides), and a unified selection surface will replace this later. Also removes the now-dead option hooks/imports so the composer no longer fires AIHubMix catalogue fetches it doesn't render. The byok* props/handlers are retained as plumbing for the future unified picker. * fix(byok): resolve AIHubMix fixed-origin base URL so model lists load Hiding the Base URL field for the fixed-origin AIHubMix gateway left config.baseUrl empty, which blocked the live model-list fetch (it gates on a valid base URL) so the Settings dropdown and the top-bar switcher showed only the static suggested models. Centralise the fixed-origin concept in state/apiProtocols (FIXED_ORIGIN_GATEWAYS / isFixedOriginGateway / resolveFixedOriginBaseUrl) and resolve the canonical origin in loadConfig so every consumer sees it — including configs already persisted with an empty baseUrl. SettingsDialog now imports the shared helpers instead of redefining them. |
||
|
|
1a582bf8c2 |
feat(web): add next-step actions after artifact generation (#3439)
* feat(web): add next-step actions after artifact generation Surface a small affordance under the last assistant message once it has produced a previewable HTML artifact, so users have an obvious next move instead of stalling or leaving after the first version. It offers recommended iteration directions (make it more premium, strengthen brand style, tighten copy, generate a second version) as multi-select chips that prefill the composer (not auto-send, so the user reviews before sending), plus a peer "Share" action that reuses the preview workspace's existing share/export menu. There is deliberately no vague "continue editing" / "optimize visuals" catch-all — free-form follow-ups belong in the composer and the visual directions are already covered by the concrete chips. Adds a next_step analytics event (exposure + share/chip clicks with chip_id) to drive the second-turn and share-rate acceptance metrics. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): point next-step Share at the deploy/share menu, not download The toolbar's "Share" button opens the deploy menu (share-link + publish online), while the download menu is export-to-disk. Sharing requires a published link, and the publish step lives in the deploy menu, so the next-step Share action should open that surface — not the export one. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): preserve next-step chip button semantics * fix(web): preserve browser tabs on artifact share --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
fd8159216a |
feat(design-systems): import shadcn registry items as design systems (#3525)
* feat(design-systems): import shadcn registry items as design systems Add a third design-system import source alongside local folders and public GitHub repos: a shadcn registry item, referenced with the shadcn CLI shorthand `<owner>/<repo>/<item>` (resolved against the repo's root registry.json) or a direct https URL to a registry-item document. The item's cssVars theme is materialized into a temporary project and run through the existing local importer, so a shadcn theme yields the same DESIGN.md + tokens.css + manifest.json as every other import. Classic shadcn bare-HSL cssVars (e.g. "222 47% 11%") are wrapped into hsl(...) so brand colors survive instead of falling back to neutrals. Exposed on both surfaces, per the capability dual-track rule: - POST /api/design-systems/import/shadcn, with ImportShadcn* DTOs in packages/contracts and a "shadcn" source in the design-system manifest schema. - `od design-systems import-shadcn <reference> [--name --import-mode --craft --json]`. - Settings -> Design systems: a new "shadcn" import source (placeholder shadcn/ui/theme-zinc), with i18n keys added across all locales. http:// references are accepted only for loopback hosts so a self-hosted local registry can be imported and tests stay deterministic. * fix(design-systems): harden shadcn importer per review - Fail fast in writeShadcnFiles: a declared registry file that 404s, exceeds the size cap, exceeds the file-count cap, or has no safe destination now throws a path-named LocalDesignSystemImportError instead of silently producing a partially-materialized design system. - Resolve the shadcn `include` registry layout recursively; an item's relative file paths now resolve against the registry.json that declared it (so `owner/repo/item` works for registries that use include). - Keep the fetch abort timer armed across response-body consumption so a server that stalls the body cannot hang the import. - Block SSRF: redirect:'error' on the default fetch, plus per-fetch host re-validation (https, or http only for loopback) on every URL we contact - entry point, raw GitHub URLs, include targets, and raw files. Adds regression tests for include resolution, a 404 declared file, an oversized inline file, and a non-loopback http include target. * fix(design-systems): close two shadcn importer review gaps - parseShadcnReference: wrap decodeURIComponent of the URL fragment so malformed percent-encoding (e.g. "#%E0%A4%A") surfaces as a 400 BAD_REQUEST instead of an uncaught 500 from the import route. - writeShadcnFiles: two declared files that collapse to the same sanitized destination (e.g. "@/button.tsx" + "button.tsx") now fail as a BAD_REQUEST naming both sources, instead of silently dropping the later file - the same partial-materialization failure mode this PR avoids. Adds parser + importer regression tests for both. * fix(design-systems): harden shadcn importer (SSRF, resource bounds, fidelity) A deeper hardening pass from an adversarial self-review of the importer. SSRF egress policy (assertFetchableUrl): loopback is allowed over http or https (self-hosted/local registries); private (10/8, 172.16/12, 192.168/16, 100.64/10), link-local (169.254/16, fe80::/10), ULA (fc00::/7), unspecified (0.0.0.0, ::), and IPv4-mapped/integer-encoded internal addresses are refused for BOTH schemes; any other host must be https. Enforced at parse time and on every fetch - entry point, include targets, and raw files - so a direct https://169.254.169.254, or an include/redirect pivot to an internal address, is blocked rather than only the http downgrade. Resource bounds: bodies are read with a running byte cap (no unbounded buffering) plus a Content-Length pre-check; the whole import shares one wall-clock deadline and a max-total-requests budget; include breadth and file count are capped. A stalled/huge body or a large include fan-out can no longer hang or OOM the import. Fidelity / fail-fast: a files[] entry with no usable destination is now a 400 (was silently skipped); registry files are materialized under a subdirectory so a crafted target cannot overwrite the importer's theme.css/package.json/ README.md; cssVars keys/values are validated so a hostile entry cannot inject extra declarations or be silently dropped; slashed git refs are encoded per segment; direct-URL items resolve path-only files relative to the item URL; fetch timeouts surface as a clean BAD_REQUEST. Adds tests for each: https-internal SSRF (entry + include), a no-destination file, a reserved-file overwrite attempt, a slashed ref, a direct-URL path-only file, cssVars injection, and a streaming over-cap body. |
||
|
|
dbc74a397c |
feat(agents): CLI detection self-rescue with actionable diagnostics (#3560)
* feat(agents): surface actionable CLI diagnostics Detect unavailable CLI agents with structured diagnostics, stream agent discovery results to the settings UI, and render actionable fix controls for common setup failures. Also harden the streamed agents client so server error events and dropped streams surface as failures instead of replacing state with a truncated agent list. * fix(agents): gate streamed rescan loading Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(agents): distinguish non-executable CLI diagnostics Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(agents): defer OAuth diagnostics without producer Generated-By: looper 0.9.2 (runner=fixer, agent=codex) --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> Co-authored-by: Looper <looper@noreply.github.com> Co-authored-by: Looper Fixer <looper-fixer@noreply.github.com> Co-authored-by: looper-fixer <looper-fixer@users.noreply.github.com> |
||
|
|
f3ec4968af |
feat(web): revamp chat UI styling + live streaming code card (#3382)
* feat(web): revamp chat UI styling + live streaming code card Restyle the chat pane (chat/tokens/code/tools/routines/composio CSS) and add a live code-component card that streams Write/Edit tool input and <artifact type="text/html"> bodies character-by-character via a new ephemeral tool_input_delta SSE event, flipping to the completed FileWriteCard/FileEditCard view on close. Includes shiki highlighting, markdown/artifact strip handling, tool-status cleanup, and i18n keys. * chore(nix): refresh pnpm deps hash for merged lock --------- Co-authored-by: qiongyu1999 <2694684348@qq.com> |
||
|
|
90aab29695 |
feat(analytics): attribute AMR entry conversions (#3579)
* feat(analytics): attribute AMR entry conversions
* test(web): cover AMR attribution entry points
* fix(daemon): gate AMR entry mirror on analytics consent
The /api/integrations/vela/analytics-entry route forwarded every AMR
entry click to the external AMR ingest endpoint regardless of consent:
mirrorAmrEntryAnalytics fired whenever globalThis.fetch existed, so an
opted-out user's click was still shipped to a third-party service.
Gate the mirror the same way the analytics-capture path does:
- null readAnalyticsContext (no x-od-analytics-* headers => Privacy
metrics off) short-circuits to { mirrored: false }.
- defense-in-depth re-read of telemetry.metrics from app-config drops
the mirror even if a stale header leak slips through after opt-out.
Adds two daemon route red specs asserting that a no-headers request and
a metrics-off request never hit the capture server.
---------
Co-authored-by: lefarcen <935902669@qq.com>
|
||
|
|
c6722d2671 |
feat: Lexical composer, interactive terminals, comment attachments & browser reference board (#3516)
* feat(daemon): add interactive terminal support with node-pty - Introduced a new terminal service to manage interactive terminal sessions. - Added routes for creating, streaming, and managing terminal sessions in the daemon. - Integrated terminal functionality into the CLI, allowing users to open interactive shells. - Updated project routes to support conversation seeding for side chats. - Enhanced the web application to include terminal tab functionality and UI components. This feature enables users to interact with a terminal directly within the application, enhancing the overall user experience and providing a more integrated development environment. * feat(web): implement embedded browser module in Design Files workspace - Added a `+` icon to the Design Files tab for opening a new Browser module. - The Browser module supports navigation features including back, forward, refresh, and address input. - Integrated a curated list of design reference URLs for user convenience. - Implemented browser data clearing functionality via IPC. - Enhanced desktop runtime to support embedded browser with appropriate security measures. - Added tests for browser functionality and URL handling. This commit establishes a new workspace for browsing and referencing design resources directly within the application, improving user experience and accessibility to design tools. * refactor(web): enhance add tab menu functionality in FileWorkspace - Updated the add tab button to toggle the visibility of the menu with improved accessibility attributes. - Refactored the add menu to be portaled to the document body for better positioning and visibility. - Adjusted CSS styles for the add menu to use fixed positioning and increased z-index for proper layering. - Minor CSS adjustments in entry layout for consistent padding. These changes improve the user experience when adding new modules in the FileWorkspace, ensuring the add menu is more accessible and visually consistent. * feat(daemon): introduce session mode for conversations - Added a new `session_mode` column to the `conversations` table with a default value of 'design'. - Implemented logic to handle `session_mode` in conversation creation, updates, and retrieval. - Enhanced the API to support `session_mode` in conversation requests, allowing for 'chat' or 'design' modes. - Updated the web application to include a session mode toggle, enabling users to switch between chat and design modes seamlessly. - Adjusted system prompts to reflect the current session mode, providing context-aware responses. This feature enhances the user experience by allowing for more flexible conversation management, catering to different interaction styles. * feat(web): enhance navigation and settings functionality in DesignBrowserPanel and EntryShell - Introduced a navigation stack in DesignBrowserPanel to manage back and forward navigation states. - Updated the browser navigation logic to handle URL history and improve user experience. - Added a settings menu in EntryShell for quick access to language and appearance options. - Implemented CSS styles for the new settings menu, ensuring a consistent and user-friendly interface. - Enhanced tests for navigation functionality and settings menu interactions. These changes improve the overall usability of the application by streamlining navigation and providing easy access to settings. * feat(daemon): enhance conversation session mode handling - Added a new `mode` flag to CLI commands for project and conversation creation, allowing users to specify 'design' or 'chat' modes. - Implemented `normalizeChatSessionModeFlag` function to validate and normalize session mode inputs. - Updated project routes to handle session mode during conversation creation and updates. - Enhanced web components to support session mode changes, including new props and handlers for managing session modes in conversations. - Adjusted UI elements to reflect the current session mode, improving user experience and interaction flexibility. This update provides a more robust framework for managing conversation modes, catering to diverse user needs and enhancing overall functionality. * feat(web): enhance HandoffButton and DesignBrowserPanel with improved functionality and styling - Updated HandoffButton to support framework-specific CLI prompts and improved local project path handling. - Enhanced DesignBrowserPanel to manage browser history with favicon support and improved address display. - Introduced new utility functions for formatting addresses and extracting hostnames. - Refactored CSS styles for better layout and responsiveness across components. - Added tests for new functionalities in HandoffButton and DesignBrowserPanel, ensuring robust behavior. These changes improve user experience by streamlining the handoff process and enhancing the design browsing capabilities within the application. * feat(web): enhance HandoffButton and ProjectView with improved instructions handling and UI updates - Updated HandoffButton to include a tabbed interface for switching between editor and CLI options, enhancing user experience. - Added support for opening the AMR website directly from the HandoffButton. - Refactored ProjectView to implement a modal for custom instructions, allowing users to edit and review instructions more intuitively. - Improved CSS styles for project instructions modal and handoff menu, ensuring better layout and responsiveness. - Added keyboard accessibility for closing the instructions modal with the Escape key. These changes streamline the handoff process and improve the usability of custom instructions within the application. * feat(daemon): add social share functionality and project folder management - Introduced new `share` command in CLI for building localized social-share targets for Open Design projects. - Implemented `printShareUsage` and `runShare` functions to handle share requests and display usage instructions. - Added API routes for social sharing, allowing users to create shareable links for projects. - Enhanced project routes with new endpoints for listing and creating project folders, improving project organization. - Updated relevant files and tests to support new functionalities, ensuring robust behavior. These changes enhance user experience by facilitating social sharing and better project management within the application. * feat(web): enhance DesignBrowserPanel and DesignFilesPanel with improved address formatting and drag-and-drop functionality - Refactored `formatAddressDisplay` to utilize a new `formatAddressDisplayParts` function, separating URL and title handling for better clarity. - Updated `DesignFilesPanel` to improve drag-and-drop interactions, including enhanced directory navigation and file management features. - Adjusted CSS styles for better visual consistency and responsiveness across components. - Added tests for new functionalities in `DesignBrowserPanel` and `DesignFilesPanel`, ensuring robust behavior. These changes improve user experience by streamlining address formatting and enhancing file management capabilities within the application. * feat(web): enhance DesignBrowserPanel and DesignFilesPanel with new reference categories and improved folder creation - Added new reference categories in DesignBrowserPanel, including 'Inspiration', 'Real Interfaces', 'Color', 'Typography', and more, each with curated design resources. - Improved folder creation logic in DesignFilesPanel to suggest names based on existing folders, enhancing user experience. - Updated CSS styles for better layout and responsiveness across components, particularly in control rows and search functionalities. - Added tests for new reference categories and folder creation features, ensuring robust functionality. These changes enrich the design resource catalog and streamline folder management, improving overall usability within the application. * feat(web): implement reference icon functionality and enhance social share features - Replaced favicon URL generation with a new `referenceIconUrl` function to provide reliable icons for curated design sites, improving visual consistency in the DesignBrowserPanel. - Updated the FileViewer component to enhance social share functionality, including clearer messaging for protected deployments and improved UI for sharing links. - Added CSS styles to support new visual states for social share components, ensuring better user experience. - Expanded tests for the new `referenceIconUrl` function and social share interactions, ensuring robust functionality. These changes enhance the design resource presentation and improve the sharing experience within the application. * feat(web): enhance DesignFilesPanel and FileViewer with improved folder management and social share functionality - Added optimistic folder path management in DesignFilesPanel to improve user experience during folder creation. - Updated social share logic in FileViewer to handle protected deployments more effectively, ensuring clearer messaging and UI updates. - Refactored CSS styles for better layout and responsiveness in both components, enhancing overall usability. - Expanded tests for new folder management features and social share interactions, ensuring robust functionality. These changes streamline folder management and enhance the social sharing experience within the application. * feat(daemon): enhance project tab management with new state handling - Updated database schema to include a new `state_json` column in the `tabs_state` table for improved project tab state management. - Implemented functions to normalize and parse project tab states, including handling browser workspace tabs. - Modified `listTabs` and `setTabs` functions to utilize the new state management features, allowing for better tracking of active tabs and saved states. - Refactored related types in the contracts and web applications to support the new tab state structure. These changes improve the functionality and user experience of managing project tabs within the application. * feat(web): enhance project tab management and browser tab functionality - Updated project routes and server logic to include `browserTabs` in the request body, improving tab state management. - Implemented validation for `browserTabs` to ensure it is an array, enhancing error handling. - Refactored `setTabs` function to accommodate the new structure for managing tabs, including browser-specific tabs. - Added tests for browser tab state persistence and management in the new `project-tabs-state.test.ts` file, ensuring robust functionality. These changes improve the user experience by providing better management and persistence of project and browser tabs within the application. * feat(web): enhance ProjectView with improved chat send management and UI updates - Added support for queue-only chat sends, allowing for better handling of messages during busy conversations. - Refactored chat send functions to improve state management and persistence of queued messages. - Updated CSS styles in the DesignFilesPanel for better layout and responsiveness, including search control enhancements. - Expanded tests for new chat send functionalities and search interactions, ensuring robust behavior. These changes improve the user experience by streamlining chat interactions and enhancing the overall design file management interface. * feat(web): improve project tab state management and caching - Enhanced the `loadTabs` function to utilize cached tab states, improving performance and user experience during data fetching. - Implemented `normalizeTabsState` and caching functions to manage tab states effectively, including validation and error handling. - Updated `writeCachedTabs` to ensure the latest state is stored in local storage, facilitating better persistence of tab information. - Modified `listTabs` in the daemon to include `updatedAt` in the state retrieval, allowing for more accurate tracking of tab updates. These changes streamline tab management and enhance the overall responsiveness of the application. * feat(web): integrate Lexical for enhanced text composition and mention handling - Added Lexical as a dependency to improve text composition capabilities within the chat interface. - Implemented mention functionality with the creation of `MentionNode` and related serialization/deserialization logic for inline mentions. - Enhanced `ChatComposer` and `ChatPane` components to support queue-only message sending and improved state management for queued messages. - Updated `DesignBrowserPanel` and `PreviewDrawOverlay` to incorporate new features for capturing and annotating browser snapshots. - Refactored various components to streamline interactions and improve user experience during chat and design tasks. These changes significantly enhance the text editing experience and provide better management of chat interactions, improving overall usability in the application. * feat(workspace): integrate terminal viewer and enhance chat functionality - Added a new terminal viewer component with customizable themes and improved styling for better user experience. - Integrated terminal functionality into the workspace, allowing users to interact with a terminal directly within the application. - Updated chat components to support active conversation states, enabling seamless message handling and interaction. - Refactored chat-related props and state management to enhance performance and maintainability. - Removed deprecated file tree explorer components to streamline the workspace interface. This update enhances the overall functionality of the workspace, providing users with a more integrated and responsive environment for both terminal and chat interactions. * feat(workspace): enhance chat and session mode functionality - Introduced a new `+` launcher in the workspace for easy access to project files and new tabs, including Side Chat and Terminal. - Added Side Chat functionality that allows users to create context-aware conversations based on existing chats. - Implemented a new terminal tab type for interactive terminal sessions using `node-pty`, enabling users to run shell commands directly within the workspace. - Enhanced chat functionality with a new session mode toggle, allowing users to switch between 'design' and 'chat' modes seamlessly. - Added a feature to copy chat responses as markdown to the clipboard, improving usability and sharing capabilities. - Updated various components and styles to support these new features, ensuring a cohesive user experience. This update significantly improves the workspace's interactivity and usability, providing users with more tools for collaboration and development. * feat(workspace): enhance session mode toggle and chat functionality - Improved the SessionModeToggle component to include localized guidance cards that provide context-aware descriptions for each mode. - Updated the UI to display a popover with mode descriptions when hovering over options, enhancing user understanding of the available modes. - Added a new copy button in the AssistantMessage footer to allow users to copy responses as raw Markdown, improving usability for external documentation. - Enhanced localization support by updating i18n keys and translations for various languages, ensuring consistent user experience across different locales. - Refactored styles for the session mode toggle and associated components to improve layout and responsiveness. This update significantly enhances the user experience by providing clearer guidance and improved functionality in chat interactions. * feat(styles): enhance session mode toggle styling for improved visibility - Added new CSS rules to ensure the session mode toggle popover and hover cards are displayed correctly with increased z-index and visibility. - Updated styles for various chat components to maintain consistent positioning and overflow behavior when session mode elements are present. - Improved overall layout responsiveness for chat interactions, enhancing user experience during mode transitions. This update refines the visual presentation of session mode toggles, ensuring they are more accessible and user-friendly. * feat(web): finish Lexical composer input with atomic mention pills * fix(web): make browser reference board scroll with a pinned toolbar The browser tab's default Reference Board (.db-start) is wrapped by PreviewDrawOverlay's position:absolute container, which is not a flex parent — so .db-start's flex:1 1 auto never bounded its height and the board grew to its content height instead of scrolling (and the sticky .db-reference-toolbar could not pin). Fill the overlay with height:100% like the .db-webview/.db-fallback siblings already do, restoring scroll and the sticky toolbar. * test(daemon): expect updatedAt in persisted tab state round-trip listTabs() returns the tabs_state row's updatedAt timestamp on the saved-state path, but these two toEqual assertions predated that field and failed strictly. Match the real shape with expect.any(Number). * feat(chat): enhance ChatComposer with session mode management and UI improvements - Introduced a new `sessionMode` prop to the ChatComposer, allowing users to switch between different session modes (e.g., 'design'). - Added a SessionModeToggle component for improved user interaction and visibility of session options. - Updated the ToolsTab type to reflect the removal of the pet option, streamlining the tools available in the composer. - Refactored styles to enhance the visibility and positioning of session mode elements, ensuring a better user experience during mode transitions. - Improved the handling of draft state and user interactions within the composer, enhancing overall functionality. These changes significantly improve the ChatComposer's usability and flexibility, providing users with clearer options and a more responsive interface. * feat(chat): implement caret floating layer for mention and slash popovers - Introduced a new `CaretFloatingLayer` component to manage the positioning of mention and slash command popovers relative to the caret. - Enhanced `ChatComposer` to utilize the caret rectangle for accurate popover placement, improving user experience during text input. - Updated `LexicalComposerInput` to pass caret position data to the trigger handling logic, allowing for dynamic popover adjustments. - Refactored styles for popovers to ensure consistent appearance and behavior, including improved animations and responsiveness. - Added accessibility features to mention and slash popovers, enhancing usability for keyboard navigation. These changes significantly improve the interaction model for mentions and commands within the chat interface, providing a more intuitive and responsive user experience. * feat(chat): update mention tab order and improve search functionality - Reordered the mention tab sections in the ChatComposer to prioritize 'Design files' over other categories, enhancing user experience during mentions. - Updated the search prompt to reflect the new tab order, ensuring clarity in search functionality. - Enhanced the mention selection logic to accommodate the new tab structure, allowing for a more intuitive navigation experience. - Added tests to verify the correct display and functionality of the updated mention tabs and search behavior. These changes significantly improve the usability of the mention feature within the chat interface, making it easier for users to find and select relevant items. * feat(chat): enhance context management in ChatComposer and HomeHero - Added functionality to manage MCP servers and connectors within the ChatComposer, allowing users to remove these contexts seamlessly. - Updated the HomeHero component to support the selection and removal of MCP servers and connectors, improving context handling in user interactions. - Enhanced the search prompt to include files, ensuring users can search across all relevant categories. - Refactored related components and styles for better integration and user experience. - Added tests to verify the correct functionality of the new context management features. These changes significantly improve the usability of context features in the chat interface, making it easier for users to manage their interactions effectively. * feat(chat): enhance message handling with session mode and plugin snapshot support - Added new columns to the database schema for `session_mode` and `applied_plugin_snapshot_json` to support enhanced message context. - Updated the `upsertMessage` and `listMessages` functions to handle the new fields, ensuring messages can store and retrieve session mode and plugin snapshot data. - Enhanced the `ChatComposer` to manage and send the applied plugin snapshot as part of the message context, improving user interaction with plugins. - Introduced a new `MessageSessionModeChip` component to visually represent the session mode in the chat interface. - Updated styles for better presentation of session mode and plugin context within messages, enhancing user experience. These changes significantly improve the context management capabilities in the chat interface, allowing for richer interactions and better tracking of session-specific data. * feat(chat): add annotation handling and UI improvements in ChatComposer and PreviewDrawOverlay - Implemented functionality to stage draw annotations into the composer input without sending, enhancing user interaction. - Added a new button in the PreviewDrawOverlay to allow users to append notes to the input, improving workflow flexibility. - Updated the ChatComposer tests to verify the correct staging of annotations and their integration into the input. - Enhanced internationalization support by adding new translation keys for annotation actions across multiple languages. These changes significantly improve the user experience by providing more intuitive annotation handling and better integration within the chat interface. * feat(capture): implement page capture functionality and enhance folder management dialogs - Added new capture functionality to allow users to take snapshots of the current page, improving user interaction with visual content. - Introduced in-app dialogs for folder creation and moving files, replacing the unsupported window.prompt in the Electron desktop host, enhancing usability across platforms. - Updated the DesignFilesPanel to support these new dialogs, ensuring a seamless experience for managing project files. - Enhanced internationalization support by adding new translation keys for folder management actions across multiple languages. These changes significantly improve the user experience by providing intuitive capture options and streamlined file management within the application. * feat(projects): implement folder deletion and enhance project file management - Added a new API endpoint to delete project folders, improving the file management capabilities within the application. - Introduced utility functions for ensuring project subdirectories and safely deleting folders, enhancing the robustness of folder operations. - Updated the DesignFilesPanel and FileWorkspace components to support folder deletion actions, providing users with a more intuitive interface for managing project files. - Enhanced internationalization support by adding new translation keys for folder management actions. These changes significantly improve the user experience by streamlining folder management and providing clearer options for users to organize their projects effectively. * feat(composer): enhance BoardComposerPopover with image attachment functionality - Added support for attaching images to comments, allowing users to upload and preview images directly within the composer. - Implemented new handlers for image input changes and clipboard pasting, improving the user experience for image uploads. - Updated the component's props to include image-related callbacks and state management for attached images. - Enhanced styles for image thumbnails and removal buttons, ensuring a cohesive design with the existing comment popover interface. These changes significantly improve the functionality of the BoardComposerPopover, providing users with a more interactive and visually rich commenting experience. * feat(file-viewer): enhance comment attachment functionality with image support - Updated the `onSendBoardCommentAttachments` prop to accept an additional `images` parameter, allowing for image attachments alongside comments. - Introduced state management for handling attached images, including functions to add and remove images, and to generate previews. - Implemented a modal for previewing attached images, improving user interaction when managing comment attachments. - Updated the `FileWorkspace` component to reflect changes in the props, ensuring consistency across components. These enhancements significantly improve the commenting experience by enabling users to attach and preview images directly within the file viewer. * feat(home-hero, project-view, styles): enhance functionality and user experience - Updated the HomeHero component to prevent unnecessary state changes during programmatic updates, improving user interaction with prompts. - Enhanced the ProjectView component to support image attachments alongside comments, allowing for a more versatile commenting experience. - Implemented a new image upload process that queues tasks efficiently, ensuring smooth handling of comment attachments. - Added CSS to reserve scrollbar space in design files, preventing layout shifts when scrollbars appear, thus enhancing visual stability. These changes collectively improve the user experience by streamlining interactions and ensuring consistent UI behavior across components. * feat(preview-comments, chat): enhance comment functionality with attachment support - Added support for `attachments_json` in the `preview_comments` table, allowing users to attach files to comments. - Updated relevant functions to handle attachments, including `upsertPreviewComment` and `listPreviewComments`, ensuring attachments are properly managed and displayed. - Enhanced the `CommentSidePanel` to render attached files, providing users with a visual representation of their attachments. - Improved the `BoardComposerPopover` and `ChatPane` components to support image attachments, including drag-and-drop functionality for reordering queued sends. These changes significantly enhance the commenting experience by enabling users to attach and manage files directly within the chat interface, improving overall usability and interaction. * refactor(comment-attachments): rename attachment normalization functions for clarity - Updated the `normalizeCommentAttachments` function to `normalizePreviewCommentAttachments` for better context in handling preview comment attachments. - Adjusted the `upsertPreviewComment` and `normalizePreviewComment` functions to utilize the renamed attachment normalization function. - Added tests to ensure that image attachments are correctly saved and retrieved, addressing a regression issue with attachment persistence. These changes enhance code clarity and maintainability while ensuring the functionality for handling comment attachments remains robust. * feat(comment-attachments): enhance comment submission with image and note validation - Updated the `upsertPreviewComment` function to require either a text note or at least one image attachment for comment submission, improving validation logic. - Modified the `BoardComposerPopover` to allow saving comments with only images, enhancing user experience by simplifying the commenting process. - Adjusted the `FileViewer` to support saving comments with image-only notes, ensuring consistency across components. - Improved styles in the chat and home hero components for better visual representation of attachments and comments. These changes collectively enhance the commenting functionality, providing users with more flexibility in how they submit comments while ensuring robust validation. * feat(project-view): implement auto-start for queued chat sends - Replaced the `startingQueuedChatSendIdRef` with a state variable `queuedAutoStartBlocked` to manage the auto-start behavior of queued chat sends. - Updated the `useEffect` to ensure that queued sends are processed one at a time after the active conversation completes, enhancing the chat experience. - Added a test to verify that queued sends auto-start correctly after the active run finishes, ensuring reliable functionality. These changes improve the handling of queued chat messages, providing a smoother user experience during conversations. * feat(project-view): refine auto-start logic for queued chat sends - Replaced the state variable `queuedAutoStartBlocked` with a reference `startingQueuedChatSendIdRef` to manage the auto-start behavior of queued chat sends more effectively. - Updated the `useEffect` to ensure that queued sends are processed sequentially, improving the handling of chat messages during active conversations. - Introduced a new state variable `queuedAutoStartTick` to track the auto-start process, enhancing the responsiveness of the chat interface. These changes improve the reliability and user experience of the chat functionality by ensuring queued messages are handled smoothly and efficiently. * feat(comment-attachments): improve attachment handling in preview comments - Updated the `upsertPreviewComment` function to merge existing and incoming attachments, ensuring that image attachments are preserved when updating comments without new files. - Introduced a new helper function, `mergePreviewCommentAttachments`, to handle the merging of attachments without duplicates, enhancing the robustness of attachment management. - Added tests to verify the correct merging of attachments and the preservation of existing attachments during comment updates, improving overall functionality and user experience. These changes enhance the commenting system by providing better management of image attachments, ensuring users can update comments seamlessly while retaining their attached images. * feat(BoardComposerPopover): enhance popover positioning and measurement - Updated the `popoverAnchorStyle` function to incorporate viewport scroll positions, ensuring the popover remains within visible bounds. - Introduced a new `PopoverSize` type to manage measured dimensions, improving the accuracy of popover placement. - Implemented a `useLayoutEffect` to dynamically measure the popover size and adjust its position accordingly, enhancing user experience during interactions. - Added tests to verify that the popover correctly adjusts its position based on target and viewport dimensions, ensuring it remains fully visible. These changes improve the usability of the BoardComposerPopover by ensuring it is properly positioned within the viewport, enhancing the overall commenting experience. * feat(comment-attachments): enhance image attachment handling in comments - Updated the `normalizeCommentAttachments` function to include image attachments in the comment payload, allowing comments to be submitted with only images. - Introduced a fallback message for comments that consist solely of image attachments, improving user guidance. - Enhanced the `renderCommentAttachmentHint` function to display image attachment details, ensuring users are informed about attached images. - Added tests to verify that image attachments are preserved in comment submissions and correctly rendered in hints, improving overall functionality and user experience. These changes enhance the commenting system by providing better support for image-only comments, ensuring users can effectively utilize image attachments in their interactions. * feat(comment-attachments): refine comment context handling and enhance drag-and-drop functionality - Updated the `normalizeCommentAttachments` function to conditionally omit comment text when the context is set to 'query', improving clarity in comment submissions. - Enhanced the `renderCommentAttachmentHint` function to only display comments when not in 'query' context, ensuring a cleaner output. - Implemented drag-and-drop functionality in the `CommentSidePanel` for reordering comments, improving user interaction and organization of comments. - Added tests to verify the correct handling of comment context and the functionality of the drag-and-drop feature, ensuring robust performance. These changes enhance the commenting system by providing clearer context management and improved usability through drag-and-drop capabilities. * feat(FileViewer, ProjectView): enhance comment attachment handling and status updates - Updated the `FileViewer` component to manage the state of sent comment IDs, ensuring that active previews are correctly updated after sending comments. - Refined the `ProjectView` component to filter out comment attachments from the board-batch source and update their status to 'applying' during processing, improving user feedback on attachment handling. - Introduced logic to handle the removal of sent comment IDs from the preview, enhancing the overall user experience when managing comments and attachments. These changes improve the functionality and responsiveness of the commenting system, providing clearer feedback and better management of comment attachments. * feat(conversation-forking): introduce message-based conversation forking - Added the ability to fork conversations from a specific message, enhancing the chat experience by allowing users to create new conversations that inherit context up to a chosen point. - Updated the CLI commands and help documentation to reflect the new `--fork-after` option, which specifies the message ID to stop copying from. - Enhanced the backend to handle the new forking logic, ensuring that only messages up to the specified ID are included in the new conversation. - Implemented tests to verify the forking functionality, ensuring robust performance and correct behavior when forking conversations. These changes improve the flexibility of conversation management, allowing users to create tailored discussions based on previous interactions. * feat(terminal-service): enhance session output management and memory handling - Introduced new parameters for managing session output, including `maxBufferBytes`, `exitTailBytes`, `flushIntervalMs`, and `flushThresholdBytes`, to optimize memory usage and performance. - Implemented a `trimBuffer` function to efficiently evict old events based on byte and count limits, improving memory management during active sessions. - Added logic to coalesce buffered PTY output into single `data` events, reducing the frequency of event emissions and enhancing performance during high-throughput scenarios. - Updated session event structure to include `byteLength`, allowing for better tracking of output size and memory usage. These changes improve the efficiency and responsiveness of terminal sessions, ensuring better resource management and user experience. * feat(workspace-context): enhance project attachment handling and workspace context management - Introduced `formatProjectAttachmentHint` function to render project attachments in a user-visible order, improving clarity for users referencing attachments. - Added `normalizeWorkspaceContextItems` function to standardize workspace context items, ensuring consistent handling of various context types. - Updated `mergeRunContextSelections` to include workspace items, enhancing the context management during chat interactions. - Enhanced `renderRunContextPrompt` to display active workspace context, providing users with better visibility of their current workspace state. - Implemented tests for new functions to ensure robust functionality and correct behavior in handling project attachments and workspace contexts. These changes improve the user experience by providing clearer context and better management of project attachments within the application. * feat(database, chat): enhance message and conversation data structure - Added `run_context_json` to the `messages` table schema to store contextual information for each message. - Updated migration logic to include the new `run_context_json` field and ensure backward compatibility. - Enhanced conversation retrieval to include `messageCount`, providing better insights into the number of messages per conversation. - Improved attachment handling in the `ChatComposer` component by introducing ordering logic for attachments, ensuring a consistent user experience. - Refactored `SessionModeToggle` to simplify state management and improve tooltip visibility. These changes enhance data management and user interaction within the chat application, providing clearer context and improved functionality. * feat(attachment-handling): improve attachment sorting and context management - Introduced `sortAttachmentsByUserOrder` function to ensure attachments are displayed in a user-defined order, enhancing clarity and usability. - Updated `historyWithApiAttachmentContext` to utilize the new sorting function, improving the context provided with message histories. - Refactored `buildAnthropicMessageContent` to apply sorting for image attachments, ensuring consistent handling across different message types. These changes enhance the user experience by providing better organization and visibility of attachments within the chat application. * feat(chat): enhance conversation listing and loading states - Improved the SQL query for listing conversations to include message counts, providing better insights into conversation activity. - Added loading states to the ChatPane component, enhancing user experience during data fetching. - Implemented a search feature in the conversation history, allowing users to filter conversations by title for easier navigation. - Updated styles for loading indicators and conversation list to improve visual feedback during loading states. These changes enhance the usability and responsiveness of the chat interface, providing users with clearer context and improved interaction capabilities. * feat(chat): optimize suggestion filtering and enhance design system integration - Refactored suggestion filtering in the ChatComposer component to utilize `useMemo`, improving performance by memoizing results based on dependencies. - Added new props in ChatPane for handling plugin and design system details, enhancing the integration of these features within the chat interface. - Updated the FileWorkspace component to manage tab states for design system and browser tabs, improving user navigation and context management. - Introduced modals for displaying plugin and design system details in the ProjectView, enhancing user experience by providing contextual information. These changes improve the efficiency of suggestion handling and enhance the overall user experience in managing plugins and design systems within the chat application. * fix(chat): adjust styling and layout for improved chat interface - Reduced the conversation row height in ChatPane for better alignment with design specifications. - Updated CSS styles across various components to enhance layout consistency, including adjustments to margins, padding, and flex properties. - Improved visual feedback and responsiveness in chat elements, ensuring a more cohesive user experience. These changes refine the chat interface, making it more visually appealing and user-friendly. * feat(comments): implement structural equality checks for comment snapshots - Added `commentSnapshotOverlayEqual` and `commentSnapshotEqual` functions to compare comment snapshots based on their structural properties, improving performance by avoiding unnecessary state updates in the `FileViewer` component. - Updated `HtmlViewer` to utilize these equality checks, optimizing the handling of live comment targets during pointer movements and hover events. - Enhanced the overall responsiveness of the comment system by preventing redundant re-renders when comment snapshots remain unchanged. These changes enhance the efficiency of comment handling and improve user experience in the commenting interface. * feat(chat): enhance comment attachment sorting and order management - Introduced `sortChatCommentAttachmentsByOrder` function to ensure comment attachments are displayed in a user-defined order, improving clarity and usability. - Updated the `currentCommentAttachments` function to utilize the new sorting logic, enhancing the organization of attachments. - Adjusted the order assignment for visual attachments to ensure consistent handling based on existing attachment orders. These changes improve the user experience by providing better organization and visibility of comment attachments within the chat application. * feat(chat): enhance queued send strip with overflow handling and styling improvements - Added overflow handling to the queued send strip, allowing for better visibility of additional queued items when the list exceeds the visible limit. - Updated CSS styles for the chat components, including adjustments to layout, padding, and font sizes to improve overall aesthetics and usability. - Refactored the structure of the queued send row to utilize a grid layout, enhancing alignment and responsiveness of the elements within the chat interface. These changes improve the user experience by providing clearer visibility of queued messages and a more polished interface. * feat(database): add index for created_at and enhance comment retrieval - Introduced a new index on `preview_comments` for `created_at` to optimize query performance when retrieving comments. - Updated the `listPreviewComments` function to order results by `created_at` and `rowid`, improving the organization of displayed comments. - Enhanced the test suite to verify the injection of the new URL preview selection bridge and its functionality in various scenarios. These changes improve the efficiency of comment retrieval and enhance the user experience in the commenting interface. * feat(tooltip): implement tooltip system for enhanced user guidance - Introduced a new `TooltipLayer` component to manage tooltip display across the application, improving user interaction by providing contextual information on hover and focus. - Updated various components to utilize the tooltip system, including buttons and icons, ensuring consistent tooltip behavior and styling. - Enhanced CSS styles for tooltips, improving visibility and responsiveness, while maintaining a cohesive design across the application. These changes enhance the user experience by providing clearer guidance and improving the overall usability of interactive elements. * feat(tooltip): enhance tooltip integration across components - Updated various components to include tooltip functionality, improving user guidance with contextual information on hover and focus. - Added `data-tooltip` and `data-tooltip-placement` attributes to buttons and interactive elements for consistent tooltip behavior. - Enhanced CSS styles to ensure tooltips are displayed correctly and responsively, maintaining a cohesive design across the application. These changes improve the overall user experience by providing clearer guidance and enhancing the usability of interactive elements. * feat(project-view): refactor comment handling and enhance tooltip positioning - Introduced `mergeSavedPreviewComment` function to streamline the management of preview comments, improving clarity and maintainability. - Updated `ProjectView` to utilize the new comment merging function, enhancing the efficiency of comment updates. - Refactored `TooltipLayer` to use x and y coordinates for positioning, improving tooltip display accuracy and responsiveness. - Enhanced CSS styles for tooltips, ensuring better visibility and layout consistency across the application. These changes improve the user experience by providing more efficient comment handling and refined tooltip interactions. * feat(design-files): enhance workspace hint and file handling in project context - Introduced `formatDesignFilesWorkspaceHint` function to provide a detailed overview of the current Design Files workspace, including folder and file listings. - Added limits for the number of folders and files displayed to improve clarity and prevent overwhelming users with excessive information. - Updated the `startServer` function to integrate the new workspace hint, ensuring that the context of existing project files and folders is communicated effectively. - Enhanced tests for the new workspace hint functionality to ensure accurate representation of project context. These changes improve user experience by providing clearer insights into the Design Files workspace and facilitating better project management. * feat(composer): atomic @mention keyboard navigation and deletion * feat(database): add attachments_json field to comments and enhance migration logic - Introduced `attachments_json` field in the database schema for comments to support attachment storage. - Updated migration functions to include the new field, ensuring existing data is properly migrated. - Refactored related SQL queries to accommodate the new field, improving data handling for comments. These changes enhance the comment functionality by allowing attachments to be stored and retrieved effectively. * chore(nix): refresh pnpm deps hash * feat(chat-composer): implement design toolbox for enhanced design actions - Added a new design toolbox feature to the ChatComposer, allowing users to access various design actions such as 'auto-match', 'motion', and 'visual-polish'. - Introduced a state management system for the design toolbox, including hooks for opening and closing the toolbox. - Enhanced the user interface with new components and styles for the design toolbox, improving accessibility and usability. - Implemented functionality to apply design actions directly from the toolbox, streamlining the design workflow. - Added tests to ensure the correct behavior of the design toolbox and its interactions within the ChatComposer. These changes significantly enhance the design capabilities within the ChatComposer, providing users with a more efficient and intuitive design experience. * feat(chat-composer): enhance design toolbox resource management - Expanded the design toolbox functionality in ChatComposer to include a comprehensive resource index, allowing for better organization and retrieval of skills, plugins, MCP servers, templates, connectors, and project files. - Introduced new types and interfaces to support the expanded resource management, improving type safety and clarity in the codebase. - Updated the design toolbox action descriptions and search functionality to reflect the new resource capabilities, enhancing user experience. - Added tests to validate the new resource indexing and search features, ensuring robust functionality. These enhancements significantly improve the design workflow by providing users with a more organized and efficient way to access various design resources. * feat(workspace): enhance workspace context management and UI integration - Introduced a new function `renderWorkspaceContextToolHints` to provide contextual hints based on the type of workspace items (browser, terminal, files, live artifacts). - Updated `ChatComposer`, `ChatPane`, and `FileWorkspace` components to support and display workspace context items, improving user interaction and accessibility. - Enhanced the `QuickSwitcher` and `TabLauncherMenu` components to include workspace context items in search results, allowing users to navigate between tabs and files more efficiently. - Added new translations and updated existing ones to reflect the inclusion of workspace tabs in the user interface. These enhancements significantly improve the usability and functionality of the workspace, providing users with better context and navigation options. * test(e2e): assert Lexical composer content with toHaveText, not toHaveValue The chat composer and home hero input are now Lexical contenteditable editors, not native form controls, so Playwright's toHaveValue (form-only) fails with "Not an input element". Switch all chat-composer-input and home-hero-input content assertions to toHaveText, and assert multi-line soft-break cases with separate toContainText checks since the editor's textContent collapses the newline (the newline reaching the sent payload is already covered by downstream message/payload assertions). * chore(nix): refresh pnpm deps hash * chore(nix): refresh pnpm deps hash * test(e2e): open settings through the entry settings menu The home settings entry is now a menu (EntrySettingsMenu): clicking the gear opens a popover whose "Settings" item opens the full execution-mode dialog. Update the three specs that assumed a single click opened the dialog directly to go through the menu trigger + open-details item. * test(web): drive HomeView context picker through the Lexical helper The home hero input is a Lexical contenteditable, so fireEvent.change / reading .value throws "element does not have a value setter". Switch the MCP+connector first-turn-context spec to setHomeHeroPrompt / homeHeroPromptText like the rest of the file already does. * Fix PR review blockers * Update Nix pnpm deps hash * Enhance project folder routes with error handling and tests - Added checks for project existence in GET, POST, and DELETE folder routes, returning a 404 error if the project is not found. - Updated tests to verify 404 responses for unknown project IDs in folder operations. - Improved folder metadata handling in project routes. - Refactored the TabLauncherMenu component for better UI structure and scrolling behavior. - Adjusted styles for the TabLauncherMenu to improve usability and visual consistency. * Update ProjectView component and enhance design toolbox localization - Modified the ProjectView component to include workspacePanelTrack in chat panel width adjustments. - Updated localization files to add new keys and translations for the design toolbox in English, Simplified Chinese, and Traditional Chinese. - Enhanced user experience by providing comprehensive tooltips and prompts for design toolbox actions. * Add browser use prompt launcher * Cover browser use prompt launcher * Add design toolbox badge translations * Refactor ChatComposer and DesignBrowserPanel components - Removed the design system picker from the ChatComposer component and integrated it into the StagedRunContexts for better context handling. - Updated the DesignBrowserPanel to include a new function for determining viewport icons and modified the browser use categories to reflect changes in action prompts and titles. - Enhanced the HandoffButton component with a new feature to copy the project path to the clipboard, improving user experience. - Added new styles for search input and empty states in design files, enhancing the UI consistency across components. * Enhance ChatPane and DesignBrowserPanel components with new features and styles - Added scroll handling features to the ChatPane component, including scrollable state management and improved user experience during chat interactions. - Updated the DesignBrowserPanel to include new localization keys and improved category titles for better clarity in the browser use prompt. - Enhanced styles for chat log scrolling behavior, providing a more intuitive interface for users. - Implemented keyboard shortcuts for tab navigation in the WorkspaceTabsBar, improving accessibility and user efficiency. * Fix web workspace test hang * Update styles and functionality for workspace tabs and chat components - Adjusted CSS for the MAC_WINDOW_CHROME to improve spacing and margins for better layout. - Enhanced the ChatComposer component to ensure proper tab order and mention handling, improving user experience. - Implemented keyboard shortcuts for navigating workspace tabs, allowing for more efficient tab management. - Updated localization strings for clarity in search prompts across multiple languages. - Improved the HandoffButton component's UI for better visibility and interaction when copying project paths. * Update visual settings flow * Refresh Nix pnpm deps hashes --------- Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com> Co-authored-by: qiongyu1999 <2694684348@qq.com> Co-authored-by: Siri-Ray <2667192167@qq.com> |
||
|
|
4aea5fdb01 |
feat: skip discovery questions for unmodified example prompts (#3257)
* feat: skip discovery questions for unmodified example prompts When a user selects a curated example prompt from the gallery and sends it without modification, the agent now skips all discovery/clarification questions and directly generates the artifact at full quality. Adds an `examplePrompt` metadata flag that injects a dedicated system prompt override instructing the agent to treat the prompt as a complete creative brief requiring showcase-quality generation. Co-Authored-By: Claude Opus 4 <noreply@anthropic.com> * feat: enrich example prompt override with title and pre-filled brief - Pass example prompt title and artifact-type-specific discovery brief through metadata so the agent receives full context without asking - Only skip discovery on the user's first example prompt usage (localStorage gated), subsequent uses follow normal discovery flow - Convert EXAMPLE_PROMPT_OVERRIDE from static string to dynamic builder that interpolates title and brief key-value pairs * fix: prevent handlePromptChange from clearing examplePromptInfoRef The ref is now only cleared by onExamplePromptStatusChange(null) which HomeHero triggers when the user actually edits the text or clicks clear. This prevents the race where usePromptExample sets the ref and then onPromptChange immediately clears it in the same tick. * fix: honor example-prompt discovery skip in API/BYOK mode API/BYOK runs compose their system prompt through @open-design/contracts, whose ProjectMetadata and composeSystemPrompt only knew about skipDiscoveryBrief. Unmodified gallery example prompts therefore skipped discovery in daemon-backed runs but still asked discovery questions in API mode, making the feature mode-dependent. Mirror the daemon's examplePrompt/examplePromptTitle/examplePromptBrief metadata and buildExamplePromptOverride into contracts so both composers behave identically. Generated-By: looper 0.8.1 (runner=fixer, agent=claude-code) --------- Co-authored-by: qiongyu1999 <2694684348@qq.com> Co-authored-by: Claude Opus 4 <noreply@anthropic.com> |
||
|
|
ee2df3bea9 |
[codex] Add safe retry policy contracts (#3569)
* Add safe retry policy contracts * Cap default safe run retry at one attempt Issue #3543 scopes the first implementation to at most one automatic same-run retry. Set DEFAULT_SAFE_RUN_RETRY_MAX_ATTEMPTS to 1 so a first failed retry (attemptCount: 1) suppresses with attempt_limit_reached instead of being eligible for a second retry, and document that the field counts retry attempts rather than the initial run. Update the policy test to assert the one-retry-by-default contract. Generated-By: looper 0.9.3 (runner=fixer, agent=claude-code) |
||
|
|
fa59871428 |
Merge origin/main into release/v0.9.0 — back-merge to reconcile v0.9.0 with main
Syncs release/v0.9.0 with main so PR #3282 (release/v0.9.0 → main) merges
cleanly. 17 conflicts resolved by commit-time + per-area ownership:
- Comment/edit/preview: keep main (newer). FileViewer combines main's #3438
(pin manual-edit inspector, no full-height panel) + #3347 deck/queue work
WITH release's #3401 hover-card anti-flicker grafted on; #2879 Button kept.
- Toolbar: footer layout (reviewer-validated,
|
||
|
|
d4521ba6a8 |
fix: harden local CLI prompt routing (#3452)
* test(contracts): pin prompt routing parity * fix(contracts): align prompt routing invariants * test(mocks): harden contract check output |
||
|
|
a6198e22fd |
Improve run failure classification and Langfuse correlation (#3412)
* Classify run failures in analytics * Add Langfuse delivery observability * Handle normalized cache token aliases * Fix normalized cache token accounting * Cover CLI token usage analytics * Add stderr diagnostics telemetry * Wire Langfuse reporting through inline message route * Add run failure telemetry smoke coverage * Add run observability regression coverage * Add asked_user_question to run_finished contract test fixture main introduced asked_user_question as a required RunFinishedProps field. The branch's run_finished contract test built the props fixture without it, so the post-merge Preflight typecheck failed. Supply the field in the base fixture so the contract test compiles against the merged contract. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * Expand observability regression matrices * Add failure detail telemetry classification * Classify permission request lifecycle failures * Classify upstream stream closure failures * Classify socket closed API failures * Count stderr lines from merged stream in analytics summary summarizeRunDiagnosticsForAnalytics summed per-chunk line counts, but stderr chunks are not line-aligned. A logical line split across two chunks was counted twice, inflating stderr_line_count_bucket. Concatenate chunks first, then count, matching collectStderrTailSummary. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * Fix first-token timing and Langfuse model labeling Drop tool_use from FIRST_TOKEN_AGENT_EVENT_TYPES so firstTokenAt is only stamped by token-producing events; tool-first runs no longer under-report time_to_first_token_ms / spawn_to_first_token_ms. Thread agent_reported_model out of the usage scan in langfuse-bridge and use it for turn.model when the request did not pin an explicit model, so Langfuse traces label the resolved model and agree with PostHog run_finished instead of keeping the default placeholder. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * Stamp antigravity first-token only when buffered stdout is flushed The antigravity stdout handler stamped firstTokenAt as soon as any buffered chunk arrived, but on the OAuth-required path agy prints the login URL to stdout and the close-time auth-prompt guard suppresses that buffer without ever flushing it. firstTokenAt was still set, making time_to_first_token_ms / spawn_to_first_token_ms look artificially fast for that failure mode. Record only the first chunk's arrival time at buffer time and stamp firstTokenAt from it at flush time, so suppressed OAuth-prompt runs never report a TTFT for output the user never saw. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * Re-derive Langfuse delivery state at run completion time The PostHog run_finished sink captured langfuseDeliveryForAnalytics once when POST /api/runs was handled and reused it in the terminal callback, so a long-running run could publish stale langfuse_expected / langfuse_delivery_status / langfuse_drop_reason if the user flipped telemetry consent or relay config mid-flight. The Langfuse sink (reportRunCompletedFromDaemon) re-reads app config at completion, so the two paths could disagree. Recompute deriveLangfuseDeliveryState inside the run-completion callback so both sinks read the same completion-time eligibility. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) * Carry total-only token usage and drop default model label in Langfuse bridge Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) --------- Co-authored-by: Amy <1184569493@qq.com> |
||
|
|
a551b8534f |
fix(analytics): map AMR runtime to its own provider id (#3511)
AMR runs spawn the vela CLI under the daemon agentId 'amr' (apps/daemon /src/runtimes/defs/amr.ts), but agentIdToTracking() had no 'amr' case, so every AMR run reported agent_provider_id='other' and could not be told apart from other unmapped agents in PostHog. Add 'amr' to the TrackingCliProviderId catalogue and the mapping switch; the feedback and settings CLI-test paths fall through to the same helper, so they pick up the value too. Also register 'amr' in the two web display-label maps (agentLabels and MemoryModelInline.cliAgentLabel) so the agent renders as 'AMR' instead of the lowercase fallback 'amr'. |
||
|
|
e61968ecb5 |
feat(web): track BYOK configuration outcomes (#3505)
Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> |
||
|
|
a7440af9e5 |
Revert "Merge pull request #3410 from nexu-io/fix/deck-comment-slide-scope-v0.9.0" (#3481)
This reverts commit |
||
|
|
adcb655108 |
fix(connectors): explain Composio custom auth requirement (#3464)
* fix(connectors): explain Composio custom auth requirement * fix(connectors): rediscover custom Composio auth configs |
||
|
|
dbcb5a0b25 |
Merge pull request #3410 from nexu-io/fix/deck-comment-slide-scope-v0.9.0
Fix deck comment slide scoping |
||
|
|
3da33f92a1 |
Harden sandbox orchestration daemon chokepoints (#3420)
* Harden sandbox orchestration chokepoints * Cover web app public copy in neutrality guard |
||
|
|
b47b092196 | Fix deck comment slide scoping | ||
|
|
601183efbb |
feat(daemon): load AMR model presets before remote catalog (#3425)
* spec * impl * fix * remove default model content * fix(amr): keep model catalogs fresh across env changes Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(amr): bust logout cache and stop preset polling Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * test(amr): cover logout cache invalidation and bounded polling Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(amr): start preset polling before agent probe Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(amr): clear stale web cache on env rescan Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(amr): restart preset polling after sign-in Generated-By: looper 0.9.2 (runner=fixer, agent=codex) * fix(daemon): remove extra AMR spawn env arg Generated-By: looper 0.9.2 (runner=fixer, agent=codex) |
||
|
|
fabfddaede |
feat(daemon): add contained project preview URLs (#3246)
* feat(daemon): add contained project preview URLs * fix(daemon): tolerate coarse artifact mtimes * fix(daemon): validate project preview scopes |
||
|
|
512e01405f |
fix(web): queue preview comments while busy and pin deck markers (#3347)
* fix(web): queue preview comments while busy and pin deck markers While a chat run is in flight, preview comments now queue instead of showing a stuck Sending state. Deck slide changes no longer collapse comment markers to 0,0 by filtering hidden targets and persisting slideIndex. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): stop comment overlay jitter from redundant target sync Skip React updates when live comment target bounds are unchanged, refresh deck targets only on slide changes, and drop characterData from the selection-bridge mutation observer to avoid streaming text churn. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): keep comment target metadata fresh * fix(web): refresh active comment text mutations * fix(daemon): key preview comments by deck slide --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com> |
||
|
|
ef5f150e69 |
Fix Studio share menu hierarchy (#3266)
* fix: clarify studio share menu hierarchy * fix: keep cli menu controls after move * fix: hide pet quick action from chat * fix: remove duplicate settings from cli menu * fix: remove reasoning from cli quick menu * fix(web): restore empty project instructions entry * fix(web): expose markdown export download * fix(web): restore avatar settings menu item * fix(web): restore avatar reasoning control * fix(web): restore inline settings shortcut * fix(web): restore share and pet composer affordances * test(web): expect inline settings shortcut * fix(web): localize share menu copy --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> |
||
|
|
a1f01e6fb7 |
feat(daemon): add project export manifest (#3245)
* feat(daemon): add project export manifest * fix(daemon): honor export manifest primary refs * fix(daemon): bind headless runs to default conversation * test(e2e): retry tools-dev namespace startup collisions * test(daemon): reject sandbox imported export manifests * fix(daemon): honor artifact manifest entry refs * fix(daemon): update renamed artifact manifest entries * fix(daemon): update artifact refs during rename * fix(daemon): preserve export refs across directory moves |
||
|
|
6bcc5e99ee |
feat(analytics): mark clarification-turn runs in run_finished (#3384)
The first session of a task does intent clarification by raising an AskUserQuestion card, which inherently produces no artifact. These clarification turns were indistinguishable from artifact-generation failures on the dashboard, inflating the "run finished -> has artifact" dropoff. Add a required `asked_user_question: boolean` to RunFinishedProps, populated daemon-side via a new `runAskedUserQuestion` helper that scans the run's agent events for an AskUserQuestion / ask_user_question tool_use. The dashboard can now exclude clarification turns from the artifact funnel instead of scoring them as failures. Co-authored-by: qiongyu1999 <2694684348@qq.com> |
||
|
|
af4a62b69a |
Add configurable project locations (#2041)
* add daemon project location support * wire project locations into web settings * localize project location settings * move default project location to settings * polish project location selection cards * fix project location i18n gaps * fix external project validation cleanup |
||
|
|
729ce2b0cb |
feat(daemon): add run-scoped MCP tool bundles (#3244)
* feat(daemon): add run-scoped MCP tool bundles * fix(daemon): keep sandbox runs in managed project dirs * fix(daemon): reject malformed run tool bundles * fix(contracts): model run-scoped mcp server inputs * fix(daemon): reject unsupported run tool bundles * fix(daemon): validate run tools before chat fallback * test(daemon): expect sandbox imported folder failure * fix(daemon): preflight sandbox project roots before run rows * fix(daemon): preflight sandbox chat project roots * fix(daemon): allow host editor for sandbox imports * fix(daemon): preflight sandbox routine project reuse * fix(daemon): reject undeliverable Claude tool bundles * fix(daemon): single-source chat route validation |
||
|
|
e8c179d3a6 |
fix: show cumulative conversation duration (#3354)
* fix: show cumulative conversation duration * fix: include usage-only run durations --------- Co-authored-by: Lanzhou3 <217479610+Lanzhou3@users.noreply.github.com> |
||
|
|
f12679185c |
fix(web): send Anthropic proxy image attachments (#3273)
* fix(web): send Anthropic proxy image attachments * fix(web): omit image attachment stubs for Anthropic proxy * fix(web): keep image fallback context aligned * fix(web): align Anthropic image attachment omission --------- Co-authored-by: 116405 <116405@ky-tech.com.cn> |
||
|
|
0fbeaf829e |
fix(#3247): Detect, terminate, and warn on fabricated role markers across all agent paths (#3303)
* fix(daemon): detect and strip fabricated role markers in model output (#3247) Three-layer defence against models emitting `## user` / `## assistant` / `## system` lines mid-response, which the chat host interprets as real turn boundaries and acts on as unauthorised instruction: 1. **System prompt**: anti-roleplay instruction elevated from a bullet under "What you don't do" to a standalone `## CRITICAL` section in `official-system.ts`, with a REMINDER pinned at the end of the composed prompt for recency bias. 2. **Stream-level detection and truncation**: shared `role-marker-guard.ts` module (`createRoleMarkerGuard` + `FABRICATED_ROLE_MARKER_RE`) used across all text paths — Claude stream (per-message guards), non-Claude structured streams (run-scoped guard via `emitGuardedTextDelta`), and BYOK proxy routes (`createDeltaGuard`). When a marker is detected, the contaminated suffix is dropped and a `fabricated_role_marker` event surfaces a warning in the UI. 3. **UI**: `StatusPill` gains `is-warning` / `is-error` CSS variants; `fabricated_role_marker` events render as amber warning pills. * fix(chat-routes): do not await reader.cancel() on stream early-return The await on reader.cancel() can hang indefinitely on response streams whose underlying source is a Uint8Array (most notably surfaced by the ollama test in proxy-routes.test.ts, which builds its mock body via `new Response(uint8array)` rather than the controller-based helper `sseResponse()`). The hung await holds the request handler open, which in turn blocks `server.close()` in the afterAll hook, producing the two test timeouts (test at 145, hook at 36) currently failing CI on #3296. Fix is in production code, not the test: don't await the cancel. It is a cleanup hint and we are returning from the function anyway, so blocking on it offers no value. fire-and-forget with an empty catch keeps the cancel signal flowing for real HTTP streams without risking a hang on mock/edge-case implementations. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(daemon): terminate child on role-marker detection (close #3247 generation vector) PR #3296's detection layer truncates display and persistence of fabricated role markers, but the underlying model subprocess keeps generating tokens after detection. Three concrete consequences: 1. The model bills the user for the entire contaminated response (we observed 5,106 chars stored in claude's session file for a turn where only the first 3,013 chars were legitimate — a 40% overhead). 2. tool_use blocks emitted AFTER the marker reach the daemon's dispatcher unchecked, since detection only gates the text-delta emission path, not content-block-stop / tool_use blocks. The model could fabricate "## user delete file X" then emit a tool_use(delete X) that the dispatcher would execute. 3. The UI surfaces a `fabricated_role_marker` warning followed by an eventual normal turn-end, blurring the distinction between "completed normally" and "killed by safety guard." This commit adds a single idempotent `abortForRoleMarker(marker)` helper in server.ts, scoped to the same closure as `child` and `runGuard`. On any detection event (per-message Claude guard, run-scoped non-Claude guard, plain stdout guard) the helper: - Emits a structured `ROLE_MARKER_HALLUCINATION` SSE error so the UI can render a security-class status distinct from a normal turn-end. The existing `fabricated_role_marker` warning is still sent and rendered as the amber pill (PR #3296's UI). - Calls `acpSession.abort()` for ACP-multiplexed agents (Hermes, Kimi, Devin, Kiro) whose I/O doesn't necessarily release on SIGTERM of the wrapper process alone. - SIGTERMs the child immediately, with the existing `scheduleForcedChildShutdown()` SIGKILL fallback at 2x grace. Wired into three sites where contamination is detected: - `emitGuardedTextDelta` (sendAgentEvent / copilot / ACP / pi-rpc text_delta paths) - Plain-stdout listener (BYOK plain mode) - The Claude stream handler's onEvent (per-message guards in claude-stream.ts surface `fabricated_role_marker` events directly via onEvent rather than through the run-scoped emitGuardedTextDelta) Tool_use blocks emitted BEFORE the marker still flow through normally — this guard can't help with those, since by the time we observe a text marker the prior content block has already finished. Closing that gap requires speculative cancellation of in-flight tool calls when a downstream text block contains a marker; that's tracked as follow-up work, not included here. Co-Authored-By: roverkai <2196140098@qq.com> Co-Authored-By: JasonBroderick <jason@buddyboss.com> * refactor(role-marker-guard): bounded tail + drop chat-style markers Addresses two review comments on #3303: (1) O(1) memory + per-delta work (review r3323982225) Replace the unbounded `accumulated` string with a rolling tail capped at TAIL_BUFFER_SIZE (64 chars — comfortably exceeds the longest marker prefix `\n<whitespace>## assistant` ≈ 16–24 chars in practice). A 50 KB assistant response delivered in 1000 chunks of 50 bytes was previously O(n²) on string concatenation alone; now it is O(1) per delta regardless of message length. The `tail.length` value carries the "already emitted" offset that the cut-point math needs, so the offset semantics at L74–78 of the prior implementation are preserved without re-introducing the full-text buffer. (2) Drop chat-style markers entirely (review r3323982234, option (a)) `User:` / `Assistant:` / `Human:` / `AI:` are removed from the regex. Rationale: - The host parses ONLY `## user` / `## assistant` / `## system` lines as turn boundaries (see `buildDaemonTranscript` in apps/web/src/providers/daemon.ts). A model emitting chat-style markers does NOT cause the original #3247 security failure. - With kill-on-detection wired in this PR (`abortForRoleMarker` in server.ts), a false positive aborts the whole run — far more expensive than a stray unflagged `User:` line in chat scrollback. Chat-style markers collide with legitimate output (form labels, email contacts, JSDoc) often enough that pairing them with kill-semantics is the wrong tradeoff. The tradeoff is now documented in the regex docblock so the kill-on-match behaviour is justified against the false-positive surface. Also aligns the prompt-side CRITICAL block in system.ts: drop the "don't emit User: / Assistant: / Human: / AI:" bullet, since we no longer enforce it. Less ambiguity for the model and the operators. Test file updated: - Chat-style positive tests flipped to negative ("does NOT match User: — chat-style out of scope") so the intentional exclusion has a permanent regression test. - Two new tests cover the bounded-tail behaviour: a marker arriving after 10 KB of clean text in small chunks, and a marker straddling a chunk boundary after 100 prior chunks. - Added test for legitimate `User: bob@example.com`-style content not triggering contamination. Test count is now 35 (up from 25); two of the new ones explicitly exercise the new bounded-tail path. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(role-marker-guard): drop \`^\` anchor after first chunk (review r3324060995) Blocking correctness bug introduced by commit 4 (bounded-tail refactor): once \`tail\` is a rolling slice of mid-stream text, \`^\` in the canonical regex \`(?:^|\\n)\\s*##\\s+(?:user|...)\` no longer represents the genuine message start. As the rolling window slides forward chunk by chunk, a sliced tail can begin with whitespace + \`##\` (or just \`##\`), letting \`^\` anchor a match against text that the full-buffer implementation correctly ignored. With kill-on-detection wired in commit 3, that false positive now SIGTERMs the run and emits a \`ROLE_MARKER_HALLUCINATION\` error — exactly the failure class called out in the docblock at L22–29. Reviewer's evidence (PerishCode, r3324060995): streaming "…take a look at the ## user content section…" one character at a time reports \`contaminated: true\` post-refactor; the same text in a single feed stays clean. Fix: keep the canonical \`FABRICATED_ROLE_MARKER_RE\` for the very first non-empty feed (where \`^\` legitimately points at the message start), and switch to an internal \`NEWLINE_ANCHORED_ROLE_MARKER_RE\` (\`\\n\\s*##\\s+(?:user|...)\` — drops the \`^\` alternative) for all subsequent feeds. A \`firstChunk\` boolean tracks the state. Real newline-preceded markers straddling chunk boundaries are still caught because the preceding \`\\n\` is retained inside the 64-char tail. Regression tests added (\`apps/daemon/tests/role-marker-guard.test.ts\`): - mid-line \`## user\` streamed char-by-char with no preceding \\n (mirrors the reviewer's repro) - space-preceded mid-line \`## user\` in a >130-char stream, which long enough to force the rolling window past the marker — exercises the exact slice condition that triggered the bug - real \\n-preceded \`## user\` still caught after a long preamble (positive case must not regress) - \`## user\` as the very first chunk still caught (\`^\` legitimately anchors on the first feed) Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(role-marker-guard): case-sensitive + tighter prefix scope (reviews r3324151877 / r3324151882) Two refinements addressing the third review on #3303: == Blocking (r3324151877) == The regex over-matched legitimate Markdown headings, and with kill-on-detection wired in commit 3 each false positive deterministically aborts a real run. Three changes tighten the match to the actual security surface — `## user` / `## assistant` / `## system` lines the chat host parses as turn boundaries — without losing any real attack pattern: 1. CASE-SENSITIVE. Dropped the `/i` flag. The host's turn-boundary delimiter is lowercase (see `buildDaemonTranscript` in apps/web/src/providers/daemon.ts), and the `## CRITICAL` system-prompt block already forbids only the lowercase forms. Title-Case headings like `## User Guide`, `## System Architecture`, `## Assistant settings` are now ignored — these are legitimate technical writing patterns LLMs emit constantly. `## USER NOTES` (all-caps) likewise no longer flags. 2. POSITIVE LOOKAHEAD `(?=[^a-z])` after the role keyword. Without it, `## userland`, `## userspace`, `## users guide`, `## systemd`, `## assistance` all match via prefix in the alternation. The lookahead requires the next character to exist and to not be a lowercase letter, so: - `## user\\n…` → match (newline is not lowercase) - `## assistantR…` → match (R is uppercase; the glued-form attack pattern still gets caught) - `## assistant.` → match (. is not a letter) - `## users guide` → no match (s is lowercase letter) - `## userland` → no match (l is lowercase letter) POSITIVE rather than NEGATIVE `(?![a-z])` because the negative form is satisfied at end-of-string, which in a streaming context means "we have `## user` but don't know what comes next yet" — would fire prematurely if `land` arrives in a later chunk. The positive form delays detection by one character in that edge case, traded for correctness. 3. `[ \\t]` instead of `\\s` for inner whitespace. Markdown role markers are single-line by convention; restricting to space/tab prevents oddities like `##\\nuser` from matching across lines. Test file: added Title-Case fixtures (`## User Guide`, `## System Architecture`, `## Assistant settings`, `## USER NOTES`) and prefix-of-longer-word fixtures (`## users guide`, `## userland`, `## systemd`, `## assistance`) — each asserting NO contamination. The existing `## usability` negative test gave false confidence as the reviewer noted (only failed via alternation-miss, not via word-boundary semantics); the new fixtures actually exercise the lookahead. Also added a positive test for `## assistant.` (glued punctuation) to balance the existing `## assistantReading` (glued uppercase) coverage. Total tests: 35 → 50. == Non-blocking (r3324151882) == Added `ROLE_MARKER_HALLUCINATION` to `API_ERROR_CODES` in `packages/contracts/src/errors.ts` alongside the existing agent/AMR codes, with a docblock comment explaining the emission contract: emitted by `server.ts::abortForRoleMarker` alongside the existing `fabricated_role_marker` warning event when the daemon detects a fabricated Markdown role marker in agent output; retryable. The code was already being emitted over the wire but unregistered — landing the registration here keeps the contract and emitter in sync as reviewer requested. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(role-marker-guard): defer complete-but-unconfirmed marker suffix Addresses review r3324277xxx — the boundary case where a stream chunk boundary lands between the role keyword and its lookahead character violated the documented "everything from the marker onward is silently dropped" contract. With (?=[^a-z]) as the lookahead, `feedText('## user')` returned `## user` as safe (no char to satisfy the lookahead → no match → pass through), so the fabricated marker line leaked into UI and app.sqlite before the next chunk confirmed contamination on the next SIGTERM cycle. Fix: introduce a `pending` state variable holding bytes that match the COMPLETE-but-unconfirmed marker prefix at end of buffer (/(?:^|\\n)[ \\t]*##[ \\t]+(?:user|assistant|assist|system)$/, no lookahead, $ anchor instead). When the no-match branch detects this suffix, withhold it from emission until the next feed either: - Confirms it (next char non-lowercase) → main regex matches → contaminated → withheld bytes dropped along with `## user`. - Denies it (next char lowercase, e.g. `userl…`) → main regex no longer matches the role keyword → withheld suffix is released and emitted alongside the new continuation. Also tied the firstChunk transition to actual byte emission rather than feed count. Previously a message that starts with `## system` followed by a separate `\\n` chunk would lose the `^` anchor on the second feed (firstChunk had flipped after the first feed even though nothing was emitted yet), silently breaking detection for that edge case. Now `firstChunk` stays true until at least one byte has crossed the emission boundary, matching the conceptual definition of "message start". Tests added (apps/daemon/tests/role-marker-guard.test.ts): - `## user` deferred at chunk boundary, confirmed by `\\n` in next - `## user` deferred at chunk boundary, denied by `land` continuation - `## assistant` deferred, confirmed by punctuation - `## User` Title-Case still passes through unconditionally - `## system` as the very first chunk: deferred, confirmed by \\n in next chunk (tests the firstChunk-stays-true-when-nothing- emitted invariant) Total tests: 50 → 55. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(claude-stream): scope role-marker guard to text_delta only, not thinking_delta Addresses review r3324xxxxxx — guarding the thinking channel buys no security and causes legitimate aborts. Why thinking is NOT a #3247 vector: - `buildDaemonTranscript` in apps/web/src/providers/daemon.ts only re-serializes `m.content` as `## ${m.role}\n...`. - Extended-thinking content is rendered to a separate `kind: 'thinking'` payload (daemon.ts:857-858) and never folded into `m.content`. - So a `## user` line in the thinking channel CANNOT become a fabricated turn boundary on the next round-trip. Why guarding it is harmful: - Models routinely emit literal `## user` / `## assistant` lines in chain-of-thought when reasoning about conversation structure ("Let me think about this. The user might phrase it as:\n## user\n …"). Common pattern in production traces. - With `abortForRoleMarker` wired in server.ts, a guard match on thinking SIGTERMs the run and surfaces a security error to the UI. The user paid for the reasoning, never sees the answer, and gets a confusing "fabricated role marker" warning for what was actually legitimate metacognition. - This directly contradicts the module's own stated philosophy ("a false positive aborts the whole run — a much more expensive failure than a stray unflagged ... line", role-marker-guard.ts). Fix: `emitSafeText` now passes thinking_delta through unconditionally, skipping both the guard and the contamination check. text_delta remains fully guarded. The single-line change at the top of emitSafeText preserves all other channels' behavior. Regression tests added (apps/daemon/tests/claude-stream-thinking.test.ts): - `## user` / `## assistant` lines in a thinking_delta — must NOT fire fabricated_role_marker, the thinking content streams intact including the marker text, and the subsequent text_delta answer still reaches the consumer (run not aborted). - Sanity check: same `## user` pattern in a text_delta DOES fire fabricated_role_marker and truncates emission at the marker. Locks in the channel-discriminated behavior. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(role-marker-guard): tie firstChunk to slicing, not byte emission Blocking review r3324xxxxxx: under the prior firstChunk transition ("any byte emitted"), a role marker that arrived at the very start of a message with its prefix split across multiple chunks bypassed detection — reopening the #3247 vector on the Claude path. Concrete cases that were missed (all are routine provider tokenizations of \`## user\n…\` at message start): - \`##\` | \` user\nDELETE…\` - \`## us\` | \`er\nDELETE…\` - \`## \` | \`user\nDELETE…\` Mechanism: the pending-deferral regex only catches COMPLETE role keywords, so a first chunk ending in a partial prefix (\`##\`, \`## \`, \`## us\`) was emitted in full. That emission flipped firstChunk to false. From that point only NEWLINE_ANCHORED_ROLE_MARKER_RE was used, which requires a literal \n before \`##\`. A marker at buffer position 0 has no preceding \n, so it could no longer match. abortForRoleMarker never fired and tool_use blocks emitted after the fabricated turn boundary reached the dispatcher. Fix: change firstChunk to track "tail has not been sliced yet" rather than "any byte emitted". While total emitted bytes <= TAIL_BUFFER_SIZE, tail still represents the entire emission so far and \`^\` in the canonical regex genuinely anchors at byte 0 of the stream — so the \`^|\n\` alternation safely catches a chunk-split message-start marker. The transition happens at the moment we would slice: once emitted > TAIL_BUFFER_SIZE, tail becomes a mid-stream window, \`^\` becomes meaningless, and we switch to the newline-only variants. Earlier iterations of this code tried two other definitions, both unsound: - "any byte emitted" (this commit fixes) — lost \`^\` before a chunk-split message-start marker could finish arriving. - "newline emitted" (briefly considered as the reviewer's alternative suggestion) — left \`^\` valid on a sliced buffer when streams hadn't emitted a newline yet, re-introducing the rolling-tail mid-stream false positive from review r3324060995. The slice-based invariant satisfies both: while we have not sliced, \`^\` is correct; once we slice, it is not. Regression tests added (apps/daemon/tests/role-marker-guard.test.ts): - \`##\` | \` user\nDELETE…\` → contaminated, marker=\`## user\` - \`## us\` | \`er\nDELETE…\` → contaminated, marker=\`## user\` - \`## \` | \`user\nDELETE…\` → contaminated, marker=\`## user\` - \`#\` | \`# user\nDELETE…\` → contaminated, marker=\`## user\` The fourth case (single \`#\` first chunk) exercises an even more adversarial tokenization than the reviewer's examples; it is also caught. Total tests: 55 → 59. Co-Authored-By: JasonBroderick <jason@buddyboss.com> * fix(tests): wrap events in stream_event envelope in thinking test feedJsonl was feeding raw events without the `{ type: 'stream_event', event: ... }` wrapper that createClaudeStreamHandler requires (line 141 of claude-stream.ts). Events silently fell through all branches, making both tests pass vacuously. Also fix TS2532 on warnings[0].marker with non-null assertion (safe after the toHaveLength(1) guard). Co-Authored-By: RoverKai <roverkai@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: roverkai <2196140098@qq.com> Co-authored-by: JasonBroderick <jason@buddyboss.com> Co-authored-by: RoverKai <roverkai@users.noreply.github.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> |
||
|
|
dacd2be814 | Merge remote-tracking branch 'origin/main' into release/v0.9.0 | ||
|
|
73b2dc853f |
Fix project empty state create action (#3082)
Co-authored-by: saifulla-khan <saifulla-khan@users.noreply.github.com> Co-authored-by: Siri-Ray <2667192167@qq.com> |
||
|
|
e69306ecbe | chore(release): prepare 0.9.0 | ||
|
|
98a2c63973 |
feat(daemon): add Antigravity agent adapter (#3157)
* feat(daemon): add Antigravity agent adapter
Adds Google Antigravity (`agy` CLI) as a coding-agent runtime. Detection
picks up `agy` on PATH, the daemon spawns `agy -p "<prompt>"` for a
single non-interactive turn, and the assistant text reply streams back
on stdout. OAuth is shared with the Antigravity IDE through the system
keyring, so users who have signed into the desktop app are authenticated
on first run with no extra step.
`agy` v1.0.3 has no JSON / stream-json / ACP output mode (upstream issue
#119), no `--model` flag (issue #35), and no MCP forwarding hook yet —
the adapter ships with `streamFormat: 'plain'` and a single `default`
fallback model so the model picker doesn't mislead users into thinking
their choice is wired through. We will upgrade buildArgs + add a
dedicated event parser when upstream ships structured output.
Also gitignores `.antigravitycli/`, the project-local config directory
`agy` auto-creates on every run (upstream issue #175).
* fix(daemon): Antigravity adapter — stdin prompt, brand icon, form loop, empty-output guard
- Switch prompt delivery from argv to stdin (`agy -p -`) to avoid the
30KB maxPromptArgBytes limit that blocked real-world composed prompts
- Add official Antigravity brand SVG icon to agent picker
- Fix repeated question-form loop for plain agents by injecting an
OVERRIDE block when form answers are already present in the transcript
- Add empty-output guard for plain agents so expired auth or silent
failures surface a user-visible error instead of a blank "Done" turn
* feat(daemon): expand Antigravity adapter — model picker, form-loop fix, OAuth launcher, log-file classification
PR #3157 follow-up integrating four iterations from end-to-end manual
testing on Gemini 3.5 Flash + GPT-OSS 120B Medium through `agy` v1.0.3.
Each section is independently verifiable; combined they're what made
the first successful artifact generation work end-to-end.
## Model picker via settings.json (agy has no --model flag)
agy v1.0.3 ships no `--model` CLI flag (upstream issue #35), but the
TUI Switch-Model picker writes the chosen label to
`~/.gemini/antigravity-cli/settings.json`'s `"model"` field, and every
`-p` invocation re-reads that file on startup — verified by capturing
the `--log-file` line `Propagating selected model override to backend:
label="<model>"`. Antigravity's `fallbackModels` now lists the 8
labels its TUI exposes (Gemini 3.1 Pro / 3.5 Flash variants, Claude
Sonnet/Opus 4.6 Thinking, GPT-OSS 120B Medium) and `buildArgs`
persists the user's choice to settings.json right before spawn. The
synthetic `default` id is preserved — picking it leaves settings.json
untouched so a user who switches models from agy's own TUI keeps
their choice.
Introduces `RuntimeAgentDef.supportsCustomModel?: boolean`. AMR's
hardcoded blocklist in `SettingsDialog.tsx` migrates to the
declarative flag (it rejects free-form ids at the ACP layer), and
antigravity opts out because its label set is a server-side enum that
silently fails on unrecognised strings.
## Form-loop fix (transcript sanitizer + stronger OVERRIDE)
The discovery form loop on weak/medium plain-stream models (GPT-OSS
120B Medium, Gemini 3.5 Flash) had two reinforcing causes:
1. `buildDaemonTranscript` packed the prior assistant turn's
literal `<question-form>` markup into the user request on the
next turn, giving the model a template to echo. New
`sanitizePriorAssistantTurnForTranscript` strips
`<question-form>...</question-form>` blocks and ```json fences
that match form-schema shape, replacing them with a brief
placeholder. User content is preserved verbatim (a user who
legitimately mentions `<question-form>` in chat keeps their
message intact).
2. The OVERRIDE block on form-answered turns was 4 lines and only
banned the bare `<question-form>` tag — models still emitted the
fenced JSON, form-asking prose ("Got it — tell me the following"),
and fake system events ("subagents stopped"). The new
`FORM_ANSWERED_SYSTEM_OVERRIDE` enumerates each anti-pattern and
pins them via tests, so silently weakening any line reintroduces
the regression.
Also adds RuntimeAgentDef.resumesSessionViaCli + RuntimeContext.
hasPriorAssistantTurn as forward-looking abstractions (skipTranscript
option on composeChatUserRequestForAgent). Antigravity does NOT opt
in — agy's `-c` resume activates an internal agentic loop with tool
retries and fallback-to-cached-response on tool errors that the OD
system prompt cannot steer; reverted after seeing byte-identical
form re-emissions caused by agy's own retry logic, not OD's transcript.
## One-click OAuth via system terminal
agy print mode can't complete Google Sign-In on its own (the OAuth
callback page asks the user to paste an auth code back into agy, but
`-p` has no input field). Before this commit the auth banner only
told the user to "open a terminal yourself."
Adds `POST /api/agents/antigravity/oauth-launch` and a cross-platform
launcher in `runtimes/terminal-launch.ts`:
- macOS: osascript → Terminal.app `do script "agy"` + activate
- Linux: tries x-terminal-emulator, gnome-terminal, konsole,
xfce4-terminal, xterm in order
- Windows: `cmd /c start "Open Design" cmd /k agy`
The endpoint hardcodes the `agy` command (no user input → no shell
injection surface) and is loopback-gated like the other daemon
endpoints. The chat's `AGENT_AUTH_REQUIRED` banner now renders a
"Sign in via terminal" button next to Retry; clicking it spawns the
terminal so the user can finish OAuth in one click.
## Silent-failure classification (auth vs quota via --log-file)
agy print mode is silent on stdout/stderr for both missing-OAuth AND
quota-exhausted failures — the upstream
`RESOURCE_EXHAUSTED (code 429): Individual quota reached` and the
`not logged into Antigravity` line only surface in agy's
`--log-file`. Without log inspection the daemon misread quota as
"auth required" and showed the wrong banner.
`RuntimeContext.agentLogFilePath` carries a daemon-owned per-run temp
path that antigravity's buildArgs translates to `--log-file <path>`.
The empty-output guard now reads that log on a `code === 0 &&
!childStdoutSeen` exit, feeds the tail to
`classifyAgentServiceFailure`, and routes:
- "not logged into Antigravity" → AGENT_AUTH_REQUIRED with
antigravityAuthGuidance
- "RESOURCE_EXHAUSTED" / "quota" / → RATE_LIMITED with
"Individual quota reached" antigravityQuotaGuidance
- none of the above (rare) → fall back to auth guidance
as the most likely cause
Both surface a terminal launcher in the auth banner: auth gets "Sign
in via terminal", quota gets "Switch model in terminal" — same
endpoint, contextual label. The handler is identical (open agy in a
terminal); the user either signs in or uses agy's Switch Model
picker to pick a model with available quota.
## Validation
- `pnpm guard` pass
- `pnpm --filter @open-design/daemon` runtime + telemetry suites:
192 passed, 1 skipped (the 1 pre-existing `task-type` failure on
origin/main is unrelated to this change)
- `pnpm --filter @open-design/web` typecheck pass; sse / amr-guidance
/ AgentIcon suites pass (51 web tests)
- Manual end-to-end on darwin + Gemini 3.5 Flash and GPT-OSS 120B
Medium: turn-1 question-form rendered correctly, turn-2 produced
`<artifact>` with full HTML (3.3KB Modern Minimal design) instead
of re-emitting the form. agy `--log-file` content correctly
classified as RATE_LIMITED when Gemini Pro quota was exhausted,
and as AGENT_AUTH_REQUIRED when keychain was cleared.
* fix(web/test): align amrAgent fixture with supportsCustomModel contract
The AMR agent definition in the daemon ships `supportsCustomModel: false`
so the Settings model picker hides the free-text "Custom…" option. The
PR changed `allowCustomModel` from `selected.id !== 'amr'` (hardcoded)
to `selected.supportsCustomModel !== false` (declarative), but the test
fixture was not updated to carry the same field — causing the
`__custom__` sentinel to appear in the picker under test.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* fix(daemon): align formAnswerTransition wording with main + scope build directive to discovery
CI surfaced two failures on the merge with main:
- chat-route.test marks submitted discovery form answers ... expected
the main-version wording 'Do not emit another <formId> form.'
- telemetry-message-finalization keeps non-discovery form answers
active ... expected task-type to fall through the else branch
('Treat these form answers as the active user turn'), not the
discovery RULE 2/RULE 3 build branch.
The colleague's earlier
|
||
|
|
08c350fb0f |
fix(analytics): bucket feedback agent/model directly on the event (#3240)
* fix(analytics): bucket feedback agent/model directly on the event
Reason × agent / reason × model splits on
`assistant_feedback_reason_submit` were 25-74% `unknown` because the
event only carried `run_id` — analyses had to join back to
`run_created/run_finished`, which loses rows whenever the feedback is
given to a message whose run sits outside the query window (the common
case for feedback on older messages), and whose `model_id` was `null`
to begin with (the user didn't pick a specific model — went with the
agent's default).
Carry `agent_provider_id` and `model_id` directly on every feedback
event so the analyses no longer need to join. Replace `null/unknown`
with the `default` bucket via `modelIdForTracking` (and let
`agentIdToTracking` fall through to `other`) at every emit site —
`null` was an analyst-hostile mix of "no selection" and "join failed";
`default` is a real, analysable bucket. On `run_finished`, upgrade the
model to the agent-reported value from initializing/model status
events when the user did not pick one — covers ACP, claude-stream,
copilot-stream, json-event-stream, qoder, pi-rpc.
* fix(analytics): use feedbackAgentProviderIdToTracking and assistantFeedbackModelId for feedback events
Wire API-mode agent ids (anthropic-api → anthropic) and agentName-parsed
model ids through the feedback emit path. Previously the feedback props used
agentIdToTracking (no anthropic-api case) and assistantModelDetail (no
agentName fallback), causing model_id='default' and agent_provider_id='other'
for API-mode agents.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* fix(analytics): extend feedback/run schema for full agent/model coverage
Layered on top of the conflict resolution and the v1 emit switchover
in
|
||
|
|
b8cdf5f0ea |
feat(mcp): generation loop + one-click Codex install (#3141)
* feat(mcp): add project creation, capability discovery, and generation tools
Lets an external coding agent (Codex, Cursor, …) drive a full design
loop over `od mcp`, not just read/write files: create a project,
discover what Open Design can make, commission a generation run, poll
it, and open the result in a browser. Complements the existing
write_file / delete_file / delete_project management tools.
New tools:
- create_project — make an empty project to generate into (start_run
needs one). Derives a slug id from the name unless given.
- list_skills / list_plugins — discover what you can ask OD to make.
- start_run / get_run / cancel_run — commission a run (OD spawns its
own agent), poll to completion, cancel. start+poll because MCP is
request/response and generation is minutes-long.
- get_run / get_project now return a browser-openable previewUrl
(entry file served raw; HTML entries render directly).
The external agent never runs a skill itself — it commissions OD to,
so the prior "skills not on MCP" boundary no longer applies.
* feat(mcp): make get_run preview hint directive
Reword the hint MCP clients receive when a run finishes so the agent
is more likely to surface the previewUrl to the user proactively —
mention the user-facing browser explicitly and call out that clients
with a built-in browser pane (e.g. Codex CLI's right-side browser)
should navigate to it directly. Also nudge start_run's hint to flag
that a previewUrl will arrive on success, so the agent knows what to
do with it before it ever sees get_run.
Pure text change; no behavior change in the tool surface or daemon.
* feat(mcp): one-click Install / Remove for Codex from Settings
Adds a toggle button on Settings → Integrations → Codex panel that runs
`codex mcp add open-design …` / `codex mcp remove open-design` via the
daemon, so users no longer need to copy TOML and paste it into
~/.codex/config.toml by hand. The copy-snippet path is unchanged and
remains the fallback when the Codex CLI isn't on PATH.
The daemon shells out to Codex CLI rather than rewriting config.toml
itself — that way we inherit Codex's own merge / dedupe / validation
rules and only track its argv. The runner is dependency-injected for
testability.
New endpoints (under /api/mcp/install/codex/*):
- GET status — probes `codex mcp get open-design`; returns
{ available, installed } so the UI can render the toggle state.
- POST — runs `codex mcp add open-design --env K=V … -- <node> <cli.js> mcp`,
reusing the same payload as /api/mcp/install-info.
- DELETE — runs `codex mcp remove open-design`.
The web UI renders the toggle only inside the Codex client panel
(`client.id === 'codex'`). When Codex CLI is missing it shows a
disabled button with an explanatory hint instead of vanishing, so users
know why one-click isn't available.
* feat(mcp): teach agents to clarify ambiguous format requests
When the user asks for a "PPT" / "deck" / "slides" / "PDF" / "doc",
that's two very different deliverables: Open Design natively produces
browser-viewable HTML/SVG (including HTML-rendered decks), but the
user may actually want a binary .pptx / .docx / .pdf — which OD does
NOT produce and which the agent would have to export from OD's output
itself. Add a paragraph to the MCP server instructions telling the
agent to ASK which one is wanted before kicking off work, rather than
silently picking one or dual-tracking both paths.
Pure prompt-text change in the instructions block; no tool surface or
behavior change. Costs ~10 lines of session-init context (one-time
per MCP session), versus dual-tracked .pptx hedging Codex was
otherwise doing on every ambiguous request.
* feat(mcp): surface agent messages, skip OD discovery, slim list_plugins
Three fixes uncovered while exercising the full MCP-driven generation
loop end-to-end with a real Codex client. Each one is a real
blocker / footgun for the external agent.
1. get_run now includes agentMessage — the inner agent's textual
output reassembled from the SSE event stream. Without this, runs
that ended in a discovery-style clarifying question (e.g. a
<question-form>) looked like "succeeded with empty output" mysteries
to the outer agent. The hint now branches on whether previewUrl
exists: with preview = show preview + relay agentMessage as the
inner agent's note; no preview = relay agentMessage as the actual
deliverable (almost always a clarifying question).
2. create_project sets skipDiscoveryBrief:true by default. The outer
agent IS the user-facing surface for MCP-driven runs, so OD's own
interactive discovery stage just creates a confusing
nested-clarification loop where its question form ends up dropped
(no files = no artifact). Better to let the outer agent gather
requirements and pass a precise prompt or plugin to start_run.
3. list_plugins flattens the daemon's bulky 16-field plugin record
(fsPath, sourceMarketplaceId, installedAt, …) into the few fields
an agent actually picks plugins on: id, title, description, kind,
tags. description / kind come from manifest.description /
manifest.od.{taskKind,kind} which the previous pass-through dropped
on the floor.
* feat(mcp): smart entry fallback + list_agents
Two fixes uncovered by exercising the full Codex-driven loop on a real
machine. Both close the gap between "Open Design has the data" and
"the external agent can find it".
1. get_project / get_run now fall back to scanning the project's file
list when metadata.entryFile is missing. We hit the case where
write_file (and a half-finished inner-agent run) put a perfectly
viewable index.html into the project, but metadata.entryFile stayed
null — so the outer agent got no previewUrl from MCP and resorted
to guessing a file:// path. Priority: declared entryFile, then
index.html anywhere, then a single .html at the project root.
Pure read-side change; no extra fetch when entryFile is already
set.
2. list_agents lets the outer agent stop guessing 'claude' / 'codex' /
'gemini' for start_run.agent. The daemon already exposed
/api/agents with 19 supported CLIs and an `available` flag. The
MCP wrapper defaults to filtering to installed agents only (so the
agent never picks one whose binary won't spawn), with
includeUnavailable:true as an opt-in to see uninstalled ones plus
their installUrl. Models truncated to 10 with modelsCount carrying
the real total — keeps the response token-economical even for
agents (opencode) with 100+ models.
* feat(mcp): tell the outer agent runs take 5–30 min, don't bypass
Direct response to a real Codex client observably cancelling an
in-flight run after 3 polls and substituting its own write_file
output ("文件时间戳没推进 → 我直接覆盖生成") — exactly the failure
mode this MCP surface exists to avoid.
start_run's hint and the session-init instructions block now both
state explicitly:
- Runs typically take 5–30 minutes.
- status:running with unchanged file mtimes is the inner agent
thinking, NOT a hang.
- Do not cancel_run out of impatience.
- Do not substitute write_file as a "faster" workaround — that
discards OD's pipeline-driven design quality.
- Poll every 30–60 seconds; report "still working" to the user
between polls.
- Only call cancel_run if the user explicitly asks.
Pure prompt-text change; no surface or behavior change. Costs ~10
lines of one-time session-init tokens + ~80 more tokens per
start_run response, in exchange for the outer agent actually
trusting the run.
* feat(mcp): persist run events to disk + expose tail-able path
Closes the in-flight visibility gap that made real Codex clients
cancel a 24-min run after 3 polls and substitute their own
write_file output, simply because polling get_run showed no change.
Daemon: every SSE event is now mirrored to a JSON-Lines file at
<RUNTIME_DATA_DIR>/runs/<runId>/events.jsonl. The path is wired
through createChatRunService's new `runsLogDir` option (null
disables, preserving legacy in-memory-only behavior). statusBody
exposes the path as `eventsLogPath`. Failures are best-effort — a
broken stream destroys itself and the run keeps going on the
in-memory event log (SSE clients are unaffected).
MCP: get_run already passed statusBody through, so eventsLogPath
surfaces automatically. The new value is that get_run during a
running status now adds a directive hint telling the outer agent to
`tail -n 50 -f <path>` in its own shell to see live progress —
that's the signal that makes the agent trust the run and stop
cancelling. The succeeded-status hint mentions the path too, for
forensics. No new tool; the field rides existing get_run polls.
Spec-first throughout:
- runs.test.ts adds 4 tests covering write-per-emit, statusBody
field, null-runsLogDir back-compat, and the no-IO guarantee
when persistence is disabled.
- mcp-runs.test.ts adds 1 test for the running-status hint.
* fix(mcp): get_run hint directs callers to pass project explicitly
The success hint in get_run previously said "project defaults to this
run's project", which is misleading: get_artifact has no run context and
falls back to /api/active when project is omitted, not to the run's
project. A client following the old guidance after creating a fresh or
non-active project could fetch the wrong project's files or fail with
"no active project".
The hint now embeds the run's projectId and tells callers to pass it
explicitly: get_artifact({ project: "<id>" }). A focused regression test
in mcp-runs.test.ts verifies the hint contains the projectId and does
not contain the incorrect active-context fallback guidance.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* fix(contracts): add eventsLogPath to ChatRunStatusResponse
The daemon's statusBody() returns eventsLogPath but the shared DTO
lacked this field, leaving web/CLI/MCP callers without a typed
accessor.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* feat(mcp): bind MCP runs to OD conversations + studio deep links
Closes the last gap that made MCP-driven runs feel like a parallel
side door: the user could not see the conversation in OD's studio
page even though the run was real, finished, and had files.
Daemon side: POST /api/runs now falls back to the project's default
conversation when the caller (MCP / SDK) only supplied projectId.
It synthesizes an assistantMessageId, writes a user message with the
prompt as content, and lets the existing
`pinAssistantMessageOnRunCreate` helper create the empty assistant
row. The existing `appendMessageAgentEvent` accumulation path then
streams text_delta events into the assistant row's content — same
as the web /api/chat flow. The response body now echoes the
resolved conversationId + assistantMessageId so MCP callers can
build a deep link.
`buildMcpInstallPayload` now also surfaces `webBaseUrl` (read from
OD_WEB_PORT, the env tools-dev exports for the web listener). MCP
clients use it to build studio deep links.
MCP side: `start_run`, `get_run`, `get_project` now return a
`studioUrl` — a browser-facing OD URL pointing at the studio page
that shows the file preview AND the chat history side by side. The
hint on each tool was updated to tell the outer agent to hand
studioUrl to the user as the primary link (previewUrl falls back to
raw-file when the user only wants the rendered output). The
webBaseUrl is fetched once via /api/mcp/install-info and cached for
5s to keep per-poll cost flat; a tiny `_resetWebBaseUrlCache` export
lets tests start each case with a clean cache.
Contracts: `ChatRunCreateResponse` gains optional conversationId +
assistantMessageId; `ChatRunStatusResponse` gains optional
eventsLogPath. Both additive, no consumer breakage.
Spec-first throughout:
- get_run includes studioUrl on success when webBaseUrl + conversationId are available
- get_run omits studioUrl when webBaseUrl is null
- start_run returns studioUrl and conversationId for the new run
- get_project returns studioUrl using the project default conversation
* fix(mcp): add skill/skillId to start_run so listed skills are actionable
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* fix(test): update mcp-get-project test to handle getWebBaseUrl fetch
The get_project handler now calls getWebBaseUrl (added with the studio
deep-link feature), which fetches /api/mcp/install-info. The test mock
only handled the /api/projects/:id URL and expected a single fetch call,
causing the assertion to fail with "called 2 times" instead of 1.
Fix: handle the /api/mcp/install-info URL in the fetch mock (returning
webBaseUrl: null), update the call count expectation to 2, and call
_resetWebBaseUrlCache in afterEach to prevent cache bleed between tests.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
* feat(mcp): tell agents to render studioUrl as a clickable markdown link
Observed in a real Codex client: Codex received studioUrl correctly
but rendered it as inline code (gray code-span), which its built-in
browser pane does NOT make clickable. The user had to copy-paste the
URL into a browser by hand even though Codex / Cursor / Zed all
auto-link markdown `[label](url)` syntax and would navigate it in
their right-side preview pane.
The three studioUrl-mentioning hints now explicitly tell the agent
to render the URL as a markdown link (e.g.
`[Open Open Design studio](URL)`) and never as inline code or bare
text. Pure prompt-text change.
* fix(runs): resolve default agent when MCP caller omits agentId; add McpRunCreateRequest contract type
- POST /api/runs: when no agentId is provided, resolve from app-config
or first available CLI before spawning — mirrors the pattern the
routine handler already uses. Prevents 'unknown agent: undefined'
failures on the create_project -> start_run(prompt) MCP path.
- packages/contracts: add McpRunCreateRequest interface for the
projectId-only / SDK caller shape so typed callers can construct the
request without casts. Exported via index.ts's existing chat re-export.
- packages/contracts/tests: add compile fixture verifying projectId-only,
projectId+message, and projectId+message+agentId shapes all type-check.
- apps/daemon/tests: add mcp-runs test asserting agent arg omitted in
start_run does not include agentId in the POSTed body.
Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code)
|
||
|
|
50f85b509a |
fix(analytics): fill run and feedback metadata (#3194)
* fix(analytics): fill run and feedback metadata * fix(analytics): map feedback API providers |
||
|
|
c847ace554 |
Add run-scoped media execution policy (#3106)
* feat(contracts): add run media execution policy * feat(daemon): enforce run media execution policy * test(daemon): cover media execution policy gates |
||
|
|
9a4816b101 |
feat(plugins): site-wide plugin detail pages, share-to-site links, landing deploy trigger (#2999)
* feat(plugins): site-wide plugin detail pages, share-to-site links, landing deploy trigger Why: a merged plugin PR didn't redeploy the landing site (plugins/** was missing from the deploy paths), and the desktop Share menu copied a local/404 link instead of the public marketplace URL. The landing plugin routing left by the detail-page rework also 404'd: the locale listing's cards used a multi-segment href while detail pages were single-segment, and only 388 bundled _official plugins had pages. What changed: - Deploy: landing-page deploy/ci trigger on plugins/**, and skip the slow previews step on an exact cache hit (cache key aligned across both workflows so a PR-built cache is reused by main). - Share URL: packages/contracts/plugin-url.ts owns the single-segment plugin URL scheme; the web Share menu and the landing site both derive links from it. Web links now point at https://open-design.ai/plugins/<slug>/. - Full detail coverage: detail pages now cover all 403 local plugins (_official incl. atoms + community), each rendered from its local manifest. Fixes the locale-listing 404s and the community manifest-name/catalog-id (- vs /) mismatch. - Self-host: daemon exposes OD_SITE_ORIGIN via /api/app-config; web falls back to the canonical origin until the daemon answers. Validation: pnpm guard, pnpm typecheck (all packages), contracts + web tests green, and a full build E2E confirming all 403 catalog ids and locale-listing cards resolve to built detail pages (0 missing). * chore: retrigger CI * ci(landing): carry plugins/** trigger + previews cache-hit into #2994 split workflows Merged origin/main, which split landing deploy into staging + manual production (#2994). git auto-migrated my landing-page-deploy.yml changes into landing-page-staging.yml via rename detection (plugins/** path, fallback-preview-card.ts cache key, cache-hit skip all carried). The new manual landing-page-production.yml didn't have them, so add the previews cache-key alignment + cache-hit skip there too (plugins/** path is N/A — production is workflow_dispatch only). * fix(ci): wrangler-action uses pnpm so it tolerates landing's workspace dep This PR added @open-design/contracts (workspace:*) to apps/landing-page/package.json so the landing site can share the plugin-url slug rules. But the landing deploy/preview steps run cloudflare/wrangler-action with packageManager: npm in workingDirectory apps/landing-page, and 'npm i wrangler' chokes on the workspace: protocol (EUNSUPPORTEDPROTOCOL), failing 'Validate landing page'. Switch all three landing wrangler-action steps (staging / ci preview / production) to packageManager: pnpm, which is workspace-aware. * test(e2e): bundled plugins now offer the README badge After this branch, buildPluginShareUrl returns a public open-design.ai link for bundled plugins (not just official-marketplace ones), so the home-starter share menu now shows 'Copy README badge'. Update the assertion from toHaveCount(0) to toBeVisible(). * fix(landing): drop @open-design/contracts dep, use a landing-local slug helper Per review on #2999: the marketing site must not import @open-design/contracts (AGENTS.md boundary — it's the web/daemon product-runtime contract layer). Move the slug/path helpers into landing-local app/_lib/plugin-slug.ts; the web client keeps contracts' plugin-url. The two derive the same scheme and are verified in lockstep by the e2e route check (403 share URLs -> 403 detail pages, 0 missing). landing no longer has a workspace dep, so revert the wrangler-action packageManager back to npm. * fix(landing): include plugins/_official in previews cache key Per review on #2999: generate-previews.ts builds bundled-plugin preview jobs from plugins/_official/**/open-design.json and renders fallback cards from manifest fields (title/description/mode/scenario/tags). With plugins/** now triggering the workflow but the cache key not hashing plugin inputs, a plugin-only PR/merge could exact-hit an old cache and skip the preview regen, shipping with a stale or missing /previews/plugins/<manifest-id>.png. Add plugins/_official/** to the cache key in all three landing workflows (ci, staging, production). community is not currently covered by generate-previews so its glob is omitted. * fix(plugins): include community marketplace installs in share gate hasPublicPage now covers sourceMarketplaceId === 'community' so the README badge and public detail link surface for community installs. Community manifest names carry a community- prefix that diverges from the landing-page route slug, so URL derivation uses sourceMarketplaceEntryName (community/<folder>) instead — pluginDetailSlug takes the last segment, matching the /plugins/<folder>/ route the landing page emits. Adds component tests for buildPluginShareUrl, badge copy, and the Open-in-marketplace link for a community/registry-starter record. Generated-By: looper 0.9.2 (runner=fixer, agent=claude-code) --------- Co-authored-by: mrcfps <mrc@powerformer.com> |
||
|
|
df8a0faff6 |
feat(runtimes): register AMR (vela) as an ACP stdio agent (#2355)
* feat(runtimes): register AMR (vela) as an ACP stdio agent
AMR is the vela CLI's ACP runtime mode. `vela agent run --runtime opencode`
speaks ACP JSON-RPC over stdio (see vela's
`specs/current/runtime/manual-agent-run-openrouter.md`); per
`docs/new-agent-runtime-acp.md` we expose it through the same `streamFormat:
'acp-json-rpc'` transport that already powers Hermes, Devin, Kimi, etc.
The new `defs/amr.ts` is the entire wiring — `buildArgs` returns
`['agent', 'run', '--runtime', 'opencode']`, `fetchModels` reuses
`detectAcpModels`, and the fallback list seeds the OpenRouter ids vela's
e2e baseline uses. `executables.ts`/`app-config.ts`/`metadata.ts` get the
matching `VELA_BIN`/`VELA_LINK_URL`/`VELA_RUNTIME_KEY`/`VELA_OPENCODE_BIN`
allowlist + install/docs URLs, so users can configure the per-agent env in
Settings without leaking into other adapters.
Coverage: `tests/fixtures/fake-vela.mjs` is a minimal ACP stub that returns
the documented `initialize` / `session/new` / `session/set_model` /
`session/prompt` shapes; `tests/amr-acp-integration.test.ts` spawns it via
`child_process.spawn` and drives a full turn through `attachAcpSession` and
`detectAcpModels`, so the ACP transport contract for AMR is end-to-end
verified locally even before a real `vela` binary is installed.
Validated:
- pnpm guard
- pnpm typecheck (all workspace projects)
- pnpm --filter @open-design/daemon test (2881/2881)
Deferred: real OpenRouter-backed turn through a built `vela` binary —
the runtime def needs no changes for that path, only `VELA_RUNTIME_KEY`
and `VELA_LINK_URL` in env (or Settings).
* fix(runtimes/amr): pin a concrete default model and bare openai ids
End-to-end validation against a freshly-built `vela` (nexu-io/vela@main)
+ OpenRouter surfaced two contract details the first AMR runtime def
got wrong:
1. vela rejects `session/prompt` with `session/set_model must be called
before session/prompt`. attachAcpSession in apps/daemon/src/acp.ts
skips set_model whenever the picked model is the synthetic 'default'
id, so AMR's fallback list must NOT include DEFAULT_MODEL_OPTION. The
def now ships a concrete `gpt-5.4-mini` as both `fetchModels`'
default option and `fallbackModels[0]`, which makes attachAcpSession
always send a real `session/set_model` for AMR turns.
2. `vela --runtime opencode` auto-prepends `openai/` to whatever modelId
it forwards to opencode's openai provider. With OpenRouter-style ids
like `openai/gpt-5.4-mini`, opencode receives the double-prefixed
`openai/openai/gpt-5.4-mini` and replies `ProviderModelNotFoundError`.
The new fallback list ships the bare ids opencode's openai registry
actually knows about (gpt-5.4, gpt-5.4-mini, gpt-5.4-fast, etc.).
Stub + tests:
- tests/fixtures/fake-vela.mjs now enforces the set_model gate the same
way real vela does, so a regression that silently goes back to
model: 'default' would surface as a fatal error in tests instead of a
hidden production failure.
- tests/amr-acp-integration.test.ts pins both contracts: no 'default' /
no 'openai/' prefix in fallbackModels, and a negative case that
asserts session/prompt fails when no model is set.
Adds `apps/daemon/scripts/verify-amr-real-vela.mjs` — a small dev-time
runner that drives `attachAcpSession` against a real `vela` binary and
prints the daemon's chat events, so future protocol drift can be checked
against an actual OpenRouter call.
Verified locally: `vela agent run --runtime opencode` + OpenRouter
returns the prompted string ("AMR-E2E-PASS") through the full daemon
pipeline; daemon test suite stays 2883/2883.
* fix(runtimes/amr): substitute concrete model when chat run sends 'default'
A plugin-driven AMR run from the UI surfaced a real-world hole in the
prior commit:
json-rpc id 3: session/set_model must be called before session/prompt
The Default-design-router plugin (and any caller that doesn't pin a
real model) sends `model: 'default'` straight through, which the AMR
runtime def cannot accept — vela rejects `session/prompt` without
`session/set_model` and attachAcpSession skips set_model whenever
model === 'default'. Just leaving DEFAULT_MODEL_OPTION out of the
adapter's `fallbackModels` is not enough: the chat-run handler in
server.ts still forwarded 'default' verbatim.
This adds `resolveModelForAgent(def, resolved, env?)` as the
single source of truth for the substitution:
1. If the caller picked a real id, pass it through.
2. Else, if `def.defaultModelEnvVar` is set and the daemon process
env has a non-empty value for it, return that (operator escape
hatch — see below).
3. Else, if the def's `fallbackModels` does NOT contain a 'default'
id, return `fallbackModels[0].id`.
4. Else, return the original value (the historic shape — defs that
list 'default' themselves are untouched).
AMR sets `defaultModelEnvVar: 'VELA_DEFAULT_MODEL'`, so when
opencode's openai-provider registry deprecates `gpt-5.4-mini`
upstream, an operator can swap the fallback id without a code change
by exporting `VELA_DEFAULT_MODEL=gpt-5.5` before launching tools-dev
/ od. Worth noting the env var must live in the daemon's `process.env`
(Settings-UI per-agent env values only reach the spawned child, not
the daemon's resolver) — the new field's docblock spells this out.
Coverage:
- `tests/runtimes/resolve-model.test.ts` — 8 unit tests covering all
four resolver branches plus the env-override happy path / fallback /
ignore-when-user-picked-a-real-id case.
- `pnpm --filter @open-design/daemon typecheck` clean.
* chore(runtimes/amr): move AMR to the top of the base agent list
So `AMR (vela)` shows up first in the agent picker / status views,
ahead of claude / codex. Pure ordering change; no behavior delta.
* feat(amr): Sign-in / Sign-out button on the AMR Settings card
The first half of the AMR work assumed the operator would set
VELA_RUNTIME_KEY / VELA_LINK_URL on the daemon process and never
surfaced login state to users. This adds the missing UX so a fresh
install can drive the full path from Settings:
- GET /api/integrations/vela/status reads ~/.vela/config.json
for the active profile and returns { loggedIn, profile, user }
(without leaking the runtime/control keys themselves).
- POST /api/integrations/vela/login spawns `vela login` once
(409 if one is already in flight). The vela CLI opens the user's
browser to the device-authorization page itself — Open Design
only needs to kick the subprocess off.
- POST /api/integrations/vela/logout removes ~/.vela/config.json
so the next status read returns logged-out.
`AmrAgentCard` is a dedicated agent-card component for AMR because
the existing `<button>` row can't host an interactive sub-control
(nested interactive elements). It polls /status after a login click
until the daemon reports loggedIn=true (or 5 minutes elapse), and
exposes a Sign-out action on hover. Other adapters (claude, codex,
hermes, …) keep their existing `<button>` card.
i18n: 8 new keys (settings.amrLogin / Logout / LoggingIn / etc.)
added to en + zh-CN. Other locales spread `en` and inherit the
English copy until translations land.
Coverage:
- `tests/integrations/vela.test.ts` pins the config.json reader
against a tmp HOME — including the negative case where a profile
has user info but no runtimeKey (still logged-out), and the
secret-leak guard ("rt-secret-*" must not appear in the projection
payload).
- `tests/components/AmrAgentCard.test.tsx` covers all four UI
states (logged-out, logging-in, logged-in, logging-out) plus the
click-propagation invariant the divergent card was built to keep.
`pnpm --filter @open-design/daemon test` 2901 / 2901 passing.
`pnpm --filter @open-design/web test` 1719 / 1719 passing.
`pnpm typecheck` + `pnpm guard` clean.
Dev script side-effects: `apps/daemon/scripts/verify-amr-real-vela.mjs`
no longer requires both VELA_RUNTIME_KEY and VELA_LINK_URL — if
VELA_PROFILE is set, the vela CLI is allowed to resolve credentials
from `~/.vela/config.json`. Added the two AMR `.mjs` fixtures to
`scripts/guard.ts` allowlist with the executable-fixture / dev-runner
rationale.
* fix(connection-test): substitute model for AMR before attachAcpSession
The chat-run path in server.ts already routes the requested model through
`resolveModelForAgent` so AMR / vela (whose CLI demands an explicit
`session/set_model` before `session/prompt`) gets the def's first
concrete fallback id when the chat run ships `model: 'default'`.
`connectionTest.ts` was wiring `attachAcpSession({ ..., model: model ?? null })`
directly, which made the Test Connection button on the AMR Settings
card deadlock with the same `session/set_model must be called before
session/prompt` error the chat-run path already handles — surfaced as a
permanent "Testing connection…" spinner in the UI.
Reuse the same helper here so Test Connection mirrors chat-run behavior.
* test(amr): three-layer end-to-end coverage for the AMR login + turn flow
The PR up to this point shipped runtime + UI code with unit-level Vitest
coverage. This commit adds the cross-layer regression net the live demo
relied on:
1. apps/daemon/tests/integrations/vela.routes.test.ts (HTTP, Vitest)
Spins up the real daemon Express app via `startServer({port:0,...})`,
persists `agentCliEnv.amr.VELA_BIN = <fake>` into app-config.json,
and exercises every /api/integrations/vela/* endpoint against the
extended fake-vela stub:
- status reads ~/.vela/config.json under various states
- login spawns the fake, waits for config.json to appear, returns
pid + startedAt + profile
- 409 already-running guard with the stub's delay knob
- logout removes the file (idempotent)
- secrets (runtimeKey / controlKey) never leak in the projection
- login → status round-trip flips loggedIn=false → true
2. e2e/tests/amr/turn.test.ts (tools-dev orchestrated, Vitest)
Boots a namespaced daemon + web pair through `createSmokeSuite`,
inlines a self-contained fake `vela` binary that handles BOTH
`vela login` (writes ~/.vela/config.json) and
`vela agent run --runtime opencode` (ACP stdio with the
`session/set_model must precede session/prompt` gate the real binary
enforces), then drives a complete /api/runs lifecycle for
`agentId: 'amr', model: 'default'` and asserts the assistant message
captures the fake's streamed text. This is the test that would have
surfaced today's plugin-default-model regression (the `set_model
before prompt` error) at PR time instead of demo time.
3. e2e/ui/amr-login-pill.test.ts (Playwright)
Mocks /api/agents + /api/integrations/vela/{status,login,logout}
to drive the Settings AMR card through the full Sign in → Signed in
→ Sign out cycle. Pins the AmrLoginPill polling contract and the
aria-label semantics (the pill's accessible name is "Sign out" once
logged in, regardless of which label the hover-state text shows).
fake-vela.mjs extensions:
- Handles `vela login` argv by writing
~/.vela/config.json for the active VELA_PROFILE and exiting 0 —
mirrors real vela's on-disk side-effect without the device-auth
loop.
- FAKE_VELA_LOGIN_DELAY_MS knob so route tests can observe the
in-flight state of the spawn lifecycle.
- FAKE_VELA_LOGIN_USER_EMAIL / _USER_PLAN to assert the surfaced
user fields end-to-end.
Validated:
- `pnpm guard` + `pnpm typecheck` (all workspace projects)
- `pnpm --filter @open-design/daemon test`: 2998 / 2998 passing,
including the new 8-test integration suite.
- `cd e2e && pnpm test tests/amr`: 1 / 1 passing.
- `cd e2e && pnpm exec playwright test ui/amr-login-pill.test.ts`:
1 / 1 passing (6.7s).
* feat(amr): package native cli and refine login ui
* feat(amr): wire vela cli beta packaging
* docs(amr): document vela ci packaging review
* docs(amr): refine vela ci integration review
* fix(ci): refresh nix pnpm dependency hashes
* fix(pack): clean up Vela CLI packaging
* fix(pack): bundle Vela CLI support files
* fix(amr): recover login attempts from stale auth state
* test: expand AMR and automations coverage
* fix(amr): address review follow-ups
* test(web): align tasks fixtures with contracts
* fix(daemon): type wildcard route params
* fix(ci): refresh PR merge validation
* fix(amr): clear env credentials on logout
* feat(settings): inline local CLI model configuration
* fix(amr): recognize daemon env credentials
* [codex] Fix Vela companion packaging (#2979)
* Fix Vela companion packaging
* Update Nix pnpm dependency hashes
* [codex] Surface AMR account failures (#2980)
* fix: surface AMR account failures
* fix: cover AMR recovery error guidance
* chore: bump beta base version to 0.8.1 (#2990)
* Fix AMR profile and packaged runtime review issues
* Detect packaged AMR OpenCode companion tree
* feat(web): polish AMR frontend flows
* Polish AMR onboarding card
* fix: read AMR login state from dot-amr config (#3048)
* test: tighten AMR credential and packaging coverage
* test: restore AMR executable test env helper
* [codex] Fix packaged mac Dock identity and AMR label (#3076)
* Fix packaged mac sidecar Dock identity
* Rename AMR assistant label
* Fix AMR live models and dot-amr login state (#3073)
* fix: read AMR login state from dot-amr config
* fix: load live AMR models before runs
* fix: point AMR onboarding link to production wallet
* fix: address AMR model review feedback
* fix: persist live AMR model fallback
* [codex] Fix AMR link catalog model ids (#3088)
* Fix packaged mac sidecar Dock identity
* Rename AMR assistant label
* Fix AMR link catalog model ids
* Fix AMR model normalization typecheck
* Use live AMR model for default runs
* fix: polish AMR runtime settings UI
* Accelerate AMR startup defaults (#3092)
* Surface AMR insufficient balance wallet URL (#3099)
* fix(web): polish onboarding controls (#3112)
* fix(web): show CLI scan loading state
* Avoid duplicate AMR wallet recharge links (#3117)
* Avoid duplicate AMR wallet recharge links
* Use Vela CLI 0.0.3 test package
* chore(nix): refresh pnpm deps hash
* Fix AMR wallet guidance display
---------
Co-authored-by: open-design-bot[bot] <282769551+open-design-bot[bot]@users.noreply.github.com>
* chore(pack): pin Vela CLI 0.0.3-test.1 (#3127)
* chore(nix): refresh pnpm deps hash
* chore(pack): pin Vela CLI 0.0.3
* chore(nix): refresh pnpm deps hash
* fix(web): suppress AMR exit 130 fallback (#3136)
* feat(web): nudge users to hosted AMR on model/auth/quota failures (#3083)
* feat(web): nudge users to hosted AMR on model/auth/quota failures
When a non-AMR agent run fails with an auth / quota / upstream model
error, surface an inline nudge under the error pill linking to Open
Design's hosted AMR gateway (https://open-design.ai/amr). The nudge
fires `surface_view` (element=run_failed_toast) on impression and
`ui_click` (element=go_amr) on the link.
Also teach the daemon to classify CLI-agent auth/quota/upstream failures
(Claude Code, codex, ...) into specific API error codes
(AGENT_AUTH_REQUIRED / RATE_LIMITED / UPSTREAM_UNAVAILABLE) instead of
the generic AGENT_EXECUTION_FAILED, so both the error message and the
nudge key off accurate codes. AMR's own runs are excluded from the
nudge — they keep the dedicated sign-in / recharge affordances.
* feat(web): rework failed-run AMR guidance into per-case error UI
Replace the single inline nudge with a per-case failed-run experience
driven by the run's error code + agent:
- The error card is now neutral gray (was red) and always carries a
retry button; it is driven by the persisted per-message error event so
it survives a reload.
- Non-AMR agent hitting a model/auth/quota wall: a theme-color promotion
card under the error card offers "switch to AMR & retry" — switches the
run to AMR, opens Settings on the AMR card, and auto-retries once the
account signs in (ProjectView polls vela login status, independent of
the Settings pill lifecycle, with success / 5-min-timeout / unmount
exits).
- AMR agent unauthorized: clearer copy + an "authorize & retry" button.
- AMR agent out of balance: clearer copy + a "top up" button to the AMR
wallet, with manual retry.
- Settings AMR card: when opened from the nudge, it scrolls into view and
pulses, and an authorize-button coachmark (a fake hand cursor that
rises in and dismisses on hover) points at the sign-in control when not
yet authorized.
analytics: surface_view (run_failed_toast) on the promotion card and
ui_click (go_amr) on its action are retained. i18n adds chat.amrCard.*
and chat.amrError.* (en / zh-CN / zh-TW translated; other locales fall
back to en) and drops the old chat.amrErrorGuidance keys.
* fix(daemon): require status context for numeric service-failure codes
Per review on #3083: the model-service classifier matched bare HTTP
status numbers (`500`, `502`, `429`, `401`), so ordinary CLI output like
`line 500`, `read 502 bytes`, or `exit code 401` could be misclassified
as a provider outage / auth wall and wrongly surface the AMR nudge. Now
a status number only counts when it carries explicit context (`HTTP 500`,
`status 503`, `code: 401`, `502 Bad Gateway`); textual provider phrases
(overloaded, bad gateway, service unavailable, rate limit, …) are
unchanged. Adds fixtures proving unrelated numeric output stays null.
* fix(web): keep error pill for failed runs ChatPane's card doesn't cover
Per review on #3083: the per-message gray error pill was suppressed for
every persisted error status event, but ChatPane only renders the
replacement top-level error card for `retryableAssistantMessage` (the
last failed assistant). So a failed turn that is no longer last (after a
follow-up) or an older failed run in history showed neither the pill nor
the card — its error detail vanished, undercutting reload/history
survival. ChatPane now passes `errorCardOwnerId` (the assistant id whose
error the card represents); AssistantMessage suppresses only that one
pill and keeps rendering StatusPill for all other error events.
* fix(daemon): don't treat a process exit code as an HTTP status
Follow-up to review on #3083: the status-context helper accepted a bare
`code` prefix, so `exit code 401` / `process exited with code 429` still
matched and got classified as AGENT_AUTH_REQUIRED / RATE_LIMITED (the
very `exit code 401` case the comment calls out as noise). `code` now
only counts when qualified (`status code` / `error code` / `response
code`) or punctuation-bound (`code: 401`); bare `exit code N` no longer
matches. Adds fixtures for exit-code lines returning null.
* chore(web): translate AMR card / error keys for 16 remaining locales
PR #3083 added 10 new `chat.amrCard.*` / `chat.amrError.*` keys but only
provided en/zh-CN/zh-TW translations; the other 16 locales fell back to
English. Translate the card title/body, three chips, primary CTA, and
the AMR self-error (auth / balance) messages and buttons for ar, de,
es-ES, fa, fr, hu, id, it, ja, ko, pl, pt-BR, ru, th, tr, uk.
* fix(amr): address review feedback on #2355
Targeted fixes for the unresolved review threads on #2355. Each fix
includes / updates a focused test.
- runtimes/executables.ts: `packagedVelaOpenCodeCompanionTree` now
verifies the inner `opencode` executable exists + is runnable, not
just the directory. This closes the false-positive availability path
that let `detectAgents()` surface AMR as available even when the
packaged companion was empty / partially copied (mrcfps, 4 threads).
- runtimes/executables.ts: `resolveAmrOpenCodeExecutable` now prefers
the bundled `<OD_RESOURCE_ROOT>/bin/libexec/opencode/opencode` over a
stale `opencode` on the user's PATH, so packaged AMR builds can't be
hijacked by a global installation.
- web/EntryShell.tsx: when the Local CLI scan returns an available
agent and the previously-selected agent is AMR, switch the selection
to the first available local agent so the runtime and persisted
agent agree before Continue.
- server.ts (model-probe branch): for AMR, check `readVelaLoginStatus`
BEFORE rejecting on an empty live-model catalog — a signed-out user
was getting `AMR_MODEL_UNAVAILABLE` ("choose a model") instead of
the correct `AMR_AUTH_REQUIRED` (sign-in affordance).
- server.ts (default model fallback): if the user asked for the AMR
agent default and the cached id is no longer in the FRESH catalog,
fall back to `liveModels[0]` from the probe instead of rejecting the
run as `AMR_MODEL_UNAVAILABLE`.
- integrations/vela.ts: route `vela login` through
`createCommandInvocation` so an npm/Node-style `vela.cmd` / `.bat`
shim on Windows gets the correct `cmd.exe /d /s /c …` wrapping with
verbatim args (matches `execAgentFile` / chat-run spawning).
- tools/pack/src/linux.ts: in containerized Linux builds, bind-mount
the host directory of `OPEN_DESIGN_VELA_CLI_BIN` and rewrite the env
to the container-side path. The host path was being passed in as-is
even though the default container only mounts /project, /tools-pack
and cache/home — `copyOptionalVelaCliBinary` saw a missing path.
Deferred (out of scope for this PR):
- `od amr status/login/logout/cancel` CLI subcommands (AGENTS.md
UI/CLI dual-track rule, server.ts:5763) — sizable surface; tracked
for a separate focused PR.
- Strict `--require-vela-cli` for Windows + mac-x64 beta builds:
prematurely blocked — `@powerformer/vela-cli` only publishes the
`darwin-arm64` platform binary today; adding the flag elsewhere
would fail the builds. Revisit once win/x64/linux binaries ship.
* fix(amr): hoist sendAmrAccountFailure above the AMR catalog preflight (TDZ)
The new signed-out AMR branch in the catalog preflight at server.ts:10875
calls `sendAmrAccountFailure(...)` to emit AMR_AUTH_REQUIRED, but the
const declaration sat ~100 lines below at the outer function scope. Because
`const` is TDZ-aware, that branch would have thrown `ReferenceError:
Cannot access 'sendAmrAccountFailure' before initialization` for the
exact users it tries to help — defeating the original intent.
Hoist the helper to just above the AMR preflight block so it's available
to every AMR code path in this function. Behavior elsewhere is unchanged.
Also rerun the daemon test suite: `launch.test.ts > resolveAgentLaunch
uses packaged built-in Vela for AMR` was creating the
`<resourceRoot>/bin/libexec/opencode/` companion *directory* only, but
this PR's earlier tightening of `packagedVelaOpenCodeCompanionTree`
also requires the inner `opencode` executable. Add it to that fixture
to match the new contract; the test was a sibling of the executables /
env-and-detection fixtures already updated in
|
||
|
|
dadf8a5bc3 |
Add tracking for Automations, Plugin Detail & Loop (#3103)
* Add tracking for Automations, Plugin Detail, and Plugin Loop - RoutinesSection: track new_automation, create, save, cancel, run_now, edit, pause, resume, delete, history button clicks - PluginDetailView: track back navigation and use_plugin action - PluginLoopHome: track clear_active, submit, card_details, card_use - Extend AutomationsClickProps with new CRUD elements - Add PluginDetailClickProps and PluginLoopClickProps contracts * fix: address review comments on plugin/automation tracking - Extract onBack handler in PluginDetailView to cover both error-path and success-path back buttons with tracking - Move create/save tracking from submit button onClick into the form submit handler to capture keyboard submissions and avoid false positives from validation failures * fix: move submit tracking into submit() handler in PluginLoopHome Same fix as RoutinesSection: tracking now fires inside submit() so keyboard Enter submissions are captured and the !trimmed guard prevents false positives. --------- Co-authored-by: qiongyu1999 <2694684348@qq.com> |
||
|
|
1e9be2fdb5 |
Add tracking for Comment panel save/send actions (#3098)
Track "Save comment" and "Send to chat" button clicks in the comment popover with a new `comment_popover` area, so we can measure the distribution of save vs send-to-chat usage. Co-authored-by: qiongyu1999 <2694684348@qq.com> |
||
|
|
170a05f5d2 |
Formalize skill artifacts into plugins (#3085)
* Add skill-to-plugin candidate flow * Fix skill plugin candidate card reuse Generated-By: looper 0.9.1 (runner=fixer, agent=codex) * Fix skill plugin candidate dismiss and URL gates Generated-By: looper 0.9.1 (runner=fixer, agent=codex) * Polish skill plugin candidate copy |
||
|
|
fce444bcab |
Consolidate chat comments preview on main (#2906)
* feat(web): queue chat sends * feat(web): render code comment directives * feat(web): add preview comments and manual edits * fix(web): polish shared chrome controls * fix(web): align queued send loading state * feat(web): open primary project artifacts * fix(web): keep queued sends and tests aligned * fix(web): restore docked comment tools layout * fix(web): align preview comment toolbar * fix(web): place local cli beside handoff * fix(web): move agent menu beside handoff * fix(web): make project instructions a direct header action * fix(web): compact handoff and toolbar labels * fix(web): clarify handoff menu and annotation label * fix(web): restore compact cursor handoff trigger * fix(web): align agent menu trigger with handoff * fix(web): add draw toolbar close action * fix(web): move inspect editing into edit mode * fix(web): avoid reserving comment sidebar in annotation mode * fix(web): float preview comments panel * fix(web): keep edit canvas full width * fix(web): polish preview annotation tools * fix(web): highlight active preview comments * fix(web): open comments panel after annotation save * fix(web): polish comment handoff controls * fix(web): remove palette preview tool * fix(web): simplify draw annotation toolbar * fix(web): restore queued tasks into composer * fix(web): restore queued send strip styling * fix(web): hide internal comment target ids * fix(web): align manual edit panel header * test(web): cover visual interaction contracts * fix(web): address PR feedback regressions * fix(web): preserve artifact chrome state * fix(daemon): restore project raw file routes --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> Co-authored-by: mrcfps <mrc@powerformer.com> |
||
|
|
2bd83b6e23 |
feat(daemon): structured diagnostics for agent connection test results (#2248 PR 1/N) (#2419)
* feat(daemon): attach structured diagnostics to agent connection test results Local agent connection-test failures currently flatten everything into a single free-form `detail` string (e.g. "exit 1"). Settings UI and CLI consumers can't tell what phase failed, which binary the daemon picked, or what the child's exit metadata looked like — they have to scrape the human-readable text. Add an optional `diagnostics` block on the connection-test response so callers can read structured fields instead. The existing `kind` and `detail` strings are kept bit-for-bit identical, so older UIs keep rendering unchanged. - packages/contracts: add `ConnectionTestPhase` (binary_resolution / version_probe / model_list / spawn / connection_smoke_test / output_parse) and a `ConnectionTestDiagnostics` interface with optional `binaryPath`, `binaryVersion`, `exitCode`, `signal`, `stdoutTail`, `stderrTail`; extend `ConnectionTestResponse.diagnostics?` to carry it. - apps/daemon/connectionTest.ts: thread a `phase` tracker through testAgentConnectionInternal, flip it at the meaningful boundaries (binary_resolution → spawn → connection_smoke_test / output_parse), and stamp diagnostics into every result return point — the four result helpers plus both early returns. Tail data already buffered by `createAgentSink` is reused; nothing new is captured. - tests: three regressions per #2248 — success path attaches phase='connection_smoke_test' + exitCode 0, exit-failed path attaches phase='spawn' + the failing exitCode + the stderr tail, and a missing-CLI path attaches an early-phase diagnostics block. This is PR 1 of the #2248 plan (contracts + minimum daemon fill); follow-ups will introduce a normalized failure classifier (binary_not_found, unsupported_version, auth_failed, quota_exceeded, network_failed, unsupported_flags, no_text_output, output_parse_failed, spawn_failed), candidate-alternative reporting via inspectAgentExecutableResolution, and the Settings "View details" disclosure. Refs #2248. * fix(connectionTest): honor diagnostics contract on all local return paths Two follow-ups from review of #2419: - packages/contracts/src/api/connectionTest.ts advertises diagnostics as 'Always set on local agent test responses', but three local returns still bypassed buildDiagnostics(): the buildArgs failure around 1295, the preflight probeAgentAuthStatus().status === 'missing' branch around 1317, and the outer catch around 1566. Thread buildDiagnostics() through all three; phase is still 'binary_resolution' at the first two and whatever the runtime advanced to at the catch. - resultFromAgentText() hard-coded exitCode: 0 even though resultFromChildExit() routes ACP clean-SIGTERM completion through this success helper (winner.code === null, winner.signal === 'SIGTERM' with acpCleanCompletion). Add an optional exit argument threaded from both call sites so the diagnostics reflect the actual child code/signal pair instead of a synthesized 0 that masks the SIGTERM teardown. Only synthesize 0 when no exit context is available (theoretical text-without-exit path). Tests: - regression locking the diagnostics contract for the preflight auth path on Cursor Agent (phase: binary_resolution, binaryPath set) * docs(contracts): widen diagnostics contract to match early-failure paths Reviewer flagged that the JSDoc-style comment on ConnectionTestResponse.diagnostics still said 'Populated only when the test actually spawned an agent CLI', but the previous follow-up made the daemon stamp diagnostics on three pre-spawn local-agent failures too: the unknown-agent and unresolved-binary branches around connectionTest.ts:1123-1148 and the preflight auth return around 1338-1353. Reword the contract so Settings/CLI consumers do not incorrectly special-case those early local failures as diagnostics === undefined. * fix(connectionTest): keep contracts browser-safe and fold probe output into preflight diagnostics Two follow-ups from review of #2419: - ConnectionTestDiagnostics.signal was typed as `NodeJS.Signals | string | null`, which made the generated .d.ts of the shared @open-design/contracts surface depend on ambient Node types. Downstream consumers reading a plain HTTP response shape should not need @types/node. Narrow to `string | null` (NodeJS.Signals literals are strings, so the daemon write site is unchanged) and document the boundary in the field comment. - The Cursor-style preflight auth path stamped diagnostics built from the smoke-test sink, which is always empty at that point because the smoke spawn never happened. As a result the diagnostics block silently dropped `cursor-agent status`'s own stderr/stdout/exit context — the only structured failure information available on that path. Thread the probe output back out of probeAgentAuthStatus() via new optional stdoutTail/stderrTail/exitCode/signal fields, then merge them into the diagnostics overrides in connectionTest.ts so Settings/CLI consumers can render the auth-failure context instead of just the guidance string. Tests: - extended the Cursor preflight regression to assert that diagnostics carries the probe's stderr ("Not logged in") and exit code (1). |
||
|
|
2b7b6590ae |
feat(comments): add comment attachment API (#2869)
* feat(comments): add comment attachment API * ci: add fork PR workflow approval script --------- Co-authored-by: chaoxiaoche <chaoxiaoche@chaoxiaochedeMacBook-Pro.local> |
||
|
|
c14baf07d3 |
Merge origin/main into release/v0.8.0
PR #2461 sync prep — resolves 14 conflicts merging 84 main-side commits on top of 58 release-side commits accumulated during the 0.8.0 cycle. Resolution summary: Take main (theirs) where main carried deliberate forward progress: - apps/web/src/components/PluginCard.tsx — 7 hunks, i18n migration: hardcoded English aria-labels/titles replaced with t() calls keyed on pluginCard.* (all 8 keys verified present in en.ts). - apps/web/src/components/TasksView.tsx — 1 hunk, source-ingestion feature: sortedRoutines (newest-first), sourceIngestionTemplates, patchSourceForm, submitSourceIngestion. activeCount/pausedCount semantics preserved (now keyed on sortedRoutines, count unchanged). - e2e/ui/app.test.ts — new node:fs/promises + tmpdir + path + @/timeouts imports needed by main-side test helpers. - e2e/ui/settings-local-cli-codex-fallback.test.ts — menu-dismissal helper block added by main. Keep both sides where each added a different field to the same object literal: - apps/web/src/components/ProjectView.tsx (locale + analyticsHints spread). - apps/web/src/components/DesignSystemFlow.tsx (locale + analyticsHints). Take release (ours) where release carried deliberate work that ships 0.8.0: - CHANGELOG.md — release-side 0.8.0 entry + PR link refs; main's Unreleased section was the same body of work, now finalized. - apps/landing-page/public/{apple-touch-icon,favicon}.png + apps/web/public/app-icon.svg — release-side visual refresh assets consistent with 0.8.0 stable ship. - tools/pack/src/linux.ts — packageVersion const required by line 466; taking main's empty line would build-error. - e2e/ui/project-management-flows.test.ts + e2e/ui/settings-api-protocol.test.ts + e2e/ui/settings-memory-routines.test.ts — release-side release-smoke hardening (shangxinyu1 + PerishFire) takes precedence on overlap. Closes-issue / unblocks: PR #2461 sync release/v0.8.0 → main. |
||
|
|
715ed04f5d |
fix(prompt): instruct discovery form to follow user's chat language (#2534)
* fix(prompt): instruct discovery form to follow user's chat language The discovery form was reaching users in English even when their UI language was Chinese (#1416). The form is generated by the LLM under guidance from packages/contracts/src/prompts/discovery.ts, but the prompt only mentioned that option labels MAY follow the user's language. The example form embedded English text for title, description, per-question labels, and placeholders, and the LLM copied that text verbatim instead of localizing. Two minimal changes to the prompt: 1. Add a sentence under RULE 1 making the language-match expectation explicit before the example forms. 2. Expand the Form authoring rules bullet so it covers every user-facing string (title, description, label, placeholder, option label) and pins the unlocalized identifiers (id, type, option value, branch values) for the runtime branch logic. Fixes #1416 * fix(prompts): mirror discovery localization rule to daemon prompt copy Apply the same 'Match the user's chat language' paragraph and the expanded 'Localize every user-facing string' bullet to apps/daemon/src/prompts/discovery.ts, which the daemon-backed chat path uses (it imports ./discovery.js, not the contracts copy). Also add apps/daemon/tests/prompts/discovery-localization-drift.test.ts, which reads both prompt copies and asserts each one contains both rules, so the contracts and daemon files cannot silently drift on this behavior. Apply-anyway reason: pnpm install / pnpm vitest could not run locally (registry DNS blocked in sandbox + node v26 vs required v24). Direct Node content assertion over both files passes. CI will run vitest. --------- Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com> |
||
|
|
1b908a8481 |
fix(daemon): restore full assistant turn after mid-flight reload reattach (#2383)
* fix(daemon): restore full assistant turn after mid-flight reload reattach When a daemon run is in progress and the browser reloads, the client reattaches and the artifact recovers, but the restored chat turn drops assistant text, thinking events, and producedFiles. Three independent defects combine to cause this: 1. The reattach onDone never populated producedFiles. The pre-turn file snapshot used as the diff baseline lived only in a closure. Now it is persisted on the assistant message as preTurnFileNames so the reattach path can rebuild the diff after reload. 2. The SSE replay used a strict `>` cursor compare. A client that had already persisted lastRunEventId equal to the final event id received zero replay events on terminal-run reattach, fell into the status-only REST fallback, and never fired a clean onDone. The server now replays the final buffered event on terminal-run reattach when the cursor is at or past the end, so the client always sees a terminal signal. 3. The text buffer flushed on visibilitychange but not on pagehide. Hard reloads on browsers where visibilitychange does not fire before teardown could lose the last ~250ms of streamed text from the persisted message. A pagehide listener now flushes synchronously. Refactor: extracted computeProducedFiles helper so the send and reattach flows share the diff logic and cannot drift apart again. Tests: - apps/web/tests/components/ProjectView.reattach-restore.test.tsx covers: reattach onDone populates producedFiles from preTurnFileNames; reattach reaches succeeded via SSE even when only the end event replays; computeProducedFiles unit cases. - apps/daemon/tests/runs.test.ts adds replay-cursor coverage for both the terminal-replay safety branch and the no-duplicate normal branch. * fix(daemon): persist preTurnFileNames end-to-end on the messages table Review on #2383 caught that `ChatMessage.preTurnFileNames` (added in packages/contracts) had no daemon-side persistence: the messages schema, upsertMessage, and normalizeMessage all ignored the field. saveMessage() would PUT the field, the daemon would silently drop it, and a real page reload would read a row without `preTurnFileNames`, so the reattach onDone fell back to `new Set(nextFiles.map(...))` and still missed files produced earlier in the turn. This commit closes the round trip: - New `pre_turn_file_names_json TEXT` column on the messages table, with a forward-compatible ALTER for existing databases (same pattern as agent_id / feedback_json / run_status). - Both upsertMessage branches (UPDATE and INSERT) now serialize m.preTurnFileNames into the new column. - listMessages, the post-upsert readback SELECT, and normalizeMessage surface the column back to callers. Round-trip tests in apps/daemon/tests/db-pre-turn-file-names.test.ts cover: write+listMessages, the UPDATE upsert path preserving the baseline, and a legacy-row case returning undefined. * fix(web): preserve terminal status + full multi-file diff on reattach Two correctness issues caught in review of the prior reattach commits: 1. The reattach onDone path hard-coded `runStatus: 'succeeded'`, which overwrote a 'failed' or 'canceled' status that the replayed terminal event had already recorded via onRunStatus. Restored messages would come back as success even when the run had actually failed or been canceled. Now derives the final status from `prev.runStatus` via the existing `resolveSucceededRunStatus` helper, mirroring the send path at line 2333. 2. When `findExistingArtifactProjectFile()` recovered an existing on-disk artifact, the produced-files list was replaced with that single file, dropping any other files the turn had created earlier. Now always computes the full diff against `preTurnFileNames`, then appends the recovered artifact only if it isn't already in that set. Extracted as `mergeRecoveredArtifact(diff, recovered)` so the logic is a unit-testable invariant. Tests in ProjectView.reattach-restore.test.tsx: - mergeRecoveredArtifact: three cases (recovered appended to pre-files, no duplication when already in the diff, passthrough on no recovery). - reattach failed-status: onRunStatus('failed') → onDone → final saveMessage has runStatus 'failed', not 'succeeded'. - reattach canceled-status: same shape for cancellation. * fix(web): force keepalive PUT on pagehide so the last buffered chunk survives reload Review on #2383 caught that onPageHide() only called flush(), which updates React state then schedules persistSoon() — a 500ms debounce. On a hard reload the page tears down before that timer fires, so the final ~250ms of streamed text never reaches the daemon. Threaded a new flushAndPersistNow() callback through createBufferedTextUpdates(). Both buffer call sites (send-path + reattach-path) supply it backed by persistMessageById(id, { keepalive: true }). saveMessage in state/projects.ts forwards the new SaveMessageOptions.keepalive flag onto fetch's keepalive option, which the browser honors specifically for unload-time requests. onPageHide now calls flush() followed by flushAndPersistNow?.(), so: - flush() pushes the buffered delta into React state synchronously - the immediate persistMessageById then PUTs the updated message with keepalive:true, surviving document teardown Regression test in ProjectView.reattach-restore.test.tsx: stream a delta, dispatch pagehide, assert saveMessage was called with the flushed content AND { keepalive: true } before the 500ms debounce would otherwise have fired. |
||
|
|
85228a2b05 |
fix(analytics): capture About-you survey across rapid-finish flow (#2713)
PR #2590 wired About-you dropdown selections to fire one `onboarding/ui_click` per pick (organization_size / use_case / hear_about_us) with the chosen value attached. Live PostHog data on nightly.11 showed a real session where every survey value was missing: the user filled all four dropdowns and clicked Finish setup inside a ~3-second window, the route navigated away before posthog-js could flush the per-dropdown rows, and the resulting `onboarding_complete_result` only said `has_about_you: True` without surfacing what the user picked. Two specific gaps surfaced. `role` was never tracked at all. `OnboardingDropdown` for role had no `emitOnboardingClick` call; `OnboardingClickProps` had no `role` field; `TrackingOnboardingClickElement` had no `'role'` element. Even on a slow-path session the dashboard could not see what role the user identified as. The other three fields (organization_size / use_case / discovery_source) fired the right click events but were one round-trip away from a route change. With per-dropdown rows as the only carrier, a Finish-setup within a ~3-second batch lost them all to navigation-side flush failure. Fix is two complementary delivery paths plus a closure-staleness repair: - Contract: `TrackingOnboardingRole` (open-string, like the other About-you survey types so adding a future role doesn't force a contract bump); `role` element on `TrackingOnboardingClickElement`; `role?` and `use_cases?` fields on `OnboardingClickProps`. New `about_you_submit` click element + survey-snapshot fields (`role`/`organization_size`/`use_cases`/`discovery_source`) on `OnboardingCompleteResultProps`. - EntryShell: emit `role/select_option` on the role dropdown so the per-pick funnel is symmetric with the other three. Introduce `profileRef` (live mirror via `useEffect`) so closures that fire faster than React commits (rapid multi-pick on `use_case`, the Finish-setup click after the last onChange) read the latest selection instead of stale render-time state. On Finish setup, emit a single `about_you_submit/continue` click with the full survey snapshot BEFORE `continue` + `onboarding_complete_result`, so the highest-value row is queued first. Mirror the same survey fields onto `onboarding_complete_result`, giving the dashboard two independent carriers for the same data — losing either path still leaves the funnel a complete picture. `use_case` multi-select also had a stale-closure bug separate from the navigation issue: the delta computation read `profile.useCase` which was closure-captured one tick behind the latest pick. Reading `profileRef.current.useCase` makes the delta correct even when two picks land in the same commit. Live data from the fix session shows `use_case` rows firing one-per-pick in real time. Validation against the live PostHog stream on namespace `onboarding-survey` (dev build, fresh install via wipe + restart): 09:51:13 role/select_option role=pm 09:51:15 organization_size/select_option organization_size=solo 09:51:16 use_case/select_option use_case=product 09:51:18 hear_about_us/select_option discovery_source=github 09:51:20 about_you_submit/continue role=pm + organization_size=solo + use_cases=['product'] + discovery_source=github 09:51:20 continue/continue 09:51:20 onboarding_complete_result has_about_you=True role=pm + organization_size=solo + use_cases=['product'] + discovery_source=github An earlier pre-fix session on the same window (same user, no reload) shows the original bug: 4 use_case rows, no role, no `about_you_submit`, no survey fields on complete despite `has_about_you: True`. Targets release/v0.8.0. pnpm --filter @open-design/contracts build green pnpm --filter @open-design/contracts test 111/111 pnpm --filter @open-design/web typecheck clean pnpm --filter @open-design/web test 1843/1843 |