@waniwani/sdk 0.11.11 → 0.11.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.
- package/README.md +92 -93
- package/dist/chat/embed.js +51 -51
- package/dist/chat/embed.js.map +1 -1
- package/dist/chat/express-js/index.d.ts +8 -1
- package/dist/chat/express-js/index.js.map +1 -1
- package/dist/chat/index.d.ts +1 -22
- package/dist/chat/index.js +7 -7
- package/dist/chat/index.js.map +1 -1
- package/dist/chat/next-js/index.d.ts +5 -3
- package/dist/chat/next-js/index.js.map +1 -1
- package/dist/chat/server/index.js +1 -1
- package/dist/chat/server/index.js.map +1 -1
- package/dist/chat/styles.css +1 -1
- package/dist/{chunk-DGSC74SV.js → chunk-DP6SAQTK.js} +1 -1
- package/dist/chunk-DP6SAQTK.js.map +1 -0
- package/dist/{chunk-5OQXAEHG.js → chunk-RZKVTH7F.js} +1 -1
- package/dist/chunk-RZKVTH7F.js.map +1 -0
- package/dist/legacy/chat/express-js/index.d.ts +407 -0
- package/dist/legacy/chat/express-js/index.js +10 -0
- package/dist/legacy/chat/express-js/index.js.map +1 -0
- package/dist/legacy/chat/next-js/index.d.ts +368 -0
- package/dist/legacy/chat/next-js/index.js +10 -0
- package/dist/legacy/chat/next-js/index.js.map +1 -0
- package/dist/legacy/index.d.ts +335 -0
- package/dist/legacy/index.js +2 -0
- package/dist/legacy/index.js.map +1 -0
- package/dist/legacy/mcp/react.d.ts +573 -0
- package/dist/legacy/mcp/react.js +68 -0
- package/dist/legacy/mcp/react.js.map +1 -0
- package/dist/mcp/index.d.ts +1153 -1117
- package/dist/mcp/index.js +5 -5
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/react.d.ts +170 -74
- package/dist/mcp/react.js +7 -7
- package/dist/mcp/react.js.map +1 -1
- package/dist/{mcp-apps-client-PUL4H54S.js → mcp-apps-client-OFYMQOI3.js} +1 -1
- package/dist/mcp-apps-client-OFYMQOI3.js.map +1 -0
- package/dist/{openai-client-QAC3ZD5W.js → openai-client-TZIOCMXP.js} +2 -2
- package/dist/openai-client-TZIOCMXP.js.map +1 -0
- package/dist/platform-LKQFC3AJ.js +3 -0
- package/package.json +34 -21
- package/dist/chunk-5OQXAEHG.js.map +0 -1
- package/dist/chunk-DGSC74SV.js.map +0 -1
- package/dist/evals/index.d.ts +0 -156
- package/dist/evals/index.js +0 -2
- package/dist/evals/index.js.map +0 -1
- package/dist/evals/scorers.d.ts +0 -92
- package/dist/evals/scorers.js +0 -8
- package/dist/evals/scorers.js.map +0 -1
- package/dist/mcp-apps-client-PUL4H54S.js.map +0 -1
- package/dist/openai-client-QAC3ZD5W.js.map +0 -1
- package/dist/platform-GKYYQBCS.js +0 -3
- /package/dist/{platform-GKYYQBCS.js.map → platform-LKQFC3AJ.js.map} +0 -0
package/dist/mcp/index.d.ts
CHANGED
|
@@ -1,285 +1,237 @@
|
|
|
1
|
-
import { ContentBlock, CallToolResult, ServerRequest, ServerNotification } from '@modelcontextprotocol/sdk/types.js';
|
|
2
1
|
import { ZodRawShapeCompat, ShapeOutput } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
3
2
|
export { ZodRawShapeCompat } from '@modelcontextprotocol/sdk/server/zod-compat.js';
|
|
4
3
|
import { RequestHandlerExtra } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
4
|
+
import { ServerRequest, ServerNotification, CallToolResult, ContentBlock } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { McpServer, ToolCallback } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
7
7
|
export { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
8
8
|
|
|
9
|
+
interface SearchResult {
|
|
10
|
+
source: string;
|
|
11
|
+
heading: string;
|
|
12
|
+
content: string;
|
|
13
|
+
score: number;
|
|
14
|
+
metadata?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
/** A file to ingest into the knowledge base */
|
|
17
|
+
interface KbIngestFile {
|
|
18
|
+
/** Filename (used as chunk source identifier) */
|
|
19
|
+
filename: string;
|
|
20
|
+
/** Markdown content of the file */
|
|
21
|
+
content: string;
|
|
22
|
+
/** Arbitrary key-value metadata attached to all chunks from this file */
|
|
23
|
+
metadata?: Record<string, string>;
|
|
24
|
+
}
|
|
25
|
+
/** Response from the ingest endpoint */
|
|
26
|
+
interface KbIngestResult {
|
|
27
|
+
/** Number of chunks created from the ingested files */
|
|
28
|
+
chunksIngested: number;
|
|
29
|
+
/** Number of files successfully processed */
|
|
30
|
+
filesProcessed: number;
|
|
31
|
+
}
|
|
32
|
+
/** Options for the search method */
|
|
33
|
+
interface KbSearchOptions {
|
|
34
|
+
/** Number of results to return (1-20, default 5) */
|
|
35
|
+
topK?: number;
|
|
36
|
+
/** Minimum similarity score threshold (0-1, default 0.3) */
|
|
37
|
+
minScore?: number;
|
|
38
|
+
/** Filter results to chunks whose metadata contains all these key-value pairs (exact match) */
|
|
39
|
+
metadata?: Record<string, string>;
|
|
40
|
+
}
|
|
41
|
+
/** A source entry in the knowledge base */
|
|
42
|
+
interface KbSource {
|
|
43
|
+
/** Source filename */
|
|
44
|
+
source: string;
|
|
45
|
+
/** Number of chunks from this source */
|
|
46
|
+
chunkCount: number;
|
|
47
|
+
/** ISO timestamp of when the source was first ingested */
|
|
48
|
+
createdAt: string;
|
|
49
|
+
}
|
|
50
|
+
/** KB client for server-side knowledge base operations */
|
|
51
|
+
interface KbClient {
|
|
52
|
+
/**
|
|
53
|
+
* Ingest files into the knowledge base.
|
|
54
|
+
*
|
|
55
|
+
* **Warning**: This is destructive — it deletes ALL existing chunks
|
|
56
|
+
* for the environment before ingesting the new files.
|
|
57
|
+
*/
|
|
58
|
+
ingest(files: KbIngestFile[]): Promise<KbIngestResult>;
|
|
59
|
+
/** Search the knowledge base for relevant chunks */
|
|
60
|
+
search(query: string, options?: KbSearchOptions): Promise<SearchResult[]>;
|
|
61
|
+
/** List all sources in the knowledge base */
|
|
62
|
+
sources(): Promise<KbSource[]>;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
type EventType = "session.started" | "tool.called" | "quote.requested" | "quote.succeeded" | "quote.failed" | "link.clicked" | "purchase.completed" | "widget_render" | "widget_click" | "widget_link_click" | "widget_error" | "widget_scroll" | "widget_form_field" | "widget_form_submit" | "user.identified";
|
|
66
|
+
interface ToolCalledProperties {
|
|
67
|
+
name?: string;
|
|
68
|
+
type?: "pricing" | "product_info" | "availability" | "support" | "other";
|
|
69
|
+
}
|
|
70
|
+
interface QuoteSucceededProperties {
|
|
71
|
+
amount?: number;
|
|
72
|
+
currency?: string;
|
|
73
|
+
}
|
|
74
|
+
interface LinkClickedProperties {
|
|
75
|
+
url?: string;
|
|
76
|
+
}
|
|
77
|
+
interface PurchaseCompletedProperties {
|
|
78
|
+
amount?: number;
|
|
79
|
+
currency?: string;
|
|
80
|
+
}
|
|
81
|
+
interface TrackingContext {
|
|
82
|
+
/**
|
|
83
|
+
* MCP request metadata passed through to the API.
|
|
84
|
+
*
|
|
85
|
+
* Location varies by MCP library:
|
|
86
|
+
* - `@vercel/mcp-handler`: `extra._meta`
|
|
87
|
+
* - `@modelcontextprotocol/sdk`: `request.params._meta`
|
|
88
|
+
*/
|
|
89
|
+
meta?: Record<string, unknown>;
|
|
90
|
+
/** Legacy metadata field supported for backward compatibility. */
|
|
91
|
+
metadata?: Record<string, unknown>;
|
|
92
|
+
/** Optional explicit correlation fields. */
|
|
93
|
+
sessionId?: string;
|
|
94
|
+
traceId?: string;
|
|
95
|
+
requestId?: string;
|
|
96
|
+
correlationId?: string;
|
|
97
|
+
externalUserId?: string;
|
|
98
|
+
/** Optional explicit envelope fields. */
|
|
99
|
+
eventId?: string;
|
|
100
|
+
timestamp?: string | Date;
|
|
101
|
+
source?: string;
|
|
102
|
+
}
|
|
9
103
|
/**
|
|
10
|
-
*
|
|
104
|
+
* Modern tracking shape (preferred).
|
|
11
105
|
*/
|
|
12
|
-
|
|
106
|
+
interface BaseTrackEvent extends TrackingContext {
|
|
107
|
+
event: EventType;
|
|
108
|
+
properties?: Record<string, unknown>;
|
|
109
|
+
}
|
|
110
|
+
type TrackEvent = ({
|
|
111
|
+
event: "session.started";
|
|
112
|
+
} & BaseTrackEvent) | ({
|
|
113
|
+
event: "tool.called";
|
|
114
|
+
properties?: ToolCalledProperties;
|
|
115
|
+
} & BaseTrackEvent) | ({
|
|
116
|
+
event: "quote.requested";
|
|
117
|
+
} & BaseTrackEvent) | ({
|
|
118
|
+
event: "quote.succeeded";
|
|
119
|
+
properties?: QuoteSucceededProperties;
|
|
120
|
+
} & BaseTrackEvent) | ({
|
|
121
|
+
event: "quote.failed";
|
|
122
|
+
} & BaseTrackEvent) | ({
|
|
123
|
+
event: "link.clicked";
|
|
124
|
+
properties?: LinkClickedProperties;
|
|
125
|
+
} & BaseTrackEvent) | ({
|
|
126
|
+
event: "purchase.completed";
|
|
127
|
+
properties?: PurchaseCompletedProperties;
|
|
128
|
+
} & BaseTrackEvent) | ({
|
|
129
|
+
event: "user.identified";
|
|
130
|
+
} & BaseTrackEvent);
|
|
13
131
|
/**
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
* OpenAI injects a global `window.openai` object.
|
|
17
|
-
* MCP Apps runs in a sandboxed iframe and uses postMessage.
|
|
132
|
+
* Legacy tracking shape supported for existing integrations.
|
|
18
133
|
*/
|
|
19
|
-
|
|
134
|
+
interface LegacyTrackEvent extends TrackingContext {
|
|
135
|
+
eventType: EventType;
|
|
136
|
+
properties?: Record<string, unknown>;
|
|
137
|
+
toolName?: string;
|
|
138
|
+
toolType?: ToolCalledProperties["type"];
|
|
139
|
+
quoteAmount?: number;
|
|
140
|
+
quoteCurrency?: string;
|
|
141
|
+
linkUrl?: string;
|
|
142
|
+
purchaseAmount?: number;
|
|
143
|
+
purchaseCurrency?: string;
|
|
144
|
+
}
|
|
20
145
|
/**
|
|
21
|
-
*
|
|
146
|
+
* Public track input accepted by `client.track()`.
|
|
22
147
|
*/
|
|
23
|
-
|
|
148
|
+
type TrackInput = TrackEvent | LegacyTrackEvent;
|
|
149
|
+
interface TrackingConfig {
|
|
150
|
+
/** Events API V2 endpoint path. */
|
|
151
|
+
endpointPath?: string;
|
|
152
|
+
/** Periodic flush interval for buffered events. */
|
|
153
|
+
flushIntervalMs?: number;
|
|
154
|
+
/** Max events per HTTP batch send. */
|
|
155
|
+
maxBatchSize?: number;
|
|
156
|
+
/** Max in-memory buffer size before oldest items are dropped. */
|
|
157
|
+
maxBufferSize?: number;
|
|
158
|
+
/** Number of retries for retryable failures. */
|
|
159
|
+
maxRetries?: number;
|
|
160
|
+
/** Retry backoff base delay. */
|
|
161
|
+
retryBaseDelayMs?: number;
|
|
162
|
+
/** Retry backoff max delay. */
|
|
163
|
+
retryMaxDelayMs?: number;
|
|
164
|
+
/** Default shutdown timeout when none is provided. */
|
|
165
|
+
shutdownTimeoutMs?: number;
|
|
166
|
+
}
|
|
167
|
+
interface TrackingShutdownOptions {
|
|
168
|
+
timeoutMs?: number;
|
|
169
|
+
}
|
|
170
|
+
interface TrackingShutdownResult {
|
|
171
|
+
timedOut: boolean;
|
|
172
|
+
pendingEvents: number;
|
|
173
|
+
}
|
|
24
174
|
/**
|
|
25
|
-
*
|
|
175
|
+
* Tracking module methods for WaniWaniClient.
|
|
26
176
|
*/
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
177
|
+
interface TrackingClient {
|
|
178
|
+
/**
|
|
179
|
+
* Send a one-shot identify event for a user.
|
|
180
|
+
* userId can be any string: an email, an internal ID, etc.
|
|
181
|
+
*/
|
|
182
|
+
identify: (userId: string, properties?: Record<string, unknown>, meta?: Record<string, unknown>) => Promise<{
|
|
183
|
+
eventId: string;
|
|
184
|
+
}>;
|
|
185
|
+
/**
|
|
186
|
+
* Track an event using modern or legacy input shape.
|
|
187
|
+
* Returns a deterministic event id immediately after enqueue.
|
|
188
|
+
*/
|
|
189
|
+
track: (event: TrackInput) => Promise<{
|
|
190
|
+
eventId: string;
|
|
191
|
+
}>;
|
|
192
|
+
/**
|
|
193
|
+
* Flush all currently buffered events.
|
|
194
|
+
*/
|
|
195
|
+
flush: () => Promise<void>;
|
|
196
|
+
/**
|
|
197
|
+
* Flush and stop the transport.
|
|
198
|
+
*/
|
|
199
|
+
shutdown: (options?: TrackingShutdownOptions) => Promise<TrackingShutdownResult>;
|
|
200
|
+
}
|
|
34
201
|
|
|
35
202
|
/**
|
|
36
|
-
*
|
|
203
|
+
* A request-scoped WaniWani client with meta pre-attached.
|
|
204
|
+
*
|
|
205
|
+
* Available as `context.waniwani` inside `createTool` handlers and flow nodes
|
|
206
|
+
* when the server is wrapped with `withWaniwani()`.
|
|
37
207
|
*/
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
bottom: number;
|
|
66
|
-
left: number;
|
|
67
|
-
right: number;
|
|
68
|
-
};
|
|
69
|
-
type SafeArea = {
|
|
70
|
-
insets: SafeAreaInsets;
|
|
71
|
-
};
|
|
72
|
-
type DeviceType = "mobile" | "tablet" | "desktop" | "unknown";
|
|
73
|
-
type UserAgent = {
|
|
74
|
-
device: {
|
|
75
|
-
type: DeviceType;
|
|
76
|
-
};
|
|
77
|
-
capabilities: {
|
|
78
|
-
hover: boolean;
|
|
79
|
-
touch: boolean;
|
|
80
|
-
};
|
|
81
|
-
};
|
|
82
|
-
/** Display mode */
|
|
83
|
-
type DisplayMode = "pip" | "inline" | "fullscreen";
|
|
84
|
-
type RequestDisplayMode = (args: {
|
|
85
|
-
mode: DisplayMode;
|
|
86
|
-
}) => Promise<{
|
|
87
|
-
/**
|
|
88
|
-
* The granted display mode. The host may reject the request.
|
|
89
|
-
* For mobile, PiP is always coerced to fullscreen.
|
|
90
|
-
*/
|
|
91
|
-
mode: DisplayMode;
|
|
92
|
-
}>;
|
|
93
|
-
type CallToolResponse = {
|
|
94
|
-
result: string;
|
|
95
|
-
};
|
|
96
|
-
/** Calling APIs */
|
|
97
|
-
type CallTool = (name: string, args: Record<string, unknown>) => Promise<CallToolResponse>;
|
|
98
|
-
/** Extra events */
|
|
99
|
-
declare const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
|
|
100
|
-
declare class SetGlobalsEvent extends CustomEvent<{
|
|
101
|
-
globals: Partial<OpenAIGlobals>;
|
|
102
|
-
}> {
|
|
103
|
-
constructor(detail: {
|
|
104
|
-
globals: Partial<OpenAIGlobals>;
|
|
105
|
-
});
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Global oai object injected by the web sandbox for communicating with chatgpt host page.
|
|
109
|
-
*/
|
|
110
|
-
declare global {
|
|
111
|
-
interface Window {
|
|
112
|
-
openai: API & OpenAIGlobals;
|
|
113
|
-
innerBaseUrl: string;
|
|
114
|
-
}
|
|
115
|
-
interface WindowEventMap {
|
|
116
|
-
[SET_GLOBALS_EVENT_TYPE]: SetGlobalsEvent;
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Result from calling a tool
|
|
122
|
-
*/
|
|
123
|
-
type ToolCallResult = CallToolResult;
|
|
124
|
-
/**
|
|
125
|
-
* Tool result notification (what the host pushes to the widget)
|
|
126
|
-
*/
|
|
127
|
-
type ToolResult = CallToolResult;
|
|
128
|
-
/**
|
|
129
|
-
* Host context - all values available from the host.
|
|
130
|
-
*/
|
|
131
|
-
type HostContext = {
|
|
132
|
-
theme: Theme;
|
|
133
|
-
locale: string;
|
|
134
|
-
displayMode: DisplayMode;
|
|
135
|
-
maxHeight: number | null;
|
|
136
|
-
safeArea: SafeArea | null;
|
|
137
|
-
toolOutput: UnknownObject | null;
|
|
138
|
-
toolResponseMetadata: UnknownObject | null;
|
|
139
|
-
widgetState: UnknownObject | null;
|
|
140
|
-
};
|
|
141
|
-
/**
|
|
142
|
-
* Unified widget client interface that works on both OpenAI and MCP Apps.
|
|
143
|
-
*
|
|
144
|
-
* Platform-specific behavior:
|
|
145
|
-
* - Display mode: OpenAI-only. MCP Apps returns "inline" and requestDisplayMode is a no-op.
|
|
146
|
-
* - Follow-up messages: Unified API, different underlying implementations.
|
|
147
|
-
*/
|
|
148
|
-
interface UnifiedWidgetClient {
|
|
149
|
-
/**
|
|
150
|
-
* Connect to the host. Must be called before using other methods.
|
|
151
|
-
* On OpenAI, this is a no-op (already connected via window.openai).
|
|
152
|
-
* On MCP Apps, this establishes the postMessage connection.
|
|
153
|
-
*/
|
|
154
|
-
connect(): Promise<void>;
|
|
155
|
-
/**
|
|
156
|
-
* Close the connection to the host and clean up resources.
|
|
157
|
-
* On OpenAI, this is a no-op.
|
|
158
|
-
* On MCP Apps, this closes the transport and removes event listeners.
|
|
159
|
-
*/
|
|
160
|
-
close(): Promise<void>;
|
|
161
|
-
/**
|
|
162
|
-
* Get the tool output (structured content returned by the tool handler).
|
|
163
|
-
* This is the main data source for widget rendering.
|
|
164
|
-
*/
|
|
165
|
-
getToolOutput<T = Record<string, unknown>>(): T | null;
|
|
166
|
-
/**
|
|
167
|
-
* Register a callback for when tool results are received.
|
|
168
|
-
* On OpenAI, this subscribes to toolOutput changes.
|
|
169
|
-
* On MCP Apps, this sets app.ontoolresult.
|
|
170
|
-
*/
|
|
171
|
-
onToolResult(callback: (result: ToolResult) => void): () => void;
|
|
172
|
-
/**
|
|
173
|
-
* Call another tool on the server.
|
|
174
|
-
*/
|
|
175
|
-
callTool(name: string, args: Record<string, unknown>): Promise<ToolCallResult>;
|
|
176
|
-
/**
|
|
177
|
-
* Open an external URL.
|
|
178
|
-
* On OpenAI: openai.openExternal({ href })
|
|
179
|
-
* On MCP Apps: app.sendOpenLink(url)
|
|
180
|
-
*/
|
|
181
|
-
openExternal(url: string): void;
|
|
182
|
-
/**
|
|
183
|
-
* Send a follow-up message to the AI.
|
|
184
|
-
* On OpenAI: openai.sendFollowUpMessage({ prompt })
|
|
185
|
-
* On MCP Apps: app.sendMessages([{ role: 'user', content: { type: 'text', text: prompt } }])
|
|
186
|
-
*/
|
|
187
|
-
sendFollowUp(prompt: string): void | Promise<void>;
|
|
188
|
-
/**
|
|
189
|
-
* Update hidden model context for the next assistant turn.
|
|
190
|
-
* On MCP Apps this uses the standard `ui/update-model-context` request.
|
|
191
|
-
* On other hosts this may fall back to best-effort behavior.
|
|
192
|
-
*/
|
|
193
|
-
updateModelContext(context: ModelContextUpdate): Promise<void> | void;
|
|
194
|
-
/**
|
|
195
|
-
* Get the current theme.
|
|
196
|
-
*/
|
|
197
|
-
getTheme(): Theme;
|
|
198
|
-
/**
|
|
199
|
-
* Subscribe to theme changes.
|
|
200
|
-
*/
|
|
201
|
-
onThemeChange(callback: (theme: Theme) => void): () => void;
|
|
202
|
-
/**
|
|
203
|
-
* Get the current locale.
|
|
204
|
-
*/
|
|
205
|
-
getLocale(): string;
|
|
206
|
-
/**
|
|
207
|
-
* Get the current display mode.
|
|
208
|
-
* OpenAI-only: returns "pip" | "inline" | "fullscreen"
|
|
209
|
-
* MCP Apps: always returns "inline"
|
|
210
|
-
*/
|
|
211
|
-
getDisplayMode(): DisplayMode;
|
|
212
|
-
/**
|
|
213
|
-
* Request a display mode change.
|
|
214
|
-
* OpenAI-only: requests the mode from the host.
|
|
215
|
-
* MCP Apps: no-op (returns current mode).
|
|
216
|
-
*/
|
|
217
|
-
requestDisplayMode(mode: DisplayMode): Promise<DisplayMode>;
|
|
218
|
-
/**
|
|
219
|
-
* Subscribe to display mode changes.
|
|
220
|
-
* OpenAI-only: subscribes to displayMode changes.
|
|
221
|
-
* MCP Apps: callback is never called.
|
|
222
|
-
*/
|
|
223
|
-
onDisplayModeChange(callback: (mode: DisplayMode) => void): () => void;
|
|
224
|
-
/**
|
|
225
|
-
* Get the safe area insets.
|
|
226
|
-
* OpenAI-only: returns insets from window.openai.safeArea.
|
|
227
|
-
* MCP Apps: returns null.
|
|
228
|
-
*/
|
|
229
|
-
getSafeArea(): SafeArea | null;
|
|
230
|
-
/**
|
|
231
|
-
* Subscribe to safe area changes.
|
|
232
|
-
* OpenAI-only: subscribes to safeArea changes.
|
|
233
|
-
* MCP Apps: callback is never called.
|
|
234
|
-
*/
|
|
235
|
-
onSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void;
|
|
236
|
-
/**
|
|
237
|
-
* Get the max height constraint.
|
|
238
|
-
* OpenAI-only: returns maxHeight from window.openai.maxHeight.
|
|
239
|
-
* MCP Apps: returns null.
|
|
240
|
-
*/
|
|
241
|
-
getMaxHeight(): number | null;
|
|
242
|
-
/**
|
|
243
|
-
* Subscribe to max height changes.
|
|
244
|
-
* OpenAI-only: subscribes to maxHeight changes.
|
|
245
|
-
* MCP Apps: callback is never called.
|
|
246
|
-
*/
|
|
247
|
-
onMaxHeightChange(callback: (maxHeight: number | null) => void): () => void;
|
|
248
|
-
/**
|
|
249
|
-
* Get the tool response metadata.
|
|
250
|
-
* OpenAI: returns metadata from window.openai.toolResponseMetadata.
|
|
251
|
-
* MCP Apps: returns `_meta` from the latest `ui/notifications/tool-result`, if provided by host.
|
|
252
|
-
*/
|
|
253
|
-
getToolResponseMetadata(): UnknownObject | null;
|
|
254
|
-
/**
|
|
255
|
-
* Subscribe to tool response metadata changes.
|
|
256
|
-
* OpenAI: subscribes to toolResponseMetadata changes.
|
|
257
|
-
* MCP Apps: fires when tool result `_meta` changes.
|
|
258
|
-
*/
|
|
259
|
-
onToolResponseMetadataChange(callback: (metadata: UnknownObject | null) => void): () => void;
|
|
260
|
-
/**
|
|
261
|
-
* Get the widget state.
|
|
262
|
-
* OpenAI-only: returns state from window.openai.widgetState.
|
|
263
|
-
* MCP Apps: returns null.
|
|
264
|
-
*/
|
|
265
|
-
getWidgetState(): UnknownObject | null;
|
|
266
|
-
/**
|
|
267
|
-
* Subscribe to widget state changes.
|
|
268
|
-
* OpenAI-only: subscribes to widgetState changes.
|
|
269
|
-
* MCP Apps: callback is never called.
|
|
270
|
-
*/
|
|
271
|
-
onWidgetStateChange(callback: (state: UnknownObject | null) => void): () => void;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
type WidgetCSP = {
|
|
275
|
-
/** Domains permitted for fetch/XHR network requests */
|
|
276
|
-
connect_domains?: string[];
|
|
277
|
-
/** Domains for static assets (images, fonts, scripts, styles) */
|
|
278
|
-
resource_domains?: string[];
|
|
279
|
-
/** Origins allowed for iframe embeds (triggers stricter app review) */
|
|
280
|
-
frame_domains?: string[];
|
|
281
|
-
/** Origins that can receive openExternal redirects without safe-link modal */
|
|
282
|
-
redirect_domains?: string[];
|
|
208
|
+
interface ScopedWaniWaniClient {
|
|
209
|
+
/** Track an event — request meta is automatically merged. */
|
|
210
|
+
track(event: TrackInput): Promise<{
|
|
211
|
+
eventId: string;
|
|
212
|
+
}>;
|
|
213
|
+
/** Identify a user — request meta is automatically merged. */
|
|
214
|
+
identify(userId: string, properties?: Record<string, unknown>): Promise<{
|
|
215
|
+
eventId: string;
|
|
216
|
+
}>;
|
|
217
|
+
/** Knowledge base client (no meta needed). */
|
|
218
|
+
readonly kb: KbClient;
|
|
219
|
+
/** @internal Resolved API config from withWaniwani(). */
|
|
220
|
+
readonly _config?: {
|
|
221
|
+
apiUrl?: string;
|
|
222
|
+
apiKey?: string;
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
type WidgetCSP = {
|
|
227
|
+
/** Domains permitted for fetch/XHR network requests */
|
|
228
|
+
connect_domains?: string[];
|
|
229
|
+
/** Domains for static assets (images, fonts, scripts, styles) */
|
|
230
|
+
resource_domains?: string[];
|
|
231
|
+
/** Origins allowed for iframe embeds (triggers stricter app review) */
|
|
232
|
+
frame_domains?: string[];
|
|
233
|
+
/** Origins that can receive openExternal redirects without safe-link modal */
|
|
234
|
+
redirect_domains?: string[];
|
|
283
235
|
};
|
|
284
236
|
type ResourceConfig = {
|
|
285
237
|
/** Unique identifier for the resource */
|
|
@@ -314,809 +266,1024 @@ type RegisteredResource = {
|
|
|
314
266
|
register: (server: McpServer) => Promise<void>;
|
|
315
267
|
};
|
|
316
268
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
|
|
328
|
-
/**
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
|
|
342
|
-
/**
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
/**
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
269
|
+
type ToolHandlerContext = {
|
|
270
|
+
/** Raw MCP request extra data (includes _meta for session extraction) */
|
|
271
|
+
extra?: {
|
|
272
|
+
_meta?: Record<string, unknown>;
|
|
273
|
+
};
|
|
274
|
+
/** Session-scoped WaniWani client — available when the server is wrapped with withWaniwani() */
|
|
275
|
+
waniwani?: ScopedWaniWaniClient;
|
|
276
|
+
};
|
|
277
|
+
type ToolConfig<TInput extends ZodRawShapeCompat> = {
|
|
278
|
+
/** The resource (HTML template) this tool renders. When present, tool returns structuredContent + widget _meta. */
|
|
279
|
+
resource?: RegisteredResource;
|
|
280
|
+
/** Tool identifier. Defaults to resource.id when resource is present, required otherwise. */
|
|
281
|
+
id?: string;
|
|
282
|
+
/** Display title. Defaults to resource.title when resource is present, required otherwise. */
|
|
283
|
+
title?: string;
|
|
284
|
+
/** Action-oriented description for the tool (tells the model WHEN to use it) */
|
|
285
|
+
description: string;
|
|
286
|
+
/** UI component description (describes WHAT the widget displays). Falls back to description. Only relevant when resource is present. */
|
|
287
|
+
widgetDescription?: string;
|
|
288
|
+
/** Input schema using zod */
|
|
289
|
+
inputSchema: TInput;
|
|
290
|
+
/** Optional loading message (defaults to "Loading..."). Only relevant when resource is present. */
|
|
291
|
+
invoking?: string;
|
|
292
|
+
/** Optional loaded message (defaults to "Loaded"). Only relevant when resource is present. */
|
|
293
|
+
invoked?: string;
|
|
294
|
+
/**
|
|
295
|
+
* When a widget calls this tool via `tools/call`, should the host auto-inject
|
|
296
|
+
* the tool's text result into the next follow-up message if the widget does
|
|
297
|
+
* not send one itself. Defaults to true. Set false for helper tools whose
|
|
298
|
+
* result is consumed programmatically by the widget.
|
|
299
|
+
*/
|
|
300
|
+
autoInjectResultText?: boolean;
|
|
301
|
+
/** Annotations describe the tool's potential impact. */
|
|
302
|
+
annotations?: {
|
|
303
|
+
readOnlyHint?: boolean;
|
|
304
|
+
idempotentHint?: boolean;
|
|
305
|
+
openWorldHint?: boolean;
|
|
306
|
+
destructiveHint?: boolean;
|
|
307
|
+
};
|
|
308
|
+
};
|
|
309
|
+
type ToolHandler<TInput extends ZodRawShapeCompat> = (input: ShapeOutput<TInput>, context: ToolHandlerContext) => Promise<{
|
|
310
|
+
/** Text content to return */
|
|
311
|
+
text: string;
|
|
312
|
+
/** Structured data returned as MCP `structuredContent`. */
|
|
313
|
+
data?: Record<string, unknown>;
|
|
314
|
+
}>;
|
|
315
|
+
type ToolToolCallback<TInput extends ZodRawShapeCompat> = ToolCallback<TInput>;
|
|
316
|
+
type RegisteredTool = {
|
|
317
|
+
id: string;
|
|
318
|
+
title: string;
|
|
319
|
+
description: string;
|
|
320
|
+
/** Register the tool on the server */
|
|
321
|
+
register: (server: McpServer) => Promise<void>;
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Server-side flow state store.
|
|
326
|
+
*
|
|
327
|
+
* Flow state is stored via the WaniWani API, keyed by session ID.
|
|
328
|
+
* Config comes from env vars (WANIWANI_API_KEY, WANIWANI_API_URL).
|
|
329
|
+
*/
|
|
330
|
+
|
|
331
|
+
interface FlowStore {
|
|
332
|
+
get(key: string): Promise<FlowTokenContent | null>;
|
|
333
|
+
set(key: string, value: FlowTokenContent): Promise<void>;
|
|
334
|
+
delete(key: string): Promise<void>;
|
|
357
335
|
}
|
|
358
|
-
|
|
359
|
-
|
|
336
|
+
|
|
337
|
+
declare const START: "__start__";
|
|
338
|
+
declare const END: "__end__";
|
|
339
|
+
declare const INTERRUPT: unique symbol;
|
|
340
|
+
declare const WIDGET: unique symbol;
|
|
341
|
+
/** A single question within an interrupt step */
|
|
342
|
+
type InterruptQuestion = {
|
|
343
|
+
/** Question to ask the user */
|
|
344
|
+
question: string;
|
|
345
|
+
/** State key where the answer will be stored */
|
|
346
|
+
field: string;
|
|
347
|
+
/** Optional suggestions to present as options */
|
|
348
|
+
suggestions?: string[];
|
|
349
|
+
/** Hidden context/instructions for this specific question (not shown to user directly) */
|
|
350
|
+
context?: string;
|
|
351
|
+
/** Validation function — runs after the user answers, before advancing to the next node */
|
|
352
|
+
validate?: (value: unknown) => MaybePromise<Record<string, unknown> | void>;
|
|
353
|
+
};
|
|
354
|
+
/**
|
|
355
|
+
* Interrupt signal — pauses the flow and asks the user one or more questions.
|
|
356
|
+
* Single-question and multi-question interrupts use the same type.
|
|
357
|
+
*/
|
|
358
|
+
type InterruptSignal = {
|
|
359
|
+
readonly __type: typeof INTERRUPT;
|
|
360
|
+
/** Questions to ask — ask all in one conversational message */
|
|
361
|
+
questions: InterruptQuestion[];
|
|
362
|
+
/** Overall hidden context/instructions for the assistant (not shown to user directly) */
|
|
363
|
+
context?: string;
|
|
364
|
+
};
|
|
365
|
+
type WidgetSignal = {
|
|
366
|
+
readonly __type: typeof WIDGET;
|
|
367
|
+
/** The id of the display tool to delegate rendering to */
|
|
368
|
+
tool: string;
|
|
369
|
+
/** Data to pass to the display tool */
|
|
370
|
+
data: Record<string, unknown>;
|
|
371
|
+
/** Description of what the widget does (for the AI's context) */
|
|
372
|
+
description?: string;
|
|
360
373
|
/**
|
|
361
|
-
*
|
|
374
|
+
* Whether the user is expected to interact with the widget before the flow continues.
|
|
375
|
+
* Defaults to true. Set to false for informational widgets that should render and then
|
|
376
|
+
* immediately advance to the next flow step.
|
|
377
|
+
*/
|
|
378
|
+
interactive?: boolean;
|
|
379
|
+
/**
|
|
380
|
+
* State key this widget fills — enables auto-skip when the field is already in state.
|
|
381
|
+
* Pass this so the engine can skip the widget step when the answer is already known.
|
|
382
|
+
*/
|
|
383
|
+
field?: string;
|
|
384
|
+
};
|
|
385
|
+
type MaybePromise<T> = T | Promise<T>;
|
|
386
|
+
/** Deep partial — allows partial updates at any nesting level (for z.object state fields). */
|
|
387
|
+
type DeepPartial<T> = {
|
|
388
|
+
[K in keyof T]?: T[K] extends Record<string, unknown> ? DeepPartial<T[K]> : T[K];
|
|
389
|
+
};
|
|
390
|
+
/**
|
|
391
|
+
* Extract known (non-index-signature) string keys from a type.
|
|
392
|
+
* Zod v4's z.object() adds `[key: string]: unknown` to inferred types,
|
|
393
|
+
* so we filter those out to get only the declared field names.
|
|
394
|
+
*/
|
|
395
|
+
type KnownStringKeys<T> = Extract<keyof {
|
|
396
|
+
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
397
|
+
}, string>;
|
|
398
|
+
/**
|
|
399
|
+
* Union of all valid field paths for a state type.
|
|
400
|
+
* - Flat fields produce their key: `"email"`
|
|
401
|
+
* - `z.object()` fields produce dot-paths to sub-fields: `"driver.name"`, `"driver.license"`
|
|
402
|
+
* - Arrays and general records (`z.record()`) are treated as flat fields.
|
|
403
|
+
* - Only 1 level of nesting is supported.
|
|
404
|
+
*/
|
|
405
|
+
type FieldPaths<TState> = {
|
|
406
|
+
[K in Extract<keyof TState, string>]: NonNullable<TState[K]> extends unknown[] ? K : NonNullable<TState[K]> extends Record<string, unknown> ? KnownStringKeys<NonNullable<TState[K]>> extends never ? K : K | `${K}.${KnownStringKeys<NonNullable<TState[K]>>}` : K;
|
|
407
|
+
}[Extract<keyof TState, string>];
|
|
408
|
+
/** Resolve a dot-path to the value type at that path in TState. */
|
|
409
|
+
type ResolveFieldType<TState, P extends string> = P extends `${infer Parent}.${infer Child}` ? Parent extends keyof TState ? Child extends keyof NonNullable<TState[Parent]> ? NonNullable<TState[Parent]>[Child] : never : never : P extends keyof TState ? TState[P] : never;
|
|
410
|
+
/**
|
|
411
|
+
* Typed interrupt function — available on the node context.
|
|
412
|
+
*
|
|
413
|
+
* First argument: an object where each key is a field path and each value
|
|
414
|
+
* describes the question for that field. Use dot-paths for nested state fields.
|
|
415
|
+
* `validate` receives the field's value typed from the Zod schema.
|
|
416
|
+
*
|
|
417
|
+
* Second argument (optional): config with overall hidden AI instructions.
|
|
418
|
+
*
|
|
419
|
+
* @example
|
|
420
|
+
* ```ts
|
|
421
|
+
* // Flat field
|
|
422
|
+
* interrupt({ breed: { question: "What breed is your pet?" } })
|
|
423
|
+
*
|
|
424
|
+
* // Nested field (z.object in state)
|
|
425
|
+
* interrupt({ "driver.name": { question: "Driver's name?" } })
|
|
426
|
+
*
|
|
427
|
+
* // Multiple questions with context
|
|
428
|
+
* interrupt(
|
|
429
|
+
* {
|
|
430
|
+
* "driver.name": { question: "Name?" },
|
|
431
|
+
* "driver.license": { question: "License?" },
|
|
432
|
+
* },
|
|
433
|
+
* { context: "Ask both questions naturally." },
|
|
434
|
+
* )
|
|
435
|
+
* ```
|
|
436
|
+
*/
|
|
437
|
+
type TypedInterrupt<TState> = (fields: {
|
|
438
|
+
[P in FieldPaths<TState>]?: {
|
|
439
|
+
question: string;
|
|
440
|
+
validate?: (value: ResolveFieldType<TState, P>) => MaybePromise<DeepPartial<TState> | void>;
|
|
441
|
+
suggestions?: string[];
|
|
442
|
+
context?: string;
|
|
443
|
+
};
|
|
444
|
+
}, config?: {
|
|
445
|
+
/** Overall hidden context/instructions for the assistant (not shown to user directly) */
|
|
446
|
+
context?: string;
|
|
447
|
+
}) => InterruptSignal;
|
|
448
|
+
/**
|
|
449
|
+
* Typed showWidget function — available on the node context.
|
|
450
|
+
* The `field` parameter accepts field paths (flat or dot-path for nested state).
|
|
451
|
+
*/
|
|
452
|
+
type TypedShowWidget<TState> = (tool: RegisteredTool | string, config: {
|
|
453
|
+
data: Record<string, unknown>;
|
|
454
|
+
description?: string;
|
|
455
|
+
interactive?: boolean;
|
|
456
|
+
field?: FieldPaths<TState>;
|
|
457
|
+
}) => WidgetSignal;
|
|
458
|
+
/**
|
|
459
|
+
* Context object passed to node handlers.
|
|
460
|
+
* Provides state, metadata, and typed helper functions for creating signals.
|
|
461
|
+
*/
|
|
462
|
+
type NodeContext<TState> = {
|
|
463
|
+
/** Current flow state (partial — fields are filled as the flow progresses) */
|
|
464
|
+
state: Partial<TState>;
|
|
465
|
+
/** Request metadata from the MCP call */
|
|
466
|
+
meta?: Record<string, unknown>;
|
|
467
|
+
/** Create an interrupt signal — pause and ask the user questions */
|
|
468
|
+
interrupt: TypedInterrupt<TState>;
|
|
469
|
+
/** Create a widget signal — pause and show a UI widget */
|
|
470
|
+
showWidget: TypedShowWidget<TState>;
|
|
471
|
+
/** Session-scoped WaniWani client — available when the server is wrapped with withWaniwani() */
|
|
472
|
+
waniwani?: ScopedWaniWaniClient;
|
|
473
|
+
};
|
|
474
|
+
/**
|
|
475
|
+
* Node handler — receives a context object and returns a signal or state updates.
|
|
476
|
+
* The return value determines behavior:
|
|
477
|
+
* - `Partial<TState>` → action node (state merged, auto-advance)
|
|
478
|
+
* - `InterruptSignal` → interrupt (pause, ask user one or more questions)
|
|
479
|
+
* - `WidgetSignal` → widget step (pause, show widget)
|
|
480
|
+
*/
|
|
481
|
+
type NodeHandler<TState> = (ctx: NodeContext<TState>) => MaybePromise<DeepPartial<TState> | InterruptSignal | WidgetSignal>;
|
|
482
|
+
/**
|
|
483
|
+
* Condition function for conditional edges.
|
|
484
|
+
* Receives current state, returns the name of the next node.
|
|
485
|
+
*/
|
|
486
|
+
type ConditionFn<TState> = (state: Partial<TState>) => string | Promise<string>;
|
|
487
|
+
type NodeOptions = {
|
|
488
|
+
/** Human-readable label for this node (used in funnel visualization and Graphs). */
|
|
489
|
+
label: string;
|
|
490
|
+
/** When true, this node is excluded from funnel analytics. */
|
|
491
|
+
hideFromFunnel?: boolean;
|
|
492
|
+
};
|
|
493
|
+
/**
|
|
494
|
+
* Config for the object form of `.addNode({ id, run, label?, hideFromFunnel? })`.
|
|
495
|
+
*
|
|
496
|
+
* Preferred over the positional form `.addNode(id, run, options?)` — metadata
|
|
497
|
+
* sits at the top where the eye lands, and the handler is a named field.
|
|
498
|
+
*/
|
|
499
|
+
type AddNodeConfig<TState, TName extends string = string> = {
|
|
500
|
+
/** Unique node id within the flow. */
|
|
501
|
+
id: TName;
|
|
502
|
+
/** Node handler — receives ctx, returns state updates or a signal. */
|
|
503
|
+
run: NodeHandler<TState>;
|
|
504
|
+
/** Human-readable label for funnel visualization and graphs. Defaults to `id`. */
|
|
505
|
+
label?: string;
|
|
506
|
+
/** When true, this node is excluded from funnel analytics. */
|
|
507
|
+
hideFromFunnel?: boolean;
|
|
508
|
+
};
|
|
509
|
+
type FlowGraphNode = {
|
|
510
|
+
id: string;
|
|
511
|
+
type: "widget" | "interrupt" | "action";
|
|
512
|
+
label: string;
|
|
513
|
+
hideFromFunnel?: boolean;
|
|
514
|
+
};
|
|
515
|
+
type FlowGraphEdge = {
|
|
516
|
+
from: string;
|
|
517
|
+
to: string;
|
|
518
|
+
type: "direct";
|
|
519
|
+
} | {
|
|
520
|
+
from: string;
|
|
521
|
+
to: string[];
|
|
522
|
+
type: "conditional";
|
|
523
|
+
};
|
|
524
|
+
type FlowGraph = {
|
|
525
|
+
flowId: string;
|
|
526
|
+
title: string;
|
|
527
|
+
nodes: FlowGraphNode[];
|
|
528
|
+
edges: FlowGraphEdge[];
|
|
529
|
+
};
|
|
530
|
+
type FlowConfig = {
|
|
531
|
+
/** Unique identifier for the flow (becomes the MCP tool name) */
|
|
532
|
+
id: string;
|
|
533
|
+
/** Display title */
|
|
534
|
+
title: string;
|
|
535
|
+
/** Description for the AI (explains when to use this flow) */
|
|
536
|
+
description: string;
|
|
537
|
+
/**
|
|
538
|
+
* Define the flow's state — each field the flow collects.
|
|
539
|
+
* Keys are the field names used in `interrupt({ field })`,
|
|
540
|
+
* values are Zod schemas with `.describe()`.
|
|
362
541
|
*
|
|
363
|
-
*
|
|
364
|
-
*
|
|
542
|
+
* The state definition serves two purposes:
|
|
543
|
+
* 1. Type inference — `TState` is automatically derived, no explicit generic needed
|
|
544
|
+
* 2. AI protocol — field names, types, and descriptions are included in the tool
|
|
545
|
+
* description so the AI can pre-fill answers via `_meta.flow.state`
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
* ```ts
|
|
549
|
+
* state: {
|
|
550
|
+
* country: z.string().describe("Country the business is based in"),
|
|
551
|
+
* status: z.enum(["registered", "unregistered"]).describe("Business registration status"),
|
|
552
|
+
* }
|
|
553
|
+
* ```
|
|
365
554
|
*/
|
|
366
|
-
|
|
367
|
-
/**
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
555
|
+
state: Record<string, z.ZodType>;
|
|
556
|
+
/**
|
|
557
|
+
* If true, instruct the agent to omit PII (names, emails, phones, addresses,
|
|
558
|
+
* IDs, ages, birthdates) from the `intent` and `context` fields. Only adds a
|
|
559
|
+
* guidance line to the tool description — does not redact server-side.
|
|
560
|
+
*/
|
|
561
|
+
omitIntentPII?: boolean;
|
|
562
|
+
/** Optional tool annotations */
|
|
563
|
+
annotations?: {
|
|
564
|
+
readOnlyHint?: boolean;
|
|
565
|
+
idempotentHint?: boolean;
|
|
566
|
+
openWorldHint?: boolean;
|
|
567
|
+
destructiveHint?: boolean;
|
|
568
|
+
};
|
|
569
|
+
};
|
|
570
|
+
/**
|
|
571
|
+
* Infer the runtime state type from a flow's state schema definition.
|
|
572
|
+
*
|
|
573
|
+
* @example
|
|
574
|
+
* ```ts
|
|
575
|
+
* const config = {
|
|
576
|
+
* state: {
|
|
577
|
+
* country: z.enum(["FR", "DE"]),
|
|
578
|
+
* status: z.enum(["registered", "unregistered"]),
|
|
579
|
+
* }
|
|
580
|
+
* };
|
|
581
|
+
* type MyState = InferFlowState<typeof config.state>;
|
|
582
|
+
* // { country: "FR" | "DE"; status: "registered" | "unregistered" }
|
|
583
|
+
* ```
|
|
584
|
+
*/
|
|
585
|
+
type InferFlowState<T extends Record<string, z.ZodType>> = {
|
|
586
|
+
[K in keyof T]: z.infer<T[K]>;
|
|
587
|
+
};
|
|
588
|
+
/**
|
|
589
|
+
* Flow tool handler — uses shared MCP types (`RequestHandlerExtra`, `CallToolResult`)
|
|
590
|
+
* so it's assignable to both MCP SDK's `ToolCallback` and Skybridge's `ToolHandler`.
|
|
591
|
+
*/
|
|
592
|
+
type FlowToolHandler = (args: Record<string, unknown>, extra: RequestHandlerExtra<ServerRequest, ServerNotification>) => CallToolResult | Promise<CallToolResult>;
|
|
593
|
+
/**
|
|
594
|
+
* A compiled flow — can be registered on an McpServer.
|
|
595
|
+
*
|
|
596
|
+
* Exposes MCP-compatible `name`, `config`, and `handler` so it can be
|
|
597
|
+
* registered directly: `server.registerTool(flow.name, flow.config, flow.handler)`
|
|
598
|
+
*/
|
|
599
|
+
type RegisteredFlow = {
|
|
600
|
+
/** Tool name — pass to `server.registerTool(flow.name, flow.config, flow.handler)`. */
|
|
601
|
+
name: string;
|
|
602
|
+
/** Tool config object — pass to `server.registerTool(flow.name, flow.config, flow.handler)`. */
|
|
603
|
+
config: {
|
|
604
|
+
title: string;
|
|
605
|
+
description: string;
|
|
606
|
+
inputSchema: ZodRawShapeCompat;
|
|
607
|
+
annotations?: {
|
|
608
|
+
readOnlyHint?: boolean;
|
|
609
|
+
idempotentHint?: boolean;
|
|
610
|
+
openWorldHint?: boolean;
|
|
611
|
+
destructiveHint?: boolean;
|
|
612
|
+
};
|
|
613
|
+
};
|
|
614
|
+
/** Tool callback — pass to `server.registerTool(flow.name, flow.config, flow.handler)`. */
|
|
615
|
+
handler: FlowToolHandler;
|
|
616
|
+
/** Register this flow on an MCP server. Shorthand for `server.registerTool(flow.name, flow.config, flow.handler)`. */
|
|
617
|
+
register: (server: McpServer) => Promise<void>;
|
|
618
|
+
/** Returns a Mermaid `flowchart TD` diagram of the flow graph. */
|
|
619
|
+
graph: () => string;
|
|
620
|
+
flowGraph: FlowGraph;
|
|
621
|
+
};
|
|
622
|
+
type FlowTokenContent = {
|
|
623
|
+
step?: string;
|
|
624
|
+
state: Record<string, unknown>;
|
|
625
|
+
field?: string;
|
|
626
|
+
widgetId?: string;
|
|
627
|
+
};
|
|
628
|
+
type InterruptQuestionData = {
|
|
629
|
+
question: string;
|
|
630
|
+
field: string;
|
|
631
|
+
suggestions?: string[];
|
|
632
|
+
context?: string;
|
|
633
|
+
};
|
|
634
|
+
type FlowInterruptContent = {
|
|
635
|
+
status: "interrupt";
|
|
636
|
+
/** Single-question shorthand */
|
|
637
|
+
question?: string;
|
|
638
|
+
field?: string;
|
|
639
|
+
suggestions?: string[];
|
|
640
|
+
/** Multi-question */
|
|
641
|
+
questions?: InterruptQuestionData[];
|
|
642
|
+
context?: string;
|
|
643
|
+
};
|
|
644
|
+
type FlowWidgetContent = {
|
|
645
|
+
status: "widget";
|
|
646
|
+
/** Display tool to call */
|
|
647
|
+
tool: string;
|
|
648
|
+
/** Data to pass to the display tool */
|
|
649
|
+
data: Record<string, unknown>;
|
|
650
|
+
description?: string;
|
|
651
|
+
/** Whether the widget expects user interaction before continuing */
|
|
652
|
+
interactive?: boolean;
|
|
653
|
+
};
|
|
654
|
+
type FlowCompleteContent = {
|
|
655
|
+
status: "complete";
|
|
656
|
+
};
|
|
657
|
+
type FlowErrorContent = {
|
|
658
|
+
status: "error";
|
|
659
|
+
error: string;
|
|
660
|
+
};
|
|
372
661
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
662
|
+
/**
|
|
663
|
+
* A LangGraph-inspired state graph builder for MCP tools.
|
|
664
|
+
*
|
|
665
|
+
* @example
|
|
666
|
+
* ```ts
|
|
667
|
+
* const flow = new StateGraph<MyState>({
|
|
668
|
+
* id: "onboarding",
|
|
669
|
+
* title: "User Onboarding",
|
|
670
|
+
* description: "Guides users through onboarding",
|
|
671
|
+
* })
|
|
672
|
+
* .addNode({
|
|
673
|
+
* id: "ask_name",
|
|
674
|
+
* run: ({ interrupt }) => interrupt({ question: "What's your name?", field: "name" }),
|
|
675
|
+
* })
|
|
676
|
+
* .addNode({
|
|
677
|
+
* id: "greet",
|
|
678
|
+
* run: ({ state }) => ({ greeting: `Hello ${state.name}!` }),
|
|
679
|
+
* })
|
|
680
|
+
* .addEdge(START, "ask_name")
|
|
681
|
+
* .addEdge("ask_name", "greet")
|
|
682
|
+
* .addEdge("greet", END)
|
|
683
|
+
* .compile();
|
|
684
|
+
* ```
|
|
685
|
+
*/
|
|
686
|
+
declare class StateGraph<TState extends Record<string, unknown>, TNodes extends string = never> {
|
|
687
|
+
private nodes;
|
|
688
|
+
private edges;
|
|
689
|
+
private nodeOptions;
|
|
690
|
+
private config;
|
|
691
|
+
constructor(config: FlowConfig);
|
|
390
692
|
/**
|
|
391
|
-
*
|
|
693
|
+
* Add a node with a handler.
|
|
392
694
|
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
*
|
|
695
|
+
* The handler receives a context object with `state`, `meta`, `interrupt`, and `showWidget`.
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```ts
|
|
699
|
+
* .addNode({
|
|
700
|
+
* id: "ask_name",
|
|
701
|
+
* label: "Ask for name",
|
|
702
|
+
* run: ({ interrupt }) => interrupt({ question: "What's your name?", field: "name" }),
|
|
703
|
+
* })
|
|
704
|
+
* ```
|
|
396
705
|
*/
|
|
397
|
-
|
|
398
|
-
/** Legacy metadata field supported for backward compatibility. */
|
|
399
|
-
metadata?: Record<string, unknown>;
|
|
400
|
-
/** Optional explicit correlation fields. */
|
|
401
|
-
sessionId?: string;
|
|
402
|
-
traceId?: string;
|
|
403
|
-
requestId?: string;
|
|
404
|
-
correlationId?: string;
|
|
405
|
-
externalUserId?: string;
|
|
406
|
-
/** Optional explicit envelope fields. */
|
|
407
|
-
eventId?: string;
|
|
408
|
-
timestamp?: string | Date;
|
|
409
|
-
source?: string;
|
|
410
|
-
}
|
|
411
|
-
/**
|
|
412
|
-
* Modern tracking shape (preferred).
|
|
413
|
-
*/
|
|
414
|
-
interface BaseTrackEvent extends TrackingContext {
|
|
415
|
-
event: EventType;
|
|
416
|
-
properties?: Record<string, unknown>;
|
|
417
|
-
}
|
|
418
|
-
type TrackEvent = ({
|
|
419
|
-
event: "session.started";
|
|
420
|
-
} & BaseTrackEvent) | ({
|
|
421
|
-
event: "tool.called";
|
|
422
|
-
properties?: ToolCalledProperties;
|
|
423
|
-
} & BaseTrackEvent) | ({
|
|
424
|
-
event: "quote.requested";
|
|
425
|
-
} & BaseTrackEvent) | ({
|
|
426
|
-
event: "quote.succeeded";
|
|
427
|
-
properties?: QuoteSucceededProperties;
|
|
428
|
-
} & BaseTrackEvent) | ({
|
|
429
|
-
event: "quote.failed";
|
|
430
|
-
} & BaseTrackEvent) | ({
|
|
431
|
-
event: "link.clicked";
|
|
432
|
-
properties?: LinkClickedProperties;
|
|
433
|
-
} & BaseTrackEvent) | ({
|
|
434
|
-
event: "purchase.completed";
|
|
435
|
-
properties?: PurchaseCompletedProperties;
|
|
436
|
-
} & BaseTrackEvent) | ({
|
|
437
|
-
event: "user.identified";
|
|
438
|
-
} & BaseTrackEvent);
|
|
439
|
-
/**
|
|
440
|
-
* Legacy tracking shape supported for existing integrations.
|
|
441
|
-
*/
|
|
442
|
-
interface LegacyTrackEvent extends TrackingContext {
|
|
443
|
-
eventType: EventType;
|
|
444
|
-
properties?: Record<string, unknown>;
|
|
445
|
-
toolName?: string;
|
|
446
|
-
toolType?: ToolCalledProperties["type"];
|
|
447
|
-
quoteAmount?: number;
|
|
448
|
-
quoteCurrency?: string;
|
|
449
|
-
linkUrl?: string;
|
|
450
|
-
purchaseAmount?: number;
|
|
451
|
-
purchaseCurrency?: string;
|
|
452
|
-
}
|
|
453
|
-
/**
|
|
454
|
-
* Public track input accepted by `client.track()`.
|
|
455
|
-
*/
|
|
456
|
-
type TrackInput = TrackEvent | LegacyTrackEvent;
|
|
457
|
-
interface TrackingConfig {
|
|
458
|
-
/** Events API V2 endpoint path. */
|
|
459
|
-
endpointPath?: string;
|
|
460
|
-
/** Periodic flush interval for buffered events. */
|
|
461
|
-
flushIntervalMs?: number;
|
|
462
|
-
/** Max events per HTTP batch send. */
|
|
463
|
-
maxBatchSize?: number;
|
|
464
|
-
/** Max in-memory buffer size before oldest items are dropped. */
|
|
465
|
-
maxBufferSize?: number;
|
|
466
|
-
/** Number of retries for retryable failures. */
|
|
467
|
-
maxRetries?: number;
|
|
468
|
-
/** Retry backoff base delay. */
|
|
469
|
-
retryBaseDelayMs?: number;
|
|
470
|
-
/** Retry backoff max delay. */
|
|
471
|
-
retryMaxDelayMs?: number;
|
|
472
|
-
/** Default shutdown timeout when none is provided. */
|
|
473
|
-
shutdownTimeoutMs?: number;
|
|
474
|
-
}
|
|
475
|
-
interface TrackingShutdownOptions {
|
|
476
|
-
timeoutMs?: number;
|
|
477
|
-
}
|
|
478
|
-
interface TrackingShutdownResult {
|
|
479
|
-
timedOut: boolean;
|
|
480
|
-
pendingEvents: number;
|
|
481
|
-
}
|
|
482
|
-
/**
|
|
483
|
-
* Tracking module methods for WaniWaniClient.
|
|
484
|
-
*/
|
|
485
|
-
interface TrackingClient {
|
|
706
|
+
addNode<TName extends string>(config: AddNodeConfig<TState, TName>): StateGraph<TState, TNodes | TName>;
|
|
486
707
|
/**
|
|
487
|
-
*
|
|
488
|
-
*
|
|
708
|
+
* @deprecated Use the object form: `.addNode({ id, run, label? })`.
|
|
709
|
+
* The positional form will be removed in v0.13.0
|
|
489
710
|
*/
|
|
490
|
-
|
|
491
|
-
eventId: string;
|
|
492
|
-
}>;
|
|
711
|
+
addNode<TName extends string>(name: TName, handler: NodeHandler<TState>, options?: NodeOptions): StateGraph<TState, TNodes | TName>;
|
|
493
712
|
/**
|
|
494
|
-
*
|
|
495
|
-
*
|
|
713
|
+
* Add a direct edge between two nodes.
|
|
714
|
+
*
|
|
715
|
+
* Use `START` as `from` to set the entry point.
|
|
716
|
+
* Use `END` as `to` to mark a terminal node.
|
|
496
717
|
*/
|
|
497
|
-
|
|
498
|
-
eventId: string;
|
|
499
|
-
}>;
|
|
718
|
+
addEdge(from: typeof START | TNodes, to: TNodes | typeof END): this;
|
|
500
719
|
/**
|
|
501
|
-
*
|
|
720
|
+
* Add a conditional edge from a node.
|
|
721
|
+
*
|
|
722
|
+
* The condition function receives current state and returns the name of the next node.
|
|
502
723
|
*/
|
|
503
|
-
|
|
724
|
+
addConditionalEdge(from: TNodes, condition: (state: Partial<TState>) => TNodes | typeof END | Promise<TNodes | typeof END>): this;
|
|
504
725
|
/**
|
|
505
|
-
*
|
|
726
|
+
* Generate a Mermaid `flowchart TD` diagram of the graph.
|
|
727
|
+
*
|
|
728
|
+
* Direct edges use solid arrows. Conditional edges use a dashed arrow
|
|
729
|
+
* to a placeholder since branch targets are determined at runtime.
|
|
730
|
+
*
|
|
731
|
+
* @example
|
|
732
|
+
* ```ts
|
|
733
|
+
* console.log(graph.graph());
|
|
734
|
+
* // flowchart TD
|
|
735
|
+
* // __start__((Start))
|
|
736
|
+
* // ask_name[ask_name]
|
|
737
|
+
* // greet[greet]
|
|
738
|
+
* // __end__((End))
|
|
739
|
+
* // __start__ --> ask_name
|
|
740
|
+
* // ask_name --> greet
|
|
741
|
+
* // greet --> __end__
|
|
742
|
+
* ```
|
|
506
743
|
*/
|
|
507
|
-
|
|
744
|
+
graph(): string;
|
|
745
|
+
/**
|
|
746
|
+
* Compile the graph into a RegisteredFlow that can be registered on an McpServer.
|
|
747
|
+
*
|
|
748
|
+
* Validates the graph structure and returns a registration-compatible object.
|
|
749
|
+
*/
|
|
750
|
+
compile(options?: {
|
|
751
|
+
store?: FlowStore;
|
|
752
|
+
}): RegisteredFlow;
|
|
753
|
+
private validate;
|
|
508
754
|
}
|
|
509
755
|
|
|
510
756
|
/**
|
|
511
|
-
*
|
|
757
|
+
* Create a new flow graph — convenience factory for `new StateGraph()`.
|
|
512
758
|
*
|
|
513
|
-
*
|
|
514
|
-
*
|
|
759
|
+
* The state type is automatically inferred from the `state` definition —
|
|
760
|
+
* no explicit generic parameter needed.
|
|
761
|
+
*
|
|
762
|
+
* @example
|
|
763
|
+
* ```ts
|
|
764
|
+
* import { createFlow, interrupt, START, END } from "@waniwani/sdk/mcp";
|
|
765
|
+
* import { z } from "zod";
|
|
766
|
+
*
|
|
767
|
+
* const flow = createFlow({
|
|
768
|
+
* id: "onboarding",
|
|
769
|
+
* title: "User Onboarding",
|
|
770
|
+
* description: "Guides users through onboarding. Use when a user wants to get started.",
|
|
771
|
+
* state: {
|
|
772
|
+
* name: z.string().describe("The user's name"),
|
|
773
|
+
* email: z.string().describe("The user's email address"),
|
|
774
|
+
* },
|
|
775
|
+
* })
|
|
776
|
+
* .addNode({
|
|
777
|
+
* id: "ask_name",
|
|
778
|
+
* run: () => interrupt({ question: "What's your name?", field: "name" }),
|
|
779
|
+
* })
|
|
780
|
+
* .addNode({
|
|
781
|
+
* id: "ask_email",
|
|
782
|
+
* run: () => interrupt({ question: "What's your email?", field: "email" }),
|
|
783
|
+
* })
|
|
784
|
+
* .addEdge(START, "ask_name")
|
|
785
|
+
* .addEdge("ask_name", "ask_email")
|
|
786
|
+
* .addEdge("ask_email", END)
|
|
787
|
+
* .compile();
|
|
788
|
+
* ```
|
|
515
789
|
*/
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
eventId: string;
|
|
520
|
-
}>;
|
|
521
|
-
/** Identify a user — request meta is automatically merged. */
|
|
522
|
-
identify(userId: string, properties?: Record<string, unknown>): Promise<{
|
|
523
|
-
eventId: string;
|
|
524
|
-
}>;
|
|
525
|
-
/** Knowledge base client (no meta needed). */
|
|
526
|
-
readonly kb: KbClient;
|
|
527
|
-
/** @internal Resolved API config from withWaniwani(). */
|
|
528
|
-
readonly _config?: {
|
|
529
|
-
apiUrl?: string;
|
|
530
|
-
apiKey?: string;
|
|
531
|
-
};
|
|
532
|
-
}
|
|
790
|
+
declare function createFlow<const TSchema extends Record<string, z.ZodType>>(config: Omit<FlowConfig, "state"> & {
|
|
791
|
+
state: TSchema;
|
|
792
|
+
}): StateGraph<InferFlowState<TSchema>>;
|
|
533
793
|
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
inputSchema: TInput;
|
|
555
|
-
/** Optional loading message (defaults to "Loading..."). Only relevant when resource is present. */
|
|
556
|
-
invoking?: string;
|
|
557
|
-
/** Optional loaded message (defaults to "Loaded"). Only relevant when resource is present. */
|
|
558
|
-
invoked?: string;
|
|
559
|
-
/**
|
|
560
|
-
* When a widget calls this tool via `tools/call`, should the host auto-inject
|
|
561
|
-
* the tool's text result into the next follow-up message if the widget does
|
|
562
|
-
* not send one itself. Defaults to true. Set false for helper tools whose
|
|
563
|
-
* result is consumed programmatically by the widget.
|
|
564
|
-
*/
|
|
565
|
-
autoInjectResultText?: boolean;
|
|
566
|
-
/** Annotations describe the tool's potential impact. */
|
|
567
|
-
annotations?: {
|
|
568
|
-
readOnlyHint?: boolean;
|
|
569
|
-
idempotentHint?: boolean;
|
|
570
|
-
openWorldHint?: boolean;
|
|
571
|
-
destructiveHint?: boolean;
|
|
572
|
-
};
|
|
794
|
+
/**
|
|
795
|
+
* Mark a Zod schema as PII — its value will be replaced with `"REDACTED"` in
|
|
796
|
+
* any `tool.called` event payload sent to the WaniWani API. The handler still
|
|
797
|
+
* receives the original value; only the tracked copy is scrubbed.
|
|
798
|
+
*
|
|
799
|
+
* Apply at the end of the schema chain so the marker is attached to the final
|
|
800
|
+
* schema:
|
|
801
|
+
*
|
|
802
|
+
* ```ts
|
|
803
|
+
* ages: redacted(z.string().describe("Comma-separated ages")),
|
|
804
|
+
* zipcode: redacted(z.string().describe("Spanish postal code")),
|
|
805
|
+
* ```
|
|
806
|
+
*
|
|
807
|
+
* Uses Zod v4's `.meta()` registry, so the marker is preserved across schema
|
|
808
|
+
* clones (`.optional()`, `.default()`, etc.).
|
|
809
|
+
*/
|
|
810
|
+
declare function redacted<T extends z.ZodType>(schema: T): T;
|
|
811
|
+
|
|
812
|
+
type WithDecodedState = {
|
|
813
|
+
decodedState: FlowTokenContent | null;
|
|
573
814
|
};
|
|
574
|
-
type
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
815
|
+
type FlowTestResult = (FlowInterruptContent & WithDecodedState) | (FlowWidgetContent & WithDecodedState) | (FlowCompleteContent & WithDecodedState) | (FlowErrorContent & WithDecodedState);
|
|
816
|
+
declare function createFlowTestHarness(flow: RegisteredFlow, options?: {
|
|
817
|
+
stateStore?: FlowStore;
|
|
818
|
+
}): Promise<{
|
|
819
|
+
start(intent: string, stateUpdates?: Record<string, unknown>, context?: string): Promise<FlowTestResult>;
|
|
820
|
+
continueWith(stateUpdates?: Record<string, unknown>): Promise<FlowTestResult>;
|
|
821
|
+
resetWith(stateUpdates: Record<string, unknown>): Promise<FlowTestResult>;
|
|
822
|
+
lastState(): Promise<FlowTokenContent | null>;
|
|
579
823
|
}>;
|
|
580
|
-
type ToolToolCallback<TInput extends ZodRawShapeCompat> = ToolCallback<TInput>;
|
|
581
|
-
type RegisteredTool = {
|
|
582
|
-
id: string;
|
|
583
|
-
title: string;
|
|
584
|
-
description: string;
|
|
585
|
-
/** Register the tool on the server */
|
|
586
|
-
register: (server: McpServer) => Promise<void>;
|
|
587
|
-
};
|
|
588
824
|
|
|
589
825
|
/**
|
|
590
|
-
*
|
|
826
|
+
* Generic key-value store backed by the WaniWani API.
|
|
591
827
|
*
|
|
592
|
-
*
|
|
593
|
-
*
|
|
828
|
+
* Values are stored as JSON objects (`Record<string, unknown>`) in the
|
|
829
|
+
* `/api/mcp/redis/*` endpoints. Tenant isolation is handled by the API key.
|
|
830
|
+
*
|
|
831
|
+
* Config is read from env vars:
|
|
832
|
+
* - `WANIWANI_API_KEY` (required)
|
|
833
|
+
* - `WANIWANI_API_URL` (optional, defaults to https://app.waniwani.ai)
|
|
834
|
+
* - `WANIWANI_ENCRYPTION_KEY` (optional, base64-encoded 32-byte key for AES-256-GCM encryption)
|
|
594
835
|
*/
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
836
|
+
interface KvStore<T = Record<string, unknown>> {
|
|
837
|
+
get(key: string): Promise<T | null>;
|
|
838
|
+
set(key: string, value: T): Promise<void>;
|
|
839
|
+
delete(key: string): Promise<void>;
|
|
840
|
+
}
|
|
841
|
+
declare class WaniwaniKvStore<T = Record<string, unknown>> implements KvStore<T> {
|
|
842
|
+
private get baseUrl();
|
|
843
|
+
private get apiKey();
|
|
844
|
+
private get encryptionKey();
|
|
845
|
+
get(key: string): Promise<T | null>;
|
|
846
|
+
set(key: string, value: T): Promise<void>;
|
|
599
847
|
delete(key: string): Promise<void>;
|
|
848
|
+
private request;
|
|
600
849
|
}
|
|
601
850
|
|
|
602
|
-
declare const START: "__start__";
|
|
603
|
-
declare const END: "__end__";
|
|
604
|
-
declare const INTERRUPT: unique symbol;
|
|
605
|
-
declare const WIDGET: unique symbol;
|
|
606
|
-
/** A single question within an interrupt step */
|
|
607
|
-
type InterruptQuestion = {
|
|
608
|
-
/** Question to ask the user */
|
|
609
|
-
question: string;
|
|
610
|
-
/** State key where the answer will be stored */
|
|
611
|
-
field: string;
|
|
612
|
-
/** Optional suggestions to present as options */
|
|
613
|
-
suggestions?: string[];
|
|
614
|
-
/** Hidden context/instructions for this specific question (not shown to user directly) */
|
|
615
|
-
context?: string;
|
|
616
|
-
/** Validation function — runs after the user answers, before advancing to the next node */
|
|
617
|
-
validate?: (value: unknown) => MaybePromise<Record<string, unknown> | void>;
|
|
618
|
-
};
|
|
619
|
-
/**
|
|
620
|
-
* Interrupt signal — pauses the flow and asks the user one or more questions.
|
|
621
|
-
* Single-question and multi-question interrupts use the same type.
|
|
622
|
-
*/
|
|
623
|
-
type InterruptSignal = {
|
|
624
|
-
readonly __type: typeof INTERRUPT;
|
|
625
|
-
/** Questions to ask — ask all in one conversational message */
|
|
626
|
-
questions: InterruptQuestion[];
|
|
627
|
-
/** Overall hidden context/instructions for the assistant (not shown to user directly) */
|
|
628
|
-
context?: string;
|
|
629
|
-
};
|
|
630
|
-
type WidgetSignal = {
|
|
631
|
-
readonly __type: typeof WIDGET;
|
|
632
|
-
/** The id of the display tool to delegate rendering to */
|
|
633
|
-
tool: string;
|
|
634
|
-
/** Data to pass to the display tool */
|
|
635
|
-
data: Record<string, unknown>;
|
|
636
|
-
/** Description of what the widget does (for the AI's context) */
|
|
637
|
-
description?: string;
|
|
638
|
-
/**
|
|
639
|
-
* Whether the user is expected to interact with the widget before the flow continues.
|
|
640
|
-
* Defaults to true. Set to false for informational widgets that should render and then
|
|
641
|
-
* immediately advance to the next flow step.
|
|
642
|
-
*/
|
|
643
|
-
interactive?: boolean;
|
|
644
|
-
/**
|
|
645
|
-
* State key this widget fills — enables auto-skip when the field is already in state.
|
|
646
|
-
* Pass this so the engine can skip the widget step when the answer is already known.
|
|
647
|
-
*/
|
|
648
|
-
field?: string;
|
|
649
|
-
};
|
|
650
|
-
type MaybePromise<T> = T | Promise<T>;
|
|
651
|
-
/** Deep partial — allows partial updates at any nesting level (for z.object state fields). */
|
|
652
|
-
type DeepPartial<T> = {
|
|
653
|
-
[K in keyof T]?: T[K] extends Record<string, unknown> ? DeepPartial<T[K]> : T[K];
|
|
654
|
-
};
|
|
655
|
-
/**
|
|
656
|
-
* Extract known (non-index-signature) string keys from a type.
|
|
657
|
-
* Zod v4's z.object() adds `[key: string]: unknown` to inferred types,
|
|
658
|
-
* so we filter those out to get only the declared field names.
|
|
659
|
-
*/
|
|
660
|
-
type KnownStringKeys<T> = Extract<keyof {
|
|
661
|
-
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K];
|
|
662
|
-
}, string>;
|
|
663
851
|
/**
|
|
664
|
-
*
|
|
665
|
-
*
|
|
666
|
-
*
|
|
667
|
-
*
|
|
668
|
-
*
|
|
852
|
+
* In-memory KvStore implementation.
|
|
853
|
+
*
|
|
854
|
+
* State lives in a `Map` for the lifetime of the process — nothing is
|
|
855
|
+
* persisted, nothing is shared across instances. Use for local development
|
|
856
|
+
* and tests. For production, plug in a Redis/Upstash/CF-KV adapter or set
|
|
857
|
+
* `WANIWANI_API_KEY` to use the hosted store.
|
|
669
858
|
*/
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
859
|
+
|
|
860
|
+
declare class MemoryKvStore<T = Record<string, unknown>> implements KvStore<T> {
|
|
861
|
+
private readonly map;
|
|
862
|
+
get(key: string): Promise<T | null>;
|
|
863
|
+
set(key: string, value: T): Promise<void>;
|
|
864
|
+
delete(key: string): Promise<void>;
|
|
865
|
+
}
|
|
866
|
+
|
|
675
867
|
/**
|
|
676
|
-
*
|
|
677
|
-
*
|
|
678
|
-
* First argument: an object where each key is a field path and each value
|
|
679
|
-
* describes the question for that field. Use dot-paths for nested state fields.
|
|
680
|
-
* `validate` receives the field's value typed from the Zod schema.
|
|
868
|
+
* Server-side API route handler for widget tracking events.
|
|
681
869
|
*
|
|
682
|
-
*
|
|
870
|
+
* Receives batched events from the `useWaniwani` React hook and forwards them
|
|
871
|
+
* to the WaniWani backend using the server-side SDK.
|
|
683
872
|
*
|
|
684
|
-
* @example
|
|
685
|
-
* ```
|
|
686
|
-
* //
|
|
687
|
-
*
|
|
873
|
+
* @example Next.js App Router
|
|
874
|
+
* ```typescript
|
|
875
|
+
* // app/api/waniwani/track/route.ts
|
|
876
|
+
* import { createTrackingRoute } from "@waniwani/sdk/mcp";
|
|
688
877
|
*
|
|
689
|
-
*
|
|
690
|
-
*
|
|
878
|
+
* const handler = createTrackingRoute({
|
|
879
|
+
* apiKey: process.env.WANIWANI_API_KEY,
|
|
880
|
+
* apiUrl: process.env.WANIWANI_API_URL,
|
|
881
|
+
* });
|
|
691
882
|
*
|
|
692
|
-
*
|
|
693
|
-
* interrupt(
|
|
694
|
-
* {
|
|
695
|
-
* "driver.name": { question: "Name?" },
|
|
696
|
-
* "driver.license": { question: "License?" },
|
|
697
|
-
* },
|
|
698
|
-
* { context: "Ask both questions naturally." },
|
|
699
|
-
* )
|
|
883
|
+
* export { handler as POST };
|
|
700
884
|
* ```
|
|
701
885
|
*/
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
};
|
|
709
|
-
}, config?: {
|
|
710
|
-
/** Overall hidden context/instructions for the assistant (not shown to user directly) */
|
|
711
|
-
context?: string;
|
|
712
|
-
}) => InterruptSignal;
|
|
713
|
-
/**
|
|
714
|
-
* Typed showWidget function — available on the node context.
|
|
715
|
-
* The `field` parameter accepts field paths (flat or dot-path for nested state).
|
|
716
|
-
*/
|
|
717
|
-
type TypedShowWidget<TState> = (tool: RegisteredTool | string, config: {
|
|
718
|
-
data: Record<string, unknown>;
|
|
719
|
-
description?: string;
|
|
720
|
-
interactive?: boolean;
|
|
721
|
-
field?: FieldPaths<TState>;
|
|
722
|
-
}) => WidgetSignal;
|
|
723
|
-
/**
|
|
724
|
-
* Context object passed to node handlers.
|
|
725
|
-
* Provides state, metadata, and typed helper functions for creating signals.
|
|
726
|
-
*/
|
|
727
|
-
type NodeContext<TState> = {
|
|
728
|
-
/** Current flow state (partial — fields are filled as the flow progresses) */
|
|
729
|
-
state: Partial<TState>;
|
|
730
|
-
/** Request metadata from the MCP call */
|
|
731
|
-
meta?: Record<string, unknown>;
|
|
732
|
-
/** Create an interrupt signal — pause and ask the user questions */
|
|
733
|
-
interrupt: TypedInterrupt<TState>;
|
|
734
|
-
/** Create a widget signal — pause and show a UI widget */
|
|
735
|
-
showWidget: TypedShowWidget<TState>;
|
|
736
|
-
/** Session-scoped WaniWani client — available when the server is wrapped with withWaniwani() */
|
|
737
|
-
waniwani?: ScopedWaniWaniClient;
|
|
738
|
-
};
|
|
886
|
+
interface TrackingRouteOptions {
|
|
887
|
+
/** API key for the WaniWani backend. Defaults to WANIWANI_API_KEY env var. */
|
|
888
|
+
apiKey?: string;
|
|
889
|
+
/** Base URL for the WaniWani backend. Defaults to https://app.waniwani.ai. */
|
|
890
|
+
apiUrl?: string;
|
|
891
|
+
}
|
|
739
892
|
/**
|
|
740
|
-
*
|
|
741
|
-
*
|
|
742
|
-
* - `Partial<TState>` → action node (state merged, auto-advance)
|
|
743
|
-
* - `InterruptSignal` → interrupt (pause, ask user one or more questions)
|
|
744
|
-
* - `WidgetSignal` → widget step (pause, show widget)
|
|
893
|
+
* Creates a POST handler that receives tracking events from `useWaniwani`
|
|
894
|
+
* and forwards them to the WaniWani backend.
|
|
745
895
|
*/
|
|
746
|
-
|
|
896
|
+
declare function createTrackingRoute(options?: TrackingRouteOptions): (request: Request) => Promise<Response>;
|
|
897
|
+
|
|
747
898
|
/**
|
|
748
|
-
*
|
|
749
|
-
*
|
|
899
|
+
* WaniWani SDK Client
|
|
900
|
+
*
|
|
901
|
+
* Extends with each module:
|
|
902
|
+
* - TrackingClient: track(), flush(), shutdown()
|
|
903
|
+
*
|
|
904
|
+
* Pass this client to framework adapters:
|
|
905
|
+
* - `toNextJsHandler(wani, { ... })` for Next.js route handlers
|
|
750
906
|
*/
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
907
|
+
interface WaniWaniClient extends TrackingClient {
|
|
908
|
+
/** @internal Resolved config — used by framework adapters */
|
|
909
|
+
readonly _config: InternalConfig;
|
|
910
|
+
/** Knowledge base client for ingestion, search, and source listing */
|
|
911
|
+
readonly kb: KbClient;
|
|
912
|
+
}
|
|
913
|
+
interface InternalConfig {
|
|
914
|
+
apiUrl: string;
|
|
915
|
+
apiKey: string | undefined;
|
|
916
|
+
tracking: Required<TrackingConfig>;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
type WaniwaniTracker = Pick<WaniWaniClient, "flush" | "track" | "identify" | "kb" | "_config">;
|
|
920
|
+
|
|
921
|
+
type UnknownRecord = Record<string, unknown>;
|
|
758
922
|
/**
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* Preferred over the positional form `.addNode(id, run, options?)` — metadata
|
|
762
|
-
* sits at the top where the eye lands, and the handler is a named field.
|
|
923
|
+
* Options for withWaniwani().
|
|
763
924
|
*/
|
|
764
|
-
type
|
|
765
|
-
/** Unique node id within the flow. */
|
|
766
|
-
id: TName;
|
|
767
|
-
/** Node handler — receives ctx, returns state updates or a signal. */
|
|
768
|
-
run: NodeHandler<TState>;
|
|
769
|
-
/** Human-readable label for funnel visualization and graphs. Defaults to `id`. */
|
|
770
|
-
label?: string;
|
|
771
|
-
/** When true, this node is excluded from funnel analytics. */
|
|
772
|
-
hideFromFunnel?: boolean;
|
|
773
|
-
};
|
|
774
|
-
type FlowGraphNode = {
|
|
775
|
-
id: string;
|
|
776
|
-
type: "widget" | "interrupt" | "action";
|
|
777
|
-
label: string;
|
|
778
|
-
hideFromFunnel?: boolean;
|
|
779
|
-
};
|
|
780
|
-
type FlowGraphEdge = {
|
|
781
|
-
from: string;
|
|
782
|
-
to: string;
|
|
783
|
-
type: "direct";
|
|
784
|
-
} | {
|
|
785
|
-
from: string;
|
|
786
|
-
to: string[];
|
|
787
|
-
type: "conditional";
|
|
788
|
-
};
|
|
789
|
-
type FlowGraph = {
|
|
790
|
-
flowId: string;
|
|
791
|
-
title: string;
|
|
792
|
-
nodes: FlowGraphNode[];
|
|
793
|
-
edges: FlowGraphEdge[];
|
|
794
|
-
};
|
|
795
|
-
type FlowConfig = {
|
|
796
|
-
/** Unique identifier for the flow (becomes the MCP tool name) */
|
|
797
|
-
id: string;
|
|
798
|
-
/** Display title */
|
|
799
|
-
title: string;
|
|
800
|
-
/** Description for the AI (explains when to use this flow) */
|
|
801
|
-
description: string;
|
|
925
|
+
type WithWaniwaniOptions = {
|
|
802
926
|
/**
|
|
803
|
-
*
|
|
804
|
-
*
|
|
805
|
-
*
|
|
927
|
+
* The WaniWani client instance. When omitted, a client is created
|
|
928
|
+
* automatically using the global config registered by `defineConfig()`,
|
|
929
|
+
* falling back to env vars.
|
|
930
|
+
*/
|
|
931
|
+
client?: WaniwaniTracker;
|
|
932
|
+
/**
|
|
933
|
+
* Optional explicit tool type. Defaults to `"other"`.
|
|
934
|
+
*/
|
|
935
|
+
toolType?: ToolCalledProperties["type"] | ((toolName: string) => ToolCalledProperties["type"] | undefined);
|
|
936
|
+
/**
|
|
937
|
+
* Optional metadata merged into every tracked event.
|
|
938
|
+
*/
|
|
939
|
+
metadata?: UnknownRecord;
|
|
940
|
+
/**
|
|
941
|
+
* Flush tracking transport after each tool call.
|
|
942
|
+
*/
|
|
943
|
+
flushAfterToolCall?: boolean;
|
|
944
|
+
/**
|
|
945
|
+
* Optional error callback for non-fatal tracking errors.
|
|
946
|
+
*/
|
|
947
|
+
onError?: (error: Error) => void;
|
|
948
|
+
/**
|
|
949
|
+
* Inject widget tracking config into tool response `_meta.waniwani` so browser
|
|
950
|
+
* widgets can send events directly to the WaniWani backend.
|
|
806
951
|
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* 2. AI protocol — field names, types, and descriptions are included in the tool
|
|
810
|
-
* description so the AI can pre-fill answers via `_meta.flow.state`
|
|
952
|
+
* Always injects `endpoint`. Injects `token` when an API key is configured
|
|
953
|
+
* and token minting succeeds.
|
|
811
954
|
*
|
|
812
|
-
* @
|
|
813
|
-
* ```ts
|
|
814
|
-
* state: {
|
|
815
|
-
* country: z.string().describe("Country the business is based in"),
|
|
816
|
-
* status: z.enum(["registered", "unregistered"]).describe("Business registration status"),
|
|
817
|
-
* }
|
|
818
|
-
* ```
|
|
955
|
+
* @default true
|
|
819
956
|
*/
|
|
820
|
-
|
|
957
|
+
injectWidgetToken?: boolean;
|
|
821
958
|
/**
|
|
822
|
-
*
|
|
823
|
-
*
|
|
824
|
-
*
|
|
959
|
+
* List of field names to strip from known location `_meta` entries
|
|
960
|
+
* (`openai/userLocation`, `waniwani/geoLocation`, `waniwani/userLocation`)
|
|
961
|
+
* before events are sent to the WaniWani API. Applied to both the
|
|
962
|
+
* request-level `_meta` and any `_meta` on the tool response.
|
|
963
|
+
*
|
|
964
|
+
* Pass e.g. `["latitude", "longitude"]` to drop coordinates only, or
|
|
965
|
+
* `["latitude", "longitude", "city", "region"]` to keep just `country`.
|
|
966
|
+
* Empty/omitted = no redaction.
|
|
967
|
+
*
|
|
968
|
+
* @default []
|
|
825
969
|
*/
|
|
826
|
-
|
|
827
|
-
/**
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
970
|
+
stripLocationFields?: readonly string[];
|
|
971
|
+
/**
|
|
972
|
+
* Replace `input.stateUpdates[field]` with `"REDACTED"` for any field
|
|
973
|
+
* marked via `redacted()` on a flow state schema. When `false` (default),
|
|
974
|
+
* the declarative markers are ignored and raw values are tracked.
|
|
975
|
+
*
|
|
976
|
+
* Wire this to an env var when you want real values in development logs
|
|
977
|
+
* but redacted values in production.
|
|
978
|
+
*
|
|
979
|
+
* @default false
|
|
980
|
+
*/
|
|
981
|
+
applyFieldRedactions?: boolean;
|
|
834
982
|
};
|
|
835
983
|
/**
|
|
836
|
-
*
|
|
984
|
+
* Wrap an MCP server so tool handlers automatically emit `tool.called` events.
|
|
837
985
|
*
|
|
838
|
-
*
|
|
839
|
-
*
|
|
840
|
-
*
|
|
841
|
-
*
|
|
842
|
-
*
|
|
843
|
-
*
|
|
844
|
-
*
|
|
845
|
-
*
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
986
|
+
* The wrapper intercepts `server.registerTool(...)` for future registrations
|
|
987
|
+
* and also walks `server._registeredTools` to wrap any tools already registered
|
|
988
|
+
* at the time of the call. This means either call order works:
|
|
989
|
+
*
|
|
990
|
+
* withWaniwani(server); server.registerTool(...); // wrap then register
|
|
991
|
+
* server.registerTool(...); withWaniwani(server); // register then wrap
|
|
992
|
+
*
|
|
993
|
+
* When `injectWidgetToken` is enabled (default), tracking config is injected
|
|
994
|
+
* into tool response `_meta.waniwani` so browser widgets can post events
|
|
995
|
+
* directly to the WaniWani backend without a server-side proxy.
|
|
996
|
+
*
|
|
997
|
+
* Widget metadata declared on the tool **definition** (e.g. skybridge's
|
|
998
|
+
* `registerWidget`, raw MCP `_meta["ui/resourceUri"]` / `_meta.ui.resourceUri`,
|
|
999
|
+
* OpenAI's `_meta["openai/outputTemplate"]`) is also forwarded into each tool
|
|
1000
|
+
* result's `_meta`, so chat UIs that only see tool results (and not
|
|
1001
|
+
* `tools/list`) can still render widgets. Handler-set keys take precedence.
|
|
849
1002
|
*/
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
};
|
|
1003
|
+
declare function withWaniwani(server: McpServer, options?: WithWaniwaniOptions): Promise<McpServer>;
|
|
1004
|
+
|
|
853
1005
|
/**
|
|
854
|
-
*
|
|
855
|
-
* so it's assignable to both MCP SDK's `ToolCallback` and Skybridge's `ToolHandler`.
|
|
1006
|
+
* Widget platform types
|
|
856
1007
|
*/
|
|
857
|
-
type
|
|
1008
|
+
type WidgetPlatform = "openai" | "mcp-apps";
|
|
858
1009
|
/**
|
|
859
|
-
*
|
|
1010
|
+
* Detects which platform the widget is running on.
|
|
860
1011
|
*
|
|
861
|
-
*
|
|
862
|
-
*
|
|
1012
|
+
* OpenAI injects a global `window.openai` object.
|
|
1013
|
+
* MCP Apps runs in a sandboxed iframe and uses postMessage.
|
|
1014
|
+
*
|
|
1015
|
+
* @deprecated Legacy MCP-widget-in-host stack. Preserved for back-compat; will move to
|
|
1016
|
+
* `@waniwani/sdk/legacy/react` in a future minor release.
|
|
863
1017
|
*/
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
graph: () => string;
|
|
885
|
-
flowGraph: FlowGraph;
|
|
1018
|
+
declare function detectPlatform(): WidgetPlatform;
|
|
1019
|
+
/**
|
|
1020
|
+
* Check if running on OpenAI platform.
|
|
1021
|
+
*
|
|
1022
|
+
* @deprecated Legacy MCP-widget-in-host stack. Preserved for back-compat; will move to
|
|
1023
|
+
* `@waniwani/sdk/legacy/react` in a future minor release.
|
|
1024
|
+
*/
|
|
1025
|
+
declare function isOpenAI(): boolean;
|
|
1026
|
+
/**
|
|
1027
|
+
* Check if running on MCP Apps platform.
|
|
1028
|
+
*
|
|
1029
|
+
* @deprecated Legacy MCP-widget-in-host stack. Preserved for back-compat; will move to
|
|
1030
|
+
* `@waniwani/sdk/legacy/react` in a future minor release.
|
|
1031
|
+
*/
|
|
1032
|
+
declare function isMCPApps(): boolean;
|
|
1033
|
+
|
|
1034
|
+
type ModelContextContentBlock = ContentBlock;
|
|
1035
|
+
type ModelContextUpdate = {
|
|
1036
|
+
content?: ModelContextContentBlock[];
|
|
1037
|
+
structuredContent?: Record<string, unknown>;
|
|
886
1038
|
};
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
1039
|
+
|
|
1040
|
+
/**
|
|
1041
|
+
* Source: https://github.com/openai/openai-apps-sdk-examples/tree/main/src
|
|
1042
|
+
*/
|
|
1043
|
+
type OpenAIGlobals<ToolInput = UnknownObject, ToolOutput = UnknownObject, ToolResponseMetadata = UnknownObject, WidgetState = UnknownObject> = {
|
|
1044
|
+
theme: Theme;
|
|
1045
|
+
userAgent: UserAgent;
|
|
1046
|
+
locale: string;
|
|
1047
|
+
maxHeight: number;
|
|
1048
|
+
displayMode: DisplayMode;
|
|
1049
|
+
safeArea: SafeArea;
|
|
1050
|
+
toolInput: ToolInput;
|
|
1051
|
+
toolOutput: ToolOutput | null;
|
|
1052
|
+
toolResponseMetadata: ToolResponseMetadata | null;
|
|
1053
|
+
widgetState: WidgetState | null;
|
|
1054
|
+
setWidgetState: (state: WidgetState) => Promise<void>;
|
|
892
1055
|
};
|
|
893
|
-
type
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
1056
|
+
type API = {
|
|
1057
|
+
callTool: CallTool;
|
|
1058
|
+
sendFollowUpMessage: (args: {
|
|
1059
|
+
prompt: string;
|
|
1060
|
+
}) => Promise<void>;
|
|
1061
|
+
openExternal(payload: {
|
|
1062
|
+
href: string;
|
|
1063
|
+
}): void;
|
|
1064
|
+
requestDisplayMode: RequestDisplayMode;
|
|
898
1065
|
};
|
|
899
|
-
type
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
questions?: InterruptQuestionData[];
|
|
907
|
-
context?: string;
|
|
1066
|
+
type UnknownObject = Record<string, unknown>;
|
|
1067
|
+
type Theme = "light" | "dark";
|
|
1068
|
+
type SafeAreaInsets = {
|
|
1069
|
+
top: number;
|
|
1070
|
+
bottom: number;
|
|
1071
|
+
left: number;
|
|
1072
|
+
right: number;
|
|
908
1073
|
};
|
|
909
|
-
type
|
|
910
|
-
|
|
911
|
-
/** Display tool to call */
|
|
912
|
-
tool: string;
|
|
913
|
-
/** Data to pass to the display tool */
|
|
914
|
-
data: Record<string, unknown>;
|
|
915
|
-
description?: string;
|
|
916
|
-
/** Whether the widget expects user interaction before continuing */
|
|
917
|
-
interactive?: boolean;
|
|
1074
|
+
type SafeArea = {
|
|
1075
|
+
insets: SafeAreaInsets;
|
|
918
1076
|
};
|
|
919
|
-
type
|
|
920
|
-
|
|
1077
|
+
type DeviceType = "mobile" | "tablet" | "desktop" | "unknown";
|
|
1078
|
+
type UserAgent = {
|
|
1079
|
+
device: {
|
|
1080
|
+
type: DeviceType;
|
|
1081
|
+
};
|
|
1082
|
+
capabilities: {
|
|
1083
|
+
hover: boolean;
|
|
1084
|
+
touch: boolean;
|
|
1085
|
+
};
|
|
921
1086
|
};
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1087
|
+
/** Display mode */
|
|
1088
|
+
type DisplayMode = "pip" | "inline" | "fullscreen";
|
|
1089
|
+
type RequestDisplayMode = (args: {
|
|
1090
|
+
mode: DisplayMode;
|
|
1091
|
+
}) => Promise<{
|
|
1092
|
+
/**
|
|
1093
|
+
* The granted display mode. The host may reject the request.
|
|
1094
|
+
* For mobile, PiP is always coerced to fullscreen.
|
|
1095
|
+
*/
|
|
1096
|
+
mode: DisplayMode;
|
|
1097
|
+
}>;
|
|
1098
|
+
type CallToolResponse = {
|
|
1099
|
+
result: string;
|
|
925
1100
|
};
|
|
1101
|
+
/** Calling APIs */
|
|
1102
|
+
type CallTool = (name: string, args: Record<string, unknown>) => Promise<CallToolResponse>;
|
|
1103
|
+
/** Extra events */
|
|
1104
|
+
declare const SET_GLOBALS_EVENT_TYPE = "openai:set_globals";
|
|
1105
|
+
declare class SetGlobalsEvent extends CustomEvent<{
|
|
1106
|
+
globals: Partial<OpenAIGlobals>;
|
|
1107
|
+
}> {
|
|
1108
|
+
constructor(detail: {
|
|
1109
|
+
globals: Partial<OpenAIGlobals>;
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* Global oai object injected by the web sandbox for communicating with chatgpt host page.
|
|
1114
|
+
*/
|
|
1115
|
+
declare global {
|
|
1116
|
+
interface Window {
|
|
1117
|
+
openai: API & OpenAIGlobals;
|
|
1118
|
+
innerBaseUrl: string;
|
|
1119
|
+
}
|
|
1120
|
+
interface WindowEventMap {
|
|
1121
|
+
[SET_GLOBALS_EVENT_TYPE]: SetGlobalsEvent;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
926
1124
|
|
|
927
1125
|
/**
|
|
928
|
-
*
|
|
1126
|
+
* Result from calling a tool
|
|
1127
|
+
*/
|
|
1128
|
+
type ToolCallResult = CallToolResult;
|
|
1129
|
+
/**
|
|
1130
|
+
* Tool result notification (what the host pushes to the widget)
|
|
1131
|
+
*/
|
|
1132
|
+
type ToolResult = CallToolResult;
|
|
1133
|
+
/**
|
|
1134
|
+
* Host context - all values available from the host.
|
|
1135
|
+
*/
|
|
1136
|
+
type HostContext = {
|
|
1137
|
+
theme: Theme;
|
|
1138
|
+
locale: string;
|
|
1139
|
+
displayMode: DisplayMode;
|
|
1140
|
+
maxHeight: number | null;
|
|
1141
|
+
safeArea: SafeArea | null;
|
|
1142
|
+
toolOutput: UnknownObject | null;
|
|
1143
|
+
toolResponseMetadata: UnknownObject | null;
|
|
1144
|
+
widgetState: UnknownObject | null;
|
|
1145
|
+
};
|
|
1146
|
+
/**
|
|
1147
|
+
* Unified widget client interface that works on both OpenAI and MCP Apps.
|
|
929
1148
|
*
|
|
930
|
-
*
|
|
931
|
-
*
|
|
932
|
-
*
|
|
933
|
-
* id: "onboarding",
|
|
934
|
-
* title: "User Onboarding",
|
|
935
|
-
* description: "Guides users through onboarding",
|
|
936
|
-
* })
|
|
937
|
-
* .addNode({
|
|
938
|
-
* id: "ask_name",
|
|
939
|
-
* run: ({ interrupt }) => interrupt({ question: "What's your name?", field: "name" }),
|
|
940
|
-
* })
|
|
941
|
-
* .addNode({
|
|
942
|
-
* id: "greet",
|
|
943
|
-
* run: ({ state }) => ({ greeting: `Hello ${state.name}!` }),
|
|
944
|
-
* })
|
|
945
|
-
* .addEdge(START, "ask_name")
|
|
946
|
-
* .addEdge("ask_name", "greet")
|
|
947
|
-
* .addEdge("greet", END)
|
|
948
|
-
* .compile();
|
|
949
|
-
* ```
|
|
1149
|
+
* Platform-specific behavior:
|
|
1150
|
+
* - Display mode: OpenAI-only. MCP Apps returns "inline" and requestDisplayMode is a no-op.
|
|
1151
|
+
* - Follow-up messages: Unified API, different underlying implementations.
|
|
950
1152
|
*/
|
|
951
|
-
|
|
952
|
-
private nodes;
|
|
953
|
-
private edges;
|
|
954
|
-
private nodeOptions;
|
|
955
|
-
private config;
|
|
956
|
-
constructor(config: FlowConfig);
|
|
1153
|
+
interface UnifiedWidgetClient {
|
|
957
1154
|
/**
|
|
958
|
-
*
|
|
959
|
-
*
|
|
960
|
-
*
|
|
961
|
-
*
|
|
962
|
-
* @example
|
|
963
|
-
* ```ts
|
|
964
|
-
* .addNode({
|
|
965
|
-
* id: "ask_name",
|
|
966
|
-
* label: "Ask for name",
|
|
967
|
-
* run: ({ interrupt }) => interrupt({ question: "What's your name?", field: "name" }),
|
|
968
|
-
* })
|
|
969
|
-
* ```
|
|
1155
|
+
* Connect to the host. Must be called before using other methods.
|
|
1156
|
+
* On OpenAI, this is a no-op (already connected via window.openai).
|
|
1157
|
+
* On MCP Apps, this establishes the postMessage connection.
|
|
970
1158
|
*/
|
|
971
|
-
|
|
1159
|
+
connect(): Promise<void>;
|
|
972
1160
|
/**
|
|
973
|
-
*
|
|
974
|
-
*
|
|
1161
|
+
* Close the connection to the host and clean up resources.
|
|
1162
|
+
* On OpenAI, this is a no-op.
|
|
1163
|
+
* On MCP Apps, this closes the transport and removes event listeners.
|
|
975
1164
|
*/
|
|
976
|
-
|
|
1165
|
+
close(): Promise<void>;
|
|
977
1166
|
/**
|
|
978
|
-
*
|
|
979
|
-
*
|
|
980
|
-
* Use `START` as `from` to set the entry point.
|
|
981
|
-
* Use `END` as `to` to mark a terminal node.
|
|
1167
|
+
* Get the tool output (structured content returned by the tool handler).
|
|
1168
|
+
* This is the main data source for widget rendering.
|
|
982
1169
|
*/
|
|
983
|
-
|
|
1170
|
+
getToolOutput<T = Record<string, unknown>>(): T | null;
|
|
984
1171
|
/**
|
|
985
|
-
*
|
|
986
|
-
*
|
|
987
|
-
*
|
|
1172
|
+
* Register a callback for when tool results are received.
|
|
1173
|
+
* On OpenAI, this subscribes to toolOutput changes.
|
|
1174
|
+
* On MCP Apps, this sets app.ontoolresult.
|
|
988
1175
|
*/
|
|
989
|
-
|
|
1176
|
+
onToolResult(callback: (result: ToolResult) => void): () => void;
|
|
990
1177
|
/**
|
|
991
|
-
*
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
*
|
|
996
|
-
*
|
|
997
|
-
*
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
*
|
|
1002
|
-
*
|
|
1003
|
-
*
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
*
|
|
1178
|
+
* Call another tool on the server.
|
|
1179
|
+
*/
|
|
1180
|
+
callTool(name: string, args: Record<string, unknown>): Promise<ToolCallResult>;
|
|
1181
|
+
/**
|
|
1182
|
+
* Open an external URL.
|
|
1183
|
+
* On OpenAI: openai.openExternal({ href })
|
|
1184
|
+
* On MCP Apps: app.sendOpenLink(url)
|
|
1185
|
+
*/
|
|
1186
|
+
openExternal(url: string): void;
|
|
1187
|
+
/**
|
|
1188
|
+
* Send a follow-up message to the AI.
|
|
1189
|
+
* On OpenAI: openai.sendFollowUpMessage({ prompt })
|
|
1190
|
+
* On MCP Apps: app.sendMessages([{ role: 'user', content: { type: 'text', text: prompt } }])
|
|
1191
|
+
*/
|
|
1192
|
+
sendFollowUp(prompt: string): void | Promise<void>;
|
|
1193
|
+
/**
|
|
1194
|
+
* Update hidden model context for the next assistant turn.
|
|
1195
|
+
* On MCP Apps this uses the standard `ui/update-model-context` request.
|
|
1196
|
+
* On other hosts this may fall back to best-effort behavior.
|
|
1197
|
+
*/
|
|
1198
|
+
updateModelContext(context: ModelContextUpdate): Promise<void> | void;
|
|
1199
|
+
/**
|
|
1200
|
+
* Get the current theme.
|
|
1201
|
+
*/
|
|
1202
|
+
getTheme(): Theme;
|
|
1203
|
+
/**
|
|
1204
|
+
* Subscribe to theme changes.
|
|
1205
|
+
*/
|
|
1206
|
+
onThemeChange(callback: (theme: Theme) => void): () => void;
|
|
1207
|
+
/**
|
|
1208
|
+
* Get the current locale.
|
|
1209
|
+
*/
|
|
1210
|
+
getLocale(): string;
|
|
1211
|
+
/**
|
|
1212
|
+
* Get the current display mode.
|
|
1213
|
+
* OpenAI-only: returns "pip" | "inline" | "fullscreen"
|
|
1214
|
+
* MCP Apps: always returns "inline"
|
|
1215
|
+
*/
|
|
1216
|
+
getDisplayMode(): DisplayMode;
|
|
1217
|
+
/**
|
|
1218
|
+
* Request a display mode change.
|
|
1219
|
+
* OpenAI-only: requests the mode from the host.
|
|
1220
|
+
* MCP Apps: no-op (returns current mode).
|
|
1221
|
+
*/
|
|
1222
|
+
requestDisplayMode(mode: DisplayMode): Promise<DisplayMode>;
|
|
1223
|
+
/**
|
|
1224
|
+
* Subscribe to display mode changes.
|
|
1225
|
+
* OpenAI-only: subscribes to displayMode changes.
|
|
1226
|
+
* MCP Apps: callback is never called.
|
|
1227
|
+
*/
|
|
1228
|
+
onDisplayModeChange(callback: (mode: DisplayMode) => void): () => void;
|
|
1229
|
+
/**
|
|
1230
|
+
* Get the safe area insets.
|
|
1231
|
+
* OpenAI-only: returns insets from window.openai.safeArea.
|
|
1232
|
+
* MCP Apps: returns null.
|
|
1233
|
+
*/
|
|
1234
|
+
getSafeArea(): SafeArea | null;
|
|
1235
|
+
/**
|
|
1236
|
+
* Subscribe to safe area changes.
|
|
1237
|
+
* OpenAI-only: subscribes to safeArea changes.
|
|
1238
|
+
* MCP Apps: callback is never called.
|
|
1239
|
+
*/
|
|
1240
|
+
onSafeAreaChange(callback: (safeArea: SafeArea | null) => void): () => void;
|
|
1241
|
+
/**
|
|
1242
|
+
* Get the max height constraint.
|
|
1243
|
+
* OpenAI-only: returns maxHeight from window.openai.maxHeight.
|
|
1244
|
+
* MCP Apps: returns null.
|
|
1245
|
+
*/
|
|
1246
|
+
getMaxHeight(): number | null;
|
|
1247
|
+
/**
|
|
1248
|
+
* Subscribe to max height changes.
|
|
1249
|
+
* OpenAI-only: subscribes to maxHeight changes.
|
|
1250
|
+
* MCP Apps: callback is never called.
|
|
1251
|
+
*/
|
|
1252
|
+
onMaxHeightChange(callback: (maxHeight: number | null) => void): () => void;
|
|
1253
|
+
/**
|
|
1254
|
+
* Get the tool response metadata.
|
|
1255
|
+
* OpenAI: returns metadata from window.openai.toolResponseMetadata.
|
|
1256
|
+
* MCP Apps: returns `_meta` from the latest `ui/notifications/tool-result`, if provided by host.
|
|
1257
|
+
*/
|
|
1258
|
+
getToolResponseMetadata(): UnknownObject | null;
|
|
1259
|
+
/**
|
|
1260
|
+
* Subscribe to tool response metadata changes.
|
|
1261
|
+
* OpenAI: subscribes to toolResponseMetadata changes.
|
|
1262
|
+
* MCP Apps: fires when tool result `_meta` changes.
|
|
1008
1263
|
*/
|
|
1009
|
-
|
|
1264
|
+
onToolResponseMetadataChange(callback: (metadata: UnknownObject | null) => void): () => void;
|
|
1010
1265
|
/**
|
|
1011
|
-
*
|
|
1012
|
-
*
|
|
1013
|
-
*
|
|
1266
|
+
* Get the widget state.
|
|
1267
|
+
* OpenAI-only: returns state from window.openai.widgetState.
|
|
1268
|
+
* MCP Apps: returns null.
|
|
1014
1269
|
*/
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
* Create a new flow graph — convenience factory for `new StateGraph()`.
|
|
1023
|
-
*
|
|
1024
|
-
* The state type is automatically inferred from the `state` definition —
|
|
1025
|
-
* no explicit generic parameter needed.
|
|
1026
|
-
*
|
|
1027
|
-
* @example
|
|
1028
|
-
* ```ts
|
|
1029
|
-
* import { createFlow, interrupt, START, END } from "@waniwani/sdk/mcp";
|
|
1030
|
-
* import { z } from "zod";
|
|
1031
|
-
*
|
|
1032
|
-
* const flow = createFlow({
|
|
1033
|
-
* id: "onboarding",
|
|
1034
|
-
* title: "User Onboarding",
|
|
1035
|
-
* description: "Guides users through onboarding. Use when a user wants to get started.",
|
|
1036
|
-
* state: {
|
|
1037
|
-
* name: z.string().describe("The user's name"),
|
|
1038
|
-
* email: z.string().describe("The user's email address"),
|
|
1039
|
-
* },
|
|
1040
|
-
* })
|
|
1041
|
-
* .addNode({
|
|
1042
|
-
* id: "ask_name",
|
|
1043
|
-
* run: () => interrupt({ question: "What's your name?", field: "name" }),
|
|
1044
|
-
* })
|
|
1045
|
-
* .addNode({
|
|
1046
|
-
* id: "ask_email",
|
|
1047
|
-
* run: () => interrupt({ question: "What's your email?", field: "email" }),
|
|
1048
|
-
* })
|
|
1049
|
-
* .addEdge(START, "ask_name")
|
|
1050
|
-
* .addEdge("ask_name", "ask_email")
|
|
1051
|
-
* .addEdge("ask_email", END)
|
|
1052
|
-
* .compile();
|
|
1053
|
-
* ```
|
|
1054
|
-
*/
|
|
1055
|
-
declare function createFlow<const TSchema extends Record<string, z.ZodType>>(config: Omit<FlowConfig, "state"> & {
|
|
1056
|
-
state: TSchema;
|
|
1057
|
-
}): StateGraph<InferFlowState<TSchema>>;
|
|
1058
|
-
|
|
1059
|
-
/**
|
|
1060
|
-
* Mark a Zod schema as PII — its value will be replaced with `"REDACTED"` in
|
|
1061
|
-
* any `tool.called` event payload sent to the WaniWani API. The handler still
|
|
1062
|
-
* receives the original value; only the tracked copy is scrubbed.
|
|
1063
|
-
*
|
|
1064
|
-
* Apply at the end of the schema chain so the marker is attached to the final
|
|
1065
|
-
* schema:
|
|
1066
|
-
*
|
|
1067
|
-
* ```ts
|
|
1068
|
-
* ages: redacted(z.string().describe("Comma-separated ages")),
|
|
1069
|
-
* zipcode: redacted(z.string().describe("Spanish postal code")),
|
|
1070
|
-
* ```
|
|
1071
|
-
*
|
|
1072
|
-
* Uses Zod v4's `.meta()` registry, so the marker is preserved across schema
|
|
1073
|
-
* clones (`.optional()`, `.default()`, etc.).
|
|
1074
|
-
*/
|
|
1075
|
-
declare function redacted<T extends z.ZodType>(schema: T): T;
|
|
1076
|
-
|
|
1077
|
-
type WithDecodedState = {
|
|
1078
|
-
decodedState: FlowTokenContent | null;
|
|
1079
|
-
};
|
|
1080
|
-
type FlowTestResult = (FlowInterruptContent & WithDecodedState) | (FlowWidgetContent & WithDecodedState) | (FlowCompleteContent & WithDecodedState) | (FlowErrorContent & WithDecodedState);
|
|
1081
|
-
declare function createFlowTestHarness(flow: RegisteredFlow, options?: {
|
|
1082
|
-
stateStore?: FlowStore;
|
|
1083
|
-
}): Promise<{
|
|
1084
|
-
start(intent: string, stateUpdates?: Record<string, unknown>, context?: string): Promise<FlowTestResult>;
|
|
1085
|
-
continueWith(stateUpdates?: Record<string, unknown>): Promise<FlowTestResult>;
|
|
1086
|
-
resetWith(stateUpdates: Record<string, unknown>): Promise<FlowTestResult>;
|
|
1087
|
-
lastState(): Promise<FlowTokenContent | null>;
|
|
1088
|
-
}>;
|
|
1089
|
-
|
|
1090
|
-
/**
|
|
1091
|
-
* Generic key-value store backed by the WaniWani API.
|
|
1092
|
-
*
|
|
1093
|
-
* Values are stored as JSON objects (`Record<string, unknown>`) in the
|
|
1094
|
-
* `/api/mcp/redis/*` endpoints. Tenant isolation is handled by the API key.
|
|
1095
|
-
*
|
|
1096
|
-
* Config is read from env vars:
|
|
1097
|
-
* - `WANIWANI_API_KEY` (required)
|
|
1098
|
-
* - `WANIWANI_API_URL` (optional, defaults to https://app.waniwani.ai)
|
|
1099
|
-
* - `WANIWANI_ENCRYPTION_KEY` (optional, base64-encoded 32-byte key for AES-256-GCM encryption)
|
|
1100
|
-
*/
|
|
1101
|
-
interface KvStore<T = Record<string, unknown>> {
|
|
1102
|
-
get(key: string): Promise<T | null>;
|
|
1103
|
-
set(key: string, value: T): Promise<void>;
|
|
1104
|
-
delete(key: string): Promise<void>;
|
|
1105
|
-
}
|
|
1106
|
-
declare class WaniwaniKvStore<T = Record<string, unknown>> implements KvStore<T> {
|
|
1107
|
-
private get baseUrl();
|
|
1108
|
-
private get apiKey();
|
|
1109
|
-
private get encryptionKey();
|
|
1110
|
-
get(key: string): Promise<T | null>;
|
|
1111
|
-
set(key: string, value: T): Promise<void>;
|
|
1112
|
-
delete(key: string): Promise<void>;
|
|
1113
|
-
private request;
|
|
1270
|
+
getWidgetState(): UnknownObject | null;
|
|
1271
|
+
/**
|
|
1272
|
+
* Subscribe to widget state changes.
|
|
1273
|
+
* OpenAI-only: subscribes to widgetState changes.
|
|
1274
|
+
* MCP Apps: callback is never called.
|
|
1275
|
+
*/
|
|
1276
|
+
onWidgetStateChange(callback: (state: UnknownObject | null) => void): () => void;
|
|
1114
1277
|
}
|
|
1115
1278
|
|
|
1116
1279
|
/**
|
|
1117
1280
|
* Creates a reusable UI resource (HTML template) that can be attached
|
|
1118
1281
|
* to tools or flow nodes.
|
|
1119
1282
|
*
|
|
1283
|
+
* @deprecated Prefer `createFlow` with `showWidget` from `@waniwani/sdk/mcp` for new code.
|
|
1284
|
+
* `createResource` is preserved for back-compat with existing customer MCPs but is no longer
|
|
1285
|
+
* documented; it will move to `@waniwani/sdk/legacy` in a future minor release.
|
|
1286
|
+
*
|
|
1120
1287
|
* @example
|
|
1121
1288
|
* ```ts
|
|
1122
1289
|
* const pricingUI = createResource({
|
|
@@ -1138,6 +1305,10 @@ declare function createResource(config: ResourceConfig): RegisteredResource;
|
|
|
1138
1305
|
* When `handler()` returns `data`, the tool includes it as MCP `structuredContent`.
|
|
1139
1306
|
* When `config.resource` is provided, the tool also returns widget metadata.
|
|
1140
1307
|
*
|
|
1308
|
+
* @deprecated Prefer `createFlow` from `@waniwani/sdk/mcp` for new code. `createTool`
|
|
1309
|
+
* is preserved for back-compat with existing customer MCPs but is no longer documented;
|
|
1310
|
+
* it will move to `@waniwani/sdk/legacy` in a future minor release.
|
|
1311
|
+
*
|
|
1141
1312
|
* @example
|
|
1142
1313
|
* ```ts
|
|
1143
1314
|
* // Widget tool (with resource)
|
|
@@ -1163,146 +1334,11 @@ declare function createResource(config: ResourceConfig): RegisteredResource;
|
|
|
1163
1334
|
*/
|
|
1164
1335
|
declare function createTool<TInput extends z.ZodRawShape>(config: ToolConfig<TInput>, handler: ToolHandler<TInput>): RegisteredTool;
|
|
1165
1336
|
/**
|
|
1166
|
-
* Registers multiple tools on the server
|
|
1167
|
-
*/
|
|
1168
|
-
declare function registerTools(server: McpServer, tools: RegisteredTool[]): Promise<void>;
|
|
1169
|
-
|
|
1170
|
-
/**
|
|
1171
|
-
* Server-side API route handler for widget tracking events.
|
|
1172
|
-
*
|
|
1173
|
-
* Receives batched events from the `useWaniwani` React hook and forwards them
|
|
1174
|
-
* to the WaniWani backend using the server-side SDK.
|
|
1175
|
-
*
|
|
1176
|
-
* @example Next.js App Router
|
|
1177
|
-
* ```typescript
|
|
1178
|
-
* // app/api/waniwani/track/route.ts
|
|
1179
|
-
* import { createTrackingRoute } from "@waniwani/sdk/mcp";
|
|
1180
|
-
*
|
|
1181
|
-
* const handler = createTrackingRoute({
|
|
1182
|
-
* apiKey: process.env.WANIWANI_API_KEY,
|
|
1183
|
-
* apiUrl: process.env.WANIWANI_API_URL,
|
|
1184
|
-
* });
|
|
1185
|
-
*
|
|
1186
|
-
* export { handler as POST };
|
|
1187
|
-
* ```
|
|
1188
|
-
*/
|
|
1189
|
-
interface TrackingRouteOptions {
|
|
1190
|
-
/** API key for the WaniWani backend. Defaults to WANIWANI_API_KEY env var. */
|
|
1191
|
-
apiKey?: string;
|
|
1192
|
-
/** Base URL for the WaniWani backend. Defaults to https://app.waniwani.ai. */
|
|
1193
|
-
apiUrl?: string;
|
|
1194
|
-
}
|
|
1195
|
-
/**
|
|
1196
|
-
* Creates a POST handler that receives tracking events from `useWaniwani`
|
|
1197
|
-
* and forwards them to the WaniWani backend.
|
|
1198
|
-
*/
|
|
1199
|
-
declare function createTrackingRoute(options?: TrackingRouteOptions): (request: Request) => Promise<Response>;
|
|
1200
|
-
|
|
1201
|
-
/**
|
|
1202
|
-
* WaniWani SDK Client
|
|
1203
|
-
*
|
|
1204
|
-
* Extends with each module:
|
|
1205
|
-
* - TrackingClient: track(), flush(), shutdown()
|
|
1206
|
-
*
|
|
1207
|
-
* Pass this client to framework adapters:
|
|
1208
|
-
* - `toNextJsHandler(wani, { ... })` for Next.js route handlers
|
|
1209
|
-
*/
|
|
1210
|
-
interface WaniWaniClient extends TrackingClient {
|
|
1211
|
-
/** @internal Resolved config — used by framework adapters */
|
|
1212
|
-
readonly _config: InternalConfig;
|
|
1213
|
-
/** Knowledge base client for ingestion, search, and source listing */
|
|
1214
|
-
readonly kb: KbClient;
|
|
1215
|
-
}
|
|
1216
|
-
interface InternalConfig {
|
|
1217
|
-
apiUrl: string;
|
|
1218
|
-
apiKey: string | undefined;
|
|
1219
|
-
tracking: Required<TrackingConfig>;
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
type WaniwaniTracker = Pick<WaniWaniClient, "flush" | "track" | "identify" | "kb" | "_config">;
|
|
1223
|
-
|
|
1224
|
-
type UnknownRecord = Record<string, unknown>;
|
|
1225
|
-
/**
|
|
1226
|
-
* Options for withWaniwani().
|
|
1227
|
-
*/
|
|
1228
|
-
type WithWaniwaniOptions = {
|
|
1229
|
-
/**
|
|
1230
|
-
* The WaniWani client instance. When omitted, a client is created
|
|
1231
|
-
* automatically using the global config registered by `defineConfig()`,
|
|
1232
|
-
* falling back to env vars.
|
|
1233
|
-
*/
|
|
1234
|
-
client?: WaniwaniTracker;
|
|
1235
|
-
/**
|
|
1236
|
-
* Optional explicit tool type. Defaults to `"other"`.
|
|
1237
|
-
*/
|
|
1238
|
-
toolType?: ToolCalledProperties["type"] | ((toolName: string) => ToolCalledProperties["type"] | undefined);
|
|
1239
|
-
/**
|
|
1240
|
-
* Optional metadata merged into every tracked event.
|
|
1241
|
-
*/
|
|
1242
|
-
metadata?: UnknownRecord;
|
|
1243
|
-
/**
|
|
1244
|
-
* Flush tracking transport after each tool call.
|
|
1245
|
-
*/
|
|
1246
|
-
flushAfterToolCall?: boolean;
|
|
1247
|
-
/**
|
|
1248
|
-
* Optional error callback for non-fatal tracking errors.
|
|
1249
|
-
*/
|
|
1250
|
-
onError?: (error: Error) => void;
|
|
1251
|
-
/**
|
|
1252
|
-
* Inject widget tracking config into tool response `_meta.waniwani` so browser
|
|
1253
|
-
* widgets can send events directly to the WaniWani backend.
|
|
1254
|
-
*
|
|
1255
|
-
* Always injects `endpoint`. Injects `token` when an API key is configured
|
|
1256
|
-
* and token minting succeeds.
|
|
1257
|
-
*
|
|
1258
|
-
* @default true
|
|
1259
|
-
*/
|
|
1260
|
-
injectWidgetToken?: boolean;
|
|
1261
|
-
/**
|
|
1262
|
-
* List of field names to strip from known location `_meta` entries
|
|
1263
|
-
* (`openai/userLocation`, `waniwani/geoLocation`, `waniwani/userLocation`)
|
|
1264
|
-
* before events are sent to the WaniWani API. Applied to both the
|
|
1265
|
-
* request-level `_meta` and any `_meta` on the tool response.
|
|
1266
|
-
*
|
|
1267
|
-
* Pass e.g. `["latitude", "longitude"]` to drop coordinates only, or
|
|
1268
|
-
* `["latitude", "longitude", "city", "region"]` to keep just `country`.
|
|
1269
|
-
* Empty/omitted = no redaction.
|
|
1270
|
-
*
|
|
1271
|
-
* @default []
|
|
1272
|
-
*/
|
|
1273
|
-
stripLocationFields?: readonly string[];
|
|
1274
|
-
/**
|
|
1275
|
-
* Replace `input.stateUpdates[field]` with `"REDACTED"` for any field
|
|
1276
|
-
* marked via `redacted()` on a flow state schema. When `false` (default),
|
|
1277
|
-
* the declarative markers are ignored and raw values are tracked.
|
|
1278
|
-
*
|
|
1279
|
-
* Wire this to an env var when you want real values in development logs
|
|
1280
|
-
* but redacted values in production.
|
|
1281
|
-
*
|
|
1282
|
-
* @default false
|
|
1283
|
-
*/
|
|
1284
|
-
applyFieldRedactions?: boolean;
|
|
1285
|
-
};
|
|
1286
|
-
/**
|
|
1287
|
-
* Wrap an MCP server so tool handlers automatically emit `tool.called` events.
|
|
1288
|
-
*
|
|
1289
|
-
* The wrapper intercepts `server.registerTool(...)` for future registrations
|
|
1290
|
-
* and also walks `server._registeredTools` to wrap any tools already registered
|
|
1291
|
-
* at the time of the call. This means either call order works:
|
|
1292
|
-
*
|
|
1293
|
-
* withWaniwani(server); server.registerTool(...); // wrap then register
|
|
1294
|
-
* server.registerTool(...); withWaniwani(server); // register then wrap
|
|
1295
|
-
*
|
|
1296
|
-
* When `injectWidgetToken` is enabled (default), tracking config is injected
|
|
1297
|
-
* into tool response `_meta.waniwani` so browser widgets can post events
|
|
1298
|
-
* directly to the WaniWani backend without a server-side proxy.
|
|
1337
|
+
* Registers multiple tools on the server.
|
|
1299
1338
|
*
|
|
1300
|
-
*
|
|
1301
|
-
*
|
|
1302
|
-
* OpenAI's `_meta["openai/outputTemplate"]`) is also forwarded into each tool
|
|
1303
|
-
* result's `_meta`, so chat UIs that only see tool results (and not
|
|
1304
|
-
* `tools/list`) can still render widgets. Handler-set keys take precedence.
|
|
1339
|
+
* @deprecated Prefer `createFlow` + `.register()` for new code. Preserved for back-compat
|
|
1340
|
+
* with existing customer MCPs; will move to `@waniwani/sdk/legacy` in a future minor release.
|
|
1305
1341
|
*/
|
|
1306
|
-
declare function
|
|
1342
|
+
declare function registerTools(server: McpServer, tools: RegisteredTool[]): Promise<void>;
|
|
1307
1343
|
|
|
1308
|
-
export { type AddNodeConfig, type ConditionFn, END, type FlowConfig, type FlowTestResult, type HostContext, type InferFlowState, type InterruptSignal, type KvStore, type NodeContext, type NodeHandler, type RegisteredFlow, type RegisteredResource, type RegisteredTool, type ResourceConfig, START, type ScopedWaniWaniClient, StateGraph, type ToolCallResult, type ToolConfig, type ToolHandler, type ToolHandlerContext, type ToolResult, type ToolToolCallback, type TrackingRouteOptions, type TypedInterrupt, type TypedShowWidget, type UnifiedWidgetClient, WaniwaniKvStore, type WidgetCSP, type WidgetPlatform, type WidgetSignal, type WithWaniwaniOptions, createFlow, createFlowTestHarness, createResource, createTool, createTrackingRoute, detectPlatform, isMCPApps, isOpenAI, redacted, registerTools, withWaniwani };
|
|
1344
|
+
export { type AddNodeConfig, type ConditionFn, END, type FlowConfig, type FlowTestResult, type HostContext, type InferFlowState, type InterruptSignal, type KvStore, MemoryKvStore, type NodeContext, type NodeHandler, type RegisteredFlow, type RegisteredResource, type RegisteredTool, type ResourceConfig, START, type ScopedWaniWaniClient, StateGraph, type ToolCallResult, type ToolConfig, type ToolHandler, type ToolHandlerContext, type ToolResult, type ToolToolCallback, type TrackingRouteOptions, type TypedInterrupt, type TypedShowWidget, type UnifiedWidgetClient, WaniwaniKvStore, type WidgetCSP, type WidgetPlatform, type WidgetSignal, type WithWaniwaniOptions, createFlow, createFlowTestHarness, createResource, createTool, createTrackingRoute, detectPlatform, isMCPApps, isOpenAI, redacted, registerTools, withWaniwani };
|