@code-yeongyu/senpi 2026.9.11 → 2026.9.12-2
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/CHANGELOG.md +40 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -1
- package/dist/core/agent-session-runtime.js +5 -1
- package/dist/core/agent-session-runtime.js.map +1 -1
- package/dist/core/agent-session.d.ts +13 -9
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +54 -121
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/cursor-history-admission.d.ts +50 -0
- package/dist/core/cursor-history-admission.d.ts.map +1 -0
- package/dist/core/cursor-history-admission.js +130 -0
- package/dist/core/cursor-history-admission.js.map +1 -0
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +2 -1
- package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
- package/dist/core/session-manager.d.ts +2 -0
- package/dist/core/session-manager.d.ts.map +1 -1
- package/dist/core/session-manager.js +22 -13
- package/dist/core/session-manager.js.map +1 -1
- package/dist/core/session-write-reservation.d.ts +9 -0
- package/dist/core/session-write-reservation.d.ts.map +1 -1
- package/dist/core/session-write-reservation.js +33 -0
- package/dist/core/session-write-reservation.js.map +1 -1
- package/dist/modes/interactive/components/ask-user-async-widget.d.ts +2 -0
- package/dist/modes/interactive/components/ask-user-async-widget.d.ts.map +1 -1
- package/dist/modes/interactive/components/ask-user-async-widget.js +17 -1
- package/dist/modes/interactive/components/ask-user-async-widget.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +2 -2
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/rpc/rpc-types.d.ts +2 -1
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
- package/dist/modes/rpc/rpc-types.js +1 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -1
- package/dist/modes/rpc/session-path-reservations.d.ts +30 -0
- package/dist/modes/rpc/session-path-reservations.d.ts.map +1 -0
- package/dist/modes/rpc/session-path-reservations.js +58 -0
- package/dist/modes/rpc/session-path-reservations.js.map +1 -0
- package/dist/modes/rpc/session-registry.d.ts +3 -1
- package/dist/modes/rpc/session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/session-registry.js.map +1 -1
- package/dist/modes/rpc/session-worker-client.d.ts +10 -8
- package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
- package/dist/modes/rpc/session-worker-client.js +14 -23
- package/dist/modes/rpc/session-worker-client.js.map +1 -1
- package/dist/modes/rpc/session-worker-credit.d.ts +17 -0
- package/dist/modes/rpc/session-worker-credit.d.ts.map +1 -0
- package/dist/modes/rpc/session-worker-credit.js +39 -0
- package/dist/modes/rpc/session-worker-credit.js.map +1 -0
- package/dist/modes/rpc/session-worker-protocol.d.ts +10 -0
- package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -1
- package/dist/modes/rpc/session-worker-protocol.js +2 -0
- package/dist/modes/rpc/session-worker-protocol.js.map +1 -1
- package/dist/modes/rpc/session-worker-signals.d.ts +7 -0
- package/dist/modes/rpc/session-worker-signals.d.ts.map +1 -0
- package/dist/modes/rpc/session-worker-signals.js +19 -0
- package/dist/modes/rpc/session-worker-signals.js.map +1 -0
- package/dist/modes/rpc/session-worker.js +12 -16
- package/dist/modes/rpc/session-worker.js.map +1 -1
- package/dist/modes/rpc/worker-session-registry.d.ts +12 -0
- package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -1
- package/dist/modes/rpc/worker-session-registry.js +51 -24
- package/dist/modes/rpc/worker-session-registry.js.map +1 -1
- package/docs/rpc.md +14 -6
- package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +24 -0
- package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
- package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +11 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +18 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +5 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts +2 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js +96 -51
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/discovery.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts +389 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js +132 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/gen/cascade_pb.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts +34 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js +41 -8
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/metadata.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js +20 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/paths.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts +18 -6
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js +77 -35
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/request.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts +9 -3
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js +30 -38
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/stream-state.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.d.ts +16 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.js +69 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/trailer.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.d.ts +26 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.js +61 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/unary.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts +6 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js +6 -4
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent/wire.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts +13 -5
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js +76 -21
- package/node_modules/@earendil-works/pi-ai/dist/api/devin-agent.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts +5 -0
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js +7 -2
- package/node_modules/@earendil-works/pi-ai/dist/auth/oauth/devin.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts +32 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js +62 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts +11 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/model.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +2 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/venice.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js +3 -3
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts +4 -2
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.d.ts.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js +18 -9
- package/node_modules/@earendil-works/pi-ai/dist/providers/devin.models.js.map +1 -1
- package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts +12 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts.map +1 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js +93 -0
- package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js.map +1 -0
- package/node_modules/@earendil-works/pi-ai/package.json +2 -2
- package/node_modules/@earendil-works/pi-pty/package.json +1 -1
- package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
- package/node_modules/@earendil-works/pi-tui/package.json +1 -1
- package/package.json +7 -7
|
@@ -18,8 +18,9 @@ import { rm } from "node:fs/promises";
|
|
|
18
18
|
import { basename, dirname } from "node:path";
|
|
19
19
|
import { isDeepStrictEqual } from "node:util";
|
|
20
20
|
import { ProviderRetryWatchdogAbortError, prepareAgentToolCall } from "@earendil-works/pi-agent-core";
|
|
21
|
-
import { contentText,
|
|
21
|
+
import { contentText, providerNotConfiguredMessage, SERVER_FALLBACK_ABORTED_DIAGNOSTIC, } from "@earendil-works/pi-ai";
|
|
22
22
|
import { cleanupSessionResources, cursorOverflowCompactionSettings, isClassifierRefusal, isContextOverflow, isCursorPayloadResourceExhausted, isCursorQuotaResourceExhausted, isCursorZeroTokenResourceExhausted, isProviderStreamStallError, isProviderTimeoutError, isRecoverableLength, isRetryableAssistantError, modelsAreEqual, resetApiProviders, shouldRetryOverflowWithoutCompact, streamSimple, } from "@earendil-works/pi-ai/compat";
|
|
23
|
+
import { getCursorContextLimit } from "@earendil-works/pi-ai/utils/cursor-context-limit";
|
|
23
24
|
import { extract429RetryAfterMs, parseRetryAfterMsMarker } from "@earendil-works/pi-ai/utils/retry-hint";
|
|
24
25
|
import { retryBackoffDelayMs } from "@earendil-works/pi-ai/utils/retry-profile/backoff";
|
|
25
26
|
import { getAgentDir } from "../config.js";
|
|
@@ -37,6 +38,7 @@ import { calculateContextTokens, collectEntriesForBranchSummary, compact, estima
|
|
|
37
38
|
import { CompactionLifecycleCoordinator } from "./compaction/lifecycle.js";
|
|
38
39
|
import { isTurnStuckOnContextOverflow } from "./compaction/stuck-overflow.js";
|
|
39
40
|
import { isWarmSummaryAnchorValid } from "./compaction/warm-anchor.js";
|
|
41
|
+
import { admitCursorHistory, cursorAdmissionBudgetBytes } from "./cursor-history-admission.js";
|
|
40
42
|
import { DEFAULT_THINKING_LEVEL } from "./defaults.js";
|
|
41
43
|
import { buildDynamicSystemPrompt } from "./dynamic-prompt/index.js";
|
|
42
44
|
import { AssistantEditError, assertExpectedLeaf, assistantTextEquals, buildEditedAssistantMessage, SessionStreamingError, } from "./edited-assistant-message.js";
|
|
@@ -57,7 +59,7 @@ import { emitSessionShutdownEvent } from "./extensions/runner.js";
|
|
|
57
59
|
import { normalizeToolExposure, RUNTIME_EXTENSION_PATH } from "./extensions/types.js";
|
|
58
60
|
import { shouldWarnHighReasoning } from "./high-reasoning-warning.js";
|
|
59
61
|
import { isManualContinueSubmission, MANUAL_CONTINUE_CUSTOM_TYPE, MANUAL_CONTINUE_DIRECTIVE, } from "./manual-continue.js";
|
|
60
|
-
import {
|
|
62
|
+
import { filterContextExcludedMessages } from "./messages.js";
|
|
61
63
|
import { ModelRegistry } from "./model-registry.js";
|
|
62
64
|
import { getModelNarrowingPatterns, resolveModelScope } from "./model-resolver.js";
|
|
63
65
|
import { PROMPT_CACHE_SAFE_WAIT_ENV, resolvePromptCacheSafeWaitSeconds } from "./prompt-cache-budget.js";
|
|
@@ -335,121 +337,11 @@ function isSameOverflowSource(message, model, upstreamModelId) {
|
|
|
335
337
|
const THINKING_LEVELS_WITH_MAX = ["off", "minimal", "low", "medium", "high", "xhigh", "max"];
|
|
336
338
|
/** Caps explicit skill expansion so one prompt cannot consume unbounded context. */
|
|
337
339
|
export const MAX_SKILL_EXPANSIONS_PER_PROMPT = 5;
|
|
338
|
-
/**
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
export
|
|
343
|
-
if (!Array.isArray(messages) || messages.length === 0)
|
|
344
|
-
return { messages, changed: false };
|
|
345
|
-
const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
|
|
346
|
-
const markerChars = [...segmenter.segment(CURSOR_TRUNCATION_MARKER)].length;
|
|
347
|
-
const result = messages.slice();
|
|
348
|
-
let changed = false;
|
|
349
|
-
// Apply the per-result character cap first, independent of the aggregate wire cap.
|
|
350
|
-
for (let messageIndex = result.length - 1; messageIndex >= 0; messageIndex--) {
|
|
351
|
-
const message = result[messageIndex];
|
|
352
|
-
if (message.role !== "toolResult" || !Array.isArray(message.content))
|
|
353
|
-
continue;
|
|
354
|
-
let content = message.content;
|
|
355
|
-
for (let partIndex = content.length - 1; partIndex >= 0; partIndex--) {
|
|
356
|
-
const part = content[partIndex];
|
|
357
|
-
if (part.type === "image" && typeof part.data === "string")
|
|
358
|
-
continue;
|
|
359
|
-
if (part.type !== "text" || typeof part.text !== "string")
|
|
360
|
-
continue;
|
|
361
|
-
const graphemes = [...segmenter.segment(part.text)].map((item) => item.segment);
|
|
362
|
-
if (graphemes.length <= maxChars)
|
|
363
|
-
continue;
|
|
364
|
-
const kept = graphemes.slice(0, Math.max(0, maxChars - markerChars)).join("");
|
|
365
|
-
const nextText = kept + CURSOR_TRUNCATION_MARKER;
|
|
366
|
-
content = content === message.content ? content.slice() : content;
|
|
367
|
-
content[partIndex] = { ...part, text: nextText };
|
|
368
|
-
result[messageIndex] = { ...message, content };
|
|
369
|
-
changed = true;
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
const measure = (candidate) => {
|
|
373
|
-
const converted = convert(candidate);
|
|
374
|
-
const activeUserMessageIndex = converted.at(-1)?.role === "user" ? converted.length - 1 : -1;
|
|
375
|
-
return measureCursorHistorySerializedBytes(converted, activeUserMessageIndex);
|
|
376
|
-
};
|
|
377
|
-
const fits = (candidate = result) => measure(candidate) <= maxBytes;
|
|
378
|
-
if (fits())
|
|
379
|
-
return { messages: changed ? result : messages, changed };
|
|
380
|
-
// Empty the oldest result bodies. Search the monotonic prefix of candidates
|
|
381
|
-
// rather than serializing once for every result (admission must stay bounded).
|
|
382
|
-
const emptyToolResult = (message) => {
|
|
383
|
-
if (message.role !== "toolResult" || !Array.isArray(message.content))
|
|
384
|
-
return message;
|
|
385
|
-
const emptiedContent = message.content.map((part) => part.type === "text" ? { ...part, text: "" } : part.type === "image" ? { ...part, data: "" } : part);
|
|
386
|
-
const content = emptiedContent.filter((part, index) => {
|
|
387
|
-
if (index === 0)
|
|
388
|
-
return true;
|
|
389
|
-
const previous = emptiedContent[index - 1];
|
|
390
|
-
const empty = part.type === "text" ? part.text === "" : part.type === "image" && part.data === "";
|
|
391
|
-
const previousEmpty = previous.type === "text" ? previous.text === "" : previous.type === "image" && previous.data === "";
|
|
392
|
-
return !empty || !previousEmpty;
|
|
393
|
-
});
|
|
394
|
-
return { ...message, content };
|
|
395
|
-
};
|
|
396
|
-
const toolResultIndexes = result.flatMap((message, index) => message.role === "toolResult" && Array.isArray(message.content) ? [index] : []);
|
|
397
|
-
const withEmptyPrefix = (count) => {
|
|
398
|
-
const candidate = result.slice();
|
|
399
|
-
for (let i = 0; i < count; i++)
|
|
400
|
-
candidate[toolResultIndexes[i]] = emptyToolResult(candidate[toolResultIndexes[i]]);
|
|
401
|
-
return candidate;
|
|
402
|
-
};
|
|
403
|
-
let low = 0;
|
|
404
|
-
let high = toolResultIndexes.length;
|
|
405
|
-
while (low < high) {
|
|
406
|
-
const middle = Math.floor((low + high) / 2);
|
|
407
|
-
if (fits(withEmptyPrefix(middle + 1)))
|
|
408
|
-
high = middle;
|
|
409
|
-
else
|
|
410
|
-
low = middle + 1;
|
|
411
|
-
}
|
|
412
|
-
const emptyCount = Math.min(low + 1, toolResultIndexes.length);
|
|
413
|
-
if (emptyCount > 0) {
|
|
414
|
-
result.splice(0, result.length, ...withEmptyPrefix(emptyCount));
|
|
415
|
-
changed = true;
|
|
416
|
-
}
|
|
417
|
-
if (fits())
|
|
418
|
-
return { messages: changed ? result : messages, changed };
|
|
419
|
-
// If metadata alone exceeds the cap, discard the oldest complete turns. This
|
|
420
|
-
// search is also monotonic and avoids quadratic whole-history reserialization.
|
|
421
|
-
const turnRanges = [];
|
|
422
|
-
const isConvertedUser = (message) => convert([message])[0]?.role === "user";
|
|
423
|
-
for (let index = 0; index < result.length; index++) {
|
|
424
|
-
if (!isConvertedUser(result[index]))
|
|
425
|
-
continue;
|
|
426
|
-
const nextUser = result.findIndex((message, nextIndex) => nextIndex > index && isConvertedUser(message));
|
|
427
|
-
turnRanges.push([index, nextUser < 0 ? result.length : nextUser]);
|
|
428
|
-
}
|
|
429
|
-
const withoutTurns = (count) => {
|
|
430
|
-
if (count === 0)
|
|
431
|
-
return result;
|
|
432
|
-
const start = turnRanges[0]?.[0] ?? 0;
|
|
433
|
-
const end = turnRanges[count - 1]?.[1] ?? start;
|
|
434
|
-
return [...result.slice(0, start), ...result.slice(end)];
|
|
435
|
-
};
|
|
436
|
-
low = 0;
|
|
437
|
-
high = turnRanges.length;
|
|
438
|
-
while (low < high) {
|
|
439
|
-
const middle = Math.floor((low + high) / 2);
|
|
440
|
-
if (fits(withoutTurns(middle + 1)))
|
|
441
|
-
high = middle;
|
|
442
|
-
else
|
|
443
|
-
low = middle + 1;
|
|
444
|
-
}
|
|
445
|
-
const turnCount = Math.min(low + 1, turnRanges.length);
|
|
446
|
-
if (turnCount > 0) {
|
|
447
|
-
const next = withoutTurns(turnCount);
|
|
448
|
-
result.splice(0, result.length, ...next);
|
|
449
|
-
changed = true;
|
|
450
|
-
}
|
|
451
|
-
return { messages: changed ? result : messages, changed };
|
|
452
|
-
}
|
|
340
|
+
/**
|
|
341
|
+
* Cursor admission lives in its own module; the names stay exported here so
|
|
342
|
+
* existing importers keep resolving them.
|
|
343
|
+
*/
|
|
344
|
+
export { CURSOR_TOOL_RESULT_MAX_CHARS, truncateToolResultBodies } from "./cursor-history-admission.js";
|
|
453
345
|
// ============================================================================
|
|
454
346
|
// AgentSession Class
|
|
455
347
|
// ============================================================================
|
|
@@ -905,6 +797,20 @@ export class AgentSession {
|
|
|
905
797
|
usage: hookResult?.usage,
|
|
906
798
|
};
|
|
907
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* Cursor states a model's real context ceiling on every conversation
|
|
802
|
+
* checkpoint. Once observed, it replaces the catalog guess on the live model
|
|
803
|
+
* so context usage, compaction thresholds and admission all size against the
|
|
804
|
+
* window the server will actually enforce.
|
|
805
|
+
*/
|
|
806
|
+
_applyObservedCursorContextWindow(model) {
|
|
807
|
+
const observed = getCursorContextLimit(model.id);
|
|
808
|
+
if (observed === undefined || observed <= 0 || observed === model.contextWindow)
|
|
809
|
+
return;
|
|
810
|
+
const previous = model.contextWindow;
|
|
811
|
+
model.contextWindow = observed;
|
|
812
|
+
this._sessionLogger.info("cursor_context_window_observed", { modelId: model.id, previous, observed });
|
|
813
|
+
}
|
|
908
814
|
_installAgentNextTurnRefresh() {
|
|
909
815
|
const previousPrepareNextTurnWithContext = this.agent.prepareNextTurnWithContext ??
|
|
910
816
|
(this.agent.prepareNextTurn
|
|
@@ -913,10 +819,33 @@ export class AgentSession {
|
|
|
913
819
|
const previousTransformContext = this.agent.transformContext;
|
|
914
820
|
this.agent.transformContext = async (messages, signal) => {
|
|
915
821
|
const transformed = previousTransformContext ? await previousTransformContext(messages, signal) : messages;
|
|
916
|
-
|
|
917
|
-
|
|
822
|
+
const model = this.model;
|
|
823
|
+
if (model?.provider !== "cursor" && model?.provider !== "cursor-cli-oauth")
|
|
824
|
+
return transformed;
|
|
825
|
+
this._applyObservedCursorContextWindow(model);
|
|
826
|
+
const budgetBytes = cursorAdmissionBudgetBytes(model.contextWindow);
|
|
827
|
+
const admission = admitCursorHistory({
|
|
828
|
+
messages: transformed,
|
|
829
|
+
budgetBytes,
|
|
830
|
+
convert: (candidate) => this.agent.convertToLlm(candidate),
|
|
831
|
+
});
|
|
832
|
+
if (admission.blankedToolResults > 0) {
|
|
833
|
+
this._sessionLogger.info("cursor_admission_truncated", {
|
|
834
|
+
blankedToolResults: admission.blankedToolResults,
|
|
835
|
+
bytesBefore: admission.bytesBefore,
|
|
836
|
+
bytesAfter: admission.bytesAfter,
|
|
837
|
+
budgetBytes,
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
if (admission.overBudget) {
|
|
841
|
+
// The request is still admitted: Cursor answers an oversized history
|
|
842
|
+
// with a 0-token resource_exhausted, which the session layer compacts.
|
|
843
|
+
this._sessionLogger.warn("cursor_admission_over_budget", {
|
|
844
|
+
bytes: admission.bytesAfter,
|
|
845
|
+
budgetBytes,
|
|
846
|
+
});
|
|
918
847
|
}
|
|
919
|
-
return transformed;
|
|
848
|
+
return admission.messages ?? transformed;
|
|
920
849
|
};
|
|
921
850
|
this.agent.prepareNextTurnWithContext = async (turn, signal) => {
|
|
922
851
|
// Enforce compaction only when this prepare precedes an actual provider
|
|
@@ -4897,7 +4826,11 @@ export class AgentSession {
|
|
|
4897
4826
|
// Size the same retained context that will be admitted to Cursor. Persisted JSONL
|
|
4898
4827
|
// remains verbatim, but the in-memory request representation is bounded first.
|
|
4899
4828
|
if (model.provider === "cursor" || model.provider === "cursor-cli-oauth") {
|
|
4900
|
-
simulatedMessages =
|
|
4829
|
+
simulatedMessages =
|
|
4830
|
+
admitCursorHistory({
|
|
4831
|
+
messages: simulatedMessages,
|
|
4832
|
+
budgetBytes: cursorAdmissionBudgetBytes(model.contextWindow),
|
|
4833
|
+
}).messages ?? simulatedMessages;
|
|
4901
4834
|
}
|
|
4902
4835
|
const contextTokens = estimateMessagesTokens(filterContextExcludedMessages(simulatedMessages));
|
|
4903
4836
|
const settings = this._getCompactionSettings();
|