Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions docs/interactive-capabilities/README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<!-- generated by scripts/generate-interactive-capabilities.mjs; do not edit -->
# OpenBitFun 功能与设置目录 / OpenBitFun Features & Settings

OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**327 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` 智能体工具。
OpenBitFun Playbook 当前包含 **22 个功能**和 **21 个设置页**,共 **43 个**用户可理解的条目、**328 项**有源码证据的子能力。每个条目有独立 Markdown,并直接服务于说明书网站、OpenBitFun 全局搜索和 `OpenBitFunControl` 智能体工具。

OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **327** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.
OpenBitFun Playbook currently contains **22 features**, **21 settings pages**, and **328** source-backed sub-capabilities across **43** user-facing entries. Every entry has its own Markdown page and directly powers the website, in-app global search, and the `OpenBitFunControl` agent tool.

## 唯一事实源 / Single source of truth

Expand Down Expand Up @@ -33,14 +33,14 @@ Docs, website, search, and agents see only features, settings, and documented su

## 控制边界 / Control boundary

- 每个子能力都明确标记为直接控制、委托给专用智能体工具、需交互打开或不支持;“打开页面”不会再被统计成“智能体已控制”。当前覆盖:直接 **49**、委托 **62**、需交互 **216**、不支持 **0**。
- 每个子能力都明确标记为直接控制、委托给专用智能体工具、需交互打开或不支持;“打开页面”不会再被统计成“智能体已控制”。当前覆盖:直接 **50**、委托 **62**、需交互 **216**、不支持 **0**。
- 稳定行为声明为带 JSON 输入契约的 `operations` 或 `options`,并绑定原生产品控制 Provider;智能体不接触原始 Tauri Command。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 327 项子能力都不会写入 system prompt。
- `OpenBitFunControl list` 和 `search` 都返回带 `nextCursor` 的精简分页结果;目录可持续增长,不靠固定总量上限。完整目录和 328 项子能力都不会写入 system prompt。
- 目录发现与契约读取不依赖 React 或可见窗口。普通配置型 option 统一由 Product Assembly 的共享 ConfigService 执行器读、写并回读,因此 Desktop、CLI 与 Headless 表面走同一份实现;只有宿主原生 operation/provider option 和界面导航按表面注册适配器,缺失时必须明确返回不可用,禁止静默回退本机。只读智能体只能发现和读取目录。

- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **49 direct**, **62 delegated**, **216 interactive**, and **0 unsupported**.
- Every documented item is classified as direct control, delegated Agent control, interactive opening, or unsupported; opening a page is never counted as direct control. Current coverage is **50 direct**, **62 delegated**, **216 interactive**, and **0 unsupported**.
- Stable behavior becomes a typed `operation` or `option` with a JSON input contract and a native product-control provider. Agents never receive raw Tauri commands.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 327 documented items enters the system prompt.
- `OpenBitFunControl list` and `search` return compact pages with a `nextCursor`; the catalog can grow without a fixed total-size ceiling. Neither the full catalog nor its 328 documented items enters the system prompt.
- Discovery and contract lookup do not depend on React or a visible window. Ordinary config-backed options are read, written, and read back by one Product Assembly ConfigService executor shared by Desktop, CLI, and headless surfaces. Only host-native operations/provider options and presentation routes install surface adapters; missing adapters return explicit unavailability without local fallback. Read-only agents may only discover and inspect entries.

## 防腐化门禁 / Anti-drift gates
Expand Down
91 changes: 85 additions & 6 deletions docs/interactive-capabilities/capabilities.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"title": "OpenBitFun Playbook",
"origin": "https://playbook.openbitfun.com",
"source": "src/shared/interactive-capabilities/catalog.json",
"digest": "dcea667ca017ed22790f12c28d5ccd4011314fca16d7e2ce9fc9cf87ff283825",
"ownerDigest": "c989ace53614c2a526fefe64816fdd6895ec71edcc28f44b6f80d1edb75bb33e",
"digest": "64a4a6c115b13f5aa15157d1affde2c7e901499e9941571a333d45bf242033ae",
"ownerDigest": "b96bf9292a92548facbd835b8b89c4865bfdd984b356c9001995f552c6a34f51",
"searchAcceptance": [
{
"id": "companion-pet-mixed",
Expand Down Expand Up @@ -138,9 +138,9 @@
"features": 22,
"settings": 21,
"userFacing": 43,
"documentedItems": 327,
"documentedItems": 328,
"controlCoverage": {
"direct": 49,
"direct": 50,
"delegated": 62,
"interactive": 216,
"unsupported": 0
Expand Down Expand Up @@ -15007,6 +15007,7 @@
"default-agent-harness",
"automatic-titles",
"title-model",
"next-input-suggestion",
"accelerated-search",
"search-index",
"remote-behavior"
Expand Down Expand Up @@ -15094,6 +15095,50 @@
"pageId": "workspace.session"
}
},
{
"id": "setting.workspace.session:option:prompt-suggestion",
"capabilityId": "setting.workspace.session",
"itemIds": [
"next-input-suggestion"
],
"kind": "option",
"risk": "write",
"executionHost": "productHost",
"availability": {
"desktop": {
"available": true
},
"cli": {
"available": true
},
"peer": {
"available": true,
"requiredCapabilities": [
"product_control_v1"
]
},
"remoteControl": {
"available": true
},
"detachedDispatch": {
"available": true
}
},
"inputSchema": {
"type": "boolean"
},
"outputSchema": {
"type": "object",
"additionalProperties": true
},
"valueSource": {
"kind": "static"
},
"presentationTarget": {
"kind": "settings",
"pageId": "workspace.session"
}
},
{
"id": "setting.workspace.session:option:workspace-search",
"capabilityId": "setting.workspace.session",
Expand Down Expand Up @@ -27504,13 +27549,15 @@
"跟随上次选择或固定默认执行框架",
"自动生成会话标题",
"本地工作区加速搜索",
"会话使用的标题模型"
"会话使用的标题模型",
"答复后的候选输入建议"
],
"highlightsEn": [
"Follow the last selection or fix a default Harness",
"Automatic session titles",
"Accelerated local-workspace search",
"Model used for session titles"
"Model used for session titles",
"Next input suggestion after a reply"
],
"items": [
{
Expand Down Expand Up @@ -27549,6 +27596,20 @@
"reasonEn": "“Make title generation inherit the primary or fast model, or use a selected model” spans multiple live-state-dependent steps and currently has no single structured Command that can deterministically complete the whole workflow; the Agent opens the exact entry and keeps the remaining interaction visible to the user."
}
},
{
"id": "next-input-suggestion",
"titleZh": "启用或停用 Agent 答复后在输入框中显示的一句话候选输入",
"titleEn": "Enable or disable the one-sentence next input suggested in the input box after an Agent reply",
"control": {
"kind": "direct",
"operations": [],
"options": [
{
"id": "prompt-suggestion"
}
]
}
},
{
"id": "accelerated-search",
"titleZh": "为本地工作区启用 Flashgrep 加速搜索",
Expand Down Expand Up @@ -27600,12 +27661,14 @@
"让新会话跟随 ChatInput 上一次选择的执行框架",
"把新会话固定为 Ultimate 执行框架",
"关闭自动生成会话标题",
"关闭答复后的候选输入建议",
"启用工作区加速搜索"
],
"agentExamplesEn": [
"Make new sessions follow the last Harness selected in ChatInput",
"Fix Ultimate as the Harness for new sessions",
"Turn off automatic session titles",
"Turn off next input suggestions after replies",
"Enable accelerated workspace search"
],
"destination": {
Expand All @@ -27624,6 +27687,16 @@
"type": "boolean"
}
},
{
"id": "prompt-suggestion",
"titleZh": "候选输入建议",
"titleEn": "Next input suggestion",
"descriptionZh": "Agent 答复完成后,在输入框中给出一句话的下一步输入建议。",
"descriptionEn": "Suggest a one-sentence next message in the input box after the Agent replies.",
"valueSchema": {
"type": "boolean"
}
},
{
"id": "workspace-search",
"titleZh": "工作区加速搜索",
Expand Down Expand Up @@ -27659,16 +27732,20 @@
"自动生成会话标题",
"本地工作区加速搜索",
"会话使用的标题模型",
"答复后的候选输入建议",
"Follow the last selection or fix a default Harness",
"Automatic session titles",
"Accelerated local-workspace search",
"Model used for session titles",
"Next input suggestion after a reply",
"让普通新会话跟随 ChatInput 上次选择,或固定使用指定执行框架",
"Make regular new sessions follow the last ChatInput selection or use a fixed Harness",
"启用或停用根据会话内容自动生成标题",
"Enable or disable automatic titles generated from session content",
"让标题模型继承主模型、快速模型或使用指定模型",
"Make title generation inherit the primary or fast model, or use a selected model",
"启用或停用 Agent 答复后在输入框中显示的一句话候选输入",
"Enable or disable the one-sentence next input suggested in the input box after an Agent reply",
"为本地工作区启用 Flashgrep 加速搜索",
"Enable Flashgrep-backed accelerated search for local workspaces",
"查看索引状态并在需要时重建工作区索引",
Expand All @@ -27678,10 +27755,12 @@
"让新会话跟随 ChatInput 上一次选择的执行框架",
"把新会话固定为 Ultimate 执行框架",
"关闭自动生成会话标题",
"关闭答复后的候选输入建议",
"启用工作区加速搜索",
"Make new sessions follow the last Harness selected in ChatInput",
"Fix Ultimate as the Harness for new sessions",
"Turn off automatic session titles",
"Turn off next input suggestions after replies",
"Enable accelerated workspace search"
],
"docsUrl": "https://playbook.openbitfun.com/capabilities/setting.workspace.session/"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Control the default Agent Harness, session-title generation, accelerated workspa
- Enable or disable automatic titles generated from session content
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 让标题模型继承主模型、快速模型或使用指定模型
- Make title generation inherit the primary or fast model, or use a selected model
- **智能体可直接控制 / Direct Agent control** · 启用或停用 Agent 答复后在输入框中显示的一句话候选输入
- Enable or disable the one-sentence next input suggested in the input box after an Agent reply
- **智能体可直接控制 / Direct Agent control** · 为本地工作区启用 Flashgrep 加速搜索
- Enable Flashgrep-backed accelerated search for local workspaces
- **智能体可定位入口,需交互完成 / Agent opens; interaction required** · 查看索引状态并在需要时重建工作区索引
Expand Down Expand Up @@ -52,6 +54,7 @@ Control the default Agent Harness, session-title generation, accelerated workspa
| 选项 / Option | 可用值 / Values | 中文说明 | English description |
| --- | --- | --- | --- |
| 自动生成会话标题 / Automatic session titles | `boolean` | 根据会话内容生成易识别的标题。 | Generate a recognizable title from session content. |
| 候选输入建议 / Next input suggestion | `boolean` | Agent 答复完成后,在输入框中给出一句话的下一步输入建议。 | Suggest a one-sentence next message in the input box after the Agent replies. |
| 工作区加速搜索 / Accelerated workspace search | `boolean` | 为本地项目启用 Flashgrep 加速搜索。 | Enable Flashgrep-backed search for local projects. |

## 可以直接对智能体说 / Try saying
Expand All @@ -62,6 +65,8 @@ Control the default Agent Harness, session-title generation, accelerated workspa
- “Fix Ultimate as the Harness for new sessions”
- “关闭自动生成会话标题”
- “Turn off automatic session titles”
- “关闭答复后的候选输入建议”
- “Turn off next input suggestions after replies”
- “启用工作区加速搜索”
- “Enable accelerated workspace search”

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 1,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "dcea667ca017ed22790f12c28d5ccd4011314fca16d7e2ce9fc9cf87ff283825",
"catalogDigest": "64a4a6c115b13f5aa15157d1affde2c7e901499e9941571a333d45bf242033ae",
"count": 216,
"reasonCounts": {
"externalAuth": 4,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"schemaVersion": 2,
"generatedFrom": "src/shared/interactive-capabilities/catalog.json",
"catalogDigest": "dcea667ca017ed22790f12c28d5ccd4011314fca16d7e2ce9fc9cf87ff283825",
"catalogDigest": "64a4a6c115b13f5aa15157d1affde2c7e901499e9941571a333d45bf242033ae",
"commandCount": 674,
"coverage": {
"commandCount": 674,
Expand Down
1 change: 1 addition & 0 deletions scripts/core-boundaries/rules/source/public-api-rules.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export const agentRuntimeRootPublicModules = [
'prompt',
'prompt_cache',
'prompt_markup',
'prompt_suggestion',
'remote_file_delivery',
'runtime',
'scheduled_job',
Expand Down
5 changes: 5 additions & 0 deletions src/apps/mobile/AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ UiState or an Intent declared there, and no module above it is visible to them.

## Remote capability ownership

For next-input suggestion replay, stale-turn retirement, host restart and session
isolation, run from `shared/` with the Android guide's Java runtime:
`./gradlew :core-feature:jvmTest --tests '*promptSuggestion*' --tests '*SessionRecordReplicaTest*'`.
These are simulated host streams, not evidence of a live remote connection.

Native mobile surfaces are controllers. Desktop and CLI own execution, files, terminals and SSH credentials. Mobile workspace selection may use only connections already saved on the controlled runtime; it must not create an SSH connection or instantiate another runtime. Carry the selected connection identity through every operation, and report missing identity instead of falling back to local files.

## Offline Mini Apps
Expand Down
1 change: 1 addition & 0 deletions src/apps/mobile/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ metadata, execution-mode IDs, speech draft merging, and completion observation i
|---|---|---|
| Execution modes | Minimal / Standard / Ultimate, with the HarmonyOS density glyph; workspace menus also retain Cowork | Enabled only by live `get_workspace_info.capabilities` containing `harness_profiles_v1`; older hosts keep Code / Cowork. Capability absence in old payloads is covered by a round-trip test. |
| Running-turn input | A nonempty remote draft offers Send; an empty composer retains Stop | Hosts advertising `dialog_steer_v1` receive `steer_turn` with the active turn ID. Older hosts retain `send_message` queueing; failed sends retain the draft and attachments. |
| Next-input suggestions | Android, iOS and HarmonyOS show the host's suggestion above an empty, idle composer; tapping fills and focuses the editor without sending | Uses the existing host session stream. Hints retain their exact text and retire on newer turns, session changes or host restarts. Older hosts simply show no hint; no new Relay API or migration is needed. |
| Plans | Legacy CreatePlan, structured plans and `.plan.md` writes show a plan card with file preview and Build | Build requires `plan_build_v1`, a completed plan tool and an idle connected session. Paths remain remote; the mobile app never runs the plan locally. |
| Offline Mini Apps | Gomoku, Regex Playground and Daily Divination open from welcome and sidebar without login | Product-owned sources are bundled by `miniapps/generate.cjs`; native WebView hosts expose only per-app allowlisted storage and clipboard. No network, Node or shell bridge. |
| Code preview | Native lexical colors, line numbers and referenced-line backgrounds | Both apps use the core-feature highlighter with native theme tokens; large files retain its bounded plain-text fallback. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.v2.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.assertTextEquals
import androidx.compose.runtime.mutableStateOf
import com.openbitfun.mobile.app.ui.chat.COMPOSER_INPUT_TEST_TAG
import com.openbitfun.mobile.app.ui.chat.ComposerBar
import com.openbitfun.mobile.app.ui.chat.MODEL_CONTROL_TEST_TAG
Expand All @@ -25,6 +27,34 @@ class ComposerModelSelectorTest {
@get:Rule
val composeRule = createComposeRule()

@Test
fun suggestionFillsTheEmptyDraftExactlyWithoutSending() {
val draft = mutableStateOf("")
val streaming = mutableStateOf(false)
var sends = 0
val suggestion = " Run the tests "
composeRule.setContent {
OpenBitFunTheme(dark = false) {
ComposerBar(draft = draft.value, images = emptyList(), busy = false,
streaming = streaming.value, phase = ConnectionPhase.CONNECTED,
model = null, capabilities = ChatComposerCapabilities.RemoteChat, placeholder = "Message",
promptSuggestion = suggestion, onDraftChange = {draft.value = it},
onRemoveImage = {}, onAttach = {}, onVoice = {}, onSend = {sends++},
onStop = {}, onOpenModels = {}, modifier = androidx.compose.ui.Modifier)
}
}
composeRule.onNodeWithTag("composer-prompt-suggestion").assertIsDisplayed()
capture("prompt-suggestion-before.png")
composeRule.onNodeWithTag("composer-prompt-suggestion").performClick()
composeRule.onNodeWithTag(COMPOSER_INPUT_TEST_TAG).assertTextEquals(suggestion)
composeRule.onNodeWithTag("composer-prompt-suggestion").assertDoesNotExist()
capture("prompt-suggestion-accepted.png")
composeRule.runOnIdle { assertEquals(0, sends); draft.value = ""; streaming.value = true }
composeRule.onNodeWithTag("composer-prompt-suggestion").assertDoesNotExist()
composeRule.runOnIdle { streaming.value = false }
composeRule.onNodeWithTag("composer-prompt-suggestion").assertIsDisplayed()
}

private fun capture(name: String) {
composeRule.waitForIdle()
val instrumentation = InstrumentationRegistry.getInstrumentation()
Expand Down
Loading
Loading