@rama_nigg/open-cursor 2.3.3 → 2.3.4

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.
@@ -12,6 +12,13 @@ export interface OpenAiToolCall {
12
12
  };
13
13
  }
14
14
 
15
+ export interface ToolCallExtractionResult {
16
+ action: "intercept" | "passthrough" | "skip";
17
+ toolCall?: OpenAiToolCall;
18
+ passthroughName?: string;
19
+ skipReason?: string;
20
+ }
21
+
15
22
  export interface ToolLoopMeta {
16
23
  id: string;
17
24
  created: number;
@@ -106,48 +113,55 @@ export function extractAllowedToolNames(tools: Array<any>): Set<string> {
106
113
  export function extractOpenAiToolCall(
107
114
  event: StreamJsonToolCallEvent,
108
115
  allowedToolNames: Set<string>,
109
- ): OpenAiToolCall | null {
116
+ ): ToolCallExtractionResult {
110
117
  if (allowedToolNames.size === 0) {
111
- return null;
118
+ return { action: "skip", skipReason: "no_allowed_tools" };
112
119
  }
113
120
 
114
121
  const { name, args, skipped } = extractToolNameAndArgs(event);
115
122
  if (skipped) {
116
- return null;
123
+ return { action: "skip", skipReason: "event_skipped" };
117
124
  }
118
125
  if (!name) {
119
- return null;
126
+ return { action: "skip", skipReason: "no_name" };
120
127
  }
121
128
 
122
129
  const resolvedName = resolveAllowedToolName(name, allowedToolNames);
123
- if (!resolvedName) {
124
- log.debug("Tool call name not allowed; skipping interception", {
125
- name,
126
- normalized: normalizeAliasKey(name),
127
- allowedToolCount: allowedToolNames.size,
128
- aliasTarget: TOOL_NAME_ALIASES.get(normalizeAliasKey(name)) ?? null,
129
- });
130
- return null;
131
- }
130
+ if (resolvedName) {
131
+ // Known tool intercept and forward to OpenCode
132
+ if (args === undefined && event.subtype === "started") {
133
+ log.debug("Tool call args extraction returned undefined", {
134
+ toolName: name,
135
+ subtype: event.subtype ?? "none",
136
+ payloadKeys: Object.entries(event.tool_call || {}).map(([k, v]) =>
137
+ `${k}:[${isRecord(v) ? Object.keys(v).join(",") : typeof v}]`),
138
+ hasCallId: Boolean(event.call_id),
139
+ });
140
+ }
132
141
 
133
- if (args === undefined && event.subtype === "started") {
134
- log.debug("Tool call args extraction returned undefined", {
135
- toolName: name,
136
- subtype: event.subtype ?? "none",
137
- payloadKeys: Object.entries(event.tool_call || {}).map(([k, v]) =>
138
- `${k}:[${isRecord(v) ? Object.keys(v).join(",") : typeof v}]`),
139
- hasCallId: Boolean(event.call_id),
140
- });
142
+ const callId = event.call_id || (event as any).tool_call_id || "call_unknown";
143
+ return {
144
+ action: "intercept",
145
+ toolCall: {
146
+ id: callId,
147
+ type: "function",
148
+ function: {
149
+ name: resolvedName,
150
+ arguments: toOpenAiArguments(args),
151
+ },
152
+ },
153
+ };
141
154
  }
142
155
 
143
- const callId = event.call_id || (event as any).tool_call_id || "call_unknown";
156
+ // Unknown tool pass through to cursor-agent
157
+ log.debug("Tool call not in allowlist; passing through to cursor-agent", {
158
+ name,
159
+ normalized: normalizeAliasKey(name),
160
+ allowedToolCount: allowedToolNames.size,
161
+ });
144
162
  return {
145
- id: callId,
146
- type: "function",
147
- function: {
148
- name: resolvedName,
149
- arguments: toOpenAiArguments(args),
150
- },
163
+ action: "passthrough",
164
+ passthroughName: name,
151
165
  };
152
166
  }
153
167
 
@@ -0,0 +1,81 @@
1
+ /**
2
+ * ToastService - OpenCode toast notification integration
3
+ *
4
+ * Provides toast notifications for MCP tool pass-through visibility.
5
+ * Gracefully degrades when client.tui.showToast is unavailable.
6
+ */
7
+
8
+ import { createLogger } from "../utils/logger.js";
9
+
10
+ const log = createLogger("services:toast");
11
+
12
+ export interface ToastOptions {
13
+ title?: string;
14
+ message: string;
15
+ variant: "info" | "success" | "warning" | "error";
16
+ }
17
+
18
+ export interface OpenCodeTuiClient {
19
+ showToast: (options: { body: { title?: string; message: string; variant: string } }) => Promise<void>;
20
+ }
21
+
22
+ export interface OpenCodeClientWithTui {
23
+ tui?: OpenCodeTuiClient;
24
+ }
25
+
26
+ export class ToastService {
27
+ private client: OpenCodeClientWithTui | null = null;
28
+
29
+ setClient(client: OpenCodeClientWithTui): void {
30
+ this.client = client;
31
+ }
32
+
33
+ async show(options: ToastOptions): Promise<void> {
34
+ if (!this.client?.tui?.showToast) {
35
+ log.debug("Toast not available; client.tui.showToast missing", { message: options.message });
36
+ return;
37
+ }
38
+
39
+ try {
40
+ await this.client.tui.showToast({
41
+ body: {
42
+ title: options.title,
43
+ message: options.message,
44
+ variant: options.variant,
45
+ },
46
+ });
47
+ } catch (error) {
48
+ log.debug("Toast failed", { error, message: options.message });
49
+ }
50
+ }
51
+
52
+ async showPassThroughSummary(tools: string[]): Promise<void> {
53
+ if (tools.length === 0) return;
54
+
55
+ const toolList = tools.length <= 3
56
+ ? tools.join(", ")
57
+ : `${tools.slice(0, 3).join(", ")} +${tools.length - 3} more`;
58
+
59
+ await this.show({
60
+ title: "MCP Tools",
61
+ message: `🎭 ${tools.length} tool${tools.length > 1 ? "s" : ""} handled by cursor-agent: ${toolList}`,
62
+ variant: "info",
63
+ });
64
+ }
65
+
66
+ async showErrorSummary(errors: string[]): Promise<void> {
67
+ if (errors.length === 0) return;
68
+
69
+ const errorList = errors.length <= 2
70
+ ? errors.join("; ")
71
+ : `${errors.slice(0, 2).join("; ")} +${errors.length - 2} more`;
72
+
73
+ await this.show({
74
+ title: "MCP Errors",
75
+ message: `⚠️ ${errors.length} MCP tool${errors.length > 1 ? "s" : ""} failed: ${errorList}`,
76
+ variant: "warning",
77
+ });
78
+ }
79
+ }
80
+
81
+ export const toastService = new ToastService();