← Sleepwing

Sleepwing dev notes

Short weekly notes on what actually shipped — newest first. No roadmap promises, just what changed and why.

A nudge is a moment, not a state

A report on October 15: five agent sessions working, and the companion showed "take a break" — never the agent status. The precedence was intended; a reminder you can't see is not a reminder. What was missing was an ending. An unanswered nudge had no expiry: the only ways out were the three bubble buttons, or locking the screen, which hands it to a system notification. Ignore it and it held the stage for as long as you stayed logged in, while real sessions went unreported behind it.

An ignored nudge now lapses after ten minutes and yields the companion back to agent status. Ten isn't a new number — it's the window a snooze already earns. If a reminder you deliberately postponed gets a second chance in ten minutes, one you ignored has had exactly as long to be acted on. The lapsed nudge closes its record honestly as skipped instead of staying pending out of sight. An agent that needs you still outranks a live nudge; that ordering was never the bug.

The window lives in a small policy type in PerchCore, with a deterministic test pinning both edges — holds at nine minutes, lapsed at ten — plus the case that matters most: a session waiting for input still wins. Shipped in 0.6.5-beta.1, build 22, on October 16.

That release almost didn't leave the machine. The signing preflight failed with "signing identity is not available in the keychain" while the certificate was valid — a doubled-prefix PERCH_SIGN_IDENTITY inherited from the calling shell matched nothing. The message pointed diagnosis at the slowest possible cause. Preflight now prints the requested value next to the keychain's actual Developer ID identities, and fails with a different message per side.

中文速览:10 月 15 日收到反馈:五个 agent 会话都在跑,桌宠却一直显示「该休息了」,看不到任何 agent 状态。优先级本身没错——健康提醒本来就该盖过「工作中」,看不见的提醒等于没有。缺的是结束条件:未响应的提醒没有任何过期时间,除非点气泡上的三个按钮,或者锁屏交给系统通知,否则你只要不登出,它就一直占着台面。现在超过十分钟未响应即自动让位,把画面交还给 agent 状态,并把这条提醒如实记为「已跳过」,而不是留一条你看不见的待处理。十分钟不是新数字,就是「稍后提醒」原本的间隔:主动推迟的提醒十分钟后能再来一次,被无视的提醒也该是同样的时长。等你输入的会话依然压过提醒——这个顺序从来不是 bug。阈值收在 PerchCore 的一个策略类型里,测试同时钉死九分钟仍保留、十分钟已过期两个边界。随 0.6.5-beta.1(build 22)于 10 月 16 日发布。这次发版差点没出门:签名预检报「钥匙串里没有该签名身份」,但证书完全有效,真正原因是从调用 shell 继承来的 PERCH_SIGN_IDENTITY 前缀重复、匹配不到任何身份。报错把排查引向了最慢的那条路,现在预检会把请求值和钥匙串里实际的 Developer ID 身份并排打出来,两边错分别报不同的错。

When the signal stops, say so

A zero-commit week — so this note explains a shipped behavior that rarely gets explained: what Sleepwing does when an agent goes quiet. Hook delivery is best-effort by design; it never blocks the host agent, which means signals can simply stop arriving.

If a "working" session produces no lifecycle event for 30 minutes, Sleepwing stops vouching for it. The task stays visible but flips to "No recent signal · status unconfirmed": the pet's aggregate mood stops counting it as active work, health timing and sleep protection pause, and nothing pretends to be done. The next real provider event clears the flag and the same task resumes in place; only a full two hours of silence removes it entirely.

The threshold is 30 minutes, not the original ten, for a concrete reason: providers report liveness per turn and per tool launch, so one long tool run — a full test suite, a slow model call — is silent the whole time it works. Ten minutes cut those off mid-run and told you your agent had stopped while it was still going. The state machine lives in a deterministic reducer in PerchCore, with unit tests pinning both edges of the threshold and the resume path.

This week handed us the same lesson on the ops side: our own metrics cron turned out to have been down for 17 days (September 23 – October 9), discovered only at weekly review. Fail-closed logs nobody reads are just silent failures with a paper trail. A stopped signal should be loudly unconfirmed — in the product, and in the pipeline that builds it.

中文速览:本周零提交,就讲一个很少被解释的已上线行为:agent 没了动静时 Sleepwing 怎么办。钩子投递本就是尽力而为,信号可能随时断。「工作中」的会话 30 分钟没有新的生命周期事件,任务会标为「暂未收到新信号 · 状态待确认」:桌宠不再把它计入活跃工作,健康计时与睡眠保护暂停,绝不假装完成;下一个真实事件到达即原地恢复,沉默满两小时才移除。阈值从 10 分钟调到 30 分钟,是因为一次长工具调用(比如跑完整测试)全程无信号,10 分钟会把它误判成停了。本周运维侧也上了同一课:metrics 定时任务静默停摆 17 天(9/23–10/9),周报组装时才发现——没人读的 fail-closed 日志等于留了底的静默失败。

Landing on the exact terminal tab

Two beta builds went out this week — 0.6.1 (build 18) and 0.6.2 (build 19) — and both exist for one feature: clicking a waiting task now lands on the exact terminal tab hosting that agent session, not just the right app. If you run a dozen tabs across iTerm and Terminal.app, "Codex is waiting for you" used to mean tab-hunting. Now the hook records the session's controlling-TTY device name (never contents, never paths), it travels as an allowlisted local resume route, and the click selects that specific tab. Unscriptable terminals keep the activate-the-app fallback, and any known terminal — Ghostty, kitty, Alacritty, WezTerm — is now eligible for recall, not just a hardcoded three.

Build 19 exists because of a genuinely annoying macOS detail: the relay first asked ttyname() about an opened /dev/tty, and macOS answers with the alias itself — literally "/dev/tty" — which routes nowhere. The fix reads the process's own e_tdev from sysctl's kinfo_proc and resolves it with devname(), which yields the real device the tab owns.

Also shipped: a proactive warning when a previously-working hook disappears from a host's config (rescanned about once a minute), so an integration can no longer die silently, and the pet now yields full-screen Spaces until an agent actually needs you.

中文速览:本周连发两个测试版(0.6.1 build 18、0.6.2 build 19),都为了同一件事:点击等待中的任务,直接落到该会话所在的那个终端标签页,而不是只把终端调到前台。钩子只记录会话的控制终端设备名(不含任何内容或路径),经允许清单内的本地路由回跳。build 19 修的是一个 macOS 细节:对已打开的 /dev/tty 调 ttyname() 返回的是别名本身,改从 sysctl 的 kinfo_proc.e_tdev 读真实设备。另外:钩子被其他工具移除时约一分钟内会主动告警,桌宠默认让出全屏空间、只在 agent 需要你时出现。

First public release week

The notarized 0.6.0 build 17 went out on GitHub Releases this week — Sleepwing's first public beta, signed with a Developer ID and stapled, macOS 14+ on Apple Silicon. The release script fails closed: if notarization doesn't come back clean, nothing ships.

The most satisfying fix was small: the completion celebration used to sprint through its frames in 1.8 seconds and read as fast-forward next to its caption. The frames now hold roughly twice as long, so the same arc lasts about three seconds, and the celebration window, the caption, and the onboarding demo pacing all derive from one constant so they can't drift apart again. A related fix keys the celebration to the on-model landing frames instead of firing mid-air.

The site also grew four SEO pages — an honest comparison, an agent-notifications guide, a TRAE user guide, and the community pet gallery — wired into a sitemap and an IndexNow submission pipeline so search engines hear about changes the same day. One embarrassing bug closed out the week: the social share card rendered CJK text in a fallback font. Fixed.

中文速览:本周 Sleepwing 发布了首个公开测试版——经过公证的 0.6.0(build 17),仅在 GitHub Releases 分发。最顺手的一个修复是庆祝动画:原来 1.8 秒跑完像快进,现在每帧停留时间加倍、整段约 3 秒,且动画窗口、文案和演示节奏都由同一个常量派生,不会再各走各的。官网新增对比、通知指南、TRAE 指南、宠物画廊四个页面,并接入了 IndexNow;分享卡片的中文字体回退问题也修掉了。