@velum-labs/cursorkit 0.1.0
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/DISCLAIMER.md +12 -0
- package/README.md +157 -0
- package/dist/src/agentTools/diff.d.ts +11 -0
- package/dist/src/agentTools/diff.js +88 -0
- package/dist/src/agentTools/policy.d.ts +3 -0
- package/dist/src/agentTools/policy.js +12 -0
- package/dist/src/agentTools/registry.d.ts +114 -0
- package/dist/src/agentTools/registry.js +663 -0
- package/dist/src/agentTools/results.d.ts +14 -0
- package/dist/src/agentTools/results.js +117 -0
- package/dist/src/agentTools/schemas.d.ts +3 -0
- package/dist/src/agentTools/schemas.js +89 -0
- package/dist/src/agentTools/surface.d.ts +11 -0
- package/dist/src/agentTools/surface.js +251 -0
- package/dist/src/certs.d.ts +8 -0
- package/dist/src/certs.js +34 -0
- package/dist/src/ck.d.ts +2 -0
- package/dist/src/ck.js +6 -0
- package/dist/src/ckLauncher.d.ts +150 -0
- package/dist/src/ckLauncher.js +1496 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +265 -0
- package/dist/src/config.d.ts +52 -0
- package/dist/src/config.js +210 -0
- package/dist/src/connectEnvelope.d.ts +16 -0
- package/dist/src/connectEnvelope.js +70 -0
- package/dist/src/desktop.d.ts +19 -0
- package/dist/src/desktop.js +167 -0
- package/dist/src/desktopConnectProxy.d.ts +26 -0
- package/dist/src/desktopConnectProxy.js +175 -0
- package/dist/src/extensions/index.d.ts +2 -0
- package/dist/src/extensions/index.js +1 -0
- package/dist/src/extensions/registry.d.ts +8 -0
- package/dist/src/extensions/registry.js +52 -0
- package/dist/src/extensions/types.d.ts +42 -0
- package/dist/src/extensions/types.js +1 -0
- package/dist/src/fixtures/modelFusion.d.ts +103 -0
- package/dist/src/fixtures/modelFusion.js +404 -0
- package/dist/src/fixtures/replay.d.ts +9 -0
- package/dist/src/fixtures/replay.js +41 -0
- package/dist/src/fixtures/sanitizer.d.ts +9 -0
- package/dist/src/fixtures/sanitizer.js +43 -0
- package/dist/src/fixtures/schema.d.ts +38 -0
- package/dist/src/fixtures/schema.js +33 -0
- package/dist/src/gen/agent/v1/agent_pb.d.ts +21577 -0
- package/dist/src/gen/agent/v1/agent_pb.js +5325 -0
- package/dist/src/gen/aiserver/v1/aiserver_pb.d.ts +135242 -0
- package/dist/src/gen/aiserver/v1/aiserver_pb.js +34430 -0
- package/dist/src/gen/anyrun/v1/anyrun_pb.d.ts +1163 -0
- package/dist/src/gen/anyrun/v1/anyrun_pb.js +374 -0
- package/dist/src/gen/google/protobuf/google_pb.d.ts +142 -0
- package/dist/src/gen/google/protobuf/google_pb.js +54 -0
- package/dist/src/gen/internapi/v1/internapi_pb.d.ts +121 -0
- package/dist/src/gen/internapi/v1/internapi_pb.js +79 -0
- package/dist/src/logger.d.ts +8 -0
- package/dist/src/logger.js +37 -0
- package/dist/src/modelFusion/cursorHarness.d.ts +146 -0
- package/dist/src/modelFusion/cursorHarness.js +647 -0
- package/dist/src/modelFusion/index.d.ts +4 -0
- package/dist/src/modelFusion/index.js +2 -0
- package/dist/src/models/registry.d.ts +22 -0
- package/dist/src/models/registry.js +30 -0
- package/dist/src/proto.d.ts +13 -0
- package/dist/src/proto.js +61 -0
- package/dist/src/providers/openai.d.ts +64 -0
- package/dist/src/providers/openai.js +355 -0
- package/dist/src/redaction.d.ts +4 -0
- package/dist/src/redaction.js +65 -0
- package/dist/src/routeInventory.d.ts +16 -0
- package/dist/src/routeInventory.js +39 -0
- package/dist/src/routes.d.ts +37 -0
- package/dist/src/routes.js +227 -0
- package/dist/src/server.d.ts +50 -0
- package/dist/src/server.js +1353 -0
- package/dist/src/services/agent.d.ts +1 -0
- package/dist/src/services/agent.js +7 -0
- package/dist/src/services/agentRun.d.ts +60 -0
- package/dist/src/services/agentRun.js +391 -0
- package/dist/src/services/chat.d.ts +11 -0
- package/dist/src/services/chat.js +47 -0
- package/dist/src/services/models.d.ts +10 -0
- package/dist/src/services/models.js +216 -0
- package/dist/src/services/serverConfig.d.ts +2 -0
- package/dist/src/services/serverConfig.js +19 -0
- package/dist/src/testing/artifacts.d.ts +14 -0
- package/dist/src/testing/artifacts.js +92 -0
- package/dist/src/testing/cli.d.ts +4 -0
- package/dist/src/testing/cli.js +192 -0
- package/dist/src/testing/localBackend.d.ts +24 -0
- package/dist/src/testing/localBackend.js +310 -0
- package/dist/src/testing/processRunner.d.ts +7 -0
- package/dist/src/testing/processRunner.js +74 -0
- package/dist/src/testing/runner.d.ts +9 -0
- package/dist/src/testing/runner.js +85 -0
- package/dist/src/testing/scenarios.d.ts +3 -0
- package/dist/src/testing/scenarios.js +2535 -0
- package/dist/src/testing/types.d.ts +66 -0
- package/dist/src/testing/types.js +1 -0
- package/dist/src/tools/baselineInventory.d.ts +12 -0
- package/dist/src/tools/baselineInventory.js +680 -0
- package/dist/src/tools/checkModelFusionProtocol.d.ts +1 -0
- package/dist/src/tools/checkModelFusionProtocol.js +274 -0
- package/dist/src/tools/checkReleasePublishConfig.d.ts +1 -0
- package/dist/src/tools/checkReleasePublishConfig.js +99 -0
- package/dist/src/tools/generateProtoInventory.d.ts +1 -0
- package/dist/src/tools/generateProtoInventory.js +89 -0
- package/dist/src/tools/normalizeGeneratedCode.d.ts +1 -0
- package/dist/src/tools/normalizeGeneratedCode.js +18 -0
- package/dist/src/tools/releaseCheck.d.ts +26 -0
- package/dist/src/tools/releaseCheck.js +367 -0
- package/dist/src/trace.d.ts +39 -0
- package/dist/src/trace.js +106 -0
- package/dist/src/translation.d.ts +6 -0
- package/dist/src/translation.js +22 -0
- package/dist/src/upstream.d.ts +20 -0
- package/dist/src/upstream.js +270 -0
- package/docs/configuration.md +55 -0
- package/docs/cursor-app.md +263 -0
- package/docs/implementation-inventory.json +609 -0
- package/docs/learnings.md +363 -0
- package/docs/model-fusion-protocol-origin.json +126 -0
- package/docs/model-fusion-protocol.md +110 -0
- package/docs/plugin-authoring.md +24 -0
- package/docs/proto-inventory.md +1477 -0
- package/docs/protocol-surface-audit.md +92 -0
- package/docs/protocol.md +52 -0
- package/docs/refreshing-protos.md +78 -0
- package/docs/release-gates.md +110 -0
- package/docs/release-summary.json +86 -0
- package/docs/route-contract-manifest.json +288 -0
- package/docs/route-policy.json +133 -0
- package/docs/service-manifest.json +9490 -0
- package/docs/test-manifest.json +155 -0
- package/docs/testing-harness.md +204 -0
- package/docs/troubleshooting.md +36 -0
- package/docs/type-manifest-summary.json +28927 -0
- package/package.json +93 -0
- package/proto/agent/v1/agent.proto +5371 -0
- package/proto/aiserver/v1/aiserver.proto +32944 -0
- package/proto/anyrun/v1/anyrun.proto +294 -0
- package/proto/google/protobuf/google.proto +37 -0
- package/proto/internapi/v1/internapi.proto +32 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function buildLocalNameAgentResponse(): Buffer;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { create, toBinary } from "@bufbuild/protobuf";
|
|
2
|
+
import { AgentV1_NameAgentResponseSchema } from "../gen/aiserver/v1/aiserver_pb.js";
|
|
3
|
+
export function buildLocalNameAgentResponse() {
|
|
4
|
+
return Buffer.from(toBinary(AgentV1_NameAgentResponseSchema, create(AgentV1_NameAgentResponseSchema, {
|
|
5
|
+
name: "Local model chat",
|
|
6
|
+
})));
|
|
7
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
import { type RequestContext, type AgentRunRequest } from "../gen/agent/v1/agent_pb.js";
|
|
3
|
+
import type { Logger } from "../logger.js";
|
|
4
|
+
import type { ModelRegistry, RegisteredModel } from "../models/registry.js";
|
|
5
|
+
import type { ChatMessage, OpenAIStreamOptions } from "../providers/openai.js";
|
|
6
|
+
export interface LocalAgentRunDecision {
|
|
7
|
+
model: RegisteredModel;
|
|
8
|
+
messages: ChatMessage[];
|
|
9
|
+
diagnostics: AgentRunDiagnostics;
|
|
10
|
+
}
|
|
11
|
+
export interface AgentRunDiagnostics {
|
|
12
|
+
modelId: string;
|
|
13
|
+
promptLength: number;
|
|
14
|
+
customSystemPromptLength: number;
|
|
15
|
+
selectedContext: {
|
|
16
|
+
extraContext: number;
|
|
17
|
+
extraContextEntries: number;
|
|
18
|
+
files: number;
|
|
19
|
+
codeSelections: number;
|
|
20
|
+
terminals: number;
|
|
21
|
+
terminalSelections: number;
|
|
22
|
+
folders: number;
|
|
23
|
+
externalLinks: number;
|
|
24
|
+
cursorRules: number;
|
|
25
|
+
cursorCommands: number;
|
|
26
|
+
documentations: number;
|
|
27
|
+
uiElements: number;
|
|
28
|
+
consoleLogs: number;
|
|
29
|
+
gitCommits: number;
|
|
30
|
+
pastChats: number;
|
|
31
|
+
pullRequests: number;
|
|
32
|
+
selectedSkills: number;
|
|
33
|
+
browsers: number;
|
|
34
|
+
documents: number;
|
|
35
|
+
};
|
|
36
|
+
promptContextNodes: number;
|
|
37
|
+
inlinePromptContextNodes: number;
|
|
38
|
+
inlinePromptContextCharacters: number;
|
|
39
|
+
mcpToolCount: number;
|
|
40
|
+
mcpToolNames: string[];
|
|
41
|
+
hasMcpFileSystemOptions: boolean;
|
|
42
|
+
hasSkillOptions: boolean;
|
|
43
|
+
excludeWorkspaceContext: boolean;
|
|
44
|
+
preFetchedBlobCount: number;
|
|
45
|
+
injectedContextMessages: number;
|
|
46
|
+
requestContext?: {
|
|
47
|
+
rules: number;
|
|
48
|
+
tools: number;
|
|
49
|
+
fileContents: number;
|
|
50
|
+
gitRepos: number;
|
|
51
|
+
projectLayouts: number;
|
|
52
|
+
agentSkills: number;
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
export declare function getLocalAgentRunDecision(payload: Buffer, models: ModelRegistry): LocalAgentRunDecision | undefined;
|
|
56
|
+
export declare function getLocalAgentRunDecisionFromClientMessage(payload: Uint8Array, models: ModelRegistry): LocalAgentRunDecision | undefined;
|
|
57
|
+
export declare function describeAgentRunPayload(payload: Uint8Array): string[];
|
|
58
|
+
export declare function withNativeRequestContext(decision: LocalAgentRunDecision, requestContext: RequestContext): LocalAgentRunDecision;
|
|
59
|
+
export declare function writeLocalAgentRunResponse(response: ServerResponse, decision: LocalAgentRunDecision, logger: Logger, options?: OpenAIStreamOptions): Promise<void>;
|
|
60
|
+
export declare function buildAgentRunDiagnostics(request: AgentRunRequest, modelId?: string): AgentRunDiagnostics;
|
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
2
|
+
import { encodeEndStream, encodeEnvelope } from "../connectEnvelope.js";
|
|
3
|
+
import { AgentClientMessageSchema, AgentRunRequestSchema, AgentServerMessageSchema, InteractionUpdateSchema, TextDeltaUpdateSchema, TurnEndedUpdateSchema, } from "../gen/agent/v1/agent_pb.js";
|
|
4
|
+
const MAX_CONTEXT_MESSAGE_CHARS = 120_000;
|
|
5
|
+
const MAX_CONTEXT_ITEM_CHARS = 20_000;
|
|
6
|
+
const MAX_TOOL_NAMES = 50;
|
|
7
|
+
export function getLocalAgentRunDecision(payload, models) {
|
|
8
|
+
const request = fromBinary(AgentRunRequestSchema, payload);
|
|
9
|
+
return getLocalAgentRunDecisionFromRequest(request, models);
|
|
10
|
+
}
|
|
11
|
+
export function getLocalAgentRunDecisionFromClientMessage(payload, models) {
|
|
12
|
+
const message = fromBinary(AgentClientMessageSchema, payload);
|
|
13
|
+
if (message.runRequest === undefined) {
|
|
14
|
+
return undefined;
|
|
15
|
+
}
|
|
16
|
+
return getLocalAgentRunDecisionFromRequest(message.runRequest, models);
|
|
17
|
+
}
|
|
18
|
+
export function describeAgentRunPayload(payload) {
|
|
19
|
+
const descriptions = [];
|
|
20
|
+
try {
|
|
21
|
+
const message = fromBinary(AgentClientMessageSchema, payload);
|
|
22
|
+
descriptions.push(`client:${describeAgentRunRequest(message.runRequest)}`);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
descriptions.push(`client:error:${errorMessage(error)}`);
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
descriptions.push(`run:${describeAgentRunRequest(fromBinary(AgentRunRequestSchema, payload))}`);
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
descriptions.push(`run:error:${errorMessage(error)}`);
|
|
32
|
+
}
|
|
33
|
+
return descriptions;
|
|
34
|
+
}
|
|
35
|
+
export function withNativeRequestContext(decision, requestContext) {
|
|
36
|
+
const contextText = nativeRequestContextToText(requestContext);
|
|
37
|
+
if (contextText === undefined) {
|
|
38
|
+
return decision;
|
|
39
|
+
}
|
|
40
|
+
const messages = [...decision.messages];
|
|
41
|
+
const finalUserMessage = messages.pop();
|
|
42
|
+
messages.push({
|
|
43
|
+
role: "system",
|
|
44
|
+
content: `Cursor native request context\n\n${contextText}`,
|
|
45
|
+
});
|
|
46
|
+
if (finalUserMessage !== undefined) {
|
|
47
|
+
messages.push(finalUserMessage);
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
...decision,
|
|
51
|
+
messages,
|
|
52
|
+
diagnostics: {
|
|
53
|
+
...decision.diagnostics,
|
|
54
|
+
requestContext: summarizeNativeRequestContext(requestContext),
|
|
55
|
+
injectedContextMessages: decision.diagnostics.injectedContextMessages + 1,
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
function getLocalAgentRunDecisionFromRequest(request, models) {
|
|
60
|
+
const modelId = request.requestedModel?.modelId || request.modelDetails?.modelId;
|
|
61
|
+
if (modelId === undefined || modelId.length === 0) {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
const model = models.get(modelId);
|
|
65
|
+
if (model === undefined) {
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
const userMessage = request.action?.userMessageAction?.userMessage;
|
|
69
|
+
const diagnostics = buildAgentRunDiagnostics(request, modelId);
|
|
70
|
+
const contextMessages = buildAgentContextMessages(request, userMessage);
|
|
71
|
+
diagnostics.injectedContextMessages = contextMessages.length;
|
|
72
|
+
return {
|
|
73
|
+
model,
|
|
74
|
+
messages: [
|
|
75
|
+
...contextMessages,
|
|
76
|
+
{
|
|
77
|
+
role: "user",
|
|
78
|
+
content: userMessage?.text ?? "",
|
|
79
|
+
},
|
|
80
|
+
],
|
|
81
|
+
diagnostics,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function describeAgentRunRequest(request) {
|
|
85
|
+
if (request === undefined) {
|
|
86
|
+
return "none";
|
|
87
|
+
}
|
|
88
|
+
const modelId = request.requestedModel?.modelId || request.modelDetails?.modelId || "none";
|
|
89
|
+
const promptLength = request.action?.userMessageAction?.userMessage?.text.length ?? 0;
|
|
90
|
+
const diagnostics = buildAgentRunDiagnostics(request, modelId);
|
|
91
|
+
return `model=${modelId},promptLength=${promptLength},contextMessages=${diagnostics.injectedContextMessages},mcpTools=${diagnostics.mcpToolCount},promptContextNodes=${diagnostics.promptContextNodes}`;
|
|
92
|
+
}
|
|
93
|
+
function errorMessage(error) {
|
|
94
|
+
return error instanceof Error ? error.message : String(error);
|
|
95
|
+
}
|
|
96
|
+
export async function writeLocalAgentRunResponse(response, decision, logger, options = {}) {
|
|
97
|
+
response.statusCode = 200;
|
|
98
|
+
if (!response.headersSent) {
|
|
99
|
+
response.setHeader("content-type", "application/connect+proto");
|
|
100
|
+
}
|
|
101
|
+
let outputCharacters = 0;
|
|
102
|
+
try {
|
|
103
|
+
for await (const text of decision.model.provider.streamCompletion(decision.messages, options)) {
|
|
104
|
+
outputCharacters += text.length;
|
|
105
|
+
response.write(encodeEnvelope(toBinary(AgentServerMessageSchema, create(AgentServerMessageSchema, {
|
|
106
|
+
interactionUpdate: create(InteractionUpdateSchema, {
|
|
107
|
+
textDelta: create(TextDeltaUpdateSchema, { text }),
|
|
108
|
+
}),
|
|
109
|
+
}))));
|
|
110
|
+
}
|
|
111
|
+
response.write(encodeEnvelope(toBinary(AgentServerMessageSchema, create(AgentServerMessageSchema, {
|
|
112
|
+
interactionUpdate: create(InteractionUpdateSchema, {
|
|
113
|
+
turnEnded: create(TurnEndedUpdateSchema, {
|
|
114
|
+
outputTokens: BigInt(Math.ceil(outputCharacters / 4)),
|
|
115
|
+
}),
|
|
116
|
+
}),
|
|
117
|
+
}))));
|
|
118
|
+
response.end(encodeEndStream());
|
|
119
|
+
logger.info("served local agent run", { model: decision.model.id });
|
|
120
|
+
logger.info("local agent run diagnostics", { ...decision.diagnostics });
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
logger.error("local agent run failed", {
|
|
124
|
+
model: decision.model.id,
|
|
125
|
+
error: error instanceof Error ? error.message : String(error),
|
|
126
|
+
});
|
|
127
|
+
endLocalAgentRunFailure(response);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function endLocalAgentRunFailure(response) {
|
|
131
|
+
if (!response.headersSent) {
|
|
132
|
+
response.statusCode = 200;
|
|
133
|
+
response.setHeader("content-type", "application/connect+proto");
|
|
134
|
+
}
|
|
135
|
+
response.end(encodeEndStream({ error: "local agent run failed" }));
|
|
136
|
+
}
|
|
137
|
+
export function buildAgentRunDiagnostics(request, modelId = request.requestedModel?.modelId ||
|
|
138
|
+
request.modelDetails?.modelId ||
|
|
139
|
+
"unknown") {
|
|
140
|
+
const userMessage = request.action?.userMessageAction?.userMessage;
|
|
141
|
+
const selectedContext = userMessage?.selectedContext;
|
|
142
|
+
const promptContextNodes = request.conversationState?.tokenDetails?.promptContextUsageTree?.nodes ??
|
|
143
|
+
[];
|
|
144
|
+
const inlinePromptContextNodes = promptContextNodes.filter((node) => node.inlineContent !== undefined && node.inlineContent.length > 0);
|
|
145
|
+
const mcpToolNames = request.mcpTools?.mcpTools
|
|
146
|
+
.map((tool) => tool.name || tool.toolName)
|
|
147
|
+
.filter((name) => name.length > 0)
|
|
148
|
+
.slice(0, MAX_TOOL_NAMES) ?? [];
|
|
149
|
+
return {
|
|
150
|
+
modelId,
|
|
151
|
+
promptLength: userMessage?.text.length ?? 0,
|
|
152
|
+
customSystemPromptLength: request.customSystemPrompt?.length ?? 0,
|
|
153
|
+
selectedContext: summarizeSelectedContext(selectedContext),
|
|
154
|
+
promptContextNodes: promptContextNodes.length,
|
|
155
|
+
inlinePromptContextNodes: inlinePromptContextNodes.length,
|
|
156
|
+
inlinePromptContextCharacters: inlinePromptContextNodes.reduce((sum, node) => sum + (node.inlineContent?.length ?? 0), 0),
|
|
157
|
+
mcpToolCount: request.mcpTools?.mcpTools.length ?? 0,
|
|
158
|
+
mcpToolNames,
|
|
159
|
+
hasMcpFileSystemOptions: request.mcpFileSystemOptions !== undefined,
|
|
160
|
+
hasSkillOptions: request.skillOptions !== undefined,
|
|
161
|
+
excludeWorkspaceContext: request.excludeWorkspaceContext === true,
|
|
162
|
+
preFetchedBlobCount: request.preFetchedBlobs.length,
|
|
163
|
+
injectedContextMessages: 0,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function buildAgentContextMessages(request, userMessage) {
|
|
167
|
+
const messages = [];
|
|
168
|
+
pushContextMessage(messages, "Cursor custom system prompt", request.customSystemPrompt);
|
|
169
|
+
pushContextMessage(messages, "Cursor subagent system reminder", userMessage?.subagentSystemReminder);
|
|
170
|
+
for (const hook of userMessage?.hookAdditionalContexts ?? []) {
|
|
171
|
+
pushContextMessage(messages, `Cursor hook context: ${hook.hookEventName}`, hook.content);
|
|
172
|
+
}
|
|
173
|
+
const selectedContextText = selectedContextToText(userMessage?.selectedContext);
|
|
174
|
+
pushContextMessage(messages, "Cursor selected context", selectedContextText);
|
|
175
|
+
const promptContextText = promptContextToText(request);
|
|
176
|
+
pushContextMessage(messages, "Cursor prompt context", promptContextText);
|
|
177
|
+
const toolText = mcpToolsToText(request);
|
|
178
|
+
pushContextMessage(messages, "Cursor available MCP tools", toolText);
|
|
179
|
+
return messages;
|
|
180
|
+
}
|
|
181
|
+
function summarizeSelectedContext(context) {
|
|
182
|
+
return {
|
|
183
|
+
extraContext: context?.extraContext.length ?? 0,
|
|
184
|
+
extraContextEntries: context?.extraContextEntries.length ?? 0,
|
|
185
|
+
files: context?.files.length ?? 0,
|
|
186
|
+
codeSelections: context?.codeSelections.length ?? 0,
|
|
187
|
+
terminals: context?.terminals.length ?? 0,
|
|
188
|
+
terminalSelections: context?.terminalSelections.length ?? 0,
|
|
189
|
+
folders: context?.folders.length ?? 0,
|
|
190
|
+
externalLinks: context?.externalLinks.length ?? 0,
|
|
191
|
+
cursorRules: context?.cursorRules.length ?? 0,
|
|
192
|
+
cursorCommands: context?.cursorCommands.length ?? 0,
|
|
193
|
+
documentations: context?.documentations.length ?? 0,
|
|
194
|
+
uiElements: context?.uiElements.length ?? 0,
|
|
195
|
+
consoleLogs: context?.consoleLogs.length ?? 0,
|
|
196
|
+
gitCommits: context?.gitCommits.length ?? 0,
|
|
197
|
+
pastChats: context?.pastChats.length ?? 0,
|
|
198
|
+
pullRequests: context?.selectedPullRequests.length ?? 0,
|
|
199
|
+
selectedSkills: context?.selectedSkills.length ?? 0,
|
|
200
|
+
browsers: context?.selectedBrowsers.length ?? 0,
|
|
201
|
+
documents: context?.selectedDocuments.length ?? 0,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
function selectedContextToText(context) {
|
|
205
|
+
if (context === undefined) {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
const sections = [];
|
|
209
|
+
for (const item of context.extraContext) {
|
|
210
|
+
appendSection(sections, "Extra context", item);
|
|
211
|
+
}
|
|
212
|
+
for (const item of context.extraContextEntries) {
|
|
213
|
+
appendSection(sections, "Extra context entry", item.data);
|
|
214
|
+
}
|
|
215
|
+
for (const file of context.files) {
|
|
216
|
+
appendSection(sections, `Selected file: ${file.relativePath ?? file.path}`, file.content);
|
|
217
|
+
}
|
|
218
|
+
for (const selection of context.codeSelections) {
|
|
219
|
+
appendSection(sections, `Selected code: ${selection.relativePath ?? selection.path}`, selection.content);
|
|
220
|
+
}
|
|
221
|
+
for (const terminal of context.terminals) {
|
|
222
|
+
appendSection(sections, `Selected terminal: ${terminal.title ?? terminal.path ?? "terminal"}`, terminal.content);
|
|
223
|
+
}
|
|
224
|
+
for (const selection of context.terminalSelections) {
|
|
225
|
+
appendSection(sections, `Selected terminal selection: ${selection.title ?? "terminal"}`, selection.content);
|
|
226
|
+
}
|
|
227
|
+
for (const rule of context.cursorRules) {
|
|
228
|
+
appendSection(sections, `Cursor rule: ${rule.rule?.fullPath ?? "unknown"}`, rule.rule?.content ?? "");
|
|
229
|
+
}
|
|
230
|
+
for (const command of context.cursorCommands) {
|
|
231
|
+
appendSection(sections, `Cursor command: ${command.name}`, command.content);
|
|
232
|
+
}
|
|
233
|
+
for (const uiElement of context.uiElements) {
|
|
234
|
+
appendSection(sections, `Selected UI element: ${uiElement.component ?? uiElement.element}`, [uiElement.textContent, uiElement.extra].filter(Boolean).join("\n"));
|
|
235
|
+
}
|
|
236
|
+
for (const log of context.consoleLogs) {
|
|
237
|
+
appendSection(sections, `Selected console log: ${log.level}`, [log.message, log.objectDataJson].filter(Boolean).join("\n"));
|
|
238
|
+
}
|
|
239
|
+
for (const commit of context.gitCommits) {
|
|
240
|
+
appendSection(sections, `Selected git commit: ${commit.sha}`, [commit.message, commit.description, commit.diff]
|
|
241
|
+
.filter((value) => value !== undefined && value.length > 0)
|
|
242
|
+
.join("\n"));
|
|
243
|
+
}
|
|
244
|
+
for (const pr of context.selectedPullRequests) {
|
|
245
|
+
appendSection(sections, `Selected pull request: ${pr.url}`, [pr.title, pr.description, pr.summaryJson]
|
|
246
|
+
.filter((value) => value !== undefined && value.length > 0)
|
|
247
|
+
.join("\n"));
|
|
248
|
+
}
|
|
249
|
+
for (const skill of context.selectedSkills) {
|
|
250
|
+
appendSection(sections, `Selected skill: ${skill.fullPath}`, [skill.description, skill.content].filter(Boolean).join("\n"));
|
|
251
|
+
}
|
|
252
|
+
for (const browser of context.selectedBrowsers) {
|
|
253
|
+
appendSection(sections, `Selected browser: ${browser.pageTitle ?? browser.url}`, browser.url);
|
|
254
|
+
}
|
|
255
|
+
return joinBounded(sections, MAX_CONTEXT_MESSAGE_CHARS);
|
|
256
|
+
}
|
|
257
|
+
function promptContextToText(request) {
|
|
258
|
+
const nodes = request.conversationState?.tokenDetails?.promptContextUsageTree?.nodes ??
|
|
259
|
+
[];
|
|
260
|
+
const sections = [];
|
|
261
|
+
for (const node of nodes) {
|
|
262
|
+
if (node.inlineContent === undefined || node.inlineContent.length === 0) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
appendSection(sections, `Prompt context node: ${node.label || node.kind || node.id}`, node.inlineContent);
|
|
266
|
+
}
|
|
267
|
+
return joinBounded(sections, MAX_CONTEXT_MESSAGE_CHARS);
|
|
268
|
+
}
|
|
269
|
+
function mcpToolsToText(request) {
|
|
270
|
+
const tools = request.mcpTools?.mcpTools ?? [];
|
|
271
|
+
if (tools.length === 0) {
|
|
272
|
+
return undefined;
|
|
273
|
+
}
|
|
274
|
+
return tools
|
|
275
|
+
.slice(0, MAX_TOOL_NAMES)
|
|
276
|
+
.map((tool) => [
|
|
277
|
+
`- ${tool.name || tool.toolName}`,
|
|
278
|
+
tool.description.length > 0 ? `: ${tool.description}` : "",
|
|
279
|
+
tool.providerIdentifier.length > 0
|
|
280
|
+
? ` (${tool.providerIdentifier})`
|
|
281
|
+
: "",
|
|
282
|
+
].join(""))
|
|
283
|
+
.join("\n");
|
|
284
|
+
}
|
|
285
|
+
function nativeRequestContextToText(requestContext) {
|
|
286
|
+
const sections = [];
|
|
287
|
+
if (requestContext.env !== undefined) {
|
|
288
|
+
appendSection(sections, "Environment", JSON.stringify(summarizeJson(requestContext.env), null, 2));
|
|
289
|
+
}
|
|
290
|
+
if (requestContext.gitRepos.length > 0) {
|
|
291
|
+
appendSection(sections, "Git repositories", JSON.stringify(summarizeJson(requestContext.gitRepos), null, 2));
|
|
292
|
+
}
|
|
293
|
+
if (requestContext.repositoryInfo.length > 0) {
|
|
294
|
+
appendSection(sections, "Repository indexing info", JSON.stringify(summarizeJson(requestContext.repositoryInfo), null, 2));
|
|
295
|
+
}
|
|
296
|
+
if (requestContext.projectLayouts.length > 0) {
|
|
297
|
+
appendSection(sections, "Project layouts", JSON.stringify(summarizeJson(requestContext.projectLayouts), null, 2));
|
|
298
|
+
}
|
|
299
|
+
for (const [filePath, content] of Object.entries(requestContext.fileContents)) {
|
|
300
|
+
appendSection(sections, `File content: ${filePath}`, content);
|
|
301
|
+
}
|
|
302
|
+
for (const rule of requestContext.rules) {
|
|
303
|
+
appendSection(sections, `Cursor rule: ${rule.fullPath || "unknown"}`, rule.content || JSON.stringify(summarizeJson(rule), null, 2));
|
|
304
|
+
}
|
|
305
|
+
for (const rule of requestContext.nonFileRules) {
|
|
306
|
+
appendSection(sections, `Cursor non-file rule: ${rule.fullPath || "unknown"}`, rule.content || JSON.stringify(summarizeJson(rule), null, 2));
|
|
307
|
+
}
|
|
308
|
+
if (requestContext.tools.length > 0) {
|
|
309
|
+
appendSection(sections, "Available Cursor tools", requestContext.tools
|
|
310
|
+
.slice(0, MAX_TOOL_NAMES)
|
|
311
|
+
.map((tool) => [
|
|
312
|
+
`- ${tool.name || tool.toolName}`,
|
|
313
|
+
tool.description.length > 0 ? `: ${tool.description}` : "",
|
|
314
|
+
tool.providerIdentifier.length > 0
|
|
315
|
+
? ` (${tool.providerIdentifier})`
|
|
316
|
+
: "",
|
|
317
|
+
].join(""))
|
|
318
|
+
.join("\n"));
|
|
319
|
+
}
|
|
320
|
+
if (requestContext.mcpInstructions.length > 0) {
|
|
321
|
+
appendSection(sections, "MCP instructions", JSON.stringify(summarizeJson(requestContext.mcpInstructions), null, 2));
|
|
322
|
+
}
|
|
323
|
+
if (requestContext.agentSkills.length > 0) {
|
|
324
|
+
appendSection(sections, "Agent skills", JSON.stringify(summarizeJson(requestContext.agentSkills), null, 2));
|
|
325
|
+
}
|
|
326
|
+
pushOptionalSection(sections, "Conversation notes", requestContext.conversationNotesListing);
|
|
327
|
+
pushOptionalSection(sections, "Shared notes", requestContext.sharedNotesListing);
|
|
328
|
+
pushOptionalSection(sections, "Hooks additional context", requestContext.hooksAdditionalContext);
|
|
329
|
+
pushOptionalSection(sections, "Cloud rule", requestContext.cloudRule);
|
|
330
|
+
return joinBounded(sections, MAX_CONTEXT_MESSAGE_CHARS);
|
|
331
|
+
}
|
|
332
|
+
function summarizeNativeRequestContext(requestContext) {
|
|
333
|
+
return {
|
|
334
|
+
rules: requestContext.rules.length + requestContext.nonFileRules.length,
|
|
335
|
+
tools: requestContext.tools.length,
|
|
336
|
+
fileContents: Object.keys(requestContext.fileContents).length,
|
|
337
|
+
gitRepos: requestContext.gitRepos.length,
|
|
338
|
+
projectLayouts: requestContext.projectLayouts.length,
|
|
339
|
+
agentSkills: requestContext.agentSkills.length,
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
function pushOptionalSection(sections, label, content) {
|
|
343
|
+
if (content !== undefined && content.trim().length > 0) {
|
|
344
|
+
appendSection(sections, label, content);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
function summarizeJson(value) {
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
return value.slice(0, 20).map((item) => summarizeJson(item));
|
|
350
|
+
}
|
|
351
|
+
if (value === null || typeof value !== "object") {
|
|
352
|
+
return typeof value === "string" ? value.slice(0, 1_000) : value;
|
|
353
|
+
}
|
|
354
|
+
const result = {};
|
|
355
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
356
|
+
if (/token|key|authorization|cookie|secret/i.test(key)) {
|
|
357
|
+
result[key] = "<redacted>";
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
result[key] = summarizeJson(entry);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return result;
|
|
364
|
+
}
|
|
365
|
+
function pushContextMessage(messages, label, content) {
|
|
366
|
+
if (content === undefined || content.trim().length === 0) {
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
messages.push({
|
|
370
|
+
role: "system",
|
|
371
|
+
content: `${label}\n\n${content}`,
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
function appendSection(sections, label, content) {
|
|
375
|
+
if (content.trim().length === 0) {
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
378
|
+
sections.push(`## ${label}\n${truncate(content, MAX_CONTEXT_ITEM_CHARS)}`);
|
|
379
|
+
}
|
|
380
|
+
function joinBounded(sections, maxCharacters) {
|
|
381
|
+
if (sections.length === 0) {
|
|
382
|
+
return undefined;
|
|
383
|
+
}
|
|
384
|
+
return truncate(sections.join("\n\n"), maxCharacters);
|
|
385
|
+
}
|
|
386
|
+
function truncate(value, maxCharacters) {
|
|
387
|
+
if (value.length <= maxCharacters) {
|
|
388
|
+
return value;
|
|
389
|
+
}
|
|
390
|
+
return `${value.slice(0, maxCharacters)}\n[truncated]`;
|
|
391
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
import { type StreamUnifiedChatRequestWithTools } from "../gen/aiserver/v1/aiserver_pb.js";
|
|
3
|
+
import type { Logger } from "../logger.js";
|
|
4
|
+
import type { ModelRegistry, RegisteredModel } from "../models/registry.js";
|
|
5
|
+
import type { OpenAIStreamOptions } from "../providers/openai.js";
|
|
6
|
+
export interface LocalChatDecision {
|
|
7
|
+
model: RegisteredModel;
|
|
8
|
+
request: StreamUnifiedChatRequestWithTools;
|
|
9
|
+
}
|
|
10
|
+
export declare function getLocalChatDecision(payload: Buffer, models: ModelRegistry): LocalChatDecision | undefined;
|
|
11
|
+
export declare function writeLocalChatResponse(response: ServerResponse, decision: LocalChatDecision, logger: Logger, options?: OpenAIStreamOptions): Promise<void>;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { create, fromBinary, toBinary } from "@bufbuild/protobuf";
|
|
3
|
+
import { encodeEndStream, encodeEnvelope } from "../connectEnvelope.js";
|
|
4
|
+
import { StreamUnifiedChatRequestWithToolsSchema, StreamUnifiedChatResponseSchema, StreamUnifiedChatResponseWithToolsSchema, } from "../gen/aiserver/v1/aiserver_pb.js";
|
|
5
|
+
import { cursorRequestToOpenAI } from "../translation.js";
|
|
6
|
+
export function getLocalChatDecision(payload, models) {
|
|
7
|
+
const request = fromBinary(StreamUnifiedChatRequestWithToolsSchema, payload);
|
|
8
|
+
const modelName = request.streamUnifiedChatRequest?.modelDetails?.modelName;
|
|
9
|
+
const model = models.get(modelName);
|
|
10
|
+
if (model === undefined) {
|
|
11
|
+
return undefined;
|
|
12
|
+
}
|
|
13
|
+
return { model, request };
|
|
14
|
+
}
|
|
15
|
+
export async function writeLocalChatResponse(response, decision, logger, options = {}) {
|
|
16
|
+
response.statusCode = 200;
|
|
17
|
+
response.setHeader("content-type", "application/connect+proto");
|
|
18
|
+
const serverBubbleId = randomUUID();
|
|
19
|
+
const messages = cursorRequestToOpenAI(decision.request);
|
|
20
|
+
try {
|
|
21
|
+
for await (const text of decision.model.provider.streamCompletion(messages, options)) {
|
|
22
|
+
response.write(encodeEnvelope(toBinary(StreamUnifiedChatResponseWithToolsSchema, create(StreamUnifiedChatResponseWithToolsSchema, {
|
|
23
|
+
streamUnifiedChatResponse: create(StreamUnifiedChatResponseSchema, {
|
|
24
|
+
text,
|
|
25
|
+
serverBubbleId,
|
|
26
|
+
}),
|
|
27
|
+
eventId: randomUUID(),
|
|
28
|
+
}))));
|
|
29
|
+
}
|
|
30
|
+
response.end(encodeEndStream());
|
|
31
|
+
logger.info("served local chat", { model: decision.model.id });
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
logger.error("local chat failed", {
|
|
35
|
+
model: decision.model.id,
|
|
36
|
+
error: error instanceof Error ? error.message : String(error),
|
|
37
|
+
});
|
|
38
|
+
endLocalChatFailure(response);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function endLocalChatFailure(response) {
|
|
42
|
+
if (!response.headersSent) {
|
|
43
|
+
response.statusCode = 200;
|
|
44
|
+
response.setHeader("content-type", "application/connect+proto");
|
|
45
|
+
}
|
|
46
|
+
response.end(encodeEndStream({ error: "local model failed" }));
|
|
47
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ServerResponse } from "node:http";
|
|
2
|
+
import type { Logger } from "../logger.js";
|
|
3
|
+
import type { ModelRegistry } from "../models/registry.js";
|
|
4
|
+
export type ModelResponseFormat = "connect" | "proto";
|
|
5
|
+
export declare function mergeAvailableModels(upstreamPayload: Buffer | undefined, models: ModelRegistry): Buffer;
|
|
6
|
+
export declare function mergeUsableModels(upstreamPayload: Buffer | undefined, models: ModelRegistry): Buffer;
|
|
7
|
+
export declare function mergeDefaultModelForCli(upstreamPayload: Buffer | undefined, models: ModelRegistry): Buffer;
|
|
8
|
+
export declare function mergeDefaultModel(upstreamPayload: Buffer | undefined, models: ModelRegistry): Buffer;
|
|
9
|
+
export declare function writeAvailableModelsResponse(response: ServerResponse, payload: Buffer, logger: Logger, format: ModelResponseFormat): void;
|
|
10
|
+
export declare function writeModelResponse(response: ServerResponse, payload: Buffer, logger: Logger, message: string, format: ModelResponseFormat): void;
|