Skip to content

feat: promote native dynamic UI, settings refresh and chat input work for 1.12.2-ry.8 - #70

Merged
CATMIAOZHI merged 90 commits into
personal/mainfrom
promote/ry.8
Oct 15, 2026
Merged

CATMIAOZHI merged 90 commits into
personal/mainfrom
promote/ry.8

Conversation

@CATMIAOZHI

Copy link
Copy Markdown
Owner

Promote personal/dev → personal/main for Operit Ry 1.12.2-ry.8

  • 检查点:promotion-checkpoint/main-20261010-3f66d67c
  • 基线:personal/main 3f66d67c4(ry.7)
  • 本轮范围:89 个提交(87 个来自 personal/dev + 版本号提交 + 单测修复),714 个文件,+31825 / -74158
  • 版本:versionName 1.12.2-ry.7 → 1.12.2-ry.8;versionCode 保持 100205 不变
  • 排除的开发版专属内容:.dev 包名与 -dev 版本后缀、DEV 角标图标与快捷方式、personal-dev 热更新通道、tools/hotbuild 的 dev 发布脚本与子模块指针、dev-quality.yml、trigger-nightly-build.yml

功能域

动态 UI(原生 A2UI 卡片)

  • 原生 A2UI 卡片与可靠的本地交互,卡片外框层级、响应式与自适应布局、Repeat 模板、P2 控件
  • 事件 schema 与原生渲染器对齐;卡片语法修复与嵌套限制放宽
  • 图表:柱状图配色与标签、环形图重绘、长数值缩放适配、去掉数据表按钮
  • 语义色:Text/RichText 支持 tone,跟随自定义气泡颜色并保留对比度兜底
  • 行内徽章与图标::badge[文字]{tone=... icon=...}、:icon[名称],与卡片共用 Lucide 描边图标集
  • 高级视觉:dynamic_ui 包内 opt-in 的 HTML 卡片(点击加载、移出视口停止渲染、无 JavascriptInterface)
  • 卡片引用编号缩短、失败提示不进正文、HTML 宿主在视口变化时保持稳定

聊天与输入

  • 组合输入预测(composer predictions)
  • 经典输入框重做:内联思考档位、点击立即生效
  • 长按说话:保留准备阶段的语音、上滑取消、无识别结果时回到空白输入
  • 上下文预算拆分与斜杠快捷指令
  • 媒体历史保留到压缩;对话历史面板更宽、更少拥挤

设置界面

  • 全新设置首页与悬浮搜索,搜索可以直接定位到具体选项
  • 主题页 iOS 风格分段标签、共享行组件、重置二次确认
  • 上下文与总结页、工具权限页、存储管理页统一重排
  • 界面语言只保留维护中的语言;中文标注为简体中文

权限与安全

  • 工具权限页在工具数量很多时保持整洁
  • 审核失败直接返回 AI 结果,不再要求人工批准
  • 聊天框权限快捷入口;移除意图式 grep

系统与工具

  • Shell 托管任务:超时、等待、取消
  • subagent 统一使用 v2
  • Codex OAuth 过期令牌单次恢复

记忆与历史

  • 修改记忆:过程记录与动态 UI 源码折叠,正文更好编辑
  • 动态 UI 卡片按可见文本学习

语音

  • 录音前响度归一,弱音量不再丢字
  • ONNX 分段听写连续;不再每次停止都报麦克风失败

工程与基础

  • 移除虚拟形象功能:dragonbones / mmd / fbx 原生模块、Filament 依赖与相关字符串
  • 工具链升级:AGP 9.1.1、Kotlin 2.2.20、Gradle 9.3.1、Compose alpha BOM、NDK 27、build-tools 37.0.0
  • 接入 Android A2UI 依赖;lint 基线校验和同步;terminal 子模块更新到含 consumer-rules.pro 的提交

验证

  • 本地(本机结果)::app:compileDebugKotlin 通过、:app:testDebugUnitTest 通过(ComposerPredictionPreferencesTest 中需要两次真实写入的重置用例在 Windows 上按假设跳过,见提交说明)、:app:lintDebug 通过
  • 独立只读审计:AUDIT: PASS
  • 开发版 APK 已在真机完成功能测试;本 PR 不含开发版专属配置
  • CI:Fast checks 为必需检查,Full Android checks 仅供参考

