@1presence/bridge 0.45.0 → 0.46.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
@@ -161,6 +161,27 @@ function describeCliFailure(apiErrorText, authFailure) {
161
161
  }
162
162
  return 'Local Mode stopped unexpectedly. Please try again.';
163
163
  }
164
+ /**
165
+ * Copy for an actionable rate-limit notice. The SDK emits `rate_limit_event`
166
+ * whenever rate-limit info CHANGES — including the routine `allowed` case on
167
+ * (nearly) every turn — so the caller must drop `allowed` and only invoke this
168
+ * for `allowed_warning` / `rejected`. `resetsAt` is a Unix timestamp; the SDK
169
+ * uses seconds, but we accept ms too in case that changes upstream.
170
+ */
171
+ function formatRateLimitNotice(status, resetsAt) {
172
+ const when = typeof resetsAt === 'number' && resetsAt > 0
173
+ ? new Date(resetsAt < 1e12 ? resetsAt * 1000 : resetsAt)
174
+ .toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' })
175
+ : null;
176
+ if (status === 'rejected') {
177
+ return when
178
+ ? `Pausing for an upstream rate limit — resumes around ${when}.`
179
+ : 'Pausing briefly for an upstream rate limit, then continuing…';
180
+ }
181
+ return when
182
+ ? `Approaching your usage limit — window resets around ${when}.`
183
+ : 'Approaching your usage limit for this period.';
184
+ }
164
185
  // ─── Prompt construction ─────────────────────────────────────────────────────────
165
186
  //
166
187
  // The gateway pushes the FULL conversation (sanitised via @presence/shared
@@ -292,14 +313,24 @@ export function spawnClaude(params) {
292
313
  // credentials in the Keychain), not an API key that would bill a separate
293
314
  // account. Options.env REPLACES the subprocess env, so spread the rest through.
294
315
  const { ANTHROPIC_API_KEY: _stripped, ...safeEnv } = process.env;
295
- // Raise Claude Code's MCP tool-result token cap (default 25,000). Over the cap,
296
- // the CLI saves the result to a tool-results/*.txt file and returns a stub that
297
- // says "read it with Read" — but Local Mode disables every built-in tool
298
- // (`tools: []` below), so the model has no Read tool and stalls. Hosted mode has
299
- // no such cap (results go straight to the Anthropic SDK), so a large skill body
300
- // or vault read breaks Local Mode only a parity bug. Lifting the cap lets
301
- // legitimate 1Presence MCP results pass inline, matching hosted. An operator
302
- // override (env already set) wins. Bounded by the model's context window anyway.
316
+ // Claude Code truncates "large" MCP tool results (token count over
317
+ // MAX_MCP_OUTPUT_TOKENS, default 25,000) one of two ways, gated by
318
+ // ENABLE_MCP_LARGE_OUTPUT_FILES:
319
+ // default (feature ON): the full result is written to a tool-results/*.json
320
+ // file and the model is handed a `<persisted-output>` stub telling it to Read
321
+ // the file. Local Mode disables every built-in tool (`tools: []` below), so
322
+ // the model has no Read it reaches for vault_read, which resolves the local
323
+ // path against the GCS prefix and fails ("No such object"). Unrecoverable.
324
+ // • OFF: the result is inline-truncated to the cap — graceful degradation the
325
+ // model can actually work with.
326
+ // Hosted mode pipes results straight to the Anthropic SDK with no truncation, so
327
+ // this breaks Local Mode only — a parity bug. Fix on two fronts: force the
328
+ // file-save path off so the unrecoverable failure mode is impossible, and raise
329
+ // the cap so legitimate large results (skill bodies, vault reads) pass inline
330
+ // instead of truncating. Operator overrides (env already set) win on both.
331
+ if (!safeEnv['ENABLE_MCP_LARGE_OUTPUT_FILES']) {
332
+ safeEnv['ENABLE_MCP_LARGE_OUTPUT_FILES'] = 'false';
333
+ }
303
334
  if (!safeEnv['MAX_MCP_OUTPUT_TOKENS']) {
304
335
  safeEnv['MAX_MCP_OUTPUT_TOKENS'] = '200000';
305
336
  }
@@ -568,9 +599,16 @@ export function spawnClaude(params) {
568
599
  break;
569
600
  }
570
601
  case 'rate_limit_event': {
571
- // SDK surfaces upstream rate-limit pauses; it retries internally.
572
- // Admin-only ephemeral notice jargon is fine in Local Mode.
573
- onNotice?.('Claude Code is pausing briefly for an upstream rate limit, then continuing…');
602
+ // The SDK emits this whenever rate-limit info CHANGES including the
603
+ // routine `allowed` case on (nearly) every turn, which previously
604
+ // spammed a phantom "pausing" notice (see vault/Bugs.md). Only surface
605
+ // a notice when the user is actually warned or throttled.
606
+ const info = m.rate_limit_info;
607
+ const status = info?.status;
608
+ if (status === 'allowed_warning' || status === 'rejected') {
609
+ // Admin-only ephemeral notice — jargon is fine in Local Mode.
610
+ onNotice?.(formatRateLimitNotice(status, info?.resetsAt));
611
+ }
574
612
  break;
575
613
  }
576
614
  // Everything else (partial messages, status, hooks, notifications,
package/dist/index.js CHANGED
@@ -327,6 +327,8 @@ async function handleMessage(conversationId, text, sessionId, history, auth, vau
327
327
  // Ephemeral, non-persisted thread notice (admin-only Local Mode). Relayed
328
328
  // by the gateway to the PWA SSE stream as a `notice` AgentEvent; it does
329
329
  // NOT go through the turn accumulator, so it never lands in history.
330
+ // Log it too: anything the user sees in chat must have a bridge-log trail.
331
+ console.log(`[${new Date().toLocaleTimeString()}] ⚠ notice: ${message}`);
330
332
  if (currentWs?.readyState === WebSocket.OPEN) {
331
333
  currentWs.send(JSON.stringify({ type: 'notice', conversationId, message }));
332
334
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.45.0",
3
+ "version": "0.46.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": {