jeopi-ai 16.2.26 → 16.2.27

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.2.27] - 2026-07-07
6
+
7
+ ### Fixed
8
+
9
+ - Leaked reasoning healing (`ThinkingInbandScanner`, and the owned `AnthropicInbandScanner`/`xml` dialect scanner) required an exact `<think>`/`<thinking>`/`<scratchpad>` tag name, so a model that consistently hallucinates a one-character-off spelling for both its open and close tag (e.g. `<thinke>...</thinke>`) leaked the entire block — tag and body — into the visible reply verbatim instead of collapsing into a thinking section. Both scanners now recognize any bare `<name>` whose name is within one character edit of a canonical thinking tag name and shares its prefix (`thinke`, `thinkin`, `scratchpaid`, …), while unrelated short words (`thing`, `thin`, `<div>`, …) are unaffected.
10
+ - Cloud Code Assist (`google-gemini-cli`/`google-antigravity`) buffers a leaked raw tool-planning JSON object (`{"thought": "…", "call": "read", "paths": […]}`, mirroring jeopi's own tool-argument shapes) out of the visible text stream instead of a structured `functionCall` — but the buffer was gated to `model.id.includes("flash")`, so every non-flash id was unprotected. That includes the `google-antigravity` default model (`gemini-3.1-pro`), other Gemini-3 pro/agent tiers, and Claude-on-antigravity ids: the identical leak rendered straight into the TUI as raw JSON on every occurrence instead of being intercepted. The buffer now applies unconditionally across the whole Cloud Code Assist surface; the underlying `isPlanningLeakPrefix`/`isPlanningLeakObject` heuristics only fire on a leading `{"thought"…}`-shaped object, so this carries no additional false-positive risk for other model families.
11
+ - `getOpenRouterHeaders()` identified every OpenRouter-routed request (chat completions and, via a duplicated inline copy in the coding-agent `image-gen` tool, image generation) as `User-Agent: Oh-My-Pi/…`, `HTTP-Referer: https://omp.sh/`, `X-OpenRouter-Title: Oh-My-Pi` — a leftover from before the fork went npm-independent, misattributing every jeopi user's OpenRouter traffic, analytics, and app rankings to the upstream project. Now sends `jeopi/<version>`, the fork's GitHub homepage, and `X-OpenRouter-Title: jeopi`; `image-gen.ts`'s duplicated header block was replaced with a call to the shared helper.
12
+ - `RemoteAuthCredentialStore`'s three mutation-refusal error messages (`replaceAuthCredentialsForProvider`, `upsertAuthCredentialForProvider`, `deleteAuthCredentialsForProvider`) told users to run `omp auth-broker login`/`omp auth-broker logout` — a command that doesn't exist; the real CLI is `jeopi auth-broker login <provider>`/`jeopi auth-broker logout <provider>`. Doc comments across `stream.ts`, `auth-broker/client.ts`, `auth-broker/discover.ts`, `auth-gateway/server.ts`, `auth-gateway/types.ts`, and `providers/pi-native-client.ts` carried the same stale `omp auth-broker`/`omp auth-gateway` command examples and product references. Fixed all to `jeopi`.
13
+
5
14
  ## [16.2.26] - 2026-07-05
6
15
 
7
16
  ### Fixed
package/README.md CHANGED
@@ -970,7 +970,7 @@ and optional mTLS material (`CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`)
970
970
  `NODE_EXTRA_CA_CERTS` (PEM file path or inline PEM, mirroring Node's contract)
971
971
  is honoured on every provider fetch — OpenAI-compatible, Codex, Ollama, Azure
972
972
  Responses, Google, and Anthropic alike — for corporate relays or private CA
973
- bundles. Bun's `fetch` does not consume the env var natively, so omp injects
973
+ bundles. Bun's `fetch` does not consume the env var natively, so jeopi injects
974
974
  the bundle into `RequestInit.tls.ca` and seeds the system root store
975
975
  alongside it.
976
976
 
@@ -1068,14 +1068,14 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a
1068
1068
 
1069
1069
  ### CLI Login
1070
1070
 
1071
- Authenticate via the [`omp`](https://omp.sh) coding-agent CLI, which drives this library's OAuth/API-key flows in-process and persists into `agent.db`:
1071
+ Authenticate via the [`jeopi`](https://github.com/akillness/jeopi) coding-agent CLI, which drives this library's OAuth/API-key flows in-process and persists into `agent.db`:
1072
1072
 
1073
1073
  ```bash
1074
- omp auth-broker login # interactive provider selection
1075
- omp auth-broker login anthropic # login to a specific provider
1076
- omp auth-broker login vllm # store vLLM API key (or placeholder for local no-auth)
1077
- omp auth-broker list # list supported providers
1078
- omp auth-broker logout # interactive — pick a stored credential to remove
1074
+ jeopi auth-broker login # interactive provider selection
1075
+ jeopi auth-broker login anthropic # login to a specific provider
1076
+ jeopi auth-broker login vllm # store vLLM API key (or placeholder for local no-auth)
1077
+ jeopi auth-broker list # list supported providers
1078
+ jeopi auth-broker logout # interactive — pick a stored credential to remove
1079
1079
  ```
1080
1080
 
1081
1081
  Credentials are saved to `agent.db` in the agent directory. `/login qianfan` opens the Qianfan console and stores the pasted API key.
@@ -1,8 +1,8 @@
1
1
  /**
2
- * HTTP client for the omp auth-broker server.
2
+ * HTTP client for the jeopi auth-broker server.
3
3
  *
4
4
  * Used by {@link RemoteAuthCredentialStore} (snapshot pulls) and by
5
- * `omp auth-broker status` (liveness checks). All endpoints except
5
+ * `jeopi auth-broker status` (liveness checks). All endpoints except
6
6
  * `/v1/healthz` require a bearer token.
7
7
  */
8
8
  import type { AuthCredential } from "../auth-storage";
@@ -13,7 +13,7 @@ export interface DiscoverAuthStorageOptions {
13
13
  cachePath?: string;
14
14
  sourceLabel?: string;
15
15
  }
16
- /** Path to the local bearer token file. Created by `omp auth-broker token`. */
16
+ /** Path to the local bearer token file. Created by `jeopi auth-broker token`. */
17
17
  export declare function getAuthBrokerTokenFilePath(): string;
18
18
  /**
19
19
  * Resolve broker connection configuration using the same precedence as the TUI:
@@ -1,12 +1,12 @@
1
1
  /**
2
- * omp auth-gateway HTTP server.
2
+ * jeopi auth-gateway HTTP server.
3
3
  *
4
4
  * Accepts any provider-format request (OpenAI chat-completions, Anthropic
5
5
  * messages, OpenAI Responses) and dispatches through pi-ai's `streamSimple()`
6
6
  * — which handles credential injection, anthropic-beta headers, codex
7
7
  * websocket transport, and all the per-provider intricacies. The gateway is
8
- * pure protocol translation: foreign wire → omp Context → pi-ai stream() →
9
- * omp events → foreign wire.
8
+ * pure protocol translation: foreign wire → jeopi Context → pi-ai stream() →
9
+ * jeopi events → foreign wire.
10
10
  *
11
11
  * Endpoints:
12
12
  * GET /healthz → unauth; ok + version
@@ -1,9 +1,9 @@
1
1
  import type { Effort } from "jeopi-catalog/effort";
2
2
  import type { AssistantMessage, AssistantMessageEventStream, CacheRetention, Context, ServiceTier, TokenTaskBudget } from "../types";
3
3
  /**
4
- * Wire types for the omp auth-gateway.
4
+ * Wire types for the jeopi auth-gateway.
5
5
  *
6
- * The gateway sits between unauthenticated clients (containerized omp,
6
+ * The gateway sits between unauthenticated clients (containerized jeopi,
7
7
  * llm-git, …) and the broker. It accepts provider-format HTTP requests
8
8
  * (OpenAI chat-completions / Anthropic messages / OpenAI Responses),
9
9
  * dispatches them through pi-ai's `streamSimple()`, and translates the
@@ -1,4 +1,12 @@
1
1
  import type { InbandScanEvent, InbandScanner } from "./types";
2
+ /**
3
+ * True for the canonical thinking tag names, plus names within one character
4
+ * edit of `think`/`thinking`/`scratchpad` that also share that name's prefix —
5
+ * the shape of a real hallucinated typo (`thinke`, `thinkin`, `scratchpaid`),
6
+ * not an unrelated short word (`thing`, `thin`, `chink` all fail the prefix
7
+ * guard despite being edit-distance 1 from `think`).
8
+ */
9
+ export declare function isThinkingTagName(name: string): boolean;
2
10
  export declare class ThinkingInbandScanner implements InbandScanner {
3
11
  #private;
4
12
  feed(text: string): InbandScanEvent[];
@@ -1,6 +1,6 @@
1
1
  import type { Api, AssistantMessageEventStream as AssistantMessageEventStreamType, Context, Model, SimpleStreamOptions } from "../types";
2
2
  /**
3
- * Stream a turn through an `omp auth-gateway` over the pi-native protocol.
3
+ * Stream a turn through a `jeopi auth-gateway` over the pi-native protocol.
4
4
  *
5
5
  * The returned {@link AssistantMessageEventStream} receives each parsed
6
6
  * `AssistantMessageEvent` verbatim from the gateway; the terminal `done` /
@@ -30,7 +30,7 @@ export declare function getEnvApiKey(provider: string): string | undefined;
30
30
  export declare function getEnvApiKeyName(provider: string): string | undefined;
31
31
  /**
32
32
  * Enumerate every provider that has an env-var fallback for `getEnvApiKey`.
33
- * Used by `omp auth-broker migrate --include-env` to discover env-sourced keys
33
+ * Used by `jeopi auth-broker migrate --include-env` to discover env-sourced keys
34
34
  * that should be uploaded to the broker.
35
35
  */
36
36
  export declare function listProvidersWithEnvKey(): string[];
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "jeopi-ai",
4
- "version": "16.2.26",
4
+ "version": "16.2.27",
5
5
  "description": "Unified LLM API with automatic model discovery and provider configuration",
6
6
  "homepage": "https://github.com/akillness/jeopi",
7
7
  "author": "Can Boluk",
@@ -38,9 +38,9 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@bufbuild/protobuf": "^2.12.0",
41
- "jeopi-catalog": "16.2.26",
42
- "jeopi-utils": "16.2.26",
43
- "jeopi-wire": "16.2.26",
41
+ "jeopi-catalog": "16.2.27",
42
+ "jeopi-utils": "16.2.27",
43
+ "jeopi-wire": "16.2.27",
44
44
  "arktype": "^2.2.0",
45
45
  "zod": "^4"
46
46
  },
@@ -1,8 +1,8 @@
1
1
  /**
2
- * HTTP client for the omp auth-broker server.
2
+ * HTTP client for the jeopi auth-broker server.
3
3
  *
4
4
  * Used by {@link RemoteAuthCredentialStore} (snapshot pulls) and by
5
- * `omp auth-broker status` (liveness checks). All endpoints except
5
+ * `jeopi auth-broker status` (liveness checks). All endpoints except
6
6
  * `/v1/healthz` require a bearer token.
7
7
  */
8
8
 
@@ -38,7 +38,7 @@ export interface DiscoverAuthStorageOptions {
38
38
  sourceLabel?: string;
39
39
  }
40
40
 
41
- /** Path to the local bearer token file. Created by `omp auth-broker token`. */
41
+ /** Path to the local bearer token file. Created by `jeopi auth-broker token`. */
42
42
  export function getAuthBrokerTokenFilePath(): string {
43
43
  return path.join(getConfigRootDir(), "auth-broker.token");
44
44
  }
@@ -328,19 +328,19 @@ export class RemoteAuthCredentialStore implements AuthCredentialStore {
328
328
 
329
329
  replaceAuthCredentialsForProvider(_provider: string, _credentials: AuthCredential[]): StoredAuthCredential[] {
330
330
  throw new AIError.AuthBrokerError(
331
- "RemoteAuthCredentialStore is read-only on the client. Use `omp auth-broker login <provider>` to mutate credentials.",
331
+ "RemoteAuthCredentialStore is read-only on the client. Use `jeopi auth-broker login <provider>` to mutate credentials.",
332
332
  );
333
333
  }
334
334
 
335
335
  upsertAuthCredentialForProvider(_provider: string, _credential: AuthCredential): StoredAuthCredential[] {
336
336
  throw new AIError.AuthBrokerError(
337
- "RemoteAuthCredentialStore is read-only on the client. Use `omp auth-broker login <provider>` to mutate credentials.",
337
+ "RemoteAuthCredentialStore is read-only on the client. Use `jeopi auth-broker login <provider>` to mutate credentials.",
338
338
  );
339
339
  }
340
340
 
341
341
  deleteAuthCredentialsForProvider(_provider: string, _disabledCause: string): void {
342
342
  throw new AIError.AuthBrokerError(
343
- "RemoteAuthCredentialStore is read-only on the client. Use `omp auth-broker logout <provider>` to mutate credentials.",
343
+ "RemoteAuthCredentialStore is read-only on the client. Use `jeopi auth-broker logout <provider>` to mutate credentials.",
344
344
  );
345
345
  }
346
346
 
@@ -1,12 +1,12 @@
1
1
  /**
2
- * omp auth-gateway HTTP server.
2
+ * jeopi auth-gateway HTTP server.
3
3
  *
4
4
  * Accepts any provider-format request (OpenAI chat-completions, Anthropic
5
5
  * messages, OpenAI Responses) and dispatches through pi-ai's `streamSimple()`
6
6
  * — which handles credential injection, anthropic-beta headers, codex
7
7
  * websocket transport, and all the per-provider intricacies. The gateway is
8
- * pure protocol translation: foreign wire → omp Context → pi-ai stream() →
9
- * omp events → foreign wire.
8
+ * pure protocol translation: foreign wire → jeopi Context → pi-ai stream() →
9
+ * jeopi events → foreign wire.
10
10
  *
11
11
  * Endpoints:
12
12
  * GET /healthz → unauth; ok + version
@@ -9,9 +9,9 @@ import type {
9
9
  } from "../types";
10
10
 
11
11
  /**
12
- * Wire types for the omp auth-gateway.
12
+ * Wire types for the jeopi auth-gateway.
13
13
  *
14
- * The gateway sits between unauthenticated clients (containerized omp,
14
+ * The gateway sits between unauthenticated clients (containerized jeopi,
15
15
  * llm-git, …) and the broker. It accepts provider-format HTTP requests
16
16
  * (OpenAI chat-completions / Anthropic messages / OpenAI Responses),
17
17
  * dispatches them through pi-ai's `streamSimple()`, and translates the
@@ -9,6 +9,7 @@ import {
9
9
  renderLegacyTextTranscript,
10
10
  stringifyJson,
11
11
  } from "./rendering";
12
+ import { isThinkingTagName } from "./thinking";
12
13
  import type {
13
14
  DialectDefinition,
14
15
  DialectRenderOptions,
@@ -22,7 +23,6 @@ const MAX_PARTIAL_TAG_LENGTH = 256;
22
23
  const MAX_PARAMETER_VALUE_LENGTH = 1_000_000;
23
24
 
24
25
  const WRAPPER_TAGS: Readonly<Record<string, true>> = { function_calls: true, tool_calls: true };
25
- const THINKING_TAGS: Record<string, true> = { thinking: true, think: true, scratchpad: true };
26
26
  const BASE_TAG_PREFIXES = [
27
27
  "<function_calls",
28
28
  "</function_calls",
@@ -217,7 +217,7 @@ export class AnthropicInbandScanner implements InbandScanner {
217
217
  this.#startInvoke(tag, "section", events);
218
218
  return true;
219
219
  }
220
- if (this.#parseThinking && !tag.closing && THINKING_TAGS[tag.localName] === true) {
220
+ if (this.#parseThinking && !tag.closing && isThinkingTagName(tag.localName)) {
221
221
  this.#startThinking(tag, "section", events);
222
222
  }
223
223
  return true;
@@ -480,7 +480,7 @@ export class AnthropicInbandScanner implements InbandScanner {
480
480
 
481
481
  #isThinkingOpen(tag: ParsedTag): boolean {
482
482
  if (!this.#parseThinking || tag.closing) return false;
483
- return THINKING_TAGS[tag.localName] === true;
483
+ return isThinkingTagName(tag.localName);
484
484
  }
485
485
 
486
486
  #relevantPrefixes(): readonly string[] {
@@ -14,17 +14,92 @@ type Tag = { readonly open: string; readonly close: string; readonly fenced?: bo
14
14
  * emits and what models leak in practice. Attributed or namespaced XML thinking
15
15
  * tags (`<thinking signature="…">`, `antml:thinking`) are recovered by the owned
16
16
  * anthropic-dialect parser, not this text-channel healing fallback.
17
+ *
18
+ * `<think>`/`<thinking>`/`<scratchpad>` are additionally matched with one-typo
19
+ * tolerance ({@link isThinkingTagName}) — weaker models occasionally hallucinate
20
+ * a near-miss spelling (`<thinke>`, `<thinkin>`) consistently for both the open
21
+ * and close tag on long turns; without this the whole malformed pair leaks
22
+ * verbatim into the visible channel instead of collapsing into a thinking block.
17
23
  */
18
24
  const TAGS: readonly Tag[] = [
19
- { open: "<think>", close: "</think>" }, // deepseek, glm, hermes, kimi, qwen3 (and anthropic/minimax/xml)
20
- { open: "<thinking>", close: "</thinking>" }, // anthropic, minimax, xml
21
- { open: "<scratchpad>", close: "</scratchpad>" }, // anthropic
22
25
  { open: "```thinking\n", close: "```", fenced: true }, // gemini fenced thinking
23
26
  { open: "<|channel>thought\n", close: "<channel|>" }, // gemma reasoning channel
24
27
  { open: "<|start|>assistant<|channel|>analysis<|message|>", close: "<|end|>" }, // harmony analysis (rendered)
25
28
  { open: "<|channel|>analysis<|message|>", close: "<|end|>" }, // harmony analysis (bare leak)
26
29
  ];
27
30
  const OPENS = TAGS.map(tag => tag.open);
31
+ /** Longest bare `<name>` this scanner ever recognizes; bounds partial-tag holding. */
32
+ const MAX_BARE_TAG_NAME_LENGTH = 20;
33
+ const BARE_TAG_OPEN_PATTERN = /<([A-Za-z][A-Za-z0-9]{0,19})>/g;
34
+ const BARE_TAG_PARTIAL_PATTERN = /<(?:[A-Za-z][A-Za-z0-9]{0,19})?$/;
35
+
36
+ /**
37
+ * True for the canonical thinking tag names, plus names within one character
38
+ * edit of `think`/`thinking`/`scratchpad` that also share that name's prefix —
39
+ * the shape of a real hallucinated typo (`thinke`, `thinkin`, `scratchpaid`),
40
+ * not an unrelated short word (`thing`, `thin`, `chink` all fail the prefix
41
+ * guard despite being edit-distance 1 from `think`).
42
+ */
43
+ export function isThinkingTagName(name: string): boolean {
44
+ const lower = name.toLowerCase();
45
+ if (lower === "thinking" || lower === "think" || lower === "scratchpad") return true;
46
+ if (lower.startsWith("think") && levenshteinAtMost(lower, "think", 1)) return true;
47
+ if (lower.startsWith("thinking") && levenshteinAtMost(lower, "thinking", 1)) return true;
48
+ if (lower.startsWith("scratchpad") && levenshteinAtMost(lower, "scratchpad", 1)) return true;
49
+ return false;
50
+ }
51
+
52
+ /** Bounded Levenshtein distance check; avoids the full DP once `max` is exceeded. */
53
+ function levenshteinAtMost(a: string, b: string, max: number): boolean {
54
+ if (Math.abs(a.length - b.length) > max) return false;
55
+ const prev = new Array<number>(b.length + 1);
56
+ const curr = new Array<number>(b.length + 1);
57
+ for (let j = 0; j <= b.length; j++) prev[j] = j;
58
+ for (let i = 1; i <= a.length; i++) {
59
+ curr[0] = i;
60
+ let rowMin = curr[0]!;
61
+ for (let j = 1; j <= b.length; j++) {
62
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
63
+ curr[j] = Math.min(prev[j]! + 1, curr[j - 1]! + 1, prev[j - 1]! + cost);
64
+ rowMin = Math.min(rowMin, curr[j]!);
65
+ }
66
+ if (rowMin > max) return false;
67
+ for (let j = 0; j <= b.length; j++) prev[j] = curr[j]!;
68
+ }
69
+ return prev[b.length]! <= max;
70
+ }
71
+
72
+ /**
73
+ * Earliest bare `<name>` tag (no attributes) whose name passes
74
+ * {@link isThinkingTagName}, paired with its matching `</name>` close. Runs
75
+ * alongside the literal {@link TAGS} idioms so `<think>`/`<thinking>`/
76
+ * `<scratchpad>` and their one-typo variants share a single fuzzy path.
77
+ */
78
+ function findBareThinkingOpen(buffer: string): (Tag & { index: number }) | undefined {
79
+ for (const match of buffer.matchAll(BARE_TAG_OPEN_PATTERN)) {
80
+ const name = match[1]!;
81
+ if (!isThinkingTagName(name)) continue;
82
+ return { open: match[0], close: `</${name}>`, index: match.index };
83
+ }
84
+ return undefined;
85
+ }
86
+
87
+ /**
88
+ * Holds back a buffer tail shaped like an unterminated bare tag (`<`,
89
+ * optionally followed by letters/digits, no `>` yet) so a diverging spelling
90
+ * isn't flushed as visible text one character before its typo would have
91
+ * matched {@link isThinkingTagName} — e.g. `<thinke` must survive until the
92
+ * closing `>` arrives and resolves it. Bounded by
93
+ * {@link MAX_BARE_TAG_NAME_LENGTH} so an angle bracket in ordinary prose
94
+ * can't stall the stream indefinitely.
95
+ */
96
+ function bareTagPartialHold(buffer: string): number {
97
+ const openIndex = buffer.lastIndexOf("<");
98
+ if (openIndex === -1) return 0;
99
+ const tail = buffer.slice(openIndex);
100
+ if (tail.length > MAX_BARE_TAG_NAME_LENGTH + 1) return 0;
101
+ return BARE_TAG_PARTIAL_PATTERN.test(tail) ? tail.length : 0;
102
+ }
28
103
 
29
104
  export class ThinkingInbandScanner implements InbandScanner {
30
105
  #buffer = "";
@@ -89,7 +164,9 @@ export class ThinkingInbandScanner implements InbandScanner {
89
164
 
90
165
  const tag = findEarliestOpen(this.#buffer);
91
166
  if (!tag) {
92
- const hold = final ? 0 : partialSuffixOverlapAny(this.#buffer, OPENS);
167
+ const hold = final
168
+ ? 0
169
+ : Math.max(partialSuffixOverlapAny(this.#buffer, OPENS), bareTagPartialHold(this.#buffer));
93
170
  const emit = this.#buffer.slice(0, this.#buffer.length - hold);
94
171
  if (emit.length > 0) events.push({ type: "text", text: emit });
95
172
  this.#buffer = this.#buffer.slice(this.#buffer.length - hold);
@@ -118,5 +195,7 @@ function findEarliestOpen(buffer: string): (Tag & { index: number }) | undefined
118
195
  const index = buffer.indexOf(tag.open);
119
196
  if (index !== -1 && (!best || index < best.index)) best = { ...tag, index };
120
197
  }
198
+ const bare = findBareThinkingOpen(buffer);
199
+ if (bare && (!best || bare.index < best.index)) best = bare;
121
200
  return best;
122
201
  }
@@ -715,7 +715,21 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
715
715
  options?.streamFirstEventTimeoutMs ?? getStreamFirstEventTimeoutMs(undefined, 300_000);
716
716
  const callerSignal = options?.signal;
717
717
  const toolNames = new Set(context.tools?.map(t => t.name) ?? []);
718
- const isFlashLeakModel = model.id.includes("flash");
718
+ // Cloud Code Assist (both `google-gemini-cli` and `google-antigravity`,
719
+ // covering Gemini and Claude-on-antigravity ids alike) can leak its raw
720
+ // tool-planning JSON (`{"thought": "…", "call": "read", "paths": […]}`,
721
+ // mirroring jeopi's own tool-argument shapes) into the visible text
722
+ // channel instead of emitting a structured `functionCall`. This was
723
+ // first captured and fixed for `gemini-3.5-flash`, but the gate on
724
+ // `model.id.includes("flash")` left every non-flash id (including the
725
+ // `google-antigravity` default model `gemini-3.1-pro`, other Gemini-3
726
+ // pro/flash-agent tiers, and Claude-on-antigravity ids) unprotected —
727
+ // so the same leak still renders straight into the TUI as raw JSON
728
+ // text on every occurrence for those models. `isPlanningLeakPrefix`/
729
+ // `isPlanningLeakObject` are narrow, shape-specific heuristics (they
730
+ // only fire on a leading `{"thought"…}`-style object), so buffering is
731
+ // safe to apply unconditionally across this whole provider surface
732
+ // rather than gating it to one model family.
719
733
 
720
734
  let started = false;
721
735
  let sawFinishReason = false;
@@ -899,7 +913,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = (
899
913
  bufferedTextSignature,
900
914
  part.thoughtSignature,
901
915
  );
902
- } else if (isFlashLeakModel && part.text.trimStart().startsWith("{")) {
916
+ } else if (part.text.trimStart().startsWith("{")) {
903
917
  isBuffering = true;
904
918
  textBuffer = part.text;
905
919
  bufferedTextSignature = part.thoughtSignature;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Client half of the pi-native auth-gateway protocol.
3
3
  *
4
- * Dispatches a {@link streamSimple}-shaped request to an `omp auth-gateway`
4
+ * Dispatches a {@link streamSimple}-shaped request to a `jeopi auth-gateway`
5
5
  * via `POST /v1/pi/stream`, reads the SSE event stream back, and pushes the
6
6
  * parsed events into a local {@link AssistantMessageEventStream} — the same
7
7
  * stream type every other provider client produces. Callers downstream of
@@ -125,7 +125,7 @@ function buildHeaders(model: Model<Api>, apiKey: string | undefined): Record<str
125
125
  }
126
126
 
127
127
  /**
128
- * Stream a turn through an `omp auth-gateway` over the pi-native protocol.
128
+ * Stream a turn through a `jeopi auth-gateway` over the pi-native protocol.
129
129
  *
130
130
  * The returned {@link AssistantMessageEventStream} receives each parsed
131
131
  * `AssistantMessageEvent` verbatim from the gateway; the terminal `done` /
package/src/stream.ts CHANGED
@@ -686,7 +686,7 @@ export function getEnvApiKeyName(provider: string): string | undefined {
686
686
 
687
687
  /**
688
688
  * Enumerate every provider that has an env-var fallback for `getEnvApiKey`.
689
- * Used by `omp auth-broker migrate --include-env` to discover env-sourced keys
689
+ * Used by `jeopi auth-broker migrate --include-env` to discover env-sourced keys
690
690
  * that should be uploaded to the broker.
691
691
  */
692
692
  export function listProvidersWithEnvKey(): string[] {
@@ -2,9 +2,9 @@ import packageJson from "../../package.json" with { type: "json" };
2
2
 
3
3
  export function getOpenRouterHeaders(): Record<string, string> {
4
4
  return {
5
- "User-Agent": `Oh-My-Pi/${packageJson.version}`,
6
- "HTTP-Referer": "https://omp.sh/",
7
- "X-OpenRouter-Title": "Oh-My-Pi",
5
+ "User-Agent": `jeopi/${packageJson.version}`,
6
+ "HTTP-Referer": packageJson.homepage,
7
+ "X-OpenRouter-Title": "jeopi",
8
8
  "X-OpenRouter-Categories": "cli-agent",
9
9
  "X-OpenRouter-Cache": "true",
10
10
  "X-OpenRouter-Cache-TTL": "3600",