@code-yeongyu/senpi 2026.9.12-2 → 2026.9.12

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.
Files changed (99) hide show
  1. package/CHANGELOG.md +0 -17
  2. package/dist/core/agent-session-runtime.d.ts.map +1 -1
  3. package/dist/core/agent-session-runtime.js +1 -5
  4. package/dist/core/agent-session-runtime.js.map +1 -1
  5. package/dist/core/agent-session.d.ts +9 -13
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +121 -54
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/extensions/builtin/cursor-cli-oauth/models.d.ts.map +1 -1
  10. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js +1 -2
  11. package/dist/core/extensions/builtin/cursor-cli-oauth/models.js.map +1 -1
  12. package/dist/core/session-manager.d.ts +0 -2
  13. package/dist/core/session-manager.d.ts.map +1 -1
  14. package/dist/core/session-manager.js +13 -22
  15. package/dist/core/session-manager.js.map +1 -1
  16. package/dist/core/session-write-reservation.d.ts +0 -9
  17. package/dist/core/session-write-reservation.d.ts.map +1 -1
  18. package/dist/core/session-write-reservation.js +0 -33
  19. package/dist/core/session-write-reservation.js.map +1 -1
  20. package/dist/modes/interactive/components/ask-user-async-widget.d.ts +0 -2
  21. package/dist/modes/interactive/components/ask-user-async-widget.d.ts.map +1 -1
  22. package/dist/modes/interactive/components/ask-user-async-widget.js +1 -17
  23. package/dist/modes/interactive/components/ask-user-async-widget.js.map +1 -1
  24. package/dist/modes/interactive/interactive-mode.js +2 -2
  25. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  26. package/dist/modes/rpc/rpc-types.d.ts +1 -2
  27. package/dist/modes/rpc/rpc-types.d.ts.map +1 -1
  28. package/dist/modes/rpc/rpc-types.js +0 -1
  29. package/dist/modes/rpc/rpc-types.js.map +1 -1
  30. package/dist/modes/rpc/session-registry.d.ts +1 -3
  31. package/dist/modes/rpc/session-registry.d.ts.map +1 -1
  32. package/dist/modes/rpc/session-registry.js.map +1 -1
  33. package/dist/modes/rpc/session-worker-client.d.ts +8 -10
  34. package/dist/modes/rpc/session-worker-client.d.ts.map +1 -1
  35. package/dist/modes/rpc/session-worker-client.js +23 -14
  36. package/dist/modes/rpc/session-worker-client.js.map +1 -1
  37. package/dist/modes/rpc/session-worker-protocol.d.ts +0 -10
  38. package/dist/modes/rpc/session-worker-protocol.d.ts.map +1 -1
  39. package/dist/modes/rpc/session-worker-protocol.js +0 -2
  40. package/dist/modes/rpc/session-worker-protocol.js.map +1 -1
  41. package/dist/modes/rpc/session-worker.js +16 -12
  42. package/dist/modes/rpc/session-worker.js.map +1 -1
  43. package/dist/modes/rpc/worker-session-registry.d.ts +0 -12
  44. package/dist/modes/rpc/worker-session-registry.d.ts.map +1 -1
  45. package/dist/modes/rpc/worker-session-registry.js +24 -51
  46. package/dist/modes/rpc/worker-session-registry.js.map +1 -1
  47. package/docs/rpc.md +6 -14
  48. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +0 -12
  49. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  50. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  51. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts +0 -11
  52. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.d.ts.map +1 -1
  53. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js +0 -18
  54. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent/measure.js.map +1 -1
  55. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts +1 -1
  56. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.d.ts.map +1 -1
  57. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js +1 -5
  58. package/node_modules/@earendil-works/pi-ai/dist/api/cursor-agent.js.map +1 -1
  59. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.d.ts.map +1 -1
  60. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js +1 -2
  61. package/node_modules/@earendil-works/pi-ai/dist/cursor/store-migration.js.map +1 -1
  62. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts +1 -1
  63. package/node_modules/@earendil-works/pi-ai/dist/index.d.ts.map +1 -1
  64. package/node_modules/@earendil-works/pi-ai/dist/index.js +1 -1
  65. package/node_modules/@earendil-works/pi-ai/dist/index.js.map +1 -1
  66. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.d.ts.map +1 -1
  67. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js +1 -2
  68. package/node_modules/@earendil-works/pi-ai/dist/providers/cursor.js.map +1 -1
  69. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  70. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  71. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  72. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  73. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  74. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  75. package/package.json +7 -7
  76. package/dist/core/cursor-history-admission.d.ts +0 -50
  77. package/dist/core/cursor-history-admission.d.ts.map +0 -1
  78. package/dist/core/cursor-history-admission.js +0 -130
  79. package/dist/core/cursor-history-admission.js.map +0 -1
  80. package/dist/modes/rpc/session-path-reservations.d.ts +0 -30
  81. package/dist/modes/rpc/session-path-reservations.d.ts.map +0 -1
  82. package/dist/modes/rpc/session-path-reservations.js +0 -58
  83. package/dist/modes/rpc/session-path-reservations.js.map +0 -1
  84. package/dist/modes/rpc/session-worker-credit.d.ts +0 -17
  85. package/dist/modes/rpc/session-worker-credit.d.ts.map +0 -1
  86. package/dist/modes/rpc/session-worker-credit.js +0 -39
  87. package/dist/modes/rpc/session-worker-credit.js.map +0 -1
  88. package/dist/modes/rpc/session-worker-signals.d.ts +0 -7
  89. package/dist/modes/rpc/session-worker-signals.d.ts.map +0 -1
  90. package/dist/modes/rpc/session-worker-signals.js +0 -19
  91. package/dist/modes/rpc/session-worker-signals.js.map +0 -1
  92. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts +0 -32
  93. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.d.ts.map +0 -1
  94. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js +0 -62
  95. package/node_modules/@earendil-works/pi-ai/dist/cursor/context-limit-store.js.map +0 -1
  96. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts +0 -12
  97. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.d.ts.map +0 -1
  98. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js +0 -93
  99. package/node_modules/@earendil-works/pi-ai/dist/utils/cursor-context-limit.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"context-limit-store.js","sourceRoot":"","sources":["../../src/cursor/context-limit-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAkB,CAAC;AACjD,IAAI,WAAsD,CAAC;AAC3D,IAAI,QAAQ,GAAG,KAAK,CAAC;AAErB,gFAAgF;AAChF,MAAM,UAAU,oCAAoC,CAAC,IAAmC;IACvF,IAAI,WAAW,KAAK,IAAI;QAAE,OAAO;IACjC,WAAW,GAAG,IAAI,CAAC;IACnB,QAAQ,GAAG,KAAK,CAAC;AAClB,CAAC;AAED,SAAS,OAAO;IACf,IAAI,QAAQ;QAAE,OAAO;IACrB,4EAA4E;IAC5E,QAAQ,GAAG,IAAI,CAAC;IAChB,IAAI,CAAC,WAAW;QAAE,OAAO;IACzB,KAAK,MAAM,CAAC,OAAO,EAAE,SAAS,CAAC,IAAI,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC;QACvD,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAC1E,CAAC;AACF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,OAAe,EAAE,SAA6B;IACtF,IAAI,SAAS,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,SAAS,IAAI,CAAC;QAAE,OAAO;IACrF,OAAO,EAAE,CAAC;IACV,IAAI,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,SAAS;QAAE,OAAO;IACtD,cAAc,CAAC,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IACvC,WAAW,EAAE,IAAI,CAAC,cAAc,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,OAAe;IACpD,OAAO,EAAE,CAAC;IACV,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACpC,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,0BAA0B,CAAC,OAAe,EAAE,aAAqB;IAChF,OAAO,qBAAqB,CAAC,OAAO,CAAC,IAAI,aAAa,CAAC;AACxD,CAAC;AAED,kFAAkF;AAClF,MAAM,UAAU,mCAAmC;IAClD,cAAc,CAAC,KAAK,EAAE,CAAC;IACvB,QAAQ,GAAG,KAAK,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Context ceilings Cursor itself reported, keyed by model id.\n *\n * `GetUsableModels` carries no window, so `CURSOR_MODEL_CAPABILITIES` is a\n * committed guess at what each family accepts. The server states the truth on\n * every conversation checkpoint (`tokenDetails.maxTokens`), so once a model has\n * been observed, that value outranks the catalog for every later request.\n *\n * This module stays browser-safe: `providers/cursor.ts` and\n * `cursor/store-migration.ts` materialize catalog windows and are bundled for\n * the browser. Node processes install file persistence through\n * `installCursorContextLimitPersistence` - see `utils/cursor-context-limit.ts`.\n */\n\nexport type CursorContextLimitPersistence = {\n\t/** Limits observed by earlier processes. Called at most once per install. */\n\treadonly load: () => ReadonlyMap<string, number>;\n\t/** Called only when a recorded limit actually changed the store. */\n\treadonly save: (limits: ReadonlyMap<string, number>) => void;\n};\n\nconst observedLimits = new Map<string, number>();\nlet persistence: CursorContextLimitPersistence | undefined;\nlet hydrated = false;\n\n/** Installs the process-wide persistence port. Idempotent per port identity. */\nexport function installCursorContextLimitPersistence(port: CursorContextLimitPersistence): void {\n\tif (persistence === port) return;\n\tpersistence = port;\n\thydrated = false;\n}\n\nfunction hydrate(): void {\n\tif (hydrated) return;\n\t// Set before loading: a load that observes nothing must not retry per read.\n\thydrated = true;\n\tif (!persistence) return;\n\tfor (const [modelId, maxTokens] of persistence.load()) {\n\t\tif (!observedLimits.has(modelId)) observedLimits.set(modelId, maxTokens);\n\t}\n}\n\n/**\n * Records the server-reported ceiling for `modelId`. The first checkpoint of a\n * conversation reports 0, so non-positive and non-finite values are ignored.\n */\nexport function recordCursorContextLimit(modelId: string, maxTokens: number | undefined): void {\n\tif (maxTokens === undefined || !Number.isFinite(maxTokens) || maxTokens <= 0) return;\n\thydrate();\n\tif (observedLimits.get(modelId) === maxTokens) return;\n\tobservedLimits.set(modelId, maxTokens);\n\tpersistence?.save(observedLimits);\n}\n\nexport function getCursorContextLimit(modelId: string): number | undefined {\n\thydrate();\n\treturn observedLimits.get(modelId);\n}\n\n/** The window to trust for `modelId`: what the server reported, else the catalog. */\nexport function resolveCursorContextWindow(modelId: string, catalogWindow: number): number {\n\treturn getCursorContextLimit(modelId) ?? catalogWindow;\n}\n\n/** Drops in-memory state so the next read re-hydrates from the installed port. */\nexport function resetCursorContextLimitStoreForTest(): void {\n\tobservedLimits.clear();\n\thydrated = false;\n}\n"]}
