Improve cursor logging
This commit is contained in:
+59
-52
@@ -83,58 +83,6 @@ type CursorEvent =
|
|||||||
| CursorToolCallEvent
|
| CursorToolCallEvent
|
||||||
| CursorResultEvent;
|
| CursorResultEvent;
|
||||||
|
|
||||||
const messageHandlers = {
|
|
||||||
system: (_event: CursorSystemEvent) => {
|
|
||||||
// system init events - no logging needed
|
|
||||||
},
|
|
||||||
user: (_event: CursorUserEvent) => {
|
|
||||||
// user messages already logged in prompt box
|
|
||||||
},
|
|
||||||
thinking: (_event: CursorThinkingEvent) => {
|
|
||||||
// thinking events are internal - no logging needed
|
|
||||||
},
|
|
||||||
assistant: (event: CursorAssistantEvent) => {
|
|
||||||
// only log finalized messages (ones with model_call_id)
|
|
||||||
// cursor emits each message twice: once without model_call_id, then again with it
|
|
||||||
if (event.model_call_id) {
|
|
||||||
const text = event.message?.content?.[0]?.text;
|
|
||||||
if (text?.trim()) {
|
|
||||||
log.box(text.trim(), { title: "Cursor" });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tool_call: (event: CursorToolCallEvent) => {
|
|
||||||
if (event.subtype === "started") {
|
|
||||||
// handle both MCP tools and built-in tools (bash, WebFetch, etc)
|
|
||||||
const mcpToolCall = event.tool_call?.mcpToolCall;
|
|
||||||
const builtinToolCall = (event.tool_call as any)?.builtinToolCall;
|
|
||||||
|
|
||||||
if (mcpToolCall?.args?.toolName && mcpToolCall?.args?.args) {
|
|
||||||
log.toolCall({
|
|
||||||
toolName: mcpToolCall.args.toolName,
|
|
||||||
input: mcpToolCall.args.args,
|
|
||||||
});
|
|
||||||
} else if (builtinToolCall?.args?.name && builtinToolCall?.args?.args) {
|
|
||||||
log.toolCall({
|
|
||||||
toolName: builtinToolCall.args.name,
|
|
||||||
input: builtinToolCall.args.args,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if (event.subtype === "completed") {
|
|
||||||
const isError = event.tool_call?.mcpToolCall?.result?.success?.isError;
|
|
||||||
if (isError) {
|
|
||||||
log.warning("Tool call failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
result: async (event: CursorResultEvent) => {
|
|
||||||
if (event.subtype === "success" && event.duration_ms) {
|
|
||||||
const durationSec = (event.duration_ms / 1000).toFixed(1);
|
|
||||||
log.debug(`Cursor completed in ${durationSec}s`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const cursor = agent({
|
export const cursor = agent({
|
||||||
name: "cursor",
|
name: "cursor",
|
||||||
install: async () => {
|
install: async () => {
|
||||||
@@ -146,6 +94,60 @@ export const cursor = agent({
|
|||||||
run: async ({ payload, apiKey, cliPath, mcpServers }) => {
|
run: async ({ payload, apiKey, cliPath, mcpServers }) => {
|
||||||
configureCursorMcpServers({ mcpServers, cliPath });
|
configureCursorMcpServers({ mcpServers, cliPath });
|
||||||
|
|
||||||
|
// track logged messages to avoid duplicates
|
||||||
|
const loggedAssistantMessages = new Set<string>();
|
||||||
|
|
||||||
|
// moved into `run` because it is stateful
|
||||||
|
// it tracks logged assistant messages to avoid duplicates
|
||||||
|
const messageHandlers = {
|
||||||
|
system: (_event: CursorSystemEvent) => {
|
||||||
|
// system init events - no logging needed
|
||||||
|
},
|
||||||
|
user: (_event: CursorUserEvent) => {
|
||||||
|
// user messages already logged in prompt box
|
||||||
|
},
|
||||||
|
thinking: (_event: CursorThinkingEvent) => {
|
||||||
|
// thinking events are internal - no logging needed
|
||||||
|
},
|
||||||
|
assistant: (event: CursorAssistantEvent) => {
|
||||||
|
const text = event.message?.content?.[0]?.text?.trim();
|
||||||
|
if (text && !loggedAssistantMessages.has(text)) {
|
||||||
|
loggedAssistantMessages.add(text);
|
||||||
|
log.box(text, { title: "Cursor" });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tool_call: (event: CursorToolCallEvent) => {
|
||||||
|
if (event.subtype === "started") {
|
||||||
|
// handle both MCP tools and built-in tools (bash, WebFetch, etc)
|
||||||
|
const mcpToolCall = event.tool_call?.mcpToolCall;
|
||||||
|
const builtinToolCall = (event.tool_call as any)?.builtinToolCall;
|
||||||
|
|
||||||
|
if (mcpToolCall?.args?.toolName && mcpToolCall?.args?.args) {
|
||||||
|
log.toolCall({
|
||||||
|
toolName: mcpToolCall.args.toolName,
|
||||||
|
input: mcpToolCall.args.args,
|
||||||
|
});
|
||||||
|
} else if (builtinToolCall?.args?.name && builtinToolCall?.args?.args) {
|
||||||
|
log.toolCall({
|
||||||
|
toolName: builtinToolCall.args.name,
|
||||||
|
input: builtinToolCall.args.args,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (event.subtype === "completed") {
|
||||||
|
const isError = event.tool_call?.mcpToolCall?.result?.success?.isError;
|
||||||
|
if (isError) {
|
||||||
|
log.warning("Tool call failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
result: async (event: CursorResultEvent) => {
|
||||||
|
if (event.subtype === "success" && event.duration_ms) {
|
||||||
|
const durationSec = (event.duration_ms / 1000).toFixed(1);
|
||||||
|
log.debug(`Cursor completed in ${durationSec}s`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const fullPrompt = addInstructions(payload);
|
const fullPrompt = addInstructions(payload);
|
||||||
|
|
||||||
@@ -188,6 +190,11 @@ export const cursor = agent({
|
|||||||
try {
|
try {
|
||||||
const event = JSON.parse(text) as CursorEvent;
|
const event = JSON.parse(text) as CursorEvent;
|
||||||
|
|
||||||
|
// skip debug logging for empty thinking deltas
|
||||||
|
if (event.type === "thinking" && event.subtype === "delta" && !event.text) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// route to appropriate handler
|
// route to appropriate handler
|
||||||
const handler = messageHandlers[event.type as keyof typeof messageHandlers];
|
const handler = messageHandlers[event.type as keyof typeof messageHandlers];
|
||||||
if (handler) {
|
if (handler) {
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ Before starting any work, you must first determine which mode to use by examinin
|
|||||||
|
|
||||||
Available modes:
|
Available modes:
|
||||||
|
|
||||||
${[...modes, ...payload.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")}
|
${(payload.modes.length > 0 ? payload.modes : modes).map((w) => ` - "${w.name}": ${w.description}`).join("\n")}
|
||||||
|
|
||||||
**IMPORTANT**: The first thing you must do is:
|
**IMPORTANT**: The first thing you must do is:
|
||||||
1. Examine the user's request/prompt carefully
|
1. Examine the user's request/prompt carefully
|
||||||
|
|||||||
@@ -135,6 +135,10 @@ export type PayloadEvent =
|
|||||||
| {
|
| {
|
||||||
trigger: "workflow_dispatch";
|
trigger: "workflow_dispatch";
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
trigger: "unknown";
|
||||||
|
[key: string]: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
// payload type for agent execution
|
// payload type for agent execution
|
||||||
|
|||||||
+1
-1
@@ -1 +1 @@
|
|||||||
summarize https://github.com/pullfrogai/scratch/issues/56, its status, and pertinent discussion on the issue
|
Tell me a joke
|
||||||
Reference in New Issue
Block a user