Skip to content

Mail and Calendar agents put cards on the glance screen; assistant pane and glance panel polish - #267

Merged
ymote merged 11 commits into
mainfrom
feat/mail-agent-glance-tools
Oct 2, 2026
Merged

ymote merged 11 commits into
mainfrom
feat/mail-agent-glance-tools

Conversation

@ymote

@ymote ymote commented Oct 2, 2026

Copy link
Copy Markdown
Contributor

Stacked on #263 (feat/l0-chat-host), which is stacked on #261. Like #263, this builds only with .sources at OctoSense-org/OctoScript#53 (3ca709c) and OctoSense-org/OctoScript-Makepad#68 (51c4c51); GitHub CI on the pinned runtime fails until those merge and the pins move.

What it does

App agents publish cards to the glance screen

  • Mail: Mail's agent gets mail.notify. The mail host service fills a fixed L0 notice card and publishes it as Mail. The model never writes card code.
  • Calendar (new desktop system app, os.calendar): its agent gets calendar.events, add_event, remove_event (destructive, so the person approves it), notify (an event card) and agenda (the next three events). They run on a new host service, apps/calendar/host-service. It takes the calendar id, so Makepad's example Calendar leaves the catalog.
  • Executor rule: App Hub has no calendar capability, so a system app's own namespace (os.calendar → calendar) runs on its own host service without a grant. Every other family still needs one.

Glance screen

  • A new card opens the glance panel and shows the newest card first. Cards in the panel go through the L0 translation, so tiles keep their colours and chips keep their labels. A tile can be up to 440 pt tall, and the wheel scrolls a card at a time.
  • A bell in the bar counts the cards and lights up when new ones arrive. "At a glance" sits in the menu after Apps.
  • A click on a toast now reaches the toast, even when a pane or window is under it.

Assistant pane (the system chat and "Ask ")

  • On a desktop it opens as a medium pane at the left. You can move and resize it, and a double-click on the header puts it back. It follows the theme, and the approval sheets now stay above it.
  • Replies render Markdown. Wrapping is CJK-aware, using wrap_styled, which every surface uses through ShellDraw::wrap. The transcript is laid out again only when it changes.
  • The prompt takes up to 8 lines, and Shift+Return starts a new line. It has a blinking caret, keyboard and mouse selection, and copy, cut and paste. On a desktop it takes the keyboard when it opens, so the macOS input method composes in it right away.
  • Fixes: the pane's #[redraw] field was draw_bg, which it never draws, so drags and scrolls did not repaint. The pane also no longer runs past the window or sits under the developer-mode banner.

Fonts

  • The shell's drawing kit used to name JetBrains Mono. It now uses the theme's font roles (regular, bold, code), like the apps. Glance cards that name no font also use those roles instead of the backend's Roboto.

Checked

  • cargo test -p octosense-shell --features app-hub --lib: 738 passed.
  • cargo test -p octosense-mail-service -p octosense-calendar-service: passed.
  • cargo check --locked -p octosense --features mobile-apps: passed.
  • Hidden --remote runs on macOS with deepseek-v4-flash:
    • The system agent asked Mail's agent and then Calendar's agent for cards. Calendar went through the first-use sheet, added an event, and its card opened the panel.
    • Drag, resize, caret, selection, Markdown, Chinese wrapping and the theme were all checked.
    • Copy and cut need the platform's menu keys, which a remote run cannot send, so the composer's tests cover them.
  • Unverified: the phone, where Calendar is not packed.

Follow-ups

  • An App Hub calendar capability, so Calendar's own window can list its events.
  • Mail's read tools: mail.accounts answers an array, and octos needs object output schemas.

🤖 Generated with Claude Code

@ymote
ymote force-pushed the feat/mail-agent-glance-tools branch from 3cd9335 to 3470859 Compare October 2, 2026 01:08
@ymote
ymote changed the base branch from feat/l0-chat-host to main October 2, 2026 03:09
ymote and others added 11 commits October 1, 2026 20:10
Mail declares an agent (read-only profile, ask_user_question) and the
`glance` capability, and ships `tools.json` with one tool, `mail.notify`
{title, body, card_id?, priority?}. It runs on the mail host service,
which fills a fixed L0 notice card (resources/notice.card) from the
call and publishes it through the shell's glance service as the calling
app, with a notification; the model never writes card code. The shell
installs the publisher at startup and passes the app's `glance` grant
from its admitted manifest (script_apps::grants), since a tool call runs
outside the app's isolate.