@@ -1,12 +0,0 @@
1
- /**
2
- * Same resolution as the conversation rotation store: an explicit override
3
- * first, then the agent directory the host configured, then the default one.
4
- */
5
- export declare function resolveCursorContextLimitStorePath(env?: NodeJS.ProcessEnv): string;
6
- /** Records the ceiling Cursor reported for `modelId`; ignores non-positive values. */
7
- export declare function recordCursorContextLimit(modelId: string, maxTokens: number | undefined): void;
8
- export declare function getCursorContextLimit(modelId: string): number | undefined;
9
- /** The window to trust for `modelId`: what the server reported, else the catalog. */
10
- export declare function resolveCursorContextWindow(modelId: string, catalogWindow: number): number;
11
- export declare function resetCursorContextLimitStoreForTest(): void;
12
- //# sourceMappingURL=cursor-context-limit.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor-context-limit.d.ts","sourceRoot":"","sources":["../../src/utils/cursor-context-limit.ts"],"names":[],"mappings":"AAqBA;;;GAGG;AACH,wBAAgB,kCAAkC,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,CAO/F;AAgDD,sFAAsF;AACtF,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAG7F;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGzE;AAED,qFAAqF;AACrF,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAG1D"}
@@ -1,93 +0,0 @@
1
- /**
2
- * Node entry point for the observed Cursor context limits.
3
- *
4
- * The store itself (`cursor/context-limit-store.ts`) is browser-safe because the
5
- * catalog builders that read it are bundled for the browser. Persistence needs
6
- * `node:fs`, so it lives here and is installed on first use rather than as an
7
- * import side effect, which a bundler is free to drop for a package declared
8
- * side-effect-free. Node callers - the `cursor-agent` stream and the coding
9
- * agent - use this module; browser-facing code uses the store directly.
10
- */
11
- import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
12
- import { dirname } from "node:path";
13
- import { installCursorContextLimitPersistence, getCursorContextLimit as readObservedLimit, recordCursorContextLimit as recordObservedLimit, resetCursorContextLimitStoreForTest as resetObservedLimits, resolveCursorContextWindow as resolveObservedWindow, } from "../cursor/context-limit-store.js";
14
- /**
15
- * Same resolution as the conversation rotation store: an explicit override
16
- * first, then the agent directory the host configured, then the default one.
17
- */
18
- export function resolveCursorContextLimitStorePath(env = process.env) {
19
- if (env.CURSOR_CONTEXT_LIMIT_STORE) {
20
- return env.CURSOR_CONTEXT_LIMIT_STORE;
21
- }
22
- const agentDir = env.SENPI_CODING_AGENT_DIR ?? env.CODING_AGENT_DIR ?? `${(env.HOME ?? ".").replace(/\/$/, "")}/.senpi/agent`;
23
- return `${agentDir.replace(/\/$/, "")}/cursor-context-limits.json`;
24
- }
25
- let savingEnabled = true;
26
- const filePersistence = {
27
- load: () => {
28
- const limits = new Map();
29
- let parsed;
30
- try {
31
- parsed = JSON.parse(readFileSync(resolveCursorContextLimitStorePath(), "utf8"));
32
- }
33
- catch (error) {
34
- // No file on first run, and a truncated or hand-edited one is the same
35
- // situation for a cache: the catalog window is the documented fallback.
36
- if (error instanceof Error)
37
- return limits;
38
- throw error;
39
- }
40
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
41
- return limits;
42
- for (const [modelId, maxTokens] of Object.entries(parsed)) {
43
- if (typeof maxTokens === "number" && Number.isFinite(maxTokens) && maxTokens > 0) {
44
- limits.set(modelId, maxTokens);
45
- }
46
- }
47
- return limits;
48
- },
49
- save: (limits) => {
50
- if (!savingEnabled)
51
- return;
52
- const path = resolveCursorContextLimitStorePath();
53
- const record = {};
54
- for (const [modelId, maxTokens] of limits)
55
- record[modelId] = maxTokens;
56
- try {
57
- mkdirSync(dirname(path), { recursive: true });
58
- const temporaryPath = `${path}.${process.pid}.tmp`;
59
- writeFileSync(temporaryPath, `${JSON.stringify(record, null, 2)}\n`);
60
- renameSync(temporaryPath, path);
61
- }
62
- catch (error) {
63
- // This file is a cache for the next process. A read-only agent directory
64
- // must not fail the live turn that observed the limit, and one failure
65
- // disables saving so a broken path cannot cost a write per checkpoint.
66
- if (!(error instanceof Error))
67
- throw error;
68
- savingEnabled = false;
69
- }
70
- },
71
- };
72
- function ensurePersistenceInstalled() {
73
- installCursorContextLimitPersistence(filePersistence);
74
- }
75
- /** Records the ceiling Cursor reported for `modelId`; ignores non-positive values. */
76
- export function recordCursorContextLimit(modelId, maxTokens) {
77
- ensurePersistenceInstalled();
78
- recordObservedLimit(modelId, maxTokens);
79
- }
80
- export function getCursorContextLimit(modelId) {
81
- ensurePersistenceInstalled();
82
- return readObservedLimit(modelId);
83
- }
84
- /** The window to trust for `modelId`: what the server reported, else the catalog. */
85
- export function resolveCursorContextWindow(modelId, catalogWindow) {
86
- ensurePersistenceInstalled();
87
- return resolveObservedWindow(modelId, catalogWindow);
88
- }
89
- export function resetCursorContextLimitStoreForTest() {
90
- savingEnabled = true;
91
- resetObservedLimits();
92
- }
93
- //# sourceMappingURL=cursor-context-limit.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cursor-context-limit.js","sourceRoot":"","sources":["../../src/utils/cursor-context-limit.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Node entry point for the observed Cursor context limits.\n *\n * The store itself (`cursor/context-limit-store.ts`) is browser-safe because the\n * catalog builders that read it are bundled for the browser. Persistence needs\n * `node:fs`, so it lives here and is installed on first use rather than as an\n * import side effect, which a bundler is free to drop for a package declared\n * side-effect-free. Node callers - the `cursor-agent` stream and the coding\n * agent - use this module; browser-facing code uses the store directly.\n */\nimport { mkdirSync, readFileSync, renameSync, writeFileSync } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport {\n\ttype CursorContextLimitPersistence,\n\tinstallCursorContextLimitPersistence,\n\tgetCursorContextLimit as readObservedLimit,\n\trecordCursorContextLimit as recordObservedLimit,\n\tresetCursorContextLimitStoreForTest as resetObservedLimits,\n\tresolveCursorContextWindow as resolveObservedWindow,\n} from \"../cursor/context-limit-store.ts\";\n\n/**\n * Same resolution as the conversation rotation store: an explicit override\n * first, then the agent directory the host configured, then the default one.\n */\nexport function resolveCursorContextLimitStorePath(env: NodeJS.ProcessEnv = process.env): string {\n\tif (env.CURSOR_CONTEXT_LIMIT_STORE) {\n\t\treturn env.CURSOR_CONTEXT_LIMIT_STORE;\n\t}\n\tconst agentDir =\n\t\tenv.SENPI_CODING_AGENT_DIR ?? env.CODING_AGENT_DIR ?? `${(env.HOME ?? \".\").replace(/\\/$/, \"\")}/.senpi/agent`;\n\treturn `${agentDir.replace(/\\/$/, \"\")}/cursor-context-limits.json`;\n}\n\nlet savingEnabled = true;\n\nconst filePersistence: CursorContextLimitPersistence = {\n\tload: () => {\n\t\tconst limits = new Map<string, number>();\n\t\tlet parsed: unknown;\n\t\ttry {\n\t\t\tparsed = JSON.parse(readFileSync(resolveCursorContextLimitStorePath(), \"utf8\"));\n\t\t} catch (error) {\n\t\t\t// No file on first run, and a truncated or hand-edited one is the same\n\t\t\t// situation for a cache: the catalog window is the documented fallback.\n\t\t\tif (error instanceof Error) return limits;\n\t\t\tthrow error;\n\t\t}\n\t\tif (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) return limits;\n\t\tfor (const [modelId, maxTokens] of Object.entries(parsed)) {\n\t\t\tif (typeof maxTokens === \"number\" && Number.isFinite(maxTokens) && maxTokens > 0) {\n\t\t\t\tlimits.set(modelId, maxTokens);\n\t\t\t}\n\t\t}\n\t\treturn limits;\n\t},\n\tsave: (limits) => {\n\t\tif (!savingEnabled) return;\n\t\tconst path = resolveCursorContextLimitStorePath();\n\t\tconst record: Record<string, number> = {};\n\t\tfor (const [modelId, maxTokens] of limits) record[modelId] = maxTokens;\n\t\ttry {\n\t\t\tmkdirSync(dirname(path), { recursive: true });\n\t\t\tconst temporaryPath = `${path}.${process.pid}.tmp`;\n\t\t\twriteFileSync(temporaryPath, `${JSON.stringify(record, null, 2)}\\n`);\n\t\t\trenameSync(temporaryPath, path);\n\t\t} catch (error) {\n\t\t\t// This file is a cache for the next process. A read-only agent directory\n\t\t\t// must not fail the live turn that observed the limit, and one failure\n\t\t\t// disables saving so a broken path cannot cost a write per checkpoint.\n\t\t\tif (!(error instanceof Error)) throw error;\n\t\t\tsavingEnabled = false;\n\t\t}\n\t},\n};\n\nfunction ensurePersistenceInstalled(): void {\n\tinstallCursorContextLimitPersistence(filePersistence);\n}\n\n/** Records the ceiling Cursor reported for `modelId`; ignores non-positive values. */\nexport function recordCursorContextLimit(modelId: string, maxTokens: number | undefined): void {\n\tensurePersistenceInstalled();\n\trecordObservedLimit(modelId, maxTokens);\n}\n\nexport function getCursorContextLimit(modelId: string): number | undefined {\n\tensurePersistenceInstalled();\n\treturn readObservedLimit(modelId);\n}\n\n/** The window to trust for `modelId`: what the server reported, else the catalog. */\nexport function resolveCursorContextWindow(modelId: string, catalogWindow: number): number {\n\tensurePersistenceInstalled();\n\treturn resolveObservedWindow(modelId, catalogWindow);\n}\n\nexport function resetCursorContextLimitStoreForTest(): void {\n\tsavingEnabled = true;\n\tresetObservedLimits();\n}\n"]}