Repository navigation
feat: Add support for Cursor Pro #119
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -10,6 +10,7 @@ const providersRouter = require("./providers-handler"); | |
| const claudeDesktopGatewayRouter = require("./claude-desktop-gateway"); | ||
| const { getRoutingHeaders, getRoutingStats, analyzeComplexity, getModelTierSelector, analyzeRisk, checkSessionPin, writeSessionPin, checkPinScoreDrift } = require("../routing"); | ||
| const { resolveTierForModelId } = require("../routing/model-slots"); | ||
| const { stripHarnessEnvelope } = require("../routing/harness-envelope"); | ||
|
|
||
| // Upstream streams can die without a clean end (reader.read() never | ||
| // resolves on a dropped socket), hanging the client forever. Every | ||
|
|
@@ -1409,7 +1410,11 @@ router.post("/v1/messages", rateLimiter, async (req, res, next) => { | |
| } | ||
| return ''; | ||
| })(); | ||
| // Suggestion-mode detection reads the RAW text (the marker is itself | ||
| // wrapper text); the force/risk probes below get envelope-stripped text | ||
| // so Cursor's <user_info>/<rules> blocks can't fire triggers on "Hi". | ||
| const isSuggestionMode = _lastUserText.includes('[SUGGESTION MODE:'); | ||
| const _lastUserAskClean = stripHarnessEnvelope(_lastUserText); | ||
| // Tool-lessness alone is NOT harness evidence: generic API clients | ||
| // (curl, benchmarks, SDKs) legitimately send bare messages and must get | ||
| // full routing — live regression 2026-07-08: a benchmark's security- | ||
|
|
@@ -1535,7 +1540,7 @@ router.post("/v1/messages", rateLimiter, async (req, res, next) => { | |
| let _pinForceBypass = null; | ||
| if (!sideTier && pinCheck.serve && pinCheck.reason === 'guards_passed' && !isSideRequest) { | ||
| try { | ||
| const _probe = { messages: [{ role: 'user', content: _lastUserText || '' }] }; | ||
| const _probe = { messages: [{ role: 'user', content: _lastUserAskClean || '' }] }; | ||
| const ca = require("../routing/complexity-analyzer"); | ||
| if (_lastUserText && ca.shouldForceReasoning(_probe)) _pinForceBypass = 'force_reasoning'; | ||
| else if (_lastUserText && ca.shouldForceCloud(_probe)) _pinForceBypass = 'force_cloud'; | ||
|
|
@@ -1883,6 +1888,15 @@ router.post("/v1/messages", rateLimiter, async (req, res, next) => { | |
| tierMethod: tier?.method || null, | ||
| tierPinned: tier?.pinned ?? null, | ||
| cacheState: _pinCacheState, | ||
| // Mid tool-exchange frames must keep serving the in-flight model | ||
| // (same invariant as the orchestrator's tool_history pin serves). | ||
| hasToolHistory: (() => { | ||
| try { return require("../routing/session-affinity").payloadHasToolHistory(req.body); } | ||
| catch { return false; } | ||
| })(), | ||
| // This branch IS the flat-fee subscription: the gate's dollar | ||
| // break-even leg has no premium to amortize here. | ||
| flatRate: true, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| // Quota pressure shortens the hold horizon inside the gate (high | ||
| // sustained burn → descents clear sooner). Zero when the ledger has | ||
| // no history — the gate behaves exactly as before. | ||
|
|
@@ -1919,6 +1933,36 @@ router.post("/v1/messages", rateLimiter, async (req, res, next) => { | |
| logger.debug({ err: err.message }, '[Routing] Pin-hold restore failed (non-fatal)'); | ||
| } | ||
| } | ||
| // Upgrades must persist the SERVED model into the pin, mirroring the | ||
| // pin_hold restore above — the write at the fresh-decision site stored | ||
| // the pre-rewrite tier model, and a pin that forgets the upgrade lets | ||
| // later frames descend below what actually served. Unlike pin_hold, | ||
| // an upgrade usually happens with no prior pin, so the payload comes | ||
| // from the fresh tier object. Floored (+continuation_inherit) and | ||
| // risk-lifted tiers stay unpinned (writeSessionPin re-checks, but the | ||
| // method is overridden to 'session_pin' here, so guard at the source). | ||
| if ( | ||
| _passthroughRoute.action === 'upgrade' | ||
| && _passthroughRoute.model | ||
| && pinCheck?.sessionId | ||
| && tier?.provider | ||
| && !String(tier?.method || '').includes('+continuation_inherit') | ||
| && tier?.escalation_source !== 'risk' | ||
| ) { | ||
| try { | ||
| const { writeSessionPin } = require("../routing/index"); | ||
| writeSessionPin(pinCheck.sessionId, { | ||
| provider: tier.provider, | ||
| model: _passthroughRoute.model, | ||
| tier: tier.tier, | ||
| score: tier.score ?? null, | ||
| method: 'session_pin', | ||
| reason: 'passthrough_upgrade', | ||
| }, req.body); | ||
| } catch (err) { | ||
| logger.debug({ err: err.message }, '[Routing] Upgrade pin persist failed (non-fatal)'); | ||
| } | ||
| } | ||
| if (_passthroughRoute.action !== 'verbatim' && _passthroughRoute.model) { | ||
| logger.info({ | ||
| from: _passthroughClientModel, | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -145,6 +145,9 @@ const STRICT = process.env.LYNKR_EMBEDDINGS_STRICT === 'true'; | |||||||||||||||||
| function _noteFallback(providerName, err) { | ||||||||||||||||||
| fallbackCount += 1; | ||||||||||||||||||
| lastProviderError = err?.message || String(err); | ||||||||||||||||||
| // Cooldown counts from the LAST failed dial, not the first attempt's start — | ||||||||||||||||||
| // the in-place retry delay must not eat into the cooldown window. | ||||||||||||||||||
| lastProviderAttempt = Date.now(); | ||||||||||||||||||
| if (embeddingProviderAvailable !== false) { | ||||||||||||||||||
| embeddingProviderAvailable = false; | ||||||||||||||||||
| degradedSince = Date.now(); | ||||||||||||||||||
|
|
@@ -168,6 +171,14 @@ function _noteRecovery(providerName) { | |||||||||||||||||
| degradedSince = null; | ||||||||||||||||||
| } | ||||||||||||||||||
|
|
||||||||||||||||||
| // One in-place retry before tripping degradation: a single transient failure | ||||||||||||||||||
| // (Ollama cold-loading the model at boot, a blip mid-restart) was flipping | ||||||||||||||||||
| // the WHOLE provider to hash embeddings for a full cooldown after every | ||||||||||||||||||
| // server start (recurring live incident, last 2026-09-26). A short backoff | ||||||||||||||||||
| // absorbs the blip; a provider that is actually down fails twice and | ||||||||||||||||||
| // degrades exactly as before. | ||||||||||||||||||
| const TRANSIENT_RETRY_DELAY_MS = 1500; | ||||||||||||||||||
|
|
||||||||||||||||||
| function _wrapProvider(providerName, providerFn) { | ||||||||||||||||||
| return async (text) => { | ||||||||||||||||||
| // While degraded, only re-attempt the provider after the cooldown; serve | ||||||||||||||||||
|
|
@@ -184,9 +195,25 @@ function _wrapProvider(providerName, providerFn) { | |||||||||||||||||
| const result = await providerFn(text); | ||||||||||||||||||
| _noteRecovery(providerName); | ||||||||||||||||||
| return result; | ||||||||||||||||||
| } catch (err) { | ||||||||||||||||||
| _noteFallback(providerName, err); | ||||||||||||||||||
| if (STRICT) throw err; | ||||||||||||||||||
| } catch (firstErr) { | ||||||||||||||||||
| // Already-degraded providers get no second chance (this attempt WAS the | ||||||||||||||||||
| // post-cooldown probe); healthy ones earn one retry before the flip. | ||||||||||||||||||
| if (embeddingProviderAvailable !== false) { | ||||||||||||||||||
| await new Promise((r) => setTimeout(r, TRANSIENT_RETRY_DELAY_MS)); | ||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Leaving as-is. The retry delay itself is a
Comment on lines
+201
to
+202
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggestion: Use a helper function like Suggestion:
Suggested change
|
||||||||||||||||||
| try { | ||||||||||||||||||
| const result = await providerFn(text); | ||||||||||||||||||
| _noteRecovery(providerName); | ||||||||||||||||||
| logger.debug({ provider: providerName, error: firstErr?.message }, | ||||||||||||||||||
| '[Embeddings] Transient provider failure absorbed by retry'); | ||||||||||||||||||
| return result; | ||||||||||||||||||
| } catch (secondErr) { | ||||||||||||||||||
| _noteFallback(providerName, secondErr); | ||||||||||||||||||
| if (STRICT) throw secondErr; | ||||||||||||||||||
| return generateHashEmbedding(text); | ||||||||||||||||||
| } | ||||||||||||||||||
| } | ||||||||||||||||||
| _noteFallback(providerName, firstErr); | ||||||||||||||||||
| if (STRICT) throw firstErr; | ||||||||||||||||||
| return generateHashEmbedding(text); | ||||||||||||||||||
| } | ||||||||||||||||||
| }; | ||||||||||||||||||
|
|
||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added "frontier" key to config/difficulty-anchors.json. Please verify the key name is intentionally "frontier" and not a typo (e.g., "frontier-level" or "frontier-tier") to maintain consistency with existing keys like "design", "code", etc., or ensure it aligns with the intended schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intentional.
frontieris the anchor classsrc/routing/intent-score.jsreads (sims.frontier,FRONTIER_MIN_SIM, the REASONING band) alongsidetrivial/substantive/heavyweight. No change.