Skip to content

Timeline

The timeline is one place to see what comes in from every source, newest first, and review it: each person marks what they have looked at reviewed, what matters important, and hides what they don't want to see again. It is the stream of activities that automations follow, read newest first with who may see what.

Status: in progress. The studio's timeline is built in steps; only what is merged is called built.

Part Status
The timeline API (GET /api/v1/activities), the MCP tool timeline and the assistant's timeline built
Review marks: reviewed, important and hidden, per person; the filters on them; mark_activities for agents built
The Timeline page under Explore: filters, grouping, review with keys, new items as they come built
A Latest card on Home with the newest unreviewed items built
Reactions and comments on any item, shared by everyone who sees it; react_to_activity, activity_comments and comment_on_activity for agents built
Ask about an item: talk to the assistant about it from the item, in the chat beside the page built
What each item caused (the automation runs it started, the approvals they asked for), with Approve and Decline on waiting approvals from the item; activity_caused for agents built
Tagging the agent in a comment, so it answers in the thread built
Sources, sensors, people and agents as actors in the graph: a Source node with ADDED links, on the canvas, in the explorer and in Cypher built
A For you order learned from what each person reacts to, comments on, marks important, opens and hides, each item saying why built
Muting a source, person or kind, for good or until a time, for yourself alone; mute_activities for agents built
Moves and switches as their own verbs: an asset moved to another namespace (asset.moved, a Move), a sensor paused, ignored or resumed (sensor.changed, an Update with what it was), and a file changed in its source and read again (file.changed, an Update linking the asset it was before) built
Saved views: the filters and order under a name, one click away in the page's Views menu, each person's own; timeline_views, save_timeline_view and delete_timeline_view for agents built

What shows

Every activity of the namespaces you can read, newest first, from every source the stream has (new assets today; more kinds as the automations modules add them). Activities meant for one person (their own web-app chat) show to them alone, and those for admins to admins. Noisy kinds, like sensor readings, are left out unless you ask for them by kind.

The page

