From b519f81c8426bef65e278800c068db99058e7db2 Mon Sep 17 00:00:00 2001 From: ymote <151983+ymote@users.noreply.github.com> Date: Thu, 1 Oct 2026 21:46:36 -0700 Subject: [PATCH] docs: app agents, the system agent and in-card chat as on 2026-10-01 Bring the app-author docs up to OctoSense main f52620c (App Hub 58c3c8ae, octos ae230ce0, Octoscript 5991dfae, Octoscript-Makepad 8f103d0c) and App Hub main 41bc959: - AI-SERVICES (en, zh-CN): a section on the system agent and app agents (who has one, first use, peer_list/peer_send_input, two lanes, questions, purge); how the person talks to an app's agent directly (the "Ask " panel, in-card chat, the app's own octos.* calls); what an app's agent gets today (ask_user_question, files.* read tools, host-service tools only for system apps, implemented_by "app" refused); the storage block (accounts, agent_workspace); outward and auto_approvable; the approval router, audit and relay budgets; script glance cards and notify; Mail and Calendar agents publishing cards; the toolbox for system apps behind toolbox-peers; sys.digest checker merged, host side still OctoSense#87; a new section on model-copy and sys.chat; the version skew of this repository's pinned runtime. - CAPABILITIES: research and crawl rows, prompt vs ask_user_question, the agent row (no longer "nothing runs it"), storage fields, reserved names; KNOWN_CAPABILITIES now in crates/app-contract. - HOST-SERVICES: OctoSense#204 merged, calendar service, mail.notify, service methods as agent tools, Mail secrets folder. - GLOSSARY, SCRIPT-API, README (en, zh-CN), AGENTS.md: the same facts. hub check outputs marked "run 2026-10-01" came from a hub built from App Hub main 41bc959; nothing was run in an OctoSense shell. Co-Authored-By: Claude Opus 5.5 --- AGENTS.md | 16 +- README.md | 45 ++- README.zh-CN.md | 36 ++- docs/AI-SERVICES.md | 572 ++++++++++++++++++++++++++++++++------ docs/AI-SERVICES.zh-CN.md | 462 +++++++++++++++++++++++++----- docs/CAPABILITIES.md | 22 +- docs/GLOSSARY.md | 13 +- docs/HOST-SERVICES.md | 24 +- docs/SCRIPT-API.md | 4 +- 9 files changed, 982 insertions(+), 212 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 83df02ad..e915c6b4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -54,7 +54,15 @@ patch around it here. answers `no service answers "…" on this device`. `llm` is for system apps only. Never put a model key in an app. Read [docs/AI-SERVICES.md](docs/AI-SERVICES.md) before adding one, and report - such a feature as not verified on a device. + such a feature as not verified on a device. An app's own agent + (`"agent": {…, "tools": ["ask_user_question"]}`) is checked here only by + `hub check`. In the shells the person talks to it directly in the + "Ask " panel the shell draws for every app with an agent, so the app + need not draw a chat of its own (desktop; the phone has no entry to the + panel yet). A store app's `tools.json` tools do not run in any shell yet, + so no screen may depend on its agent calling them. L0 cards that use + `sys.chat`, `sys.digest` or `model-copy` text need a newer runtime than + this repository pins: report them as not verified here. - **Only needed capabilities.** Each capability maps to something a screen does ([docs/CAPABILITIES.md](docs/CAPABILITIES.md)). Remove what is unused. - **No dummy screenshots.** Screenshots are captures of the real app in a real @@ -64,8 +72,10 @@ patch around it here. for you; after signing, any edit needs stamp and sign again (human). - **Keep the bundle clean.** Only `manifest.json`, `listing.json`, the entry, artwork and screenshots go in `bundle/` (plus `tools.json`, `AGENT.md` and - `skills/` for an app that declares its own agent; the shells give it a peer - and its tools, but do not install `AGENT.md` or skills yet: + `skills/` for an app that declares its own agent; the shells give it a peer, + `ask_user_question` and read access to its account folder, and register + its tools (which run only for system apps today), but do not install + `AGENT.md` or skills yet: [docs/AI-SERVICES.md](docs/AI-SERVICES.md#an-apps-own-agent)). Notes, keys, logs, review packets and `.local-state/` stay out. - **Run headless.** Start apps with `tools/octo run … --hidden` (Makepad's diff --git a/README.md b/README.md index a703f72c..be210cdc 100644 --- a/README.md +++ b/README.md @@ -240,18 +240,22 @@ release), `capabilities` (the permissions it asks for), `network.hosts` (bare host names, with `net`), and `integrity.bundle_blake3`, written by `hub stamp` (plus a signature after `hub sign-manifest`). Optional requests (`storage.max_bytes`, `compute.*`, `agent`) are clamped to the host's -ceilings; `hub check` prints the resulting `grants:` line. +ceilings; `hub check` prints the resulting `grants:` line. `storage` may +also say whether the app keeps accounts and what its agent may read +(`storage.accounts`, `storage.agent_workspace`). The id may not be, or end +in, a name the host reserves (`terminal`, `rinx`, `system`, `toolbox`, …). **Capabilities** form a closed list defined by the App Hub: `storage`, `net`, `images`, `web`, `camera`, `microphone`, `library`, `location`, `mail`, -`llm`, `news`, `glance`, `model`, `prompt`, `ledger.read`, `clipboard`, plus 49 exact +`llm`, `news`, `glance`, `model`, `research`, `crawl`, `prompt`, `ledger.read`, `clipboard`, plus 49 exact host-service names (four `octos.*` for the device's assistant, 45 `matrix.*` for Rinx). Not requested means not granted, and the store shows the person one plain-language line per capability before install. Ask for the least the app needs. What each unlocks, which have no working path yet (`prompt`, `ledger.read`, `clipboard`) and which only a -system app can use (`llm`): [docs/CAPABILITIES.md](docs/CAPABILITIES.md). -What the assistant capabilities do today (nothing yet, in OctoSense): +system app can use (`llm`, `news`, and for now `research` and `crawl`): +[docs/CAPABILITIES.md](docs/CAPABILITIES.md). What the assistant +capabilities and an app's own agent do today in OctoSense: [docs/AI-SERVICES.md](docs/AI-SERVICES.md). **`listing.json`**: subtitle, description, category, keywords, icon, @@ -304,16 +308,27 @@ lists every check. ## AI in your app OctoSense runs one octos agent kernel per shell, configured by the person in -the AI providers system app; keys never reach an app. **A contained app -cannot use it yet**: the gate accepts the four `octos.*` capabilities, but no -OctoSense shell serves them to apps, and `card-host` serves no host services. -`llm` is provider management for system apps only, and the one-shot `model` -capability (App Hub #24) has no service in any shell yet. An app's own agent -(`tools.json`, `AGENT.md`, skills, triggers) is admitted by App Hub `main` and -planned in OctoSense's ADR 0002, but nothing runs it yet. Build apps that are -complete without AI, and read [docs/AI-SERVICES.md](docs/AI-SERVICES.md) -before adding an AI feature: the exact names, a verified call that handles -"unavailable", what the person sees, the errors, and the planned route. +the AI providers system app; keys never reach an app. On it run the +**system agent** (the shell's own assistant, which the person talks to in +the system chat) and one **app agent** per app that declares one, each on +its own host-owned peer. As of 2026-10-01, on OctoSense `main` +([docs/AI-SERVICES.md](docs/AI-SERVICES.md) has the details and sources): + +| An app can | How | In `card-host` | +| --- | --- | --- | +| Make a one-shot, schema-checked model call | `model` capability, `host.request("model.complete", …)`, within a daily budget | `no service answers "model"` | +| Talk to the assistant from its own screens | the four `octos.*` capabilities, once the person allows the app's agent on a first-use sheet | `no service answers "octos"` | +| Have its own agent, which the person talks to directly (the shell's "Ask " panel, drawn for every app with an agent; an in-card chat; the app's own screens) and the system agent can hand work to | an `agent` block (`"tools": ["ask_user_question"]`), optional `tools.json`; the agent reads the app's account folder (`accounts/device/`) | checked by `hub check` only | +| Publish cards to the glance screen, with an in-card chat its agent answers and model-written text marked AI-written | `glance` capability, `glance.publish` (L0 `sys.chat`, `model-copy`) | `no service answers "glance"`; this repository's runtime predates `sys.chat` | + +Still **coming**: a store app's own `tools.json` tools running (today only +system apps' host-service tools run), the host choosing the agent's model +from `needs`, triggers and background runs, installing `AGENT.md` and +skills, and the system toolbox for store apps. `llm` is provider management +for system apps only. Build apps that are complete without AI, and read +[docs/AI-SERVICES.md](docs/AI-SERVICES.md) before adding an AI feature: the +exact names, a verified call that handles "unavailable", what the person +sees, the errors, and what is planned. ## Running an app @@ -452,7 +467,7 @@ Limits, stated plainly: | [docs/SCRIPT-API.md](docs/SCRIPT-API.md) | The Splash language and every API a contained app may call | | [docs/CAPABILITIES.md](docs/CAPABILITIES.md) | Each capability: what it unlocks, what the person sees, the rules | | [docs/HOST-SERVICES.md](docs/HOST-SERVICES.md) | `host.request`, sheets, "secrets are the host's", adding a service | -| [docs/AI-SERVICES.md](docs/AI-SERVICES.md) ([简体中文](docs/AI-SERVICES.zh-CN.md)) | OctoSense's assistant (octos): what an app can use today, a verified example, what is planned (an app's own agent, its tools, the system toolbox, glance cards, `sys.digest`), with a dated status table | +| [docs/AI-SERVICES.md](docs/AI-SERVICES.md) ([简体中文](docs/AI-SERVICES.zh-CN.md)) | OctoSense's assistant (octos): the system agent and app agents, what an app can use today, a verified example, an app's own agent and its tools, the system toolbox, glance cards, `sys.digest`, AI-written text and in-card chat (`sys.chat`), with a dated status table | | [docs/PUBLISHING.md](docs/PUBLISHING.md) | Publishing to the App Hub, with verified outputs and the checklist | | [docs/GLOSSARY.md](docs/GLOSSARY.md) | One meaning per term | | [docs/NATIVE-WORKSPACE.md](docs/NATIVE-WORKSPACE.md), [docs/l0/](docs/l0/) | Sibling-source setup for the native runtime; L0 card examples | diff --git a/README.zh-CN.md b/README.zh-CN.md index b4800f15..031907ff 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -221,14 +221,16 @@ my-app/ the app's own git repository `capabilities`(申请的权限)、`network.hosts`(纯主机名,需配合 `net`),以及由 `hub stamp` 写入的 `integrity.bundle_blake3`(`hub sign-manifest` 之后还有签名)。可选的 申请项(`storage.max_bytes`、`compute.*`、`agent`)会被限制在宿主的上限以内; -`hub check` 输出的 `grants:` 行就是实际授予的结果。 +`hub check` 输出的 `grants:` 行就是实际授予的结果。`storage` 还可以声明应用是否按账户 +保存数据、它的 Agent 能读什么(`storage.accounts`、`storage.agent_workspace`)。应用 id +不能是、也不能以宿主保留的名字结尾(`terminal`、`rinx`、`system`、`toolbox` 等)。 **Capabilities(权限)** 是 App Hub 定义的封闭列表:`storage`、`net`、`images`、`web`、 -`camera`、`microphone`、`library`、`location`、`mail`、`llm`、`news`、`glance`、`model`、`prompt`、`ledger.read`、 +`camera`、`microphone`、`library`、`location`、`mail`、`llm`、`news`、`glance`、`model`、`research`、`crawl`、`prompt`、`ledger.read`、 `clipboard`,另有 49 个精确的宿主服务名(设备助手的 4 个 `octos.*`,Rinx 的 45 个 `matrix.*`)。未申请即不授予;安装前商店会为每项权限向用户显示一行通俗说明。只申请应用真正 需要的。每项权限解锁什么、哪些目前还没有可用路径(`prompt`、`ledger.read`、`clipboard`)、 -哪些只有系统应用能用(`llm`):[docs/CAPABILITIES.md](docs/CAPABILITIES.md)。 -助手相关权限目前能做什么(在 OctoSense 中暂时什么也做不了): +哪些只有系统应用能用(`llm`、`news`,以及暂时的 `research` 和 `crawl`):[docs/CAPABILITIES.md](docs/CAPABILITIES.md)。 +助手相关权限和应用自己的 Agent 目前在 OctoSense 中能做什么: [docs/AI-SERVICES.zh-CN.md](docs/AI-SERVICES.zh-CN.md)。 **`listing.json`**:副标题、描述、类别、关键词、图标、截图、实际测试过的平台、年龄分级, @@ -269,14 +271,24 @@ my-app/ the app's own git repository ## 应用中的 AI OctoSense 每个 Shell 运行一个 octos Agent 内核,由用户在系统应用 AI providers 中配置; -密钥永远不会到达应用。**隔离运行的应用目前还不能使用它**:准入检查接受 4 个 `octos.*` -权限,但没有任何 OctoSense Shell 向应用提供这些服务,`card-host` 也不提供任何宿主服务。 -`llm` 是只供系统应用使用的模型提供方管理服务,一次性调用模型的 `model` 权限(App Hub #24) -目前也还没有任何 Shell 提供服务。应用自己的 Agent(`tools.json`、`AGENT.md`、 -skills、触发器)已被 App Hub `main` 接受,并在 OctoSense ADR 0002 中规划,但目前没有任何 -地方运行它。请把应用做成不依赖 AI 也完整可用;添加 AI 功能之前先读 +密钥永远不会到达应用。内核上运行着**系统 Agent**(Shell 自己的助手,用户在系统对话中与它交谈), +以及每个声明了 Agent 的应用各自的**应用 Agent**,每个都在宿主持有的独立 peer 上。 +截至 2026-10-01,在 OctoSense `main` 上(细节与出处见 +[docs/AI-SERVICES.zh-CN.md](docs/AI-SERVICES.zh-CN.md)): + +| 应用可以 | 方式 | 在 `card-host` 中 | +| --- | --- | --- | +| 进行一次性、按 schema 校验的模型调用 | `model` 权限,`host.request("model.complete", …)`,受每日预算限制 | `no service answers "model"` | +| 在自己的界面上与助手对话 | 4 个 `octos.*` 权限,用户在首次使用的确认页上允许该应用的 Agent 之后 | `no service answers "octos"` | +| 拥有自己的 Agent:用户可以直接与它对话(Shell 为每个有 Agent 的应用绘制的“Ask ”面板、卡片内对话、应用自己的界面),系统 Agent 也可以把任务交给它 | `agent` 块(`"tools": ["ask_user_question"]`),可选 `tools.json`;该 Agent 可以读取应用的账户文件夹(`accounts/device/`) | 只能用 `hub check` 检查 | +| 向 glance 屏幕发布卡片,卡片内可与应用 Agent 对话,模型写的文字标为 AI 撰写 | `glance` 权限,`glance.publish`(L0 `sys.chat`、`model-copy`) | `no service answers "glance"`;本仓库锁定的运行时早于 `sys.chat` | + +仍在**规划中**:商店应用自己的 `tools.json` 工具真正运行(目前只有系统应用的 host-service +工具能运行)、宿主根据 `needs` 为 Agent 选择模型、触发器与后台运行、安装 `AGENT.md` 和 +skills,以及面向商店应用的系统工具箱。`llm` 是只供系统应用使用的模型提供方管理服务。 +请把应用做成不依赖 AI 也完整可用;添加 AI 功能之前先读 [docs/AI-SERVICES.zh-CN.md](docs/AI-SERVICES.zh-CN.md):准确的名称、一个经过验证并处理 -“不可用”状态的调用、用户看到什么、错误信息,以及规划中的路线。 +“不可用”状态的调用、用户看到什么、错误信息,以及规划中的内容。 ## 运行应用 @@ -388,7 +400,7 @@ curl -s 127.0.0.1:8161/quit; curl -s 127.0.0.1:8162/quit | [docs/SCRIPT-API.md](docs/SCRIPT-API.md) | Splash 语言及隔离应用可调用的全部 API | | [docs/CAPABILITIES.md](docs/CAPABILITIES.md) | 每项权限:解锁什么、用户看到什么、规则 | | [docs/HOST-SERVICES.md](docs/HOST-SERVICES.md) | `host.request`、面板、“密钥归宿主所有”、新增宿主服务 | -| [docs/AI-SERVICES.zh-CN.md](docs/AI-SERVICES.zh-CN.md)([English](docs/AI-SERVICES.md)) | OctoSense 的助手(octos):应用目前能用什么、经过验证的示例、规划中的内容(应用自己的 Agent、它的工具、系统工具箱、glance 卡片、`sys.digest`),附带日期的状态表 | +| [docs/AI-SERVICES.zh-CN.md](docs/AI-SERVICES.zh-CN.md)([English](docs/AI-SERVICES.md)) | OctoSense 的助手(octos):系统 Agent 与应用 Agent、应用目前能用什么、经过验证的示例、应用自己的 Agent 及其工具、系统工具箱、glance 卡片、`sys.digest`、AI 撰写的文字与卡片内对话(`sys.chat`),附带日期的状态表 | | [docs/PUBLISHING.md](docs/PUBLISHING.md) | 发布到 App Hub,附经过验证的输出和检查清单 | | [docs/GLOSSARY.md](docs/GLOSSARY.md) | 每个术语只有一个含义 | | [docs/NATIVE-WORKSPACE.md](docs/NATIVE-WORKSPACE.md)、[docs/l0/](docs/l0/) | 原生运行时的兄弟仓库配置;L0 卡片示例 | diff --git a/docs/AI-SERVICES.md b/docs/AI-SERVICES.md index 55466eb2..a46017d8 100644 --- a/docs/AI-SERVICES.md +++ b/docs/AI-SERVICES.md @@ -4,25 +4,37 @@ English | [简体中文](AI-SERVICES.zh-CN.md) How a script app built here can use the assistant inside OctoSense (the octos agent kernel the shell runs), what works today, and what is planned. State as -of 2026-09-30: App Hub `main` `0f33211`, OctoSense `main` `7082ff5` (which -pins App Hub `0f332112`). Since this page was first written (2026-09-27) the -shells gained the `model` service, the `octos` service for contained apps -behind first-use consent, `glance` for any app granted it, and an agent (a -peer and an "Ask " panel) for every app that declares one. OctoSense's -[`docs/ai-services.md`](https://github.com/OctoSense-org/OctoSense/blob/main/docs/ai-services.md) -and [`docs/architecture.md`](https://github.com/OctoSense-org/OctoSense/blob/main/docs/architecture.md) +of 2026-10-01: App Hub `main` `41bc959`, OctoSense `main` `f52620c` (which +pins App Hub `58c3c8ae`, octos `ae230ce0`, Octoscript `5991dfae` and +Octoscript-Makepad `8f103d0c`). Since this page was first written +(2026-09-27) the shells gained the `model` service, the `octos` service for +contained apps behind first-use consent, `glance` for any app granted it, and +an agent (a peer and an "Ask " panel) for every app that declares one. +Since 2026-09-30 they also give that agent `ask_user_question` and read tools +over its own folder, keep one agent per account, erase it when the app is +uninstalled, let a person chat with it inside an L0 card (`sys.chat`, with +the model's words marked AI-written), and let system apps' agents put cards +on the glance screen through their own tools (Mail, Calendar). OctoSense's +[`docs/ai-services.md`](https://github.com/OctoSense-org/OctoSense/blob/main/docs/ai-services.md), +[`docs/architecture.md`](https://github.com/OctoSense-org/OctoSense/blob/main/docs/architecture.md) +and [ADR 0004](https://github.com/OctoSense-org/OctoSense/blob/main/docs/adr/0004-native-apps-hosting-and-peers.md) describe the shell side. The later sections cover what is being built on top: an app's own agent declared in its bundle, the tools it exposes, the system toolbox, publishing -to the glance screen, `sys.digest` cards, and News end to end. Each feature +to the glance screen, `sys.digest` cards, AI-written text and in-card chat, +and News end to end. Each feature there is marked **available** (merged on `main` of the repository named, and usable as described) or **coming** (in an open pull request, named, or only in an ADR: the shape shown may change before it lands, so do not build a submission on it yet). Commands marked **✓ run** were run for this page on 2026-09-27 (macOS, Apple silicon); every other command is quoted from the named source and marked not run. The 2026-09-30 updates are quoted from the -named pull requests and code, not run. +named pull requests and code, not run. The 2026-10-01 updates were read in +the code at the revisions above; the `hub check` outputs marked +**✓ run 2026-10-01** came from a `hub` built from App Hub `main` `41bc959` +(the `app-contract`, `app-policy` and `app-hub` crates); nothing on this page +was run in an OctoSense shell. > **Building an app needs no AI.** Nothing in this repository calls a model > or needs an API key, and you may use any coding agent (Codex, Claude Code, @@ -36,6 +48,7 @@ The deep version, for shell and native-module developers: - [The short answer](#the-short-answer) - [How the assistant is built](#how-the-assistant-is-built) +- [The system agent and app agents](#the-system-agent-and-app-agents) - [The assistant capabilities](#the-assistant-capabilities) - [A minimal call, and handling "unavailable"](#a-minimal-call-and-handling-unavailable) - [What the person sees](#what-the-person-sees) @@ -48,6 +61,7 @@ The deep version, for shell and native-module developers: - [The system toolbox](#the-system-toolbox) - [Publishing to the glance screen](#publishing-to-the-glance-screen) - [Cards bound to findings: `sys.digest`](#cards-bound-to-findings-sysdigest) +- [AI-written text and in-card chat: `model-copy`, `sys.chat`](#ai-written-text-and-in-card-chat-model-copy-syschat) - [Card levels and render-and-critique](#card-levels-and-render-and-critique) - [End to end: News](#end-to-end-news) - [Testing without a provider](#testing-without-a-provider) @@ -67,9 +81,10 @@ decline, and a device may have no kernel (iOS) or no provider. | Declare `octos.turn.start` (and friends) and call it | The gate accepts the name. In `card-host` the call answers `no service answers "octos" on this device` (verified, below). In an OctoSense shell that hosts a kernel, the first call waits for the person to allow the app's agent (`Waiting for the person to allow this app's agent (OctoSense asks the first time)`); after that the app talks to its own peer ([OctoSense#106](https://github.com/OctoSense-org/OctoSense/pull/106), [#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)). | | Declare `model` and call `model.complete` | The gate accepts it (App Hub [#24](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/24)), and the OctoSense shells serve it ([OctoSense#95](https://github.com/OctoSense-org/OctoSense/pull/95), merged): a one-shot, schema-checked call answered from the person's own AI providers. `card-host` answers `no service answers "model" on this device`. See [One-shot model calls](#one-shot-model-calls-model). | | Declare `llm` | The gate accepts it, but the `llm` service manages the device's AI providers (it has no prompt method) and answers only `os.*` system apps: `llm is for OctoSense's own apps.` Do not request it. | -| Declare an `agent` in `manifest.json` | Admitted and clamped by the gate. Once the person allows it, the shell gives the app its own peer, and the person can talk to it in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)). Model choice from `needs`, triggers and background runs are still **coming** ([An app's own agent](#an-apps-own-agent)). | -| Ship `tools.json`, `AGENT.md`, `skills/` | Admitted by the gate. The shell registers the bundle's tools with the app's peer and relays their calls ([OctoSense#145](https://github.com/OctoSense-org/OctoSense/pull/145), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)); `AGENT.md` and skills are not installed into the peer yet. | -| Call `glance.publish` | Answered for any contained app granted the `glance` capability ([OctoSense#86](https://github.com/OctoSense-org/OctoSense/pull/86)) ([Publishing to the glance screen](#publishing-to-the-glance-screen)). | +| Declare an `agent` in `manifest.json` | Admitted and clamped by the gate. Once the person allows it, the shell gives the app its own peer, the person can talk to it in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)), and the system agent can hand it work. Its agent may keep the kernel's `ask_user_question` ([OctoSense#190](https://github.com/OctoSense-org/OctoSense/pull/190)) and reads its own account folder ([#205](https://github.com/OctoSense-org/OctoSense/pull/205), [#249](https://github.com/OctoSense-org/OctoSense/pull/249)). Model choice from `needs`, triggers and background runs are still **coming** ([An app's own agent](#an-apps-own-agent)). | +| Ship `tools.json`, `AGENT.md`, `skills/` | Admitted by the gate. The shell registers the bundle's tools with the app's peer and relays their calls ([OctoSense#145](https://github.com/OctoSense-org/OctoSense/pull/145), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)), but a call runs only on a host service of the app's namespace, so today only system apps' tools run (News, Mail, Calendar); a store app's tool answers an error ([What the agent gets today](#what-an-apps-agent-gets-today)). `AGENT.md` and skills are not installed into the peer yet. | +| Call `glance.publish` | Answered for any contained app granted the `glance` capability ([OctoSense#86](https://github.com/OctoSense-org/OctoSense/pull/86)): an L0 card, or an interactive Splash card, optionally with a notification ([Publishing to the glance screen](#publishing-to-the-glance-screen)). | +| Put `sys.chat` or `model-copy` text in an L0 card | The OctoSense shells answer `sys.chat` on the publishing app's glance cards with its agent, and draw model-written text marked AI-written ([OctoSense#263](https://github.com/OctoSense-org/OctoSense/pull/263), [OctoScript#53](https://github.com/OctoSense-org/OctoScript/pull/53)). This repository's pinned runtime predates both, so the L0 checker that `card-host` and `card-studio` use here does not know them (read from [`native-runtime.lock.json`](../native-runtime.lock.json), not run) ([AI-written text and in-card chat](#ai-written-text-and-in-card-chat-model-copy-syschat)). | | Put a provider API key in the bundle | Never. No keys, tokens or passwords in an app ([AGENTS.md](../AGENTS.md#rules-for-every-app)). | A plain HTTPS API your app declares under `net` is just a web request, even @@ -87,10 +102,13 @@ providers. - **AI providers** (a system app) is where the person picks models and types keys, on host-owned sheets. Keys stay in the platform's secret store; no app ever sees one. -- **App peers.** An app the host grants the assistant gets its own octos peer: - private conversation contexts, workspace and memory - (`app//acct-`), owned by the shell's system agent. The app gets a - scoped service, never the kernel, a provider or a key. +- **App peers.** An app the host grants the assistant gets its own octos peer + for each account it keeps (one `device` account unless the manifest says + `storage.accounts: true`): private conversation contexts, a workspace (the + account's folder, `apps//accounts//`) and memory + (`app//acct-`), owned by the shell's system agent + (`crates/app-peers`). The app gets a scoped service, never the kernel, a + provider or a key. - **Approvals are the person's, in the app that asked.** The system agent never approves for an app. @@ -103,17 +121,104 @@ service ([OctoSense#106](https://github.com/OctoSense-org/OctoSense/pull/106)), use ([#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)). `OCTOSENSE_CONTAINED_APPS=1` skips the question for every app (a developer override) and `0` turns it off. +## The system agent and app agents + +What OctoSense `main` does, read in `crates/shell/src/agents.rs`, +`crates/ai-host/src/contained.rs`, `crates/shell/src/questions/` and +`crates/app-peers` (ADR 0004 §4, §6). Not run for this page. + +- **Which apps have an agent.** A script app whose manifest declares an + `agent` block or any `octos.*` name, or whose bundle ships `tools.json`; + a native app whose shell entry grants `octos.*` (Rinx). Settings › + Assistant lists each one and turns it off. On 2026-10-01 the system apps + News, Mail and Calendar have one; Mail, Calendar and News declare no + `octos.*` names, so the shell drives their agents and the apps never call + `host.request("octos…")`. +- **First use.** The person allows an app's agent on a first-use sheet, + raised by the app's own first `octos.*` call, by its "Ask " panel + (the bar's "Ask " or Shift+F8 on the desktop), or by the system + agent's `agents.ask`. From then on the shell prepares the + peer at startup and registers its tools, whether or not the app is open. + A script app's peer is `card.` (so `card.os.mail`), never a native + module's. +- **The system agent** is the shell's own session + (`_main:api:octosense#system`), which the person talks to in the system + chat (the assistant pane: the dock's Assistant icon, F8 on the desktop, a + home tile on the phone). It sees every prepared app peer with `peer_list`, + hands one work with `peer_send_input`, reads what it wrote with + `peer_gather`, and asks the shell with `agents.list` / `agents.ask` about + apps whose agent the person has not allowed yet. It holds no app's tools, + never approves for an app and cannot close an app's peer. +- **Two lanes per app agent.** The system agent's requests run in one lane, + the person's in another, in parallel and sharing history; the person + reaches the app's agent directly, without the system agent + ([below](#the-person-talks-to-an-apps-agent-directly)). +- **Questions.** An agent that keeps `ask_user_question` may ask the person + something. A question from a turn the person or the app started is shown + in the app's conversation; one from a turn the system agent started goes + to the system chat. Unanswered after 10 minutes, it is declined. +- **Example** (the shape of OctoSense#267's live run, made there with a real + model; not run here): the person asks the system agent for a calendar + card; it hands the request to Calendar's agent with `peer_send_input` + (Calendar's first-use sheet appears if the person has not allowed it + yet); Calendar's agent calls one of its own tools (`calendar.notify`, + `calendar.agenda`), whose host service fills a fixed L0 card it ships and + publishes it to the glance screen as Calendar. The model never writes the + card's code. +- **Lifecycle.** A peer keeps its memory across launches. Signing out of an + account suspends that account's agent; removing the account or + uninstalling the app erases it (octos `peer/purge`, + [OctoSense#248](https://github.com/OctoSense-org/OctoSense/pull/248)), so + a reinstall starts a new agent. + +### The person talks to an app's agent directly + +An app's agent is not only reachable through the system agent: the person +can talk to it directly, in three places, all of them the **person's lane** +of the same conversation. Nothing runs until the person has allowed the +app's agent on its first-use sheet. Read in OctoSense +`crates/shell/src/app_chat/mod.rs`, `crates/shell/src/glance_chat.rs` and +`crates/ai-host/src/contained.rs` at `f52620c`; not run for this page. + +| Where | What the person does | What the app author writes | +| --- | --- | --- | +| The shell's **"Ask " panel** | Opens it for the focused app's agent from the bar's "Ask " button, Shift+F8, or Setup › Assistant › "Ask this app's agent" (desktop). It stands right of the system chat. Send is a turn of the person's, run in parallel with the system agent's lane; its Stop stops only the person's turn, and a separate "Stop the system agent's task" row stops the other lane's. Questions the app's agent asks in the person's lane are answered here. | Nothing: the shell draws the panel for **every** app that has an agent, whether or not the app has a chat screen of its own. | +| An **in-card chat** on the glance screen | Types into a card the app published; the app's own agent answers in the card. | An L0 card with `sys.chat` and `ChatEntry`, published with `glance` ([below](#ai-written-text-and-in-card-chat-model-copy-syschat)). | +| The **app's own screens** | Uses whatever chat or "ask" control the app draws. | `host.request("octos.session.open" / "octos.turn.start" / "octos.session.history" / "octos.turn.interrupt", …)` on the `octos` service, with those names in `capabilities` ([A minimal call](#a-minimal-call-and-handling-unavailable)). | + +- **One conversation, two lanes.** The system agent's lane is the peer's + own session (`_main:api:octosense#peer-…`); the person's lane is a request + context opened with shared history (`…#peerctx-…`). Each lane sees the + other's recent messages, read only. Turns are labelled by speaker, and + the events and `octos.session.history` rows carry `lane` (`person` or + `system_agent`) and `speaker`. +- A turn from the panel or the system chat is the person's own + (`TurnTrigger::Person`); one an app starts with `octos.turn.start`, or a + card's chat, is labelled the person's but counts as the app's run for the + approval rules ([above](#the-assistant-capabilities)). +- **Stop on the shell's approval surface** for an app's agent (the button + under its pending approvals and questions, `approvals::stop_agent`) + declines what that agent is waiting on and stops its running turns in + **both** lanes: the person owns the device. +- **On the phone** the panel is built as a full-screen sheet, but on `main` + no touch control opens it (the phone's Assistant tile opens the system + chat); not run on a device. An app's own screens and its glance cards + work there as on the desktop. +- Native modules and process apps reach the same person's lane through + their own channels (`open_conversation` on the injected service, or the + peer link); a script app uses the `octos.*` names above. + ## The assistant capabilities Four exact names, each its own consent (`KNOWN_CAPABILITIES` in App Hub -`crates/app-policy/src/manifest.rs`; the names and their store lines in +`crates/app-contract/src/manifest.rs`; the names and their store lines in `crates/app-policy/src/services.rs`). A prefix grants nothing: `octos.` or `octos.admin` is refused by the gate. | Capability | Call | Args | Answer (`r.data`) | The store says | | --- | --- | --- | --- | --- | | `octos.session.open` | `octos.session.open` | `{}` | `{open: true, model: {lane, provider, model} or nil}` | Open its own conversation with the assistant | -| `octos.session.history` | `octos.session.history` | `{}` | the conversation, `{session_id, messages: [...], …}` | Read its own conversations with the assistant | +| `octos.session.history` | `octos.session.history` | `{}` | the conversation, `{session_id, messages: [...], …}`: both lanes merged by time, each message with its `lane` and speaker | Read its own conversations with the assistant | | `octos.turn.start` | `octos.turn.start` | `{text}` (1 byte to 32 KiB) | `{turn_id, text}`, the reply, once the turn ends | Ask the assistant to work for it, using the device's AI settings | | `octos.turn.interrupt` | `octos.turn.interrupt` | `{}` | stops the running turn | Stop assistant work it started | @@ -126,7 +231,14 @@ refused (`Unsupported Octos arguments`): an app supplies text (in the OctoSense shells, also `trigger`, one of `person`, `app` or `incoming`, and `from`, saying what started the turn), never a session, profile, provider, model or approval decision. One turn runs at a time per app instance, and a turn -gives up after 180 s. +gives up after 180 s. A script app gets no pushed events: it reads the +conversation, the system agent's turns included, with `octos.session.history`. + +`trigger: "person"` from an app labels the turn as the person's in the +transcript, but the approval rules treat it as the app's own run: a standing +rule such as "when I start it" never answers it. Only the shell's own +composers (the "Ask " panel, the system chat) vouch for the person +([OctoSense#215](https://github.com/OctoSense-org/OctoSense/pull/215)). ## A minimal call, and handling "unavailable" @@ -199,7 +311,16 @@ list. [OctoSense#120](https://github.com/OctoSense-org/OctoSense/pull/120) and [#145](https://github.com/OctoSense-org/OctoSense/pull/145); Rinx's own for its mini-apps); the system agent never answers them. A script app cannot approve anything: no - argument carries a decision. + argument carries a decision. The sheet shows every argument in full + (hidden and control characters as their code points, a command one line + per row), and Approve stays disabled until the person has scrolled through + all of them ([OctoSense#218](https://github.com/OctoSense-org/OctoSense/pull/218)). +- **A first-use sheet** for the app's agent, listing what it may read + ("Its own memory", or "No files: only what its tools return" with + `storage.agent_workspace: "none"`), what it may use ("Ask you questions" + for an agent that keeps `ask_user_question`) and where the model runs + (`crates/shell/src/approvals/consent.rs`). Settings › Assistant turns it + off again. ## Errors @@ -210,6 +331,8 @@ list. | `Waiting for the person to allow this app's agent (OctoSense asks the first time)` | The person has not allowed the app's agent yet; the shell is asking | Show it; the person's answer decides the next call | | `The assistant is turned off for apps on this device` | The device turned the assistant off for apps (`OCTOSENSE_CONTAINED_APPS=0`) | Show "unavailable" and carry on | | `The assistant is not available on this device` | The shell could not start the app's peer | The same | +| `Add an account in the app before using its assistant` | The app keeps accounts (`storage.accounts: true`) and has none yet | Offer the app's own way to add an account | +| `This app's manifest does not declare that assistant service` | The shell's own check behind the isolate's: the manifest lists no `octos.*` name, or not this one | Declare it, or remove the call | | `no service answers "model" on this device` | No `model` service on this host (`card-host`) | The same | | `: ` from `model.complete`, `` one of `capability`, `no_provider`, `rate`, `budget`, `bad_request`, `invalid_output`, `too_large`, `provider` | The `model` service refused the call ([details](#the-model-service)) | Show the sentence; keep the app usable without the model | | `Unsupported Octos arguments` | An argument other than `text` (turn start) or anything at all (the others) | Send only `{text}` or `{}` | @@ -316,7 +439,7 @@ host.request("model.complete", { ## Status at a glance -As of 30 Sep 2026. **available** means merged on `main` of the repository +As of 1 Oct 2026. **available** means merged on `main` of the repository named; **coming** means an open pull request (named) or only an ADR. | Feature | Status | Pull requests, source | @@ -326,40 +449,63 @@ named; **coming** means an open pull request (named) or only an ADR. | `octos.*` for a contained script app in OctoSense | **available** where the shell hosts a kernel (not iOS), once the person allows the app's agent at first use | OctoSense [#106](https://github.com/OctoSense-org/OctoSense/pull/106), [#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#184](https://github.com/OctoSense-org/OctoSense/pull/184) | | `llm`: the person's AI providers, masked keys, host sheets | **available**, system apps (`os.*`) only; no prompt method | OctoSense [`apps/ai-providers/host-service`](https://github.com/OctoSense-org/OctoSense/tree/main/apps/ai-providers/host-service) | | `model.complete`: one-shot, schema-checked model call | **available**: the capability ([App-Hub#24](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/24)) and the service ([OctoSense#95](https://github.com/OctoSense-org/OctoSense/pull/95)), in the shells' App Hub pin | [above](#one-shot-model-calls-model) | -| An app's own agent in the bundle: `agent` (profile, model needs, triggers, skills), `tools.json`, `AGENT.md`, `skills/` | **available** in the App Hub gate ([App-Hub#18](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/18)) and the shells: a peer and the "Ask " panel once the person allows it ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)); `AGENT.md` and skills are not installed into the peer yet | [An app's own agent](#an-apps-own-agent) | -| The host picking the model from `needs` and `tier`; triggers and `background` firing | **coming** (ADR 0002 step 2, M3) | [The manifest's `agent`](#the-manifests-agent) | -| App tools registered with the app's peer (`peer/tools/register`, `peer/tool/call`) | **available**: the kernel side ([octos#2567](https://github.com/octos-org/octos/pull/2567)) and the shell registering the bundle's tools and relaying their calls ([OctoSense#145](https://github.com/OctoSense-org/OctoSense/pull/145), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)) | [The app's tools and peer tools](#the-apps-tools-and-peer-tools) | -| In-app conversation with the app's agent, run-time approvals, app memory | Conversation **available** in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)); approvals on the shell's sheets ([#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#145](https://github.com/OctoSense-org/OctoSense/pull/145)); app memory **coming** (ADR 0002 §9, M7) | [What is enforced where](#what-is-enforced-where) | -| System toolbox: templates, `workflow.run`, `workflow.fork`, the `research` module | Templates **available** ([OctoSense#82](https://github.com/OctoSense-org/OctoSense/pull/82), `crates/toolbox`) and the `research` and `crawl` capabilities in App Hub ([App-Hub#26](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/26)); granting the toolbox to app agents is **coming** ([OctoSense#64](https://github.com/OctoSense-org/OctoSense/issues/64); the `toolbox-peers` build feature is off in the shipped shells) | [The system toolbox](#the-system-toolbox) | +| An app's own agent in the bundle: `agent` (profile, model needs, triggers, skills), `tools.json`, `AGENT.md`, `skills/` | **available** in the App Hub gate ([App-Hub#18](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/18)) and the shells: a peer, the "Ask " panel and the system agent's `peer_send_input` once the person allows it ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)); `AGENT.md` and skills are not installed into the peer yet | [An app's own agent](#an-apps-own-agent) | +| The kernel's `ask_user_question` for an app's agent (`agent.tools: ["ask_user_question"]`, the only kernel tool a contained agent may keep) | **available** ([App-Hub#37](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/37), [OctoSense#190](https://github.com/OctoSense-org/OctoSense/pull/190)) | [What the agent gets today](#what-an-apps-agent-gets-today) | +| Host read tools on the app's peer: `files.list`, `files.read`, `files.search` over the account folder; the person's lane reads that folder (`read_parent`) | **available** on Unix platforms, not Windows ([OctoSense#205](https://github.com/OctoSense-org/OctoSense/pull/205), [#249](https://github.com/OctoSense-org/OctoSense/pull/249); octos [#2647](https://github.com/octos-org/octos/pull/2647)) | OctoSense `crates/shell/src/host_tools/files.rs` | +| One agent per account (`storage.accounts`), and the agent erased with the account or the app (`peer/purge`) | **available** ([App-Hub#44](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/44), [OctoSense#233](https://github.com/OctoSense-org/OctoSense/pull/233), [#248](https://github.com/OctoSense-org/OctoSense/pull/248); octos [#2649](https://github.com/octos-org/octos/pull/2649)) | [Where the agent works](#where-the-agent-works-storage) | +| The host picking the model from `needs` and `tier`; triggers and `background` firing | **coming** (ADR 0002 step 2, M3). The shell does not read `agent.profile` or `agent.model` yet | [The manifest's `agent`](#the-manifests-agent) | +| App tools registered with the app's peer (`peer/tools/register`, `peer/tool/call`) | **available**: the kernel side ([octos#2567](https://github.com/octos-org/octos/pull/2567)) and the shell registering the bundle's tools and relaying their calls ([OctoSense#145](https://github.com/OctoSense-org/OctoSense/pull/145), [#184](https://github.com/OctoSense-org/OctoSense/pull/184)). A call runs only on a host service of the app's namespace, so only system apps' tools run; `implemented_by: "app"` tools answer an error until the Card runner can take them (**coming**) | [The app's tools and peer tools](#the-apps-tools-and-peer-tools) | +| System apps' agents putting cards on the glance screen through their own tools (`mail.notify`, `calendar.notify`, `calendar.agenda`: the host service fills a fixed L0 card) | **available** for Mail and Calendar ([OctoSense#267](https://github.com/OctoSense-org/OctoSense/pull/267)); Calendar is desktop only | OctoSense `apps/mail/host-service`, `apps/calendar/host-service` | +| In-app conversation with the app's agent, run-time approvals, app memory | Conversation **available** in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)) and in a card ([`sys.chat`](#ai-written-text-and-in-card-chat-model-copy-syschat)); approvals on the shell's sheets ([#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#145](https://github.com/OctoSense-org/OctoSense/pull/145), [#215](https://github.com/OctoSense-org/OctoSense/pull/215), [#218](https://github.com/OctoSense-org/OctoSense/pull/218)), every tool call audited ([#222](https://github.com/OctoSense-org/OctoSense/pull/222)); the peer's memory is kept per account, promotion rules **coming** (ADR 0002 §9, M7) | [What is enforced where](#what-is-enforced-where) | +| System toolbox: templates, `workflow.run`, `workflow.fork`, `toolbox.search`, `toolbox.web_read`, `toolbox.deep_crawl` | Templates **available** ([OctoSense#82](https://github.com/OctoSense-org/OctoSense/pull/82), `crates/toolbox`) and the `research` and `crawl` capabilities in App Hub ([App-Hub#26](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/26)). Granted to app agents only for system apps (`os.*`) that declare them, and only where the shell is built with `toolbox-peers` (the phone's default, not the desktop's); for store apps **coming** ([OctoSense#64](https://github.com/OctoSense-org/OctoSense/issues/64)) | [The system toolbox](#the-system-toolbox) | | `news` host service (a data service, no model) | **available**, system apps (`os.*`) only | OctoSense [`apps/news/host-service`](https://github.com/OctoSense-org/OctoSense/tree/main/apps/news/host-service) | -| `glance.publish`, `glance.withdraw`, `glance.list` (the service) | **available** ([OctoSense#72](https://github.com/OctoSense-org/OctoSense/pull/72)) to any contained app granted `glance` ([OctoSense#86](https://github.com/OctoSense-org/OctoSense/pull/86)) | [Publishing to the glance screen](#publishing-to-the-glance-screen) | +| `glance.publish`, `glance.withdraw`, `glance.list` (the service) | **available** ([OctoSense#72](https://github.com/OctoSense-org/OctoSense/pull/72)) to any contained app granted `glance` ([OctoSense#86](https://github.com/OctoSense-org/OctoSense/pull/86)); interactive `script` cards and `notify` notifications on `main` (`crates/shell/src/glance.rs`) | [Publishing to the glance screen](#publishing-to-the-glance-screen) | | The `glance` capability | **available** in App Hub ([App-Hub#22](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/22)) and the shells ([OctoSense#86](https://github.com/OctoSense-org/OctoSense/pull/86)) | [Who may publish](#who-may-publish) | -| `sys.digest(app:, id:, fields:)` L0 source | **coming**: [OctoScript#40](https://github.com/OctoSense-org/OctoScript/pull/40), [OctoScript-Makepad#50](https://github.com/OctoSense-org/OctoScript-Makepad/pull/50), [OctoSense#87](https://github.com/OctoSense-org/OctoSense/pull/87) | [Cards bound to findings](#cards-bound-to-findings-sysdigest) | +| `sys.digest(app:, id:, fields:)` L0 source | The checker **available** (merged as [OctoScript#40](https://github.com/OctoSense-org/OctoScript/pull/40), repinned by [OctoScript-Makepad#50](https://github.com/OctoSense-org/OctoScript-Makepad/pull/50), in the shells' runtime pin); the shell filling it from the toolbox's runs **coming** ([OctoSense#87](https://github.com/OctoSense-org/OctoSense/pull/87), open) | [Cards bound to findings](#cards-bound-to-findings-sysdigest) | +| Model-written text in L0 text slots, marked AI-written; `sys.chat` and `ChatEntry` (in-card chat with the app's agent) | **available**: the checker ([OctoScript#53](https://github.com/OctoSense-org/OctoScript/pull/53)), the kit's mark ([OctoScript-Makepad#68](https://github.com/OctoSense-org/OctoScript-Makepad/pull/68)) and the host side on glance cards and in AppCard ([OctoSense#263](https://github.com/OctoSense-org/OctoSense/pull/263), `crates/l0-chat`); not in this repository's pinned runtime | [AI-written text and in-card chat](#ai-written-text-and-in-card-chat-model-copy-syschat) | | Render and critique a card: `card-studio`, `card-host --remote` | **available** (App Hub `main`, [App-Hub#19](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/19)); run by an app's agent **coming** (M6) | [Card levels and render-and-critique](#card-levels-and-render-and-critique) | In one line: a store app can call `model.complete`, publish glance cards -when granted `glance`, and, once the person allows its agent, talk to the -assistant through its own peer, whose tools the shell registers; model choice -from `needs`, triggers, background runs, app memory and the system toolbox -for app agents are still coming. +when granted `glance` (with an in-card chat its agent answers), and, once +the person allows its agent, talk to the assistant through its own peer, +which keeps `ask_user_question` and reads the app's own folder; a store +app's own tools, model choice from `needs`, triggers, background runs, +`AGENT.md` and skills, and the system toolbox for store apps are still +coming. What an app author can do today: - Build the app's own screens so they are complete without AI, and show "unavailable" as a normal state. -- Declare an agent (`agent`, `tools.json`, `AGENT.md`, skills) that passes - `hub check`, knowing the shells give it a peer and register its tools but - do not yet install `AGENT.md` or skills, choose its model or fire its - triggers. -- Write and render L0 cards with `card-studio`, and design them around - `sys.digest` once OctoScript#40 lands. - -**Version skew to know about.** `tools/octo check` runs the App Hub checkout -beside this repository (`main`). The OctoSense shells pin an App Hub commit of -their own (`0f332112` on 2026-09-30, in OctoSense's `native-apps.json`), which -can lag App Hub `main`. Manifests refuse unknown fields, so a field newer than -the shells' pin passes `octo check` but is refused by the shells until their -pin moves. +- Declare an agent (`agent` with `tools: ["ask_user_question"]`, + `tools.json`, `AGENT.md`, skills) that passes `hub check`, knowing the + shells give it a peer, a conversation and read access to the app's account + folder, but do not yet run a store app's own tools, install `AGENT.md` or + skills, choose its model or fire its triggers. +- Keep the data the agent should see in the account folder + (`accounts/device/` in the app's storage), as the system apps do + ([Where the agent works](#where-the-agent-works-storage)). +- Write and render L0 cards with `card-studio`; design cards around + `sys.digest`, `model-copy` and `sys.chat`, knowing this repository's + runtime cannot check those yet. + +**Version skew to know about.** + +- `tools/octo check` runs the App Hub checkout beside this repository + (`main`). The OctoSense shells pin an App Hub commit of their own + (`58c3c8ae` on 2026-10-01, in OctoSense's `Cargo.toml` and + `native-apps.json`), which can lag App Hub `main`. Both take the manifest + rules from one crate, `octosense-app-contract` 1.x from crates.io + ([ADR 0005](https://github.com/OctoSense-org/OctoSense/blob/main/docs/adr/0005-app-contract.md)). + A manifest at the default `schema_minor` (0) still refuses unknown fields + (**✓ run 2026-10-01**: ``hub: manifest is not valid: unknown field `future_field`, expected one of `schema`, `id`, … `requires`, `schema_minor` ``), + so do not use a field the shells' pin does not know. +- This repository's `card-host` and `card-studio` are built against the + runtime its [`native-runtime.lock.json`](../native-runtime.lock.json) + pins: Octoscript-Makepad `cb66de07`, which pins Octoscript `dbd48cfb`. + That Octoscript has neither `sys.digest` nor `model-copy` in text slots, + `sys.chat` or `ChatEntry`, which the shells' pin (`5991dfae`) has. Until + the lock moves, such a card can be checked only in an OctoSense checkout. If you want to prepare, draft `tools.json` and `AGENT.md` outside `bundle/`, from App Hub's News example @@ -373,15 +519,65 @@ from App Hub's News example [OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184) (2026-09-30) the shell gives an app that declares an agent (or `octos.*`, or ships `tools.json`) its own peer once the person allows it, registers the bundle's tools with it, and lets the person talk to -it in the "Ask " panel. No shell installs `AGENT.md` or skills into the -peer, selects a model or fires a trigger yet (ADR 0002, Implementation step -2). +it in the "Ask " panel; the system agent can hand it work +([The system agent and app agents](#the-system-agent-and-app-agents)). No +shell installs `AGENT.md` or skills into the peer, selects a model or fires a +trigger yet (ADR 0002, Implementation step 2). The contract is App Hub's [PUBLISHING § The app's agent and tools](https://github.com/OctoSense-org/OctoSense-App-Hub/blob/main/docs/PUBLISHING.md#the-apps-agent-and-tools); the complete worked example is App Hub's [`crates/app-policy/tests/fixtures/news-agent`](https://github.com/OctoSense-org/OctoSense-App-Hub/tree/main/crates/app-policy/tests/fixtures/news-agent). +### What an app's agent gets today + +What the shell registers on a script app's peer (`card.`), from +OctoSense `crates/shell/src/host_tools/` at `f52620c` (read, not run): + +| Tool | Where it comes from | A store app | A system app (`os.*`) | +| --- | --- | --- | --- | +| `ask_user_question` (octos kernel tool) | `agent.tools: ["ask_user_question"]` | **yes** | **yes** (News, Mail, Calendar) | +| `files.list`, `files.read`, `files.search` (read, no approval) | every peer whose agent has a workspace, on Unix platforms | **yes**: its account folder only, 128 KiB per read, 500 entries per listing, 100 matches per search | **yes** | +| Its own `tools.json` tools, `implemented_by: "host-service"` | run on the host service of the tool's namespace, with the app's identity, as its own `host.request` would; the family must be granted, or be the system app's own namespace | answers `not_granted`: a store app has no host service of its own (unless its namespace happens to be a family it was granted, such as an id ending in `.mail` with `mail`; then the call is the same as its own `host.request`) | **yes**: `news.list`, `news.read`, `mail.notify`, `calendar.events`, `calendar.add_event`, `calendar.remove_event`, `calendar.notify`, `calendar.agenda` | +| Its own `tools.json` tools, `implemented_by: "app"` | would run in the app's script | answers ` runs in the app's own script; open the app to use it` until the Card runner can take them (**coming**) | the same | +| The generic host tools `ledger.read`, `ledger.write`, `net.fetch`, `storage.read`, `storage.write`, `card.render` | `agent.tools` | admitted by the gate, but no shell implements them (not found in OctoSense `crates/`) | the same | +| Other apps' shareable tools (`mail.send`) | a dotted name in `agent.tools` | refused by the store's gate: `app requests tool "mail.send", which this host does not offer contained apps` (**✓ run 2026-10-01**) | granted by the shell to its namespace's owner app; none is granted today | +| System toolbox tools | the `research` / `crawl` capabilities | **coming** | with `toolbox-peers` ([below](#the-system-toolbox)); no system app declares `research` yet | +| `dev.run` (a shell command) | developer mode, for the apps it covers | development builds only | the same | + +So a store app's agent can talk with the person and the system agent, ask +the person questions, and read what the app keeps in its account folder; it +cannot act through tools of its own yet. The relay also caps every agent at +32 tool calls a turn and 1000 a day by default +(`crates/shell/src/host_tools/relay.rs`). + +### Where the agent works: `storage` + +App Hub's `storage` block ([App-Hub#44](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/44), +ADR 0004 §11) says what the agent sees: + +| Field | Meaning | +| --- | --- | +| `storage.accounts` | `true`: data and one agent per account (Mail). Omitted or `false`: one `device` folder and one agent | +| `storage.agent_workspace` | `"account"` (the default): the agent's workspace is the account's folder, which its conversation reads through octos `read_parent`. `"none"`: no files, only what its tools return | +| `storage.cache_max_bytes` | the ceiling for the jail's `cache/` | + +The account folder is `accounts//` inside the app's own storage +(`accounts/device/` for an app without accounts). The system apps keep +their data there (`let DATA = "accounts/device/"` in News, YouTube, Photos +and Maps since [OctoSense#229](https://github.com/OctoSense-org/OctoSense/pull/229)) and refetchable +caches in `cache/`, so their agents can read it; data written at the top of +the jail stays out of the agent's reach. A store app following the same +layout (`fs` paths under `accounts/device/`) should get the same, by the +same code, but that is **unverified**: no store app with an agent has been +run in a shell for this page. `storage.external` is +for native apps and is refused in a script manifest. In-card chat +transcripts are kept in the same folder, under `chat/` +([below](#ai-written-text-and-in-card-chat-model-copy-syschat)). + +The gate admits `"storage": {"accounts": false, "agent_workspace": "account"}` +(**✓ run 2026-10-01**). + ### The design (ADR 0002) What OctoSense @@ -455,14 +651,22 @@ $ hub check --allow-unsigned ``` (The only refusal was the template's missing screenshot, which a real capture -fixes.) +fixes.) The system apps declare a smaller agent, which the shells act on +today: + +```json +"agent": { "profile": "read-only", "tools": ["ask_user_question"], "model": { "needs": ["tool_calling"] } } +``` + +With `"capabilities": ["storage", "glance"]` it gives the same `grants:` +line (**✓ run 2026-10-01**, App Hub `41bc959`). -| Field | Rule (App Hub `manifest.rs`, `policy.rs`) | Enforced today | +| Field | Rule (App Hub `crates/app-contract/src/manifest.rs`, `crates/app-policy/src/policy.rs`) | Enforced today | | --- | --- | --- | -| `profile` | `read-only`, `workspace-write` or `workspace-write-never-ask`; there is no full access | gate | -| `tools` | generic host tools only: `ledger.read ledger.write net.fetch storage.read storage.write card.render`; anything else is refused | gate | +| `profile` | `read-only`, `workspace-write` or `workspace-write-never-ask`; there is no full access | gate; the shell does not read it yet (the peer's limits come from its workspace and its registered tools) | +| `tools` | the generic host tools `ledger.read ledger.write net.fetch storage.read storage.write card.render` (admitted, not yet implemented by any shell), and of the octos kernel's own tools only `ask_user_question`; anything else is refused. **✓ run 2026-10-01**: `web_search` gives `[refused] agent: agent.tools names the octos kernel tool "web_search"; a contained app's agent may keep only ask_user_question` | gate; the shells hand `ask_user_question` to the peer ([OctoSense#190](https://github.com/OctoSense-org/OctoSense/pull/190)) | | `max_iterations`, `token_budget` | clamped to 8 and 200 000 | gate (`grants:` line) | -| `model.needs` | from `tool_calling vision long_context reasoning structured_output multilingual` | gate; the gate warns when an app has tools but no `tool_calling` | +| `model.needs` | from `tool_calling vision long_context reasoning structured_output multilingual` | gate; the gate warns when an app has tools but no `tool_calling`. The shell does not choose a model from it yet | | `model.tier` | `fast`, `standard` (default) or `strong` | gate | | `model.local_only` | app-wide; data must not leave the person's devices | gate (shareable tools must say `private_data: false`) | | `model.per_task` | named tasks `[a-z_]{1,32}`, at most 8, that `AGENT.md` refers to | gate | @@ -501,13 +705,26 @@ refused. ### Approvals: `risk` and `confirm` Declared per tool in `tools.json` (next section). Whether a call needs the -person comes from `risk`; whose surface asks comes from `confirm` (App Hub -PUBLISHING): +person comes from `risk` and `outward`; whose surface asks comes from +`confirm`; whether a standing rule may answer comes from `auto_approvable` +(App Hub PUBLISHING; `outward` and `auto_approvable` since +[App-Hub#44](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/44)): | `risk` | Runs | | --- | --- | | `read` (looks), `act` (changes the app's own state) | unattended | | `destructive` (sends, posts, shares, buys, deletes) | only after the person approves | +| `act` with `outward: true` (reaches outside the device) | only after the person approves, like a destructive tool | + +- `outward: true` on a `read` tool is refused (**✓ run 2026-10-01**): + `[refused] tools: brief.note.share is outward but its risk is read: a call that reaches outside the device is at least act`. + On an `act` tool the gate warns: + `[warning] tools: brief.note.share is outward: every call waits for the host's approval`. +- `auto_approvable: false` (default `true`): no standing rule ("allow for an + hour") may approve it, so every call needs the person live. Use it for + permanent deletion, payments, sharing outside the device, account and + security changes. The shell takes whichever is stricter, its own rule or + the tool's declaration. | `risk: "destructive"` with | Person present | Person absent | | --- | --- | --- | @@ -535,8 +752,10 @@ is closed, on a schedule; only if you allow it, and you can turn it off." and | --- | --- | | Declarations (fields, sizes, names, schemas, risk, confirm) | **available**: the App Hub gate refuses or warns | | The approval gate at run time (kernel); approve, edit or decline | **available**: the kernel side ([octos#2567](https://github.com/octos-org/octos/pull/2567)) and the shell's approval sheets ([OctoSense#120](https://github.com/OctoSense-org/OctoSense/pull/120), [#145](https://github.com/OctoSense-org/OctoSense/pull/145)); approvals stay on those sheets, not in the app's conversation ([#184](https://github.com/OctoSense-org/OctoSense/pull/184)) | -| In-app conversation with the app's agent | **available** in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)) | -| Memory in `app//…`, promotion by rule | **coming**: ADR 0002 §9, M7. There is no manifest field; memory rules are prose in `AGENT.md` | +| The shell's approval router (`crates/shell/src/approvals/`) | **available**: developer mode, then `confirm: "app"` (the owning app's sheet), then `auto_approvable: false` (always the person), then the person's standing rules, then the live sheet. A rule's conditions fail closed on arguments it cannot read ([#215](https://github.com/OctoSense-org/OctoSense/pull/215)); Approve waits until every argument row was on screen ([#218](https://github.com/OctoSense-org/OctoSense/pull/218)) | +| An audit of every tool call | **available**: each call when it arrives and when it ends, with the caller, the owning app, the tool and a SHA-256 of the arguments (never the arguments), in `logs/tool-calls.jsonl` under the shell's home ([#222](https://github.com/OctoSense-org/OctoSense/pull/222)) | +| In-app conversation with the app's agent | **available** in the shell's "Ask " panel ([OctoSense#184](https://github.com/OctoSense-org/OctoSense/pull/184)) and in a glance card's `sys.chat` ([#263](https://github.com/OctoSense-org/OctoSense/pull/263)) | +| Memory in `app//acct-`, kept per account and erased with it | **available** ([#248](https://github.com/OctoSense-org/OctoSense/pull/248)); promotion by rule **coming** (ADR 0002 §9, M7). There is no manifest field; memory rules are prose in `AGENT.md` | | Overlays by the system agent | **coming**: ADR 0002 §11, M8 | ## The app's tools and peer tools @@ -578,11 +797,22 @@ by the App Hub gate (`crates/app-policy/src/agent.rs`): - `implemented_by`: `host-service` (native code that holds data, network or secrets) or `app` (the app's script, for tools that only reshape its own data). The shell runs a `host-service` tool on the host service of its - namespace, with the app's identity, as the app's own `host.request` would; - it refuses an `app` tool for now (` runs in the app's own script; - open the app to use it`; OctoSense `crates/shell/src/host_tools/script_apps.rs`). -- `background`, `shareable`, `private_data`, `confirm`: see + namespace, with the app's identity, as the app's own `host.request` would, + never from a sheet (`may_prompt: false`), and only when the manifest was + granted that family or the family is a system app's own namespace + (`os.calendar` → `calendar`); otherwise it answers ` was not granted + the service`. It refuses an `app` tool for now (` runs in + the app's own script; open the app to use it`; OctoSense + `crates/shell/src/host_tools/script_apps.rs`). A store app has no host + service of its own, so neither kind runs for it yet. +- `background`, `shareable`, `private_data`, `confirm`, `outward`, + `auto_approvable`: see [An app's own agent](#approvals-risk-and-confirm). +- Each call's arguments are checked against `input_schema` (at most 64 KiB) + and its result against `output_schema` (at most 256 KiB) by the shell's + relay. octos needs an object `output_schema`, so a tool that answers a bare + array (as `mail.accounts` does) cannot be offered as is (OctoSense#267's + follow-ups). The shell hands them to the app's peer: the kernel side is [octos#2567](https://github.com/octos-org/octos/pull/2567) ("host-registered @@ -597,6 +827,8 @@ peer and relays the calls ([OctoSense#145](https://github.com/OctoSense-org/Octo | `peer/tool/call` | kernel → host | `{peer, session_id, context_id, turn_id, call_id, tool_call_id, args_digest, name, args, risk, confirm_required, timeout_ms, tools_version}` | | `peer/tool/result` | host → kernel | `{session_id, peer, host_token, call_id, ok?, data?, error?, status?: "awaiting_confirmation"}` | | `peer/tool/cancel` | kernel → host | `{call_id, reason: timeout \| cancelled}` | +| `peer/tools/unregister` | host → kernel | releases a closed app's tool route ([octos#2658](https://github.com/octos-org/octos/pull/2658), [OctoSense#247](https://github.com/OctoSense-org/OctoSense/pull/247)) | +| `peer/purge` | host → kernel | erases a host-owned peer, its transcript and memory, when the account is removed or the app uninstalled ([octos#2649](https://github.com/octos-org/octos/pull/2649), [OctoSense#248](https://github.com/OctoSense-org/OctoSense/pull/248)) | The model sees `news.list` as `news_list`. Defaults: 30 s per call, results at most 256 KiB, approvals expire after an hour. A gated call (destructive, @@ -610,11 +842,24 @@ or outward) with `confirm: host` waits for a kernel approval; with The templates are **available** ([OctoSense#82](https://github.com/OctoSense-org/OctoSense/pull/82), merged: `crates/toolbox`, crate `octosense-toolbox`), and App Hub has the `research` and `crawl` capabilities ([App-Hub#26](https://github.com/OctoSense-org/OctoSense-App-Hub/pull/26)). -Granting the toolbox to an app's agent is **coming** -([OctoSense#64](https://github.com/OctoSense-org/OctoSense/issues/64)): the -shells' `toolbox-peers` build feature is off in the shipped builds, and -`crawl` / `deep_crawl` is not implemented (the toolbox's one module is -`research`). +Granting it to app agents is on `main` behind the `toolbox-peers` build +feature (`crates/ai-host/src/toolbox_peers.rs`), which the phone's default +build includes and the desktop's does not: + +- `research` gives `workflow.run`, `workflow.fork`, `toolbox.search` and + `toolbox.web_read`; `crawl`, with `max_depth` and `max_pages` above 0 in + the scope, gives `toolbox.deep_crawl`. Each is offered only after the + person allowed the app's agent. +- **Only system apps (`os.*`) get them for now**: a script app's + `research` / `crawl` declaration is honoured only for an `os.*` id, so a + store app cannot grant itself research by writing it into its manifest. + The code marks this as temporary until the shell reads App Hub's verified + research scope. For store apps it is **coming** + ([OctoSense#64](https://github.com/OctoSense-org/OctoSense/issues/64)). No + system app declares `research` on 2026-10-01. +- A store app that declares `research` must also carry the top-level + `research` scope object, or the gate refuses it (**✓ run 2026-10-01**): + `[refused] policy: app dev.example.brief requests research but declares no research scope; add a top-level "research" object (octos's scope; {} means no limits)`. An app's agent never searches, crawls or drives a browser itself. It is granted toolbox tools, and the host runs them outside the app, under the @@ -659,7 +904,9 @@ Per #82's `src/api.rs`, a call is tagged by tool name: Errors are `{"error": {kind, message}}`, `kind` one of `manifest check widening pin not_found not_granted params runtime io`. The agent reaches these as peer tools ([previous section](#the-apps-tools-and-peer-tools)), not -through `host.request`. +through `host.request`. Of this table the shell offers app agents +`workflow.run` and `workflow.fork`, next to `toolbox.search`, +`toolbox.web_read` and `toolbox.deep_crawl` (`crates/toolbox/src/peer.rs`). ### Scope, budgets, provenance @@ -673,7 +920,10 @@ through `host.request`. - **Provenance is the host's**, never the model's: each source carries `id, url, title, source, language, published_at, retrieved_at, evidence_sha256, via`. A URL in the output that the host did not retrieve fails the run. -- **Results** are written to `/toolbox/runs/