CATMIAOZHI and others added 30 commits October 15, 2026 10:50
… observe

wait_agent blocked for its full timeout even when no other agent could reach
the caller, so a mis-called wait just sat there until the model gave up on it.

One lock snapshot now decides the result: an empty caller mailbox, no other
job or spawn reservation in the root conversation and no streaming peer turn
mean the wait cannot succeed, so it returns an explicit idle outcome instead.
The invariant holds because finish() enqueues its message before the job leaves
jobs, send() enqueues before it starts one, and the completion callback of a job
that was cancelled before its body ran now enqueues the terminal notification
inside the same lock region that removes the job; previously that callback
dropped the job first and notified afterwards, leaving a window in which a
caller could snapshot an empty tree and give up while the notification was
still on its way.

Reconciliation reads the caller's stored history, so the loop reconciles on its
first pass and then at most once per second; pending input keeps its 100ms
cadence and senders still push their own deliveries. Limits are unchanged.

The tool description, the idle result text and the parity document explain the
result and tell the model to call wait_agent again once a child it just
created is running, because non-task tool calls run serially in call order and
a wait placed before its own spawn can see the tree still empty.

Validated with :app:compileDebugKotlin and the full :app:testDebugUnitTest
suite (2765 tests, 0 failures).
The floating settings button was positioned from the bottom bar's full
height, and that height includes the todo / prefix-warning dock sitting above
the input card. As soon as the dock had a row to show, the button was pushed a
whole line above the notice icon beside it, and opening the todo panel moved
it again.

The dock now reports its own height and the centre of its row content, so the
button is placed from the input card height plus that row centre. Collapsed
and expanded todo states no longer differ. With an empty dock the formula
reduces to the previous one, and the first-frame fallback is unchanged.
The classic input is one card now: the draft on top, the actions underneath.
The field keeps a frame of its own only when the card behind it is see-through,
which is what the transparent themes need to stay readable.

The action row carries a thinking strength chip. Its popup owns the dial while a
finger is on it and writes the level once the gesture settles, so dragging no
longer toggles the switch and rewrites the preference on every frame - that was
the stutter seen right after turning thinking off. The setter is idempotent, so a
drag that re-enables thinking cannot flip back mid-gesture.

Dictation runs inside the composer: a live level strip, a stop button, and a
separate action that fills the message box and never sends. A failed pass keeps
the panel up with the reason and a retry.

Also: the fullscreen button moved to the top right of the draft and the
duplicate one in the action row is gone; the voice entry uses a level-bars icon;
the slider thumb is round and springs when pressed; the stop captions share the
thumb geometry instead of drifting with the label width.

Verified with :app:compileDebugKotlin and an independent read-only audit.
…ating taps

The dial was lifted clear of the composer with padding inside the popup, so the
strip below the panel still belonged to the popup window: a tap landing there
counted as an inside tap and the popup refused to close. The lift now moves the
window itself, which leaves the panel where it was and lets any tap outside
dismiss it.

The slider is drawn from measurements of the reference: the pill spans the
panel, its height drives the thumb slot so the thumb's centre lands on the first
and last stop circles, the filled rail is slimmer than the pill and grows out of
the first stop, and the stop dots sit exactly where the thumb travels.

Also: the voice conversation entry uses the level-bars icon in the agent
composer, the floating window row and its menu; the fullscreen button is centred
with the draft again, because IconButton's 48dp minimum touch target was
stretching the row and leaving the icon below the line it belongs to; and the
track no longer draws its dots or its shortest rail on the wrong side in
right-to-left layouts.

Verified with :app:compileDebugKotlin and independent read-only audits.
Enable bounded isolated next-message predictions by default while respecting saved opt-outs. Add native ghost text with glyph-only double-tap adoption, cache restoration, contrast-safe styling, and stable scrolling/focus behavior.

