Skip to content

Feature/push schema unification 0.8#8

Merged
Tosd0 merged 33 commits into
mainfrom
feature/push-schema-unification-0.8
May 26, 2026
Merged

Feature/push schema unification 0.8#8
Tosd0 merged 33 commits into
mainfrom
feature/push-schema-unification-0.8

Conversation

@Tosd0

@Tosd0 Tosd0 commented May 26, 2026

Copy link
Copy Markdown
Owner

close #6

Tosd0 and others added 30 commits May 19, 2026 18:18
Coordinated minor across the whole amsg ecosystem. New @rei-standard/amsg-shared
package defines the AmsgPush discriminated union keyed by `messageKind`
(`content` / `reasoning` / `tool_request` / `error`) as a literal-type
discriminator, plus the four builders and type guards. All four existing
amsg sub-packages migrate onto it.

Versions (all `*-next.0` pre-release; CI publishes to `next` dist-tag):
- @rei-standard/amsg-shared 0.1.0-next.0 (new)
- @rei-standard/amsg-instant 0.7.0 → 0.8.0-next.0
- @rei-standard/amsg-server 2.3.2 → 2.4.0-next.0
- @rei-standard/amsg-sw 2.0.1 → 2.1.0-next.0
- @rei-standard/amsg-client 2.2.3 → 2.3.0-next.0

Inter-package deps pinned exact (no caret).

Highlights:
- LLM-driven paths (instant legacy + agentic-loop hook, server prompted/auto)
  auto-emit ReasoningPush before the content burst when the LLM response
  carries non-empty `reasoning_content`. Same `sessionId` across reasoning
  + content + every agentic-loop iteration. Hook path has an
  `autoEmitReasoning: false` opt-out on `createInstantHandler`.
- Legacy `{ type: 'error', code: '...' }` envelope removed. HOOK_THREW /
  LOOP_EXCEEDED now ship as `ErrorPush` (`messageKind: 'error'`).
- SW dispatches per-kind `postMessage` events to controlled clients
  (`rei-amsg-{content,reasoning,tool-request,error,unknown}-received`).
  showNotification only fires for `content` (and legacy un-kinded payloads).
- Server messageId is now deterministic across retries for scheduled rows
  (`msg_task_{id}_{i}`); UUID fallback only when `task.id == null`.
- shared package uses `tsc --allowJs --declaration --emitDeclarationOnly`
  for type emission (tsup's `dts:true` doesn't extract JSDoc types from
  .js entries) — the .d.ts now declares real discriminated-union types
  so TS callers can narrow on `push.messageKind`.

Tests: 226/226 pass (client 6 / instant 121 / server 74 / shared 14 / sw 11).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
把 server 2.3.1 / instant 0.6.1 / client 2.2.3 引入的"不合法 avatarUrl
直接 400"放宽为"console.warn + 把字段置空 + 继续"。头像是装饰性字段,
单个错误 URL 不应该 fail 整个调度 / 推送。

- server validateScheduleMessagePayload:data:/oversized/malformed 的
  avatarUrl 在 payload 上置 null,schedule 继续创建。
- server update-message handler:不合法的 avatarUrl 从 patch 里 delete,
  存储里的旧头像保持不变;其它字段照常应用。
- instant validateInstantPayload / validateContinuePayload:同样置 null
  策略,/instant 与 /continue 不再因为装饰字段挂掉。
- client _validateAvatarUrl → _sanitizeAvatarUrl:本地不再抛
  INVALID_AVATAR_URL_LOCAL,改为 console.warn + 置空 + 照常发请求;
  updateMessage 路径 delete 字段以匹配服务端 update 语义。

