@oh-my-pi/pi-ai 17.4.2 → 18.0.0

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,18 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.0.0] - 2026-08-22
6
+
7
+ ### Added
8
+
9
+ - Added reversible private-use glyph tokenization for Claude-compatible provider requests, including prompt notices, streamed response decoding, and safe handling of unresolved model-authored glyph tokens.
10
+
11
+ ## [17.4.3] - 2026-08-21
12
+
13
+ ### Fixed
14
+
15
+ - Fixed completed Anthropic turns remaining busy when the provider sent `message_stop` but kept the SSE connection open, which stranded tool execution and queued steering until timeout.
16
+
5
17
  ## [17.4.2] - 2026-08-21
6
18
 
7
19
  ### Added
@@ -1,5 +1,69 @@
1
- export declare const oauthCredentialSchema: import("@oh-my-pi/omptype").Type<import("../index.js").OAuthCredential, import("../index.js").OAuthCredential>, remoteOauthCredentialSchema: import("@oh-my-pi/omptype").Type<import("../index.js").RemoteOAuthCredential, import("../index.js").RemoteOAuthCredential>, apiKeyCredentialSchema: import("@oh-my-pi/omptype").Type<import("../index.js").ApiKeyCredential, import("../index.js").ApiKeyCredential>, writableAuthCredentialSchema: import("@oh-my-pi/omptype").Type<import("../index.js").AuthCredential, import("../index.js").AuthCredential>, snapshotCredentialSchema: import("@oh-my-pi/omptype").Type<import("../index.js").SnapshotCredential, import("../index.js").SnapshotCredential>, credentialSnapshotEntrySchema: import("@oh-my-pi/omptype").Type<import("../index.js").AuthCredentialSnapshotEntry, import("../index.js").AuthCredentialSnapshotEntry>, credentialBlockSnapshotSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialBlockSnapshot, import("./types.js").CredentialBlockSnapshot>, snapshotEntrySchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotEntry, import("./types.js").SnapshotEntry>, refresherScheduleSchema: import("@oh-my-pi/omptype").Type<import("./types.js").RefresherSchedule, import("./types.js").RefresherSchedule>, snapshotResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotResponse, import("./types.js").SnapshotResponse>, snapshotStreamSnapshotEventSchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotStreamSnapshotEvent, import("./types.js").SnapshotStreamSnapshotEvent>, snapshotStreamEntryEventSchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotStreamEntryEvent, import("./types.js").SnapshotStreamEntryEvent>, snapshotStreamRemovedEventSchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotStreamRemovedEvent, import("./types.js").SnapshotStreamRemovedEvent>, snapshotStreamEventSchema: import("@oh-my-pi/omptype").Type<import("./types.js").SnapshotStreamEvent, import("./types.js").SnapshotStreamEvent>, healthzResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").HealthzResponse, import("./types.js").HealthzResponse>, usageResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").UsageResponse, import("./types.js").UsageResponse>, usageHistoryResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").UsageHistoryResponse, import("./types.js").UsageHistoryResponse>, clientUsageReportRequestSchema: import("@oh-my-pi/omptype").Type<import("../index.js").ClientUsageReport, import("../index.js").ClientUsageReport>, clientUsageReportResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").ClientUsageReportResponse, import("./types.js").ClientUsageReportResponse>, clientUsageSummaryResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").ClientUsageSummaryResponse, import("./types.js").ClientUsageSummaryResponse>, credentialRefreshResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialRefreshResponse, import("./types.js").CredentialRefreshResponse>, credentialDisableRequestSchema: import("@oh-my-pi/omptype").Type<{
1
+ /**
2
+ * ArkType schemas for the auth-broker wire protocol.
3
+ *
4
+ * Shared between the server (validates inbound request bodies) and the client
5
+ * (validates responses from the broker). Schemas mirror the TypeScript types
6
+ * in `./types.ts` 1:1; the types remain the source of truth for static typing,
7
+ * and `Type` is asserted-compatible with them where possible.
8
+ *
9
+ * Envelope and fixed-shape schemas use `"+": "reject"` so unknown keys are
10
+ * rejected — the previous implementation used a hand-rolled `hasOnlyFields`
11
+ * allowlist for the same effect. The OAuth credential schema is the deliberate
12
+ * exception (standard type keeps extra keys): it preserves provider-specific extension fields so
13
+ * they round-trip through the broker instead of being dropped (see below).
14
+ */
15
+ import { type FluentType } from "@oh-my-pi/omptype";
16
+ import { type ApiKeyCredential, type AuthCredential, type AuthCredentialSnapshotEntry, type DisabledCredentialSummary, type OAuthCredential, type RemoteOAuthCredential, type SnapshotCredential } from "../auth-storage.js";
17
+ import type { ClientUsageReportRequest, ClientUsageReportResponse, ClientUsageSummaryResponse, CredentialBlockRequest, CredentialBlockResponse, CredentialBlockSnapshot, CredentialBlocksDeleteResponse, CredentialDisableResponse, CredentialRefreshResponse, CredentialUploadRequest, CredentialUploadResponse, DisabledCredentialsResponse, HealthzResponse, RefresherSchedule, SnapshotEntry, SnapshotResponse, SnapshotStreamEntryEvent, SnapshotStreamEvent, SnapshotStreamRemovedEvent, SnapshotStreamSnapshotEvent, UsageHistoryResponse, UsageResponse, UsageStaleResponse } from "./types.js";
18
+ /** Real OAuth credential (broker-side) — refresh token is the actual upstream value. */
19
+ export declare const oauthCredentialSchema: FluentType<OAuthCredential>;
20
+ /** OAuth credential as it appears in broker snapshots — refresh replaced with sentinel. */
21
+ export declare const remoteOauthCredentialSchema: FluentType<RemoteOAuthCredential>;
22
+ export declare const apiKeyCredentialSchema: FluentType<ApiKeyCredential>;
23
+ /** Discriminated union accepted on POST /v1/credential (writes). */
24
+ export declare const writableAuthCredentialSchema: FluentType<AuthCredential>;
25
+ /** Discriminated union returned in snapshots (refresh is sentinel for OAuth). */
26
+ export declare const snapshotCredentialSchema: FluentType<SnapshotCredential>;
27
+ export declare const credentialSnapshotEntrySchema: FluentType<AuthCredentialSnapshotEntry>;
28
+ export declare const credentialBlockSnapshotSchema: FluentType<CredentialBlockSnapshot>;
29
+ export declare const snapshotEntrySchema: FluentType<SnapshotEntry>;
30
+ export declare const refresherScheduleSchema: FluentType<RefresherSchedule>;
31
+ export declare const snapshotResponseSchema: FluentType<SnapshotResponse>;
32
+ /** First frame on connect — full snapshot embedded inline with a `kind` tag. */
33
+ export declare const snapshotStreamSnapshotEventSchema: FluentType<SnapshotStreamSnapshotEvent>;
34
+ /** Per-credential upsert/refresh delta. */
35
+ export declare const snapshotStreamEntryEventSchema: FluentType<SnapshotStreamEntryEvent>;
36
+ /** Per-credential delete delta. */
37
+ export declare const snapshotStreamRemovedEventSchema: FluentType<SnapshotStreamRemovedEvent>;
38
+ /** Discriminated union over every event frame the snapshot stream emits. */
39
+ export declare const snapshotStreamEventSchema: FluentType<SnapshotStreamEvent>;
40
+ export declare const healthzResponseSchema: FluentType<HealthzResponse>;
41
+ /**
42
+ * Broker `/v1/usage` response. Reports are full {@link UsageReport}s minus the
43
+ * heavy provider-specific `raw` field (the server strips it before send) — we
44
+ * keep `raw` optional in the underlying schema so a misconfigured broker that
45
+ * forgot to strip still validates.
46
+ */
47
+ export declare const usageResponseSchema: FluentType<UsageResponse>;
48
+ /** Broker `/v1/usage/history` response — recorded usage-limit snapshots, oldest first. */
49
+ export declare const usageHistoryResponseSchema: FluentType<UsageHistoryResponse>;
50
+ /** Broker `POST /v1/usage/observed` request — one client's batched observed usage. */
51
+ export declare const clientUsageReportRequestSchema: FluentType<ClientUsageReportRequest>;
52
+ export declare const clientUsageReportResponseSchema: FluentType<ClientUsageReportResponse>;
53
+ /** Broker `GET /v1/usage/clients` response — per-client token burn aggregates. */
54
+ export declare const clientUsageSummaryResponseSchema: FluentType<ClientUsageSummaryResponse>;
55
+ export declare const credentialRefreshResponseSchema: FluentType<CredentialRefreshResponse>;
56
+ export declare const credentialDisableRequestSchema: FluentType<{
2
57
  cause?: string;
3
- }, {
4
- cause?: string;
5
- }>, credentialDisableResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialDisableResponse, import("./types.js").CredentialDisableResponse>, disabledCredentialSummarySchema: import("@oh-my-pi/omptype").Type<import("../index.js").DisabledCredentialSummary, import("../index.js").DisabledCredentialSummary>, disabledCredentialsResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").DisabledCredentialsResponse, import("./types.js").DisabledCredentialsResponse>, credentialBlockRequestSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialBlockSnapshot, import("./types.js").CredentialBlockSnapshot>, credentialBlockResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialBlockResponse, import("./types.js").CredentialBlockResponse>, credentialBlocksDeleteResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialBlocksDeleteResponse, import("./types.js").CredentialBlocksDeleteResponse>, usageStaleResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").UsageStaleResponse, import("./types.js").UsageStaleResponse>, credentialUploadRequestSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialUploadRequest, import("./types.js").CredentialUploadRequest>, credentialUploadResponseSchema: import("@oh-my-pi/omptype").Type<import("./types.js").CredentialUploadResponse, import("./types.js").CredentialUploadResponse>;
58
+ }>;
59
+ export declare const credentialDisableResponseSchema: FluentType<CredentialDisableResponse>;
60
+ /** One disabled-credential tombstone identity + cause, never token material. */
61
+ export declare const disabledCredentialSummarySchema: FluentType<DisabledCredentialSummary>;
62
+ /** Broker `GET /v1/credentials/disabled` response. */
63
+ export declare const disabledCredentialsResponseSchema: FluentType<DisabledCredentialsResponse>;
64
+ export declare const credentialBlockRequestSchema: FluentType<CredentialBlockRequest>;
65
+ export declare const credentialBlockResponseSchema: FluentType<CredentialBlockResponse>;
66
+ export declare const credentialBlocksDeleteResponseSchema: FluentType<CredentialBlocksDeleteResponse>;
67
+ export declare const usageStaleResponseSchema: FluentType<UsageStaleResponse>;
68
+ export declare const credentialUploadRequestSchema: FluentType<CredentialUploadRequest>;
69
+ export declare const credentialUploadResponseSchema: FluentType<CredentialUploadResponse>;
@@ -44,6 +44,7 @@ export * from "./usage/xai-oauth.js";
44
44
  export * from "./usage/zai.js";
45
45
  export * from "./utils/anthropic-auth.js";
46
46
  export * from "./utils/event-stream.js";
47
+ export * from "./utils/glyph-codec.js";
47
48
  export * from "./utils/openrouter-headers.js";
48
49
  export * from "./utils/retry.js";
49
50
  export * from "./utils/schema/index.js";
@@ -0,0 +1,22 @@
1
+ import type { Context, CursorExecHandlers } from "../types.js";
2
+ import { AssistantMessageEventStream } from "./event-stream.js";
3
+ /** Per-request glyph codec applied at the provider wire boundary. */
4
+ export interface GlyphCodec {
5
+ /** Context with model-bound glyphs encoded and one conditional convention notice. */
6
+ context: Context;
7
+ /** Decodes glyph tokens in the stream's terminal assistant message. */
8
+ wrap(inner: AssistantMessageEventStream): AssistantMessageEventStream;
9
+ /** Decodes Cursor Pi calls and encodes their provider-bound results. */
10
+ wrapCursorExecHandlers(handlers: CursorExecHandlers): CursorExecHandlers;
11
+ /** Whether this request has encoded at least one glyph or literal token prefix. */
12
+ active: boolean;
13
+ }
14
+ /** Encode one string; returns the same reference when nothing changed. */
15
+ export declare function encodeGlyphText(text: string): string;
16
+ /** Decodes glyph tokens and unescapes `⟦E⟧` to `⟦`. */
17
+ export declare function decodeGlyphText(text: string): string;
18
+ /**
19
+ * Encode a context for the provider wire without mutating stored history.
20
+ * Reapplying it to the returned branded context is an inert identity pass.
21
+ */
22
+ export declare function applyGlyphCodec(context: Context): GlyphCodec;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-ai",
4
- "version": "17.4.2",
4
+ "version": "18.0.0",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Stencil Labs, Inc.",
@@ -37,10 +37,10 @@
37
37
  "fmt": "biome format --write ."