Production and AndroidTest Kotlin compilation and 32 focused JVM tests passed. Independent backend/UI and fresh UI/UX audits are clean. User explicitly waived cloud lint; real-device IME and accessibility checks remain outstanding.
Block new requests when a selected model is deleted, preserve model identity across list changes, and show a persistent configuration notice in chat. Also fix wrapped task counts and clipped attachment labels.
Rate-limit streaming progress logs by time. Coalesce display-only text without changing tool or revision ordering, and throttle large node snapshots while preserving structural and final updates.
The default scheme still used the previous OpenCode Rainy tones, so the light theme
looked washed out next to the RainyToken/Bongocat reference: a pink primary app bar
with near-white label text, flat grey cards, and error and decorative accents sharing
one rose.

Light mode now uses a pale sakura page, white panels with the warm inset, a deep rose
primary with readable labels, and a separate divider tone. Cards draw white with the
theme ink and border, so they read as cards instead of blending into the page.

Forced app bar text is measured against the opaque bar it is drawn on, which the old
light-on-pale combination failed; a transparent toolbar keeps the chosen tone because
the image behind it is unknown. Status bar icons follow the resolved fill the same way.

Dark mode keeps its existing palette and gains only the shared accent roles.
…witch

The RainyToken palette made the send button, the context ring and other accents a dark
berry, and the app bar barely separated from the near-white page.

Light mode now defaults to softer sakura accents that still clear 3:1 as text on the pink
card fills and carry white icons at about 4.3:1, and the default app bar and status bar
get their own sakura band. A new "Enhanced contrast" setting (off by default) restores the
deeper accents and the automatic deepening of pale custom colors and drawer accents. It is
saved with character and group themes and removed on reset.

The light and dark palette generators were duplicated in Theme.kt; both the live theme and
the web chat snapshot now compose the scheme through one resolver.
Dynamic UI is now rich content inside the answer instead of a framed
interactive card. Blocks render without a host frame, background or
padding, so prose and UI interleave like Markdown paragraphs; a root Card
is the explicit opt-in frame for self-contained tools. The source toggle
only appears when a block has a problem, and insets became translucent so
they read on any bubble color.

Add a native Flow component for steps and linear flowcharts with arrows
(1..8 steps, auto horizontal/vertical, validated at compile, expansion
and render time). Rewrite the guide around "prose first, display-only
second, interaction only when the user must act", and add display-only
examples (flow_steps, flowchart, data_story) plus a mixed_branch example.
Rewrite the package description with suitable and unsuitable scenarios, add DECIDE FIRST and VISUAL RULES to read_guide, and drop decorative headers from display-only examples.
Models can write :badge[label]{tone icon} and :icon[name] in Markdown text; they render as native spans in paragraphs, headings, lists and table cells, while copy and speech keep only the badge label.
The native inline link plugin committed to LINK on ']' so ':badge[ok]{...}' and plain '[x]' became links and lost a character. It now waits for '('. Sessions gain finish() so text still pending at the end of a stream, or when it is cancelled, is emitted as plain text instead of being dropped.
The badge syntax was only described inside the dynamic_ui package summary, so models read it as a tool to activate and rarely used it, and CLI mode, disabled tools or restricted packages hid it entirely. Add a short reply-format rule to every system prompt and drop the duplicate from the package summary.
Badges and icons are part of the Dynamic UI package: the package summary states when to use them, read_guide documents the syntax and icon rules, and the global reply-format rule added in the previous commit is removed.
…ition

Communication audio with noise suppression often reaches the voice detector well below full scale, so continuous dictation dropped normal speech. Apply a capped software gain (up to 8x, peak target 0.5) to local recognizer input, including preroll, while the meter keeps showing raw microphone level.
Grid rows stretched only Display tiles, so panels with a wrapping title left uneven rows. Panels in a shared grid row now stretch to the tallest cell, and h1-h4 text in narrow multi-column cells uses one 18sp size so short titles stay on one line.
… rolls over

The review list keeps the newest 200 reviews across all chats, so older conversations lost the review line under their tool calls. Finished reviews are now also kept, sanitized, in a per-chat file that the badge falls back to and that is deleted with its chat. Existing stored reviews are migrated. Archived denials no longer offer the one-time approval.
…hing

The package description asked the model to decide on UI before loading it, so it rarely loaded. Explaining, introducing, analyzing or comparing now loads the package and its guide first; whether to place a block stays the model's choice.
…, branch state

