Commit graph

1795 commits

Author SHA1 Message Date
Kayshen-X ed4154d91c refactor(editor): sink remaining agent-settings cards to jian Card
MCP server card + ACP agent card + 2 built-in provider cards (muted fill + border, r8/r10) now route through jian Card, looks preserved. The whole agent-settings card cluster (7 cards) now shares the canonical primitive.
2026-06-20 10:07:58 +08:00
Kayshen-X 07e1101eb0 refactor(editor): sink MCP + System settings cards to jian Card
agent_settings_mcp client-config card (card fill, r8) + agent_settings_system auto-update card (muted fill, r10) now route their chrome through jian Card, looks preserved. Three agent-settings cards now share the canonical primitive.
2026-06-20 09:06:06 +08:00
Kayshen-X 2d9ccce73a refactor(editor): sink agent provider card chrome to jian Card
paint_agent_card's hand-rolled conditional fill_round_rect + stroke_round_rect chrome now goes through jian Card; the fill choice (outlined ClaudeCode -> muted, hover -> accent, else transparent) stays caller-side so the look is preserved exactly (radius 10, border). Bumps vendor/jian to 5f903d6. Demonstrates the enhance-jian-then-sink pattern.
2026-06-20 08:59:17 +08:00
Kayshen-X 3e54a66f31 chore(vendor): bump jian — Select check-left + Switch hover fidelity 2026-06-20 08:29:33 +08:00
Kayshen-X d68b959c98 chore(vendor): bump jian to f02b4a5 — layout_scene 800-line split 2026-06-20 08:16:57 +08:00
Kayshen-X a004a95b79 chore(vendor): bump jian to fd4111f — Input field 2026-06-20 07:57:04 +08:00
Kayshen-X 65b5bf8a6b chore(vendor): bump jian to c12f277 — Swatch/Tooltip/Tabs/ToggleGroup/Accordion/Popover 2026-06-20 07:54:01 +08:00
Kayshen-X 64a898f71a fix(canvas): skip colour-drag scene patch for Ref/slotless anchors
Codex review of the web twin flagged a Ref-anchor mismatch: set_selected_color
is a no-op on a Ref (it carries no fill/stroke slot), so the doc write does not
land — yet the scene patch would still mutate the Ref's scene node, briefly
showing a colour the document never received (snapping back on release). Native
caught the common case via its instance-write redirect, but a non-redirected
Ref (and any gradient-only / slotless node) slipped through on both paths.

Gate both try_patch_color_drag paths on the freshly-written hex actually being
present (first_solid_fill_hex / first_solid_stroke_hex); absent → the write did
not land → fall back to the full rebuild. Same conservative guard on native +
web.
2026-06-20 02:27:31 +08:00
Kayshen-X 6ee455f7b1 perf(canvas): patch scene fill/stroke in place on web colour-picker drag
Web twin of 8b457a50/9056458c: a colour-picker drag rebuilt the whole layout
per mouse-move (doc fill changed → scene cache miss → taffy + reshape) though
a solid colour touches no layout. try_patch_color_drag patches the anchor's
resolved scene fill / stroke via LayoutScene::set_node_fill /
set_node_stroke_color (folding paint-body + node opacity) and invalidates the
cache. The patch path still flags doc_sync_dirty (live-canvas push) but skips
the scene rebuild — reachable from this descendant module without touching the
spine. Variable-mode / gradient-stop / effect / new-stroke fall back to rebuild.
2026-06-20 01:38:40 +08:00
Kayshen-X f2c074d9d3 fix(canvas): bake paint-body opacity into the colour-drag scene patch
Codex review caught a BLOCKER: the loader folds TWO opacity factors into
resolved scene paint alpha — the fill/stroke body's own opacity (apply_alpha
in style_payload) AND node cumulative opacity — but the colour-drag fast path
only baked node opacity, so a fill/stroke authored below 100% painted too
opaque on every drag frame until release reconciled. Fold the body opacity in
host-side via first_solid_fill_opacity / new first_solid_stroke_opacity before
the patch; jian-scene still bakes node opacity on top, matching scene-build.
+unit test for the new stroke-opacity accessor.
2026-06-20 01:18:37 +08:00
Kayshen-X d653cd4b33 chore(vendor): bump jian to afe3fc8 — Separator + Progress 2026-06-20 00:49:33 +08:00
Kayshen-X c354b0b6b2 chore(vendor): bump jian to 199268e — Button Secondary/Link + Badge/Checkbox/Radio/Card 2026-06-20 00:45:40 +08:00
Kayshen-X e839c40770 perf(canvas): patch scene fill/stroke in place on colour-picker drag
A colour-picker drag rewrites doc fill on every mouse-move, so the scene
build cache (which compares the doc) rebuilt the whole layout — taffy + a
full SceneNode reshape — per drag frame, even though a solid colour change
touches no layout. Mirror the node-drag fast path: patch the anchor's
resolved scene fill / stroke via LayoutScene::set_node_fill /
set_node_stroke_color and invalidate the cache, skipping mark_dirty so the
patch survives the frame. Variable-mode, instance redirects, gradient-stop /
effect targets, and brand-new strokes fall back to the full rebuild.