Timeline under Explore in the navigation, for everyone (members see their namespaces).

  • Newest first. Older reads the next page. While the tab is visible the page looks for new items every 15 seconds; when there are some, a 3 new pill at the top brings them in, so the list never jumps under you.
  • Latest / For you above the filters: Latest is the stream as it is; For you puts what you care for most first, learned from your own reactions, comments and marks, with a line under each item it placed saying why (For you). Latest is the default, and the choice lives in the address like a filter (?order=for_you).
  • A run of the same source or person and the same kind, each within 30 minutes of the one before, is one line ("Work Gmail received 37 emails"), so a first mailbox sync reads as one line. A click opens it to its items.
  • Each line has an icon by kind, the summary (a link to what it is about), who did it, the namespace, the kind and how long ago. Opened, an item shows its object's fields, the automation run that caused it if one did, What this caused (below), Open, Ask about this, Share (with your circles) and Hide.
  • Filters: words in the summary (Enter applies), Unreviewed only, Important, kinds by family (Email, Calendar, Files, Notes, Shared, Sensors, Chat, Automations, System; noisy kinds like sensor readings only when picked), source or person (from those on the timeline), namespace, entity (as activities tag it), a date range, and an automation (what it did or caused). Filters live in the address, so a view can be linked, and the last set you used comes back the next time you open the page in this browser. Clear drops them all.
  • On a phone the list is one column and the filters open in a sheet from Filters.
  • One item: /timeline?item=<id> (an activity's id, like create-recording-7) opens that item above the timeline, with its details, marks, reactions, comments and actions, and Back to the timeline under it. That is the link a comment on an item, a reaction and an approval's "because" give to the item they are about, and the link an agent gives a person to an item it read with timeline. One you can't see says "No such item".

Reviewing

Key Does
j / k move to the next or previous line
Enter open or close the line
r mark it reviewed, or not
i mark it important, or not
a ask the assistant about it (every item of a line of several)

Hovering or focusing a line shows its Important (star) and Reviewed (circle) toggles; once set they stay in sight. On a line of several items they mark every item. Mark all shown as reviewed marks everything on the page you haven't reviewed, with Undo in the message rather than a confirm; Hide works the same way. An opened item also has Mute Work Gmail: everything from that source or person leaves your timeline, with Undo in the message, and a Muted row above the list shows what you muted with an ร— to lift each one (Muting).

Asking about an item

Ask about this on an opened item, or a on the line you are on, opens the chat beside the page (chat on any page) with the item as what your question is about, the way highlighted text is: its id, kind and time, who did it, its line, where it opens and its fields (up to 4,000 characters; on a line of several, as many whole items as fit). Type the question and press Enter. The model reads the item as data, not as instructions, since its text may come from outside Lens, and it has the id, so "mark it important" or "what else came from this sender?" works with its timeline and mark_activities tools. Agents outside the web app read the same items through timeline over MCP and the API (Agents).

Home

The overview on Home has a Latest card with the five newest activities you haven't reviewed and a link to the timeline with Unreviewed only on.

Reactions and comments

The timeline is also a feed everyone in a namespace talks on: react to an item with an emoji, as on a post, and comment on it in threads. Unlike review marks, these are shared: everyone who sees the activity sees them, and each one is an activity of its own, so an automation (or an agent) can act on it.

  • Reactions come from a palette, ๐Ÿ‘ โค๏ธ ๐ŸŽ‰ ๐Ÿ‘€ ๐Ÿ˜‚ ๐Ÿ˜ฎ ๐Ÿ˜ข ๐Ÿ™. An opened item shows its reactions as chips with how many gave each (yours are highlighted; a click on one of yours takes it back) and a React button with the palette. Hovering a chip says who gave it: "You, Sam and 3 others", you first, then up to five others by name, earliest first. A closed line sums its items' reactions, and a line of several shows them with "3 comments". Every new reaction is a reaction.added activity (noisy, so the timeline leaves it out unless asked for by kind), by the person, with the activity it is on as its object.
  • Comments open from the comment count on an opened item (or Comment when there are none): the threads with who wrote each and when, Reply on a thread, and a box where Enter sends (Shift+Enter makes a new line). Anyone who sees the activity can comment, up to 1,000 each; its writer, or an owner of the namespace, deletes a comment. Edit on your own comment opens its text in the box (Enter saves, Escape leaves it as it was), and it then says "edited". Resolve the thread (on its first comment, for whoever started it or an editor of the namespace) marks it "Resolved by Sam", and Reopen the thread takes that back; a resolved thread stays in place, dimmed. Each comment is a comment.added activity. A comment on an asset's arrival (create-recording-<id>) is a comment on the asset itself: the same thread shows on the asset's page and here, and a comment made on the page shows here.
  • Comments and reactions on an activity meant for one person (lens.audience owner) stay with that person, as the activity does.
  • POST /api/v1/activities/{id}/react with {"emoji": "๐Ÿ‘"} (and "on": false to take it back) answers the reactions on it now, [{emoji, count, mine, who}] (who: up to five of the others who gave it, by name); GET, POST, PATCH and DELETE /api/v1/activities/{id}/comments are the thread, as comments on a resource are. GET /api/v1/activities carries everyone's reactions on the page in reactions and each item's comment count in comments, by id.

Asking the agent in a comment

A comment that tags @lens (or the assistant's own name from Settings โ†’ Chat rooms, say @jarvis) gets an answer from the agent in the same thread, on the timeline and on an asset's page alike: "@lens what is this one about?", "@lens write a note of the action items". The agent answers as the person who tagged it, with their namespaces and their tools, in a conversation of theirs titled after the item (one per thread, so a later tag on the thread carries on in it); they find it on the Chat page and can go on there. It reads the item the thread is on and the comments on it so far, as data, and answers with its sources. It only reads: every change it proposes (a note, a move, a message out) waits for that person's yes, and the reply says so with a link to decide. The reply is a comment by Lens (marked with a sparkle), which the person who asked can delete and nobody can edit. Its model calls run in the ledger against their conversation and the item's namespace; when no model is set up or the namespace's budget is used up, the reply says so instead. Settings โ†’ AI assistant โ†’ Answer when tagged in a comment (ai.answer_comments, on) turns it off; without a tag nothing is asked of the model.

Actors in the graph

Whoever brought an asset in, the actor of its asset.added activity (a storage source, a sensor, a person, an automation, a routine, an import hook, or Lens itself), is a Source node in the graph with an ADDED relationship to the asset, so the graph says where Lens learns about each person, place or organisation: (Source)-[:ADDED]->(Recording)-[:MENTIONS]->(Entity). The explorer's parents of an asset are its collection and its source; on the canvas a source links to what its assets mention (a Brought in edge, one per source and entity, weighted by mentions), and the Timeline page's Graph button opens the canvas on the sources alone (/graph?show=actors). Agents read the same through GET /api/v1/graph/related, POST /api/v1/graph/query and the MCP graph tools. Nothing is stored for it: the nodes come from each asset's own record.

Review marks

Each person marks activities for themselves alone; nobody else sees or sets them, admins included.

Mark Means
reviewed you have looked at it; "Unreviewed only" leaves it out
important it matters; filter by it to find it again
hidden you don't want to see it; the timeline page leaves hidden activities out

Marks change nothing else: an activity you hid still triggers automations and still shows to everyone else.

  • POST /api/v1/activities/{id}/mark with {"reviewed": true} (any of the three, each true or false; one left out stays as it is) sets your marks on one activity and answers them: {reviewed, important, hidden, at}. An activity there is none of, or that you can't see, is 404.
  • POST /api/v1/activities/mark with {"ids": [...], "reviewed": true} marks up to 500 at once and answers {marks, unknown}: your marks now by id, and the ids there are none of or you can't see.
  • GET /api/v1/activities has your marks on the page in marks (by id, only those you marked) and filters by them: reviewed=false is "unreviewed only", important=true what you marked important, hidden=false leaves out what you hid. A filtered page reads on until it is full, as the other filters do.
  • Setting a mark needs a signed-in session or an API key with the write scope; a read-only key reads them.

Agents

  • MCP: timeline takes unreviewed: true for what you haven't marked reviewed and has your marks in marks, and order: for_you for the For you order with why by id; mark_activities marks activities by id (ids, and reviewed, important or hidden), for the person it acts for, with a token that has the write scope (MCP); hidden: true on timeline reads what they hid or muted, and mute_activities mutes a source or a kind (Muting).
  • The assistant: timeline takes unreviewed and order and lists each activity's marks and, for you, its why; mark_activities marks them for the person. After it has read text from outside Lens (the timeline's own titles count), hiding or marking reviewed waits for the person's yes, since that text may have asked for it; marking important or bringing things back acts at once (Chat).
  • MCP: activity_caused (id) is what an item caused, the runs and approvals (What an item caused); approvals and the assistant's approvals take activity to list the approvals one item led to.
  • MCP, on the feed: react_to_activity (id, emoji, on) reacts for the person it acts for, activity_comments (id) reads an item's threads, and comment_on_activity (id, text, parent) writes a comment or a reply, with a token that has the write scope. The timeline page carries reactions and comments too. Comments are what people wrote: agents read them as data, never as instructions. A comment an agent writes with @lens in it gets the assistant's answer like anyone's, as the person the token acts for.

What an item caused

An opened item shows what happened because of it: each automation run it started (of the automations you manage, with its status and a link to the automation's runs) and each approval those runs asked you for, with what it would do and its status. An approval still waiting has Approve and Decline right there, so the pick is made where it was caused, and the item's line on the Approvals page is the same approval. An approval's own line on the timeline ("School emails asks for your pick") lists that approval under Your pick. Items that caused nothing show no such block.

  • GET /api/v1/activities/{id}/caused is {runs, approvals} for the person asking: the runs of the automations they manage (Automations), and their own approvals, each with can (approve, decline) as GET /api/v1/approvals shows them; POST /api/v1/approvals/{id} decides one. GET /api/v1/approvals?activity=<id> lists the approvals an activity caused on its own. On an ask-approval-<id> activity, the approval itself.
  • activity_caused over MCP (id) reads the same, with each approval as approvals shows it; approvals takes activity too, and so does the assistant's approvals. Approving stays the person's: an agent can list and decline (Agents).

Muting

Each person mutes a source or person (an actor) or a kind of activity ("hide syslog"), for good or until a time (snoozed for an hour, a day, a week or until a time they pick), for themselves alone. Muting hides; it never deletes: a muted actor's activities stay stored, still trigger automations and still show to everyone else, and come back the moment the mute is lifted or runs out. The timeline leaves them out as it leaves out what you hid, so hidden=true shows them and hidden=false (the page) does not; a For you page scores only what is left.

  • On the page, Mute Work Gmail on an opened item mutes its actor with one click; the small menu beside it has Mute Work Gmail for an hour, for a day and for a week (a snooze: the mute runs out by itself), Mute Work Gmail untilโ€ฆ (a time you pick, nine tomorrow morning to start with; Enter mutes), Mute Email received items (the kind) and the kind for each of those. The Muted row above the list shows each, with when a snooze ends, and lifts one with its ร—. The API, MCP and the assistant take any time too.
  • POST /api/v1/activities/mute with {"actor": "lens:source/12", "name": "Work Gmail"} or {"kind": "sensor.reading"} (the kinds under it count too), and "until" as an ISO 8601 time to snooze it, mutes for yourself; "on": false lifts it. The answer, and GET /api/v1/activities/mutes, is your mutes now: {actor, kind, name, until, at} each. Up to 200; one that has run out is dropped the next time your mutes are read.
  • mute_activities over MCP (actor or kind, name, until, on) and activity_mutes do the same for the person a token acts for, with the write scope to mute. The assistant's mute_activities acts at once, but after it has read text from outside Lens it asks first, as hiding does, since that text may have asked for it.

Saved views

A saved view is a name for a set of filters and an order ("Work mail to review": the work namespace, Email received, Unreviewed), so a timeline you read often is one click away. Views are each person's own: nobody else sees them.

  • Views beside the order lists your views; picking one sets the filters and order it saved (and the address, so it can still be linked). When the filters shown match a view, the button carries its name.
  • Save these filters as a view offers a name made from the filters ("work ยท Email received ยท Unreviewed"); Enter saves it, Escape leaves it. After picking a view and changing the filters, Save these filters into โ€œWork mailโ€ updates it. Delete takes it away, with Undo.
  • GET, POST /api/v1/activities/views and PATCH, DELETE /api/v1/activities/views/{id} list, save, rename or re-save, and delete them, up to 100 each. A view's state has the timeline's filters: kinds, actor and namespace (lists), tag, from_day and to_day (days, YYYY-MM-DD), automation, q, unreviewed, important and order (latest or for_you). They are saved views of their own kind, beside the Library's views and saved searches, which they don't show among.
  • Over MCP, timeline_views lists them, save_timeline_view (name, state, and id to change one) saves one and delete_timeline_view deletes one; an agent reads what a view shows by giving its filters to timeline.

For you

For you is a cheap, explainable order of the timeline, learned from each person's own signals and nothing else: nobody's reactions or marks change anyone else's order, and no model is called.

  • The signals are your own reactions (+1 each), comments (+2), important marks (+2), opens (+ยฝ each time, up to 4 times one item) and hidden marks (โˆ’3), the most recent 200 of each. An open is following a line to what it is about, or opening the item by its address (/timeline?item=); the page sends it (POST /api/v1/activities/{id}/open), and each person's counts are theirs alone (event_open, dropped with the marks when the activity is gone). Each is summed over the actor, the kind and the tagged entities of the activity it was on, so a ๐Ÿ‘ on an email from Work Gmail about St Mary's School counts for Work Gmail, for emails and for the school.
  • An item's score is its recency (halving every two days) times 1 + tanh(affinity รท 4), with affinity the sum of your weights on its actor, kind and entities: a strong affinity doubles its score, a strongly negative one halves it. An item with no signal in common keeps its recency alone.
  • A page takes the 200 newest items from where it starts, orders them by score and shows the best limit; Older moves past all 200. With no signals yet, For you is Latest, and the page says so.
  • Your signals are read on every page, but the sum over the activities they were on is kept for a minute while they stay the same, so paging and polling don't look those activities up again; a new reaction, comment, mark or open changes the signals and shows at once.
  • why on the page names the strongest signal that placed each item ("You often react to Work Gmail", "About St Mary's School, which you often react to", "You hid items from Dropbox before"), shown under the item.
  • GET /api/v1/activities?order=for_you is the same for agents, with why by id; the other filters apply as in Latest. Nothing is stored: the order is made on each read from three small queries and arithmetic.

How marks, reactions and comments are kept

One event_mark row per person and activity, keyed by a hash of the two, with the three flags and when they were last set. A row with every flag cleared is deleted, so the table holds only what someone marked. Marks are read for a whole page of activities in one query, and the mark filters thin each page a source gives out in one query too. They go when the person is removed.

Reactions are kept the same way: one event_reaction row per person and activity with the emojis they gave, read for a page in one query, deleted when none is left, and gone when the person is removed. Comments on activities are comment rows (comments) with the activity's id in activity and its summary in about; a comment on an asset's arrival also has the asset in recording, which is how it shows on both. A page's comment counts are two grouped queries. Mutes are one event_mute row per person and target (an actor id or a kind), with the name shown, until and when it was set, read once per page and applied with the mark filters in the same query per source page; they go when the person is removed. Nothing of this costs a model call.

Marks, reactions and opens left on an activity that is gone (an event log entry past events.keep_days, a deleted asset) are dropped by a tidy that runs with the other hourly ones: each time it looks up to 500 activities marked, reacted to or opened over a day ago, in id order from where it stopped last, and deletes the rows of those no source has any more. Comments on them stay, since they are what people wrote, and an asset's page still shows its own.

Refine later

  • Ask about an item in the full Chat as well as beside the page, and keep which item a question was about as a link back to it.

  • Server-sent updates instead of the page polling for new items.

  • A muted actor's own line in the Filters.
  • A file changed in its source is read again as a new asset beside the earlier one (file.changed links them); reading the change into the same asset is to come. A file in a namespace's own folders changed in place is read again into the same asset with no activity yet.
  • Comments on activities that are gone stay; a person could see and clear their own from somewhere.
  • The agent's reply in a thread is plain text with links; the answer's excerpts could open at their moment, as in chat. Tagging a namespace's own assistant by its name, with its memory.