Models now get card-1, card-2 per turn instead of a 36-character UUID that they sometimes miscopied; the UUID stays internal for live surfaces and state files. An unresolved reference keeps its original fence and renders as a one-line notice instead of writing a failure sentence into the answer. Model-facing history, summaries and role-bridged turns replace expanded card JSON with its visible text. Branching copies each chat's card state so selections and live data survive.
Memory learning now reads each card as the readable text the user saw, without the chat prompt's length cap, and keeps the text of Html cards. Cursors saved against the raw message restart that message instead of drifting.
Each permission group shows a count and its first eight tools as compact chips; the rest open in a searchable list grouped by package. Tools allowed by default fold into one line.
The remove button grows to 40dp so chips are hard to clear by accident, and every chip shares the same height.
The show-all dialog gets a header with the group color and total, a search field with clear, package sections with count badges, and grouped rows with a clear remove button. The add-tool dialog shares its width, color and search field.
Defaults sit in one card with Max first, the budget field and 200K-500K presets shown only when Max is off, and the trigger field only when auto compaction is on. Summary format settings fold into one header card named after the bound model config.
Adds shared SettingsKit components, rebuilds the settings home as grouped cards in the context page style, and adds a bottom search bar that finds pages, group names and common options inside sub pages.
Replaces the move-to-folder button list with a folder picker that marks the current place, shows parent paths and offers search; gives the message long-press menu larger rows and red delete items; fixes the white sort menu in dark theme; localizes the bulk delete confirmation.
…tion

Global display, language, storage and layout pages use the shared settings cards. A search result for an option now opens its page, scrolls to the row and flashes it. Resetting display settings asks first and says what it clears. Clear cookies looks like a delete action, the context entry moves into the AI model group, and moving a chat to a folder confirms where it went.
… restyled

Folder and chat drag no longer snaps back after quick moves: the lock waits for both lists to match before releasing. Rows slide with a spring while dragging, with haptics on pick up, swap and drop. The settings search bar no longer jumps above the keyboard. Component, tag market, waifu, HTTP chat, tool permission, agent profile, functional model and custom emoji pages now use the shared settings style, and search lands on more of their options.
Theme cards now share the 20dp settings card shape and the background-aware card color, section titles use SettingsSectionTitle, dividers are softer, and the bound character card gets the brand tint. Adds SettingsCardShape and rememberSettingsCardColor to SettingsKit.
… the settings style

Elevated and outlined cards become settings cards with the shared shape and background-aware color, section headers use the icon badge, oversized titles shrink, and the backup hint uses the info banner. Stats keep their white cards with the shared corner radius.
…s and safer reset

Theme tabs become a segmented control that opens the right tab from settings search. Saves show a floating pill, reset asks first, switch rows use the shared settings row, color rows sit label-left with a dot on the right, chosen options are clearly marked, and bubble image sliders fold under Crop and scale. Removes the unused input-style and input color branches.
Windows cannot rewrite an existing preferences file: DataStore 1.0.x renames the scratch file with File.renameTo, which fails when the target exists. The single test wrote three times, so it always failed locally while passing on Linux CI. Split it into a one-write case that runs everywhere and the two-write reset case that is skipped on Windows, and record the constraint in the class comment.
The workspace-file empty rows read resources from LocalContext.current, which lint rejects as LocalContextGetResourceValueCall. Resolve them with stringResource during composition like the rest of the screen.
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 15, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
🔒 Security Review ✅ Completed 2026-10-15T03:33:48.583960Z 0985ff1 PR opened

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@CATMIAOZHI
CATMIAOZHI merged commit a5afba0 into personal/main Oct 15, 2026
14 of 15 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: 0985ff1858

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

)
}
return extractImageLinkTags(message).mapNotNull { tag ->
val data = ImagePoolManager.resolveImage(tag.id, tag.sourcePath) ?: return@mapNotNull null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

P1 Badge Security: Reject assistant-supplied image source paths

When composer predictions are enabled—the new default—and the selected model supports vision, a model response can emit <link type="image" id="fresh" src="/known/path.jpg">. The automatic prediction replays that assistant turn, and this line passes its untrusted src to resolveImage, which opens and encodes any app-readable image before the provider serializer includes it in the next request. This bypasses tool permission review and lets a malicious or prompt-injected provider exfiltrate known device/workspace images. Rehydrate only IDs backed by a trusted attachment registry; never resolve paths parsed from assistant text.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant