eleven / ocho Public

Clone
git clone https://amadan.net/eleven/ocho

Plain git — no account needed to clone.

Download

Download this file

1# πŸ€– Views β€” a bot shows you data, in place
2
3Status: **built** (branch `thread-views`). The evolution of the terse
4service-op notice ("Done β€” issues.list on github returned 50 items"): when a
5bot has list-shaped data worth *looking at* β€” GitHub issues, news headlines,
6search results β€” it can now create a **view** in the chat where you asked.
7
8## What a view is (and isn't)
9
10A view is **schema + data, never code**. It is:
11
12- **A surface the current chat carries.** Not an app, not a separate chat, no
13 sidebar entry. It opens from an **Open view** chip on the bot's reply as a
14 full-height right-hand column (the command panel's `side` placement β€” a
15 vertical sidebar suits list-shaped content; narrow screens fall back to the
16 in-flow band), and dismisses like any panel.
17- **Thread state, like everything else.** A view rides the thread's own
18 encrypted log (`view.create` / `view.update` / `view.remove` events), folded
19 client-side by the `views` registry app and covered by the ordinary app-state
20 snapshots β€” so it syncs to every member and every device, and any bot or app
21 in the chat can read or replace it. The server stores ciphertext, as ever.
22- **Rendered by the app schema.** The stored spec is expanded (a pure
23 function, `viewSchema`) into a real app-schema list that the shared
24 schema-engine renders β€” the same machinery that draws To-do and Reminders on
25 web and native. Views inherit search, sections, and rows for free, and the
26 native runtime can render them the day it adds the same expansion.
27
28Why schema+data instead of letting the model write a renderer: the engine is
29reviewed platform code, so a model-authored view is *inert data* β€” it can't
30fetch, can't script, can't touch the DOM. That's why views need no sandbox
31(unlike member apps, which are code and get the opaque-origin iframe).
32
33## The spec (what a bot sends)
34
35```jsonc
36{
37 "type": "view.create", // or view.update (replace by id), view.remove
38 "id": "vw-a1b2c3",
39 "title": "Top GitHub issues", // ≀ 80 chars
40 "glyph": "β–€", // optional, ≀ 4 chars
41 "items": [ // 1..50
42 {
43 "id": "1", // optional; assigned when absent
44 "title": "Crash on login", // required, ≀ 300
45 "subtitle": "#412 Β· opened by amy", // optional, ≀ 300
46 "note": "Prio: urgent", // optional, ≀ 500
47 "url": "https://github.com/…", // optional, https only, ≀ 600
48 "accent": true // optional; tints the subtitle
49 }
50 ]
51}
52```
53
54Bounded twice, fail-closed both times: the sidecar validates the model's
55`create_view` tool input (`internal/bot/view.go`, modeled on `normalizeCard`)
56before anything posts, and the client fold re-validates every event
57independently (`web/static/apps/views.js`) β€” payloads aren't sender-signed, so
58the fold trusts nothing: strings clamped, non-https URLs dropped, ≀ 50 items,
59whole spec ≀ 32 KB, ≀ 20 live views per thread (oldest evicted). A row's tap
60opens its `url` in a new tab (`rel=noopener`); rows without one aren't
61tappable. Everything renders via `textContent` β€” a view can display data and
62link out, nothing else.
63
64## How Eleven creates one
65
66`create_view` is a bot tool in two places:
67
68- **The research loop** (`internal/bot/research.go`) β€” the main path. "List
69 the 5 highest priority GitHub issues" β†’ the member taps the research consent
70 chip β†’ the loop reads `issues.list` under the short-lived grant, the model
71 sorts, calls `create_view`, and the sidecar posts the `view.create` event.
72 Validation errors return as the tool_result, so the model fixes its own spec.
73- **The conversational brain** (`internal/bot/brain.go`) β€” an executor, like
74 `create_app`: when the data is already in context, the model can create a
75 view directly.
76
77Either way the sidecar β€” never the model β€” appends the **`open_view` chip** to
78the reply, labeled with the view's real title, so the chip can't claim one
79view and open another. `open_view` joins the client chip whitelist
80(`ui.js CHIP_TYPES`) at the harmless-and-reversible bar: the tap only opens a
81panel over state already folded on this device.
82
83## Reuse: the news-bot shape
84
85A charter bot posts its morning summary, creates a view of the articles, and
86the reply carries **Open view** β€” headlines listed, each tapping through to
87the article. Same rail, no new machinery. `/views` (on the views app) reopens
88any live view in the thread later, so the chip isn't the only door.
89
90## Scope
91
92Web-first, deliberately: native renders through the webview surfaces today,
93and the schema expansion is pure + twin-able (`docs/app-schema.md Β§ tap.url`
94notes the one additive primitive native still ignores). Fold vectors live at
95`test/event-vectors/views.json` so the native fold lands against the same
96spec. Interactive views (rows that emit events back to the thread) are the
97natural next rung β€” the schema engine already supports actions; the spec just
98doesn't expose them yet.
99