@optima-chat/agentic-sdk 0.26.2 → 0.27.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/delivery-outbox.d.ts +84 -0
- package/dist/client/delivery-outbox.d.ts.map +1 -0
- package/dist/client/delivery-outbox.js +377 -0
- package/dist/client/delivery-outbox.js.map +1 -0
- package/dist/client/gateway-client.d.ts +33 -0
- package/dist/client/gateway-client.d.ts.map +1 -1
- package/dist/client/gateway-client.js +124 -0
- package/dist/client/gateway-client.js.map +1 -1
- package/dist/hooks/use-current-chat.d.ts +2 -0
- package/dist/hooks/use-current-chat.d.ts.map +1 -1
- package/dist/hooks/use-current-chat.js +11 -1
- package/dist/hooks/use-current-chat.js.map +1 -1
- package/dist/provider/chat-provider.d.ts.map +1 -1
- package/dist/provider/chat-provider.js +10 -0
- package/dist/provider/chat-provider.js.map +1 -1
- package/dist/store/chat-store.d.ts +5 -0
- package/dist/store/chat-store.d.ts.map +1 -1
- package/dist/store/chat-store.js +15 -0
- package/dist/store/chat-store.js.map +1 -1
- package/dist/types.d.ts +9 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gw#1587 客户端投递保证:出站 outbox 状态机 + 串行 drain 调度器。
|
|
3
|
+
*
|
|
4
|
+
* 语义源 = spec gw-1587-client-msg-delivery §5.1/§5.2/§5.3(v6.1,五轮评审收敛)。
|
|
5
|
+
* 关键不变量:
|
|
6
|
+
* - G1:armed 连接上带生效 client_msg_id 的消息,终态只有 delivered / failed(可见可重试)。
|
|
7
|
+
* - 终态信号 = reconnect_exhausted / 显式 disconnect() / disarm 判定——**绝不用**
|
|
8
|
+
* 'disconnected' 状态回调(reconnect() 的 identity guard 吞 onclose、不发回调,§1.8)。
|
|
9
|
+
* - eventwait 60s 重探**不计 transientCount**(15min 兜底是唯一上限——v4 评审:计数会使
|
|
10
|
+
* 5×60s 恒先到顶,5-15min 在飞 turn false-failed 复活)。
|
|
11
|
+
* - deliveryState 只驱动消息级指示,**绝不写 conv 级 processing/isThinking**(§5.4)。
|
|
12
|
+
*/
|
|
13
|
+
export type DeliveryState = 'pending' | 'delivered' | 'failed';
|
|
14
|
+
export type FailedCause = 'arm_timeout' | 'disarm' | 'conn_terminal' | 'terminal' | 'timeout_exhaust' | 'transient_exhaust';
|
|
15
|
+
export interface DeliveryOutboxHooks {
|
|
16
|
+
/** 直发一帧(true=已写入 socket buffer)。 */
|
|
17
|
+
sendFrame(frameJson: string): boolean;
|
|
18
|
+
/** ack 超时时主动重连(僵尸 socket 主模式的唯一有效动作,§1.8/F4)。 */
|
|
19
|
+
reconnect(): void;
|
|
20
|
+
/** 消息级投递状态变化(→ store per-message deliveryState;绝不碰 conv 级态)。 */
|
|
21
|
+
onStateChange(clientMsgId: string, state: DeliveryState, cause?: FailedCause): void;
|
|
22
|
+
/**
|
|
23
|
+
* MAJOR-2 修:一条帧真正投出(drain dispatch)时回调——host 在此置 thinking(spec §5.1
|
|
24
|
+
* 「入队不置 thinking,drain 投出时置位」)。gap 入队后终局 failed 的消息从不 dispatch →
|
|
25
|
+
* 不置 thinking → 不 wedge composer(#619 同型)。
|
|
26
|
+
*/
|
|
27
|
+
onDispatch?(clientMsgId: string, conversationId: string): void;
|
|
28
|
+
}
|
|
29
|
+
export declare class DeliveryOutbox {
|
|
30
|
+
private readonly hooks;
|
|
31
|
+
private entries;
|
|
32
|
+
private draining;
|
|
33
|
+
private drainAgain;
|
|
34
|
+
private armed;
|
|
35
|
+
private connected;
|
|
36
|
+
private reconnectRequestedForTimeout;
|
|
37
|
+
constructor(hooks: DeliveryOutboxHooks);
|
|
38
|
+
/** connected 边沿(armed 判定尚未发生——只记状态,不 drain)。 */
|
|
39
|
+
onConnected(): void;
|
|
40
|
+
/** 连接断开(非终态——正常重连路径;in-flight ack 等待作废回 pending)。 */
|
|
41
|
+
onDisconnected(): void;
|
|
42
|
+
/**
|
|
43
|
+
* armed 判定(本连接 session_ready 处理完成 / 10s 兜底)。
|
|
44
|
+
* armed=true:全量 drain(eventwait 转 pending + failedCause∈{arm_timeout,disarm} 且未
|
|
45
|
+
* retry 的条目自动恢复——**跨连接**也生效,§5.1 恢复规则)。
|
|
46
|
+
* armed=false(disarm/兜底):存量非 delivered 全量 failed(G1 例外③)。
|
|
47
|
+
*/
|
|
48
|
+
onArmedDecision(armed: boolean, cause: 'session_ready' | 'arm_timeout'): void;
|
|
49
|
+
/** 终态信号:reconnect_exhausted / 显式 disconnect()。 */
|
|
50
|
+
onConnTerminal(): void;
|
|
51
|
+
get isArmed(): boolean;
|
|
52
|
+
/** armed 下 sendMessage 入队(先于任何 send;由 drain 统一按序投)。 */
|
|
53
|
+
enqueue(clientMsgId: string, conversationId: string, frame: Record<string, unknown>): void;
|
|
54
|
+
/**
|
|
55
|
+
* 手动重试(§5.2):同 id、双计数归零(归零后从 0 重计)、resend:true;
|
|
56
|
+
* eventwait 中重试同语义;retry 后再次进入 eventwait 视为新「首次进入」(时钟重置)。
|
|
57
|
+
* disarmed 连接的 retry 由调用方(GatewayClient)走旧语义重发并调 removeEntry。
|
|
58
|
+
*/
|
|
59
|
+
retry(clientMsgId: string): boolean;
|
|
60
|
+
/** disarmed retry:旧语义重发由调用方做,条目出 outbox(无跟踪,spec §5.1 明文)。 */
|
|
61
|
+
removeEntry(clientMsgId: string): Record<string, unknown> | null;
|
|
62
|
+
getState(clientMsgId: string): {
|
|
63
|
+
state: DeliveryState | 'eventwait';
|
|
64
|
+
failedCause?: FailedCause;
|
|
65
|
+
} | null;
|
|
66
|
+
/** PR-A「id∈本 tab outbox」判定(duplicate finish 提示过滤等)。 */
|
|
67
|
+
has(clientMsgId: string): boolean;
|
|
68
|
+
/** 只读取帧(disarmed retry 发送成功前不出队,MINOR-2)。 */
|
|
69
|
+
peekFrame(clientMsgId: string): Record<string, unknown> | null;
|
|
70
|
+
routeEvent(event: Record<string, unknown>): void;
|
|
71
|
+
private toPending;
|
|
72
|
+
private toEventwait;
|
|
73
|
+
private onTransient;
|
|
74
|
+
private toFailed;
|
|
75
|
+
private failAllNonDelivered;
|
|
76
|
+
private emit;
|
|
77
|
+
private emitState;
|
|
78
|
+
private clearTimers;
|
|
79
|
+
private kickDrain;
|
|
80
|
+
private drainLoop;
|
|
81
|
+
private dispatchOne;
|
|
82
|
+
private onAckTimeout;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=delivery-outbox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-outbox.d.ts","sourceRoot":"","sources":["../../src/client/delivery-outbox.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC;AAC/D,MAAM,MAAM,WAAW,GACnB,aAAa,GACb,QAAQ,GACR,eAAe,GACf,UAAU,GACV,iBAAiB,GACjB,mBAAmB,CAAC;AAsBxB,MAAM,WAAW,mBAAmB;IAClC,oCAAoC;IACpC,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC;IACtC,iDAAiD;IACjD,SAAS,IAAI,IAAI,CAAC;IAClB,gEAAgE;IAChE,aAAa,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,EAAE,WAAW,GAAG,IAAI,CAAC;IACpF;;;;OAIG;IACH,UAAU,CAAC,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAChE;AA0BD,qBAAa,cAAc;IAQb,OAAO,CAAC,QAAQ,CAAC,KAAK;IAPlC,OAAO,CAAC,OAAO,CAA4B;IAC3C,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,4BAA4B,CAAS;gBAEhB,KAAK,EAAE,mBAAmB;IAIvD,gDAAgD;IAChD,WAAW,IAAI,IAAI;IAUnB,qDAAqD;IACrD,cAAc,IAAI,IAAI;IAUtB;;;;;OAKG;IACH,eAAe,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,eAAe,GAAG,aAAa,GAAG,IAAI;IAmB7E,kDAAkD;IAClD,cAAc,IAAI,IAAI;IAItB,IAAI,OAAO,IAAI,OAAO,CAErB;IAID,uDAAuD;IACvD,OAAO,CAAC,WAAW,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAY1F;;;;OAIG;IACH,KAAK,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAmBnC,8DAA8D;IAC9D,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQhE,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG;QAAE,KAAK,EAAE,aAAa,GAAG,WAAW,CAAC;QAAC,WAAW,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI;IAKvG,uDAAuD;IACvD,GAAG,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO;IAIjC,6CAA6C;IAC7C,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAM9D,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAqEhD,OAAO,CAAC,SAAS;IAQjB,OAAO,CAAC,WAAW;IAmBnB,OAAO,CAAC,WAAW;IAkBnB,OAAO,CAAC,QAAQ;IAShB,OAAO,CAAC,mBAAmB;IAM3B,OAAO,CAAC,IAAI;IAIZ,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,WAAW;IAOnB,OAAO,CAAC,SAAS;YAKH,SAAS;IA4BvB,OAAO,CAAC,WAAW;IAYnB,OAAO,CAAC,YAAY;CAerB"}
|
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gw#1587 客户端投递保证:出站 outbox 状态机 + 串行 drain 调度器。
|
|
3
|
+
*
|
|
4
|
+
* 语义源 = spec gw-1587-client-msg-delivery §5.1/§5.2/§5.3(v6.1,五轮评审收敛)。
|
|
5
|
+
* 关键不变量:
|
|
6
|
+
* - G1:armed 连接上带生效 client_msg_id 的消息,终态只有 delivered / failed(可见可重试)。
|
|
7
|
+
* - 终态信号 = reconnect_exhausted / 显式 disconnect() / disarm 判定——**绝不用**
|
|
8
|
+
* 'disconnected' 状态回调(reconnect() 的 identity guard 吞 onclose、不发回调,§1.8)。
|
|
9
|
+
* - eventwait 60s 重探**不计 transientCount**(15min 兜底是唯一上限——v4 评审:计数会使
|
|
10
|
+
* 5×60s 恒先到顶,5-15min 在飞 turn false-failed 复活)。
|
|
11
|
+
* - deliveryState 只驱动消息级指示,**绝不写 conv 级 processing/isThinking**(§5.4)。
|
|
12
|
+
*/
|
|
13
|
+
const ACK_TIMEOUT_MS = 10_000;
|
|
14
|
+
const MAX_SENDS = 3;
|
|
15
|
+
const MAX_TRANSIENT = 5;
|
|
16
|
+
const BACKOFF_BASE_MS = 5_000;
|
|
17
|
+
const BACKOFF_CAP_MS = 60_000;
|
|
18
|
+
const EVENTWAIT_PROBE_MS = 60_000;
|
|
19
|
+
const EVENTWAIT_CEILING_MS = 15 * 60_000;
|
|
20
|
+
const MIN_RESEND_GAP_MS = 1_000;
|
|
21
|
+
/** billing_error 分类(spec §3.3):rate_limited → transient;其余一切值(含未知)→ terminal。 */
|
|
22
|
+
function billingIsTransient(reason) {
|
|
23
|
+
return reason === 'rate_limited';
|
|
24
|
+
}
|
|
25
|
+
/** 终结腿 error code 分类(spec §3.3 表)。 */
|
|
26
|
+
const TERMINAL_ERROR_CODES = new Set(['GUARDRAIL_BLOCKED', 'CONVERSATION_ID_REQUIRED']);
|
|
27
|
+
const TRANSIENT_ERROR_CODES = new Set(['SESSION_NOT_FOUND', 'MESSAGE_SAVE_FAILED', 'MESSAGE_QUEUE_FULL']);
|
|
28
|
+
const EVENTWAIT_ERROR_CODE = 'CONCURRENT_CONVERSATION_BLOCKED';
|
|
29
|
+
/** eventwait 等待键有效判据(v6 钉死):非空 string 且 ≠'unknown' 哨兵。 */
|
|
30
|
+
function validWaitKey(v) {
|
|
31
|
+
return typeof v === 'string' && v.length > 0 && v !== 'unknown';
|
|
32
|
+
}
|
|
33
|
+
export class DeliveryOutbox {
|
|
34
|
+
hooks;
|
|
35
|
+
entries = new Map();
|
|
36
|
+
draining = false;
|
|
37
|
+
drainAgain = false;
|
|
38
|
+
armed = false;
|
|
39
|
+
connected = false;
|
|
40
|
+
reconnectRequestedForTimeout = false; // 并发超时去抖:一轮至多一次 reconnect
|
|
41
|
+
constructor(hooks) {
|
|
42
|
+
this.hooks = hooks;
|
|
43
|
+
}
|
|
44
|
+
// ── 连接生命周期(由 GatewayClient 驱动) ──────────────────────
|
|
45
|
+
/** connected 边沿(armed 判定尚未发生——只记状态,不 drain)。 */
|
|
46
|
+
onConnected() {
|
|
47
|
+
this.connected = true;
|
|
48
|
+
// MAJOR-1 修:armed 必随连接重置为 false——判定在本连接 session_ready/兜底后重新下发。
|
|
49
|
+
// 否则重连 gap 内保留上一连接的 armed=true,enqueue/backoff-到期/迟到 receipt 的
|
|
50
|
+
// kickDrain 会在 session_ready 之前直发(spec §5.1「判定前窗口只入队不发」的 reconnect
|
|
51
|
+
// 路径回归——三方同判 BLOCKING 骨架不变量)。
|
|
52
|
+
this.armed = false;
|
|
53
|
+
this.reconnectRequestedForTimeout = false;
|
|
54
|
+
}
|
|
55
|
+
/** 连接断开(非终态——正常重连路径;in-flight ack 等待作废回 pending)。 */
|
|
56
|
+
onDisconnected() {
|
|
57
|
+
this.connected = false;
|
|
58
|
+
for (const e of this.entries.values()) {
|
|
59
|
+
if (e.awaitingAck) {
|
|
60
|
+
this.clearTimers(e);
|
|
61
|
+
e.awaitingAck = false; // 回 pending,重连 armed 后由 drain 重投
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* armed 判定(本连接 session_ready 处理完成 / 10s 兜底)。
|
|
67
|
+
* armed=true:全量 drain(eventwait 转 pending + failedCause∈{arm_timeout,disarm} 且未
|
|
68
|
+
* retry 的条目自动恢复——**跨连接**也生效,§5.1 恢复规则)。
|
|
69
|
+
* armed=false(disarm/兜底):存量非 delivered 全量 failed(G1 例外③)。
|
|
70
|
+
*/
|
|
71
|
+
onArmedDecision(armed, cause) {
|
|
72
|
+
this.armed = armed;
|
|
73
|
+
if (armed) {
|
|
74
|
+
for (const e of this.entries.values()) {
|
|
75
|
+
if (e.state === 'eventwait')
|
|
76
|
+
this.toPending(e);
|
|
77
|
+
else if (e.state === 'failed' && !e.userRetried
|
|
78
|
+
&& (e.failedCause === 'arm_timeout' || e.failedCause === 'disarm')) {
|
|
79
|
+
e.state = 'pending';
|
|
80
|
+
e.failedCause = undefined;
|
|
81
|
+
this.emit(e, 'pending');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
this.kickDrain();
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const failCause = cause === 'arm_timeout' ? 'arm_timeout' : 'disarm';
|
|
88
|
+
this.failAllNonDelivered(failCause);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/** 终态信号:reconnect_exhausted / 显式 disconnect()。 */
|
|
92
|
+
onConnTerminal() {
|
|
93
|
+
this.failAllNonDelivered('conn_terminal');
|
|
94
|
+
}
|
|
95
|
+
get isArmed() {
|
|
96
|
+
return this.armed;
|
|
97
|
+
}
|
|
98
|
+
// ── 入队与重试 ────────────────────────────────────────────────
|
|
99
|
+
/** armed 下 sendMessage 入队(先于任何 send;由 drain 统一按序投)。 */
|
|
100
|
+
enqueue(clientMsgId, conversationId, frame) {
|
|
101
|
+
if (this.entries.has(clientMsgId))
|
|
102
|
+
return; // 幂等
|
|
103
|
+
this.entries.set(clientMsgId, {
|
|
104
|
+
clientMsgId, conversationId, frame,
|
|
105
|
+
state: 'pending', sendCount: 0, timeoutCount: 0, transientCount: 0,
|
|
106
|
+
lastSendAt: 0, enqueuedAt: Date.now(),
|
|
107
|
+
awaitingAck: false, userRetried: false,
|
|
108
|
+
});
|
|
109
|
+
this.emitState(clientMsgId, 'pending');
|
|
110
|
+
if (this.armed && this.connected)
|
|
111
|
+
this.kickDrain();
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 手动重试(§5.2):同 id、双计数归零(归零后从 0 重计)、resend:true;
|
|
115
|
+
* eventwait 中重试同语义;retry 后再次进入 eventwait 视为新「首次进入」(时钟重置)。
|
|
116
|
+
* disarmed 连接的 retry 由调用方(GatewayClient)走旧语义重发并调 removeEntry。
|
|
117
|
+
*/
|
|
118
|
+
retry(clientMsgId) {
|
|
119
|
+
const e = this.entries.get(clientMsgId);
|
|
120
|
+
if (!e || e.state === 'delivered')
|
|
121
|
+
return false;
|
|
122
|
+
this.clearTimers(e);
|
|
123
|
+
e.userRetried = true;
|
|
124
|
+
e.sendCount = 0;
|
|
125
|
+
e.timeoutCount = 0;
|
|
126
|
+
e.transientCount = 0;
|
|
127
|
+
e.eventwaitKey = undefined;
|
|
128
|
+
e.eventwaitSince = undefined;
|
|
129
|
+
e.failedCause = undefined;
|
|
130
|
+
e.awaitingAck = false;
|
|
131
|
+
e.state = 'pending';
|
|
132
|
+
this.emit(e, 'pending');
|
|
133
|
+
if (this.connected && this.armed)
|
|
134
|
+
this.kickDrain();
|
|
135
|
+
else
|
|
136
|
+
this.hooks.reconnect(); // 非 connected:主动重连(§5.2 retryMessage 分支)
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
/** disarmed retry:旧语义重发由调用方做,条目出 outbox(无跟踪,spec §5.1 明文)。 */
|
|
140
|
+
removeEntry(clientMsgId) {
|
|
141
|
+
const e = this.entries.get(clientMsgId);
|
|
142
|
+
if (!e)
|
|
143
|
+
return null;
|
|
144
|
+
this.clearTimers(e);
|
|
145
|
+
this.entries.delete(clientMsgId);
|
|
146
|
+
return e.frame;
|
|
147
|
+
}
|
|
148
|
+
getState(clientMsgId) {
|
|
149
|
+
const e = this.entries.get(clientMsgId);
|
|
150
|
+
return e ? { state: e.state, failedCause: e.failedCause } : null;
|
|
151
|
+
}
|
|
152
|
+
/** PR-A「id∈本 tab outbox」判定(duplicate finish 提示过滤等)。 */
|
|
153
|
+
has(clientMsgId) {
|
|
154
|
+
return this.entries.has(clientMsgId);
|
|
155
|
+
}
|
|
156
|
+
/** 只读取帧(disarmed retry 发送成功前不出队,MINOR-2)。 */
|
|
157
|
+
peekFrame(clientMsgId) {
|
|
158
|
+
return this.entries.get(clientMsgId)?.frame ?? null;
|
|
159
|
+
}
|
|
160
|
+
// ── 事件路由(GatewayClient.handleMessage 每帧调用) ──────────────
|
|
161
|
+
routeEvent(event) {
|
|
162
|
+
const type = event['type'];
|
|
163
|
+
const cmid = event['client_msg_id'];
|
|
164
|
+
// 1) receipt:pending/eventwait → delivered;failed → delivered(迟到回执,§4.7 缓解②)
|
|
165
|
+
if (type === 'message_receipt' && typeof cmid === 'string') {
|
|
166
|
+
const e = this.entries.get(cmid);
|
|
167
|
+
if (!e)
|
|
168
|
+
return; // 旁观 tab / 未知——静默忽略
|
|
169
|
+
this.clearTimers(e);
|
|
170
|
+
e.awaitingAck = false;
|
|
171
|
+
e.state = 'delivered';
|
|
172
|
+
this.emit(e, 'delivered');
|
|
173
|
+
this.kickDrain(); // 串行 drain:本条到终态,投下一条
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
// 2) 带本条目 id 的 error/billing_error/finish
|
|
177
|
+
if ((type === 'error' || type === 'billing_error' || type === 'finish') && typeof cmid === 'string') {
|
|
178
|
+
const e = this.entries.get(cmid);
|
|
179
|
+
if (!e)
|
|
180
|
+
return; // 旁观 tab:不动 outbox
|
|
181
|
+
if (e.state === 'delivered')
|
|
182
|
+
return; // post-receipt 腿(队列溢出等):忽略,保持 delivered
|
|
183
|
+
if (e.state === 'failed')
|
|
184
|
+
return; // 已终态:只有迟到 receipt 能翻转
|
|
185
|
+
if (type === 'finish')
|
|
186
|
+
return; // 拒绝回声的配对 finish:不 wake 不 terminal(§5.2 排除规则)
|
|
187
|
+
if (type === 'billing_error') {
|
|
188
|
+
const reason = event['billing']?.['reason'];
|
|
189
|
+
if (billingIsTransient(reason))
|
|
190
|
+
this.onTransient(e);
|
|
191
|
+
else
|
|
192
|
+
this.toFailed(e, 'terminal');
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
// type === 'error'
|
|
196
|
+
const code = event['error']?.['code'];
|
|
197
|
+
if (code === EVENTWAIT_ERROR_CODE) {
|
|
198
|
+
const waitKey = event['error']['activeConversationId'];
|
|
199
|
+
if (validWaitKey(waitKey))
|
|
200
|
+
this.toEventwait(e, waitKey);
|
|
201
|
+
else
|
|
202
|
+
this.onTransient(e); // 等待键无效(含 'unknown' 哨兵)→ 退化纯 backoff
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (typeof code === 'string' && TERMINAL_ERROR_CODES.has(code)) {
|
|
206
|
+
this.toFailed(e, 'terminal');
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
if (typeof code === 'string' && TRANSIENT_ERROR_CODES.has(code)) {
|
|
210
|
+
this.onTransient(e);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
// 未知 code:保守按 transient(retryable 语义缺省;超时漏斗兜底)
|
|
214
|
+
this.onTransient(e);
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
// 3) eventwait wake:等待键 conv 的 finish/error 到达(不带本条目 id 的事件)
|
|
218
|
+
if ((type === 'finish' || type === 'error') && typeof event['conversation_id'] === 'string') {
|
|
219
|
+
const wakeConv = event['conversation_id'];
|
|
220
|
+
let woke = false;
|
|
221
|
+
for (const e of this.entries.values()) {
|
|
222
|
+
if (e.state === 'eventwait' && e.eventwaitKey === wakeConv) {
|
|
223
|
+
this.toPending(e); // wake 不计数(§5.2)
|
|
224
|
+
woke = true;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
if (woke)
|
|
228
|
+
this.kickDrain();
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
// ── 内部:状态转移 ────────────────────────────────────────────
|
|
232
|
+
toPending(e) {
|
|
233
|
+
this.clearTimers(e);
|
|
234
|
+
e.awaitingAck = false;
|
|
235
|
+
e.state = 'pending';
|
|
236
|
+
this.emit(e, 'pending');
|
|
237
|
+
if (this.connected && this.armed)
|
|
238
|
+
this.kickDrain();
|
|
239
|
+
}
|
|
240
|
+
toEventwait(e, waitKey) {
|
|
241
|
+
this.clearTimers(e);
|
|
242
|
+
e.awaitingAck = false;
|
|
243
|
+
e.state = 'eventwait';
|
|
244
|
+
e.eventwaitKey = waitKey;
|
|
245
|
+
if (e.eventwaitSince === undefined)
|
|
246
|
+
e.eventwaitSince = Date.now(); // 首次进入锚定;重探/wake 往返不重置
|
|
247
|
+
this.emit(e, 'pending'); // 对外仍呈 pending(§6 文案「另一会话处理中」由 UI 层按需细分)
|
|
248
|
+
// 60s 重探(不计 transientCount——15min 兜底是唯一上限)
|
|
249
|
+
e.waitTimer = setTimeout(() => {
|
|
250
|
+
if (e.state !== 'eventwait')
|
|
251
|
+
return;
|
|
252
|
+
if (Date.now() - (e.eventwaitSince ?? 0) >= EVENTWAIT_CEILING_MS) {
|
|
253
|
+
this.toFailed(e, 'transient_exhaust');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
this.toPending(e);
|
|
257
|
+
}, EVENTWAIT_PROBE_MS);
|
|
258
|
+
this.kickDrain(); // 让位(§5.3):本条离开可投集合,后续条目不被阻塞
|
|
259
|
+
}
|
|
260
|
+
onTransient(e) {
|
|
261
|
+
this.clearTimers(e);
|
|
262
|
+
e.awaitingAck = false;
|
|
263
|
+
e.transientCount += 1;
|
|
264
|
+
if (e.transientCount >= MAX_TRANSIENT) {
|
|
265
|
+
this.toFailed(e, 'transient_exhaust');
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
// 指数 backoff 5s→60s,到期转 pending 入 drain(不直发)
|
|
269
|
+
const delay = Math.min(BACKOFF_BASE_MS * 2 ** (e.transientCount - 1), BACKOFF_CAP_MS);
|
|
270
|
+
e.state = 'eventwait'; // 复用等待态承载 backoff(无 waitKey——不响应 wake,只等定时器)
|
|
271
|
+
e.eventwaitKey = undefined;
|
|
272
|
+
e.waitTimer = setTimeout(() => {
|
|
273
|
+
if (e.state === 'eventwait')
|
|
274
|
+
this.toPending(e);
|
|
275
|
+
}, delay);
|
|
276
|
+
this.kickDrain(); // 让位:backoff 期间后续条目继续投
|
|
277
|
+
}
|
|
278
|
+
toFailed(e, cause) {
|
|
279
|
+
this.clearTimers(e);
|
|
280
|
+
e.awaitingAck = false;
|
|
281
|
+
e.state = 'failed';
|
|
282
|
+
e.failedCause = cause;
|
|
283
|
+
this.emit(e, 'failed');
|
|
284
|
+
this.kickDrain(); // 串行 drain:本条终态,投下一条
|
|
285
|
+
}
|
|
286
|
+
failAllNonDelivered(cause) {
|
|
287
|
+
for (const e of this.entries.values()) {
|
|
288
|
+
if (e.state !== 'delivered' && e.state !== 'failed')
|
|
289
|
+
this.toFailed(e, cause);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
emit(e, state) {
|
|
293
|
+
this.hooks.onStateChange(e.clientMsgId, state, e.failedCause);
|
|
294
|
+
}
|
|
295
|
+
emitState(clientMsgId, state) {
|
|
296
|
+
const e = this.entries.get(clientMsgId);
|
|
297
|
+
if (e)
|
|
298
|
+
this.emit(e, state);
|
|
299
|
+
}
|
|
300
|
+
clearTimers(e) {
|
|
301
|
+
if (e.ackTimer) {
|
|
302
|
+
clearTimeout(e.ackTimer);
|
|
303
|
+
e.ackTimer = undefined;
|
|
304
|
+
}
|
|
305
|
+
if (e.waitTimer) {
|
|
306
|
+
clearTimeout(e.waitTimer);
|
|
307
|
+
e.waitTimer = undefined;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
// ── 串行 drain(单飞;可投条目间 FIFO;eventwait/backoff 让位原序回位) ──
|
|
311
|
+
kickDrain() {
|
|
312
|
+
if (this.draining) {
|
|
313
|
+
this.drainAgain = true;
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
void this.drainLoop();
|
|
317
|
+
}
|
|
318
|
+
async drainLoop() {
|
|
319
|
+
this.draining = true;
|
|
320
|
+
try {
|
|
321
|
+
do {
|
|
322
|
+
this.drainAgain = false;
|
|
323
|
+
while (this.connected && this.armed) {
|
|
324
|
+
// 严格串行(§5.3):任一条目在飞(awaitingAck)→ 等它终局;其终局处理必 kickDrain 续投
|
|
325
|
+
if ([...this.entries.values()].some((e) => e.awaitingAck))
|
|
326
|
+
break;
|
|
327
|
+
const next = [...this.entries.values()]
|
|
328
|
+
.filter((e) => e.state === 'pending' && !e.awaitingAck)
|
|
329
|
+
.sort((a, b) => a.enqueuedAt - b.enqueuedAt)[0];
|
|
330
|
+
if (!next)
|
|
331
|
+
break;
|
|
332
|
+
const gap = Date.now() - next.lastSendAt;
|
|
333
|
+
if (gap < MIN_RESEND_GAP_MS) {
|
|
334
|
+
await new Promise((r) => setTimeout(r, MIN_RESEND_GAP_MS - gap));
|
|
335
|
+
continue; // 期间状态可能已变,重取
|
|
336
|
+
}
|
|
337
|
+
this.dispatchOne(next);
|
|
338
|
+
// 串行:等本条离开 awaitingAck(receipt/terminal/超时清它并 kickDrain 续投)
|
|
339
|
+
break;
|
|
340
|
+
}
|
|
341
|
+
} while (this.drainAgain);
|
|
342
|
+
}
|
|
343
|
+
finally {
|
|
344
|
+
this.draining = false;
|
|
345
|
+
if (this.drainAgain)
|
|
346
|
+
this.kickDrain();
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
dispatchOne(e) {
|
|
350
|
+
e.sendCount += 1;
|
|
351
|
+
e.lastSendAt = Date.now();
|
|
352
|
+
e.awaitingAck = true;
|
|
353
|
+
const frame = e.sendCount > 1 || e.userRetried
|
|
354
|
+
? { ...e.frame, resend: true }
|
|
355
|
+
: e.frame;
|
|
356
|
+
this.hooks.sendFrame(JSON.stringify(frame)); // 返 false 也留 awaitingAck——超时漏斗兜底
|
|
357
|
+
this.hooks.onDispatch?.(e.clientMsgId, e.conversationId); // 投出时置 thinking(§5.1)
|
|
358
|
+
e.ackTimer = setTimeout(() => this.onAckTimeout(e), ACK_TIMEOUT_MS);
|
|
359
|
+
}
|
|
360
|
+
onAckTimeout(e) {
|
|
361
|
+
if (e.state !== 'pending' || !e.awaitingAck)
|
|
362
|
+
return;
|
|
363
|
+
e.awaitingAck = false;
|
|
364
|
+
e.timeoutCount += 1; // 独立超时轴(不含 transient 重投的 send——双轴混淆是 v4 评审教训)
|
|
365
|
+
if (e.timeoutCount >= MAX_SENDS) {
|
|
366
|
+
this.toFailed(e, 'timeout_exhaust'); // 第 3 次超时 → failed(漏斗封闭,v4 M-4)
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
// k<3:不在原 socket 重投(僵尸 socket 主模式无效动作)→ 主动 reconnect(去抖),
|
|
370
|
+
// 重投发生在重连后的 armed 边沿全量 drain。
|
|
371
|
+
if (!this.reconnectRequestedForTimeout) {
|
|
372
|
+
this.reconnectRequestedForTimeout = true;
|
|
373
|
+
this.hooks.reconnect();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
//# sourceMappingURL=delivery-outbox.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"delivery-outbox.js","sourceRoot":"","sources":["../../src/client/delivery-outbox.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ServerEvent, ClientEvent } from '@optima-chat/gateway-protocol';
|
|
2
2
|
import type { ChatError, ConnectionState, TokenProviderOpts } from '../types.js';
|
|
3
|
+
import { type DeliveryState, type FailedCause } from './delivery-outbox.js';
|
|
3
4
|
export interface GatewayClientOptions {
|
|
4
5
|
gatewayUrl: string;
|
|
5
6
|
tokenProvider: (opts?: TokenProviderOpts) => Promise<string> | string;
|
|
@@ -34,6 +35,17 @@ export interface GatewayClientOptions {
|
|
|
34
35
|
maxDelay?: number;
|
|
35
36
|
};
|
|
36
37
|
heartbeatInterval?: number;
|
|
38
|
+
/**
|
|
39
|
+
* gw#1587 投递保证:消息级 deliveryState 变化(pending/delivered/failed)。
|
|
40
|
+
* 消费方(provider→chat-store)只驱动消息级指示——绝不写 conv 级 processing/isThinking
|
|
41
|
+
* (spec §5.4 分层)。cause 仅 failed 时有值。
|
|
42
|
+
*/
|
|
43
|
+
onDeliveryStateChange?: (clientMsgId: string, state: DeliveryState, cause?: FailedCause) => void;
|
|
44
|
+
/**
|
|
45
|
+
* gw#1587 §5.1:一条消息真正投出(drain dispatch)时回调——host 在此置 thinking
|
|
46
|
+
* (入队不置、投出才置;gap 入队后 failed 的消息从不 dispatch,不 wedge composer)。
|
|
47
|
+
*/
|
|
48
|
+
onMessageDispatched?: (clientMsgId: string, conversationId: string) => void;
|
|
37
49
|
}
|
|
38
50
|
/**
|
|
39
51
|
* WebSocket client for the Optima gateway. Handles connect/reconnect,
|
|
@@ -90,7 +102,28 @@ export declare class GatewayClient {
|
|
|
90
102
|
private pendingOpenResolve;
|
|
91
103
|
private readonly reconnectConfig;
|
|
92
104
|
private readonly heartbeatInterval;
|
|
105
|
+
/**
|
|
106
|
+
* gw#1587 armed 三态(per-connection):connected 边沿置 undecided;本连接 session_ready
|
|
107
|
+
* 处理完成(或 10s 兜底)后定 armed/disarmed。判定前窗口(undecided)sendMessage 只入队
|
|
108
|
+
* 不发(spec §5.1)。
|
|
109
|
+
*/
|
|
110
|
+
private deliveryArmState;
|
|
111
|
+
private armTimer;
|
|
112
|
+
private readonly delivery;
|
|
93
113
|
constructor(options: GatewayClientOptions);
|
|
114
|
+
/**
|
|
115
|
+
* gw#1587:drain 直发(绕过 send() 的入队拦截;帧已含 outbox 序列化的 resend 标记)。
|
|
116
|
+
* OTel per-frame span 有意省略(重投帧非用户新动作;接受的观测简化)。
|
|
117
|
+
*/
|
|
118
|
+
private sendRawFrame;
|
|
119
|
+
private cancelArmTimer;
|
|
120
|
+
/** gw#1587 §5.1:10s 兜底——connected 锚点起计,session_ready 到达或断连即取消。 */
|
|
121
|
+
private startArmTimer;
|
|
122
|
+
/** 手动重试:armed/undecided 走 outbox retry;disarmed 走旧语义重发(保 id+resend,出 outbox)。 */
|
|
123
|
+
retryDelivery(clientMsgId: string): boolean;
|
|
124
|
+
/** PR-A「id ∈ 本 tab outbox」判定(duplicate finish 提示过滤)。 */
|
|
125
|
+
hasDeliveryEntry(clientMsgId: string): boolean;
|
|
126
|
+
isDeliveryArmed(): boolean;
|
|
94
127
|
/**
|
|
95
128
|
* Current identity (userId/sessionId) for stamping OTel spans. Pulls the
|
|
96
129
|
* latest values from the host-provided callbacks so spans reflect live store
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gateway-client.d.ts","sourceRoot":"","sources":["../../src/client/gateway-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACZ,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EACV,SAAS,EACT,eAAe,EACf,iBAAiB,EAClB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"gateway-client.d.ts","sourceRoot":"","sources":["../../src/client/gateway-client.ts"],"names":[],"mappings":"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"}
|