@1presence/bridge 0.95.0 → 0.97.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/dist/claude.js +28 -19
- package/dist/index.js +5 -4
- package/package.json +1 -1
package/dist/claude.js
CHANGED
|
@@ -140,13 +140,17 @@ export function isPermissionTransportFailure(text) {
|
|
|
140
140
|
export function isLiveTurnResult(result, producedRealOutput) {
|
|
141
141
|
return producedRealOutput || result.is_error === true;
|
|
142
142
|
}
|
|
143
|
-
const MCP_TOOL_UNREGISTERED_RE = new RegExp(`No such tool available:\\s*(?:${MCP_TOOL_PREFIX}|${LEGACY_MCP_TOOL_PREFIX})`, 'i');
|
|
143
|
+
const MCP_TOOL_UNREGISTERED_RE = new RegExp(`No such tool available:\\s*((?:${MCP_TOOL_PREFIX}|${LEGACY_MCP_TOOL_PREFIX})[A-Za-z0-9_]+)`, 'i');
|
|
144
144
|
export function shouldReconnectRemoteMcp(args) {
|
|
145
|
-
const { toolName, isError, text } = args;
|
|
145
|
+
const { toolName, isError, text, served } = args;
|
|
146
146
|
if (MCP_SESSION_EXPIRED_RE.test(text))
|
|
147
147
|
return true;
|
|
148
|
-
|
|
148
|
+
const unregistered = MCP_TOOL_UNREGISTERED_RE.exec(text);
|
|
149
|
+
if (unregistered) {
|
|
150
|
+
if (served && served.size > 0 && !served.has(unregistered[1]))
|
|
151
|
+
return false;
|
|
149
152
|
return true;
|
|
153
|
+
}
|
|
150
154
|
if (!isError || !isPresenceToolName(toolName))
|
|
151
155
|
return false;
|
|
152
156
|
if (OUTBOUND_NETWORK_TOOLS.has(toolName))
|
|
@@ -241,7 +245,9 @@ export function makeGatedPromptStream(messages) {
|
|
|
241
245
|
};
|
|
242
246
|
}
|
|
243
247
|
export function spawnClaude(params) {
|
|
244
|
-
const { conversationId, presenceSessionId, text, uid, history, vaultFileOpen, clientCapabilities, syncedFolders, model: perTurnModel, onEvent, onDone, onError, onNotice } = params;
|
|
248
|
+
const { conversationId, presenceSessionId, chatSessionId, text, uid, history, vaultFileOpen, clientCapabilities, syncedFolders, model: perTurnModel, onEvent, onDone, onError, onNotice } = params;
|
|
249
|
+
const sessionTag = ` · session ${chatSessionId ?? conversationId}`;
|
|
250
|
+
const errLine = (text) => paint(SECTION_COLORS.error, `${text}${sessionTag}`);
|
|
245
251
|
const systemPromptPath = join(tmpdir(), `agent-${uid}.md`);
|
|
246
252
|
const mcpConfigPath = join(tmpdir(), `mcp-${uid}.json`);
|
|
247
253
|
if (verbose) {
|
|
@@ -266,7 +272,7 @@ export function spawnClaude(params) {
|
|
|
266
272
|
debugBlock('user · this turn', SECTION_COLORS.user, text);
|
|
267
273
|
}
|
|
268
274
|
else {
|
|
269
|
-
process.stderr.write(`[bridge] session ${
|
|
275
|
+
process.stderr.write(`[bridge] session ${chatSessionId ?? '(none)'} turn ${conversationId}\n`);
|
|
270
276
|
}
|
|
271
277
|
void vaultFileOpen;
|
|
272
278
|
void clientCapabilities;
|
|
@@ -388,7 +394,7 @@ export function spawnClaude(params) {
|
|
|
388
394
|
if (blockText && TOOL_CALL_XML_RE.test(blockText)) {
|
|
389
395
|
const cleaned = stripToolCallXml(blockText);
|
|
390
396
|
if (cleaned !== blockText) {
|
|
391
|
-
process.stderr.write(
|
|
397
|
+
process.stderr.write(errLine(`[bridge] suppressed confabulated tool-call XML in assistant text`) + '\n');
|
|
392
398
|
block['text'] = cleaned;
|
|
393
399
|
blockText = cleaned;
|
|
394
400
|
}
|
|
@@ -402,7 +408,7 @@ export function spawnClaude(params) {
|
|
|
402
408
|
apiErrorText = blockText.trim();
|
|
403
409
|
if (isAuthFailure)
|
|
404
410
|
sawAuthFailure = true;
|
|
405
|
-
process.stderr.write(
|
|
411
|
+
process.stderr.write(errLine(`[bridge] ${blockText.replace(/\n+/g, ' ')}`) + '\n');
|
|
406
412
|
return false;
|
|
407
413
|
}
|
|
408
414
|
producedRealOutput = true;
|
|
@@ -433,7 +439,7 @@ export function spawnClaude(params) {
|
|
|
433
439
|
if (block['is_error']) {
|
|
434
440
|
const detail = toolResultText(out).replace(/\s+/g, ' ').trim();
|
|
435
441
|
const shown = detail.length > 400 ? `${detail.slice(0, 400)}…` : detail;
|
|
436
|
-
process.stderr.write(
|
|
442
|
+
process.stderr.write(errLine(`[bridge] ✗ tool error ← ${name}: ${shown || 'no detail'}`) + '\n');
|
|
437
443
|
}
|
|
438
444
|
if (!verbose && !debug)
|
|
439
445
|
continue;
|
|
@@ -460,7 +466,7 @@ export function spawnClaude(params) {
|
|
|
460
466
|
const detail = joinErrorDetail(event['result'], event['errors']);
|
|
461
467
|
apiErrorText = joinErrorDetail(apiErrorText, detail) || apiErrorText;
|
|
462
468
|
const subtype = event['subtype'] ? ` subtype=${event['subtype']}` : '';
|
|
463
|
-
process.stderr.write(
|
|
469
|
+
process.stderr.write(errLine(`[bridge] result error${status != null ? ` (${status})` : ''}${subtype}: ${apiErrorText || 'unknown'}`) + '\n');
|
|
464
470
|
if (!detail) {
|
|
465
471
|
let raw;
|
|
466
472
|
try {
|
|
@@ -469,7 +475,7 @@ export function spawnClaude(params) {
|
|
|
469
475
|
catch {
|
|
470
476
|
raw = String(event);
|
|
471
477
|
}
|
|
472
|
-
process.stderr.write(
|
|
478
|
+
process.stderr.write(errLine(`[bridge] result raw: ${raw.slice(0, 2000)}`) + '\n');
|
|
473
479
|
}
|
|
474
480
|
}
|
|
475
481
|
}
|
|
@@ -541,6 +547,7 @@ export function spawnClaude(params) {
|
|
|
541
547
|
inputReleaseTimer.unref?.();
|
|
542
548
|
let lastMcpReconnectAt = 0;
|
|
543
549
|
let mcpReconnecting = false;
|
|
550
|
+
let servedPresenceTools = new Set();
|
|
544
551
|
const MCP_RECONNECT_THROTTLE_MS = 10_000;
|
|
545
552
|
const maybeReconnectRemoteMcp = (q) => {
|
|
546
553
|
const now = Date.now();
|
|
@@ -548,11 +555,11 @@ export function spawnClaude(params) {
|
|
|
548
555
|
return;
|
|
549
556
|
mcpReconnecting = true;
|
|
550
557
|
lastMcpReconnectAt = now;
|
|
551
|
-
process.stderr.write(
|
|
558
|
+
process.stderr.write(errLine('[bridge] remote MCP connection lost — reconnecting and retrying') + '\n');
|
|
552
559
|
onNotice?.('Reconnecting to 1Presence tools…');
|
|
553
560
|
void q.reconnectMcpServer(REMOTE_MCP_SERVER_NAME)
|
|
554
561
|
.then(() => process.stderr.write(paint(SECTION_COLORS.result, '[bridge] remote MCP reconnected') + '\n'))
|
|
555
|
-
.catch((err) => process.stderr.write(
|
|
562
|
+
.catch((err) => process.stderr.write(errLine(`[bridge] remote MCP reconnect failed: ${err.message}`) + '\n'))
|
|
556
563
|
.finally(() => { mcpReconnecting = false; });
|
|
557
564
|
};
|
|
558
565
|
try {
|
|
@@ -566,9 +573,10 @@ export function spawnClaude(params) {
|
|
|
566
573
|
const subtype = m.subtype;
|
|
567
574
|
if (subtype === 'init') {
|
|
568
575
|
const init = m;
|
|
569
|
-
const event = { type: 'system', subtype: 'init', model: init.model, apiKeySource: init.apiKeySource, claude_code_version: init.claude_code_version };
|
|
576
|
+
const event = { type: 'system', subtype: 'init', model: init.model, apiKeySource: init.apiKeySource, claude_code_version: init.claude_code_version, session_id: init.session_id };
|
|
570
577
|
if (handleEvent(event))
|
|
571
578
|
onEvent(event);
|
|
579
|
+
servedPresenceTools = new Set((init.tools ?? []).filter((t) => isPresenceToolName(t)));
|
|
572
580
|
const surface = assessRemoteMcpSurface(m);
|
|
573
581
|
const surfaceLine = `[bridge] mcp surface: ${REMOTE_MCP_SERVER_NAME}=${surface.status}, ${surface.toolCount} tool(s)`;
|
|
574
582
|
if (surfaceLine !== lastSurfaceLog) {
|
|
@@ -584,9 +592,9 @@ export function spawnClaude(params) {
|
|
|
584
592
|
detail = ` — ${remote.error.replace(/\s+/g, ' ').trim().slice(0, 300)}`;
|
|
585
593
|
}
|
|
586
594
|
catch { }
|
|
587
|
-
process.stderr.write(
|
|
595
|
+
process.stderr.write(errLine(`[bridge] FATAL no 1Presence tools this turn (${surface.status}) — ending the turn rather than answering blind${detail}`) + '\n');
|
|
588
596
|
if (surface.status === 'needs-auth' && !detail) {
|
|
589
|
-
process.stderr.write(
|
|
597
|
+
process.stderr.write(errLine('[bridge] ↳ the server asked for OAuth, which Local Mode does not use — this is NOT a stale local token, and restarting will not clear it. The gateway is rejecting this account\'s token on /mcp.') + '\n');
|
|
590
598
|
}
|
|
591
599
|
active.delete(conversationId);
|
|
592
600
|
clearTimeout(inputReleaseTimer);
|
|
@@ -598,7 +606,7 @@ export function spawnClaude(params) {
|
|
|
598
606
|
}
|
|
599
607
|
else if (subtype === 'api_retry') {
|
|
600
608
|
const r = m;
|
|
601
|
-
process.stderr.write(
|
|
609
|
+
process.stderr.write(errLine(`[bridge] api retry ${r.attempt ?? '?'}/${r.max_retries ?? '?'}${r.error_status != null ? ` (${r.error_status})` : ''}: ${r.error ?? 'unknown'}${r.retry_delay_ms != null ? `, next in ${r.retry_delay_ms}ms` : ''}`) + '\n');
|
|
602
610
|
const attempt = r.attempt ?? 0;
|
|
603
611
|
if (attempt >= 2 || (r.retry_delay_ms ?? 0) >= 2000) {
|
|
604
612
|
onNotice?.(formatRetryNotice(attempt, r.max_retries ?? 0, r.retry_delay_ms));
|
|
@@ -623,7 +631,7 @@ export function spawnClaude(params) {
|
|
|
623
631
|
const full = joinErrorDetail(`API Error: ${am.error}`, msgText, rid);
|
|
624
632
|
if (!apiErrorText || /^API Error: \w+$/.test(apiErrorText))
|
|
625
633
|
apiErrorText = full;
|
|
626
|
-
process.stderr.write(
|
|
634
|
+
process.stderr.write(errLine(`[bridge] assistant error: ${joinErrorDetail(am.error, msgText, rid)}`) + '\n');
|
|
627
635
|
break;
|
|
628
636
|
}
|
|
629
637
|
const event = { type: 'assistant', message: am.message, error: am.error };
|
|
@@ -646,7 +654,7 @@ export function spawnClaude(params) {
|
|
|
646
654
|
const resultText = toolResultText(b['content']);
|
|
647
655
|
if (b['is_error'] === true && isPermissionTransportFailure(resultText)) {
|
|
648
656
|
const detail = resultText.replace(/\s+/g, ' ').trim().slice(0, 300);
|
|
649
|
-
process.stderr.write(
|
|
657
|
+
process.stderr.write(errLine(`[bridge] FATAL permission transport lost — ending the turn rather than answering blind: ${detail}`) + '\n');
|
|
650
658
|
active.delete(conversationId);
|
|
651
659
|
clearTimeout(inputReleaseTimer);
|
|
652
660
|
input.release();
|
|
@@ -658,6 +666,7 @@ export function spawnClaude(params) {
|
|
|
658
666
|
toolName: toolNames.get(b['tool_use_id'] ?? '') ?? '',
|
|
659
667
|
isError: b['is_error'] === true,
|
|
660
668
|
text: resultText,
|
|
669
|
+
served: servedPresenceTools,
|
|
661
670
|
})) {
|
|
662
671
|
maybeReconnectRemoteMcp(q);
|
|
663
672
|
break;
|
|
@@ -735,7 +744,7 @@ export function spawnClaude(params) {
|
|
|
735
744
|
return;
|
|
736
745
|
const message = err?.message ?? String(err);
|
|
737
746
|
const stack = err?.stack;
|
|
738
|
-
process.stderr.write(
|
|
747
|
+
process.stderr.write(errLine(`[bridge] query() threw: ${stack || message}`) + '\n');
|
|
739
748
|
if (/40[13]\b|unauthor|invalid (api key|authentication)|please run \/login/i.test(message)) {
|
|
740
749
|
sawAuthFailure = true;
|
|
741
750
|
}
|
package/dist/index.js
CHANGED
|
@@ -280,7 +280,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
|
|
|
280
280
|
if (!isTokenValid(auth.token)) {
|
|
281
281
|
const message = 'Authentication expired and refresh failed — please restart the bridge to sign in again.';
|
|
282
282
|
stopTurnTimer();
|
|
283
|
-
console.error(paint(SECTION_COLORS.error, `[bridge] ${message} (${err.message})`));
|
|
283
|
+
console.error(paint(SECTION_COLORS.error, `[bridge] ${message} (${err.message}) · session ${sessionId ?? conversationId}`));
|
|
284
284
|
if (currentWs?.readyState === WebSocket.OPEN) {
|
|
285
285
|
currentWs.send(JSON.stringify({ type: 'error', conversationId, message }));
|
|
286
286
|
}
|
|
@@ -294,7 +294,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
|
|
|
294
294
|
catch (err) {
|
|
295
295
|
const message = `System prompt refresh failed: ${err.message}`;
|
|
296
296
|
stopTurnTimer();
|
|
297
|
-
console.error(paint(SECTION_COLORS.error, `[${new Date().toLocaleTimeString()}] ✗ ${message}`));
|
|
297
|
+
console.error(paint(SECTION_COLORS.error, `[${new Date().toLocaleTimeString()}] ✗ ${message} · session ${sessionId ?? conversationId}`));
|
|
298
298
|
if (currentWs?.readyState === WebSocket.OPEN) {
|
|
299
299
|
currentWs.send(JSON.stringify({ type: 'error', conversationId, message }));
|
|
300
300
|
}
|
|
@@ -352,6 +352,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
|
|
|
352
352
|
spawnClaude({
|
|
353
353
|
conversationId,
|
|
354
354
|
presenceSessionId: claudePinnedSessionId,
|
|
355
|
+
chatSessionId: turnSessionId,
|
|
355
356
|
text,
|
|
356
357
|
uid: activeAuth.uid,
|
|
357
358
|
history,
|
|
@@ -414,7 +415,7 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
|
|
|
414
415
|
},
|
|
415
416
|
onError: (message, usage, model) => {
|
|
416
417
|
const elapsed = stopTurnTimer();
|
|
417
|
-
console.error(paint(SECTION_COLORS.error, `[${new Date().toLocaleTimeString()}] ✗ ${message} (${formatElapsed(elapsed)})`));
|
|
418
|
+
console.error(paint(SECTION_COLORS.error, `[${new Date().toLocaleTimeString()}] ✗ ${message} (${formatElapsed(elapsed)}) · session ${turnSessionId}`));
|
|
418
419
|
const mapped = toBridgeUsage(usage);
|
|
419
420
|
if (currentWs?.readyState === WebSocket.OPEN) {
|
|
420
421
|
currentWs.send(JSON.stringify({
|
|
@@ -555,7 +556,7 @@ function connect(auth, retryDelay = 1000) {
|
|
|
555
556
|
startTurnTimer();
|
|
556
557
|
handleMessage(conversationId, text, sessionId ?? null, hist, auth, vaultFileOpen, clientCapabilities, syncedFolders, agentSlug, model).catch((err) => {
|
|
557
558
|
stopTurnTimer();
|
|
558
|
-
console.error(paint(SECTION_COLORS.error, `[bridge] handleMessage error: ${err.message}`));
|
|
559
|
+
console.error(paint(SECTION_COLORS.error, `[bridge] handleMessage error: ${err.message} · session ${sessionId ?? conversationId}`));
|
|
559
560
|
});
|
|
560
561
|
});
|
|
561
562
|
ws.on('close', (code) => {
|