@1presence/bridge 0.81.0 → 0.82.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.
Files changed (2) hide show
  1. package/dist/claude.js +49 -5
  2. package/package.json +1 -1
package/dist/claude.js CHANGED
@@ -123,6 +123,10 @@ const OUTBOUND_NETWORK_TOOLS = new Set([
123
123
  'mcp__1presence__web_fetch',
124
124
  'mcp__1presence__web_search',
125
125
  ]);
126
+ const PERMISSION_TRANSPORT_DEAD_RE = /Tool permission request failed/i;
127
+ export function isPermissionTransportFailure(text) {
128
+ return PERMISSION_TRANSPORT_DEAD_RE.test(text);
129
+ }
126
130
  export function shouldReconnectRemoteMcp(args) {
127
131
  const { toolName, isError, text } = args;
128
132
  if (MCP_SESSION_EXPIRED_RE.test(text))
@@ -193,9 +197,26 @@ function buildPromptMessages(history) {
193
197
  });
194
198
  return out;
195
199
  }
196
- async function* promptStream(messages) {
197
- for (const m of messages)
198
- yield m;
200
+ const INPUT_HOLD_MAX_MS = 20 * 60_000;
201
+ export function makeGatedPromptStream(messages) {
202
+ let open;
203
+ let released = false;
204
+ const gate = new Promise((resolve) => { open = resolve; });
205
+ async function* generate() {
206
+ for (const m of messages)
207
+ yield m;
208
+ await gate;
209
+ }
210
+ return {
211
+ stream: generate(),
212
+ release() {
213
+ if (released)
214
+ return;
215
+ released = true;
216
+ open();
217
+ },
218
+ get isReleased() { return released; },
219
+ };
199
220
  }
200
221
  export function spawnClaude(params) {
201
222
  const { conversationId, presenceSessionId, text, uid, history, vaultFileOpen, clientCapabilities, syncedFolders, model: perTurnModel, onEvent, onDone, onError, onNotice } = params;
@@ -497,6 +518,10 @@ export function spawnClaude(params) {
497
518
  ...(pinnedModel ? { model: pinnedModel } : {}),
498
519
  };
499
520
  const promptMessages = buildPromptMessages(history);
521
+ const input = makeGatedPromptStream(promptMessages);
522
+ abort.signal.addEventListener('abort', () => input.release(), { once: true });
523
+ const inputReleaseTimer = setTimeout(() => input.release(), INPUT_HOLD_MAX_MS);
524
+ inputReleaseTimer.unref?.();
500
525
  let lastMcpReconnectAt = 0;
501
526
  let mcpReconnecting = false;
502
527
  const MCP_RECONNECT_THROTTLE_MS = 10_000;
@@ -514,7 +539,7 @@ export function spawnClaude(params) {
514
539
  .finally(() => { mcpReconnecting = false; });
515
540
  };
516
541
  try {
517
- const q = query({ prompt: promptStream(promptMessages), options });
542
+ const q = query({ prompt: input.stream, options });
518
543
  for await (const m of q) {
519
544
  if (m.isReplay)
520
545
  continue;
@@ -574,10 +599,21 @@ export function spawnClaude(params) {
574
599
  const b = block;
575
600
  if (b['type'] !== 'tool_result')
576
601
  continue;
602
+ const resultText = toolResultText(b['content']);
603
+ if (b['is_error'] === true && isPermissionTransportFailure(resultText)) {
604
+ const detail = resultText.replace(/\s+/g, ' ').trim().slice(0, 300);
605
+ process.stderr.write(paint(SECTION_COLORS.error, `[bridge] FATAL permission transport lost — ending the turn rather than answering blind: ${detail}`) + '\n');
606
+ active.delete(conversationId);
607
+ clearTimeout(inputReleaseTimer);
608
+ input.release();
609
+ abort.abort();
610
+ onError('Local Mode lost its tool connection mid-turn, so nothing was read or changed. Please send that again.', usage, extractedModel);
611
+ return;
612
+ }
577
613
  if (shouldReconnectRemoteMcp({
578
614
  toolName: toolNames.get(b['tool_use_id'] ?? '') ?? '',
579
615
  isError: b['is_error'] === true,
580
- text: toolResultText(b['content']),
616
+ text: resultText,
581
617
  })) {
582
618
  maybeReconnectRemoteMcp(q);
583
619
  break;
@@ -602,6 +638,10 @@ export function spawnClaude(params) {
602
638
  };
603
639
  if (handleEvent(event))
604
640
  onEvent(event);
641
+ if (producedRealOutput) {
642
+ clearTimeout(inputReleaseTimer);
643
+ input.release();
644
+ }
605
645
  break;
606
646
  }
607
647
  case 'auth_status': {
@@ -643,6 +683,8 @@ export function spawnClaude(params) {
643
683
  }
644
684
  catch (err) {
645
685
  active.delete(conversationId);
686
+ clearTimeout(inputReleaseTimer);
687
+ input.release();
646
688
  if (abort.signal.aborted)
647
689
  return;
648
690
  if (killedForViolation)
@@ -659,6 +701,8 @@ export function spawnClaude(params) {
659
701
  return;
660
702
  }
661
703
  active.delete(conversationId);
704
+ clearTimeout(inputReleaseTimer);
705
+ input.release();
662
706
  if (killedForViolation)
663
707
  return;
664
708
  if (abort.signal.aborted)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.81.0",
3
+ "version": "0.82.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",