PAYLOAD_TOO_LARGE_LOCAL(3KB 本地体积上限)保留不变,仍是真正的整包过大
信号。validateAvatarUrl 顶层 export 行为不变,仍是纯函数返回错误描述。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
standards 文档把 §6.2 从"严格 400"重写成"console.warn + 置空 + 继续",
§1 增量摘要与 §11 版本史一起更新;各包 README 同步调用方契约(client
README 的"本地预校验"小节改成"本地软清空",去掉
INVALID_AVATAR_URL_LOCAL 的 try/catch 示例,只留 PAYLOAD_TOO_LARGE_LOCAL)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
之前 SW createNotificationFromPayload 的标题链是
  pushNotification.title || payload.title || 'New notification'
custom hook(0.7.x 自定义 envelope)如果忘了塞 title 字段,但塞了
contactName,通知就会掉到 'New notification' 这种英文兜底上。

加一档 contactName 兜底,与 server/instant 默认 envelope 的
title: '来自 ${contactName}' 行为对齐:
  pushNotification.title
  || payload.title
  || (payload.contactName && '来自 ${payload.contactName}')
  || 'New notification'

amsg-sw 2.0.1 → 2.0.2(patch)。

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

四个 amsg next 包同步从 *-next.0 → *-next.1:
- @rei-standard/amsg-server 2.4.0-next.0 → 2.4.0-next.1
- @rei-standard/amsg-instant 0.8.0-next.0 → 0.8.0-next.1
- @rei-standard/amsg-client 2.3.0-next.0 → 2.3.0-next.1
- @rei-standard/amsg-sw 2.1.0-next.0 → 2.1.0-next.1

next.0 → next.1 只两件事:
1. avatarUrl 软清空(与 stable 2.3.3 / 0.7.1 / 2.2.4 对齐)
2. SW 标题 fallback 至 「来自 {contactName}」(与 stable 2.0.2 对齐)

三轴 push schema、ReasoningPush、shared 包都**完全不动**;
@rei-standard/amsg-shared 也保持 0.1.0-next.0 不需要 bump。
四个包对 shared 的 pinned-exact 依赖都不变。
package-lock.json 同步到新版号。

Tests: 226/226 pass (server 74 / instant 121 / sw 11 / shared 14 / client 6).

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

shared next.2:
- ReasoningPush 加四个可选索引字段(messageIndex/totalMessages, chunkIndex/totalChunks),单 chunk 单 segment 时不写到 wire,老 SW 字节级兼容
- 导出 chunkReasoningByUtf8Bytes — UTF-8 codepoint-safe 字节切分 helper