Verified on a scratch home (deepseek-v4-flash, demo mailbox, developer
mode, OCTOSENSE_CONTAINED_APPS=1): the system chat's peer_send_input
started os.mail's turn, Mail's agent called mail.notify, and the card
and toast "Mail · Hello" appeared.

Mail's read tools (accounts, list, message) are left out: octos takes a
tool only with an object output schema, and mail.accounts answers an
array.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e moves and sizes

A toast is drawn over the card window, the glance panel, the chat panes
and the windows, but the panes took the pointer first: a click on a toast
over the system chat pressed the pane's button beneath it (New
conversation) and never opened the card. A press on a toast now goes to
the toast alone, after the modal sheets and menus only.

On a desktop the system chat and "Ask <app>" panes can be placed: the
header moves the pane, an edge or a corner sizes it (with resize
cursors), never below 320 x 240 or off screen; a double-click on the
header puts it back in its column. The pane also keeps to the window: its
surface starts under the bar yet is the window's height, so its bottom
edge and padding hung past the window.

Verified in a hidden --remote run with the fake Mail toasts: a click on
the UPS toast where it covers New conversation opened the UPS card; the
header, right and bottom edges moved and sized the pane; a double-click
reset it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…links

The pane's `#[redraw]` field was `draw_bg`, which it never draws, so every
repaint it asked for (a drag, a scroll, a hover) repainted nothing: the
pane moved only when something else redrew the window. Its own area is
now the redraw target. A 30-step header drag on a visible window: about
2 ms from each move to its frame.

The prompt's caret blinks (half a second, as makepad's TextInput) while
typed text lands in it: its prompt holds the key focus, or nothing does
and the shell hands typed text to it (F8). Typing keeps it on; an empty
prompt shows it before the placeholder. `ShellDraw::text_field_caret`
draws the field with the caret on request; `text_field` is unchanged.

The pane also stays above the developer-mode banner, which covered the
bottom of the prompt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s several lines and follows the theme

Replies are Markdown (system_chat/markdown.rs): headings, bold, inline
code on a tint, links in the accent, bulleted and numbered lists with
hanging indents, quotes with a bar, rules, fenced code and table rows on
a tint with their spacing kept. What the person typed is shown as typed.

Wrapping (shell/ui.rs `wrap_styled`, used by `ShellDraw::wrap` for every
surface) breaks between CJK characters, never before closing
punctuation, and inside a word longer than a line (a URL); before, a
Chinese sentence was one word, put on one line and cut with an
ellipsis. Styled runs wrap together.

The prompt takes up to eight lines and then shows its last ones (the
caret's); Shift+Return breaks a line, a paste keeps its breaks, Return
and an input method's Enter still send. On a desktop the prompt takes
the keyboard when the pane opens (F8, the menu), so the macOS input
method composes Chinese in it at once with its candidates at the caret:
makepad hands keys to the input method only while a text field holds
the focus.

The chat panes now take the shell's material and palette like its other
surfaces, so they are light in the light theme and dark in the dark one.
The laid-out transcript is cached until the conversation, the width or
the font changes.

Checked in a hidden --remote run with deepseek-v4-flash: a two-line
Chinese and English prompt wrapped in the prompt; the reply's heading,
bold, code, nested list, numbered list, quote, rule, code block and
table drew in the light theme. Chinese marked bold draws at the regular
weight (the fallback CJK font has no bold face).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lays out the transcript

The prompt wrapped the draft like a reply: spaces were only break points,
so a typed space showed (and moved the caret) only with the next
character, and a run of spaces showed as one. It now wraps with
`wrap_exact`, which keeps every character: a space at a line's end hangs
past it, blank lines stay, and the lines put back together are the text.
The caret is measured at the drawn size.

The transcript's cache was keyed on the chat's generation, which typing
bumps, so every keystroke laid the whole transcript out again; it is now
keyed on the items, the running turn, the width and the font.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
On a desktop the system chat opens at the left, the column's width and
60% of the screen's height (at most 640), centred top to bottom, instead
of a full-height column at the right; "Ask <app>" stands right of it when
both are open. A double-click on the header puts the pane back there.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ards light it

The glance panel had only F9. The bar now carries a bell among its
status icons: the cards on the glance screen counted on a badge, lit in
the accent while cards have arrived since the panel was last open
(cards published after `glance_seen_ms`); clicking it toggles the panel,
which then wears the open-panel pill. The menu lists "At a glance" right
after Apps. A published, replaced or withdrawn card refreshes the bar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s' does

The shell's drawing kit named JetBrains Mono for every label (the bar,
menus, toasts, panels, the card window's title, the chat panes), while
apps take the theme's font roles (makepad's font policy: IBM Plex Sans,
LXGW WenKai for CJK, Noto Color Emoji, unless a style names its own) and
glance cards the backend's Roboto. The kit now draws with the theme's
regular and bold roles, and its code role for code (`with_code`: code
blocks and inline code in the chat panes); Chinese gets the theme's CJK
faces, bold included.

