import type { QueuedEntry } from '../client/outbound-queue.js'; import type { OutboundRelease } from '../store/chat-store.js'; import type { ChatError, Message, SendMessageOptions } from '../types.js'; export declare function useCurrentChat(): { messages: Message[]; isStreaming: boolean; isThinking: boolean; error: ChatError | null; /** * 发一条用户消息。**返回这条消息的 `clientMsgId`**。 * * 它就是**帧上的 `client_msg_id`**,即 gw#1587 `message_receipt` 回执所对应的那个 id。 * 这正是它被返回出来的原因:Brief 决策档的契约(agentic-chat * `docs/coo-redesign/TIMELINE-CONTRACT-DELTA.md` 总纲 ②)逐字要求「决策指令 MUST 带 * `client_msg_id`,发送成功 := 收到**对应**的 `message_receipt` 后才 PATCH」——调用方 * 得先知道「对应」的是哪一个。 * (⚠️ 宿主若实现了 `ChatProvider.onBeforeSend` 并改写出站帧上的 `client_msg_id`, * 这条对应关系就断了——那是宿主自找的,SDK 不防。) * * **帧进 outbox 时**,同一个 id 还同时是:`Message.deliveryState` 的索引键 * (`pending` / `delivered` / `failed`)与 `retryMessage(messageId)` 的入参。 * ⚠️ 帧**没**进 outbox 时(下面 ① ②)两者都不成立:`deliveryState` 恒 `undefined` * (既不是 pending 也不是 failed),`retryMessage()` 恒返 `false`。 * * 🔴 **拿到 id ≠ 一定会等到 `delivered`。** 唯一可靠的不变量是: * * > **凡 outbox 把条目判 `failed`,receipt 就不会再来**——`FailedCause` 今天有 * > **6 个**取值(`arm_timeout` / `disarm` / `conn_terminal` / `terminal` / * > `timeout_exhaust` / `transient_exhaust`,见 `delivery-outbox.ts`),且这个集合会长。 * * ⇒ 调用方**必须**:① 用 `session_ready.session.supportsMessageReceipt` 能力位 gate * (契约:未武装时直接禁用决策按钮,不设启发式降级判据)。**SDK 没有暴露这个位的专用 * hook**——`useSession()` 不含它、`GatewayClient.isDeliveryArmed()` 拿不到实例;经 * `ChatProvider.onRawEvent` 观测 `session_ready` 自存一份即可(类型在 gateway-protocol * 的 `SessionInfo` 上)。② **同时监听 * `Message.deliveryState === 'failed'`**,别只等 `delivered`,否则 UI 永久 pending。 * * 收不到 receipt 的**典型例子**(不是穷举,别拿它做判断依据): * ① `content` 为空(纯附件发送)——帧上根本不附 `client_msg_id`,也不进 outbox; * ② 连接 `disarmed`(旧 gateway / repos-null 拓扑)——`GatewayClient` 把 id 剥掉直发; * ③ arm 判定期入队、随后判定落 disarmed——帧一次都没离开客户端(`arm_timeout`/`disarm`); * ④ 连接终局(重连耗尽 / 显式断开)——帧可能已出网(`conn_terminal`); * ⑤ **额度耗尽 / guardrail 拦截**(`terminal`)——注意这条下**能力位是 true、内容非空、 * 连接健康**,上面 ①–④ 一条都不成立,只有 `failed` 观测得到; * ⑥ ack 超时三次 / transient 预算耗尽(`timeout_exhaust` / `transient_exhaust`)。 * ⑦ **held**(ac#1021 生成中发送)——同一会话还有一条插话消息在飞时,本帧**尚未入 * outbox**:`deliveryState` 恒 `undefined`、`retryMessage()` 恒返 `false`,直到前一条 * 收敛、本帧被 drain 出网才开始走上面的漏斗。此档下 `sendMessage` **不**抛错(消息 * 已被接管),渲染态读 `Message.midTurn` 观测。 * * 发送失败时抛错、无返回值(`client.send()` 返 `false` ⇒ 本函数抛 * `Error('Failed to send message')`),调用方本就要 catch。 * * ac#1053(B 档 = `!(midTurnSteerSupported === true && 连接已武装)`;A 档下两个新 option 被忽略): * - `queueWhileProcessing: true` 且本会话在飞 / 队列非空 / 有在途 id ⇒ **入本地队列、不出网**、 * ⛔ 进消息列表,返回的 id 即条目 id(`queuedMessages[i].messageId`);队列满抛 * `Error('queue_full')`(上传前预判,⛔ 上传;上传期间被别处填满则上传后抛)。 * - 带 option 但不需排队 ⇒ 照常直发并记账,且写 `lastRelease`(起了新回合)。 * - `trackOutstanding: true` ⇒ 照常发送并记账(它的终态构成放队列的边沿)。 */ sendMessage: (content: string, options?: SendMessageOptions) => Promise; abort: () => void; resetConversation: () => Promise; loadHistory: () => Promise; hasMoreHistory: boolean; isLoadingHistory: boolean; /** gw#1587:重试 failed 消息(messageId = 消息 id = client_msg_id)。 */ retryMessage: (messageId: string) => boolean; /** ac#1053:当前会话的本地排队(按入队顺序;未出网、不在 `messages` 里)。只读:是 store 里的活数组,⛔ 就地修改。 */ queuedMessages: readonly QueuedEntry[]; /** ac#1053:按 id 出队(取消回填由宿主用条目的 `content`);不在队里 ⇒ false。 */ cancelQueuedMessage: (messageId: string) => boolean; /** * ac#1053:立即放出该条(不必是队头、不看边沿谓词)。空闲时放出会写 `lastRelease`; * 发不出去(未连通 / 宿主拦截)⇒ false,条目留队。 */ sendQueuedMessageNow: (messageId: string) => boolean; /** ac#1053:当前会话的在途数(Δ10:本 session 未见过带 `clientMsgId` 的 finish 时恒 0)。 */ outstandingCount: number; /** ac#1053:当前会话的队列会不会在回合结束时自己放(Δ12;标签「排队中」/「等待发送」)。 */ willAutoDrain: boolean; /** ac#1053:最近一次「起了新回合」的放出(全局单槽,`seq` 单调);宿主据此重置思考计时。 */ lastRelease: OutboundRelease | null; }; //# sourceMappingURL=use-current-chat.d.ts.map