@chatpanel/bridge 0.11.5 → 0.11.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chatpanel/bridge",
3
- "version": "0.11.5",
3
+ "version": "0.11.6",
4
4
  "type": "module",
5
5
  "description": "Local bridge that exposes the AI coding agents installed on your machine \u2014 Claude Code (CLI), Codex (CLI), and Antigravity CLI (formerly Gemini CLI, which remains available for business/enterprise) \u2014 to the ChatPanel Chrome extension over a localhost SSE endpoint. Bring your own agent.",
6
6
  "keywords": [
@@ -52,6 +52,7 @@ export function startTelegram({
52
52
  // which it has: both expose chat()/cancel() and fold into one reply state.
53
53
  transport = bridge,
54
54
  model = '',
55
+ provider = '',
55
56
  system = '',
56
57
  redact = { tier: 'basic' },
57
58
  privacy = 'standard',
@@ -158,7 +159,7 @@ export function startTelegram({
158
159
 
159
160
  try {
160
161
  const finalState = await transport.chat(
161
- { agent, model, system, messages, images, options: { reach } },
162
+ { agent, model, provider, system, messages, images, options: { reach } },
162
163
  {
163
164
  baseUrl, token, signal,
164
165
  onEvent: (ev, state) => {
@@ -40,7 +40,7 @@ const inflight = new Map();
40
40
  * not know which transport it has: streams events to onEvent(ev, state) and returns the folded
41
41
  * final state.
42
42
  */
43
- export async function chat({ model = '', system = '', messages = [], options = {} }, {
43
+ export async function chat({ model = '', provider = '', system = '', messages = [], options = {} }, {
44
44
  baseUrl, signal, onEvent = () => {},
45
45
  } = {}) {
46
46
  const runId = `gw_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 8)}`;
@@ -57,7 +57,18 @@ export async function chat({ model = '', system = '', messages = [], options = {
57
57
  try {
58
58
  const res = await fetch(`${String(baseUrl).replace(/\/$/, '')}/v1/chat/completions`, {
59
59
  method: 'POST',
60
- headers: { 'content-type': 'application/json' },
60
+ headers: {
61
+ 'content-type': 'application/json',
62
+ // ChatPanel's routing metadata goes in HEADERS, never in the request body. It rode the
63
+ // body first and NVIDIA answered "unsupported parameters": OpenAI-compatible providers
64
+ // validate the body strictly and reject unknown fields, while ignoring unknown headers.
65
+ // The gateway strips x-chatpanel-* before forwarding, so the provider never sees it.
66
+ ...(options?.reach ? { 'x-chatpanel-reach': options.reach } : {}),
67
+ // WHICH provider, not just which model. A model id is not a unique key — two providers
68
+ // can serve the same one — so without this the gateway picks whichever destination it
69
+ // lists first and the call goes out on a key the user never chose.
70
+ ...(provider ? { 'x-chatpanel-destination': provider } : {}),
71
+ },
61
72
  body: JSON.stringify({
62
73
  model,
63
74
  stream: true,
@@ -65,9 +76,6 @@ export async function chat({ model = '', system = '', messages = [], options = {
65
76
  ...(system ? [{ role: 'system', content: system }] : []),
66
77
  ...messages.map((m) => ({ role: m.role, content: String(m.content ?? '') })),
67
78
  ],
68
- // Carried through so a capped phone's reach still reaches the router. The gateway
69
- // ignores what it does not know, which is what keeps an older gateway working.
70
- ...(options?.reach ? { chatpanel: { reach: options.reach } } : {}),
71
79
  }),
72
80
  signal: ac.signal,
73
81
  });
@@ -41,7 +41,8 @@ const TELEGRAM_API = 'https://api.telegram.org';
41
41
  // its own bridge backend, the same agents. They are mutually exclusive: update() clears one
42
42
  // when the other is set, because "which thing answers" is one choice, not two.
43
43
  export const DEFAULT_SETTINGS = Object.freeze({
44
- agent: 'claude', model: '', gatewayUrl: DEFAULT_GATEWAY_URL, privacy: 'standard', tier: 'basic',
44
+ agent: 'claude', model: '', provider: '', gatewayUrl: DEFAULT_GATEWAY_URL,
45
+ privacy: 'standard', tier: 'basic',
45
46
  });
46
47
 
47
48
  // Restart backoff. A long-poll that dies (network drop, laptop asleep, Telegram hiccup) must
@@ -137,6 +138,7 @@ export function createChannelService({
137
138
  botToken,
138
139
  transport: viaGateway ? gateway : bridgeTransport,
139
140
  model: viaGateway ? settings.model : '',
141
+ provider: viaGateway ? (settings.provider || '') : '',
140
142
  baseUrl: viaGateway ? (settings.gatewayUrl || DEFAULT_GATEWAY_URL) : bridge.baseUrl,
141
143
  token: bridge.token,
142
144
  pairing,
@@ -247,12 +249,12 @@ export function createChannelService({
247
249
  async update(patch = {}) {
248
250
  await load();
249
251
  const next = { ...settings };
250
- for (const k of ['agent', 'model', 'gatewayUrl', 'privacy', 'tier', 'system']) {
252
+ for (const k of ['agent', 'model', 'provider', 'gatewayUrl', 'privacy', 'tier', 'system']) {
251
253
  if (patch[k] != null) next[k] = patch[k];
252
254
  }
253
255
  // Picking one target unpicks the other. Without this a stale `model` would silently win
254
256
  // over the agent the user just chose, and the screen would disagree with the machine.
255
- if (patch.agent != null && patch.model == null) next.model = '';
257
+ if (patch.agent != null && patch.model == null) { next.model = ''; next.provider = ''; }
256
258
  if (patch.model) next.agent = '';
257
259
  settings = next;
258
260
  await saveSettings();
@@ -297,6 +299,7 @@ export function createChannelService({
297
299
  settings: {
298
300
  agent: settings.agent,
299
301
  model: settings.model || '',
302
+ provider: settings.provider || '',
300
303
  gatewayUrl: settings.gatewayUrl || DEFAULT_GATEWAY_URL,
301
304
  privacy: settings.privacy,
302
305
  tier: settings.tier,
package/src/server.js CHANGED
@@ -67,7 +67,7 @@ import {
67
67
  // Hardcoded (not read from package.json) so it survives Bun's single-file
68
68
  // --compile, where package.json isn't on a readable FS. CI fails the publish if
69
69
  // this drifts from package.json, so the two can't silently diverge.
70
- const VERSION = '0.11.5';
70
+ const VERSION = '0.11.6';
71
71
  const HOST = process.env.CHATPANEL_BRIDGE_HOST || '127.0.0.1';
72
72
  const PORT = Number(process.env.CHATPANEL_BRIDGE_PORT) || 4319;
73
73