English | 简体中文
Host OS API status tracks file, location and external-link support, existing services, remaining gaps and validation limits.
OctoSense Updates checks and downloads verified host releases, then opens the OS installation flow. It is available in desktop RC6. RC5 and older releases need one manual upgrade before using it for later versions.
The source build adds macOS Install & Restart, with app close review and a retained previous bundle; this is not in RC6 yet. See the updater guide.
Capabilities and execution boundaries explains declaration-only APIs, consent, accounts and app isolation.
Desktop prerelease download: OctoSense 0.1.0-rc.6 includes app-contract 1.12.0, WASI TCP/UDP/DNS, shared components, GitHub-attested app installation and OctoSense Updates. Choose the package for your operating system and architecture. The release notes record verified checksums, GitHub signature, platform acceptance and remaining limits. This desktop release does not update Android Home.
Run the live Mail → card → Calendar demo with your own mailbox: pinned source, selective policy, fictional test inputs, human draft edits and Calendar navigation.
OctoSense is an agent shell that runs on top of an ordinary operating system. On screen it is a launcher and apps that look like the ones you know. Behind them, one AI kernel runs a system agent that works for the person and an app agent for each app that has one. Agents reach apps, and risky actions reach the person, only through the shell.
This repository holds the shell, its services, the system apps, and the three products built from them. Most native apps come from other repositories: the OctoSense fork of Makepad, App Hub and Rinx (What it depends on).
The script apps share responsive, theme-aware interface styles across desktop and phone. Local native UX checks cover saved state and actual interaction; phone-size previews remain separate from device acceptance.
| Product | What it is | Where |
|---|---|---|
| OctoSense desktop | The shell as one Makepad window on macOS, Windows or Linux; platform requirements and validation are recorded in the release notes | desktop/ |
| OctoSense Home | The phone shell, an ordinary Home app for any Android phone (also OpenHarmony and the iOS simulator) | phone/ |
| OctoSense ROM | LineageOS 22.2 for the OnePlus 6 with Home, the privileged system bridge, Quickstep and SystemUI preinstalled | rom/ |
Building an app? You don't need this repository to build, check or publish one. Start with OctoSense App Flow (formerly Design Flow; read
AGENTS.md, thendocs/QUICKSTART.md) and OctoSense-App-Hub. The system apps inapps/are complete examples. Build the desktop shell from here only to try your app in a shell before you publish it (PUBLISHING §4).
Native chart APIs describe Makepad D3 widgets shared by full apps and Glance, their compatibility requirement and data handling limits. The integration is included in desktop RC6.
Android Home supports page swipes from either side edge, velocity-aware settling, and elastic Search/Glance scrolling; apps and opened cards retain Back. See phone gestures.
| Term | What it means here |
|---|---|
| octos | An open-source agent kernel written in Rust (octos-org/octos). It runs agent sessions: a conversation with a model, with its own tools, memory and workspace folder. A session can own other sessions, called peers. OctoSense runs one octos per device. |
| OUP | The octos UI protocol: JSON-RPC 2.0 messages (octos-ui/v1alpha1) between octos and its clients, normally over the kernel's stdin and stdout. |
| Shell | The one OctoSense process on a device. It draws everything, hosts the apps and holds the only full connection to octos. Desktop and phone build the same crate, crates/shell. |
| System agent | The person's assistant: one octos session that owns and supervises every app agent. |
| App agent | An octos peer for one app and one account, so Mail with two accounts has two agents. Each has its own memory, transcript, model and tool list, and usually a folder to work in. |
| Lane | One of an app agent's two conversations, which run side by side: the system agent's lane, and the person's lane, which also carries the turns the app starts itself. |
| Native app | A Rust app built with Makepad and listed in native-apps.json. It runs inside the shell as a module, or as its own sandboxed process (the Terminal on the desktop). |
| Script app | An OctoScript bundle: a manifest, a UI written in Splash (Makepad's UI script language) and, optionally, its agent's tools in tools.json. It reaches the shell only through host.request. The system apps (Mail, Calendar, News and the rest) and every store app are script apps. |
| Card runner | App Hub's runtime for script apps, inside the shell process: one isolated script VM per app instance, with its own file jail and quota. |
| Host service | Rust code in the shell that answers one family of host.request calls (mail.*, calendar.*) and runs that app's agent tools. |
| Peer link | How native apps other than Rinx talk to their own agent through the shell: Makepad's OctosPeer client, carried over the app's hub connection when it is a process, or over an in-memory channel when it is a module. |
| Glance card | A small card an app or its agent posts to the glance panel (desktop) or glance page (phone). It is either an L0 card (declarations only: the host fills in the data, and there are no expressions or calls) or a Splash script. |
| Approval router | The shell code that decides whether a tool call runs, needs the person, or is refused. |
To read the code in order, start with From an app window to an agent turn. The product walkthrough adds how to run each product.
Connected App Hub samples share a host-owned GitHub/Google OAuth service, without an OctoSense account. Start with the service and sample guide and ADR 0010. GitHub Notes reuses Rinx’s Markdown editor; Inbox Assistant and Google Calendar are ordinary bundles. Provider login requires distributor-supplied OAuth registrations. RC2, RC1 and older beta.2 downloads contain none; the RC6 release workflow also supplies no provider registrations. An operator must supply the private host oauth/clients.json override or build with the registration settings. Installing an app from App Hub does not configure its provider login. Ordinary app users should receive a configured build. Live GitHub and Google sign-in has passed on macOS, and a macOS test-account Calendar login/save passed on its recorded build; this is not public Google verification. GitHub writes and Gmail sends are still unverified, and Google sign-in on Android still needs its native adapter.
App Hub in this source build defaults to the GitHub-attested catalog and supports
publisher-github-v1 releases through app-contract 1.12.0 (desktop RC2: 1.10.0; RC1: 1.8.0). Developers request
publication by opening an App Hub submission issue.
App Hub accepts only GitHub-attested releases, so a developer needs no publisher
key: the app's public repository is its publisher identity
(App Hub ADR 0002).
A Hub admin still approves admission. The first catalog fetch needs a network connection;
an old offline catalog is not converted into a GitHub proof. Custom local test
catalogs can explicitly select OCTOSENSE_HUB_CATALOG=legacy with a fresh app-data
directory; a library with a v2 cache refuses that downgrade. Beta.2 does not
support this publishing mode. RC2 can install compatible GitHub-attested apps; apps that require shared components or newer host APIs need a compatible newer host. See capability and release status.
App Studio on the phone now has developer-only tools to check, open, inspect, exercise and locally install an offline main.splash app. Preview state is disposable; installed app state persists under its developer grant. Follow the Studio code walkthrough.
A fresh model-authored Task Planner passed 129 physical-device tool calls on a OnePlus 6 before the merges of main, and a second one passed the same flow on a stock Xiaomi after them (re-validation); both runs covered interaction and installed-state persistence, and the portrait app and keyboard visual review passed on both. Fault injection remains pending; image generation, the editable toolbox flow and public publishing remain future work. Home's build options provide developer mode and a separate test package.
One shell process per device, one octos kernel per shell, and every agent is a session in that kernel. The shell is the kernel's only full client. It starts octos and holds its host token, starts every app agent's turns, relays every call to an app's tools, and owns every approval. Apps never talk to the kernel.
Text version (Mermaid)
flowchart LR
person(["Person"])
ext["Talk to Octos client<br/>web or terminal, opt-in"]
subgraph shellp["OctoSense shell process"]
ui["Window manager, launcher,<br/>system chat, Ask app panel, sheets"]
mods["Native modules<br/>App Hub, Rinx, Notes, Clock, …"]
runner["Card runner<br/>script apps, glance cards"]
aihost["ai-host + app-peers broker<br/>host connection"]
relay["Host-tool relay"]
router["Approval router"]
svc["App host services<br/>mail, calendar, news"]
end
term["Terminal<br/>process app in an OS sandbox"]
subgraph kern["octos kernel: child process, in process on OpenHarmony"]
sys["System agent<br/>system session"]
peers["App agents<br/>one peer per app and account"]
end
person --> ui
ui --- mods
ui --- runner
ui -->|"person's turns:<br/>Ask app, in-card chat"| aihost
mods -->|"OctosPeer, OctosAppService"| aihost
runner -->|"host.request octos.*"| aihost
term <-->|"hub: frames, AI bus"| ui
term -.->|"peer link"| aihost
aihost <-->|"OUP, host token"| kern
sys -->|"peer_send_input"| peers
kern -->|"peer/tool/call, approvals"| relay
relay --> router
relay -->|"app tools"| svc
svc -->|"glance.publish: cards"| runner
ext -.->|"external token:<br/>system conversation only"| sys
- The shell hosts the window manager, the native apps, the Card runner with the script apps, the system chat, the approval router and the host-tool relay. Its AI side is
crates/ai-host, with the app-peers broker that drives each app agent. - The octos kernel (
crates/kernel) starts on its first connection and exits with the shell. On the desktop and Android it is a child process that speaks OUP over stdio; on OpenHarmony it runs inside the shell; iOS has none. The person picks models and enters keys in the AI providers app, on host sheets. Keys stay with the shell (in the macOS keychain, elsewhere in a file in the shell's own data) and never reach an app. - Process apps run outside the shell in an OS sandbox (Seatbelt on macOS, Landlock and seccomp on Linux, none yet on Windows). Today those are the Terminal and Task (Task Manager, which has no agent), on a desktop built from a checkout; release packages don't ship their binaries yet, so there the Terminal runs inside the shell and Task is absent. The Terminal sends its frames over the shell's local hub and reaches its agent over the peer link on the same connection.
- External clients (Talk to Octos, opt-in) can use the system conversation from a browser or a terminal with a limited token. They get no app agent, no
peer/*method and no host tool.
The opt-in AppCard prototype is the one exception to all this: it opens its own kernel connection instead of going through the broker. For the full picture with code paths, read docs/architecture.md; for the trust model and how to test the AI services locally, docs/ai-services.md; for the decisions behind it, ADR 0004.
The system agent is the octos session _main:api:octosense#system. The person talks to it in the system chat: F8 or the dock's Assistant icon on the desktop, the Assistant tile on the phone. It has two sets of tools:
- Its own kernel tools, the fixed list in
SYSTEM_AGENT_TOOLS: thepeer_*tools to supervise app agents, plus files in its own workspace, memory, questions to the person and web search. octos's own shell tools are never on it. - Host tools from the shell:
agents.listandagents.askto find app agents and ask the person to allow one,agents.provisionandagents.statusto run Mail's new-mail automation,terminal.runwhile Setup's Command execution switch is on and the Terminal runs as its own sandboxed process, and the read tools native apps share with it (the table's last column).
The system agent can never approve a tool call; only the person can.
An app agent exists once the person allows it, on a sheet shown once per app. When it runs depends on the kind of app:
- A script app's agent is prepared as soon as it is allowed, and again at each startup, with the app's tools registered, so the system agent's
peer_listsees it even while the app is closed. - A native app's agent belongs to the app's open window and is live only while the app is open; its memory and transcript persist between openings. Its tools run in that window too: with Notes closed, a call answers "Open Notes first".
Signing out keeps an agent. Removing the account or uninstalling the app erases its transcripts and memory.
These apps have an agent:
| App | Kind | Its agent's own tools | What the system agent may call |
|---|---|---|---|
| Rinx (Matrix chat) | native, in the shell | octos's file, memory and web tools | – |
| Terminal (desktop) | native; its own process in a checkout build, inside the shell in a release package | terminal.read_screen, terminal.read_scrollback |
terminal.run, only while it runs as its own sandboxed process, behind Setup's switch, approved per command |
| Calculator, Clock, Notes, Reminders, Weather | native, in the shell | each app's read tools | the same read tools |
| App Hub | native, in the shell | apphub.search, apphub.installed, apphub.updates, read only: installs and updates stay on App Hub's own screens |
the same read tools |
| script app | mail.* tools scoped to the signed-in account: reads, cards (mail.publish_card), and reply drafts it can propose but never send |
– | |
| Calendar | script app | calendar.events, calendar.add_event, calendar.remove_event (asks first), calendar.notify, calendar.agenda |
– |
| News | script app | news.list, news.read, news.notify |
– |
| Photos, Maps, YouTube; Camera on phones | script apps | <app>.notify |
– |
AI providers has no agent.
The system agent never runs an app's tools itself. It asks the app's agent to do the work:
- The system agent calls
peer_send_inputwith the request in plain words. - octos does not run that turn on its own. It passes it to the shell as a
peer/inputevent. - The shell starts the turn on the app agent's session, with the app's tools, memory and approval rules. It refuses the input if the person has not allowed the agent or the account is signed out.
- The result lands on the peers' shared blackboard, where the system agent reads it with
peer_gather.
Here the person asks for a heads-up on the glance screen, and Mail's agent posts a card:
Text version (Mermaid)
sequenceDiagram
autonumber
actor P as Person
participant S as System agent
participant B as Shell: app-peers broker
participant A as Mail's agent (kernel-issued peer)
participant R as Shell: tool relay
participant M as Mail's host service
participant G as Shell: glance service
P->>S: "Tell me on the glance screen when ..."
S->>B: peer_send_input (octos delivers peer/input)
B->>A: turn/start on the peer's session, with Mail's tools
A->>R: peer/tool/call mail.notify {title, body}
R->>R: grant, consent, schema, budget
R->>M: run on Mail's host service
M->>G: glance.publish as os.mail: notice.card, notify
G-->>P: desktop: a toast and the glance panel; phone: a shade notification
M-->>R: {card_id}
R-->>A: peer/tool/result
A-->>S: the turn's result on the blackboard (peer_gather)
The <app>.notify tools fill a fixed card template (notice.card, or Calendar's event and agenda cards), so the model writes only the text. Mail also has mail.publish_card, which takes a card the model wrote and checks it before publishing. Either way the shell publishes under the admitted app identity; glance declarations describe usage, while card validation and caller checks still apply.
Mail's agent can also start on its own. Once the person has signed in, allowed Mail's agent and asked the system agent to turn on new-mail processing (agents.provision), the host syncs the inbox independently of model turns and queues each new message for the agent. The agent reads the message with its scoped tools and decides whether to post a card. The Mail event walkthrough describes this built-in path. Independently installed Gmail apps get the same kind of event through the connected-account service: they declare the account-bound trigger <app namespace>.new_message, where the app namespace is the last segment of the app id (OAuth guide).
The system agent and the person talk to the same app agent, each in a lane of their own:
Text version (Mermaid)
flowchart TB
sys["System agent"]
person(["Person"])
mini["Rinx mini apps"]
subgraph peer["App agent: one peer per app and account"]
direction TB
lane1["System agent's lane<br/>peer session #peer-app"]
lane2["Person's lane<br/>request context #peerctx-app.id<br/>opened with share_history"]
own[("The app's workspace,<br/>memory and tools")]
ctx["Other request contexts<br/>no sharing"]
end
sys -->|"peer_send_input"| input["octos: peer/input<br/>to the shell"]
input -->|"the shell starts the turn"| lane1
person -->|"the shell's Ask-app panel,<br/>a card's chat or the app's own UI"| lane2
lane1 -.->|"recent messages, read-only"| lane2
lane2 -.->|"recent messages, read-only"| lane1
lane1 --- own
lane2 --- own
mini -->|"open_context"| ctx
- The system agent's lane is the peer's own session,
…#peer-<app>. - The person's lane is a request context,
…#peerctx-<app>.<id>, opened withshare_history. The person's turns run there, and so do the turns the app starts itself.
The lanes run in parallel, so the person never waits behind a system agent task. Each turn sees the other lane's recent messages as read-only context, and every message is labelled with its speaker ([from the person: Mail], [from the system agent]). The person's turns also leave their results on the blackboard, so the system agent knows what was done. Rinx mini apps get private contexts of their own (open_context), shared with neither lane.
The person can talk to any app's agent directly. These turns run in the person's lane with the app's tools, memory and approvals, just as the system agent's turns do.
| Where | How |
|---|---|
| "Ask <app>" panel | A shell panel for every app with an agent, opened from the bar's "Ask <app>" button or with Shift+F8. On the desktop it opens beside the system chat. The phone draws it full screen but has no touch control for it yet. |
| In-card chat | Open a card from an app with an agent and switch to its Chat tab, or type in a card that declares sys.chat (below). |
| The app's own UI | An app can open the person's lane itself (next section), though no shipped app does yet; for all of them the "Ask <app>" panel is the way in. |
The panel asks for consent first and shows both lanes, each message with its speaker. Its Stop button ends only the person's own turn. docs/architecture.md §2 covers the rest of its behavior.
An app reaches its agent only through the shell, never through the raw kernel protocol, and the shell stamps the app's identity on every call.
| Kind of app | API | Used by |
|---|---|---|
| Script app | host.request("octos.session.open" / "octos.session.history" / "octos.turn.start" / "octos.turn.interrupt"), for an admitted app with an assistant offer and user consent |
store apps; the shell also drives system-app agents. |
| Native app, in the shell or as a process | Makepad's OctosPeer client over the peer link: open the link, then serve_tools to answer the agent's calls to the app's own tools. The same code works in either hosting. |
App Hub, Calculator, Clock, Notes, Reminders, Weather, Terminal |
| Native app with the injected service | OctosAppService: open_conversation for the person's lane, open_context for a private context |
Rinx, which uses only open_context, for its mini apps |
A script app first offers an assistant through its agent/tools metadata. Its capability list can describe these calls; it does not create or authorize an assistant:
"capabilities": ["octos.session.open", "octos.turn.start"]and a few lines of Splash that open the person's lane and send one turn:
fn ask(){
ui.answer.set_text("Waiting for the assistant…")
host.request("octos.session.open", {}, fn(s){
if !s.is_ok {
ui.answer.set_text("Assistant unavailable: " + s.error)
return
}
host.request("octos.turn.start", {text: ui.prompt.text()}, fn(r){
if r.is_ok { ui.answer.set_text(r.data.text) }
else { ui.answer.set_text("Assistant unavailable: " + r.error) }
})
})
}
In a shell with a kernel, the first call asks the person to allow the app's agent. Treat "unavailable" as a normal state: the device may have no kernel (iOS) or no provider, or the person may have said no. This example and the rest of the API are in App Flow's AI-SERVICES guide.
An agent can only work with what its app hands it. A script app declares all of this in its bundle; a native app declares it in its native-apps.json entry.
- A declaration. The manifest's
agentblock selects tools from the host's admission offer, names the model features it needs (tool_calling) and can supply anAGENT.mdwith instructions and skills, which the shell sends with every turn. Toolbox tools are host-owned shared tools; listing them does not add them to the default store offer. A native app's entry also says which of its tools its own agent may call (own_tools) and which the system agent may call (system_tools). - Tools.
tools.jsondescribes each tool, named<app>.<tool>: its input schema, itsrisk(read,actordestructive), who confirms it (confirm: hostfor a shell sheet,appfor the app's own sheet) and whether other apps' agents may use it (shareable). - Something to run the tools. A declared tool needs an executor: the app's host service (Mail, Calendar, News), the shell's notice service (
<app>.notifyfor the other system apps) or a native app's open window. A store app has no host service of its own, buthost_methodcan map one of its tools to a reviewed method of a shared service (inbox.message→gmail.message); the tool then runs on that service as the app (architecture.md §4). Since desktop RC1, a tool markedimplemented_by: "app"runs in the app's own Splash code while the app is open, withrequires: ["script-tools-v1"]; a closed app returnsapp_not_running. The olderdesktop-v0.1.0-beta.2refuses such a call withapp_tool_unavailable. - Data. The agent works in its account's folder,
apps/<app id>/accounts/<account hash>/(a singledevicefolder for an app without accounts), and reads it with the host's read-onlyfiles.list,files.readandfiles.search(on Unix). A script app can declarestorage.agent_workspace: "none"to give its agent no folder, so it sees only what its tools return; a native app's agent gets its folder either way. No agent sees another account's folder. - Memory. Each agent has its own memory namespace,
app/<app>/acct-<hash>, erased with the account. - A way to reach the person. Its tools can publish admitted Glance templates or L0 cards under the app's verified identity; a
glancedeclaration describes this use, not permission to bypass review. - Events (new mail only, for now). A
triggers.eventsentry lets an agent react without being asked. Mail's agent handles new mail, guided by itsAGENT.mdand a triage skill. An installed app gets new-mail events from its Gmail connection: it needs a valid app-owned connection and an allowed agent whoseagentblock setsbackground: trueand lists<app namespace>.new_message, such asinbox.new_message(OAuth guide).
The steps for adding a tool (manifest, tools.json, grant, handler, approval path) are in AGENTS.md, and the design is ADR 0002.
Text version (Mermaid)
sequenceDiagram
autonumber
participant Ag as App agent turn
participant K as octos kernel
participant Re as Shell relay
participant Ro as Approval router
participant P as Person
participant Ex as Owning app
Ag->>K: call calendar.remove_event
alt confirm host
K->>Ro: approval/requested, host_tool
Ro->>Ro: dev mode, then standing rules
Ro->>P: shell sheet with the exact arguments
P->>Ro: approve or deny
Ro->>K: approval/respond
K->>Re: peer/tool/call, approved
Re->>Re: grant, schema and budget checks
else confirm app
K->>Re: peer/tool/call
Re->>Re: grant, schema and budget checks
Re->>Ro: hand-off, acknowledged to the kernel
Ro->>Ex: the app's own sheet, with the caller
P->>Ex: approve or deny
end
Re->>Ex: run on the app's executor
Ex->>Re: result, checked against its schema
Re->>K: result, answered once
Note over Ro,P: No answer in 10 min, denied, never approved.<br/>Turn still running 30 s later, interrupted.
- The relay (
crates/shell/src/host_tools/) receives everypeer/tool/call. It checks that this caller may use this tool, validates the arguments against the tool's schema and charges the caller's budget (by default 32 tool calls a turn and 1000 a day). Only then does it run the tool in the app that owns it: a native app's open window, a script app's host service, or a process app over its peer link. It checks the result against the schema too. - The approval router (
crates/shell/src/approvals/) decides in a fixed order. Developer mode, which only the person can turn on, approves routed calls for the apps it covers. Aconfirm: apptool goes to the app's own sheet. Calls that must always ask, such as a Terminal command, go straight to a sheet. Everything else may be decided by the person's standing rules, and otherwise a shell sheet shows the exact arguments. Every decision is written to an audit log. architecture.md §5 gives the full order. Sending mail is outside this order. The person always approves the exact message with a physical press on the host's review: a tap on Android or a click on macOS (unverified on macOS, where no real message has been sent). Synthetic and remote input are refused, and developer mode cannot skip the review. - Deadlines. An approval or question nobody answers in 10 minutes is denied, never approved. If the turn is still running 30 seconds later, the shell interrupts it so the next turn can start.
An admitted app publishes cards as itself (glance.publish, glance.withdraw, glance.list); the shell takes the publisher from the caller, never from the arguments. A card runs under its app's own permissions, so a button pressed in a card is the app's own action, not an agent tool call. The desktop README describes the glance panel where cards appear. On the phone, a card's notification opens that card's workspace, or the glance page if the card is gone.
A card can carry a conversation with its app's agent, which answers in the person's lane:
- An opened card becomes a workspace: full screen on the phone, a centred window on the desktop. If the publishing app has an agent, the workspace has Card and Chat tabs, even when the card declares no chat. The shell gives the agent the card's data and local state as context, bound to the account that published it, and the chat uses only the app's existing tools and consent.
- Mail reply cards have Email and Chat tabs over one saved draft. The agent can edit the draft and propose sending it, but only the person sends, with a physical press on the host's review (A tool call with an approval). Windows and Linux cannot approve a send yet. Composed Mail cards has the details and the phone test results.
- A card that declares
sys.chat(app, thread, fields)keeps its declared thread.
The shell owns every transcript, kept in the app's account folder, and records as the person's only what the person typed. Model-written text is marked as AI-written and never runs as an action. See crates/l0-chat.
An agent's ask_user_question appears where its turn came from: in the app's conversation for the person's and the app's own turns, in the system chat for the system agent's. Only the person answers, on a shell surface.
A phone runs the shell, the kernel, up to a dozen app agents and their apps at the same time. These design choices keep that cheap:
- One kernel, and agents are sessions. Every agent lives in the same octos kernel. There, an agent is stored state (its transcript, memory and blackboard entries) plus Tokio tasks while a turn runs. No agent has a process of its own.
- One connection to the kernel. The system chat and every app agent's broker share a single stream to the kernel. A small router (
crates/kernel/src/router.rs) gives each request a unique id and sends each notification to the consumers of its session. - Threads per service, not per turn. Turns are Tokio tasks. In the shell, the kernel service runs on one two-worker Tokio runtime, each live agent's broker on a one-thread runtime of its own (so a slow agent cannot stall another agent or the UI), and the system chat on one thread that wakes the UI through Makepad's
SignalToUIonly when something changed. The octos child process (desktop and Android) uses Tokio's default runtime, one worker per CPU core. - Started on demand, stopped when idle. The kernel starts on its first connection: at startup if the person has already allowed a script app's agent, otherwise when an agent or the system chat first needs it. The system chat connects only while its pane is open or a turn is running. With Talk to Octos off, the kernel stops when its last connection closes. An app with no granted agent gets no broker and no peer.
- Apps share the shell process. Script apps are isolates in one Card runner and native apps are modules, so most apps cost no process of their own. A script error stays inside its isolate, and a native module's panic is caught at the module boundary. Only an app declared as its own process (the Terminal, for its OS sandbox, and Task, which has no module) runs as one, where its binary is available.
- Zero-copy frames for process apps. The Terminal's frames reach the shell as shared GPU surfaces: IOSurface on macOS, D3D11 shared handles on Windows, DMA_BUF on Linux. On Linux the shell starts the Terminal as a process only in a Vulkan build in a Wayland session, and runs it inside the shell otherwise. Task has no module, so it runs as a process on Linux either way; where the GL driver cannot export a DMA_BUF, its frames are copied.
| Path | What it is |
|---|---|
desktop/ |
Desktop packaging, package octosense: the entry point (src/main.rs only), catalogs (config/apps.json), the window-manager sync from upstream Makepad (upstream/, scripts/upstream.py), the desktop's system-app selection. |
phone/ |
The Home app, package octosense-home (APK id dev.makepad.octosense): the entry point, the built-in Settings app, Android, OpenHarmony and iOS packaging, the phone side of the system bridge (android/), the phone's system-app selection. |
rom/ |
The OnePlus 6 ROM image only: vendor/, patches/, image, flash and OTA scripts, the Home APK build scripts, web-installer/, product tests. |
crates/shell/ |
The shell, package octosense-shell: window manager, app hosting, the system chat and "Ask <app>" panel, the host-tool relay and approval router, the phone layer, themes, wallpapers and icons. |
crates/ai-host/ |
The shell's AI services behind one entry point, package octosense-ai-host: the kernel service, the llm and model services, and the octos host service that gives each script app its agent (card.<app id>). |
crates/kernel/ |
The octos kernel as a shell service, package octosense-kernel: one per process, configured by AI providers, shared by every consumer; the system agent's exact tool list. |
crates/app-peers/ |
The app-agent broker, package octosense-app-peers: one peer per (app, account), its two lanes, its tools, peer/input, deadlines, purge. |
crates/l0-chat/ |
The host side of an L0 card's in-card chat (sys.chat), package octosense-l0-chat. |
crates/toolbox/ |
The system toolbox, package octosense-toolbox: workflow templates and mod.research, available behind toolbox-peers subject to the host's tool offer, agent consent and research scope. |
crates/wasm-host/ |
The runtime for apps' own WebAssembly functions, package octosense-wasm-host: Wasmtime, the sandbox and its limits. Current builds include the wasm service on macOS, Windows, Linux, Android and OpenHarmony (Pulley); see the linked guide for platform acceptance. |
apps/ |
The system apps (News, Photos, Maps, Camera, Mail, Calendar, AI providers, YouTube, Quick Deck, PDF Tools, Writer) as script apps, their host services (mail, calendar, news, llm), apps/reference, and the opt-in AppCard assistant. |
native-apps.json |
Every native app: where its code comes from, how it is hosted on each platform, and its agent. tools/native_apps.py generates the shell's code and Cargo entries from it. |
tools/ |
setup.py (the pinned framework sources), the reviewed Makepad runtime patch (runtime-patches/), kernel-artifact.py (the octos kernel build), check-shell-graph.sh (dependency-graph guards). |
docs/ |
The architecture, the code walkthroughs and the ADRs. |
Cargo.toml, Cargo.lock, native-runtime.lock.json, runtime-patches.lock.json |
One workspace. Every external dependency is pinned once; the runtime locks name the OctoScript-Makepad release and the reviewed patch on top of Makepad. |
The shell exists once, in crates/shell (ADR 0001): desktop and phone differ by target and features, not by copies of the source. CI fails if a shell source file appears in two crates.
Since desktop 0.1.0-rc.1, the desktop shell also shows web pages inside apps on Linux (under X11 or XWayland) and on Windows, using the system's WebKitGTK or WebView2 engine. Desktop embedded browser lists what each platform needs. This does not change sign-in or approval support on these platforms.
Pinned exactly once, in the root Cargo.toml and the runtime locks:
| Repository | Role |
|---|---|
| makepad (OctoSense fork) | The UI framework, the cargo-makepad packager and the native apps Calculator, Clock, Notes, Reminders, Weather, Terminal, Sheets and Task. Checked out in .sources/makepad, plus the reviewed runtime patch. |
| OctoScript-Makepad, OctoScript | The runtime release that names the Makepad and OctoScript revisions (native-runtime.lock.json). |
| OctoSense-App-Hub | The signed catalog, the store, and the Card runner that contains every script app. |
| octos | The agent kernel. Android bundles it in the APK as liboctos.so; the desktop runs the packaged octos-kernel beside the shell, checked against this revision (tools/kernel-artifact.py --host --stage builds it). |
| Rinx | Matrix chats and mini apps, hosted as a native module. |
| OctosCode | The coding client of the person's assistant, hosted as a native module that reaches the kernel through its port in the coding scope (ADR 0003, item 9). |
Related, not build inputs: OctoSense-App-Flow (how apps are built and published), OctoScript-Android and OctoScript-OH (other renderers), the OctoSense website.
Stable Rust (cargo in ~/.cargo/bin), Git, Python 3.9+ (3.11 for desktop/scripts/upstream.py) and, on macOS, the Xcode Command Line Tools. Makepad and OctoScript resolve to checkouts in .sources/ (git-ignored) that the setup script prepares at the pinned revisions:
git clone https://github.com/OctoSense-org/OctoSense.git
cd OctoSense
python3 tools/setup.py # prepare .sources/ (makepad, octoscript, octoscript-makepad)
python3 tools/setup.py --check --cargo # verify: one Makepad, App Hub, octos and Rinx in the graph--update moves clean checkouts after the locks change; --cache DIR borrows Git objects from existing clones (DIR/makepad, DIR/octoscript, DIR/octoscript-makepad). Local changes in .sources/ are preserved.
Already have clones of these repositories? Use them as a hub.
Keep one clone of each on the machine and make every .sources/ entry a git worktree of it, so there is one object store per repository and no stale copy. Name the directory that holds the clones (as <dir>/makepad, <dir>/octoscript, <dir>/octoscript-makepad) once, in ~/.config/octosense/sources.json:
{ "hub": "/path/to/clones" }or per run with --hub DIR or OCTOSENSE_SOURCES_HUB=DIR. Setup then fetches each pinned revision into that clone and runs git worktree add --detach .sources/<name> <rev> instead of cloning; --update moves the worktrees. Without a hub (CI, a fresh machine) it clones as before, and --no-hub forces that. A .sources/ entry that is already a full clone is reported, not deleted; --convert replaces it with a worktree when it holds no local work.
Before deleting a checkout of this repository, remove its .sources/ worktrees so the clones keep no stale entries:
python3 tools/setup.py --remove-worktrees # git worktree remove + prune in each clone; stops on local work
git worktree remove <this checkout> # if it is itself a worktreeDesktop (from the root or desktop/; details in desktop/README.md):
cargo run --release -p octosense
cargo check --locked -p octosense --features mobile-apps # the set phones link
cargo check --locked -p octosense -p octosense-appcard --features mobile-apps,app-appcardThe assistant needs the octos kernel beside the shell: python3 tools/kernel-artifact.py --host --stage target/release builds the pinned revision and stages it, once per octos pin. The desktop refuses a staged kernel of another revision and says so (Build and run). Without one, the desktop runs without an assistant.
Phone (from phone/, which selects the phone's system apps; details in phone/README.md):
cd phone
cargo run --release -p octosense-home --features mobile-only # Home in a phone-sized window
cargo check --locked -p octosense-home --features mobile-apps
python3 ../rom/scripts/build-home.py --help # the Home and Bridge APK pair, liboctos.so bundledROM image (Linux build host, external LineageOS tree; not in CI): rom/README.md.
Path-filtered workflows in .github/workflows/, so a change runs only the jobs its paths need:
| Workflow | Runs for | Checks |
|---|---|---|
desktop.yml |
desktop/, crates/, apps/, the workspace files, tools/ |
compiles the desktop (default, mobile-apps, mobile-apps,app-appcard), the shell graph guards, one copy of every shell source, the tools/ tests |
phone.yml |
phone/, crates/, apps/, the workspace files, tools/ |
compiles Home and its bundled modules, the shell graph guards, and runs the tests of the shell, Home, the AI services, App Hub admission and runtime policy on macOS |
apps.yml |
apps/, crates/, the workspace files, tools/setup.py |
the kernel service, app peers, AI providers config, the Mail and llm host services, the shell's AI services, AppCard |
rom.yml |
rom/, phone/android/, the phone's Android resources and tests, tools/kernel-artifact.py |
product tests, the generated Agent Binder client, the web installer |
release-desktop.yml |
a desktop-v* tag, a manual run, or a pull request that changes the packaging |
unsigned desktop packages for macOS, Windows and Linux and the private-path scan; for a tag, signing and a draft release (desktop/README.md) |
Each workflow's graph check (tools/setup.py --check --cargo) asserts one Makepad, one App Hub, one octos and one Rinx in the locked graph. tools/ci-local.sh runs the same steps on your own machine (docs/local-ci.md).
Each product is tagged on its own: desktop-v*, home-v* (the APK) and rom-v* (the image), with build receipts that record the repository commit. A desktop-v* tag builds the desktop packages (.dmg, Windows installer, .deb, .AppImage) into a draft release (Release builds). System apps ship only inside the shells, admitted by digest. rom/docs/updates.md explains how phones find ROM updates.
main is protected: every change goes through a pull request, and force pushes are blocked. One change is one pull request, across desktop/, phone/, crates/ and apps/ as needed. Rules for people and coding agents are in AGENTS.md.
Apache License 2.0 (LICENSE, NOTICE). Source copied from Makepad keeps its MIT notice (LICENSES/). Dependencies keep their own licenses.



