@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 +49 -11
- package/dist/index.js +2 -0
- package/package.json +1 -1
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
|
-
//
|
|
296
|
-
//
|
|
297
|
-
//
|
|
298
|
-
// (
|
|
299
|
-
//
|
|
300
|
-
//
|
|
301
|
-
//
|
|
302
|
-
//
|
|
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
|
|
572
|
-
//
|
|
573
|
-
|
|
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
|
}
|