跳转到内容

通知

代码里 notification*Inbox(收件箱);瞬时打断使用 alert* / deliverLocalReminder。产品上的 通知 / 提醒 / 本机打断 三分法、任务 due vs 多提醒、Habitat 睡到下次 时间发现、壳层 Attention 集中订阅 → 切面 notification-and-reminder.md

PG-backed in-app inbox for user and agent subjects (entity model). Cron job results, task due (target), env/health changes, and LLM tools write here; Shell UI lists and marks read via SAP. Advance task reminders are not Inbox rows — see the aspect.

不写 PG、不做跨设备认领。 各 shell 端在启动时注册 AlertBackend(web / desktop / mobile)。产品提醒统一经 portal-sdk/local-reminderdeliverLocalReminder

本机条件通道
desktop 且伴侣窗口显示(getCompanionVisibleenqueueCompanionBubble(不弹本机 OS)
否则(含 web / mobile)deliverAlert(系统通知)

多端策略为宽松:各 Portal 独立提醒;伴侣打开 ≠ 人在旁,因此压制手机/Web。同端在源路由 focused 时可用 suppressOsWhenFocused 压制 OS。伴侣气泡视为已读/ack。

Alert 分两档(同一契约,成对):

API含义
即时deliverLocalReminderdeliverAlert / bubble现在立刻提示
预登记scheduleLocalAlert / cancelScheduledAlert在未来时刻本机弹;必须可取消

硬约束:schedule ⊕ cancel 成对。只登记不能取消 = 不可用(暂停/手动中止后仍会到点骚扰)。同 tagschedule = replace(先 cancel 再登记);cancel 对不存在的 id/tag 幂等成功

用户 Inbox 新建经 notification.subscribeInboxnotification.created 推到各端,再走 deliverLocalReminder。Chat 未读会话数上升同理(ChatUnreadShellWatcher)。subscribeInbox 的 pump 与 chat 一样绑定 Habitat RPC WS 会话(断线 abort,重连后可重建);勿用进程级单例 Map,否则重连后推送会静默丢失而 notification.list 仍正常。

scheduleDurability预登记存活边界
mobileos杀进程后 OS 仍可按 at 弹出(Local Notifications)
desktopprocess应用未退出(托盘存活)即可;关主窗口仍响;quit 后不保证
webprocess(页进程)best-effort:标签页存活才准;关标签即丢

例外(Habitat 同步,非 Alert):运行中活跃态(pomodoro_active + pomodoro.active.*)跨端 last-write-wins阶段结束本机提醒仍仅本机deliverLocalReminder)。多端同步靠 put / clear 后的 pomodoro.active.changed;重连与页面可见时 active.get 兜底,不作周期轮询

阶段开始 / 继续时:伴侣显示则 scheduleLocalAlertphaseEndsAt);伴侣显示时预登记(OS 定时器无法走气泡),靠 PomodoroShellWatcher 即时路径气泡。暂停、手动取消进行中会话runPhaseAbort)、阶段完成等路径 cancelScheduledAlert。伴侣显隐切换时重新 sync 预登记。

事件Inbox(目标)本机打断SSOT
任务到期 due✓(该 World 的 subject)可经 notification.createdtask_item + inbox
任务提前提醒(可多条)✓(直达,不经 Inbox)task_item reminders
notification_send按收件 subject 需要打断时inbox
Chat 未读上升conversation
番茄钟阶段结束pomodoro_session

上表为目标态;现状 gap 见切面。番茄钟阶段结束不写 inbox;会话历史由 pomodoro_session entity 承担。

实现:src/client/portal-sdk/local-reminder.ts + portal-sdk/alert/ + 各端 backend。

即时通道(无伴侣 / 非 desktop)预登记
desktop伴侣气泡或 Tauri 桌面通知(showNativeAlertprocess;伴侣可见时跳过
webWeb Notification API页内 setTimeout(best-effort)
mobileTauri Android 本地通知(showNativeAlertschedule / cancel

(以下为 Inbox 专章,保留原行为说明。)

PG-backed in-app inbox for user and agent subjects (entity model). Cron job results, task due, env/health, and LLM tools write here; Shell UI lists and marks read via SAP. Advance reminders are not Inbox rows.

Subject entity ids are bound at Habitat boot into ResolvedWorldContext and persisted to habitat_runtime_config.worlds (see entity-model.md). Operators do not need to hand-maintain these ids on a new instance.

Optional override (advanced; rarely needed):

# habitat_runtime_config (Shell → Habitat 服务设置 → worlds),或冷启动后由 boot 自动回写
worlds:
user_subject_id: 109 # type=user entity
agent_subject_id: 110 # type=agent entity

Legacy notifications.user_subject_id / agent_subject_id are still read as fallback when worlds is unset.

user_world_id / agent_world_id are derived at Habitat boot from each subject’s default_private_world_id.

Each row stores recipient_kind (user | agent) and recipient_id (entity id string from ResolvedWorldContext).

WriterTypical recipient
Cron success (when notify_on_success)both user + agent
Cron failureboth user + agent
Task due (target)subject of the task’s World
Env/health baseline changeboth user + agent (builtin-env-health)
In-process builtin failureboth user + agent(无 cron_log 时的替代)
notification_send tooluser / agent / both; optional subject_id

梦境流水线不会创建通知(提醒已移除)。

未读的 agent 通知在推理时通过仅运行时的 assistant(name=notification_context) 轮次注入,位置在最后一条 user 消息之前。它们不会持久化到对话消息中。

注入块包含处理协议(按是否需要行动三分流——而非按 source_kind)。

对每条注入的 [id:…] 行,按内容分类(而非按写入方/来源):

类别行动标记已读
仅信息若有帮助则在回复中确认批量 notification_mark_read({ ids: [...] })
需行动,可快速完成约 3 轮工具内处理完成后对该 id 调用 notification_mark_read
需行动,耗时/不确定长任务前先询问用户批准并处理前不要标记已读

未标记已读的未读项会在下一轮 user 时重新注入。若注入块被截断,使用 notification_list(recipient=agent, read_filter=unread)

Load via toolset_load with notification.

ToolScope parameter
notification_sendOptional subject_id (overrides target); default target=both
notification_listOptional subject_id (overrides recipient); default recipient=agent
notification_mark_readNotification id only (global)

subject_id must be the configured user_subject_id or agent_subject_id from system prompt / ResolvedWorldContext.

Target (see notification-and-reminder): Habitat sleep-until-next (one next-fire timer). Due → Inbox for the entity’s World subject; advance reminders → local interrupt only. Do not use PG cron_jobs / cron_log for discovery.

Current code: builtin-task-reminders runs via in-process Bun.cron (* * * * *, no PG cron row / no empty-scan cron_log). Still transitional vs sleep-until-next; single remind_at with remind-else-due writes the user Inbox; scan walks user_world_id only.

ToolSet notification

  • notification_send
  • notification_list
  • notification_mark_readidids(批量,最多 20)

注册后包含在默认对话 toolset 中。

  • notification.list — 需要 recipient_kind + 可选 recipient_id
  • notification.markRead
  • notification.recipients — UI 标签页用的已配置主体 id
  • notification.subscribeInbox — WS;用户 Inbox 新建推送 notification.created(本机提醒)

v1 无 SAP 创建 RPC;写入由 Habitat 内部 + 工具完成。