@oh-my-pi/pi-ai 16.3.11 → 16.3.13
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 +32 -0
- package/dist/types/auth-broker/discover.d.ts +1 -1
- package/dist/types/auth-retry.d.ts +3 -1
- package/dist/types/auth-storage.d.ts +13 -8
- package/dist/types/providers/anthropic.d.ts +1 -0
- package/dist/types/providers/cursor.d.ts +5 -0
- package/dist/types/providers/openai-shared.d.ts +6 -3
- package/dist/types/providers/register-builtins.d.ts +5 -0
- package/dist/types/registry/oauth/callback-server.d.ts +2 -0
- package/dist/types/registry/oauth/xai-oauth.d.ts +1 -7
- package/dist/types/registry/registry.d.ts +2 -1
- package/dist/types/registry/xai-oauth.d.ts +2 -1
- package/dist/types/types.d.ts +2 -0
- package/dist/types/utils/event-stream.d.ts +7 -0
- package/dist/types/utils/idle-iterator.d.ts +10 -0
- package/package.json +4 -4
- package/src/auth-broker/discover.ts +20 -16
- package/src/auth-broker/remote-store.ts +55 -5
- package/src/auth-gateway/server.ts +1 -0
- package/src/auth-retry.ts +7 -2
- package/src/auth-storage.ts +210 -46
- package/src/providers/anthropic.ts +78 -8
- package/src/providers/cursor.ts +17 -10
- package/src/providers/openai-codex-responses.ts +37 -6
- package/src/providers/openai-responses.ts +1 -1
- package/src/providers/openai-shared.ts +30 -14
- package/src/providers/register-builtins.ts +15 -0
- package/src/registry/oauth/__tests__/xai-oauth.test.ts +55 -0
- package/src/registry/oauth/callback-server.ts +40 -12
- package/src/registry/oauth/xai-oauth.ts +6 -10
- package/src/registry/xai-oauth.ts +2 -1
- package/src/stream.ts +7 -1
- package/src/types.ts +2 -0
- package/src/usage/openai-codex.ts +3 -2
- package/src/utils/event-stream.ts +26 -0
- package/src/utils/idle-iterator.ts +65 -9
|
@@ -135,6 +135,16 @@ export interface IdleTimeoutIteratorOptions {
|
|
|
135
135
|
* keepalive/no-op events from keeping a stalled tool call alive forever.
|
|
136
136
|
*/
|
|
137
137
|
isProgressItem?: (item: unknown) => boolean;
|
|
138
|
+
/**
|
|
139
|
+
* Reports consumer-side local work in flight for the stream: the provider
|
|
140
|
+
* transport is waiting on a server-requested local tool bridge (e.g. the
|
|
141
|
+
* Cursor exec channel) before anything can flow upstream again. While it
|
|
142
|
+
* returns true, an expired idle / first-item deadline slides forward
|
|
143
|
+
* instead of aborting — the silence is ours, not a provider stall. The
|
|
144
|
+
* watchdog re-arms with a full budget once the local work completes, so a
|
|
145
|
+
* provider that stalls afterwards is still caught.
|
|
146
|
+
*/
|
|
147
|
+
hasPendingLocalWork?: () => boolean;
|
|
138
148
|
/**
|
|
139
149
|
* Cancel iteration as soon as this signal aborts. Required for caller-driven
|
|
140
150
|
* cancellation (ESC) when the underlying transport does not surface signal
|
|
@@ -157,7 +167,7 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
157
167
|
options: IdleTimeoutIteratorOptions,
|
|
158
168
|
): AsyncGenerator<T> {
|
|
159
169
|
const firstItemTimeoutMs = options.firstItemTimeoutMs ?? options.idleTimeoutMs;
|
|
160
|
-
|
|
170
|
+
let firstItemDeadlineMs =
|
|
161
171
|
firstItemTimeoutMs !== undefined && firstItemTimeoutMs > 0 ? Date.now() + firstItemTimeoutMs : undefined;
|
|
162
172
|
const abortSignal = options.abortSignal;
|
|
163
173
|
const iterator = iterable[Symbol.asyncIterator]();
|
|
@@ -197,6 +207,28 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
197
207
|
};
|
|
198
208
|
let lastProgressAt = Date.now();
|
|
199
209
|
|
|
210
|
+
const hasPendingLocalWork = (): boolean => {
|
|
211
|
+
if (!options.hasPendingLocalWork) return false;
|
|
212
|
+
try {
|
|
213
|
+
return options.hasPendingLocalWork();
|
|
214
|
+
} catch {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
// Local work means the current gap is attributable to the consumer side,
|
|
219
|
+
// not the provider: slide the active deadline a full budget past now
|
|
220
|
+
// instead of aborting. Once the work completes the watchdog resumes from
|
|
221
|
+
// the last extension, so a provider that stalls afterwards is still caught.
|
|
222
|
+
const extendDeadlineForLocalWork = (): void => {
|
|
223
|
+
if (awaitingFirstItem) {
|
|
224
|
+
if (firstItemDeadlineMs !== undefined && firstItemTimeoutMs !== undefined) {
|
|
225
|
+
firstItemDeadlineMs = Date.now() + firstItemTimeoutMs;
|
|
226
|
+
}
|
|
227
|
+
} else {
|
|
228
|
+
lastProgressAt = Date.now();
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
|
|
200
232
|
const noTimeoutEnforced =
|
|
201
233
|
(firstItemTimeoutMs === undefined || firstItemTimeoutMs <= 0) &&
|
|
202
234
|
(options.idleTimeoutMs === undefined || options.idleTimeoutMs <= 0);
|
|
@@ -271,6 +303,12 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
271
303
|
timer = setTimeout(onTimerFire, Math.max(0, deadlineMs - Date.now()));
|
|
272
304
|
};
|
|
273
305
|
|
|
306
|
+
// The in-flight iterator.next() promise, persisted across loop iterations:
|
|
307
|
+
// a deadline extension for pending local work loops without consuming it,
|
|
308
|
+
// and issuing a second next() while one is outstanding would drop an item.
|
|
309
|
+
let pendingNext:
|
|
310
|
+
| Promise<{ kind: "next"; result: IteratorResult<T> } | { kind: "error"; error: unknown }>
|
|
311
|
+
| undefined;
|
|
274
312
|
try {
|
|
275
313
|
let raceCount = 0;
|
|
276
314
|
while (true) {
|
|
@@ -291,21 +329,29 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
291
329
|
if (firstItemDeadlineMs !== undefined) {
|
|
292
330
|
activeTimeoutMs = firstItemDeadlineMs - Date.now();
|
|
293
331
|
if (activeTimeoutMs <= 0) {
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
332
|
+
if (!hasPendingLocalWork()) {
|
|
333
|
+
options.onFirstItemTimeout?.();
|
|
334
|
+
closeIterator();
|
|
335
|
+
throw new AIError.StreamTimeoutError(options.firstItemErrorMessage ?? options.errorMessage);
|
|
336
|
+
}
|
|
337
|
+
extendDeadlineForLocalWork();
|
|
338
|
+
activeTimeoutMs = firstItemDeadlineMs! - Date.now();
|
|
297
339
|
}
|
|
298
340
|
}
|
|
299
341
|
} else if (options.idleTimeoutMs !== undefined && options.idleTimeoutMs > 0) {
|
|
300
342
|
activeTimeoutMs = options.idleTimeoutMs - (Date.now() - lastProgressAt);
|
|
301
343
|
if (activeTimeoutMs <= 0) {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
344
|
+
if (!hasPendingLocalWork()) {
|
|
345
|
+
options.onIdle?.();
|
|
346
|
+
closeIterator();
|
|
347
|
+
throw new AIError.StreamTimeoutError(options.errorMessage);
|
|
348
|
+
}
|
|
349
|
+
extendDeadlineForLocalWork();
|
|
350
|
+
activeTimeoutMs = options.idleTimeoutMs;
|
|
305
351
|
}
|
|
306
352
|
}
|
|
307
353
|
|
|
308
|
-
|
|
354
|
+
pendingNext ??= withRacy(iterator.next());
|
|
309
355
|
|
|
310
356
|
const racers: Array<
|
|
311
357
|
Promise<
|
|
@@ -314,7 +360,7 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
314
360
|
| { kind: "timeout" }
|
|
315
361
|
| { kind: "abort" }
|
|
316
362
|
>
|
|
317
|
-
> = [
|
|
363
|
+
> = [pendingNext];
|
|
318
364
|
|
|
319
365
|
const enforceTimeout = !noTimeoutEnforced && activeTimeoutMs !== undefined && activeTimeoutMs > 0;
|
|
320
366
|
if (enforceTimeout) {
|
|
@@ -333,11 +379,21 @@ export async function* iterateWithIdleTimeout<T>(
|
|
|
333
379
|
let continuing = false;
|
|
334
380
|
try {
|
|
335
381
|
const outcome = await Promise.race(racers);
|
|
382
|
+
if (outcome.kind === "next" || outcome.kind === "error") {
|
|
383
|
+
pendingNext = undefined;
|
|
384
|
+
}
|
|
336
385
|
if (outcome.kind === "abort") {
|
|
337
386
|
closeIterator();
|
|
338
387
|
throw abortReason(abortSignal!);
|
|
339
388
|
}
|
|
340
389
|
if (outcome.kind === "timeout") {
|
|
390
|
+
if (hasPendingLocalWork()) {
|
|
391
|
+
// A local tool is still running; the provider cannot make
|
|
392
|
+
// progress until we hand its result back. Keep waiting.
|
|
393
|
+
extendDeadlineForLocalWork();
|
|
394
|
+
continuing = true;
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
341
397
|
if (!awaitingFirstItem) {
|
|
342
398
|
options.onIdle?.();
|
|
343
399
|
} else {
|