A glance card's text follows the theme too: the backend names Roboto (its
reference renderer's face) for text in no font of its own, and the shell
rewrites those styles to the theme's bold role at weight 500 and up and
its regular one below (`glance_card::theme_fonts`); a mood with its own
font (atro's Montserrat) keeps it.

New glance cards light the bar's bell in the palette's accent, not the
bar's alert colour.

Checked in hidden --remote runs with the demo Mail cards: bar, toasts,
card window, glance panel and chat pane in the theme's fonts, Chinese in
LXGW WenKai. Found, not changed here: in the glance panel (not the card
window) the shipping card's tiles draw light with white text and the
request card's chips lose their labels, with or without this change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e and scrolls

A card an agent published reached only a toast: the glance panel stayed
closed, and when open it drew cards through the general translation,
which re-lowers chips the Material way and drops their labels, and drew
the shipping card's tiles light under white text. Now:

- a new card opens the glance panel on a desktop (not over an open card
  window), showing the newest card first; a card the open panel shows
  posts no toast over it;
- `glance_card::lower` uses Octoscript-Makepad's L0 translation, as the
  card window does: tiles in the kit's colours, chips with their labels;
- a tile may be 440 pt tall (was 260), room for a whole action card with
  its buttons;
- the wheel over the panel moves a card at a time, with "N more above /
  below" at its foot;
- the panel's `#[redraw]` field was `draw_bg`, which it never draws; its
  area is the redraw target now; a press outside it that closes it is
  logged.

Checked in a hidden --remote run with the demo Mail cards: the panel
opened by itself on the UPS card (tiles, Track, Mark done) with "1 more
above", and the wheel brought the request card (Reply, Mark done, Ask).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…election, copy and paste

The prompt's caret was pinned to the end. The composer now keeps a caret
and a selection: arrows move it (Option by words, Command to the line's
and, with Up/Down, the text's ends), Home and End, Shift selects,
Command+A selects all; Up and Down go by the drawn lines, keeping the
caret's x. A click places the caret, Shift+click or a drag selects, a
double-click takes a word. Typing, a paste and an input method's
composition go in at the caret over the selection; Backspace and Delete
take the selection or the character beside the caret; Command+C and
Command+X copy and cut the selection (the shell answers TextCopy and
TextCut for the pane typing goes to). The selection is drawn behind the
text, the caret where it is, and the lines shown follow the caret.

Checked in a hidden --remote run: Option+arrows and Shift selected
"hello card", a double-click took "Mail" and Backspace removed it, a drag
selected across words. Copy and cut go through the platform's menu
keys, which a remote run cannot send: covered by the composer's tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ts event and agenda cards on the glance screen

Calendar (`os.calendar`, desktop) joins Mail as a system app with an
agent: its manifest declares the agent (read-only profile,
ask_user_question) and `glance`, and `tools.json` gives it
`calendar.events`, `calendar.add_event`, `calendar.remove_event`
(destructive: the person approves), `calendar.notify` (an event card)
and `calendar.agenda` (the next three events). Its peer has octos's
per-app memory like Mail's.

The tools run on a new host service, `octosense-calendar-service`
(apps/calendar/host-service): events in the host's own directory
(`<host_dir>/calendar/events.json`), local times, and fixed L0 cards
(`event.card`: day and time tiles, place, notes; `agenda.card`) filled
from the events and published through the glance service as Calendar,
so the model never writes card code. Only Calendar may call it.

App Hub's closed capability list names no `calendar`, so the shell's
executor now runs a system app's own namespace on its own service
(`os.calendar` → `calendar`) without a grant; any other family still
needs one. Calendar's window says how to reach the agent: listing the
events there waits for App Hub's `calendar` capability. It takes the
`calendar` id, so Makepad's example Calendar leaves the catalog, as its
Mail and Photos did.

The approval sheets and the Approvals page now take the shell's theme
and material too: lifted with the chat panes, they stay over them (the
first-use sheet had drawn under the Assistant pane).

Checked in a hidden --remote run with deepseek-v4-flash: the system agent
asked for Calendar's agent, the first-use sheet showed over the pane,
Allow, then Calendar's agent added "Dentist" (2026-10-02 15:00-16:00, Main
St) and published its event card, which opened the glance panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ymote
ymote force-pushed the feat/mail-agent-glance-tools branch from 3470859 to 0e0393f Compare October 2, 2026 03:14
@ymote

ymote commented Oct 2, 2026

Copy link
Copy Markdown
Contributor Author

Local CI passed on 0e0393f (tools/ci-local.sh --only desktop,phone,apps --linux-host, 22m40s, Darwin arm64; the ubuntu jobs on the Linux build host, Linux x86_64).

GitHub would run for this PR: desktop.yml, phone.yml, apps.yml. Merged on this local pass (macOS runner queue); GitHub CI runs on the merge commit on main.

Job Step Result Time
ci-local / drift The local mapping fits the workflows PASS -
desktop.yml / desktop Prepare the pinned framework sources PASS 0s
desktop.yml / desktop Compile the desktop with the shared runtime PASS 1m40s
desktop.yml / desktop One copy of every shell source PASS -
desktop.yml / desktop Desktop tools and scripts PASS 8s
desktop.yml / desktop One Makepad, App Hub, octos and Rinx in the locked graph PASS 1s
desktop.yml / desktop The native apps' generated places match native-apps.json PASS 0s
phone.yml / home Prepare pinned sources and the reviewed runtime patch PASS 0s
phone.yml / home Compile Home and its bundled modules PASS 2m29s
phone.yml / home Verify the single runtime graph PASS 2s
phone.yml / home The native apps' generated places match native-apps.json PASS 0s
phone.yml / home The pinned octos revision PASS 0s
phone.yml / home Build octos (unless cached) PASS -
phone.yml / home Build Home, App Hub admission and runtime policy tests PASS 3m28s
phone.yml / home Test Home, App Hub admission and runtime policy PASS 8m05s
phone.yml / home Two-lane scenario (real kernel, scripted model) PASS 36s
apps.yml / services (linux) python3 tools/setup.py PASS 0s
apps.yml / services (linux) Kernel service PASS 10s
apps.yml / services (linux) App peers PASS 23s
apps.yml / services (linux) AI providers config PASS 3s
apps.yml / services (linux) L0 in-card chat (sys.chat, host side) PASS 12s
apps.yml / services (linux) System toolbox (workflow templates) PASS 1m28s
apps.yml / kernel-security (linux) python3 tools/setup.py PASS 0s
apps.yml / kernel-security (linux) The pinned octos revision PASS -
apps.yml / kernel-security (linux) Build octos (unless cached) PASS -
apps.yml / kernel-security (linux) Real-kernel tests PASS 30s
apps.yml / apps python3 tools/setup.py PASS 1s
apps.yml / apps Host services (Mail, AI providers, News) PASS 1m08s
apps.yml / apps Shell AI services (crates/ai-host) PASS 3m51s
apps.yml / apps AppCard runtime setup safeguards PASS 0s
apps.yml / apps AppCard clippy (own crates; shared runtime compiles) PASS 39s
apps.yml / apps AppCard transport and store PASS 27s
apps.yml / apps One Makepad, App Hub, octos and Rinx in the locked graph PASS 4s
linux-host / sandbox (linux) python3 tools/setup.py PASS 0s
linux-host / sandbox (linux) Process sandbox on Linux (Landlock, seccomp) PASS 1m16s

Notes:

  • GitHub runs Python 3.12; this run used Python 3.9.6
  • GitHub runs Python 3.12; the Linux host used Python 3.14.4

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant