Skip to content

release(amsg): SSE transport + instant backup push, de-deprecate in-server instant (shared 0.2.0 / client 2.5.0 / instant 0.9.1 / server 2.5.2 / sw 2.3.1)#11

Merged
Tosd0 merged 7 commits into
mainfrom
dev
Jun 14, 2026
Merged

Conversation

@Tosd0

@Tosd0 Tosd0 commented Jun 14, 2026

Copy link
Copy Markdown
Owner

概要

把 dev 上累积的这一轮 amsg 改动合入 main。核心是把 instant 消息的传输底座换成 SSE 默认传输 + 常驻 Web Push backup,配套 client / SW / shared 的能力补齐,外加几个 bug 修复与文档校准。

发版版本:amsg-shared 0.2.0 · amsg-client 2.5.0 · amsg-instant 0.9.1 · amsg-server 2.5.2 · amsg-sw 2.3.1

改了什么

传输与生命周期

  • SSE 成为默认 transport,引入 lifecycle hooks 与 SSE consumer
  • amsg-instant:常驻 SSE backup push、可配置 keepalive;SSE 分支正确接入 waitUntil 生命周期;controller.close() 延后到 backup push 全部 settle 之后再触发,避免提前关流丢推送

client

  • 新增 deliver() 投递原语
  • 新增 consumeInstantStream,payload 上限可配
  • SSE 出错改为直接 throw(替代 return)

service worker

  • 投递去重 + 页面投递桥接 + 通知处理
  • IndexedDB 连接韧性(断连/重连兜底)

shared

  • notification.silent 与完整 NotificationData 字段

server

  • in-server instant 路径(messageType: 'instant'取消弃用,恢复为一等公民:DB 路径适合长时间生成 / 零丢失,无状态场景用 amsg-instant
  • readReasoningContent<think> fallback 抽取重排到切句之前,避免私有 reasoning 被当 ContentPush 推给用户

质量

  • 锁定 "LLM never enters waitUntil" 契约测试
  • SW 连接韧性测试
  • /simplify 扫出的 4 个潜在 bug 修复

合并方式

main 落后 dev 16 个 commit、领先 0,快进式合并,无冲突。

🤖 Generated with Claude Code

Tosd0 and others added 7 commits June 3, 2026 21:08
…ER ack

Bump @rei-standard/amsg-sw 2.2.0 → 2.3.0.

- 连接被浏览器强制关闭(backing store 出错 / 存储压力 / 清数据)后自愈:
  挂 onclose 清缓存 + 事务级一次重开兜底,dedupe 与 queue/multipart 库同等覆盖,
  并发恢复时只剔除真正失效的那个连接。无需重启 SW。
- DELIVER ack 新增可选字段 businessError:onBusinessPayload 失败时透传,ok 维持
  「已收下并分发」语义(非破坏,成功时不出现该字段)。businessError 在 dedupe
  记录的读写路径上一致持久化(await 后一律读最新 + firstSeenAt 身份校验),不被
  重复包、通知补写或 TTL 换人擦掉或张冠李戴。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
# 主要变化
新增 `client.deliver(payload, opts)` 作为推荐入口,统一协调
SSE / JSON 两路 transport 与 caller 提供的「观察通道」Promise,
返回 5 值 outcome(delivered / cancelled / timeout / send-failed
/ completed-unconfirmed),让接入方一眼就能正确分辨「真送达」与
「transport 失败」。

平台无关:库不绑 Service Worker / IndexedDB / Web Push / 任何
具体后端;observation 通道由 caller 注入一个普通 Promise,谁能
拿到 receipt 都能接(SW broadcast、Electron IPC、原生桥、轮询
fallback……)。

# 为什么这样设计
amsg-instant 0.9.0+ 强制 backupPush='on' 后,SSE + Web Push
双通道并发。旧 API 的两条单一信号路径都变得不可靠:
sendInstant() 200 ≠ 真送达;consumeInstantStream() reject ≠
真发送失败。naive 接入方 try/catch reject 会持续误报失败。
deliver() 把 transport 降为辅助快通道,把 receipt 作为唯一真相,
彻底消除这条陷阱。

# 旧 API 软降级
- sendInstant / consumeInstantStream 仍可用,wire-level 字节不变
- JSDoc 改标 "Low-level",指向 deliver()
- 新增可选 opts.expectsBackupPush 触发一次性 dev warning
- 留两 minor 缓冲到 3.0.0,不立刻 @deprecated

# 多 agent self-review + Codex 独立 review
9-angle finder(line-scan / removed-behavior / cross-file /
JS pitfalls / wrapper / reuse / simplification / efficiency /
altitude)抓 15 条 + Codex 二次抓 7 条,全部修:CRLF SSE 帧分隔
(含跨 chunk seam)、TextDecoder EOF flush、本地校验错误不再被
埋成 send-failed、abort 全路径 honor(pre-flight + 异步 build
后 + post-transport grace)、AbortSignal listener 卸载、
transport-only 短路 grace、application/+json 结构化后缀识别、
Content-Type 改 media-type 解析、Promise reaction 累积消除。

发布在 `next` dist-tag 给 SullyOS 端到端验证(iOS PWA + SW
双通道 + 后台杀 fetch 等 Node 单测无法仿真的场景);55 条单测全
绿后再 graduate 到 latest 作为 2.5.0 正式版。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ettle (0.9.1-next.0)

SSE 模式下让 ReadableStream.start() 完整 await LLM 调用与所有 backup / fallback push
之后才 controller.close()。响应仍在产出期间 runtime 不会施加 wall-clock 上限,
慢 LLM + iOS Safari 后台杀 SSE socket 的组合下也能把这一轮消息送达。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…0.9.1-next.1)

