Skip to content

Commit 335972d

Browse files
committed
feat(app): calendar in Connections, today's events in the Feed, .ics as a card
- Connections → Calendar: add a feed by name and private .ics link or file path (with a note on where Google, Outlook, iCloud and Fastmail hide the link), see each feed's event count / last read / error, remove it, set the working hours used for free time, re-read every feed. Also in first-run setup next to email. - Feed: a Today block with the day's events (tomorrow's once today is done), place and time, muted once over; refreshes when a feed is re-read. - An .ics the agent drafts opens as an event card — title, when, where, notes — with an Add to calendar button; artifact cards and the Library call it an Event. The viewer's small RFC 5545 reader is unit-tested. - Models that HTML-escape their arguments ("Alex & Alice") no longer put entities on the calendar: draft unescapes title, place and notes. - settings.connectors.calendar and the avatar menu hint; zh-CN strings; docs (configuration, app, cli, example config, READMEs) and CHANGELOG. - Static bundle rebuilt.
1 parent 7b14f1f commit 335972d

29 files changed

Lines changed: 1108 additions & 515 deletions

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ All notable changes to OpenMuse. The format follows [Keep a Changelog](https://k
66

77
### Added
88

9+
- **Calendar connector.** Any calendar with a private iCalendar link — Google, Outlook, iCloud, Fastmail, Nextcloud — or an `.ics` file on disk, added from *Connections → Calendar* (the screen says where each provider hides the link) or `openmuse calendar add NAME URL`; the link is kept in the vault. Feeds are read on the spot and refreshed in the background; recurring events, exceptions and moved instances are expanded per RFC 5545. The agent gets a `calendar` tool — agenda, search, free time inside working hours — and *draft*: an event it proposes is written as an `.ics` file and shown as a card with *Add to calendar*; it never writes to the calendar itself. Today's and tomorrow's events are in the system prompt; the Feed shows them under *Today*. `openmuse calendar agenda | free | feeds | add | remove`; `openmuse doctor` reports the feeds.
910
- **Memory that stays tidy.** A fact that changed is updated in place (`remember` takes `replaces=<id>`, and a line that says the same thing in other words replaces the old one on its own) instead of piling up next to the old version. A periodic tidy-up — after every eight new lines or weekly, and *Memory → Tidy up* on demand, `openmuse memory tidy [--dry-run]` from the CLI — merges lines that say the same thing, keeps the newer fact when two contradict, and drops one-off requests that were never facts about the user. The model proposes; OpenMuse checks: a merged line may add no words that were not there (checked by character for Chinese), nothing the user wrote themselves is dropped, at most a fifth of the store changes per pass. Every merge, drop and update is logged with the text it replaced — *Memory → Recent changes* and `openmuse memory changes` show them, each with an undo (`memory restore <id>`). A tidy-up that changed something is one line in the Feed and the chat.
1011
- Recall weighs rare words: a word that is in half the memories no longer decides which one is meant.
1112

‎README.md‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
| Use it from the terminal instead | [CLI](docs/cli.md) |
4040
| Point it at DeepSeek, OpenAI, Ollama, or an internal gateway | [Models](#models) · [Configuration](docs/configuration.md) |
4141
| Understand what it will and won't do without asking | [Sentinel](#sentinel) · [docs/sentinel.md](docs/sentinel.md) |
42-
| Connect email, a browser, or MCP servers | [Configuration → Connectors](docs/configuration.md#connectors) |
42+
| Connect email, a calendar, a browser, or MCP servers | [Configuration → Connectors](docs/configuration.md#connectors) |
4343
| Read the code | [Architecture](#architecture) · [docs/architecture.md](docs/architecture.md) |
4444
| Run it in Docker | [Deployment](docs/deployment.md) |
4545
| See it as an app on a (simulated) phone, notifications included | [demo/mobilegym](demo/mobilegym/README.md) |
@@ -54,7 +54,7 @@ Meta's Muse is an agent that does things rather than answering questions: it res
5454
- Ideas: suggested next actions based on your goals, memory and recent conversations.
5555
- Memory you can read and edit. Durable facts about you are saved by the agent and shown in a tab; anything can be forgotten with one tap. When a fact changes, the line is updated rather than doubled, and a periodic tidy-up merges what says the same thing and drops what was never a fact — every change listed with an undo.
5656
- A Sentinel, a credential vault, taint tracking and an append-only audit log. See [Sentinel](#sentinel).
57-
- Tools: files, shell, Python, web search and fetch, email (with one-time codes scrubbed before the model sees them), an optional Playwright browser, and any [MCP](https://modelcontextprotocol.io) server.
57+
- Tools: files, shell, Python, web search and fetch, email (with one-time codes scrubbed before the model sees them), your calendar (read from its private `.ics` link; events it proposes arrive as a card you add with a tap), an optional Playwright browser, and any [MCP](https://modelcontextprotocol.io) server.
5858
- Runs on any OpenAI-compatible model. DeepSeek, OpenAI, OpenRouter, Ollama, vLLM, or a company gateway with custom headers.
5959

6060
## Why OpenMuse
@@ -119,9 +119,9 @@ Prefer a terminal? `openmuse chat` gives you the same agent with approvals in th
119119
| Reminders | "Remind me at six to call mum" — one message at that time, in the chat you said it in. "Every weekday at 07:30, a one-line weather check for my ride" — a routine: the agent does the work then and reports. Kept whatever the proactivity level or quiet hours; listed under *Upcoming* and in the terminal. |
120120
| Library | Everything the agent made — pages, documents, trackers, images — opened in the app. Pages render in a sandbox that cannot reach your token or the API. |
121121
| Avatar | The menu: the approvals queue across all chats, the activity log, permissions you granted (revoke any one), what is upcoming (background work, check-ins, reminders), memory, connections, and settings (name, avatar, personality, Sentinel mode, background work, language). |
122-
| Connections | Plug things in from the phone: the model (provider presets, key straight into the vault, one-tap test), your mailbox (read and send, with a sign-in check), the browser, MCP servers, and the vault itself. Keys and passwords never reach the model. |
122+
| Connections | Plug things in from the phone: the model (provider presets, key straight into the vault, one-tap test), your mailbox (read and send, with a sign-in check), your calendar (the private `.ics` link of Google / Outlook / iCloud / Fastmail, kept in the vault; today's events show in the Feed), the browser, MCP servers, and the vault itself. Keys and passwords never reach the model. |
123123

124-
The first time you open it, a short setup runs instead: your name, your Muse's name and style, the model and key, optionally your mail. Skip it if `config.toml` already says it all.
124+
The first time you open it, a short setup runs instead: your name, your Muse's name and style, the model and key, optionally your mail and calendar. Skip it if `config.toml` already says it all.
125125

126126
Add it to the home screen and turn on notifications (Settings): the phone buzzes when the agent needs an approval, has a question, finished something in the background, or a reminder or check-in is due — standard Web Push through the browser, no account with anyone — and the icon shows how many cards are waiting. Needs `https://` or `localhost`; see [deployment](docs/deployment.md).
127127

@@ -191,7 +191,7 @@ flowchart LR
191191
G <--> V[(vault.enc)]
192192
T --> F[files · shell · python]
193193
T --> W[web_search · web_fetch · browser]
194-
T --> E[email]
194+
T --> E[email · calendar]
195195
T --> MCP[MCP servers]
196196
T <--> M[(memory.db)]
197197
T <--> GO[(goals.db)]
@@ -242,7 +242,8 @@ More in [docs/architecture.md](docs/architecture.md).
242242
- [x] Browser view: watch the agent browse live, take over for sign-ins
243243
- [x] Local models (Ollama) with automatic prompt-mode fallback
244244
- [ ] Triggers for goals: cron, webhooks, new mail
245-
- [ ] Calendar and contacts connectors (via MCP)
245+
- [x] Calendar connector: any private `.ics` link or file; agenda, free time, drafted events as *Add to calendar* cards
246+
- [ ] Contacts connector
246247
- [x] Memory that stays tidy: rare-word recall, updates instead of duplicates, a periodic tidy-up with undo
247248
- [ ] Memory recall with embeddings
248249
- [ ] Per-tool sandboxes for `shell` and `python_execute`

‎README_zh.md‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@
3939
| 在终端里用 | [CLI](docs/cli.md) |
4040
| 接 DeepSeek、OpenAI、Ollama 或公司网关 | [模型](#模型) · [配置](docs/configuration.md) |
4141
| 搞清楚它什么会直接做、什么会先问 | [Sentinel](#sentinel) · [docs/sentinel.md](docs/sentinel.md) |
42-
| 接邮箱、浏览器或 MCP 服务器 | [配置 → Connectors](docs/configuration.md#connectors) |
42+
| 接邮箱、日历、浏览器或 MCP 服务器 | [配置 → Connectors](docs/configuration.md#connectors) |
4343
| 读代码 | [架构](#架构) · [docs/architecture.md](docs/architecture.md) |
4444
| 用 Docker 跑 | [部署](docs/deployment.md) |
4545
| 在(模拟)手机上把它当成一个 App 用,通知也有 | [demo/mobilegym](demo/mobilegym/README.md) |
@@ -54,7 +54,7 @@ Meta 的 Muse 不是聊天机器人,而是一个动手的 Agent:查资料、
5454
- Ideas:基于你的目标、记忆和近期对话给出的下一步建议。
5555
- 你能看、能改的记忆。Agent 记下的关于你的长期事实在页签里一览无余,点一下就能让它忘掉。事实变了就更新原条目而不是再加一条;定期整理会合并意思相同的条目、去掉并非事实的内容——每处改动都列出来,可一键撤销。
5656
- Sentinel 守门人、凭据保险库、污点追踪和只追加的审计日志。见 [Sentinel](#sentinel)。
57-
- 工具:文件、shell、Python、网页搜索与抓取、邮件(一次性验证码在模型看到之前就被抹掉)、可选的 Playwright 浏览器,以及任何 [MCP](https://modelcontextprotocol.io) 服务器。
57+
- 工具:文件、shell、Python、网页搜索与抓取、邮件(一次性验证码在模型看到之前就被抹掉)、日历(通过私密 `.ics` 链接读取;它提议的日程会变成一张卡片,点一下加入日历)、可选的 Playwright 浏览器,以及任何 [MCP](https://modelcontextprotocol.io) 服务器。
5858
- 任何 OpenAI 兼容模型都能跑:DeepSeek、OpenAI、OpenRouter、Ollama、vLLM,或者带自定义请求头的公司网关。
5959

6060
## 为什么是 OpenMuse
@@ -119,9 +119,9 @@ openmuse serve --host 0.0.0.0 # 打印链接和二维码
119119
| 提醒 | “六点提醒我给妈妈打电话”——到点在你说这句话的那个聊天里发一条消息,仅此而已。“每个工作日 07:30 查一下路上的天气,一句话”——这是例程:agent 到点用工具把事做了再汇报。不受主动性等级和免打扰时段影响;在“即将进行”里和终端里都能看到。 |
120120
| Library | Agent 做出来的一切——网页、文档、追踪表、图片——直接在 App 里打开。网页在沙箱里渲染,拿不到你的 token,也调不了 API。 |
121121
| 头像 | 菜单:跨所有聊天的审批队列、活动记录、你授予的权限(可逐条撤销)、即将进行的事(后台工作、打卡、提醒)、记忆、连接,以及设置(名字、头像、性格、Sentinel 模式、后台工作、语言)。 |
122-
| 连接 | 在手机上接入一切:模型(服务商预设、密钥直接进 vault、一键测试)、邮箱(收发邮件,保存时校验登录)、浏览器、MCP 服务器,以及 vault 本身。密钥和密码永远不会经过模型。 |
122+
| 连接 | 在手机上接入一切:模型(服务商预设、密钥直接进 vault、一键测试)、邮箱(收发邮件,保存时校验登录)、日历(Google / Outlook / iCloud / Fastmail 的私密 `.ics` 链接,保存在 vault 里;当天日程显示在 Feed 里)、浏览器、MCP 服务器,以及 vault 本身。密钥和密码永远不会经过模型。 |
123123

124-
第一次打开时会先走一段简短的引导:你的名字、你的 Muse 的名字和风格、模型和密钥、可选的邮箱。`config.toml` 已经写全的话可以直接跳过。
124+
第一次打开时会先走一段简短的引导:你的名字、你的 Muse 的名字和风格、模型和密钥、可选的邮箱和日历。`config.toml` 已经写全的话可以直接跳过。
125125

126126
把它添加到主屏幕并在设置里打开通知:agent 需要审批、有问题要问、后台做完了一件事,或者到了提醒、打卡的时间,手机就会响——走浏览器标准的 Web Push,不用在任何地方注册账号——图标上还会显示有几张卡片在等你。需要 `https://` 或 `localhost`,见 [部署说明](docs/deployment.md)。
127127

@@ -242,7 +242,8 @@ flowchart LR
242242
- [x] 浏览器视图:实时观看 Agent 浏览网页,登录时接管
243243
- [x] 本地模型(Ollama),自动降级到提示词工具模式
244244
- [ ] 目标触发器:cron、webhook、新邮件
245-
- [ ] 日历与联系人连接器(通过 MCP)
245+
- [x] 日历连接器:任意私密 `.ics` 链接或文件;日程、空闲时间、起草的日程以“加入日历”卡片给出
246+
- [ ] 联系人连接器
246247
- [x] 记忆保持整洁:按稀有词召回、更新而非重复、定期整理并可撤销
247248
- [ ] 基于向量的记忆召回
248249
- [ ] `shell` 与 `python_execute` 的独立沙箱

‎config/config.example.toml‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,18 @@ address = "{{vault:EMAIL_ADDRESS}}"
128128
password = "{{vault:EMAIL_PASSWORD}}"
129129
scrub_secrets = true # strip one-time codes / reset links before the model reads mail
130130

131+
[connectors.calendar]
132+
enabled = false
133+
refresh_minutes = 30
134+
day_start = "09:00" # working hours, for "when am I free"
135+
day_end = "18:00"
136+
# Private .ics links (Google: Settings → Integrate calendar → Secret address in iCal format;
137+
# Outlook: Shared calendars → Publish; iCloud: Share Calendar → Public Calendar) or .ics files.
138+
# The link is the secret: openmuse vault set CALENDAR_WORK
139+
# [[connectors.calendar.feeds]]
140+
# name = "Work"
141+
# url = "{{vault:CALENDAR_WORK}}"
142+
131143
[browser]
132144
enabled = false # pip install "openmuse[browser]" && playwright install chromium
133145
headless = true

0 commit comments

Comments
 (0)