@1presence/bridge 0.42.0 → 0.44.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 CHANGED
@@ -82,6 +82,26 @@ function debugBlock(label, colorCode, body) {
82
82
  const rule = `── ${label} `.padEnd(64, '─');
83
83
  process.stderr.write(`\n${paint(colorCode, rule)}\n${body.trimEnd()}\n`);
84
84
  }
85
+ // Strip confabulated tool-call XML out of assistant text. Real tool calls arrive
86
+ // as structured tool_use blocks, never as text — so when assistant text contains
87
+ // <function_calls>/<function_results>/<invoke>, the model is role-playing the tool
88
+ // protocol because it had no callable tools, and that raw XML (often invented
89
+ // internal-looking content) must never reach the user. Kept as a tiny local copy
90
+ // rather than importing @presence/shared so the published bridge stays decoupled.
91
+ const TOOL_CALL_XML_RE = /<function_calls\b|<function_results\b|<invoke\b/i;
92
+ function stripToolCallXml(text) {
93
+ if (!text || !TOOL_CALL_XML_RE.test(text))
94
+ return text;
95
+ let out = text
96
+ .replace(/<function_calls>[\s\S]*?<\/function_calls>/gi, '')
97
+ .replace(/<function_results>[\s\S]*?<\/function_results>/gi, '')
98
+ .replace(/<invoke\b[\s\S]*?<\/invoke>/gi, '');
99
+ // Truncated / unclosed opener: drop from the first stray opener to the end.
100
+ out = out.replace(/<function_calls\b[\s\S]*$/i, '')
101
+ .replace(/<function_results\b[\s\S]*$/i, '')
102
+ .replace(/<invoke\b[\s\S]*$/i, '');
103
+ return out.trim();
104
+ }
85
105
  // Render one replayed-history content block as a single readable line for the
86
106
  // debug transcript. Tool calls and results are inlined so a history turn shows
87
107
  // exactly what the model received — text, the tools it ran, and what they
@@ -348,7 +368,19 @@ export function spawnClaude(params) {
348
368
  }
349
369
  }
350
370
  else if (block['type'] === 'text') {
351
- const blockText = block['text'];
371
+ let blockText = block['text'];
372
+ // Drop confabulated tool-call XML before this event is forwarded to
373
+ // the gateway (onEvent forwards THIS object, so mutate it in place).
374
+ // Happens when a turn ran with no callable tools and the model
375
+ // role-played the protocol in prose. See vault/Bugs.md 2026-05-28.
376
+ if (blockText && TOOL_CALL_XML_RE.test(blockText)) {
377
+ const cleaned = stripToolCallXml(blockText);
378
+ if (cleaned !== blockText) {
379
+ process.stderr.write(paint(SECTION_COLORS.result, `[bridge] suppressed confabulated tool-call XML in assistant text`) + '\n');
380
+ block['text'] = cleaned;
381
+ blockText = cleaned;
382
+ }
383
+ }
352
384
  if (blockText) {
353
385
  // The CLI/SDK can report auth/API failures as a synthetic assistant
354
386
  // text turn whose wording varies. Detect by the structured signal
@@ -414,6 +446,11 @@ export function spawnClaude(params) {
414
446
  if (!apiErrorText && typeof event['result'] === 'string') {
415
447
  apiErrorText = event['result'].trim();
416
448
  }
449
+ // Operator visibility — a result-borne error would otherwise reach the
450
+ // user as a chat error with NOTHING in the bridge logs (the failure
451
+ // mode that made the empty-content `invalid_request` regression so hard
452
+ // to diagnose). Mechanical log only; no product logic.
453
+ process.stderr.write(paint(SECTION_COLORS.result, `[bridge] result error${status ? ` (${status})` : ''}: ${apiErrorText || 'unknown'}`) + '\n');
417
454
  }
418
455
  }
419
456
  return true;
@@ -440,7 +477,10 @@ export function spawnClaude(params) {
440
477
  settingSources: [], // no user/project settings or memory
441
478
  allowedTools: ['mcp__1presence__*'], // auto-approve our MCP surface (no prompt)
442
479
  canUseTool, // hard deny anything else
443
- extraArgs: { tools: '' }, // disable ALL built-in tools (= CLI --tools "")
480
+ tools: [], // disable ALL built-in tools; MCP tools come via mcpServers and survive.
481
+ // (The old `extraArgs: { tools: '' }` passed a malformed --tools "" that
482
+ // cleared the whole tool surface — including MCP — so the model had no
483
+ // callable tools and confabulated tool calls as text. See vault/Bugs.md.)
444
484
  cwd: BRIDGE_CWD,
445
485
  abortController: abort,
446
486
  includePartialMessages: false, // whole messages, matching the old non-partial path
package/dist/config.js CHANGED
@@ -78,12 +78,13 @@ function detectClaudeDefaultModel() {
78
78
  // `--model` flag passed to the subprocess).
79
79
  const MODEL_OPTIONS = [
80
80
  { num: 1, model: null, label: 'Use Claude Code default' },
81
- { num: 2, model: 'claude-opus-4-7', label: 'claude-opus-4-7' },
82
- { num: 3, model: 'claude-sonnet-4-6', label: 'claude-sonnet-4-6' },
83
- { num: 4, model: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
81
+ { num: 2, model: 'claude-opus-4-8', label: 'claude-opus-4-8' },
82
+ { num: 3, model: 'claude-opus-4-7', label: 'claude-opus-4-7' },
83
+ { num: 4, model: 'claude-sonnet-4-6', label: 'claude-sonnet-4-6' },
84
+ { num: 5, model: 'claude-haiku-4-5', label: 'claude-haiku-4-5' },
84
85
  ];
85
86
  const PROMPT_TIMEOUT_MS = 10_000;
86
- const DEFAULT_OPTION_NUM = 1;
87
+ const DEFAULT_OPTION_NUM = 2;
87
88
  function promptForModel(defaultModel) {
88
89
  return new Promise((resolve) => {
89
90
  const initialIdx = Math.max(0, MODEL_OPTIONS.findIndex((o) => o.num === DEFAULT_OPTION_NUM));
package/dist/index.js CHANGED
@@ -182,6 +182,12 @@ function writeMcpConfig(auth) {
182
182
  type: 'sse',
183
183
  url: `${GATEWAY_HTTP}/mcp`,
184
184
  headers: { Authorization: `Bearer ${token}` },
185
+ // Force every 1Presence tool into the prompt from turn 1. Without this the
186
+ // SDK defers MCP tools behind tool-search, so the model sees tool *names*
187
+ // (from the system prompt) but has nothing callable and confabulates the
188
+ // call + result as text. Also blocks the turn until the MCP server connects
189
+ // (5s cap) — a loud failure beats a silent tool-less run. See vault/Bugs.md.
190
+ alwaysLoad: true,
185
191
  },
186
192
  },
187
193
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.42.0",
3
+ "version": "0.44.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
5
  "type": "module",
6
6
  "bin": {
@@ -25,7 +25,7 @@
25
25
  "devDependencies": {
26
26
  "@types/node": "^20.0.0",
27
27
  "@types/ws": "^8.18.1",
28
- "tsx": "^4.19.0",
28
+ "tsx": "^4.22.3",
29
29
  "typescript": "^5.5.0"
30
30
  }
31
31
  }