Skip to content

Commit c730d8f

Browse files
authored
feat(logbook): automatic work journal plugin with Control UI timeline tab (#99930)
* feat(logbook): automatic work journal plugin with a plugin-contributed Control UI tab Squash of PR #99930 work for rebase onto the Control UI route refactor: - extensions/logbook: Dayflow-style capture -> observations -> timeline cards pipeline with SQLite store, node capture commands, standup/ask, retention - plugin SDK/gateway seam: surface "tab" Control UI descriptors projected into hello-ok controlUiTabs (scope-filtered, deterministic order) - Control UI: dynamic plugin tabs with bundled Logbook view - docs, tests, labeler wiring * feat(ui): port plugin tabs and Logbook to the route-owned Control UI architecture - shared /plugin route carries the tab id in the query (?id=<tab>), matching the router's exact-path contract - openclaw-plugin-page renders bundled views (Logbook), sandboxed plugin frames (descriptor path), or the unavailable card - sidebar renders hello controlUiTabs after each group's static routes - Logbook view/controller live under ui/src/pages/plugin/ * fix(ui): namespace plugin tabs by pluginId to prevent cross-plugin tab id collisions * fix(logbook): prefer app capture nodes and rotate off failing nodes * fix(plugins): reject protocol-relative Control UI tab paths * fix(logbook): harden automatic journal * docs(changelog): remove maintainer self-credit * chore(ui): refresh locale metadata after rebase * fix(logbook): preserve analysis window boundaries * fix(logbook): align status privacy and timezone * fix(ui): stop hidden plugin tab polling
1 parent 3f38063 commit c730d8f

92 files changed

Lines changed: 6754 additions & 131 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/labeler.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
- any-glob-to-any-file:
3737
- "extensions/googlechat/**"
3838
- "docs/channels/googlechat.md"
39+
"plugin: logbook":
40+
- changed-files:
41+
- any-glob-to-any-file:
42+
- "extensions/logbook/**"
43+
- "docs/plugins/logbook.md"
3944
"plugin: google-meet":
4045
- changed-files:
4146
- any-glob-to-any-file:

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai
66

77
### Changes
88

9+
- **Logbook work journal:** add a disabled-by-default bundled plugin that turns paired-node screen snapshots into a private timeline, daily standup, and timeline-grounded Q&A in a plugin-contributed Control UI tab. (#99930)
910
- **Control UI message context:** reveal per-message token, context, and model details from the timestamp on hover or activation instead of showing a separate Context button.
1011
- **Control UI session titles:** reveal truncated recent-session names with a reduced-motion-safe hover animation.
1112
- **Control UI sidebar usage:** remove the provider usage quota row from the expanded sidebar while keeping usage details available in the chat composer and Usage page. Thanks @shakkernerd.

apps/macos/Tests/OpenClawIPCTests/MacGatewayChatTransportMappingTests.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ struct MacGatewayChatTransportMappingTests {
2222
server: [:],
2323
features: [:],
2424
snapshot: snapshot,
25+
controluitabs: nil,
2526
pluginsurfaceurls: nil,
2627
auth: [:],
2728
policy: [:])

apps/shared/OpenClawKit/Sources/OpenClawProtocol/GatewayModels.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ public struct HelloOk: Codable, Sendable {
125125
public let server: [String: AnyCodable]
126126
public let features: [String: AnyCodable]
127127
public let snapshot: Snapshot
128+
public let controluitabs: [[String: AnyCodable]]?
128129
public let pluginsurfaceurls: [String: AnyCodable]?
129130
public let auth: [String: AnyCodable]
130131
public let policy: [String: AnyCodable]
@@ -135,6 +136,7 @@ public struct HelloOk: Codable, Sendable {
135136
server: [String: AnyCodable],
136137
features: [String: AnyCodable],
137138
snapshot: Snapshot,
139+
controluitabs: [[String: AnyCodable]]?,
138140
pluginsurfaceurls: [String: AnyCodable]?,
139141
auth: [String: AnyCodable],
140142
policy: [String: AnyCodable])
@@ -144,6 +146,7 @@ public struct HelloOk: Codable, Sendable {
144146
self.server = server
145147
self.features = features
146148
self.snapshot = snapshot
149+
self.controluitabs = controluitabs
147150
self.pluginsurfaceurls = pluginsurfaceurls
148151
self.auth = auth
149152
self.policy = policy
@@ -155,6 +158,7 @@ public struct HelloOk: Codable, Sendable {
155158
case server
156159
case features
157160
case snapshot
161+
case controluitabs = "controlUiTabs"
158162
case pluginsurfaceurls = "pluginSurfaceUrls"
159163
case auth
160164
case policy

docs/docs.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1273,6 +1273,7 @@
12731273
"plugins/codex-harness",
12741274
"plugins/codex-native-plugins",
12751275
"plugins/codex-computer-use",
1276+
"plugins/logbook",
12761277
"plugins/google-meet",
12771278
"plugins/workboard",
12781279
"plugins/webhooks",

docs/docs_map.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5523,6 +5523,18 @@ Do not edit it by hand; run `pnpm docs:map:gen`.
55235523
- H2: Native Runtime
55245524
- H2: Troubleshooting
55255525

5526+
## plugins/logbook.md
5527+
5528+
- Route: /plugins/logbook
5529+
- Headings:
5530+
- H2: Default state
5531+
- H2: Requirements
5532+
- H2: How it works
5533+
- H2: Configuration
5534+
- H2: Dashboard tab
5535+
- H2: Gateway methods
5536+
- H2: Privacy notes
5537+
55265538
## plugins/manage-plugins.md
55275539

55285540
- Route: /plugins/manage-plugins

docs/plugins/logbook.md

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
---
2+
summary: "Optional automatic work journal built from periodic screen snapshots"
3+
read_when:
4+
- You want a Dayflow-style timeline of your day in the Control UI
5+
- You are enabling or configuring the bundled Logbook plugin
6+
- You want standup summaries or day recall grounded in screen activity
7+
title: "Logbook plugin"
8+
---
9+
10+
The Logbook plugin turns screen activity into an automatic work journal. It
11+
captures periodic screen snapshots from a paired node (for example the OpenClaw
12+
Mac app), summarizes them with a vision model into timestamped observations,
13+
and synthesizes those into timeline cards you can browse in the
14+
[Control UI](/web/control-ui). On top of the timeline it generates daily
15+
standup notes and answers questions about your day.
16+
17+
Everything stays local: snapshots and the timeline database live under the
18+
Gateway state directory. Only analysis batches are sent to the model you
19+
configure, so pick a local model if snapshots must never leave the machine.
20+
21+
## Default state
22+
23+
Logbook is a bundled plugin and is disabled by default. Screen capture is
24+
opt-in.
25+
26+
Enable it with:
27+
28+
```bash
29+
openclaw plugins enable logbook
30+
openclaw gateway restart
31+
```
32+
33+
Then open the dashboard and pick the Logbook tab:
34+
35+
```bash
36+
openclaw dashboard
37+
```
38+
39+
The Logbook tab is contributed through the plugin Control UI tab surface
40+
(`registerControlUiDescriptor` with `surface: "tab"`), so it appears in the
41+
sidebar only while the plugin is enabled on the connected gateway.
42+
43+
## Requirements
44+
45+
- A connected node that can capture the screen. The macOS app node advertises
46+
`screen.snapshot` by default (see [Nodes](/nodes)); headless macOS node
47+
hosts (`openclaw node host run`) get a plugin-provided `logbook.snapshot`
48+
command backed by the system `screencapture` tool when Logbook is enabled.
49+
- A vision model whose media-understanding provider supports structured
50+
extraction (the bundled Codex plugin does, for example `codex/gpt-5.5`).
51+
Logbook resolves the model in order:
52+
1. `plugins.entries.logbook.config.visionModel` (`"provider/model"` ref)
53+
2. the first image-capable Codex entry under `tools.media.image.models` or
54+
`tools.media.models` (other media providers do not currently expose the
55+
structured extraction contract Logbook requires)
56+
- Timeline card synthesis, standup notes, and "ask your day" answers use the
57+
default agent model via the plugin LLM runtime.
58+
59+
## How it works
60+
61+
1. **Capture**: every `captureIntervalSeconds` (default 30s) Logbook invokes
62+
`screen.snapshot` on the capture node and stores a scaled JPEG frame.
63+
Consecutive identical frames are marked idle and excluded from analysis.
64+
2. **Observe**: once an analysis window (default 15 minutes) elapses, the
65+
frames are sent to the vision model, which returns timestamped activity
66+
observations ("VS Code: editing store.ts, fixing a type error").
67+
3. **Synthesize**: observations plus the last 45 minutes of existing cards are
68+
revised into timeline cards (10-60 minutes each) with a title, summary,
69+
category, main app, and any brief distractions.
70+
4. **Prune**: frames older than `retentionDays` (default 14) are deleted.
71+
Cards, observations, and standups are kept.
72+
73+
Frames and the timeline database live under `<state-dir>/logbook/`.
74+
75+
## Configuration
76+
77+
```json
78+
{
79+
"plugins": {
80+
"entries": {
81+
"logbook": {
82+
"enabled": true,
83+
"config": {
84+
"captureIntervalSeconds": 30,
85+
"analysisIntervalMinutes": 15,
86+
"screenIndex": 0,
87+
"maxWidth": 1440,
88+
"nodeId": "my-mac",
89+
"visionModel": "codex/gpt-5.5",
90+
"retentionDays": 14,
91+
"captureEnabled": true
92+
}
93+
}
94+
}
95+
}
96+
}
97+
```
98+
99+
All keys are optional. Leave `nodeId` unset to use the first connected node
100+
that supports `screen.snapshot`. Set `captureEnabled: false` to keep the
101+
timeline UI available without capturing; the dashboard also has a session-only
102+
pause toggle.
103+
104+
## Dashboard tab
105+
106+
- **Timeline**: expandable cards per activity with category colors, the main
107+
app, distraction chips, and a snapshot keyframe.
108+
- **Day at a glance**: focus ratio, category breakdown, top apps.
109+
- **Daily standup**: turns yesterday plus today into a ready-to-paste update.
110+
- **Ask your day**: natural-language questions answered from the tracked
111+
timeline ("when did I review the gateway PR?").
112+
- **Analyze now**: closes the current capture window immediately instead of
113+
waiting for the analysis interval.
114+
115+
## Gateway methods
116+
117+
Logbook registers Gateway RPC methods for the dashboard. `logbook.status`,
118+
`logbook.days`, and `logbook.timeline` return derived text and are readable
119+
with `operator.read`. Everything that returns raw screenshot pixels
120+
(`logbook.frames`, `logbook.frame`), spends model tokens (`logbook.standup`,
121+
`logbook.ask`), or mutates runtime state (`logbook.capture.set`,
122+
`logbook.analyze.now`) requires `operator.write`. The Control UI tab requires
123+
`operator.write` because the bundled view exposes those actions and raw frame
124+
previews; read-only clients may call the derived-text methods directly.
125+
126+
## Privacy notes
127+
128+
- Snapshots can contain anything on screen, including secrets. Frames never
129+
leave the machine except as model input for analysis batches.
130+
- Use a structured-extraction provider that runs locally, when available and
131+
explicitly configured, for a fully on-device pipeline.
132+
- Frames, the timeline database, and temporary captures are written with
133+
owner-only file permissions.
134+
- Adding `screen.snapshot` to `gateway.nodes.denyCommands` is the
135+
screen-capture kill switch: it blocks app-node capture and Logbook's own
136+
`logbook.snapshot` command alike.
137+
- Setting `tools.media.image.enabled: false` also stops Logbook from borrowing
138+
the media image models for analysis; only an explicit `visionModel` in the
139+
plugin config is used then.

docs/plugins/sdk-overview.md

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ plugins.
202202
| `api.registerTrustedToolPolicy(...)` | Manifest-gated trusted pre-plugin tool policy that can block or rewrite tool params |
203203
| `api.registerToolMetadata(...)` | Tool catalog display metadata without changing the tool implementation |
204204
| `api.registerCommand(...)` | Scoped plugin commands; command results can set `continueAgent: true` or `suppressReply: true`; Discord native commands support `descriptionLocalizations` |
205-
| `api.session.controls.registerControlUiDescriptor(...)` | Control UI contribution descriptors for session, tool, run, or settings surfaces |
205+
| `api.session.controls.registerControlUiDescriptor(...)` | Control UI contribution descriptors for session, tool, run, settings, or tab surfaces |
206206
| `api.lifecycle.registerRuntimeLifecycle(...)` | Cleanup callbacks for plugin-owned runtime resources on reset/delete/reload paths |
207207
| `api.agent.events.registerAgentEventSubscription(...)` | Sanitized event subscriptions for workflow state and monitors |
208208
| `api.runContext.setRunContext(...)` / `getRunContext(...)` / `clearRunContext(...)` | Per-run plugin scratch state cleared on terminal run lifecycle |
@@ -211,6 +211,28 @@ plugins.
211211
| `api.session.workflow.scheduleSessionTurn(...)` / `unscheduleSessionTurnsByTag(...)` | Bundled-only Cron-backed scheduled session turns plus tag-based cleanup |
212212
| `api.session.controls.registerSessionAction(...)` | Typed session actions clients can dispatch through the Gateway |
213213

214+
A `surface: "tab"` descriptor adds a sidebar tab to the Control UI. Active
215+
plugins' tab descriptors are advertised to dashboard clients in the gateway
216+
hello (`controlUiTabs`), so the tab appears only while the plugin is enabled.
217+
Bundled plugins may ship a first-class dashboard view for their tab; other
218+
plugins can set `path` to a plugin HTTP route (see
219+
`api.registerHttpRoute(...)`) that the dashboard renders in a sandboxed frame.
220+
`icon` is a dashboard icon name hint, `group` picks the sidebar section
221+
(`control` or `agent`), `order` sorts among plugin tabs, and `requiredScopes`
222+
hides the tab from connections lacking those operator scopes:
223+
224+
```typescript
225+
api.session.controls.registerControlUiDescriptor({
226+
surface: "tab",
227+
id: "logbook",
228+
label: "Logbook",
229+
description: "Your day as a timeline, built from screen snapshots.",
230+
icon: "sun",
231+
group: "control",
232+
requiredScopes: ["operator.write"],
233+
});
234+
```
235+
214236
Use the grouped namespaces for new plugin code:
215237

216238
- `api.session.state.registerSessionExtension(...)`

0 commit comments

Comments
 (0)