38
38
  },
39
39
  "dependencies": {
40
- "@oh-my-pi/omptype": "17.4.2",
41
- "@oh-my-pi/pi-catalog": "17.4.2",
42
- "@oh-my-pi/pi-utils": "17.4.2",
43
- "@oh-my-pi/pi-wire": "17.4.2"
40
+ "@oh-my-pi/omptype": "18.0.0",
41
+ "@oh-my-pi/pi-catalog": "18.0.0",
42
+ "@oh-my-pi/pi-utils": "18.0.0",
43
+ "@oh-my-pi/pi-wire": "18.0.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/bun": "^1.3.14"
@@ -30,21 +30,40 @@ import type {
30
30
  UsageStaleResponse,
31
31
  } from "./types";
32
32
  import { AUTH_BROKER_CAPABILITIES_HEADER, AUTH_BROKER_CAPABILITY_CODEX_METER_BLOCK_SCOPES } from "./types";
33
- import { getAuthBrokerWireSchemas } from "./wire-schema-resource";
34
-
35
- type AuthBrokerResponseSchemaName =
36
- | "clientUsageReportResponseSchema"
37
- | "clientUsageSummaryResponseSchema"
38
- | "credentialBlockResponseSchema"
39
- | "credentialBlocksDeleteResponseSchema"
40
- | "credentialDisableResponseSchema"
41
- | "credentialRefreshResponseSchema"
42
- | "credentialUploadResponseSchema"
43
- | "disabledCredentialsResponseSchema"
44
- | "healthzResponseSchema"
45
- | "usageHistoryResponseSchema"
46
- | "usageResponseSchema"
47
- | "usageStaleResponseSchema";
33
+ import {
34
+ clientUsageReportResponseSchema,
35
+ clientUsageSummaryResponseSchema,
36
+ credentialBlockResponseSchema,
37
+ credentialBlocksDeleteResponseSchema,
38
+ credentialDisableResponseSchema,
39
+ credentialRefreshResponseSchema,
40
+ credentialUploadResponseSchema,
41
+ disabledCredentialsResponseSchema,
42
+ healthzResponseSchema,
43
+ snapshotResponseSchema,
44
+ snapshotStreamEventSchema,
45
+ usageHistoryResponseSchema,
46
+ usageResponseSchema,
47
+ usageStaleResponseSchema,
48
+ } from "./wire-schemas";
49
+
50
+ /** Response schema per endpoint, keyed by the name `#request` callers pass. */
51
+ const RESPONSE_SCHEMAS = {
52
+ clientUsageReportResponseSchema,
53
+ clientUsageSummaryResponseSchema,
54
+ credentialBlockResponseSchema,
55
+ credentialBlocksDeleteResponseSchema,
56
+ credentialDisableResponseSchema,
57
+ credentialRefreshResponseSchema,
58
+ credentialUploadResponseSchema,
59
+ disabledCredentialsResponseSchema,
60
+ healthzResponseSchema,
61
+ usageHistoryResponseSchema,
62
+ usageResponseSchema,
63
+ usageStaleResponseSchema,
64
+ } as const;
65
+
66
+ type AuthBrokerResponseSchemaName = keyof typeof RESPONSE_SCHEMAS;
48
67
 
49
68
  export interface AuthBrokerClientOptions {
50
69
  /** Base URL (e.g. `https://broker.tailnet:8765`). Trailing slashes are trimmed. */
@@ -155,7 +174,7 @@ export class AuthBrokerClient {
155
174
  }
156
175
  const text = await response.text();
157
176
  const raw = this.#parseJson(text, response.status);
158
- const validated = getAuthBrokerWireSchemas().snapshotResponseSchema(raw);
177
+ const validated = snapshotResponseSchema(raw);
159
178
  if (validated instanceof type.errors) {
160
179
  throw new AuthBrokerError("Auth broker response failed schema validation", {
161
180
  status: response.status,
@@ -224,7 +243,7 @@ export class AuthBrokerClient {
224
243
  cause: err,
225
244
  });
226
245
  }
227
- const validated = getAuthBrokerWireSchemas().snapshotStreamEventSchema(parsed);
246
+ const validated = snapshotStreamEventSchema(parsed);
228
247
  if (validated instanceof type.errors) {
229
248
  throw new AuthBrokerError("Auth broker stream event failed schema validation", {
230
249
  body: validated.summary,
@@ -391,7 +410,7 @@ export class AuthBrokerClient {
391
410
  const response = await this.#fetchRaw(method, path, opts);
392
411
  const text = await response.text();
393
412
  const raw = this.#parseJson(text, response.status);
394
- const validated = getAuthBrokerWireSchemas()[opts.schema](raw);
413
+ const validated = RESPONSE_SCHEMAS[opts.schema](raw);
395
414
  if (validated instanceof type.errors) {
396
415
  throw new AuthBrokerError("Auth broker response failed schema validation", {
397
416
  status: response.status,
@@ -41,7 +41,12 @@ import {
41
41
  DEFAULT_SERVER_IDLE_TIMEOUT_S,
42
42
  DEFAULT_STREAM_KEEPALIVE_MS,
43
43
  } from "./types";
44
- import { getAuthBrokerWireSchemas } from "./wire-schema-resource";
44
+ import {
45
+ clientUsageReportRequestSchema,
46
+ credentialBlockRequestSchema,
47
+ credentialDisableRequestSchema,
48
+ credentialUploadRequestSchema,
49
+ } from "./wire-schemas";
45
50
 
46
51
  const DEFAULT_EXTERNAL_CHANGE_POLL_MS = 250;
47
52
 
@@ -712,7 +717,7 @@ export function startAuthBroker(opts: AuthBrokerServerOptions): AuthBrokerServer
712
717
  return json(200, { generatedAt: Date.now(), entries });
713
718
  }
714
719
  if (req.method === "POST" && pathname === "/v1/usage/observed") {
715
- const parsed = await parseBody(req, getAuthBrokerWireSchemas().clientUsageReportRequestSchema);
720
+ const parsed = await parseBody(req, clientUsageReportRequestSchema);
716
721
  if (!parsed.ok) return parsed.response;
717
722
  // Arktype's inferred union collides the `entries` field with
718
723
  // Array.prototype.entries; the schema already validated the shape.
@@ -779,7 +784,7 @@ export function startAuthBroker(opts: AuthBrokerServerOptions): AuthBrokerServer
779
784
  const disableMatch = req.method === "POST" ? pathname.match(DISABLE_ROUTE) : null;
780
785
  if (disableMatch) {
781
786
  const id = Number.parseInt(disableMatch[1], 10);
782
- const parsed = await parseBody(req, getAuthBrokerWireSchemas().credentialDisableRequestSchema, {
787
+ const parsed = await parseBody(req, credentialDisableRequestSchema, {
783
788
  allowEmpty: true,
784
789
  });
785
790
  if (!parsed.ok) return parsed.response;
@@ -797,7 +802,7 @@ export function startAuthBroker(opts: AuthBrokerServerOptions): AuthBrokerServer
797
802
  const blockMatch = req.method === "POST" ? pathname.match(BLOCK_ROUTE) : null;
798
803
  if (blockMatch) {
799
804
  const id = Number.parseInt(blockMatch[1], 10);
800
- const parsed = await parseBody(req, getAuthBrokerWireSchemas().credentialBlockRequestSchema);
805
+ const parsed = await parseBody(req, credentialBlockRequestSchema);
801
806
  if (!parsed.ok) return parsed.response;
802
807
  const block: StoredCredentialBlock = {
803
808
  credentialId: id,
@@ -847,7 +852,7 @@ export function startAuthBroker(opts: AuthBrokerServerOptions): AuthBrokerServer
847
852
  }
848
853
  }
849
854
  if (req.method === "POST" && pathname === "/v1/credential") {
850
- const parsed = await parseBody(req, getAuthBrokerWireSchemas().credentialUploadRequestSchema);
855
+ const parsed = await parseBody(req, credentialUploadRequestSchema);
851
856
  if (!parsed.ok) return parsed.response;
852
857
  const { provider, credential } = parsed.data;
853
858
  try {