pi-cursor-sdk 0.1.14 → 0.1.16
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 +57 -0
- package/README.md +68 -14
- package/docs/cursor-live-smoke-checklist.md +271 -0
- package/docs/cursor-model-ux-spec.md +27 -4
- package/docs/cursor-native-tool-replay.md +99 -0
- package/docs/cursor-native-tool-visual-audit.md +183 -0
- package/package.json +6 -2
- package/src/context.ts +214 -16
- package/src/cursor-bridge-contract.ts +27 -0
- package/src/cursor-live-run-accounting.ts +65 -0
- package/src/cursor-mcp-timeout-override.ts +111 -0
- package/src/cursor-native-tool-display.ts +409 -49
- package/src/cursor-pi-tool-bridge.ts +1174 -0
- package/src/cursor-provider.ts +614 -146
- package/src/cursor-question-tool.ts +252 -0
- package/src/cursor-session-agent.ts +372 -0
- package/src/cursor-session-cwd.ts +28 -0
- package/src/cursor-session-scope.ts +65 -0
- package/src/cursor-state.ts +38 -10
- package/src/cursor-tool-names.ts +67 -0
- package/src/cursor-tool-transcript.ts +730 -61
- package/src/cursor-usage-accounting.ts +71 -0
- package/src/index.ts +27 -3
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { Api, AssistantMessage, Context, Model } from "@earendil-works/pi-ai";
|
|
2
|
+
import {
|
|
3
|
+
CURSOR_APPROX_CHARS_PER_TOKEN,
|
|
4
|
+
CURSOR_IMAGE_TOKEN_ESTIMATE,
|
|
5
|
+
estimateCursorContextTokens,
|
|
6
|
+
estimateCursorPromptTokens,
|
|
7
|
+
estimateCursorTextTokens,
|
|
8
|
+
type CursorPrompt,
|
|
9
|
+
type CursorPromptOptions,
|
|
10
|
+
} from "./context.js";
|
|
11
|
+
|
|
12
|
+
export interface CursorUsagePromptOptions extends CursorPromptOptions {
|
|
13
|
+
maxInputTokens: number;
|
|
14
|
+
charsPerToken: number;
|
|
15
|
+
imageTokenEstimate: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getPromptInputTokenBudget(model: Model<Api>): number {
|
|
19
|
+
const outputReserveTokens = Math.min(model.maxTokens, Math.max(1, Math.floor(model.contextWindow * 0.2)));
|
|
20
|
+
return Math.max(1, model.contextWindow - outputReserveTokens);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function getCursorPromptOptions(model: Model<Api>): CursorUsagePromptOptions {
|
|
24
|
+
return {
|
|
25
|
+
maxInputTokens: getPromptInputTokenBudget(model),
|
|
26
|
+
charsPerToken: CURSOR_APPROX_CHARS_PER_TOKEN,
|
|
27
|
+
imageTokenEstimate: CURSOR_IMAGE_TOKEN_ESTIMATE,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function estimateCursorPromptInputTokens(prompt: CursorPrompt, options: Pick<CursorPromptOptions, "charsPerToken" | "imageTokenEstimate">): number {
|
|
32
|
+
return estimateCursorPromptTokens(prompt, options);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function stringifyUsageValue(value: unknown): string {
|
|
36
|
+
try {
|
|
37
|
+
return JSON.stringify(value) ?? "";
|
|
38
|
+
} catch {
|
|
39
|
+
return String(value);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function estimateCursorAssistantSessionOutputTokens(message: AssistantMessage): number {
|
|
44
|
+
const parts = message.content
|
|
45
|
+
.map((block) => {
|
|
46
|
+
if (block.type === "text") return block.text;
|
|
47
|
+
if (block.type === "thinking") return block.thinking;
|
|
48
|
+
if (block.type === "toolCall") {
|
|
49
|
+
return `Tool call (${block.name}, call ${block.id}): ${stringifyUsageValue(block.arguments)}`;
|
|
50
|
+
}
|
|
51
|
+
return "";
|
|
52
|
+
})
|
|
53
|
+
.filter(Boolean);
|
|
54
|
+
return estimateCursorTextTokens(parts.join("\n"), { charsPerToken: CURSOR_APPROX_CHARS_PER_TOKEN });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function withAssistantMessage(context: Context, partial: AssistantMessage): Context {
|
|
58
|
+
return { ...context, messages: [...context.messages, partial] };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function estimateCursorContextTotalTokens(partial: AssistantMessage, model: Model<Api>, context: Context): number {
|
|
62
|
+
return estimateCursorContextTokens(withAssistantMessage(context, partial), getCursorPromptOptions(model));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function applyCursorApproximateUsage(partial: AssistantMessage, model: Model<Api>, context: Context, sessionInputTokens: number): void {
|
|
66
|
+
partial.usage.input = sessionInputTokens;
|
|
67
|
+
partial.usage.output = estimateCursorAssistantSessionOutputTokens(partial);
|
|
68
|
+
partial.usage.cacheRead = 0;
|
|
69
|
+
partial.usage.cacheWrite = 0;
|
|
70
|
+
partial.usage.totalTokens = estimateCursorContextTotalTokens(partial, model, context);
|
|
71
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,28 @@
|
|
|
1
|
-
import type { ExtensionAPI, ProviderConfig, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
1
|
+
import type { ExtensionAPI, ExtensionContext, ProviderConfig, ProviderModelConfig } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { discoverModels, type CursorModelFallbackIssue } from "./model-discovery.js";
|
|
3
3
|
import { registerCursorFastControls } from "./cursor-state.js";
|
|
4
4
|
import { registerCursorNativeToolDisplay } from "./cursor-native-tool-display.js";
|
|
5
|
+
import { registerCursorPiToolBridge } from "./cursor-pi-tool-bridge.js";
|
|
6
|
+
import { registerCursorQuestionTool } from "./cursor-question-tool.js";
|
|
7
|
+
import { registerCursorSessionCwd } from "./cursor-session-cwd.js";
|
|
8
|
+
import { registerCursorSessionAgent } from "./cursor-session-agent.js";
|
|
5
9
|
import { streamCursor } from "./cursor-provider.js";
|
|
6
10
|
|
|
11
|
+
type CursorExtensionApi =
|
|
12
|
+
& Pick<ExtensionAPI, "registerProvider">
|
|
13
|
+
& {
|
|
14
|
+
registerCommand(name: string, options: {
|
|
15
|
+
description?: string;
|
|
16
|
+
handler: (args: string, ctx: Pick<ExtensionContext, "hasUI"> & { ui: Pick<ExtensionContext["ui"], "notify"> }) => Promise<void> | void;
|
|
17
|
+
}): void;
|
|
18
|
+
}
|
|
19
|
+
& Parameters<typeof registerCursorSessionCwd>[0]
|
|
20
|
+
& Parameters<typeof registerCursorSessionAgent>[0]
|
|
21
|
+
& Parameters<typeof registerCursorFastControls>[0]
|
|
22
|
+
& Parameters<typeof registerCursorNativeToolDisplay>[0]
|
|
23
|
+
& Parameters<typeof registerCursorQuestionTool>[0]
|
|
24
|
+
& Parameters<typeof registerCursorPiToolBridge>[0];
|
|
25
|
+
|
|
7
26
|
function createCursorProviderConfig(models: ProviderModelConfig[]): ProviderConfig {
|
|
8
27
|
return {
|
|
9
28
|
name: "Cursor",
|
|
@@ -15,13 +34,18 @@ function createCursorProviderConfig(models: ProviderModelConfig[]): ProviderConf
|
|
|
15
34
|
};
|
|
16
35
|
}
|
|
17
36
|
|
|
18
|
-
function registerCursorProvider(pi: ExtensionAPI, models: ProviderModelConfig[]): void {
|
|
37
|
+
function registerCursorProvider(pi: Pick<ExtensionAPI, "registerProvider">, models: ProviderModelConfig[]): void {
|
|
19
38
|
pi.registerProvider("cursor", createCursorProviderConfig(models));
|
|
20
39
|
}
|
|
21
40
|
|
|
22
|
-
export default async function (pi:
|
|
41
|
+
export default async function (pi: CursorExtensionApi) {
|
|
42
|
+
// Session cwd must register before other session_start listeners that depend on it.
|
|
43
|
+
registerCursorSessionCwd(pi);
|
|
44
|
+
registerCursorSessionAgent(pi);
|
|
23
45
|
registerCursorFastControls(pi);
|
|
24
46
|
registerCursorNativeToolDisplay(pi);
|
|
47
|
+
registerCursorQuestionTool(pi);
|
|
48
|
+
registerCursorPiToolBridge(pi);
|
|
25
49
|
let fallbackIssue: CursorModelFallbackIssue | undefined;
|
|
26
50
|
const models = await discoverModels({
|
|
27
51
|
onFallback: (issue) => {
|