@fitchmultz/pi-subagents 0.52.17 → 0.52.18
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/CHANGELOG.md +17 -0
- package/dist/build-info.js +1 -1
- package/dist/pi-intercom/inbound-delivery.js +2 -6
- package/dist/pi-intercom/reply-tracker.js +47 -55
- package/dist/pi-intercom/session-events.js +2 -0
- package/docs/intercom.md +6 -2
- package/package.json +1 -1
- package/src/pi-intercom/inbound-delivery.ts +2 -6
- package/src/pi-intercom/reply-tracker.ts +50 -58
- package/src/pi-intercom/session-events.ts +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## 0.52.18 - 2026-11-06
|
|
6
|
+
|
|
7
|
+
<!-- npm-release:unreleased:start -->
|
|
8
|
+
|
|
9
|
+
- Keep replied or retired asks out of reply attribution when their queued input reaches a later native turn.
|
|
10
|
+
- Preserve unanswered queued asks through abort recovery without sending a premature recipient-failure reply.
|
|
11
|
+
|
|
12
|
+
<!-- npm-release:unreleased:end -->
|
|
13
|
+
|
|
14
|
+
- Keep retired asks out of native reply attribution (#341) ([27a93f9](https://github.com/fitchmultz/pi-subagents/commit/27a93f9bc52aaa9e454ffc593017b66b01461bc4))
|
|
15
|
+
|
|
16
|
+
### What's Changed
|
|
17
|
+
|
|
18
|
+
- Keep retired asks out of native reply attribution by @fitchmultz in https://github.com/fitchmultz/pi-subagents/pull/341
|
|
19
|
+
|
|
20
|
+
**Full Changelog**: https://github.com/fitchmultz/pi-subagents/compare/v0.52.17...v0.52.18
|
|
21
|
+
|
|
5
22
|
## 0.52.17 - 2026-11-05
|
|
6
23
|
|
|
7
24
|
<!-- npm-release:unreleased:start -->
|
package/dist/build-info.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const EXTENSION_BUILD = Object.freeze({"version":"0.52.
|
|
1
|
+
export const EXTENSION_BUILD = Object.freeze({"version":"0.52.18","sha256":"83db07f9acbdd6ff5985c46e1f5f5411f68b6766697a2dfab019b3d00dbba1c1"});
|
|
@@ -131,7 +131,7 @@ export class InboundDeliveryOwner {
|
|
|
131
131
|
const trigger = index === -1 ? undefined : rest.splice(index, 1)[0];
|
|
132
132
|
const replyTarget = entries.find((entry) => entry.message.expectsReply === true) ?? trigger;
|
|
133
133
|
if (replyTarget && this.journal.get(replyTarget.message.id) === replyTarget) {
|
|
134
|
-
this.hooks.replies.queueTurnContext(
|
|
134
|
+
this.hooks.replies.queueTurnContext(replyTarget.message.id);
|
|
135
135
|
}
|
|
136
136
|
for (const entry of rest) {
|
|
137
137
|
if (!this.live(generation)) {
|
|
@@ -218,11 +218,7 @@ export class InboundDeliveryOwner {
|
|
|
218
218
|
this.journal.markNative(entry.message.id);
|
|
219
219
|
}
|
|
220
220
|
if (delivery !== "passive") {
|
|
221
|
-
this.hooks.replies.queueTurnContext(
|
|
222
|
-
from: entry.from,
|
|
223
|
-
message: entry.message,
|
|
224
|
-
receivedAt: Date.now(),
|
|
225
|
-
});
|
|
221
|
+
this.hooks.replies.queueTurnContext(entry.message.id);
|
|
226
222
|
}
|
|
227
223
|
}
|
|
228
224
|
nativeMessage(entry) {
|
|
@@ -55,9 +55,9 @@ function replyBySender(contexts, to) {
|
|
|
55
55
|
}
|
|
56
56
|
export class ReplyTracker {
|
|
57
57
|
pendingAsks = new Map();
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
58
|
+
pendingTurnIds = [];
|
|
59
|
+
currentTurnId = null;
|
|
60
|
+
activeAgentId = null;
|
|
61
61
|
askTimeoutMs;
|
|
62
62
|
constructor(askTimeoutMs = 10 * 60 * 1000) {
|
|
63
63
|
this.askTimeoutMs = askTimeoutMs;
|
|
@@ -70,61 +70,58 @@ export class ReplyTracker {
|
|
|
70
70
|
}
|
|
71
71
|
return context;
|
|
72
72
|
}
|
|
73
|
-
queueTurnContext(
|
|
74
|
-
if (
|
|
73
|
+
queueTurnContext(messageId) {
|
|
74
|
+
if (!this.pendingAsks.has(messageId) || this.hasTurnContext(messageId)) {
|
|
75
75
|
return;
|
|
76
76
|
}
|
|
77
|
-
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
this.pendingTurnContexts.push(context);
|
|
77
|
+
this.pendingTurnIds.push(messageId);
|
|
81
78
|
}
|
|
82
79
|
beginTurn(now = Date.now()) {
|
|
83
80
|
this.pruneExpired(now);
|
|
84
|
-
this.
|
|
85
|
-
if (this.
|
|
86
|
-
this.
|
|
81
|
+
this.currentTurnId = this.pendingTurnIds.shift() ?? null;
|
|
82
|
+
if (this.currentTurnId !== null) {
|
|
83
|
+
this.activeAgentId = this.currentTurnId;
|
|
87
84
|
}
|
|
88
85
|
}
|
|
89
86
|
currentTurn() {
|
|
90
|
-
|
|
87
|
+
const id = this.currentTurnId ?? this.activeAgentId;
|
|
88
|
+
return id === null ? null : (this.pendingAsks.get(id) ?? null);
|
|
91
89
|
}
|
|
92
90
|
endTurn() {
|
|
93
|
-
this.
|
|
91
|
+
this.currentTurnId = null;
|
|
94
92
|
}
|
|
95
93
|
endAgent() {
|
|
96
|
-
this.
|
|
97
|
-
this.
|
|
94
|
+
this.currentTurnId = null;
|
|
95
|
+
this.activeAgentId = null;
|
|
98
96
|
}
|
|
99
97
|
reset() {
|
|
100
98
|
this.pendingAsks.clear();
|
|
101
|
-
this.
|
|
102
|
-
this.
|
|
103
|
-
this.activeAgentContext = null;
|
|
99
|
+
this.pendingTurnIds.length = 0;
|
|
100
|
+
this.endAgent();
|
|
104
101
|
}
|
|
105
102
|
expireSender(sessionId) {
|
|
106
|
-
const
|
|
103
|
+
const expiring = new Set([...this.pendingAsks.values()]
|
|
104
|
+
.filter((context) => context.from.id === sessionId)
|
|
105
|
+
.map((context) => context.message.id));
|
|
107
106
|
let expired = 0;
|
|
108
|
-
for (const
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
expired += 1;
|
|
112
|
-
}
|
|
107
|
+
for (const messageId of expiring) {
|
|
108
|
+
this.pendingAsks.delete(messageId);
|
|
109
|
+
expired += 1;
|
|
113
110
|
}
|
|
114
|
-
const beforeQueued = this.
|
|
115
|
-
for (let index = this.
|
|
116
|
-
const
|
|
117
|
-
if (
|
|
118
|
-
this.
|
|
111
|
+
const beforeQueued = this.pendingTurnIds.length;
|
|
112
|
+
for (let index = this.pendingTurnIds.length - 1; index >= 0; index -= 1) {
|
|
113
|
+
const id = this.pendingTurnIds[index];
|
|
114
|
+
if (id !== undefined && expiring.has(id)) {
|
|
115
|
+
this.pendingTurnIds.splice(index, 1);
|
|
119
116
|
}
|
|
120
117
|
}
|
|
121
|
-
expired += beforeQueued - this.
|
|
122
|
-
if (this.
|
|
123
|
-
this.
|
|
118
|
+
expired += beforeQueued - this.pendingTurnIds.length;
|
|
119
|
+
if (this.currentTurnId !== null && expiring.has(this.currentTurnId)) {
|
|
120
|
+
this.currentTurnId = null;
|
|
124
121
|
expired += 1;
|
|
125
122
|
}
|
|
126
|
-
if (this.
|
|
127
|
-
this.
|
|
123
|
+
if (this.activeAgentId !== null && expiring.has(this.activeAgentId)) {
|
|
124
|
+
this.activeAgentId = null;
|
|
128
125
|
expired += 1;
|
|
129
126
|
}
|
|
130
127
|
return expired;
|
|
@@ -132,26 +129,21 @@ export class ReplyTracker {
|
|
|
132
129
|
resolveReplyTarget(options, now = Date.now()) {
|
|
133
130
|
this.pruneExpired(now);
|
|
134
131
|
const pending = Array.from(this.pendingAsks.values());
|
|
135
|
-
const contexts = this.currentTurnContext
|
|
136
|
-
? [
|
|
137
|
-
this.currentTurnContext,
|
|
138
|
-
...pending.filter((context) => context.message.id !== this.currentTurnContext?.message.id),
|
|
139
|
-
]
|
|
140
|
-
: pending;
|
|
141
132
|
const replyTo = options.replyTo ?? "";
|
|
142
133
|
const to = options.to ?? "";
|
|
143
134
|
if (replyTo !== "") {
|
|
144
|
-
return explicitReply(
|
|
135
|
+
return explicitReply(pending, replyTo, to);
|
|
145
136
|
}
|
|
146
137
|
if (to !== "") {
|
|
147
|
-
return replyBySender(
|
|
138
|
+
return replyBySender(pending, to);
|
|
148
139
|
}
|
|
149
140
|
return this.implicitReplyTarget(pending);
|
|
150
141
|
}
|
|
151
142
|
/** Without an explicit target, the turn context wins, then a sole pending ask. */
|
|
152
143
|
implicitReplyTarget(pending) {
|
|
153
|
-
|
|
154
|
-
|
|
144
|
+
const current = this.currentTurnId === null ? undefined : this.pendingAsks.get(this.currentTurnId);
|
|
145
|
+
if (current) {
|
|
146
|
+
return current;
|
|
155
147
|
}
|
|
156
148
|
const [solePending] = pending;
|
|
157
149
|
if (pending.length === 1 && solePending !== undefined) {
|
|
@@ -170,22 +162,22 @@ export class ReplyTracker {
|
|
|
170
162
|
return Array.from(this.pendingAsks.values()).sort((a, b) => a.receivedAt - b.receivedAt);
|
|
171
163
|
}
|
|
172
164
|
hasTurnContext(messageId) {
|
|
173
|
-
return (this.
|
|
174
|
-
this.
|
|
175
|
-
this.
|
|
165
|
+
return (this.pendingTurnIds.includes(messageId) ||
|
|
166
|
+
this.currentTurnId === messageId ||
|
|
167
|
+
this.activeAgentId === messageId);
|
|
176
168
|
}
|
|
177
169
|
removeContext(messageId) {
|
|
178
170
|
this.pendingAsks.delete(messageId);
|
|
179
|
-
for (let index = this.
|
|
180
|
-
if (this.
|
|
181
|
-
this.
|
|
171
|
+
for (let index = this.pendingTurnIds.length - 1; index >= 0; index -= 1) {
|
|
172
|
+
if (this.pendingTurnIds[index] === messageId) {
|
|
173
|
+
this.pendingTurnIds.splice(index, 1);
|
|
182
174
|
}
|
|
183
175
|
}
|
|
184
|
-
if (this.
|
|
185
|
-
this.
|
|
176
|
+
if (this.currentTurnId === messageId) {
|
|
177
|
+
this.currentTurnId = null;
|
|
186
178
|
}
|
|
187
|
-
if (this.
|
|
188
|
-
this.
|
|
179
|
+
if (this.activeAgentId === messageId) {
|
|
180
|
+
this.activeAgentId = null;
|
|
189
181
|
}
|
|
190
182
|
}
|
|
191
183
|
pruneExpired(now) {
|
|
@@ -46,6 +46,8 @@ export class IntercomSessionEvents {
|
|
|
46
46
|
if (client?.isConnected() !== true ||
|
|
47
47
|
!context ||
|
|
48
48
|
context.message.expectsReply !== true ||
|
|
49
|
+
// A native turn can start without consuming its queued input after an abort.
|
|
50
|
+
journal.get(context.message.id) !== undefined ||
|
|
49
51
|
error === null ||
|
|
50
52
|
error === "") {
|
|
51
53
|
return;
|
package/docs/intercom.md
CHANGED
|
@@ -330,8 +330,12 @@ not an end-to-end exactly-once guarantee for arbitrary tool side effects.
|
|
|
330
330
|
|
|
331
331
|
Idle recovery preserves admission order within the non-passive backlog and wakes
|
|
332
332
|
once on its last item; passive breadcrumbs are appended before that waking work.
|
|
333
|
-
The first eligible ask remains the implicit reply target, independently
|
|
334
|
-
message triggers the turn.
|
|
333
|
+
The first unresolved eligible ask remains the implicit reply target, independently
|
|
334
|
+
of which message triggers the turn. Reply attribution refers to the unresolved
|
|
335
|
+
ask by ID. Placing delayed input does not revive an already replied, expired, or
|
|
336
|
+
retired reply obligation. A failed turn can retire only an ask whose input Pi has
|
|
337
|
+
consumed; queued input remains answerable after abort recovery.
|
|
338
|
+
A busy recipient's steer is handed to Pi's native
|
|
335
339
|
queue and consumed at its next tool boundary; no extension probe asks another
|
|
336
340
|
tool to give up the turn.
|
|
337
341
|
|
package/package.json
CHANGED
|
@@ -185,7 +185,7 @@ export class InboundDeliveryOwner {
|
|
|
185
185
|
const trigger = index === -1 ? undefined : rest.splice(index, 1)[0];
|
|
186
186
|
const replyTarget = entries.find((entry) => entry.message.expectsReply === true) ?? trigger;
|
|
187
187
|
if (replyTarget && this.journal.get(replyTarget.message.id) === replyTarget) {
|
|
188
|
-
this.hooks.replies.queueTurnContext(
|
|
188
|
+
this.hooks.replies.queueTurnContext(replyTarget.message.id);
|
|
189
189
|
}
|
|
190
190
|
for (const entry of rest) {
|
|
191
191
|
if (!this.live(generation)) {
|
|
@@ -295,11 +295,7 @@ export class InboundDeliveryOwner {
|
|
|
295
295
|
this.journal.markNative(entry.message.id);
|
|
296
296
|
}
|
|
297
297
|
if (delivery !== "passive") {
|
|
298
|
-
this.hooks.replies.queueTurnContext(
|
|
299
|
-
from: entry.from,
|
|
300
|
-
message: entry.message,
|
|
301
|
-
receivedAt: Date.now(),
|
|
302
|
-
});
|
|
298
|
+
this.hooks.replies.queueTurnContext(entry.message.id);
|
|
303
299
|
}
|
|
304
300
|
}
|
|
305
301
|
private nativeMessage(entry: InboundMessageEntry): Parameters<ExtensionAPI["sendMessage"]>[0] {
|
|
@@ -93,9 +93,9 @@ function replyBySender(contexts: readonly IntercomContext[], to: string): Interc
|
|
|
93
93
|
}
|
|
94
94
|
export class ReplyTracker {
|
|
95
95
|
private readonly pendingAsks = new Map<string, IntercomContext>();
|
|
96
|
-
private readonly
|
|
97
|
-
private
|
|
98
|
-
private
|
|
96
|
+
private readonly pendingTurnIds: string[] = [];
|
|
97
|
+
private currentTurnId: string | null = null;
|
|
98
|
+
private activeAgentId: string | null = null;
|
|
99
99
|
private readonly askTimeoutMs: number;
|
|
100
100
|
|
|
101
101
|
constructor(askTimeoutMs = 10 * 60 * 1000) {
|
|
@@ -115,67 +115,66 @@ export class ReplyTracker {
|
|
|
115
115
|
return context;
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
queueTurnContext(
|
|
119
|
-
if (
|
|
118
|
+
queueTurnContext(messageId: string): void {
|
|
119
|
+
if (!this.pendingAsks.has(messageId) || this.hasTurnContext(messageId)) {
|
|
120
120
|
return;
|
|
121
121
|
}
|
|
122
|
-
|
|
123
|
-
return;
|
|
124
|
-
}
|
|
125
|
-
this.pendingTurnContexts.push(context);
|
|
122
|
+
this.pendingTurnIds.push(messageId);
|
|
126
123
|
}
|
|
127
124
|
|
|
128
125
|
beginTurn(now = Date.now()): void {
|
|
129
126
|
this.pruneExpired(now);
|
|
130
|
-
this.
|
|
131
|
-
if (this.
|
|
132
|
-
this.
|
|
127
|
+
this.currentTurnId = this.pendingTurnIds.shift() ?? null;
|
|
128
|
+
if (this.currentTurnId !== null) {
|
|
129
|
+
this.activeAgentId = this.currentTurnId;
|
|
133
130
|
}
|
|
134
131
|
}
|
|
135
132
|
|
|
136
133
|
currentTurn(): IntercomContext | null {
|
|
137
|
-
|
|
134
|
+
const id = this.currentTurnId ?? this.activeAgentId;
|
|
135
|
+
return id === null ? null : (this.pendingAsks.get(id) ?? null);
|
|
138
136
|
}
|
|
139
137
|
|
|
140
138
|
endTurn(): void {
|
|
141
|
-
this.
|
|
139
|
+
this.currentTurnId = null;
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
endAgent(): void {
|
|
145
|
-
this.
|
|
146
|
-
this.
|
|
143
|
+
this.currentTurnId = null;
|
|
144
|
+
this.activeAgentId = null;
|
|
147
145
|
}
|
|
148
146
|
|
|
149
147
|
reset(): void {
|
|
150
148
|
this.pendingAsks.clear();
|
|
151
|
-
this.
|
|
152
|
-
this.
|
|
153
|
-
this.activeAgentContext = null;
|
|
149
|
+
this.pendingTurnIds.length = 0;
|
|
150
|
+
this.endAgent();
|
|
154
151
|
}
|
|
155
152
|
|
|
156
153
|
expireSender(sessionId: string): number {
|
|
157
|
-
const
|
|
154
|
+
const expiring = new Set(
|
|
155
|
+
[...this.pendingAsks.values()]
|
|
156
|
+
.filter((context) => context.from.id === sessionId)
|
|
157
|
+
.map((context) => context.message.id),
|
|
158
|
+
);
|
|
158
159
|
let expired = 0;
|
|
159
|
-
for (const
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
expired += 1;
|
|
163
|
-
}
|
|
160
|
+
for (const messageId of expiring) {
|
|
161
|
+
this.pendingAsks.delete(messageId);
|
|
162
|
+
expired += 1;
|
|
164
163
|
}
|
|
165
|
-
const beforeQueued = this.
|
|
166
|
-
for (let index = this.
|
|
167
|
-
const
|
|
168
|
-
if (
|
|
169
|
-
this.
|
|
164
|
+
const beforeQueued = this.pendingTurnIds.length;
|
|
165
|
+
for (let index = this.pendingTurnIds.length - 1; index >= 0; index -= 1) {
|
|
166
|
+
const id = this.pendingTurnIds[index];
|
|
167
|
+
if (id !== undefined && expiring.has(id)) {
|
|
168
|
+
this.pendingTurnIds.splice(index, 1);
|
|
170
169
|
}
|
|
171
170
|
}
|
|
172
|
-
expired += beforeQueued - this.
|
|
173
|
-
if (this.
|
|
174
|
-
this.
|
|
171
|
+
expired += beforeQueued - this.pendingTurnIds.length;
|
|
172
|
+
if (this.currentTurnId !== null && expiring.has(this.currentTurnId)) {
|
|
173
|
+
this.currentTurnId = null;
|
|
175
174
|
expired += 1;
|
|
176
175
|
}
|
|
177
|
-
if (this.
|
|
178
|
-
this.
|
|
176
|
+
if (this.activeAgentId !== null && expiring.has(this.activeAgentId)) {
|
|
177
|
+
this.activeAgentId = null;
|
|
179
178
|
expired += 1;
|
|
180
179
|
}
|
|
181
180
|
return expired;
|
|
@@ -188,30 +187,23 @@ export class ReplyTracker {
|
|
|
188
187
|
this.pruneExpired(now);
|
|
189
188
|
|
|
190
189
|
const pending = Array.from(this.pendingAsks.values());
|
|
191
|
-
const contexts = this.currentTurnContext
|
|
192
|
-
? [
|
|
193
|
-
this.currentTurnContext,
|
|
194
|
-
...pending.filter(
|
|
195
|
-
(context) => context.message.id !== this.currentTurnContext?.message.id,
|
|
196
|
-
),
|
|
197
|
-
]
|
|
198
|
-
: pending;
|
|
199
|
-
|
|
200
190
|
const replyTo = options.replyTo ?? "";
|
|
201
191
|
const to = options.to ?? "";
|
|
202
192
|
if (replyTo !== "") {
|
|
203
|
-
return explicitReply(
|
|
193
|
+
return explicitReply(pending, replyTo, to);
|
|
204
194
|
}
|
|
205
195
|
if (to !== "") {
|
|
206
|
-
return replyBySender(
|
|
196
|
+
return replyBySender(pending, to);
|
|
207
197
|
}
|
|
208
198
|
return this.implicitReplyTarget(pending);
|
|
209
199
|
}
|
|
210
200
|
|
|
211
201
|
/** Without an explicit target, the turn context wins, then a sole pending ask. */
|
|
212
202
|
private implicitReplyTarget(pending: readonly IntercomContext[]): IntercomContext {
|
|
213
|
-
|
|
214
|
-
|
|
203
|
+
const current =
|
|
204
|
+
this.currentTurnId === null ? undefined : this.pendingAsks.get(this.currentTurnId);
|
|
205
|
+
if (current) {
|
|
206
|
+
return current;
|
|
215
207
|
}
|
|
216
208
|
const [solePending] = pending;
|
|
217
209
|
if (pending.length === 1 && solePending !== undefined) {
|
|
@@ -234,24 +226,24 @@ export class ReplyTracker {
|
|
|
234
226
|
|
|
235
227
|
private hasTurnContext(messageId: string): boolean {
|
|
236
228
|
return (
|
|
237
|
-
this.
|
|
238
|
-
this.
|
|
239
|
-
this.
|
|
229
|
+
this.pendingTurnIds.includes(messageId) ||
|
|
230
|
+
this.currentTurnId === messageId ||
|
|
231
|
+
this.activeAgentId === messageId
|
|
240
232
|
);
|
|
241
233
|
}
|
|
242
234
|
|
|
243
235
|
private removeContext(messageId: string): void {
|
|
244
236
|
this.pendingAsks.delete(messageId);
|
|
245
|
-
for (let index = this.
|
|
246
|
-
if (this.
|
|
247
|
-
this.
|
|
237
|
+
for (let index = this.pendingTurnIds.length - 1; index >= 0; index -= 1) {
|
|
238
|
+
if (this.pendingTurnIds[index] === messageId) {
|
|
239
|
+
this.pendingTurnIds.splice(index, 1);
|
|
248
240
|
}
|
|
249
241
|
}
|
|
250
|
-
if (this.
|
|
251
|
-
this.
|
|
242
|
+
if (this.currentTurnId === messageId) {
|
|
243
|
+
this.currentTurnId = null;
|
|
252
244
|
}
|
|
253
|
-
if (this.
|
|
254
|
-
this.
|
|
245
|
+
if (this.activeAgentId === messageId) {
|
|
246
|
+
this.activeAgentId = null;
|
|
255
247
|
}
|
|
256
248
|
}
|
|
257
249
|
|
|
@@ -33,7 +33,7 @@ type Connection = Readonly<
|
|
|
33
33
|
>
|
|
34
34
|
>;
|
|
35
35
|
type Journal = Readonly<
|
|
36
|
-
Pick<InboundJournal, "clearNative" | "consume" | "reconcileConsumed" | "reset" | "retire">
|
|
36
|
+
Pick<InboundJournal, "clearNative" | "consume" | "get" | "reconcileConsumed" | "reset" | "retire">
|
|
37
37
|
>;
|
|
38
38
|
type ReplyWaitHandle = Readonly<Pick<ReplyWait, "reject">>;
|
|
39
39
|
type InboundDeliveryHandle = Readonly<
|
|
@@ -101,6 +101,8 @@ export class IntercomSessionEvents {
|
|
|
101
101
|
client?.isConnected() !== true ||
|
|
102
102
|
!context ||
|
|
103
103
|
context.message.expectsReply !== true ||
|
|
104
|
+
// A native turn can start without consuming its queued input after an abort.
|
|
105
|
+
journal.get(context.message.id) !== undefined ||
|
|
104
106
|
error === null ||
|
|
105
107
|
error === ""
|
|
106
108
|
) {
|