@pix3/agent-bridge 0.2.0 → 0.2.1
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/README.md +75 -75
- package/dist/cli.js +171 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.js +140 -0
- package/dist/config.js.map +1 -0
- package/dist/index.js +293 -0
- package/dist/index.js.map +1 -0
- package/dist/proxy.js +45 -0
- package/dist/proxy.js.map +1 -0
- package/dist/sessions.js +517 -0
- package/dist/sessions.js.map +1 -0
- package/dist/wire.js +121 -0
- package/dist/wire.js.map +1 -0
- package/package.json +47 -38
- package/src/cli.ts +195 -195
- package/src/config.ts +179 -179
- package/src/index.ts +350 -350
- package/src/proxy.ts +68 -68
- package/src/sessions.ts +597 -597
- package/src/wire.ts +161 -161
- package/tsconfig.json +16 -16
package/dist/sessions.js
ADDED
|
@@ -0,0 +1,517 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Harness inversion: exposes the stateless Anthropic Messages protocol (which pix3's agent loop
|
|
3
|
+
* speaks) on top of stateful Claude Agent SDK sessions (the sanctioned consumer of a Claude
|
|
4
|
+
* Code / MAX-subscription login).
|
|
5
|
+
*
|
|
6
|
+
* How one pix3 request round-trips:
|
|
7
|
+
*
|
|
8
|
+
* 1. pix3 POSTs `/v1/messages` (system + tools + full history). The manager routes it to a live
|
|
9
|
+
* session (or starts one). New trailing user message -> pushed into the SDK's streaming input.
|
|
10
|
+
* 2. The SDK calls the model. pix3's tools are registered as an in-process MCP server whose
|
|
11
|
+
* CallTool handler BLOCKS: when the model requests a tool, the assistant message (with
|
|
12
|
+
* `tool_use` blocks) is returned as the HTTP response and the handler's promise stays pending.
|
|
13
|
+
* 3. pix3 executes the tool in the editor and POSTs again with `tool_result` blocks. Those
|
|
14
|
+
* resolve the blocked handlers, the SDK forwards the results to the model, and the next
|
|
15
|
+
* assistant message answers this HTTP request. A turn with no tool calls ends with the SDK's
|
|
16
|
+
* `result` message -> `stop_reason: "end_turn"`.
|
|
17
|
+
*
|
|
18
|
+
* The real conversation state lives in the SDK session; pix3's replayed history is used only for
|
|
19
|
+
* routing/correlation. When nothing matches (bridge restart, edited history, model switch) the
|
|
20
|
+
* manager degrades gracefully: it starts a fresh session whose first user message is a plain-text
|
|
21
|
+
* transcript of the history.
|
|
22
|
+
*/
|
|
23
|
+
import { randomUUID } from 'node:crypto';
|
|
24
|
+
import os from 'node:os';
|
|
25
|
+
import { query } from '@anthropic-ai/claude-agent-sdk';
|
|
26
|
+
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
27
|
+
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
28
|
+
import { HttpError, extractToolResults, isRecord, renderTranscript, systemToText, toUserContent, } from "./wire.js";
|
|
29
|
+
const MCP_SERVER_NAME = 'pix3';
|
|
30
|
+
const MCP_PREFIX = `mcp__${MCP_SERVER_NAME}__`;
|
|
31
|
+
/** Hard cap on how long one `/v1/messages` request may wait for the model. */
|
|
32
|
+
const RESPONSE_TIMEOUT_MS = 20 * 60 * 1000;
|
|
33
|
+
const IDLE_TIMEOUT_MS = 45 * 60 * 1000;
|
|
34
|
+
const MAX_SESSIONS = 4;
|
|
35
|
+
class Deferred {
|
|
36
|
+
promise;
|
|
37
|
+
resolve;
|
|
38
|
+
reject;
|
|
39
|
+
constructor() {
|
|
40
|
+
this.promise = new Promise((resolve, reject) => {
|
|
41
|
+
this.resolve = resolve;
|
|
42
|
+
this.reject = reject;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Push-based async iterable used as the SDK's streaming input. */
|
|
47
|
+
class AsyncQueue {
|
|
48
|
+
buffer = [];
|
|
49
|
+
waiters = [];
|
|
50
|
+
ended = false;
|
|
51
|
+
push(item) {
|
|
52
|
+
const waiter = this.waiters.shift();
|
|
53
|
+
if (waiter)
|
|
54
|
+
waiter({ value: item, done: false });
|
|
55
|
+
else
|
|
56
|
+
this.buffer.push(item);
|
|
57
|
+
}
|
|
58
|
+
end() {
|
|
59
|
+
this.ended = true;
|
|
60
|
+
for (const waiter of this.waiters.splice(0)) {
|
|
61
|
+
waiter({ value: undefined, done: true });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
[Symbol.asyncIterator]() {
|
|
65
|
+
return {
|
|
66
|
+
next: () => {
|
|
67
|
+
if (this.buffer.length > 0) {
|
|
68
|
+
return Promise.resolve({ value: this.buffer.shift(), done: false });
|
|
69
|
+
}
|
|
70
|
+
if (this.ended)
|
|
71
|
+
return Promise.resolve({ value: undefined, done: true });
|
|
72
|
+
return new Promise(resolve => this.waiters.push(resolve));
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
const toolUseName = (block) => typeof block.name === 'string' ? block.name : '';
|
|
78
|
+
const stripPrefix = (name) => name.startsWith(MCP_PREFIX) ? name.slice(MCP_PREFIX.length) : name;
|
|
79
|
+
const sameJson = (a, b) => {
|
|
80
|
+
try {
|
|
81
|
+
return JSON.stringify(a) === JSON.stringify(b);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const toCallToolResult = (result) => ({
|
|
88
|
+
content: [{ type: 'text', text: result.content || '(empty result)' }],
|
|
89
|
+
...(result.isError ? { isError: true } : {}),
|
|
90
|
+
});
|
|
91
|
+
export class BridgeSession {
|
|
92
|
+
id = randomUUID().slice(0, 8);
|
|
93
|
+
model;
|
|
94
|
+
/** Expected `messages.length` of pix3's NEXT request if this chat simply continues. */
|
|
95
|
+
transcriptLen = 0;
|
|
96
|
+
lastActivity = Date.now();
|
|
97
|
+
closed = false;
|
|
98
|
+
log;
|
|
99
|
+
q;
|
|
100
|
+
input = new AsyncQueue();
|
|
101
|
+
toolNames;
|
|
102
|
+
pending = [];
|
|
103
|
+
/** Assistant content blocks accumulated since the last HTTP response. */
|
|
104
|
+
buffer = [];
|
|
105
|
+
seenToolUseIds = new Set();
|
|
106
|
+
waiting = null;
|
|
107
|
+
waitingTimer = null;
|
|
108
|
+
requestLen = 0;
|
|
109
|
+
lastUsage = null;
|
|
110
|
+
lastSystem;
|
|
111
|
+
pendingContextRefresh = null;
|
|
112
|
+
/** Set after an interrupt: the interrupted turn's late `result` must not answer a new request. */
|
|
113
|
+
discardNextResult = false;
|
|
114
|
+
constructor(request, log) {
|
|
115
|
+
this.log = log;
|
|
116
|
+
this.model = request.model;
|
|
117
|
+
this.lastSystem = systemToText(request.system);
|
|
118
|
+
const tools = request.tools ?? [];
|
|
119
|
+
this.toolNames = new Set(tools.map(tool => tool.name));
|
|
120
|
+
this.q = query({
|
|
121
|
+
prompt: this.input,
|
|
122
|
+
options: {
|
|
123
|
+
model: request.model,
|
|
124
|
+
systemPrompt: this.lastSystem || undefined,
|
|
125
|
+
// pix3's tools, exposed verbatim (JSON Schema and all) via an in-process MCP server.
|
|
126
|
+
mcpServers: {
|
|
127
|
+
[MCP_SERVER_NAME]: { type: 'sdk', name: MCP_SERVER_NAME, instance: this.buildMcpServer(tools) },
|
|
128
|
+
},
|
|
129
|
+
strictMcpConfig: true,
|
|
130
|
+
// No built-in tools: the bridge process must never touch the local FS/shell/web on the
|
|
131
|
+
// model's behalf — every capability comes from pix3 and executes inside the editor.
|
|
132
|
+
tools: [],
|
|
133
|
+
allowedTools: tools.map(tool => `${MCP_PREFIX}${tool.name}`),
|
|
134
|
+
permissionMode: 'bypassPermissions',
|
|
135
|
+
allowDangerouslySkipPermissions: true,
|
|
136
|
+
settingSources: [],
|
|
137
|
+
persistSession: false,
|
|
138
|
+
cwd: os.tmpdir(),
|
|
139
|
+
includePartialMessages: false,
|
|
140
|
+
stderr: data => {
|
|
141
|
+
const line = data.trim();
|
|
142
|
+
if (line)
|
|
143
|
+
this.log(`[${this.id}] cli: ${line.slice(0, 400)}`);
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
void this.pump();
|
|
148
|
+
}
|
|
149
|
+
get busy() {
|
|
150
|
+
return this.waiting !== null;
|
|
151
|
+
}
|
|
152
|
+
hasPendingToolUse(toolUseId) {
|
|
153
|
+
return this.pending.some(call => call.toolUseId === toolUseId);
|
|
154
|
+
}
|
|
155
|
+
toolsMatch(tools) {
|
|
156
|
+
const names = (tools ?? []).map(tool => tool.name);
|
|
157
|
+
return names.length === this.toolNames.size && names.every(name => this.toolNames.has(name));
|
|
158
|
+
}
|
|
159
|
+
/** Advance the session with one pix3 request and wait for the next assistant boundary. */
|
|
160
|
+
handleRequest(request, signal) {
|
|
161
|
+
if (this.closed)
|
|
162
|
+
throw new HttpError(409, 'Session already closed.');
|
|
163
|
+
if (this.waiting)
|
|
164
|
+
throw new HttpError(409, 'Session is still processing a previous request.');
|
|
165
|
+
this.lastActivity = Date.now();
|
|
166
|
+
this.trackSystemDrift(request);
|
|
167
|
+
const last = request.messages[request.messages.length - 1];
|
|
168
|
+
const toolResults = extractToolResults(last);
|
|
169
|
+
if (toolResults.length > 0) {
|
|
170
|
+
this.resolveToolResults(toolResults);
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
this.pushUserMessage(toUserContent(last));
|
|
174
|
+
}
|
|
175
|
+
return this.awaitResponse(request, signal);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Recovery path: no live session matched, so this fresh session is seeded with a plain-text
|
|
179
|
+
* transcript of pix3's history (built by the manager) instead of a real user message.
|
|
180
|
+
*/
|
|
181
|
+
handleTranscriptReplay(request, transcript, signal) {
|
|
182
|
+
this.lastActivity = Date.now();
|
|
183
|
+
this.pushUserMessage([{ type: 'text', text: transcript }]);
|
|
184
|
+
return this.awaitResponse(request, signal);
|
|
185
|
+
}
|
|
186
|
+
close(reason) {
|
|
187
|
+
if (this.closed)
|
|
188
|
+
return;
|
|
189
|
+
this.closed = true;
|
|
190
|
+
this.log(`[${this.id}] closing (${reason})`);
|
|
191
|
+
this.cancelPendingCalls('The session was closed.');
|
|
192
|
+
this.failWaiting(new HttpError(500, `Claude Code session closed: ${reason}`));
|
|
193
|
+
this.input.end();
|
|
194
|
+
try {
|
|
195
|
+
this.q.close();
|
|
196
|
+
}
|
|
197
|
+
catch {
|
|
198
|
+
/* already terminated */
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
// -- request plumbing -------------------------------------------------------
|
|
202
|
+
awaitResponse(request, signal) {
|
|
203
|
+
const waiting = new Deferred();
|
|
204
|
+
this.waiting = waiting;
|
|
205
|
+
this.requestLen = request.messages.length;
|
|
206
|
+
this.waitingTimer = setTimeout(() => {
|
|
207
|
+
if (this.waiting === waiting) {
|
|
208
|
+
this.waiting = null;
|
|
209
|
+
waiting.reject(new HttpError(504, 'Timed out waiting for the model.'));
|
|
210
|
+
}
|
|
211
|
+
}, RESPONSE_TIMEOUT_MS);
|
|
212
|
+
if (signal.aborted) {
|
|
213
|
+
this.onHttpAbort(waiting);
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
signal.addEventListener('abort', () => this.onHttpAbort(waiting), { once: true });
|
|
217
|
+
}
|
|
218
|
+
// A tool_use flush may already be satisfiable (parallel calls resolved in a prior tick).
|
|
219
|
+
this.tryAssignAndFlush();
|
|
220
|
+
return waiting.promise.finally(() => {
|
|
221
|
+
if (this.waitingTimer)
|
|
222
|
+
clearTimeout(this.waitingTimer);
|
|
223
|
+
this.waitingTimer = null;
|
|
224
|
+
this.lastActivity = Date.now();
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
pushUserMessage(blocks) {
|
|
228
|
+
const content = [];
|
|
229
|
+
if (this.pendingContextRefresh) {
|
|
230
|
+
content.push({
|
|
231
|
+
type: 'text',
|
|
232
|
+
text: `<context-refresh>\nUpdated live editor context (replaces the earlier snapshot):\n${this.pendingContextRefresh}\n</context-refresh>`,
|
|
233
|
+
});
|
|
234
|
+
this.pendingContextRefresh = null;
|
|
235
|
+
}
|
|
236
|
+
content.push(...blocks);
|
|
237
|
+
if (content.length === 0)
|
|
238
|
+
content.push({ type: 'text', text: '(empty message)' });
|
|
239
|
+
this.input.push({
|
|
240
|
+
type: 'user',
|
|
241
|
+
message: { role: 'user', content: content },
|
|
242
|
+
parent_tool_use_id: null,
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
resolveToolResults(results) {
|
|
246
|
+
for (const result of results) {
|
|
247
|
+
let index = this.pending.findIndex(call => call.toolUseId === result.toolUseId);
|
|
248
|
+
if (index < 0)
|
|
249
|
+
index = this.pending.findIndex(call => call.toolUseId === undefined);
|
|
250
|
+
if (index < 0) {
|
|
251
|
+
this.log(`[${this.id}] no pending tool call for result ${result.toolUseId} — dropped`);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const [call] = this.pending.splice(index, 1);
|
|
255
|
+
call.resolve(toCallToolResult(result));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* pix3 saw the `system` field change between requests (the editor appends live scene context to
|
|
260
|
+
* a stable head). The SDK session's system prompt is fixed, so the drift is injected into the
|
|
261
|
+
* next user message instead.
|
|
262
|
+
*/
|
|
263
|
+
trackSystemDrift(request) {
|
|
264
|
+
const system = systemToText(request.system);
|
|
265
|
+
if (system === this.lastSystem)
|
|
266
|
+
return;
|
|
267
|
+
let common = 0;
|
|
268
|
+
const max = Math.min(system.length, this.lastSystem.length);
|
|
269
|
+
while (common < max && system[common] === this.lastSystem[common])
|
|
270
|
+
common += 1;
|
|
271
|
+
// Back up to a line boundary so the injected snippet starts cleanly.
|
|
272
|
+
const lineStart = system.lastIndexOf('\n', common);
|
|
273
|
+
const suffix = system.slice(lineStart >= 0 ? lineStart + 1 : 0);
|
|
274
|
+
this.pendingContextRefresh = suffix.slice(0, 32_000);
|
|
275
|
+
this.lastSystem = system;
|
|
276
|
+
}
|
|
277
|
+
onHttpAbort(waiting) {
|
|
278
|
+
if (this.waiting !== waiting)
|
|
279
|
+
return;
|
|
280
|
+
this.log(`[${this.id}] request aborted by client — interrupting`);
|
|
281
|
+
this.waiting = null;
|
|
282
|
+
waiting.reject(new HttpError(499, 'Request cancelled.'));
|
|
283
|
+
this.buffer = [];
|
|
284
|
+
this.discardNextResult = true;
|
|
285
|
+
this.cancelPendingCalls('Cancelled by the user.');
|
|
286
|
+
void this.q.interrupt().catch(() => { });
|
|
287
|
+
}
|
|
288
|
+
cancelPendingCalls(message) {
|
|
289
|
+
for (const call of this.pending.splice(0)) {
|
|
290
|
+
call.resolve({ content: [{ type: 'text', text: message }], isError: true });
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
failWaiting(error) {
|
|
294
|
+
const waiting = this.waiting;
|
|
295
|
+
if (!waiting)
|
|
296
|
+
return;
|
|
297
|
+
this.waiting = null;
|
|
298
|
+
waiting.reject(error);
|
|
299
|
+
}
|
|
300
|
+
// -- SDK message pump -------------------------------------------------------
|
|
301
|
+
async pump() {
|
|
302
|
+
try {
|
|
303
|
+
for await (const message of this.q) {
|
|
304
|
+
if (message.type === 'assistant' && !message.parent_tool_use_id) {
|
|
305
|
+
this.onAssistantMessage(message.message);
|
|
306
|
+
}
|
|
307
|
+
else if (message.type === 'result') {
|
|
308
|
+
this.onResult(message);
|
|
309
|
+
}
|
|
310
|
+
else if (message.type === 'system' && message.subtype === 'init') {
|
|
311
|
+
this.log(`[${this.id}] Claude Code session ${message.session_id ?? '?'} (${this.model})`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (!this.closed)
|
|
315
|
+
this.close('CLI stream ended');
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
this.log(`[${this.id}] pump error: ${error instanceof Error ? error.message : String(error)}`);
|
|
319
|
+
if (!this.closed) {
|
|
320
|
+
this.closed = true;
|
|
321
|
+
this.cancelPendingCalls('The session crashed.');
|
|
322
|
+
this.failWaiting(new HttpError(502, `Claude Code error: ${error instanceof Error ? error.message : String(error)}`));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
onAssistantMessage(message) {
|
|
327
|
+
const content = Array.isArray(message.content) ? message.content : [];
|
|
328
|
+
for (const block of content) {
|
|
329
|
+
if (block.type === 'tool_use') {
|
|
330
|
+
const id = typeof block.id === 'string' ? block.id : '';
|
|
331
|
+
if (this.seenToolUseIds.has(id))
|
|
332
|
+
continue;
|
|
333
|
+
this.seenToolUseIds.add(id);
|
|
334
|
+
}
|
|
335
|
+
this.buffer.push(block);
|
|
336
|
+
}
|
|
337
|
+
if (isRecord(message.usage))
|
|
338
|
+
this.lastUsage = message.usage;
|
|
339
|
+
this.tryAssignAndFlush();
|
|
340
|
+
}
|
|
341
|
+
onResult(result) {
|
|
342
|
+
if (this.discardNextResult) {
|
|
343
|
+
this.discardNextResult = false;
|
|
344
|
+
this.buffer = [];
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
if (result.subtype !== 'success') {
|
|
348
|
+
const errors = Array.isArray(result.errors) ? result.errors.join('; ') : String(result.subtype);
|
|
349
|
+
this.failWaiting(new HttpError(502, `Claude Code stopped: ${errors}`));
|
|
350
|
+
this.buffer = [];
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!this.waiting) {
|
|
354
|
+
this.buffer = [];
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
if (this.buffer.length === 0 && typeof result.result === 'string' && result.result) {
|
|
358
|
+
this.buffer.push({ type: 'text', text: result.result });
|
|
359
|
+
}
|
|
360
|
+
const stop = result.stop_reason === 'max_tokens' || result.stop_reason === 'refusal'
|
|
361
|
+
? result.stop_reason
|
|
362
|
+
: 'end_turn';
|
|
363
|
+
this.respond(stop);
|
|
364
|
+
}
|
|
365
|
+
/** Handler for the in-process MCP server: park the call until pix3 posts its tool_result. */
|
|
366
|
+
onToolCall(name, input) {
|
|
367
|
+
this.log(`[${this.id}] tool call: ${name}`);
|
|
368
|
+
return new Promise(resolve => {
|
|
369
|
+
this.pending.push({ name, input, resolve });
|
|
370
|
+
this.tryAssignAndFlush();
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Match parked MCP calls to the `tool_use` blocks buffered from the assistant message (by name +
|
|
375
|
+
* identical input, falling back to first-with-same-name), then — if an HTTP request is waiting
|
|
376
|
+
* and every buffered pix3 tool_use has its parked call — answer it with `stop_reason: tool_use`.
|
|
377
|
+
*/
|
|
378
|
+
tryAssignAndFlush() {
|
|
379
|
+
const toolUseBlocks = this.buffer.filter(block => block.type === 'tool_use' && toolUseName(block).startsWith(MCP_PREFIX));
|
|
380
|
+
const assigned = new Set(this.pending.map(call => call.toolUseId).filter((id) => id !== undefined));
|
|
381
|
+
for (const call of this.pending) {
|
|
382
|
+
if (call.toolUseId)
|
|
383
|
+
continue;
|
|
384
|
+
const candidates = toolUseBlocks.filter(block => !assigned.has(String(block.id)) && stripPrefix(toolUseName(block)) === call.name);
|
|
385
|
+
const match = candidates.find(block => sameJson(block.input ?? {}, call.input)) ?? candidates[0];
|
|
386
|
+
if (match) {
|
|
387
|
+
call.toolUseId = String(match.id);
|
|
388
|
+
assigned.add(call.toolUseId);
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (!this.waiting || toolUseBlocks.length === 0)
|
|
392
|
+
return;
|
|
393
|
+
const allParked = toolUseBlocks.every(block => this.pending.some(call => call.toolUseId === String(block.id)));
|
|
394
|
+
if (allParked)
|
|
395
|
+
this.respond('tool_use');
|
|
396
|
+
}
|
|
397
|
+
respond(stopReason) {
|
|
398
|
+
const waiting = this.waiting;
|
|
399
|
+
if (!waiting) {
|
|
400
|
+
this.buffer = [];
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
const content = this.buffer
|
|
404
|
+
.filter(block => block.type === 'text' ||
|
|
405
|
+
(block.type === 'tool_use' && toolUseName(block).startsWith(MCP_PREFIX)))
|
|
406
|
+
.map(block => block.type === 'tool_use'
|
|
407
|
+
? { ...block, name: stripPrefix(toolUseName(block)) }
|
|
408
|
+
: block);
|
|
409
|
+
this.buffer = [];
|
|
410
|
+
this.waiting = null;
|
|
411
|
+
this.transcriptLen = this.requestLen + 1;
|
|
412
|
+
waiting.resolve({
|
|
413
|
+
status: 200,
|
|
414
|
+
body: {
|
|
415
|
+
id: `msg_bridge_${randomUUID().replace(/-/g, '').slice(0, 24)}`,
|
|
416
|
+
type: 'message',
|
|
417
|
+
role: 'assistant',
|
|
418
|
+
model: this.model,
|
|
419
|
+
content,
|
|
420
|
+
stop_reason: stopReason,
|
|
421
|
+
usage: this.lastUsage ?? {},
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
buildMcpServer(tools) {
|
|
426
|
+
const server = new McpServer({ name: MCP_SERVER_NAME, version: '1.0.0' }, { capabilities: { tools: {} } });
|
|
427
|
+
server.server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
428
|
+
tools: tools.map(tool => ({
|
|
429
|
+
name: tool.name,
|
|
430
|
+
description: tool.description,
|
|
431
|
+
inputSchema: (isRecord(tool.input_schema) && typeof tool.input_schema.type === 'string'
|
|
432
|
+
? tool.input_schema
|
|
433
|
+
: { type: 'object', ...tool.input_schema }),
|
|
434
|
+
_meta: { 'anthropic/alwaysLoad': true },
|
|
435
|
+
})),
|
|
436
|
+
}));
|
|
437
|
+
server.server.setRequestHandler(CallToolRequestSchema, async (request) => this.onToolCall(request.params.name, request.params.arguments ?? {}));
|
|
438
|
+
return server;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
export class SessionManager {
|
|
442
|
+
log;
|
|
443
|
+
sessions = [];
|
|
444
|
+
sweeper;
|
|
445
|
+
constructor(log) {
|
|
446
|
+
this.log = log;
|
|
447
|
+
this.sweeper = setInterval(() => this.sweep(), 60_000);
|
|
448
|
+
this.sweeper.unref();
|
|
449
|
+
}
|
|
450
|
+
async handle(request, signal) {
|
|
451
|
+
this.sessions = this.sessions.filter(session => !session.closed);
|
|
452
|
+
const last = request.messages[request.messages.length - 1];
|
|
453
|
+
const toolResults = extractToolResults(last);
|
|
454
|
+
if (toolResults.length > 0) {
|
|
455
|
+
const session = this.sessions.find(candidate => toolResults.every(result => candidate.hasPendingToolUse(result.toolUseId)));
|
|
456
|
+
if (session)
|
|
457
|
+
return session.handleRequest(request, signal);
|
|
458
|
+
this.log('tool results with no live session (bridge restarted?) — replaying transcript');
|
|
459
|
+
return this.replay(request, signal);
|
|
460
|
+
}
|
|
461
|
+
if (request.messages.length > 1) {
|
|
462
|
+
const session = this.sessions.find(candidate => !candidate.busy &&
|
|
463
|
+
candidate.transcriptLen === request.messages.length - 1 &&
|
|
464
|
+
candidate.model === request.model &&
|
|
465
|
+
candidate.toolsMatch(request.tools));
|
|
466
|
+
if (session)
|
|
467
|
+
return session.handleRequest(request, signal);
|
|
468
|
+
this.log(`no session matches a ${request.messages.length}-message history (restart/edit/model switch) — replaying transcript`);
|
|
469
|
+
return this.replay(request, signal);
|
|
470
|
+
}
|
|
471
|
+
const session = this.create(request);
|
|
472
|
+
this.log(`[${session.id}] new session (${request.model}, ${request.tools?.length ?? 0} tools)`);
|
|
473
|
+
return session.handleRequest(request, signal);
|
|
474
|
+
}
|
|
475
|
+
closeAll(reason) {
|
|
476
|
+
clearInterval(this.sweeper);
|
|
477
|
+
for (const session of this.sessions.splice(0))
|
|
478
|
+
session.close(reason);
|
|
479
|
+
}
|
|
480
|
+
replay(request, signal) {
|
|
481
|
+
const session = this.create(request);
|
|
482
|
+
const transcript = [
|
|
483
|
+
'The conversation below already happened in the pix3 editor (the previous bridge session was lost).',
|
|
484
|
+
'Continue it from where it left off — do not re-introduce yourself or redo completed work.',
|
|
485
|
+
'',
|
|
486
|
+
'<conversation-replay>',
|
|
487
|
+
renderTranscript(request.messages),
|
|
488
|
+
'</conversation-replay>',
|
|
489
|
+
'',
|
|
490
|
+
'Respond to the last entry above.',
|
|
491
|
+
].join('\n');
|
|
492
|
+
this.log(`[${session.id}] replay session (${request.messages.length} messages)`);
|
|
493
|
+
return session.handleTranscriptReplay(request, transcript, signal);
|
|
494
|
+
}
|
|
495
|
+
create(request) {
|
|
496
|
+
while (this.sessions.length >= MAX_SESSIONS) {
|
|
497
|
+
const idle = this.sessions.filter(session => !session.busy);
|
|
498
|
+
const victim = (idle.length > 0 ? idle : this.sessions).reduce((a, b) => a.lastActivity <= b.lastActivity ? a : b);
|
|
499
|
+
this.sessions = this.sessions.filter(session => session !== victim);
|
|
500
|
+
victim.close('evicted (too many sessions)');
|
|
501
|
+
}
|
|
502
|
+
const session = new BridgeSession(request, this.log);
|
|
503
|
+
this.sessions.push(session);
|
|
504
|
+
return session;
|
|
505
|
+
}
|
|
506
|
+
sweep() {
|
|
507
|
+
const now = Date.now();
|
|
508
|
+
for (const session of [...this.sessions]) {
|
|
509
|
+
if (session.closed || (!session.busy && now - session.lastActivity > IDLE_TIMEOUT_MS)) {
|
|
510
|
+
this.sessions = this.sessions.filter(candidate => candidate !== session);
|
|
511
|
+
if (!session.closed)
|
|
512
|
+
session.close('idle timeout');
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sessions.js","sourceRoot":"","sources":["../src/sessions.ts"],"names":[],"mappings":"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"}
|
package/dist/wire.js
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire types + mapping helpers for the Anthropic Messages-shaped requests that the pix3 editor
|
|
3
|
+
* sends (see pix3's `AnthropicLlmProvider.buildBody`). Everything arrives as untyped JSON, so the
|
|
4
|
+
* shapes here are deliberately loose records with narrow accessor helpers.
|
|
5
|
+
*/
|
|
6
|
+
export class HttpError extends Error {
|
|
7
|
+
status;
|
|
8
|
+
constructor(status, message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.status = status;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
|
+
/** Parse + minimally validate a `/v1/messages` body. Throws {@link HttpError} on bad input. */
|
|
15
|
+
export const parseMessagesRequest = (raw) => {
|
|
16
|
+
if (!isRecord(raw))
|
|
17
|
+
throw new HttpError(400, 'Request body must be a JSON object.');
|
|
18
|
+
if (typeof raw.model !== 'string' || !raw.model)
|
|
19
|
+
throw new HttpError(400, 'Missing "model".');
|
|
20
|
+
if (!Array.isArray(raw.messages) || raw.messages.length === 0) {
|
|
21
|
+
throw new HttpError(400, 'Missing "messages".');
|
|
22
|
+
}
|
|
23
|
+
for (const message of raw.messages) {
|
|
24
|
+
if (!isRecord(message) || (message.role !== 'user' && message.role !== 'assistant')) {
|
|
25
|
+
throw new HttpError(400, 'Each message needs a user/assistant role.');
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (raw.tools !== undefined) {
|
|
29
|
+
if (!Array.isArray(raw.tools))
|
|
30
|
+
throw new HttpError(400, '"tools" must be an array.');
|
|
31
|
+
for (const tool of raw.tools) {
|
|
32
|
+
if (!isRecord(tool) || typeof tool.name !== 'string' || typeof tool.description !== 'string') {
|
|
33
|
+
throw new HttpError(400, 'Each tool needs a name and description.');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return raw;
|
|
38
|
+
};
|
|
39
|
+
/** Flatten the `system` field (string, or Anthropic text blocks) into one plain string. */
|
|
40
|
+
export const systemToText = (system) => {
|
|
41
|
+
if (typeof system === 'string')
|
|
42
|
+
return system;
|
|
43
|
+
if (Array.isArray(system)) {
|
|
44
|
+
return system
|
|
45
|
+
.map(block => (typeof block.text === 'string' ? block.text : ''))
|
|
46
|
+
.join('');
|
|
47
|
+
}
|
|
48
|
+
return '';
|
|
49
|
+
};
|
|
50
|
+
export const contentToBlocks = (content) => typeof content === 'string' ? [{ type: 'text', text: content }] : content;
|
|
51
|
+
/** Extract `tool_result` blocks from a message (pix3 sends result content as a plain string). */
|
|
52
|
+
export const extractToolResults = (message) => {
|
|
53
|
+
const results = [];
|
|
54
|
+
for (const block of contentToBlocks(message.content)) {
|
|
55
|
+
if (block.type !== 'tool_result')
|
|
56
|
+
continue;
|
|
57
|
+
const content = typeof block.content === 'string'
|
|
58
|
+
? block.content
|
|
59
|
+
: Array.isArray(block.content)
|
|
60
|
+
? block.content
|
|
61
|
+
.map(part => (isRecord(part) && typeof part.text === 'string' ? part.text : ''))
|
|
62
|
+
.join('\n')
|
|
63
|
+
: '';
|
|
64
|
+
results.push({
|
|
65
|
+
toolUseId: typeof block.tool_use_id === 'string' ? block.tool_use_id : '',
|
|
66
|
+
content,
|
|
67
|
+
isError: block.is_error === true,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return results;
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* Map user-authored blocks (text/image) to Anthropic user content; tool_result blocks are skipped.
|
|
74
|
+
* pix3's `cache_control` breakpoints are stripped — the Claude Code harness manages its own cache
|
|
75
|
+
* breakpoints, and stacking pix3's on top could exceed the API's 4-breakpoint limit.
|
|
76
|
+
*/
|
|
77
|
+
export const toUserContent = (message) => contentToBlocks(message.content)
|
|
78
|
+
.filter(block => block.type === 'text' || block.type === 'image')
|
|
79
|
+
.map(block => {
|
|
80
|
+
if ('cache_control' in block) {
|
|
81
|
+
const { cache_control: _dropped, ...rest } = block;
|
|
82
|
+
return rest;
|
|
83
|
+
}
|
|
84
|
+
return block;
|
|
85
|
+
});
|
|
86
|
+
const clip = (text, max) => text.length > max ? `${text.slice(0, max)}… [truncated]` : text;
|
|
87
|
+
/**
|
|
88
|
+
* Render a pix3 conversation history as a plain-text transcript. Used as a degraded recovery path
|
|
89
|
+
* when no live Claude Code session matches the request (bridge restart, edited history, model
|
|
90
|
+
* switch): the transcript becomes the first user message of a fresh session.
|
|
91
|
+
*/
|
|
92
|
+
export const renderTranscript = (messages) => {
|
|
93
|
+
const lines = [];
|
|
94
|
+
for (const message of messages) {
|
|
95
|
+
const speaker = message.role === 'user' ? 'User' : 'Assistant';
|
|
96
|
+
for (const block of contentToBlocks(message.content)) {
|
|
97
|
+
switch (block.type) {
|
|
98
|
+
case 'text':
|
|
99
|
+
if (typeof block.text === 'string' && block.text.trim()) {
|
|
100
|
+
lines.push(`${speaker}: ${clip(block.text, 6000)}`);
|
|
101
|
+
}
|
|
102
|
+
break;
|
|
103
|
+
case 'image':
|
|
104
|
+
lines.push(`${speaker}: [image omitted]`);
|
|
105
|
+
break;
|
|
106
|
+
case 'tool_use':
|
|
107
|
+
lines.push(`Assistant called tool ${String(block.name)}(${clip(JSON.stringify(block.input ?? {}), 2000)})`);
|
|
108
|
+
break;
|
|
109
|
+
case 'tool_result': {
|
|
110
|
+
const text = typeof block.content === 'string' ? block.content : JSON.stringify(block.content);
|
|
111
|
+
lines.push(`Tool result${block.is_error === true ? ' (error)' : ''}: ${clip(text, 4000)}`);
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
default:
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return lines.join('\n');
|
|
120
|
+
};
|
|
121
|
+
//# sourceMappingURL=wire.js.map
|