Bumps vendor/jian to a86832d (the scene-patch methods). Native only; the
web host's live-sync push gate needs the same wiring once its spine settles.
2026-06-20 00:44:06 +08:00
Kayshen-X 25a5b02ce2 refactor(editor): sink agent-settings switch into jian Switch
paint_settings_switch dropped its hand-rolled track+thumb paint and now delegates to jian Switch (geometry identical: knob 16, inset 2). Removes the duplicated switch paint; off-track is now the shadcn input token. Hit stays host-side rect.contains (behavior-identical on desktop). Bumps vendor/jian to 2708666. First Phase A component sink.
2026-06-20 00:27:35 +08:00
Kayshen-X 6e43abd5ef feat(editor): add shadcn input/ring/secondary/destructive-foreground tokens to Theme
Theme gains the canonical shadcn fields; button::tokens_from_theme maps them into jian Tokens (+ radius 6.0). Unblocks the Switch off-track=input reconciliation and Button secondary variant. Bumps vendor/jian to 9cd5d50.
2026-06-20 00:24:00 +08:00
Kayshen-X fef880b8a5 chore(lock): reconcile Cargo.lock with jian-scene dep edits
Add jian-scene; drop op-editor-ui from op-mcp/op-pen-loader — matches the
scene-unification Cargo.toml changes so the lock isn't stale.
2026-06-20 00:16:24 +08:00
Kayshen-X eb02c293d2 refactor(editor): hoist relative-alpha + over helpers into util
6 git_panel modules each duplicated fn alpha (RELATIVE: multiplies c.a*factor) and git_panel_empty duplicated over(). Hoisted to util::alpha / util::over (single source); locals now delegate. NOTE: align_toolbar::alpha SETS absolute alpha (Color{a,..}) — a different op (= Color::with_alpha), deliberately left separate. Part of atomic-sink Phase 0.4.
2026-06-20 00:11:58 +08:00
Kayshen-X f5c1ef555f docs(crates): note scene moved to jian-scene + scene_vars to op-editor-core 2026-06-20 00:08:18 +08:00
Kayshen-X 00599838cf refactor(editor): collapse 6 tokens_from_theme copies to one canonical
ai_chat_input_text / property_panel_fill_picker / property_panel_text_input / agent_settings_caret / locale_picker / git_panel_text each duplicated the byte-identical Theme->jian Tokens map. They now delegate to the canonical button::tokens_from_theme; unused Density imports dropped. -114 net lines, no behavior change. Part of atomic-sink Phase 0.3.
2026-06-20 00:06:50 +08:00
Kayshen-X 4fb17fb54c perf(canvas): cache parsed SVG paths in the CanvasKit bridge
Every chrome icon / lucide glyph / brand logo / vector node re-parsed its d
string via CK.Path.MakeFromSVGString on every frame. Cache the parsed,
untransformed path keyed on d; callers draw a .copy() they transform + delete,
leaving the cached original pristine. Bounded (1024) with wasm-heap cleanup on
overflow. Mirrors the native svg_path_cache. Verified on :3100 — icons render
identically, no JS errors.
2026-06-19 23:59:17 +08:00
Kayshen-X 1009124332 refactor(pen-loader): use jian-scene + op-editor-core, drop op-editor-ui
Scene types -> jian_scene::layout_scene; scene_vars -> op_editor_core::scene_vars;
geometry/image -> op_editor_core::render_backend. Builder editor_state_to_layout_scene
stays here. 76+3 tests green; no op_editor_ui ref left.
2026-06-19 23:58:07 +08:00
Kayshen-X 26e9d2fa55 chore(vendor): bump jian to eb58f8f — Switch::hit 2026-06-19 23:49:30 +08:00
Kayshen-X bfef05ca13 refactor(mcp): use jian-scene, drop op-editor-ui dep
read_tools imports NodeKind/SceneNode from jian-scene; scene values still
come from op-pen-loader's builder. 314 tests green; no op_editor_ui ref left.
2026-06-19 23:46:03 +08:00
Kayshen-X 9add35583c refactor(editor): source CursorHint from jian-core; host owns handle+Pointer map
CursorHint now lives in jian-core (re-exported via op_host_native). for_handle becomes the host free fn cursor_for_handle (depends on OP SelectionHandle). Adds a Pointer arm to the desktop CursorIcon map. Behavior preserved; bumps vendor/jian to 44c7b75.
2026-06-19 23:42:30 +08:00
Kayshen-X 71fa9baa5b refactor(editor-ui): re-export scene from jian-scene/op-editor-core
Delete the moved modules (layout_scene, layout_scene_hit, scene_vars + their
tests); shim re-export jian_scene::{layout_scene,layout_scene_hit} +
op_editor_core::scene_vars so every consumer path (incl. dirty hosts) resolves
unchanged. Paint imports path geometry from jian-scene. 647 tests green.
2026-06-19 23:39:02 +08:00
Kayshen-X d76ba4528d refactor(editor-core): move scene_vars in from op-editor-ui
VariableTable/Value/Scalar (unresolved theme refs + NodeId) now live with
the canonical variable system. NodeId/Color resolve at the op-editor-core
crate root. 26 scene_vars tests green.
2026-06-19 23:18:43 +08:00
Kayshen-X 210fc21bf6 chore(vendor): bump jian submodule — jian-scene crate + scene skeleton removal 2026-06-19 23:12:30 +08:00
Kayshen-X 7678352071 ci: fix stale op-web-daemon path after crate rename to op-host-services
wasm-bundle-build.yml comment still pointed at crates/op-web-daemon/src/web_static.rs;
the rename sweep covered crates/Dockerfile/tools but not .github/. Now op-host-services.
2026-06-19 22:41:43 +08:00
Kayshen-X 88a571c028 refactor(host): rename op-web-daemon crate to op-host-services
The extracted headless crate is consumed by BOTH op-host-desktop (the GUI binary, for its
embedded --serve-web/MCP/chat/export) AND op-host-web-server — so 'web-daemon' was misleading.
Renamed crate dir + package + lib (op_web_daemon -> op_host_services) across all consumer files
(114 refs in 24 files) + the 4 Cargo.toml deps + Dockerfile/guard comments; identity docs
rewritten (it's the GUI-free host backend — daemon/MCP/AI/export/persistence — not web-specific).
No behavior change. (Lock renamed accordingly; the concurrent actor's op-host-web serde line excluded.)
2026-06-19 22:35:22 +08:00
Kayshen-X b7951fc598 ci(host): build web image from op-host-web-server + headless-boundary guard (Phase 6, Tasks 6.2-6.4)
Dockerfile.web-rust builds -p op-host-web-server (was op-host-desktop) and drops ALL GL/X11
build + runtime apt deps (libegl/libgles/libgbm/libxkbcommon/libwayland/libxcb) — the raster
server links none; only freetype/fontconfig + CJK fonts remain for skia text. COPY + CMD repointed
to /app/op-host-web-server. New tools/check-web-server-headless.sh fails CI if op-host-web-server's
isolated dep graph pulls winit/glutin/casement/muda/accesskit-adapters or skia-safe with gl (bare
accesskit core allowed per Codex Issue 1); wired into rust-check.yml + its paths filter. Stale
web_static.rs path comment fixed (op-host-desktop -> op-web-daemon). Desktop-app build job untouched.
2026-06-19 22:14:56 +08:00
Kayshen-X f198cdf76a feat(host): add headless op-host-web-server binary (Phase 6, Task 6.1)
A thin binary linking ONLY op-web-daemon — no winit/glutin/muda/accesskit-adapters/skia-GL.
Routes --serve-web/--mcp/--mcp-http argv to op_web_daemon::web_canvas_server::run_web_canvas +
op_web_daemon::mcp_serve::{run,run_http} (mirrors the desktop dispatcher, headless-only — no GUI
fallback). Added to root default-members. VERIFIED via cargo tree: 0 winit/glutin/casement/muda/
accesskit-adapters + skia-safe without gl (raster) — the web-server image now excludes all
desktop/GL code, answering the original question. (Lock also drops the actor-removed op-app crate.)
2026-06-19 22:09:58 +08:00
Kayshen-X c6743453b6 refactor(host): move web_canvas_server + web_chat_standard, daemon fully in op-web-daemon (Phase 5, Task 5.3)
The web-canvas daemon (web_canvas_server + its #[path] tests) + the CLI standard-mode
chat handler (web_chat_standard) — the mutually-coupled top of the closure — move together
to op_web_daemon; all their op_web_daemon::* refs flip to crate:: (the daemon is now
self-contained intra-crate). The op-host-desktop mcp_serve residual dispatcher's --serve-web
arm repoints to op_web_daemon::web_canvas_server::{parse_serve_web_args, run_web_canvas}.
WebCanvasState::new marked #[cfg(test)] (test-only callers) — resolves a long-standing
dead_code warning. op-host-desktop now holds only the GUI host + argv dispatcher; the
headless daemon is entirely in op-web-daemon. op-web-daemon 326 tests green, 0 warnings.
2026-06-19 22:01:31 +08:00
Kayshen-X c24c4bb73f refactor: remove orphan op-app crate files
Companion to the op-app doc-reference removals. op-app was a 47-line
re-export shell with zero dependents (no crate deps on it, no imports,
not in default-members/CI, ships nothing). Editor-UI composition lives
in op-editor-ui; nothing to host here. YAGNI.
2026-06-19 21:58:22 +08:00
Kayshen-X a172b74023 refactor(host): move chat_intent to op-web-daemon; extract host-coupled test (Phase 5, Task 5.3a)
The CLI standard-mode intent router moves to op_web_daemon::chat_intent; its 23
headless tests (which reach chat_intent's #[cfg(test)] internals) move with it as the
#[path] sibling. The 1 host-coupled test (cli_new_design_clears_agent_frame_indicators_after_done
— drives the GUI design-session pumps via WidgetHostNative) is extracted to
op-host-desktop/src/chat_intent_host_tests.rs against the pub run_cli_turn/CliTurnPlan API +
crate::design_session pumps. chat_session/chat_session_launch/web_chat_standard refs repointed.
op-web-daemon 276 + host test 1 green; no dep/lock change.
2026-06-19 21:55:33 +08:00
Kayshen-X d5727d322d refactor(host): move mcp_live to op-web-daemon (Phase 5, Task 5.2)
The live MCP HTTP server (McpLiveServer + mcp_live/screenshot + tests) moves to
op_web_daemon::mcp_live; its op_web_daemon::{mcp_serve,export} refs flip to crate::.
start_with_wake stays generic over F: Fn() (the winit wake closure is built at the
mcp_runtime call site, not inside mcp_live). McpLiveServer methods + McpPumpOutcome
fields promoted to pub for the desktop consumers; the test-convenience start(port)
made a non-cfg(test) doc-hidden seam. main.rs field + mcp_runtime + main_tests +
web_canvas_server(screenshot) repointed. op-web-daemon 253 + live-MCP 8 green; no dep/lock change.
2026-06-19 21:27:35 +08:00
Kayshen-X 7acd125ccd refactor(host): move mcp_serve runners to op-web-daemon, keep argv dispatch (Phase 5, Task 5.1)
The MCP stdio/HTTP server runners (run, run_http, process_message*, serve_http_connection,
rebuild_registry, the wire/doc_sync/schemas/file_path submodules + tests) move to
op_web_daemon::mcp_serve. The argv dispatcher run_cli_if_requested stays as the op-host-desktop
residual, routing --mcp/--mcp-http to op_web_daemon::mcp_serve::{run,run_http} and --serve-web
to crate::web_canvas_server (until 5.3). Test helper tool_text relocated to mcp_serve as a
non-cfg(test) pub seam. web_canvas_server/mcp_live/web_canvas_server_tests crate::mcp_serve::
refs repointed (~65). op-web-daemon 248 tests green; no dep/lock change.
2026-06-19 21:16:10 +08:00
Kayshen-X 18a162c732 refactor(host): split design_session; keep GUI pumps in op-host-desktop (Phase 4, Task 4.4)
Worker spawn (start, run_design_worker) + viewport-fit math (fit_design_viewport_to_content,
active_content_bounds, design_canvas_size) move to op_web_daemon::design_session. The
host-coupled UI pumps (pump_commands/pump_progress — take &mut WidgetHostNative) + the
progress-line renderer (render_progress/progress_label, used only by pump_progress, so
kept with their caller per the call graph — deviates from the plan's MOVE list) stay as
the residual. active_content_bounds/design_canvas_size made pub for the KEEP-side fit
tests; residual re-exports DesignSession (pub use) for main.rs zero-churn. web_chat_standard
fit + chat_session_launch start/DesignSession + chat_intent run_design_worker repointed.
Completes Phase 4. design_session 8 + op-web-daemon 215 tests green; no dep/lock change.
2026-06-19 21:01:47 +08:00
Kayshen-X 199d9bbec9 feat(sdk): add React + Vue adapters for op-web-sdk
Plan 3 of the web embedding SDK: @zseven-w/op-web-sdk-react and -vue — thin
read-only framework adapters over the TS core. Each: <DesignView> (canvas +
viewer lifecycle), provider/inject of OpViewer, and read-only
useDocument/useViewport/useActivePage. React via useSyncExternalStore
(cache-invalidate-on-event); Vue via an async shallowRef provider so children
inject the viewer after load. Self-contained (no pen-* deps; framework peer);
React 5/5 + Vue 4/4 tests; both tsc clean.
2026-06-19 21:01:27 +08:00
Kayshen-X baae07addd refactor(host): split settings_io; keep touch_recent in op-host-desktop (Phase 4, Task 4.3)
The headless settings persistence (Fingerprint/fingerprint/save_if_changed/load/save
+ the on-disk *Payload DTOs, id allocators, detect_system_locale, enum<->str converters)
+ settings_io_tests move to op_web_daemon::settings_io. The host-coupled touch_recent
(takes &mut WidgetHostNative — orphan rule) stays as the op-host-desktop residual.
main.rs / app_handler / web_canvas_server load/save/fingerprint/save_if_changed refs
repointed to op_web_daemon::; persistence keeps crate::settings_io::touch_recent. 11
settings_io tests green; no dep/lock change.
2026-06-19 20:45:30 +08:00
Kayshen-X 24472bdbe7 fix(host): scope test-only AcpAgentProbeOutcome import to the test module
Task 4.2's acp_agent_probe_host residual imported AcpAgentProbeOutcome at module
level, but only the pump tests name it (the pump reads the outcome's fields through
the inferred type) — an unused-import warning in non-test builds. Move it into mod tests.
2026-06-19 20:44:54 +08:00
Kayshen-X da195b995b refactor(host): split provider/acp probe hosts; move job structs + probe fns (Phase 4, Task 4.2)
ProviderConnectJob / AcpAgentConnectJob (DesktopApp fields — codex Issue 5) + the
probe fns (probe_acp_agent_config, acp_config_for_probe, normalize_provider_probe_outcome,
AcpAgentProbeOutcome) move to op_web_daemon::{provider_probe_host,acp_agent_probe_host}.
The impl DesktopApp pumps stay desktop-side, driving the jobs through a new pub API:
poll()/provider()/id() accessors + pending_for_test promoted to non-cfg(test) pub;
AcpAgentProbeOutcome::connected/failed made pub for the pump tests. Residuals re-export
the job structs (pub use) so main.rs DesktopApp field types resolve with zero churn
(Step 2b). web_canvas_server[_tests] probe refs repointed. No dep/lock change.
2026-06-19 20:37:00 +08:00
Kayshen-X 97a4854ddf refactor(host): move discovery cluster + refactor poll_into to take EditorState (Phase 4, Task 4.1)
model_discovery + provider_probe_models + provider_probe (mutually coupled per
Issue 2) + the provider_probe_tests #[path] sibling move together to op-web-daemon.
ModelProbe::poll_into refactored from &mut WidgetHostNative to &mut EditorState
(removing the cluster's only host coupling); the app_handler caller now marks the
state dirty, mirroring image_search. main.rs ModelProbe field/constructors + 3
consumers (web_canvas_server[_tests], provider_probe_host) repointed. op-web-daemon
204 tests green; no dep/lock change.
2026-06-19 20:26:48 +08:00
Kayshen-X 00b773e0ad refactor(host): move ai_proxy to op-web-daemon (Phase 3, Task 3.3)
The Vercel-AI-SDK-compatible chat proxy moves into the daemon; its
op_web_daemon::chat_{builtin_http,claude,copilot,http_server,subprocess} refs flip
to crate:: (intra-crate). 2 consumers (web_chat_standard, web_canvas_server — both
Phase-5 modules) repointed to op_web_daemon::ai_proxy. Completes Phase 3. 20
ai_proxy tests green; no dep/lock change.
2026-06-19 20:19:05 +08:00
Kayshen-X fe40be77dd refactor(host): move the AI-chat provider cluster to op-web-daemon (Phase 3, Task 3.2)
chat_runtime / chat_claude / chat_copilot / chat_http_server / chat_subprocess /
chat_builtin_http / chat_agent_loop (the mutually-recursive provider + agent-loop
cluster) + their 3 #[path] sibling test files move together. Their
op_web_daemon::chat_attachment/spawn/canvas_tools/subprocess_quirks refs flip to
crate:: (now intra-crate); BlockingRecvIter::new + top-level pub(crate) items
promoted to pub. 17 external refs repointed across web_chat_standard /
chat_session_launch / ai_proxy / design_session / chat_acp / acp_agent_probe_host /
chat_session_tests. op-web-daemon 158 tests green; no dep/lock change.
2026-06-19 20:16:02 +08:00
Kayshen-X 3a11cedbd8 refactor(host): split chat_attachment headless helpers into op-web-daemon (Phase 3, Task 3.1)
base64 / temp-file spill / prompt building / Claude image-Read flow move to op_web_daemon::chat_attachment; the rfd file-picker drain_attachment_pick stays desktop-side and imports media_type_for_path back (codex Issue 3). 21 provider refs repointed across chat_{runtime,claude,copilot,http_server,subprocess,builtin_http,acp}.
2026-06-19 20:05:48 +08:00
Kayshen-X 2ae343785b refactor(host): carve headless doc-io out of persistence into op-web-daemon (Phase 2, Task 2.3)
The host-free load/save core moves to op_web_daemon::doc_io: save_to_path,
load_editor_state, document_fingerprint, is_supported_document/_figma_import,
ActionOutcome, ErrorKind + the shared EditorState helpers (set_file_name_display,
preserve_app_preferences, active_page_bbox, sidecar_path). The rfd/winit dialog
flow (handle_open/save, run_action, show_error_dialog) stays in op-host-desktop
and imports them back. 23 external refs repointed; codex Issue-4 shared helpers
promoted to pub. Adds op-i18n for the dialog strings. op-web-daemon stays GL-free.
2026-06-19 19:57:28 +08:00
Kayshen-X 2281643d62 refactor(host): move export cluster to op-web-daemon (Phase 2, Task 2.2)
export + export/{scene_painter,screenshot,export_svg} + export_pdf — server-side
PNG/SVG/PDF rasterization via op-host-native's raster NativeFrameBackend (no GL).
Self-contained cluster; promoted to pub; 5 consumers repointed. op-web-daemon
still links zero winit/glutin + skia without gl (verified).
2026-06-19 19:38:51 +08:00
Kayshen-X e9378673ed refactor(host): move pre_validator to op-web-daemon (Phase 2 leaf)
Pre-validation adapter (PlannedFix -> EditorCommand via op-design-lint) for the
design pipeline. Promoted to pub; 2 consumers repointed. Completes Task 2.1
(all 7 host-free leaves moved). No dep/lock change.
2026-06-19 19:28:55 +08:00
Kayshen-X 586a37b73c refactor(host): move chat_system_prompt to op-web-daemon (Phase 2 leaf)
Builds the agent/chat system prompts (skills + design memory) for the chat
bridges. Promoted to pub; 3 consumers repointed. No dep/lock change.
2026-06-19 19:27:26 +08:00
Kayshen-X fa3427631f refactor(host): move chat_provider_llm to op-web-daemon (Phase 2 leaf)
The LlmClient adapter bridging op-orchestrator's LLM trait to op-ai chat
providers. Promoted to pub; 3 consumers (web_chat_standard, chat_intent,
chat_session_launch) repointed. No dep/lock change.
2026-06-19 19:25:52 +08:00