@chatpanel/bridge 0.11.6 → 0.11.7
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 +1 -1
- package/src/channels/adapters/telegram.js +47 -4
- package/src/channels/service.js +24 -8
- package/src/server.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chatpanel/bridge",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.7",
|
|
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": [
|
|
@@ -38,6 +38,29 @@ const HELP = [
|
|
|
38
38
|
* it needs is injected — a bot token, the bridge address+token, a pairing store, an event sink
|
|
39
39
|
* — so nothing here reads a secret or reaches for global state.
|
|
40
40
|
*/
|
|
41
|
+
// What a turn driven from a phone can actually DO, told to the agent up front.
|
|
42
|
+
//
|
|
43
|
+
// Without this the agent plans as if it were sitting at a terminal: asked to create a note it
|
|
44
|
+
// announced it would look for a notes folder, discovered writes were not enabled, said "let me
|
|
45
|
+
// check what notes tools are available" — and stopped mid-thought, having done nothing and
|
|
46
|
+
// explained nothing. The ceiling is real and enforced elsewhere (reach caps the toolset); the
|
|
47
|
+
// agent simply had no way to know about it, so it spent a turn discovering it and left the
|
|
48
|
+
// user staring at half a sentence.
|
|
49
|
+
export function channelSystem(userSystem, reach) {
|
|
50
|
+
const limits = !reach || reach === 'any' ? '' : [
|
|
51
|
+
"You are answering a message sent from the user's PHONE, relayed to this machine.",
|
|
52
|
+
reach === 'device'
|
|
53
|
+
? 'You cannot read files, run commands, browse the web, or change anything.'
|
|
54
|
+
: "You can READ files and search the user's ChatPanel history. You cannot write or edit "
|
|
55
|
+
+ 'files, run shell commands, or browse the web — those are switched off for messages '
|
|
56
|
+
+ 'from a phone, and no tool will grant them.',
|
|
57
|
+
'If a request needs something you cannot do, say so plainly in your FIRST reply and offer '
|
|
58
|
+
+ 'what you can do instead. Never begin work you cannot finish.',
|
|
59
|
+
'Answers are read on a phone: be brief, and put the answer first.',
|
|
60
|
+
].join(' ');
|
|
61
|
+
return [limits, userSystem].filter(Boolean).join('\n\n');
|
|
62
|
+
}
|
|
63
|
+
|
|
41
64
|
export function startTelegram({
|
|
42
65
|
botToken,
|
|
43
66
|
baseUrl,
|
|
@@ -54,6 +77,15 @@ export function startTelegram({
|
|
|
54
77
|
model = '',
|
|
55
78
|
provider = '',
|
|
56
79
|
system = '',
|
|
80
|
+
// Read at the START OF EACH MESSAGE rather than captured when the loop starts.
|
|
81
|
+
//
|
|
82
|
+
// `chats` below holds every conversation's history AND its privacy vault, and it lives only
|
|
83
|
+
// as long as this loop. Settings used to be baked in at start, so changing the model — or
|
|
84
|
+
// the privacy mode, or anything else — had to stop and respawn the loop, which silently threw
|
|
85
|
+
// all of that away: the next message arrived as "a fresh session with no prior conversation",
|
|
86
|
+
// and PERSON_1 stopped meaning the same person. Switching which model answers is not a
|
|
87
|
+
// reason to forget what you were talking about.
|
|
88
|
+
route = null,
|
|
57
89
|
redact = { tier: 'basic' },
|
|
58
90
|
privacy = 'standard',
|
|
59
91
|
logger = console,
|
|
@@ -120,7 +152,7 @@ export function startTelegram({
|
|
|
120
152
|
}
|
|
121
153
|
if (name === 'stop') {
|
|
122
154
|
const st = chatState(norm.chatId);
|
|
123
|
-
const ok = await transport.cancel(st.runId, { baseUrl, token });
|
|
155
|
+
const ok = await ((route && route().transport) || transport).cancel(st.runId, { baseUrl, token });
|
|
124
156
|
st.runId = null;
|
|
125
157
|
return void send(norm.chatId, ok ? '⏹ stopped.' : 'nothing running.');
|
|
126
158
|
}
|
|
@@ -157,11 +189,22 @@ export function startTelegram({
|
|
|
157
189
|
// on the bridge renders all-but-last as history and the last as "answer this now".
|
|
158
190
|
const messages = [...st.history, { role: 'user', content: redacted }];
|
|
159
191
|
|
|
192
|
+
// Whatever the settings say RIGHT NOW — see `route` above.
|
|
193
|
+
const live = (route && route()) || {};
|
|
194
|
+
const useTransport = live.transport || transport;
|
|
160
195
|
try {
|
|
161
|
-
const finalState = await
|
|
162
|
-
{
|
|
196
|
+
const finalState = await useTransport.chat(
|
|
197
|
+
{
|
|
198
|
+
agent: live.agent ?? agent,
|
|
199
|
+
model: live.model ?? model,
|
|
200
|
+
provider: live.provider ?? provider,
|
|
201
|
+
system: channelSystem(live.system ?? system, reach),
|
|
202
|
+
messages,
|
|
203
|
+
images,
|
|
204
|
+
options: { reach },
|
|
205
|
+
},
|
|
163
206
|
{
|
|
164
|
-
baseUrl, token, signal,
|
|
207
|
+
baseUrl: live.baseUrl || baseUrl, token, signal,
|
|
165
208
|
onEvent: (ev, state) => {
|
|
166
209
|
if (state.runId) st.runId = state.runId;
|
|
167
210
|
// Driven by the folded STATE, not by an event's `type`: the bridge emits
|
package/src/channels/service.js
CHANGED
|
@@ -132,14 +132,28 @@ export function createChannelService({
|
|
|
132
132
|
function spawnLoop(botToken) {
|
|
133
133
|
controller = new AbortController();
|
|
134
134
|
running = true;
|
|
135
|
-
//
|
|
136
|
-
|
|
135
|
+
// Resolved PER MESSAGE, not once at start. The loop owns every conversation's history and
|
|
136
|
+
// privacy vault, so restarting it to pick up a settings change threw away the thing the
|
|
137
|
+
// user came for. Now nothing needs a restart: the next message simply reads this.
|
|
138
|
+
const route = () => {
|
|
139
|
+
const viaGateway = !!settings.model;
|
|
140
|
+
return {
|
|
141
|
+
transport: viaGateway ? gateway : bridgeTransport,
|
|
142
|
+
agent: viaGateway ? '' : settings.agent,
|
|
143
|
+
model: viaGateway ? settings.model : '',
|
|
144
|
+
provider: viaGateway ? (settings.provider || '') : '',
|
|
145
|
+
baseUrl: viaGateway ? (settings.gatewayUrl || DEFAULT_GATEWAY_URL) : bridge.baseUrl,
|
|
146
|
+
system: settings.system || '',
|
|
147
|
+
privacy: settings.privacy,
|
|
148
|
+
};
|
|
149
|
+
};
|
|
137
150
|
const done = startAdapter({
|
|
138
151
|
botToken,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
152
|
+
route,
|
|
153
|
+
transport: route().transport,
|
|
154
|
+
model: route().model,
|
|
155
|
+
provider: route().provider,
|
|
156
|
+
baseUrl: route().baseUrl,
|
|
143
157
|
token: bridge.token,
|
|
144
158
|
pairing,
|
|
145
159
|
savePairing,
|
|
@@ -258,8 +272,10 @@ export function createChannelService({
|
|
|
258
272
|
if (patch.model) next.agent = '';
|
|
259
273
|
settings = next;
|
|
260
274
|
await saveSettings();
|
|
261
|
-
//
|
|
262
|
-
|
|
275
|
+
// NO RESTART. The loop reads settings per message through `route`, and restarting it
|
|
276
|
+
// would discard every conversation's history and vault — which is exactly what made
|
|
277
|
+
// changing the model answer the next question with "this looks like a fresh session".
|
|
278
|
+
// The only change that still needs a restart is a new bot token, and connect() does that.
|
|
263
279
|
return { settings: { ...settings } };
|
|
264
280
|
},
|
|
265
281
|
|
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.
|
|
70
|
+
const VERSION = '0.11.7';
|
|
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
|
|