instant next.2:
- fix: splitPattern 在 hook 模式下重新生效(0.7 的「ignored 启动 warn」是设计抽风,splitPattern 跟 hook 决策完全正交)
- new: reasoningSplitPattern / errorSplitPattern — 按 messageKind 独立的句切配置,两个 kind 默认不切
- new: reasoningChunkBytes handler option(默认 2000,null 禁用)— reasoning 超阈值自动按 UTF-8 边界切,绝大多数 reasoning-heavy 部署不再需要 BlobStore
- ToolRequestPush 切片:toolCalls 绑定到含最后一段 prefix 的 chunk,前 N-1 段降级为 ContentPush(无 toolCalls)
- 两层 cascade:reasoningSplitPattern(语义切)→ reasoningChunkBytes(字节切),Layer 1 段间 1500ms / Layer 2 chunk 间 100ms
- new event: reasoning_chunked,仅在 byte chunking 实际触发时 fire 一次

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`npm ci` failed in the next.2 release workflow because the previous
commit bumped amsg-instant's amsg-shared dependency string (next.0 →
next.2) but didn't refresh the root package-lock.json. Run `npm
install` to update the lockfile's amsg-instant workspace entry to
match.

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

Two fixes targeting the same leaky-typedef root cause — untyped spread
bypasses TS excess-property check, so silently-misused fields look
correct to the IDE but no-op (or get ignored) at runtime.

amsg-shared 0.1.0-next.3:
- `NotificationDirective` typedef + `ContentPush.notification?` / `ToolRequestPush.notification?` — types the 7 fields amsg-sw `createNotificationFromPayload` actually consumes (`title` / `body` / `icon` / `badge` / `tag` / `renotify` / `requireInteraction`), closing the leaky-typedef path where callers had to spread untyped
- `buildContentPush` / `buildToolRequestPush` 加 `notification?` 入参 — passthrough(同 `metadata`),shape-validate (plain object; string fields must be string; boolean fields must be boolean); unknown keys 透传保持 SW forward-compat
- ToolRequestPush 上挂 `notification` 的理由:amsg-instant 切片时 demoted 前 N-1 段成 ContentPush,spread 整个 cleanPushObj,notification 跟着走;Reasoning / Error 不加(SW silent dispatch)
- 7 字段全 typed(不只 title/body):只 typed 2 个会让另外 5 个仍走 untyped spread,recreate 同源 footgun

amsg-instant 0.8.0-next.3:
- fix: `pushPayload.splitPattern` per-push override。hook 在自己返回的 pushPayload 上写 `splitPattern: null` 不再被静默忽略——`splitHookPushPayload` 用 `pushPattern !== undefined` 检测在场(`undefined` 跟字段缺省等价,回退请求级;`null` / `[]` 显式关切)。字段名固定 `splitPattern`,不分 kind——push 的 messageKind 决定切谁的文本
- 校验沿用 validation.js 新 export 的 `validateSplitPattern`,形状错 / 正则不可编译 → 抛 `HookError` 信息形如 `pushPayload.splitPattern invalid: <原因>`(明确点位,避免跟请求级混;去重 validateSplitPattern 自带的 splitPattern 前缀)
- Strip 是 clone-based:`const {splitPattern, ...rest} = pushObj` 生成新对象,原 pushObj 不动;hook 复用模板对象不会被库吃掉字段
- splitHookPushPayload 每个 push 跑一次,N-段切片 / ToolRequestPush prefix 降级都从已剥离 cleanPushObj spread,无二次切

测试:
- shared 33/33(new: 8 个 notification arg 用例覆盖 ContentPush + ToolRequestPush 透传、undefined 不写 key、非 object 拒绝、字段类型校验、未知字段透传)
- instant 175/175(new: 12 个 per-push override 用例覆盖 null/[] override、override > outer、reasoning/error 上 override 开切、undefined fallback、malformed 抛 HookError、ToolRequestPush demote+strip、no-match strip、clone-based 不 mutate、5 段非递归切)

Coordinated:
- shared 0.1.0-next.2 → 0.1.0-next.3
- instant 0.8.0-next.2 → 0.8.0-next.3(amsg-shared dep pin 同步)
- package-lock.json 已 sync — 避免 next.2 那次的 `npm ci` 失败重演
- amsg-server / amsg-sw / amsg-client 不动

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Request body fields `splitPattern` / `reasoningSplitPattern` /
`errorSplitPattern` are now rejected with INVALID_PAYLOAD_FORMAT and a
migration hint pointing at `decision.pushPayloads`. Removes
`validatePerKindSplitPatterns` from validation and stops re-exporting
`splitMessageIntoSentences` (legacy path still uses it internally;
hook authors don't get it back).

`validateSplitPattern` itself stays for now — `splitHookPushPayload`
in message-processor.js still calls it on the per-push override path.
That call site is rewired by Task 2/3 of this migration, at which point
the helper goes too.

handler.test.mjs picks up `splitMessageIntoSentences` directly from
src/message-processor.js (instead of via the dropped re-export) so the
file still loads. The legacy describe blocks that exercise the now-
rejected fields stay in place and fail loudly until Task 6 deletes them.

Breaking on purpose — next.4 is pre-release; we're consolidating two
overlapping mechanisms (lib-side splitPattern auto-split + hook-side
pushPayload) into one (pushPayloads array) before 1.0.

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

assertValidDecision now requires `pushPayloads: [...]` on `finish` /
`tool-request` decisions. The singular `pushPayload` field is rejected
with a migration line. Empty `pushPayloads: []` is rejected and points
the caller at `decision: 'skip-push'`. Per-push `splitPattern` is also
rejected.

These cases all route through the existing HOOK_THREW pipeline.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Hook's finish/tool-request decisions now read `pushPayloads: PushPayload[]`
and the lib delivers exactly that array in order with 1500ms spacing.
Per-push: `messageId` is auto-filled when the hook didn't set one;
`messageIndex` / `totalMessages` are always overwritten with the
array-derived values.

Removed: sendChunkedPush (only consumer was this branch). LOOP_EXCEEDED
diagnostic now goes through sendPushWithMaybeBlob directly (it's one
push by construction).

`splitMessageIntoSentences` is no longer exported; still used internally
by runLegacyInstant. splitHookPushPayload / pickSplitConfig /
splitOnceByRegex / DEFAULT_SPLIT_REGEX / validateSplitPattern survive
one more task so reasoning Layer-1 split keeps working — Task 4 wipes
them as part of the reasoning rewrite.

test/helpers.mjs: createFetchRouter gains a `pushHandler` option for
per-call response control (used by the mid-array-throw test); existing
`onPush` and the default 201 behaviour are unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
emitReasoning is now a single transform: byte chunking via
chunkReasoningByUtf8Bytes when reasoningContent exceeds
ctx.reasoningChunkBytes, single-push passthrough otherwise. The
Layer-1 sentence split (reasoningSplitPattern) is gone — callers
wanting sentence-level reasoning chunks should disable
autoEmitReasoning and build the pushes themselves.

Removed: splitHookPushPayload, pickSplitConfig,
expandReasoningPushChunks, validateSplitPattern,
SPLIT_PATTERN_MAX_LENGTH, SPLIT_PATTERN_MAX_ITEMS, and the
validateSplitPattern import in message-processor.js. All five survived
Task 3 only because expandReasoningPushChunks still called
splitHookPushPayload for the now-deleted Layer-1 reasoning split.

splitMessageIntoSentences (and its private helpers DEFAULT_SPLIT_REGEX
+ splitOnceByRegex) kept — runLegacyInstant still applies the default
sentence regex to content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ation fallback + no-op event

Code-review follow-ups to b68e249. Three pure-doc additions in
message-processor.js:
- Comment on `>= 4` threshold floor explaining the shared-lib RangeError
  contract it's mirroring.
- JSDoc on sliceReasoningPush's `iteration` param documenting the legacy
  undefined → iter_0 fallback.
- JSDoc on emitReasoning noting that reasoning_chunked does not fire on
  single-push (no-op chunking) emissions.

No logic change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Mechanical s/pushPayload:/pushPayloads: [/ on every finish/tool-request
hook return across agentic-loop, reasoning-push, and the
agentic-loop-skeleton example. The X inside each wrapper is byte-for-byte
preserved; only the key name + array wrap change.

Two agentic-loop assertions pinned the OLD "no auto-fill" behaviour
and needed updating to match next.4's sendPushesSequentially:

- "pushes the hook-returned payload and returns finished" — deep-equal
  on the decrypted body now includes auto-injected messageId / messageIndex:1
  / totalMessages:1 (single-element array).
- "ASCII length 2600 → direct push; length 2601 → blob" — JSON overhead
  recomputed to account for the three auto-fill fields the framework adds
  before the byte-cap check.

split-pattern-hook.test.mjs intentionally untouched — Task 6 deletes
the whole file. handler.test.mjs's splitMessageIntoSentences /
splitPattern blocks are still slated for Task 6 deletion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
split-pattern-hook.test.mjs (1659 lines pinning lib-side hook split
behaviour) is removed wholesale — the lib no longer splits anything
in hook mode.

handler.test.mjs's `splitPattern (0.6.0)` describe block and
top-level `splitMessageIntoSentences` describe block are removed —
the public split helper is gone (legacy path still uses it
internally), and request-level splitPattern field rejection is
covered by the new `next.4 — split-pattern fields removed` describe
landed in Task 1.

Also drops the `splitMessageIntoSentences` symbol from the test
file's import (it was triggering a module-load failure since Task 3
un-exported it from message-processor.js).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pins the next.4 hook contract per spec §测试要求: single push,
multi-push spacing, mid-array throw, HookError rejection cases,
kind/decision decoupling, messageId precedence, index auto-fill, and
reasoning auto-emit interplay.

Most cases overlap with Task-2/Task-3 tests in agentic-loop.test.mjs,
but this file is the dedicated, auditable contract document.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drop the splitPattern / reasoningSplitPattern / errorSplitPattern
subsections + per-push override sub-section (~150 lines). Add the new
pushPayloads contract block, the messageId/messageIndex/totalMessages
auto-fill table, and three worked examples (single push, 3-chunk
content with per-chunk notification.body, tool-request mixing content
and multi-toolCalls).

Also drops the splitPattern line from the request payload typedef
and the "same splitPattern as v0.6" claim in the legacy-compat note
(legacy still uses the default sentence regex internally; the public
knob is gone).

Historical references to splitPattern in version-history sections
intentionally left intact — Task 9's migration guide handles them.

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

Two code-review follow-ups to ff4b03e:
- L311 said `messageKind / ... / messageKind 特定字段`; second
  occurrence generalised to "kind 特定字段" with concrete examples.
- 3-row auto-fill table normalised to the same "when + what" pattern
  per row.

No semantic change.

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

Pre-release breaking change documented end-to-end:
- CHANGELOG.md: BREAKING section enumerating Removed / Changed /
  Unchanged + migration cheat sheet
- docs/migration-0.8.0-next.4.md: long-form companion with motivation,
  5 worked recipes (one-shot, splitPattern→caller, per-chunk
  notification, tool-request mixed kinds, manual reasoning), FAQ, and
  stable-bits inventory
- package.json: 0.8.0-next.3 → 0.8.0-next.4

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…urce + CHANGELOG wording

Three code-review follow-ups to 1088430:
- Recipe 5's .map((piece) => ...) referenced an out-of-scope `i`;
  fixed to .map((piece, i) => ...).
- Recipe 5 now imports `randomUUID` from node:crypto explicitly (or
  globalThis.crypto.randomUUID() on Workers/browsers) so the
  messageId template isn't an unresolved reference.
- CHANGELOG "Removed" subsection's "all gone (or only kept where
  needed)" wording rewritten to honestly say which helpers stayed for
  the legacy path.

No code change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…+ dead-code cleanup

Final pre-tag cleanup from aggregate review:
- README §Migrating from v0.6 / §Public exports no longer claim
  buildInstantPushPayload + splitMessageIntoSentences are exported
  (next.0 / next.4 removed them respectively).
- migration-0.8.0-next.4.md adds a "amsg-server unaffected" note so
  callers maintaining both packages don't accidentally carry the
  request-body splitPattern across.
- runLegacyInstant drops `payload.splitPattern ?? null` — validation
  now rejects splitPattern unconditionally, so the fallback is dead
  code.
- examples/README.md drops the splitPattern row that referenced
  amsg-instant.

No code-behaviour change; tests still 128/128.

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

Reviewer feedback flagged that the next.4 README + migration-guide
examples leak SullyOS-specific concepts (sanitize/raw split, business
tag names like [[SEND_EMOJI]], LLM personas like 来自 Sully,
SullyOS-specific tool names like notion_read_diary, CJK-space-as-line-
break heuristic) — readers were getting the impression the library is
specifically for Chinese chat-character apps.

Replaced with portable shapes:
- Motivation prose now talks about "multiple heuristics in one pass"
  and "inline business tags" without naming a specific tag format.
- Example 2 / Recipe 3 uses generic <thinking> internal markup + a
  game-master persona ("Quest Master") with English text — the point
  is "banner ≠ payload" without dragging in CJK / business tags.
- Example 3 / Recipe 4 uses generic lookup_user / fetch_weather tool
  names with English narration.
- "metadata.directives" SullyOS-ism replaced with a generic
  description of per-chunk state (tracking IDs, A/B variants, locale,
  single-fire side-effect flags).

No code change, no API change, no test change.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… pin partial-failure error code

Three post-tag follow-ups to 0.8.0-next.4:

- Inline runLegacyInstant's sentence-split pipeline; delete the three
  module-internal helpers (DEFAULT_SPLIT_REGEX, splitOnceByRegex,
  splitMessageIntoSentences). Legacy v0.6-compat byte-for-byte
  preserved.

- assertValidDecision now rejects pushPayloads[i].messageId when set
  to anything other than a non-empty string (catches '', null, 0,
  objects, etc.). Hook setting messageId = '' was previously
  preserved on the wire and broke SW IDB keyPath silently.

- sendPushesSequentially wraps transport failures with
  code: 'PUSH_SEND_FAILED' / messageIndex / statusCode (matches
  runLegacyInstant's existing wrapping). HookError and
  PayloadTooLargeError keep their own codes to avoid re-classifying
  shape violations as transport failures. Case 3 of the contract
  matrix + the agentic-loop partial-failure test now both pin the
  error code.

Tests: 128 → 131, all green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lockfile drift from the version field update in 1088430 — the
lock now matches package.json:version. Pure regenerated artifact,
no dependency graph change.

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

非破坏性修复:

- assistant + 非空 tool_calls: content 允许空串 / null / 缺省 (符合 OpenAI Chat
  Completions 协议 — 只发工具调用不带 narration 是合法的). 对 tool_calls 数组
  做轻量形状校验 ({ id, type:'function', function:{ name, arguments } }).
- tool 消息: content 允许空串 (工具返空结果合法); tool_call_id 强校为必填
  字符串 — 这是 OpenAI 协议硬约束, 库之前漏校.
- 其他 role (system / user / 无 tool_calls 的 assistant): 维持原校验.

ChatMessage typedef 同步: role 收窄为字面量联合; content 加入 null;
tool_calls 改为结构化签名; tool_call_id 文档化必填. dist *.d.ts/*.d.cts 由
tsup 从 JSDoc 自动生成.

补 7 条 test (3 accept / 4 reject), 全量 138/138 通过.

Co-Authored-By: Claude Opus 4.7 (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 coordinated minor upgrade (v2.4 / v0.8) across the @rei-standard/amsg ecosystem (shared, server, instant, client, sw) to unify the push wire shape under the @rei-standard/amsg-shared AmsgPush discriminated union (using messageKind as the discriminator) and auto-emit ReasoningPush before content bursts. In amsg-instant, the legacy splitPattern request-body fields are removed, shifting splitting responsibility to the hook/caller side via the new pushPayloads array. Additionally, a generic _multipart transport is introduced to handle oversized payloads transparently, and waitUntil lifecycle support is added to protect background processing. The reviewer feedback identifies a critical race condition in the Service Worker's acceptMultipartChunk due to non-atomic IndexedDB operations under concurrent chunk delivery, recommending a promise chain lock. Other suggestions include making the onBusinessPayload promise check more robust by supporting any thenable, and shallow-copying user-returned push objects in sendPushesSequentially to avoid direct mutation.

Comment thread packages/rei-standard-amsg/sw/src/index.js Outdated
Comment thread packages/rei-standard-amsg/sw/src/index.js
Comment thread packages/rei-standard-amsg/instant/src/message-processor.js Outdated
@Tosd0 Tosd0 merged commit 2e52a59 into main May 26, 2026
1 check 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.

[ENHANCEMENT] 分句传配置

1 participant