sneakoscope 10.5.9 → 10.6.1

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/README.md CHANGED
@@ -17,7 +17,7 @@
17
17
  Sneakoscope Codex (`sks`) is an open-source local tool for the Codex CLI and Codex desktop app. It adds a macOS control center, bounded agent workflows, repository context, and evidence checks for SKS releases.
18
18
  <!-- END SKS SEARCH VISIBILITY MARKETING -->
19
19
 
20
- Current package: **SKS 10.5.9**, prepared for npm publication.
20
+ Current package: **SKS 10.6.1**, prepared for npm publication.
21
21
  [Release notes](https://github.com/mandarange/Sneakoscope-Codex/releases/tag/v10.5.8)
22
22
  · [Changelog](CHANGELOG.md). Install the latest stable release from npm.
23
23
 
@@ -259,7 +259,7 @@ dependencies = [
259
259
 
260
260
  [[package]]
261
261
  name = "sks-core"
262
- version = "10.5.9"
262
+ version = "10.6.1"
263
263
  dependencies = [
264
264
  "globset",
265
265
  "grep-matcher",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "sks-core"
3
- version = "10.5.9"
3
+ version = "10.6.1"
4
4
  edition = "2021"
5
5
 
6
6
  [dependencies]
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema": "sks.skills-manifest.v1",
3
- "package_version": "10.5.9",
3
+ "package_version": "10.6.1",
4
4
  "skills": [
5
5
  {
6
6
  "canonical_name": "sks",
@@ -13,9 +13,15 @@ export const BRIDGE_COMPACTION_ENVELOPE_PREFIX = 'sks-bridge-compaction.v1.';
13
13
  const COMPACTION_ITEM_TYPES = new Set(['compaction', 'compaction_summary', 'context_compaction']);
14
14
  const MAX_SUMMARY_CHARS = 512 * 1024;
15
15
  const MAX_RETAINED_USER_MESSAGE_CHARS = 80_000;
16
+ const SUMMARY_REASONING_EFFORT_CEILING = 'medium';
17
+ const EFFORTS_ABOVE_SUMMARY_CEILING = new Set(['high', 'xhigh', 'max', 'ultra']);
16
18
  function isObject(value) {
17
19
  return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
18
20
  }
21
+ export function safeUpstreamErrorId(value) {
22
+ const text = String(value ?? '').trim();
23
+ return /^[A-Za-z0-9_.:-]{1,64}$/.test(text) ? text : null;
24
+ }
19
25
  export function isCompactionTriggerItem(item) {
20
26
  return isObject(item) && item.type === 'compaction_trigger';
21
27
  }
@@ -56,6 +62,10 @@ export function expandBridgeCompactionItems(payload) {
56
62
  payload.input = input;
57
63
  return changed;
58
64
  }
65
+ function carriesToolDefinitions(body) {
66
+ return (Array.isArray(body.tools) && body.tools.length > 0)
67
+ || (Array.isArray(body.input) && body.input.some((item) => isObject(item) && item.type === 'additional_tools'));
68
+ }
59
69
  export function buildCompactionSummaryRequest(payload, upstreamModel) {
60
70
  const { type: _eventType, ...request } = payload;
61
71
  const history = Array.isArray(payload.input) ? payload.input.filter((item) => !isCompactionTriggerItem(item)) : [];
@@ -68,17 +78,110 @@ export function buildCompactionSummaryRequest(payload, upstreamModel) {
68
78
  ],
69
79
  stream: true,
70
80
  };
71
- if (Array.isArray(body.tools) && body.tools.length > 0)
81
+ if (carriesToolDefinitions(body))
72
82
  body.tool_choice = 'none';
73
83
  else {
74
84
  delete body.tools;
75
85
  delete body.tool_choice;
76
86
  }
77
- delete body.parallel_tool_calls;
87
+ body.parallel_tool_calls = false;
88
+ if (isObject(body.reasoning) && EFFORTS_ABOVE_SUMMARY_CEILING.has(String(body.reasoning.effort))) {
89
+ body.reasoning = { ...body.reasoning, effort: SUMMARY_REASONING_EFFORT_CEILING };
90
+ }
78
91
  delete body.previous_response_id;
79
92
  expandBridgeCompactionItems(body);
80
93
  return body;
81
94
  }
95
+ function frameEvent(frame) {
96
+ try {
97
+ const event = JSON.parse(frame.toString('utf8'));
98
+ return isObject(event) ? event : null;
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ }
104
+ export class ResponsesContinuationChain {
105
+ maxBytes;
106
+ links = [];
107
+ bytes = 0;
108
+ constructor(maxBytes) {
109
+ this.maxBytes = maxBytes;
110
+ }
111
+ sent(create, frame) {
112
+ const previous = typeof create.previous_response_id === 'string' ? create.previous_response_id : null;
113
+ const last = this.links?.at(-1);
114
+ const link = { create: frame, outputs: [], responseId: null, settled: false };
115
+ if (!previous) {
116
+ this.links = [link];
117
+ this.bytes = frame.length;
118
+ }
119
+ else if (this.links && last?.settled && last.responseId === previous) {
120
+ this.links.push(link);
121
+ this.track(frame.length);
122
+ }
123
+ else {
124
+ this.links = null;
125
+ }
126
+ }
127
+ received(frame) {
128
+ const link = this.links?.at(-1);
129
+ if (!link || link.settled)
130
+ return;
131
+ if (frame.includes('"response.output_item.done"')) {
132
+ link.outputs.push(frame);
133
+ this.track(frame.length);
134
+ }
135
+ else if (frame.includes('"response.completed"')) {
136
+ const event = frameEvent(frame);
137
+ if (event?.type !== 'response.completed')
138
+ return;
139
+ link.settled = true;
140
+ link.responseId = isObject(event.response) && typeof event.response.id === 'string' ? event.response.id : null;
141
+ }
142
+ else if (frame.includes('"response.failed"') || frame.includes('"response.incomplete"') || frame.includes('"type":"error"')) {
143
+ const type = frameEvent(frame)?.type;
144
+ if (type === 'response.failed' || type === 'response.incomplete' || type === 'error') {
145
+ link.settled = true;
146
+ link.responseId = null;
147
+ }
148
+ }
149
+ }
150
+ fullInput(create) {
151
+ const delta = Array.isArray(create.input) ? create.input : [];
152
+ const previous = typeof create.previous_response_id === 'string' ? create.previous_response_id : null;
153
+ if (!previous)
154
+ return delta;
155
+ const last = this.links?.at(-1);
156
+ if (!this.links || !last?.settled || last.responseId !== previous)
157
+ return null;
158
+ const input = [];
159
+ for (const link of this.links) {
160
+ const sent = frameEvent(link.create);
161
+ if (!sent || !Array.isArray(sent.input))
162
+ return null;
163
+ input.push(...sent.input);
164
+ for (const frame of link.outputs) {
165
+ const event = frameEvent(frame);
166
+ if (event?.type === 'response.output_item.done' && isObject(event.item))
167
+ input.push(event.item);
168
+ }
169
+ }
170
+ return [...input, ...delta];
171
+ }
172
+ track(size) {
173
+ this.bytes += size;
174
+ if (this.bytes > this.maxBytes)
175
+ this.links = null;
176
+ }
177
+ }
178
+ function summaryError(error, fallback) {
179
+ const record = isObject(error) ? error : {};
180
+ return {
181
+ code: safeUpstreamErrorId(record.code) ?? safeUpstreamErrorId(record.type) ?? fallback,
182
+ param: safeUpstreamErrorId(record.param),
183
+ };
184
+ }
82
185
  function messageText(item) {
83
186
  if (!isObject(item) || item.type !== 'message' || (item.role !== undefined && item.role !== 'assistant'))
84
187
  return '';
@@ -97,10 +200,16 @@ export function parseProviderSummary(text) {
97
200
  const parsed = JSON.parse(trimmed);
98
201
  const response = isObject(parsed) && isObject(parsed.response) ? parsed.response : parsed;
99
202
  const summary = outputText(isObject(response) ? response.output : null).trim();
100
- return { summary: summary || null, usage: isObject(response) && isObject(response.usage) ? response.usage : null, failed: !summary };
203
+ const failure = isObject(response) && isObject(response.error) ? summaryError(response.error, 'response_failed') : null;
204
+ return {
205
+ summary: summary || null,
206
+ usage: isObject(response) && isObject(response.usage) ? response.usage : null,
207
+ failed: !summary,
208
+ error: summary ? null : failure,
209
+ };
101
210
  }
102
211
  catch {
103
- return { summary: null, usage: null, failed: true };
212
+ return { summary: null, usage: null, failed: true, error: null };
104
213
  }
105
214
  }
106
215
  const finished = [];
@@ -109,6 +218,7 @@ export function parseProviderSummary(text) {
109
218
  let usage = null;
110
219
  let completed = false;
111
220
  let failed = false;
221
+ let error = null;
112
222
  for (const block of text.split(/\r?\n\r?\n/)) {
113
223
  const data = block.split(/\r?\n/).filter((line) => line.startsWith('data:')).map((line) => line.slice(5).trimStart()).join('\n');
114
224
  if (!data || data === '[DONE]')
@@ -142,20 +252,41 @@ export function parseProviderSummary(text) {
142
252
  }
143
253
  break;
144
254
  case 'response.failed':
145
- case 'response.incomplete':
255
+ failed = true;
256
+ error ??= summaryError(isObject(event.response) ? event.response.error : null, 'response_failed');
257
+ break;
258
+ case 'response.incomplete': {
259
+ failed = true;
260
+ const details = isObject(event.response) ? event.response.incomplete_details : null;
261
+ error ??= { code: 'response_incomplete', param: isObject(details) ? safeUpstreamErrorId(details.reason) : null };
262
+ break;
263
+ }
146
264
  case 'error':
147
265
  failed = true;
266
+ error ??= summaryError(isObject(event.error) ? event.error : { code: event.code, param: event.param }, 'response_failed');
148
267
  break;
149
268
  default:
150
269
  break;
151
270
  }
152
271
  }
153
272
  const summary = (finished.join('\n\n') || completedOutput || deltas).trim();
154
- return { summary: summary || null, usage, failed: failed || !completed || !summary };
273
+ return { summary: summary || null, usage, failed: failed || !completed || !summary, error };
155
274
  }
156
275
  export function summaryWithinBounds(summary) {
157
276
  return typeof summary === 'string' && summary.length > 0 && summary.length <= MAX_SUMMARY_CHARS;
158
277
  }
278
+ export function summaryFailure(result, status) {
279
+ const named = result.error ? `:${result.error.code}${result.error.param ? `:${result.error.param}` : ''}` : '';
280
+ if (status >= 300) {
281
+ return {
282
+ code: status === 429 ? 'rate_limit_exceeded' : result.error?.code ?? 'bridge_upstream_request_failed',
283
+ logCode: `bridge_compaction_summary_status_${status}${named}`,
284
+ };
285
+ }
286
+ return result.error
287
+ ? { code: result.error.code, logCode: `bridge_compaction_summary_failed${named}` }
288
+ : { code: 'bridge_compaction_summary_unusable', logCode: 'bridge_compaction_summary_unusable' };
289
+ }
159
290
  function wholeNumber(value) {
160
291
  return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? Math.floor(value) : null;
161
292
  }
@@ -6,9 +6,9 @@ import { BRIDGE_OFFICIAL_ROUTE_ID } from '../bridge-contracts.js';
6
6
  import { buildOfficialPassthroughHeaders, buildProviderUpstreamHeaders, rewriteResponseHeaders } from './header-policy.js';
7
7
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
8
8
  import { OPENROUTER_ONLY_REFUSAL_CODES, openRouterOnlyEnabled } from './exclusive-provider-guard.js';
9
- import { ensureDesktopBridgeRemoteTarget, isUnreachableUpstreamError, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader } from './security.js';
9
+ import { ensureDesktopBridgeRemoteTarget, isNoRouteUpstreamError, isUnreachableUpstreamError, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode, singleBridgeHeader } from './security.js';
10
10
  import { desktopBridgeListenOrigin } from './state.js';
11
- import { buildCompactionSummaryRequest, compactEndpointResult, compactionCreatedEvent, compactionFailedEvent, compactionResponseEvents, expandBridgeCompactionItems, newCompactionResponseId, parseProviderSummary, requestsRemoteCompaction, sseEvent, summaryWithinBounds, } from './compaction-adapter.js';
11
+ import { buildCompactionSummaryRequest, compactEndpointResult, compactionCreatedEvent, compactionFailedEvent, compactionResponseEvents, expandBridgeCompactionItems, newCompactionResponseId, parseProviderSummary, requestsRemoteCompaction, safeUpstreamErrorId, sseEvent, summaryFailure, summaryWithinBounds, } from './compaction-adapter.js';
12
12
  import { DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES, DesktopBridgeError, DesktopBridgeRequestBodyTooLargeError } from './types.js';
13
13
  const MAX_UPSTREAM_ERROR_BODY_BYTES = 1024 * 1024;
14
14
  function decodeBridgeRequestBody(body, encoding, maximum) {
@@ -48,10 +48,21 @@ function decodeBridgeRequestBody(body, encoding, maximum) {
48
48
  throw new DesktopBridgeError('bridge_responses_body_invalid_json');
49
49
  }
50
50
  }
51
+ const encodedBodyRefusals = new Set();
52
+ function keepsClientEncoding(route) {
53
+ return route.provider_id === BRIDGE_OFFICIAL_ROUTE_ID
54
+ || (route.provider_id === 'codex-lb' && !encodedBodyRefusals.has(route.provider_id));
55
+ }
56
+ async function reencodeLikeClient(plain, encoding) {
57
+ const compress = zlib.zstdCompress;
58
+ if (encoding !== 'zstd' || !compress)
59
+ return null;
60
+ return new Promise((resolve) => compress(plain, (error, result) => resolve(error ? null : result)));
61
+ }
51
62
  const RESPONSES_PATHS = ['/backend-api/codex/responses', '/api/v1/responses', '/v1/responses'];
52
63
  function modelBodyKind(req, pathname, config) {
53
64
  if (RESPONSES_PATHS.includes(pathname))
54
- return 'responses';
65
+ return req.method === 'POST' ? 'responses' : null;
55
66
  return openRouterOnlyEnabled(config) && req.method === 'POST'
56
67
  && RESPONSES_PATHS.some((prefix) => pathname.startsWith(`${prefix}/`)) ? 'sub-endpoint' : null;
57
68
  }
@@ -91,14 +102,17 @@ export async function prepareDesktopBridgeRequest(req, config) {
91
102
  let body = null;
92
103
  let payload = null;
93
104
  let contentEncodingStripped = false;
105
+ let encoding = '';
106
+ let encodedBody = null;
94
107
  const bodyKind = modelBodyKind(req, pathname, config);
95
108
  if (bodyKind) {
96
109
  const maximum = config.maxRequestBodyBytes ?? DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES;
97
110
  body = await readBoundedBody(req, maximum);
98
- const encoding = String(req.headers['content-encoding'] || '').trim().toLowerCase();
111
+ encoding = String(req.headers['content-encoding'] || '').trim().toLowerCase();
99
112
  let decoded = body;
100
113
  if (encoding && encoding !== 'identity') {
101
114
  decoded = decodeBridgeRequestBody(body, encoding, maximum);
115
+ encodedBody = body;
102
116
  contentEncodingStripped = true;
103
117
  }
104
118
  if (bodyKind === 'responses' || decoded.length > 0) {
@@ -134,8 +148,18 @@ export async function prepareDesktopBridgeRequest(req, config) {
134
148
  const compaction = bodyKind === 'responses' && requestsRemoteCompaction(payload)
135
149
  ? 'trigger'
136
150
  : bodyKind === 'sub-endpoint' && isCompactEndpoint(req, pathname) ? 'endpoint' : null;
151
+ let plainBody = null;
152
+ const answeredByBridge = Boolean(compaction) && route.provider_id !== BRIDGE_OFFICIAL_ROUTE_ID;
153
+ if (encodedBody && body && !answeredByBridge && keepsClientEncoding(route)) {
154
+ const upstreamBody = rewrite ? await reencodeLikeClient(body, encoding) : encodedBody;
155
+ if (upstreamBody) {
156
+ plainBody = body;
157
+ body = upstreamBody;
158
+ contentEncodingStripped = false;
159
+ }
160
+ }
137
161
  const credential = route.provider_id === BRIDGE_OFFICIAL_ROUTE_ID ? null : await resolveCredential(config, route);
138
- return { body, route, credential, contentEncodingStripped, compaction, payload };
162
+ return { body, route, credential, contentEncodingStripped, plainBody, compaction, payload };
139
163
  }
140
164
  function connectTimeout(request, config) {
141
165
  request.once('socket', (socket) => {
@@ -172,14 +196,11 @@ function writeHttpBridgeError(res, error, req) {
172
196
  });
173
197
  res.end(JSON.stringify({ error: { type: 'sks_bridge_error', code, message: code } }));
174
198
  }
175
- function safeUpstreamErrorId(value) {
176
- const text = String(value ?? '').trim();
177
- return /^[A-Za-z0-9_.:-]{1,64}$/.test(text) ? text : null;
178
- }
179
- const TRANSIENT_UPSTREAM_STATUSES = new Set([500, 502, 503, 524]);
199
+ const TRANSIENT_UPSTREAM_STATUSES = new Set([500, 502, 503, 520, 521, 522, 523, 524]);
180
200
  const TRANSIENT_UPSTREAM_IDENTIFIERS = new Set(['upstream_error', 'upstream_request_timeout']);
181
201
  export const TRANSIENT_UPSTREAM_REPLAY_LIMIT = 3;
182
202
  const TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS = [200, 400, 800];
203
+ export const NO_ROUTE_REPLAY_BACKOFF_MS = [250, 500, 1_000, 2_000, 3_000, 4_000];
183
204
  function isTransientUpstreamIdentifier(value) {
184
205
  return Boolean(value && TRANSIENT_UPSTREAM_IDENTIFIERS.has(value));
185
206
  }
@@ -204,6 +225,7 @@ function redactedUpstreamClientMessage(statusCode, transient) {
204
225
  return 'temporary_upstream_failure';
205
226
  return 'bridge_upstream_request_failed';
206
227
  }
228
+ const UNREADABLE_BODY_HINT = /json|pars(?:e|ing)|decod|encod|compress|utf-?8/i;
207
229
  async function readRedactedUpstreamError(response) {
208
230
  let total = 0;
209
231
  const chunks = [];
@@ -218,14 +240,15 @@ async function readRedactedUpstreamError(response) {
218
240
  }
219
241
  let upstreamCode = null;
220
242
  let upstreamType = null;
243
+ const text = Buffer.concat(chunks).toString('utf8');
221
244
  try {
222
- const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
245
+ const parsed = JSON.parse(text);
223
246
  upstreamCode = safeUpstreamErrorId(parsed?.error?.code) ?? safeUpstreamErrorId(parsed?.detail);
224
247
  upstreamType = safeUpstreamErrorId(parsed?.error?.type);
225
248
  }
226
249
  catch {
227
250
  }
228
- return { upstreamCode, upstreamType };
251
+ return { upstreamCode, upstreamType, unreadableBody: UNREADABLE_BODY_HINT.test(text.slice(0, 4096)) };
229
252
  }
230
253
  function buildRedactedUpstreamErrorBody(statusCode, upstreamCode, upstreamType, transient) {
231
254
  return Buffer.from(JSON.stringify({
@@ -259,6 +282,7 @@ export function destroyDesktopBridgeUpstreamAgents() {
259
282
  for (const agent of upstreamAgents.values())
260
283
  agent.destroy();
261
284
  upstreamAgents.clear();
285
+ encodedBodyRefusals.clear();
262
286
  }
263
287
  const STALE_CONNECTION_ERROR_CODES = new Set(['ECONNRESET', 'EPIPE', 'ECONNABORTED']);
264
288
  function isStalePooledSocketFailure(request, error) {
@@ -278,6 +302,8 @@ class StalePooledSocketFailure extends Error {
278
302
  }
279
303
  class UnreachableUpstreamFailure extends StalePooledSocketFailure {
280
304
  }
305
+ class EncodedBodyRefusedFailure extends StalePooledSocketFailure {
306
+ }
281
307
  export async function forwardHttp(req, res, config, prepared, authenticatedLocalBaseUrl = desktopBridgeListenOrigin(config)) {
282
308
  try {
283
309
  const request = prepared || await prepareDesktopBridgeRequest(req, config);
@@ -322,7 +348,9 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
322
348
  delete headers['content-encoding'];
323
349
  const agent = upstreamAgent(remote.secure, `${remote.address}:${remote.port}`, config.idleTimeoutMs);
324
350
  const replayable = Buffer.isBuffer(request.body);
325
- const attempt = (useFreshConnection, canReplay) => new Promise((resolve, reject) => {
351
+ let body = request.body;
352
+ let plainFallbackActive = false;
353
+ const attempt = (useFreshConnection, budget) => new Promise((resolve, reject) => {
326
354
  let responseStarted = false;
327
355
  let settled = false;
328
356
  const abort = () => { if (!res.writableEnded)
@@ -347,7 +375,7 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
347
375
  res.once('close', abort);
348
376
  upstream.once('error', (error) => {
349
377
  if (!responseStarted && isUnreachableUpstreamError(error)) {
350
- if (replayable && canReplay) {
378
+ if (replayable && (isNoRouteUpstreamError(error) ? budget.noRoute : budget.transient)) {
351
379
  finish(new UnreachableUpstreamFailure(error));
352
380
  return;
353
381
  }
@@ -361,8 +389,12 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
361
389
  });
362
390
  upstream.once('response', (response) => {
363
391
  const statusCode = response.statusCode || 502;
392
+ if (plainFallbackActive && statusCode < 400 && !official && !encodedBodyRefusals.has(provider.provider_id)) {
393
+ encodedBodyRefusals.add(provider.provider_id);
394
+ logHttpRejection({ code: 'bridge_upstream_encoded_body_refused', transport: 'http', ...(req.method === undefined ? {} : { method: req.method }), ...(req.url === undefined ? {} : { url: req.url }), provider_id: provider.provider_id });
395
+ }
364
396
  if (official) {
365
- if (statusCode >= 400 && TRANSIENT_UPSTREAM_STATUSES.has(statusCode) && replayable && canReplay) {
397
+ if (statusCode >= 400 && TRANSIENT_UPSTREAM_STATUSES.has(statusCode) && replayable && budget.transient) {
366
398
  response.resume();
367
399
  responseStarted = true;
368
400
  finish(new StalePooledSocketFailure(new DesktopBridgeError('bridge_upstream_transient_mislabel')));
@@ -392,9 +424,15 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
392
424
  return;
393
425
  }
394
426
  if (statusCode >= 400) {
395
- void readRedactedUpstreamError(response).then(({ upstreamCode, upstreamType }) => {
427
+ void readRedactedUpstreamError(response).then(({ upstreamCode, upstreamType, unreadableBody }) => {
428
+ if (budget.plainFallback && (statusCode === 415
429
+ || (unreadableBody && !upstreamType && !upstreamCode && (statusCode === 400 || statusCode === 422)))) {
430
+ responseStarted = true;
431
+ finish(new EncodedBodyRefusedFailure(new DesktopBridgeError(`bridge_upstream_encoded_body_refused_${statusCode}`)));
432
+ return;
433
+ }
396
434
  const transientMislabel = isTransientUpstreamFailure(statusCode, upstreamType, upstreamCode);
397
- if (transientMislabel && replayable && canReplay) {
435
+ if (transientMislabel && replayable && budget.transient) {
398
436
  responseStarted = true;
399
437
  finish(new StalePooledSocketFailure(new DesktopBridgeError(upstreamCode === 'upstream_request_timeout'
400
438
  ? 'bridge_upstream_request_timeout'
@@ -438,24 +476,46 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
438
476
  }
439
477
  void pipeline(response, res).then(() => finish(), finish);
440
478
  });
441
- if (request.body)
442
- upstream.end(request.body);
479
+ if (body)
480
+ upstream.end(body);
443
481
  else
444
482
  void pipeline(req, upstream).catch((error) => { if (!responseStarted)
445
483
  finish(error); });
446
484
  });
447
- let remainingReplays = replayable ? TRANSIENT_UPSTREAM_REPLAY_LIMIT : 0;
485
+ let transientReplays = replayable ? TRANSIENT_UPSTREAM_REPLAY_LIMIT : 0;
486
+ let noRouteWaits = replayable ? NO_ROUTE_REPLAY_BACKOFF_MS.length : 0;
448
487
  let useFreshConnection = false;
449
488
  for (;;) {
450
489
  try {
451
- await attempt(useFreshConnection, remainingReplays > 0);
490
+ await attempt(useFreshConnection, {
491
+ transient: transientReplays > 0,
492
+ noRoute: noRouteWaits > 0,
493
+ plainFallback: Boolean(request.plainBody) && !plainFallbackActive,
494
+ });
452
495
  return;
453
496
  }
454
497
  catch (error) {
455
- if (!(error instanceof StalePooledSocketFailure) || remainingReplays <= 0)
498
+ if (!(error instanceof StalePooledSocketFailure))
456
499
  throw error;
457
- remainingReplays -= 1;
458
- if (error instanceof UnreachableUpstreamFailure) {
500
+ let backoffMs = 0;
501
+ if (error instanceof EncodedBodyRefusedFailure) {
502
+ body = request.plainBody;
503
+ plainFallbackActive = true;
504
+ headers['content-length'] = String(body.length);
505
+ delete headers['content-encoding'];
506
+ }
507
+ else if (error instanceof UnreachableUpstreamFailure) {
508
+ const noRoute = isNoRouteUpstreamError(error.reason);
509
+ if ((noRoute ? noRouteWaits : transientReplays) <= 0)
510
+ throw error;
511
+ if (noRoute) {
512
+ backoffMs = NO_ROUTE_REPLAY_BACKOFF_MS[NO_ROUTE_REPLAY_BACKOFF_MS.length - noRouteWaits];
513
+ noRouteWaits -= 1;
514
+ }
515
+ else {
516
+ backoffMs = TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[TRANSIENT_UPSTREAM_REPLAY_LIMIT - transientReplays];
517
+ transientReplays -= 1;
518
+ }
459
519
  const staleAddress = remote.address;
460
520
  await refreshDesktopBridgeRemoteTarget(remote, config.remoteLookup);
461
521
  agent.destroy();
@@ -468,6 +528,10 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
468
528
  });
469
529
  }
470
530
  else {
531
+ if (transientReplays <= 0)
532
+ throw error;
533
+ backoffMs = TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[TRANSIENT_UPSTREAM_REPLAY_LIMIT - transientReplays];
534
+ transientReplays -= 1;
471
535
  if (!useFreshConnection) {
472
536
  agent.destroy();
473
537
  upstreamAgents.delete(`${remote.secure ? 'https' : 'http'}:${remote.address}:${remote.port}`);
@@ -480,9 +544,6 @@ export async function forwardHttp(req, res, config, prepared, authenticatedLocal
480
544
  });
481
545
  }
482
546
  useFreshConnection = true;
483
- const backoffIndex = TRANSIENT_UPSTREAM_REPLAY_LIMIT - remainingReplays - 1;
484
- const backoffMs = TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[Math.max(0, backoffIndex)]
485
- ?? TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS.length - 1];
486
547
  if (backoffMs)
487
548
  await delay(backoffMs);
488
549
  }
@@ -496,8 +557,9 @@ const MAX_COMPACTION_SUMMARY_RESPONSE_BYTES = 32 * 1024 * 1024;
496
557
  const COMPACTION_KEEPALIVE_MS = 15_000;
497
558
  export async function requestProviderCompactionSummary(config, input, body, onAccepted = () => undefined) {
498
559
  const remote = input.provider.remote;
499
- let remaining = TRANSIENT_UPSTREAM_REPLAY_LIMIT;
500
- for (let attempt = 0;; attempt += 1) {
560
+ let transientReplays = TRANSIENT_UPSTREAM_REPLAY_LIMIT;
561
+ let noRouteWaits = NO_ROUTE_REPLAY_BACKOFF_MS.length;
562
+ for (;;) {
501
563
  await ensureDesktopBridgeRemoteTarget(remote, config.remoteLookup);
502
564
  const target = resolveDesktopBridgeTarget(input.requestUrl, remote);
503
565
  const headers = buildProviderUpstreamHeaders(input.clientHeaders, {
@@ -537,22 +599,31 @@ export async function requestProviderCompactionSummary(config, input, body, onAc
537
599
  });
538
600
  upstream.end(body);
539
601
  });
540
- if (result.status < 300 || remaining <= 0)
602
+ if (result.status < 300 || transientReplays <= 0)
541
603
  return result;
542
604
  const { upstreamCode, upstreamType } = upstreamErrorIds(result.body);
543
605
  if (!isTransientUpstreamFailure(result.status, upstreamType, upstreamCode))
544
606
  return result;
607
+ await delay(TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[TRANSIENT_UPSTREAM_REPLAY_LIMIT - transientReplays]);
608
+ transientReplays -= 1;
545
609
  }
546
610
  catch (error) {
547
- if (remaining <= 0 || error instanceof DesktopBridgeError)
611
+ if (error instanceof DesktopBridgeError)
612
+ throw error;
613
+ const noRoute = isNoRouteUpstreamError(error);
614
+ if ((noRoute ? noRouteWaits : transientReplays) <= 0)
548
615
  throw error;
549
616
  if (isUnreachableUpstreamError(error))
550
617
  await refreshDesktopBridgeRemoteTarget(remote, config.remoteLookup);
618
+ if (noRoute) {
619
+ await delay(NO_ROUTE_REPLAY_BACKOFF_MS[NO_ROUTE_REPLAY_BACKOFF_MS.length - noRouteWaits]);
620
+ noRouteWaits -= 1;
621
+ }
622
+ else {
623
+ await delay(TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[TRANSIENT_UPSTREAM_REPLAY_LIMIT - transientReplays]);
624
+ transientReplays -= 1;
625
+ }
551
626
  }
552
- remaining -= 1;
553
- const backoffMs = TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS[Math.min(attempt, TRANSIENT_UPSTREAM_REPLAY_BACKOFF_MS.length - 1)];
554
- if (backoffMs)
555
- await delay(backoffMs);
556
627
  }
557
628
  }
558
629
  function upstreamErrorIds(body) {
@@ -593,9 +664,10 @@ async function answerCompactionWithProviderSummary(req, res, config, input) {
593
664
  });
594
665
  try {
595
666
  const call = await requestProviderCompactionSummary(config, input, body, startStream);
667
+ const parsed = parseProviderSummary(call.body.toString('utf8'));
596
668
  if (call.status >= 300) {
597
669
  const { upstreamCode, upstreamType } = upstreamErrorIds(call.body);
598
- log(upstreamCode ? `bridge_compaction_summary_status_${call.status}:${upstreamCode}` : `bridge_compaction_summary_status_${call.status}`, call.status);
670
+ log(summaryFailure(parsed, call.status).logCode, call.status);
599
671
  const transient = isTransientUpstreamFailure(call.status, upstreamType, upstreamCode);
600
672
  const clientStatus = transient ? 503 : call.status;
601
673
  const errorBody = buildRedactedUpstreamErrorBody(call.status, upstreamCode, upstreamType, transient);
@@ -606,12 +678,12 @@ async function answerCompactionWithProviderSummary(req, res, config, input) {
606
678
  res.end(errorBody);
607
679
  return;
608
680
  }
609
- const parsed = parseProviderSummary(call.body.toString('utf8'));
610
681
  const summary = parsed.failed || !summaryWithinBounds(parsed.summary) ? null : parsed.summary;
611
682
  if (!summary) {
612
- log('bridge_compaction_summary_unusable', call.status);
683
+ const failure = summaryFailure(parsed, call.status);
684
+ log(failure.logCode, call.status);
613
685
  if (streaming) {
614
- res.end(sseEvent(compactionFailedEvent(responseId, publicModel, 'bridge_compaction_summary_unusable')));
686
+ res.end(sseEvent(compactionFailedEvent(responseId, publicModel, failure.code)));
615
687
  }
616
688
  else {
617
689
  writeHttpBridgeError(res, new DesktopBridgeError('bridge_compaction_summary_unusable'), req);
@@ -6,7 +6,7 @@ import { buildOfficialPassthroughWebSocketHeaders, buildProviderWebSocketHeaders
6
6
  import { createDesktopBridgeRejectionLogger } from './rejection-log.js';
7
7
  import { assertDesktopBridgeRouteContext, ensureDesktopBridgeRemoteTarget, isUnreachableUpstreamError, refreshDesktopBridgeRemoteTarget, resolveAndBindDesktopBridgeRouteContext, resolveCodexSessionIdentity, resolveDesktopBridgeTarget, safeBridgeErrorCode } from './security.js';
8
8
  import { DEFAULT_DESKTOP_BRIDGE_MAX_REQUEST_BODY_BYTES, DesktopBridgeError } from './types.js';
9
- import { buildCompactionSummaryRequest, compactionCreatedEvent, compactionFailedEvent, compactionResponseEvents, expandBridgeCompactionItems, newCompactionResponseId, parseProviderSummary, requestsRemoteCompaction, summaryWithinBounds, } from './compaction-adapter.js';
9
+ import { buildCompactionSummaryRequest, compactionCreatedEvent, compactionFailedEvent, compactionResponseEvents, expandBridgeCompactionItems, newCompactionResponseId, parseProviderSummary, requestsRemoteCompaction, ResponsesContinuationChain, summaryFailure, summaryWithinBounds, } from './compaction-adapter.js';
10
10
  import { requestProviderCompactionSummary } from './http-forward.js';
11
11
  const MAX_PENDING_MESSAGES = 256;
12
12
  const CLOSE_GRACE_MS = 1_000;
@@ -53,6 +53,7 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
53
53
  const pending = [];
54
54
  let closeTimer = null;
55
55
  let handshakeTimer = null;
56
+ const chain = new ResponsesContinuationChain(maxBytes);
56
57
  const initialTimer = setTimeout(closeUnbound, config.websocketInitialCreateTimeoutMs ?? INITIAL_CREATE_TIMEOUT_MS);
57
58
  initialTimer.unref();
58
59
  const localSocket = socket;
@@ -118,8 +119,7 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
118
119
  if (typeof model !== 'string' || !model.trim())
119
120
  throw new DesktopBridgeError('bridge_websocket_model_required');
120
121
  const nextIdentity = resolveCodexSessionIdentity(req.headers, message.create);
121
- if (bound && ((nextIdentity.thread_id && nextIdentity.thread_id !== identity.thread_id)
122
- || (nextIdentity.session_id && nextIdentity.session_id !== identity.session_id))) {
122
+ if (bound && nextIdentity.thread_id && nextIdentity.thread_id !== identity.thread_id) {
123
123
  throw new DesktopBridgeError('bridge_codex_session_identity_conflict');
124
124
  }
125
125
  identity = bound ? withLineage(identity, nextIdentity) : nextIdentity;
@@ -141,6 +141,7 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
141
141
  }
142
142
  if (route.upstream_model !== model || expanded)
143
143
  message.data = Buffer.from(JSON.stringify(create));
144
+ message.routed = create;
144
145
  }
145
146
  async function answerCompaction(create) {
146
147
  const route = bound;
@@ -150,7 +151,13 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
150
151
  const credential = await config.resolveProviderCredential(provider.provider_id, provider.credential_generation);
151
152
  const responseId = newCompactionResponseId();
152
153
  const send = (event) => relay(client, Buffer.from(JSON.stringify(event)), false);
153
- const body = Buffer.from(JSON.stringify(buildCompactionSummaryRequest(create, route.upstream_model)));
154
+ const input = chain.fullInput(create);
155
+ if (!input) {
156
+ logRejection({ code: 'bridge_compaction_history_unavailable', transport: 'websocket', provider_id: provider.provider_id, public_model: route.public_model, ...(req.url ? { url: req.url } : {}) });
157
+ send(compactionFailedEvent(responseId, route.public_model, 'bridge_compaction_history_unavailable'));
158
+ return;
159
+ }
160
+ const body = Buffer.from(JSON.stringify(buildCompactionSummaryRequest({ ...create, input }, route.upstream_model)));
154
161
  let accepted = false;
155
162
  try {
156
163
  const call = await requestProviderCompactionSummary(config, { provider, credential, clientHeaders: req.headers, requestUrl: req.url || '/' }, body, () => {
@@ -161,15 +168,11 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
161
168
  });
162
169
  if (stopped)
163
170
  return;
164
- if (call.status >= 300) {
165
- logRejection({ code: `bridge_compaction_summary_status_${call.status}`, transport: 'websocket', ...(req.url ? { url: req.url } : {}) });
166
- send(compactionFailedEvent(responseId, route.public_model, call.status === 429 ? 'rate_limit_exceeded' : 'bridge_upstream_request_failed'));
167
- return;
168
- }
169
171
  const parsed = parseProviderSummary(call.body.toString('utf8'));
170
- if (parsed.failed || !summaryWithinBounds(parsed.summary)) {
171
- logRejection({ code: 'bridge_compaction_summary_unusable', transport: 'websocket', ...(req.url ? { url: req.url } : {}) });
172
- send(compactionFailedEvent(responseId, route.public_model, 'bridge_compaction_summary_unusable'));
172
+ if (call.status >= 300 || parsed.failed || !summaryWithinBounds(parsed.summary)) {
173
+ const failure = summaryFailure(parsed, call.status);
174
+ logRejection({ code: failure.logCode, transport: 'websocket', status: call.status, provider_id: provider.provider_id, public_model: route.public_model, ...(req.url ? { url: req.url } : {}) });
175
+ send(compactionFailedEvent(responseId, route.public_model, failure.code));
173
176
  return;
174
177
  }
175
178
  const events = compactionResponseEvents(responseId, route.public_model, parsed.summary, parsed.usage);
@@ -244,8 +247,10 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
244
247
  peer.on('message', (data, binary) => {
245
248
  if (stopped)
246
249
  return;
250
+ const frame = bytes(data);
251
+ chain.received(frame);
247
252
  try {
248
- relay(client, bytes(data), binary);
253
+ relay(client, frame, binary);
249
254
  }
250
255
  catch (error) {
251
256
  fail(error);
@@ -291,8 +296,11 @@ export function forwardResponsesWebSocket(req, socket, head, config) {
291
296
  return;
292
297
  if (message.compaction)
293
298
  await answerCompaction(message.compaction);
294
- else
299
+ else {
295
300
  relay(upstream, message.data, message.binary);
301
+ if (message.routed && bound?.provider_id !== BRIDGE_OFFICIAL_ROUTE_ID)
302
+ chain.sent(message.routed, message.data);
303
+ }
296
304
  pending.shift();
297
305
  pendingBytes -= message.originalBytes;
298
306
  }
@@ -115,7 +115,7 @@ function codexMetadataObject(value, source) {
115
115
  }
116
116
  return parsed;
117
117
  }
118
- function oneCodexIdentity(values, kind) {
118
+ function oneCodexIdentity(values) {
119
119
  const canonical = values
120
120
  .filter((value) => value !== undefined && value !== null && value !== '')
121
121
  .map(canonicalSessionId);
@@ -123,6 +123,17 @@ function oneCodexIdentity(values, kind) {
123
123
  throw new DesktopBridgeError('bridge_codex_session_identity_conflict');
124
124
  return canonical[0] || null;
125
125
  }
126
+ function firstSessionId(values) {
127
+ for (const value of values) {
128
+ if (value === undefined || value === null || value === '')
129
+ continue;
130
+ try {
131
+ return canonicalSessionId(value);
132
+ }
133
+ catch { }
134
+ }
135
+ return null;
136
+ }
126
137
  export function resolveCodexSessionIdentity(headers, payload = null) {
127
138
  const turnMetadataValue = singleBridgeHeader(headers, 'x-codex-turn-metadata');
128
139
  const turnMetadata = turnMetadataValue === null ? null : codexMetadataObject(turnMetadataValue, 'header');
@@ -132,12 +143,12 @@ export function resolveCodexSessionIdentity(headers, payload = null) {
132
143
  singleBridgeHeader(headers, 'thread-id'),
133
144
  turnMetadata?.thread_id,
134
145
  clientMetadata?.thread_id,
135
- ], 'thread');
136
- const sessionId = oneCodexIdentity([
137
- singleBridgeHeader(headers, 'session-id'),
138
- turnMetadata?.session_id,
146
+ ]);
147
+ const sessionId = firstSessionId([
139
148
  clientMetadata?.session_id,
140
- ], 'session');
149
+ turnMetadata?.session_id,
150
+ singleBridgeHeader(headers, 'session-id'),
151
+ ]);
141
152
  if (sessionId && !threadId)
142
153
  throw new DesktopBridgeError('bridge_codex_thread_id_missing');
143
154
  const nested = nestedTurnMetadata(clientMetadata);
@@ -348,8 +359,22 @@ export function assertDesktopBridgeRouteContext(request, config) {
348
359
  return route;
349
360
  }
350
361
  export async function resolveAndBindDesktopBridgeRouteContext(request, config) {
362
+ const pinsRead = config.providerSessionPins;
363
+ let steady = null;
364
+ try {
365
+ steady = assertDesktopBridgeRouteContext(request, config);
366
+ }
367
+ catch {
368
+ steady = null;
369
+ }
370
+ const steadyPin = steady?.session_pin;
371
+ if (steady && !steadyPin)
372
+ return steady;
373
+ const persisted = steadyPin ? pinsRead.find((entry) => entry.thread_id === steadyPin.thread_id) : undefined;
374
+ if (steady && steadyPin && persisted && sameSessionPin(persisted, steadyPin))
375
+ return steady;
351
376
  return withSessionPinMutation(config, async () => {
352
- const route = assertDesktopBridgeRouteContext(request, config);
377
+ const route = steady && config.providerSessionPins === pinsRead ? steady : assertDesktopBridgeRouteContext(request, config);
353
378
  const pin = route.session_pin;
354
379
  if (!pin)
355
380
  return route;
@@ -455,7 +480,10 @@ export async function resolveDesktopBridgeRemoteTarget(raw, lookup = defaultLook
455
480
  if (!loopback && addresses.some((row) => FORBIDDEN_REMOTE_ADDRESSES.check(row.address, row.family === 4 ? 'ipv4' : 'ipv6'))) {
456
481
  throw new DesktopBridgeError('bridge_remote_dns_private_address');
457
482
  }
483
+ const avoided = options.avoidAddress ? addresses.find((row) => row.address === options.avoidAddress) : undefined;
458
484
  const selected = (options.preferAddress ? addresses.find((row) => row.address === options.preferAddress) : undefined)
485
+ ?? (avoided ? addresses.find((row) => row.family !== avoided.family) : undefined)
486
+ ?? (options.preferFamily ? addresses.find((row) => row.family === options.preferFamily && row.address !== options.avoidAddress) : undefined)
459
487
  ?? addresses.find((row) => row.address !== options.avoidAddress)
460
488
  ?? addresses[0];
461
489
  if (!selected)
@@ -498,6 +526,11 @@ export function isUnreachableUpstreamError(error) {
498
526
  const code = error?.code;
499
527
  return typeof code === 'string' && UNREACHABLE_UPSTREAM_ERROR_CODES.has(code);
500
528
  }
529
+ const NO_ROUTE_UPSTREAM_ERROR_CODES = new Set(['ENETUNREACH', 'EHOSTUNREACH', 'ENETDOWN', 'EHOSTDOWN', 'EADDRNOTAVAIL', 'EAFNOSUPPORT']);
530
+ export function isNoRouteUpstreamError(error) {
531
+ const code = error?.code;
532
+ return typeof code === 'string' && NO_ROUTE_UPSTREAM_ERROR_CODES.has(code);
533
+ }
501
534
  export const REMOTE_REFRESH_COOLDOWN_MS = 5_000;
502
535
  export const REMOTE_TARGET_TTL_MS = 5 * 60_000;
503
536
  export async function refreshDesktopBridgeRemoteTarget(remote, lookup = defaultLookup, reason = 'unreachable') {
@@ -512,7 +545,7 @@ export async function refreshDesktopBridgeRemoteTarget(remote, lookup = defaultL
512
545
  let resolvedAt = state.resolvedAt;
513
546
  try {
514
547
  const fresh = await resolveDesktopBridgeRemoteTarget(remote.baseUrl, lookup, reason === 'stale'
515
- ? { preferAddress: remote.address }
548
+ ? { preferAddress: remote.address, preferFamily: remote.family }
516
549
  : reason === 'unreachable' ? { avoidAddress: remote.address } : {});
517
550
  const changed = remote.unresolved === true || fresh.address !== remote.address || fresh.family !== remote.family;
518
551
  remote.address = fresh.address;
@@ -291,6 +291,9 @@ export async function startPreparedDesktopBridge(input, options = {}) {
291
291
  if (!socket.destroyed)
292
292
  socket.end('HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n');
293
293
  });
294
+ server.on('drop', () => {
295
+ logBridgeRejection({ code: 'bridge_connection_limit_reached', transport: 'http' });
296
+ });
294
297
  server.on('upgrade', (req, socket, head) => {
295
298
  try {
296
299
  assertLoopbackPeer(req.socket.remoteAddress);
@@ -1 +1 @@
1
- export const PACKAGE_VERSION = '10.5.9';
1
+ export const PACKAGE_VERSION = '10.6.1';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "sneakoscope",
3
3
  "displayName": "ㅅㅋㅅ",
4
- "version": "10.5.9",
4
+ "version": "10.6.1",
5
5
  "description": "Sneakoscope Codex (`sks`) is an open-source trust layer for Codex CLI and ChatGPT Desktop. It coordinates bounded AI coding agents, records machine-verifiable evidence, preserves project memory, and blocks release claims that are not supported by current tests or artifacts. Search visibility outcomes are measured separately; SKS does not promise rankings or traffic.",
6
6
  "type": "module",
7
7
  "homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
@@ -199,9 +199,9 @@
199
199
  "ws": "^8.22.0"
200
200
  },
201
201
  "devDependencies": {
202
- "@types/node": "^24.19.1",
202
+ "@types/node": "^24.19.2",
203
203
  "@types/ws": "^8.18.2",
204
- "jsdom": "30.1.2",
204
+ "jsdom": "30.1.3",
205
205
  "mermaid": "12.1.0"
206
206
  }
207
207
  }