@oh-my-pi/pi-ai 18.4.10 → 18.4.11
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 +7 -0
- package/package.json +6 -6
- package/src/auth-broker/remote-store.ts +35 -19
- package/src/providers/cursor.ts +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [18.4.11] - 2026-10-02
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed Cursor cached prompt token accounting to prevent duplicate input-token and cost reporting on cached turns.
|
|
10
|
+
- Fixed auth-broker credential handling so late token-refresh responses cannot restore logged-out credentials or overwrite a newer login.
|
|
11
|
+
|
|
5
12
|
## [18.4.10] - 2026-10-02
|
|
6
13
|
|
|
7
14
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oh-my-pi/pi-ai",
|
|
3
|
-
"version": "18.4.
|
|
3
|
+
"version": "18.4.11",
|
|
4
4
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai",
|
|
@@ -155,11 +155,11 @@
|
|
|
155
155
|
"fmt": "oxfmt --no-error-on-unmatched-pattern 'src/**/*.{ts,tsx}' '{test,bench,examples,scripts}/**/*.ts' '*.ts'"
|
|
156
156
|
},
|
|
157
157
|
"dependencies": {
|
|
158
|
-
"@oh-my-pi/omptype": "18.4.
|
|
159
|
-
"@oh-my-pi/pi-catalog": "18.4.
|
|
160
|
-
"@oh-my-pi/pi-natives": "18.4.
|
|
161
|
-
"@oh-my-pi/pi-utils": "18.4.
|
|
162
|
-
"@oh-my-pi/pi-wire": "18.4.
|
|
158
|
+
"@oh-my-pi/omptype": "18.4.11",
|
|
159
|
+
"@oh-my-pi/pi-catalog": "18.4.11",
|
|
160
|
+
"@oh-my-pi/pi-natives": "18.4.11",
|
|
161
|
+
"@oh-my-pi/pi-utils": "18.4.11",
|
|
162
|
+
"@oh-my-pi/pi-wire": "18.4.11"
|
|
163
163
|
},
|
|
164
164
|
"devDependencies": {
|
|
165
165
|
"@types/bun": "^1.3.14"
|
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
type OAuthCredential,
|
|
18
18
|
type OAuthRefreshReason,
|
|
19
19
|
REMOTE_REFRESH_SENTINEL,
|
|
20
|
+
type RemoteOAuthCredential,
|
|
20
21
|
type StoredAuthCredential,
|
|
21
22
|
type StoredCredentialBlock,
|
|
22
23
|
} from "../auth/types";
|
|
@@ -849,15 +850,7 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
849
850
|
|
|
850
851
|
async markCredentialSuspect(credentialId: number, opts: { signal?: AbortSignal } = {}): Promise<void> {
|
|
851
852
|
this.#noteActivity();
|
|
852
|
-
|
|
853
|
-
if (entry.credential.type !== "oauth") {
|
|
854
|
-
throw new AIError.AuthBrokerError(`Broker returned non-OAuth credential for id=${credentialId}`);
|
|
855
|
-
}
|
|
856
|
-
if (!this.#applyCredentialEntry(entry)) {
|
|
857
|
-
throw new AIError.AuthBrokerError(
|
|
858
|
-
`Broker refreshed credential id=${credentialId} outside the configured account pool`,
|
|
859
|
-
);
|
|
860
|
-
}
|
|
853
|
+
await this.#refreshThroughBroker(credentialId, opts.signal, "auth-recovery");
|
|
861
854
|
this.#maybeRefreshSnapshot("suspect credential refresh");
|
|
862
855
|
}
|
|
863
856
|
|
|
@@ -1168,21 +1161,12 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
1168
1161
|
reason?: OAuthRefreshReason,
|
|
1169
1162
|
): Promise<OAuthCredentials> {
|
|
1170
1163
|
this.#noteActivity();
|
|
1171
|
-
const
|
|
1172
|
-
if (entry.credential.type !== "oauth") {
|
|
1173
|
-
throw new AIError.AuthBrokerError(`Broker returned non-OAuth credential for id=${credentialId}`);
|
|
1174
|
-
}
|
|
1175
|
-
if (!this.#applyCredentialEntry(entry)) {
|
|
1176
|
-
throw new AIError.AuthBrokerError(
|
|
1177
|
-
`Broker refreshed credential id=${credentialId} outside the configured account pool`,
|
|
1178
|
-
);
|
|
1179
|
-
}
|
|
1164
|
+
const refreshed = await this.#refreshThroughBroker(credentialId, signal, reason);
|
|
1180
1165
|
if (!this.#streamingActive) {
|
|
1181
1166
|
await this.refreshSnapshot().catch(error => {
|
|
1182
1167
|
logger.debug("auth-broker snapshot refresh after credential refresh failed", { error: String(error) });
|
|
1183
1168
|
});
|
|
1184
1169
|
}
|
|
1185
|
-
const refreshed = entry.credential;
|
|
1186
1170
|
return {
|
|
1187
1171
|
access: refreshed.access,
|
|
1188
1172
|
refresh: REMOTE_REFRESH_SENTINEL,
|
|
@@ -1194,6 +1178,38 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
|
|
|
1194
1178
|
};
|
|
1195
1179
|
}
|
|
1196
1180
|
|
|
1181
|
+
/**
|
|
1182
|
+
* Refresh one credential through the broker and apply the reply. If this
|
|
1183
|
+
* client's copy changed to something else while the request was in flight,
|
|
1184
|
+
* the reply may be stale, so the broker's current row wins: a logout stays
|
|
1185
|
+
* logged out and a newer login is kept.
|
|
1186
|
+
*/
|
|
1187
|
+
async #refreshThroughBroker(
|
|
1188
|
+
credentialId: number,
|
|
1189
|
+
signal?: AbortSignal,
|
|
1190
|
+
reason?: OAuthRefreshReason,
|
|
1191
|
+
): Promise<RemoteOAuthCredential> {
|
|
1192
|
+
const local = () => this.#snapshot.credentials.find(candidate => candidate.id === credentialId);
|
|
1193
|
+
const before = JSON.stringify(local()?.credential);
|
|
1194
|
+
let { entry } = await this.#client.refreshCredential(credentialId, signal, reason);
|
|
1195
|
+
const current = JSON.stringify(local()?.credential);
|
|
1196
|
+
if (current !== before && current !== JSON.stringify(entry.credential)) {
|
|
1197
|
+
await this.refreshSnapshot();
|
|
1198
|
+
const latest = local();
|
|
1199
|
+
if (!latest) throw new AIError.AuthBrokerError(`Credential id=${credentialId} was removed during refresh`);
|
|
1200
|
+
entry = latest;
|
|
1201
|
+
}
|
|
1202
|
+
if (entry.credential.type !== "oauth") {
|
|
1203
|
+
throw new AIError.AuthBrokerError(`Broker returned non-OAuth credential for id=${credentialId}`);
|
|
1204
|
+
}
|
|
1205
|
+
if (!this.#applyCredentialEntry(entry)) {
|
|
1206
|
+
throw new AIError.AuthBrokerError(
|
|
1207
|
+
`Broker refreshed credential id=${credentialId} outside the configured account pool`,
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
return entry.credential;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1197
1213
|
/**
|
|
1198
1214
|
* Store-level hook consumed by `AuthStorage.usage.reports()` — proxies
|
|
1199
1215
|
* to the broker's `/v1/usage` endpoint. Shared per-credential caches and
|
package/src/providers/cursor.ts
CHANGED
|
@@ -5341,7 +5341,9 @@ export function processInteractionUpdate(
|
|
|
5341
5341
|
* summed 22 against a final 36 — and never report input, cache, or reasoning
|
|
5342
5342
|
* tokens, so every bucket the final frame reports replaces the streamed
|
|
5343
5343
|
* estimate. Unreported counters decode as `undefined`; a frame that reports
|
|
5344
|
-
* nothing at all leaves the streamed totals untouched.
|
|
5344
|
+
* nothing at all leaves the streamed totals untouched. `inputTokens` counts the
|
|
5345
|
+
* whole prompt, cache hits and writes included, so fresh input is what remains
|
|
5346
|
+
* after both are taken out.
|
|
5345
5347
|
*/
|
|
5346
5348
|
function applyTurnEndedUsage(usage: Usage, update: TurnEndedUpdate): void {
|
|
5347
5349
|
const input = Number(update.inputTokens ?? 0n);
|
|
@@ -5350,7 +5352,7 @@ function applyTurnEndedUsage(usage: Usage, update: TurnEndedUpdate): void {
|
|
|
5350
5352
|
const cacheWrite = Number(update.cacheWriteTokens ?? 0n);
|
|
5351
5353
|
const reasoning = Number(update.reasoningTokens ?? 0n);
|
|
5352
5354
|
if (input <= 0 && output <= 0 && cacheRead <= 0 && cacheWrite <= 0) return;
|
|
5353
|
-
if (input > 0) usage.input = input;
|
|
5355
|
+
if (input > 0) usage.input = Math.max(input - cacheRead - cacheWrite, 0);
|
|
5354
5356
|
if (output > 0) usage.output = output;
|
|
5355
5357
|
if (cacheRead > 0) usage.cacheRead = cacheRead;
|
|
5356
5358
|
if (cacheWrite > 0) usage.cacheWrite = cacheWrite;
|