English | 简体中文
沿着可执行程序入口,追踪应用托管、数据访问和 Agent 交互。以下路径相对于仓库根目录。 Agent 架构导读继续介绍内核、peer、工具路由和 Tokio 任务。
启动配方:未验证。 先按根目录 README 准备依赖;已有框架克隆时,通过其 sources hub 配置复用。
| 组件 | 入口和职责 |
|---|---|
| 桌面 | desktop/src/main.rs 启动 octosense,为 octosense-shell 提供薄入口。 |
| Home | phone/src/main.rs 启动 octosense-home,在共用 Shell 上增加设置和平台集成。 |
| 原生应用 | 实现 Makepad AppModule 的 Rust 模块,或通过窗口管理协议托管的可执行程序。 |
| 隔离脚本应用 | App Hub 的 Card runner 校验 manifest.json,在受限 Makepad Script/Splash VM 中运行 main.splash。 |
| 宿主服务 | Rust 按明确的应用身份执行操作并返回数据。 |
| 应用 Agent | 按应用/账户限定的 octos peer,拥有会话、工作目录及获授予的工具。 |
| 系统 Agent | Shell 的助手会话,拥有发现、委派和选定系统操作的工具。 |
| ROM 特权 agent | Android 的 Java/Binder 平台服务 AgentPlatformService。 |
隔离应用的 main.splash 使用 Makepad Script/Splash。L0 .card 文件经过 Octoscript
独立的解析/检查器,再通过 lowering 转为 Makepad 可渲染内容。产品中同时使用这两条路径;
阅读或修改某个文件时,沿着它实际的加载路径追踪。
准备依赖后,在仓库根目录运行:
# 独立的 Reference 窗口。
cargo run --locked -p octosense-reference
# 把 Reference 链接进桌面,并在启动后打开。
MAKEPAD_WM_TEST_APP=reference cargo run --locked -p octosense --features app-reference -- --module reference
# 标准桌面:App Hub、Rinx、Terminal、原生 Makepad 应用和内核集成。
cargo run --locked --release -p octosense隐藏窗口检查可添加 MAKEPAD_HIDE_WINDOWS=1 MAKEPAD_REMOTE=8000,
使用 /help 描述的 Makepad 控制接口,并通过 /quit 退出。
--module 选择托管方式;MAKEPAD_WM_TEST_APP 请求启动应用。
按以下顺序阅读:
apps/reference/src/lib.rs:ReferenceView保存count;handle_event接收按钮和文本动作、修改标签;draw_walk将绘图交给内部View。- 同文件的
ReferenceModule::register注册控件类型;create返回InstanceParts,包含根控件、服务执行器和退出回调。ReferenceExecutor不暴露工具。 native-apps.json声明 Reference 的源码、feature、 托管、存储和空 Agent 授权。生成器据此生成原生应用注册表和 Cargo feature 区块。desktop/src/main.rs导入 Shell 的App并调用octosense_main!。在crates/shell/src/lib.rs中,该宏调用 Makepad 的app_main!并设置包目录。App::launch_app_with_args查找注册的应用,按需检查脚本应用身份和准入, 准备存储,必要时聚焦已有窗口,然后选择模块或进程托管。ModuleHost::create创建实例范围、 存储命名空间、回复句柄、视口和 VM,再创建模块。宿主策略决定它可以获得哪些已声明的 Agent 服务。
进程托管继续看 clients.rs 和
crates/process-apps。Shell 启动子进程、连接窗口管理协议,
转发输入并显示子进程画面。Terminal 在 macOS/Windows,以及启用 Vulkan 的 Linux
Wayland 会话中通常走这条路径;模块回退和覆盖配置见桌面 README。
原生应用还可以通过 peer_link
访问自己的 Agent。进程应用在已有的、经过认证的 hub socket 上发送 octos_peer 消息。
Shell 将帧归属到它启动的应用,检查 agent.octos 授权和用户同意,再把请求接到模块也使用的
app-peer broker。模块的 OctosPeer 通道则通过 module_connected 和
on_module_frame 接入同一路径。工具结果和取消沿该链路返回;进程死亡时,关闭它的上下文,
让等待中的调用失败,同时保留持久 peer。Terminal 是唯一带 Agent 的进程应用:它的
native-apps.json 条目为 Agent 授予了全部四个 octos.* 服务;它自己的 Agent 只能调用两个只读工具
terminal.read_screen 和 terminal.read_scrollback(own_tools)。native-apps.json
中的另一个进程应用 Task 没有 Agent。
桌面会打包选定的系统 bundle;可以从启动器打开 News 或 Calendar。
单独预览时,先按 App Hub 仓库的说明准备依赖,然后在该检出目录运行 card-host:
# 将路径替换为你的 OctoSense 检出目录。
cargo run --locked --release -p octosense-card-host --bin card-host -- --bundle /path/to/OctoSense/apps/news/bundle --system--system 准入内置 os.* bundle。Shell 的宿主服务和 Agent UI 需要完整 Shell。
例如,在 OctoSense 根目录运行 Mail 的演示服务:
MAKEPAD_APP_CONFIG='{"mail_demo":true}' cargo run --locked --release -p octosense演示账户密码为 demo,发送留在演示环境。新商店应用在 OctoSense App Flow(原 Design Flow)中开发,使用 App Hub 的准入/发布流程。本地签名目录配方用于在发布前测试应用能否安装到 Shell。
desktop/system-apps.json 和
phone/system-apps.json 选择 apps/ 中的 bundle。
阅读 apps.rs 中的启动器条目、agent_apps 和
register_host_services。App Hub 的原生 CARD_MODULE 托管这些解释执行程序;
可选的 AppCard 助手使用自己的模块。
在桌面上,脚本应用的窗口按清单 window 请求的尺寸打开。window 是 App Hub 的可选提示,
需要 "schema_minor": 1。lib.rs 中的
App::launch_module_as 用 apps::window_hint 从 Card 运行器将要打开的应用包中读取它,
desktop_layout.rs 中的 preferred_size
再把它限制在桌面减去每个新窗口都保留的边距之内(左右各 42 点,上下各 32 点),且不小于
80 x 60。程序坞浮在桌面之上、不预留区域时(macOS 和 OctoSense 风格),底部边距改为从程序坞
上沿算起(desktop::dock_overlay),所以窗口的下边缘不会位于程序坞后面。没有 window 时,窗口宽度取桌面的 72%、高度取 76%,最大 1000 x 720 点。
两种情况下用户都可以调整窗口大小;手机忽略这个提示。PDF Tools 请求它设计图的 1536 x 1024。
原生模块打开额外窗口(makepad 的 ModuleWindows::open)时,也可以为它指定尺寸。
App::drain_module_windows 通过 desktop_layout::requested_size 以同样的方式限制这个尺寸;
尺寸为零、为负数或不是有限值时视为没有指定,窗口按默认尺寸打开。OctoSense 自带的原生应用
目前都没有指定尺寸:Rinx 的 Moments 和 Article editor 窗口不传尺寸,其他原生应用不打开额外窗口。
通过标题栏按钮、双击标题或最大化快捷键最大化的窗口会填满工作区;吸附的窗口填满工作区中
属于它的那一部分。两者都止于浮在桌面之上的程序坞的上沿:DesktopWindows::rects 填满程序坞
上方的 desktop_layout::room,程序坞的高度由 shell 在每次绘制之前从 App::dock_overlay 取得并记录
(DesktopWindows::dock),新窗口的尺寸也用同一个查询。Shell 全屏、程序坞因此隐藏时
(desktop::dock_auto_hides),这个高度为 0,最大化的窗口会填满整个工作区。
还原时窗口回到它自己的位置和尺寸。程序坞预留区域(Windows)或没有程序坞时,最大化的窗口
像以前一样填满整个工作区。
按 ⌘F 或在 macOS 系列样式中点绿色按钮,窗口进入全屏,铺满整个桌面。DesktopWindow::fullscreen
与 maximized 相互独立,WmLayout::rects_of 给窗口越过外边距的矩形,这块矩形由
WmDesk::draw_walk 每帧按桌面自身的矩形设置。WmDesk::draw_tile 只绘制窗口内容。顶栏和浮动程序坞
隐藏(desktop::front_fullscreen),直到指针停在屏幕顶边或底边(edge_reveal.rs、
App::top_edge、App::place_bar)。退出全屏时,窗口的矩形、缩放状态和它在窗口堆叠中的位置都
原样恢复,因为它们从未改变。
脚本应用在用户点按或点击的处理函数中调用
host.request("window.fullscreen", {on: true}, fn(r){…}),让自己的窗口进入演示全屏
(app_fullscreen.rs)。Shell 的 window
宿主服务把请求排入队列。事件处理完后,App::serve_window_requests 查找由该应用的 Card
运行器运行发出请求的那个 isolate 的窗口(App::window_of),所以应用只能改动自己的窗口。
用户在最近五秒内没有在这个窗口里按过应用时,请求被拒绝(Activation):makepad 无法分辨
host.request 来自哪个处理函数,所以 Shell 像浏览器一样保留一个短暂的激活状态。只有
MpRunViewAction::Pressed 才算数:MpModuleView 在按下落在模块的活动根部件上、且上方没有
宿主服务面板时发出它;分组标签、已停止应用的关闭画面和进程窗口只发出用于切换焦点的 Clicked。
演示全屏以应用自己的 {on: false} 以外的方式结束时,窗口的按下记录随之作废
(Presenter::finish),所以应用不能在钩子里立刻弹回全屏。来自 Agent 工具调用的
{on: true} 也会被拒绝:App Hub 的 Card 运行器在应用的工具调用进行期间收回请求的界面权限
(ServiceCall::may_prompt),而且从不因为某个能力而收回。随后
WmLayout::set_fullscreen 让这个窗口进入全屏,WmState::presentation 让任何屏幕边缘都
不再唤出顶栏、标题栏或程序坞(desktop::dock_shows、App::desktop_pointer),就像幻灯片的
放映模式。Shell 自己的窗口如果还不在宿主全屏中,就进入全屏(HostFullscreen,使用栏上窗口
控制按钮的同一个调用:macOS 上是原生全屏,其他平台是 makepad 的最大化;makepad 的
Window::fullscreen 在 macOS 上目前不起作用),OSD 显示 "Press Esc to exit full screen"
三秒。以下任一情况结束演示全屏:Esc、{on: false}、⌘F、窗口关闭或失去焦点、应用模块出错、
切换风格、用户离开宿主全屏。App::sync_presentation 在每个事件之后检查;只有这次演示全屏让
宿主窗口进入了全屏时,宿主窗口才退出全屏,即使它在演示结束之后 20 秒内才进入全屏
(PENDING_LEAVE_SECONDS)。回答是
{applied, fullscreen, fills_screen},应用可选的
on_app_fullscreen(on) 钩子在开始和结束时都会收到通知。在手机上,这个请求不改变任何东西
(fills_screen: true)。隐藏窗口运行的 Shell(MAKEPAD_HIDE_WINDOWS)从不让宿主窗口进入
全屏,所以在真实显示器上进入 macOS 全屏尚未验证;Windows 和 Linux 上的最大化同样未验证。
准入验证应用包,解析元数据与资源上限。能力名称披露用途,不是执行许可。脚本的 host.request(...) 按应用身份执行。
App Hub 的 crates/appstore/src/services.rs 定义 HostService、ServiceCall 和回复机制;
调用携带应用身份和宿主目录,供服务检查。只有明确声明的 Agent 工具,才会把相应操作提供给模型。
从 Calendar 的 tools.json 和
CalendarService 开始。
JSON 声明 schema 和策略;Rust 的 handle 分派方法,load/save 管理
<host_dir>/calendar/events.json。
sequenceDiagram
participant H as 人或系统 Agent
participant A as Calendar 应用 Agent
participant R as Shell 工具中继
participant C as Calendar Rust 服务
participant D as events.json
H->>A: 请求 Calendar 添加日程
A->>R: calendar.add_event 和结构化参数
R->>R: 检查调用者、授权、schema 和策略
R->>C: 以 os.calendar 身份执行
C->>D: 读取、校验并保存
C-->>R: 对象结果(id/start)
R-->>A: 工具结果
A-->>H: 回答原会话
script_apps.rs 加载工具并实现
HostServiceExecutor;relay.rs 负责授权。
calendar.remove_event 需要破坏性操作审批。Shell 的
审批路由 应用用户设定的常设规则,或显示确认面板;
已注册的 confirm: app 工具使用所属应用的面板。开发者模式提供另外一种由用户开启的覆盖。
路由在仅所有者可访问的审计日志中记录决定。这些路径决定工具是否运行;模型只提供调用和参数。
内置声明提供以下操作:
| 拥有 Agent 的应用 | 工具与实现 |
|---|---|
| News | news.list、news.read、news.notify;News 宿主服务处理调用,并将通知交给 Shell。 |
| accounts/folders/sync/list/peek 绑定 broker 账户;peek 不标记已读。notify/publish_card 经 Shell 发布,skip_event 记录无需操作的决策。发信和凭据仍不对 Agent 开放。 | |
| Calendar | calendar.events、add_event、update_event、remove_event、notify、agenda;Rust 服务管理日程及事件/议程卡片模板。 |
| Photos、Maps、YouTube、Camera | 只有 <namespace>.notify;Shell 的 NoticeService 处理各应用的命名空间。Camera 由 Home 打包。 |
AI providers 配置宿主,目前不声明应用 Agent。Calendar 的月历、按日列表和编辑器
披露 calendar 用途,服务检查已准入应用身份与数据范围,并与上述 Agent 工具共享本地日程存储。Glance 中已保存日程
的卡片可打开 Calendar 中同一条记录;编辑日程也会刷新它的卡片。
共用通知的实现见 glance_notice.rs 和
resources/glance/notice.card。
Mail 和 News 保留自己的服务并安装通知回调;serve_system_apps 只为尚无服务的命名空间
注册 NoticeService。publish_args 填入应用名称/图标、时间、标题和正文,
再由 glance::publish_for 检查发布者当前准入状态与账户身份。通知卡片可以打开所属应用,
并设置 notify: true。Calendar 继续使用自己的事件和议程模板。
更通用的 glance.publish API 接受 L0 source
和可选 data,或自带值的 Splash script,两种卡片都在发布应用的策略下渲染。
notify: true 排入一条 toast 通知;用户关闭卡片时,宿主的 glance::dismiss 删除它。
上述固定通知工具接受文本参数,script-card API 则用于更丰富的应用界面。
用户在这些界面上的操作使用应用自己的 API 权限。
在 main 上,Agent 的工具调用若最终映射到 glance.publish,只能指定应用已准入应用包中的模板
并提供 initial 对象,或提交合法的 L0 源码,不能提交 script(见
script_apps.rs 中的 check_agent_publication)。
desktop-v0.1.0-beta.2 仍接受 Agent 发布的 script 卡片。
实现旁的测试直接展示这些约定:script_apps.rs 中的
a_host_service_tool_runs_as_the_apps_own_request、glance_notice.rs 中的
system_apps_without_a_service_of_their_own_get_the_notice_service,以及 glance.rs 中的
a_script_card_is_admitted_as_it_is。它们分别检查调用归属、命名空间回退和脚本卡片准入路径。
增加工具时,保持数据边界清晰:
| 边界 | 访问路径 |
|---|---|
| 脚本存储 | 在有配额的应用私有隔离目录中使用运行时 storage API,即使遗漏 storage 声明也一样。 |
| Agent 工作目录 | 通过获授权的文件工具和 Shell 策略访问 peer 的应用/账户目录。 |
| 宿主服务数据库 | 经明确的 Rust 方法/工具访问 Calendar 日程、Mail 缓存和 News 数据。凭据保留在宿主确认面板和保险库中。 |
跨应用调用由请求方在 agent.tools 中列出带点号的工具名,所有者必须声明可共享,
relay 也必须授权。准入还检查 HostLimits.offered_tools:默认提供的工具中没有
mail.send 等名称。先补齐声明、准入 offer 和执行器路径,再将其写成可用的集成方案。
Agent 导读继续解释委派和向其他 Agent 请求帮助。
桌面上聚焦拥有 Agent 的应用,从栏按钮、Shift+F8 或菜单打开 Ask <app>。
F8 打开系统 Agent。agents.list 报告应用 Agent;agents.ask 等待首次同意和 peer
准备完成,再返回 peer slug。系统 Agent 用 peer_send_input 发送任务,用 peer_gather
获取答案。声明了 sys.chat 的卡片也可以访问其应用 Agent。工作区中的发布者如果声明了 agent 却没有内嵌会话,宿主会提供 Card / Chat 页签及绑定账户的会话;发布数据和当前 L0 状态只是上下文,不授予工具权限。Enable assistant 打开首次使用许可。这也适用于手机上的内置通知和 Calendar 卡片。显式 Mail 演示会话仍返回预设文本。手机触控导航尚无打开独立 Ask-app 面板的控件。
继续阅读 app_chat/、
system_chat/、
agents.rs、
contained.rs 和
app-peers。人的工作和系统 Agent 的工作使用 peer 上
不同的 session/lane;面板的 Stop 中断人的回合。Makepad 处理 UI 事件,broker/内核通道
传递请求和通知;配套导读将这些通道映射到 Rust 任务。
桌面需配置兼容的 OCTOS_APP_CORE_BIN,并在 AI providers 中设置提供方;
octos-core 在构建中启用集成。详见内核指南。
Android 打包 liboctos.so 并作为子进程运行,OpenHarmony 在进程内运行内核。
应用 UI 托管与内核托管分别选择。
在 phone/ 中运行 Cargo,让其 .cargo/config.toml 选择手机版 bundle:
cargo run --locked --release -p octosense-home --features mobile-only
# 同时链接 Reference 和 Sheets。
cargo run --locked --release -p octosense-home --features mobile-only,mobile-appsphone/src/main.rs 定义含有 #[deref] shell: ShellApp
和 SettingsRuntime 的 App。install_ext 注册受信任的设置模块。handle_event
先处理设置启动/计时及入口 intent,把事件传给 Shell,最后消费排队的平台包和设置请求。
如果设置更新丢失,先检查这个顺序。
settings_app.rs、
settings_script.rs 和
settings_script_host_facade.rs
将脚本控制器/UI 接到 Rust 宿主校验。权限来自编译进来的受信任单例。
android_settings.rs 根据 channel 分派观测与结果。
命令被接受的结果和随后观测到的平台状态使用不同处理器;后者确认设备当前实际状态。
Android 路径继续进入
MakepadAppExtension.java、
其客户端,以及 phone/android/contracts/。
桥接的 SystemBridgeService.java
使用 Binder 回调和调用者检查。独立 Home 在 Android 授予的权限/角色内工作,
ROM 提供额外的特权组件。Home 构建说明
介绍 APK 打包及单独的设备验证步骤。
阅读 octosense.mk 和
Android.bp 中的产品配置,再看
build-home.py、
stage-home.py 和
stage-forks.sh。构建产生 Home/Bridge APK 对和回执,
staging 校验并复制产物,LineageOS 构建产生镜像。安装、刷机和 OTA 分别有自己的脚本和验证步骤。
AgentPlatformService.java
在 caller 中检查 Binder UID、允许的包身份和平台签名。
IAgentPlatform.aidl
定义的方法调用平台后端并返回能力/结果。Android 管理其服务生命周期和 Binder 执行;
LLM 系统对话则归前文介绍的 octos 内核所有。
Home 的 AgentPlatformClient 连接这一可选 ROM 服务。System Bridge、Quickstep、
SystemUI 和 Settings broker 各自有 Android 角色及权限边界。
按照 ROM 的验证说明,
在分配给本任务的设备上检查启动、平台操作和更新行为。
apps/appcard/module/src/lib.rs 将 AppCard
适配为 AppModule;apps/appcard/app/app 实现
router/composer 和生成卡片,旁边还有 transport/store/render crates。
使用 --features app-appcard 启用;默认构建和 mobile-apps 不包含它。
共用 Shell 独立实现系统聊天、应用聊天和隔离应用 peer。AppCard 的旧 personal-data
集成读取较早的原生 Mail 格式,而当前 Mail 在宿主服务中管理缓存。
学习原生托管先看 Reference,学习应用工具先看 Calendar/News;
修改 AppCard 的路由和卡片生成产品时,再读它自己的文档。
crates/updater-ui 是不提供代理工具的原生模块。在 macOS 上,其工作线程校验并
暂存发布包,再把等待中的辅助程序交给 crates/shell/src/update_restart.rs。
shell 请求进程内模块和独立应用退出;取消或未回答的关闭请求不会为更新而强制
结束应用。只有完成关闭确认才提交辅助程序。旧进程退出后,辅助程序先按已安装
应用自身的签名身份检查新应用的代码签名(crates/updater/src/macos/codesign.rs,
使用 Apple 的 Security 框架),再原子交换应用包,并以相同配置启动。
crates/updater/src/macos 确认新版本启动,或恢复保留的旧包;提交之后任何一步
失败都会重新打开旧应用。更新器指南 说明旧发布的
兼容性检查和手动恢复途径。