@oh-my-pi/pi-ai 18.2.8 → 18.2.9

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.
Files changed (45) hide show
  1. package/CHANGELOG.md +33 -18
  2. package/dist/types/auth-gateway/types.d.ts +5 -0
  3. package/dist/types/auth-storage.d.ts +35 -32
  4. package/dist/types/index.d.ts +1 -0
  5. package/dist/types/providers/amazon-bedrock.d.ts +7 -0
  6. package/dist/types/providers/claude-code-fingerprint.d.ts +22 -3
  7. package/dist/types/providers/google-gemini-cli.d.ts +0 -2
  8. package/dist/types/providers/openai-chat-server-schema.d.ts +2 -2
  9. package/dist/types/usage/claude-api.d.ts +22 -0
  10. package/dist/types/usage/claude-reset.d.ts +44 -0
  11. package/dist/types/usage.d.ts +111 -5
  12. package/dist/types/utils/schema/json-schema-validator.d.ts +5 -2
  13. package/dist/types/utils/tool-call-loop-guard.d.ts +1 -1
  14. package/package.json +6 -6
  15. package/src/auth/sqlite-credential-store.ts +44 -1
  16. package/src/auth-broker/remote-store.ts +6 -6
  17. package/src/auth-broker/wire-schemas.ts +14 -0
  18. package/src/auth-gateway/server.ts +4 -0
  19. package/src/auth-gateway/types.ts +5 -0
  20. package/src/auth-storage.ts +263 -140
  21. package/src/error/flags.ts +10 -0
  22. package/src/index.ts +1 -0
  23. package/src/providers/amazon-bedrock.ts +55 -5
  24. package/src/providers/anthropic.ts +45 -11
  25. package/src/providers/aws-credentials.ts +124 -11
  26. package/src/providers/claude-code-fingerprint.ts +55 -3
  27. package/src/providers/gitlab-duo.ts +20 -4
  28. package/src/providers/google-gemini-cli.ts +0 -8
  29. package/src/providers/google-shared.ts +1 -18
  30. package/src/providers/openai-chat-server-schema.ts +1 -1
  31. package/src/providers/openai-chat-server.ts +3 -1
  32. package/src/providers/openai-codex-responses.ts +27 -5
  33. package/src/providers/openai-completions.ts +122 -19
  34. package/src/providers/pi-native-server.ts +1 -0
  35. package/src/registry/oauth/anthropic.ts +2 -3
  36. package/src/stream.ts +13 -4
  37. package/src/usage/alibaba-token-plan.ts +7 -1
  38. package/src/usage/claude-api.ts +66 -0
  39. package/src/usage/claude-reset.ts +638 -0
  40. package/src/usage/claude.ts +37 -59
  41. package/src/usage/kimi.ts +32 -1
  42. package/src/usage.ts +52 -5
  43. package/src/utils/schema/json-schema-validator.ts +23 -10
  44. package/src/utils/tool-call-loop-guard.ts +2 -2
  45. package/src/utils/validation.ts +145 -50
@@ -88,7 +88,7 @@ type SerializedCredentialRecord = {
88
88
  identityKey: string | null;
89
89
  };
90
90
 
91
- const AUTH_SCHEMA_VERSION = 7;
91
+ const AUTH_SCHEMA_VERSION = 8;
92
92
  const SQLITE_NOW_EPOCH = "CAST(strftime('%s','now') AS INTEGER)";
93
93
  const LEGACY_CODEX_BLOCK_PROVIDER_KEY = "openai-codex:oauth";
94
94
  const LEGACY_CODEX_BLOCK_SCOPE = "shared";
@@ -970,6 +970,9 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
970
970
  if (fromVersion < 7) {
971
971
  this.#migrateAuthSchemaV6ToV7();
972
972
  }
973
+ if (fromVersion < 8) {
974
+ this.#migrateAuthSchemaV7ToV8();
975
+ }
973
976
  }
974
977
 
975
978
  #migrateAuthSchemaV0ToV1(): void {
@@ -1141,6 +1144,46 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
1141
1144
  migrate.immediate();
1142
1145
  }
1143
1146
 
1147
+ #migrateAuthSchemaV7ToV8(): void {
1148
+ const migrate = this.#db.transaction(() => {
1149
+ // SingularityAPI split into two providers — the pay-as-you-go universal
1150
+ // gateway and the slot-reserved lanes — and keys stored under the
1151
+ // retired shared id belong to one or the other by format: the gateway
1152
+ // issues `sk-sapi-...` while the lanes issue plain `sk-...` (both
1153
+ // observed live 2026-09-22), and neither key is accepted by the other
1154
+ // host. Route each stored credential to the product that serves it
1155
+ // instead of orphaning it under an id nothing reads anymore.
1156
+ const select = this.#db.prepare(
1157
+ "SELECT id, credential_type, data FROM auth_credentials WHERE provider = 'singularityapi'",
1158
+ );
1159
+ let rows: Array<{ id: number; credential_type: string; data: string }>;
1160
+ try {
1161
+ rows = select.all() as Array<{ id: number; credential_type: string; data: string }>;
1162
+ } finally {
1163
+ select.finalize();
1164
+ }
1165
+ for (const row of rows) {
1166
+ let provider = "singularityapi-tech";
1167
+ try {
1168
+ const parsed = JSON.parse(row.data) as { key?: unknown };
1169
+ if (typeof parsed.key === "string" && parsed.key.startsWith("sk-sapi-")) {
1170
+ provider = "singularityapi-dev";
1171
+ }
1172
+ } catch {
1173
+ // Unparsable payload takes the lane default; whichever product the
1174
+ // key really belongs to then answers 401 and the owner re-logs in.
1175
+ }
1176
+ this.#db.run("UPDATE auth_credentials SET provider = ? WHERE id = ?", [provider, row.id]);
1177
+ this.#db.run(
1178
+ "UPDATE auth_credential_blocks SET provider_key = ? WHERE credential_id = ? AND provider_key = ?",
1179
+ [`${provider}:${row.credential_type}`, row.id, `singularityapi:${row.credential_type}`],
1180
+ );
1181
+ }
1182
+ this.#writeAuthSchemaVersion(8);
1183
+ });
1184
+ migrate.immediate();
1185
+ }
1186
+
1144
1187
  #backfillCredentialIdentityKeys(): void {
1145
1188
  const selectRowsStmt = this.#db.prepare(
1146
1189
  "SELECT id, provider, credential_type, data, disabled_cause, identity_key FROM auth_credentials WHERE identity_key IS NULL ORDER BY id ASC",
@@ -175,8 +175,8 @@ function usageOverlayKey(
175
175
  const accountId = ids.accountId?.trim().toLowerCase();
176
176
  const email = ids.email?.trim().toLowerCase();
177
177
  const projectId = ids.projectId?.trim().toLowerCase();
178
- if (accountId) base = `account:${accountId}`;
179
- else if (email) base = `email:${email}`;
178
+ if (email) base = `email:${email}`;
179
+ else if (accountId) base = `account:${accountId}`;
180
180
  else if (projectId) base = `project:${projectId}`;
181
181
  const orgId = ids.orgId?.trim().toLowerCase();
182
182
  if (orgId) return base ? `${provider}\0org:${orgId}|${base}` : `${provider}\0org:${orgId}`;
@@ -1556,6 +1556,10 @@ function reportMatchesIdentity(
1556
1556
  projectId: string | undefined,
1557
1557
  ): boolean {
1558
1558
  const metadata = (report.metadata ?? {}) as Record<string, unknown>;
1559
+ const metaEmail = readMetadataString(metadata, "email")?.toLowerCase();
1560
+ // Email identifies the member within shared Team workspace account/org ids.
1561
+ // When both sides provide it, a mismatch is decisive.
1562
+ if (email && metaEmail) return metaEmail === email;
1559
1563
  if (accountId) {
1560
1564
  const metaAccount = readMetadataString(metadata, "accountId") ?? readMetadataString(metadata, "account_id");
1561
1565
  if (metaAccount && metaAccount.toLowerCase() === accountId) return true;
@@ -1563,10 +1567,6 @@ function reportMatchesIdentity(
1563
1567
  if (limit.scope.accountId?.toLowerCase() === accountId) return true;
1564
1568
  }
1565
1569
  }
1566
- if (email) {
1567
- const metaEmail = readMetadataString(metadata, "email");
1568
- if (metaEmail && metaEmail.toLowerCase() === email) return true;
1569
- }
1570
1570
  if (projectId) {
1571
1571
  const metaProject = readMetadataString(metadata, "projectId") ?? readMetadataString(metadata, "project_id");
1572
1572
  if (metaProject && metaProject.toLowerCase() === projectId) return true;
@@ -235,7 +235,21 @@ const usageLimitSchema = type({
235
235
 
236
236
  const usageResetCreditsSchema = type({
237
237
  availableCount: "number",
238
+ "redeemableCount?": "number",
239
+ "nextCreditId?": "string",
240
+ "eligible?": "boolean",
241
+ "reason?": "string",
242
+ "cooldownUntil?": "string",
238
243
  "credits?": type({
244
+ "id?": "string",
245
+ "title?": "string",
246
+ "program?": "string",
247
+ "remainingCount?": "number",
248
+ "usable?": "boolean",
249
+ "requiresLimit?": "boolean",
250
+ "clears?": "string[]",
251
+ "blocking?": "string[]",
252
+ "usedFractions?": { "[string]": "number" },
239
253
  "grantedAt?": "string",
240
254
  "expiresAt?": "string",
241
255
  "status?": "string",
@@ -161,6 +161,10 @@ function buildStreamOptions(parsed: ParsedFormatRequest, api: Api, signal: Abort
161
161
  }
162
162
  if (options.reasoning !== undefined) opts.reasoning = options.reasoning;
163
163
  if (options.disableReasoning !== undefined) opts.disableReasoning = options.disableReasoning;
164
+ if (options.forceReasoningOff !== undefined) {
165
+ opts.disableReasoning = options.forceReasoningOff;
166
+ opts.forceReasoningOff = options.forceReasoningOff;
167
+ }
164
168
  if (options.hideThinkingSummary !== undefined) opts.hideThinkingSummary = options.hideThinkingSummary;
165
169
  if (options.taskBudget !== undefined) opts.taskBudget = options.taskBudget;
166
170
  if (options.anthropicPrefixMismatchBehavior !== undefined) {
@@ -59,6 +59,11 @@ export interface AuthGatewayParsedRequestOptions {
59
59
  reasoning?: Effort;
60
60
  /** Force-disable reasoning (Anthropic `thinking: { type: "disabled" }`). */
61
61
  disableReasoning?: boolean;
62
+ /**
63
+ * Preserve an explicit wire-level reasoning-off request through providers
64
+ * that distinguish it from the generic disable hint.
65
+ */
66
+ forceReasoningOff?: boolean;
62
67
  /**
63
68
  * Explicit Anthropic `thinking.budget_tokens`. Mirrors Rust's
64
69
  * `resolve_thinking_budget`: pins onto whichever effort the client