@oh-my-pi/pi-ai 16.4.2 → 16.4.4
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
CHANGED
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [16.4.3] - 2026-07-11
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- Fixed auth database upgrades from schema v5 by creating the OAuth credential refresh-lease table before lease statements are prepared.
|
|
10
|
+
- Fixed an issue in the Responses API where empty tool results were incorrectly serialized with a "(see attached image)" placeholder, causing models to look for non-existent attachments.
|
|
11
|
+
- Fixed OpenAI Responses server non-streaming envelopes to always include the required "incomplete_details" field, using null for completed responses.
|
|
12
|
+
- Preserved Cloud Code Assist tool schemas when mixed-type unions carry branch-local validation descriptions.
|
|
13
|
+
|
|
5
14
|
## [16.4.2] - 2026-07-10
|
|
6
15
|
|
|
7
16
|
### Fixed
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "16.4.
|
|
4
|
+
"version": "16.4.4",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://omp.sh",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@bufbuild/protobuf": "^2.12.0",
|
|
41
|
-
"@oh-my-pi/pi-catalog": "16.4.
|
|
42
|
-
"@oh-my-pi/pi-utils": "16.4.
|
|
43
|
-
"@oh-my-pi/pi-wire": "16.4.
|
|
41
|
+
"@oh-my-pi/pi-catalog": "16.4.4",
|
|
42
|
+
"@oh-my-pi/pi-utils": "16.4.4",
|
|
43
|
+
"@oh-my-pi/pi-wire": "16.4.4",
|
|
44
44
|
"arktype": "2.2.2",
|
|
45
45
|
"zod": "^4"
|
|
46
46
|
},
|
package/src/auth-storage.ts
CHANGED
|
@@ -5637,6 +5637,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
5637
5637
|
} catch {
|
|
5638
5638
|
// Ignore chmod failures (e.g., Windows)
|
|
5639
5639
|
}
|
|
5640
|
+
SqliteAuthCredentialStore.#ensureAuthCredentialRefreshLeasesTable(db);
|
|
5640
5641
|
return new SqliteAuthCredentialStore(db);
|
|
5641
5642
|
} catch (err) {
|
|
5642
5643
|
db?.close();
|
|
@@ -5655,6 +5656,18 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
5655
5656
|
);
|
|
5656
5657
|
}
|
|
5657
5658
|
|
|
5659
|
+
static #ensureAuthCredentialRefreshLeasesTable(db: Database): void {
|
|
5660
|
+
db.run(`
|
|
5661
|
+
CREATE TABLE IF NOT EXISTS auth_credential_refresh_leases (
|
|
5662
|
+
credential_id INTEGER PRIMARY KEY,
|
|
5663
|
+
owner TEXT NOT NULL,
|
|
5664
|
+
expires_at_ms INTEGER NOT NULL,
|
|
5665
|
+
updated_at INTEGER NOT NULL
|
|
5666
|
+
);
|
|
5667
|
+
CREATE INDEX IF NOT EXISTS idx_auth_credential_refresh_leases_expires ON auth_credential_refresh_leases(expires_at_ms);
|
|
5668
|
+
`);
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5658
5671
|
#initializeSchema(): void {
|
|
5659
5672
|
// Install the busy handler BEFORE any lock-taking statement (incl.
|
|
5660
5673
|
// `PRAGMA journal_mode=WAL`, which acquires an exclusive lock during WAL
|
|
@@ -5820,15 +5833,7 @@ export class SqliteAuthCredentialStore implements AuthCredentialStore {
|
|
|
5820
5833
|
}
|
|
5821
5834
|
|
|
5822
5835
|
#createAuthCredentialRefreshLeasesTable(): void {
|
|
5823
|
-
this.#db
|
|
5824
|
-
CREATE TABLE IF NOT EXISTS auth_credential_refresh_leases (
|
|
5825
|
-
credential_id INTEGER PRIMARY KEY,
|
|
5826
|
-
owner TEXT NOT NULL,
|
|
5827
|
-
expires_at_ms INTEGER NOT NULL,
|
|
5828
|
-
updated_at INTEGER NOT NULL
|
|
5829
|
-
);
|
|
5830
|
-
CREATE INDEX IF NOT EXISTS idx_auth_credential_refresh_leases_expires ON auth_credential_refresh_leases(expires_at_ms);
|
|
5831
|
-
`);
|
|
5836
|
+
SqliteAuthCredentialStore.#ensureAuthCredentialRefreshLeasesTable(this.#db);
|
|
5832
5837
|
}
|
|
5833
5838
|
|
|
5834
5839
|
#migrateAuthSchema(fromVersion: number): void {
|
|
@@ -568,6 +568,10 @@ function responseStatusForStopReason(message: AssistantMessage): ResponseStatus
|
|
|
568
568
|
return "completed";
|
|
569
569
|
}
|
|
570
570
|
|
|
571
|
+
function incompleteDetailsForStatus(status: ResponseStatus): { reason: "max_output_tokens" } | null {
|
|
572
|
+
return status === "incomplete" ? { reason: "max_output_tokens" } : null;
|
|
573
|
+
}
|
|
574
|
+
|
|
571
575
|
function buildReasoningItem(part: ThinkingContent): ReasoningOutputItem {
|
|
572
576
|
const baseId = part.itemId ?? makeReasoningId();
|
|
573
577
|
if (part.thinkingSignature) {
|
|
@@ -718,7 +722,7 @@ function buildResponseEnvelope(
|
|
|
718
722
|
model: requestedModelId,
|
|
719
723
|
output: items,
|
|
720
724
|
usage,
|
|
721
|
-
|
|
725
|
+
incomplete_details: incompleteDetailsForStatus(status),
|
|
722
726
|
...(status === "failed" ? { error: { message: message.errorMessage ?? "response failed" } } : {}),
|
|
723
727
|
};
|
|
724
728
|
}
|
|
@@ -812,6 +816,7 @@ export function encodeStream(
|
|
|
812
816
|
model: requestedModelId,
|
|
813
817
|
output,
|
|
814
818
|
usage: null,
|
|
819
|
+
incomplete_details: incompleteDetailsForStatus(status),
|
|
815
820
|
});
|
|
816
821
|
|
|
817
822
|
const openMessage = (signature?: MessageSignature): OpenMessage => {
|
|
@@ -1242,7 +1247,7 @@ export function encodeStream(
|
|
|
1242
1247
|
model: requestedModelId,
|
|
1243
1248
|
output: items,
|
|
1244
1249
|
usage,
|
|
1245
|
-
|
|
1250
|
+
incomplete_details: incompleteDetailsForStatus(status),
|
|
1246
1251
|
...(status === "failed"
|
|
1247
1252
|
? { error: { message: message?.errorMessage ?? "response failed" } }
|
|
1248
1253
|
: {}),
|
|
@@ -1267,6 +1272,7 @@ export function encodeStream(
|
|
|
1267
1272
|
model: requestedModelId,
|
|
1268
1273
|
output: [],
|
|
1269
1274
|
error: { message: err instanceof Error ? err.message : String(err) },
|
|
1275
|
+
incomplete_details: null,
|
|
1270
1276
|
},
|
|
1271
1277
|
}),
|
|
1272
1278
|
),
|
|
@@ -1720,12 +1720,19 @@ export function appendResponsesToolResultMessages<TApi extends Api>(
|
|
|
1720
1720
|
const hasImages = toolResult.content.some((block): block is ImageContent => block.type === "image");
|
|
1721
1721
|
const omittedImages = hasImages && !supportsImages;
|
|
1722
1722
|
const normalized = normalizeResponsesToolCallId(toolResult.toolCallId);
|
|
1723
|
+
// "(see attached image)" is only truthful when the result actually carries
|
|
1724
|
+
// images (they ride as a separate user message on the Responses API). A
|
|
1725
|
+
// genuinely empty text result (empty file read, silent tool) must stay
|
|
1726
|
+
// empty — the placeholder sent models chasing an attachment that never
|
|
1727
|
+
// existed.
|
|
1723
1728
|
const output = (
|
|
1724
1729
|
omittedImages
|
|
1725
1730
|
? joinTextWithImagePlaceholder(textResult, true)
|
|
1726
1731
|
: textResult.length > 0
|
|
1727
1732
|
? textResult
|
|
1728
|
-
:
|
|
1733
|
+
: hasImages
|
|
1734
|
+
? "(see attached image)"
|
|
1735
|
+
: ""
|
|
1729
1736
|
).toWellFormed();
|
|
1730
1737
|
if (strictResponsesPairing && !knownCallIds.has(normalized.callId)) {
|
|
1731
1738
|
// Strict backends (Azure, Copilot) reject unpaired outputs outright, but
|
|
@@ -547,7 +547,11 @@ function collapseMixedTypeCombinerVariants(schema: JsonObject, combiner: "anyOf"
|
|
|
547
547
|
|
|
548
548
|
const existingValue = mergedVariantFields[key];
|
|
549
549
|
if (existingValue !== undefined && !areJsonValuesEqual(existingValue, variantValue)) {
|
|
550
|
-
return schema;
|
|
550
|
+
if (key !== "description") return schema;
|
|
551
|
+
// Descriptions are annotations, so merge branch-local spill text instead of
|
|
552
|
+
// treating it as a structural incompatibility.
|
|
553
|
+
mergedVariantFields[key] = mergeSchemaDescriptions(existingValue, variantValue);
|
|
554
|
+
continue;
|
|
551
555
|
}
|
|
552
556
|
mergedVariantFields[key] = variantValue;
|
|
553
557
|
}
|
|
@@ -588,7 +592,9 @@ function collapseMixedTypeCombinerVariants(schema: JsonObject, combiner: "anyOf"
|
|
|
588
592
|
const value = mergedVariantFields[key];
|
|
589
593
|
const existingValue = nextSchema[key];
|
|
590
594
|
if (existingValue !== undefined && !areJsonValuesEqual(existingValue, value)) {
|
|
591
|
-
return schema;
|
|
595
|
+
if (key !== "description") return schema;
|
|
596
|
+
nextSchema[key] = mergeSchemaDescriptions(existingValue, value);
|
|
597
|
+
continue;
|
|
592
598
|
}
|
|
593
599
|
if (existingValue === undefined) {
|
|
594
600
|
nextSchema[key] = value;
|
|
@@ -597,6 +603,13 @@ function collapseMixedTypeCombinerVariants(schema: JsonObject, combiner: "anyOf"
|
|
|
597
603
|
return nextSchema;
|
|
598
604
|
}
|
|
599
605
|
|
|
606
|
+
function mergeSchemaDescriptions(existing: unknown, incoming: unknown): string {
|
|
607
|
+
if (typeof existing !== "string") return typeof incoming === "string" ? incoming : "";
|
|
608
|
+
if (typeof incoming !== "string" || incoming.length === 0 || existing === incoming) return existing;
|
|
609
|
+
if (existing.length === 0) return incoming;
|
|
610
|
+
return `${existing}\n\n${incoming}`;
|
|
611
|
+
}
|
|
612
|
+
|
|
600
613
|
function collapseSameTypeCombinerVariants(schema: JsonObject, combiner: "anyOf" | "oneOf"): JsonObject {
|
|
601
614
|
const variantsRaw = schema[combiner];
|
|
602
615
|
if (!Array.isArray(variantsRaw) || variantsRaw.length === 0) return schema;
|