新增三条 SSE 生命周期回归断言:

- 多 chunk + 中途 abort:每条 chunk 都通过 fallback push 投递,全部在 close 之前
- SSE 模式 waitUntil 注册数恒为 1(只剩 startDone deferred)
- 慢 LLM 必须先于 controller.close() resolve

任何让 LLM 调用或 push HTTP 漂出 start() 的改动都会被拦下。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- sw: catch showNotification rejection so onNotificationSettled still
  runs; otherwise the dedupe record stays notificationStatePending:true
  forever and every backup duplicate is swallowed as
  'first-delivery-pending'.

- instant: replace strict `accept === 'application/json'` with
  acceptsJsonOnly() — tolerates `application/json; charset=utf-8` and
  `application/json, */*` so callers that ask for JSON actually get JSON
  instead of being silently routed into the SSE branch.

- client: have sendInstant() set Accept: application/json. The default
  fetch Accept is `*/*` which would land on the SSE branch and break
  the subsequent res.json() with SyntaxError on the SSE bytes.

- instant + server: strip <think>/<thinking>/<thought> spans from
  messageContent before sentence-splitting when readReasoningContent's
  regex fallback matched. Previously the same private chain-of-thought
  shipped twice — once as a ReasoningPush, once as raw markup embedded
  inside the ContentPush burst.

Cleanup folded in:

- client: drop _urlBase64ToUint8Array (byte-for-byte duplicate of
  shared base64UrlToBytes) and hoist a module-level TEXT_ENCODER so
  _encrypt + _assertPayloadSize stop allocating one per call.

- client: _maybeWarnLowLevel docstring + warn message reconciled with
  the actual code path — the previous "Pass expectsBackupPush: false to
  silence" claim was a lie because the default already silences.

Tests: 403/403 across client / instant / server / shared / sw.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…server 2.5.1 / sw 2.3.1

- amsg-client 2.5.0-next.0 → 2.5.0: graduate `deliver()` primitive to
  latest dist-tag; fold in Accept-header pin on sendInstant() and three
  internal cleanups.
- amsg-instant 0.9.1-next.1 → 0.9.1: graduate stream-lifecycle work;
  add tolerant Accept parsing + <think>-tag strip from ContentPush.
- amsg-server 2.5.0 → 2.5.1: mirror the <think>-tag strip so scheduled
  pushes do not leak chain-of-thought into the user-facing burst.
- amsg-sw 2.3.0 → 2.3.1: catch showNotification rejections so the
  dedupe record cannot stall at notificationStatePending:true.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The in-server instant path (messageType: 'instant', create task -> process by UUID -> delete task) is now a first-class supported path again. Removed the @deprecated JSDoc and the steering toward amsg-instant; relabeled the 'legacy in-server instant' comments to neutral wording. JSDoc/README now describe both instant paths by their own characteristics: the DB-backed in-server path for long-running or zero-loss generations, and the stateless amsg-instant package for short messages on edge runtimes. Runtime behavior unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a platform-agnostic delivery primitive deliver() in the client SDK, which coordinates foreground transport (SSE/JSON) with an out-of-band observation channel to provide reliable delivery outcomes. It also includes several critical bug fixes and improvements across the workspace: preventing private chain-of-thought reasoning tags from leaking into user-facing content, improving IndexedDB connection resilience in the Service Worker, and surfacing business-callback failures on DELIVER acks. The reviewer identified a high-severity issue in the refactored consumeInstantStream method where the options.signal is ignored, which would break the active cancellation mechanism.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread packages/rei-standard-amsg/client/src/index.js
@Tosd0 Tosd0 merged commit 0360fb1 into main Jun 14, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant