@waniwani/sdk 0.12.3 → 0.12.4-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- var y=class extends Error{constructor(t,r){super(t);this.status=r;this.name="WaniWaniError"}};import{existsSync as j,readFileSync as V}from"fs";import{resolve as L}from"path";var O="waniwani.json",g;function w(){if(g!==void 0)return g;try{let e=L(process.cwd(),O);if(!j(e))return g=null,null;let n=V(e,"utf-8");return g=JSON.parse(n),g}catch{return g=null,null}}var k="__waniwani_config__";function K(e){return globalThis[k]=e,e}function R(){return globalThis[k]}var N="@waniwani/sdk";function b(e){let{apiUrl:n,apiKey:t}=e;function r(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}async function i(s,a,o){let u=r(),m=`${n.replace(/\/$/,"")}${a}`,f={Authorization:`Bearer ${u}`,"X-WaniWani-SDK":N},h={method:s,headers:f};o!==void 0&&(f["Content-Type"]="application/json",h.body=JSON.stringify(o));let d=await fetch(m,h);if(!d.ok){let U=await d.text().catch(()=>"");throw new y(U||`KB API error: HTTP ${d.status}`,d.status)}return(await d.json()).data}return{async ingest(s){return i("POST","/api/mcp/kb/ingest",{files:s})},async search(s,a){return i("POST","/api/mcp/kb/search",{query:s,...a})},async sources(){return i("GET","/api/mcp/kb/sources")}}}function v(e,n){for(let t of n){let r=e[t];if(typeof r=="string"&&r.length>0)return r}}var q=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],z=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],Y=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],$=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],H=["correlationId","openai/requestId"];function x(e){return e?v(e,q):void 0}function _(e){return e?v(e,z):void 0}function M(e){return e?v(e,Y):void 0}function C(e){return e?v(e,$):void 0}function D(e){return e?v(e,H):void 0}var X=[{key:"waniwani/sessionId",source:"chatbar"},{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}];function W(e){if(!e)return;let n=e["waniwani/source"];if(typeof n=="string"&&n.length>0)return n;for(let{key:t,source:r}of X){let i=e[t];if(typeof i=="string"&&i.length>0)return r}}var G="@waniwani/sdk";function E(e,n={}){let t=n.now??(()=>new Date),r=n.generateId??A,i=Q(e),s=T(e.meta),a=T(e.metadata),o=ee(e,s),u=c(e.eventId)??r(),m=te(e.timestamp,t),f=c(e.source)??W(s)??n.source??G,h=I(e)?{...e}:void 0,d={...a};return Object.keys(s).length>0&&(d.meta=s),h&&(d.rawLegacy=h),{id:u,type:"mcp.event",name:i,source:f,timestamp:m,correlation:o,properties:J(e,i),metadata:d,rawLegacy:h}}function A(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function J(e,n){if(!I(e))return T(e.properties);let t=Z(e,n),r=T(e.properties);return{...t,...r}}function Z(e,n){switch(n){case"tool.called":{let t={};return c(e.toolName)&&(t.name=e.toolName),c(e.toolType)&&(t.type=e.toolType),t}case"quote.succeeded":{let t={};return typeof e.quoteAmount=="number"&&(t.amount=e.quoteAmount),c(e.quoteCurrency)&&(t.currency=e.quoteCurrency),t}case"link.clicked":{let t={};return c(e.linkUrl)&&(t.url=e.linkUrl),t}case"purchase.completed":{let t={};return typeof e.purchaseAmount=="number"&&(t.amount=e.purchaseAmount),c(e.purchaseCurrency)&&(t.currency=e.purchaseCurrency),t}default:return{}}}function Q(e){return I(e)?e.eventType:e.event}function ee(e,n){let t=c(e.requestId)??_(n),r=c(e.sessionId)??x(n),i=c(e.traceId)??M(n),s=c(e.externalUserId)??C(n),a=c(e.correlationId)??D(n)??t,o={};return r&&(o.sessionId=r),i&&(o.traceId=i),t&&(o.requestId=t),a&&(o.correlationId=a),s&&(o.externalUserId=s),o}function te(e,n){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let t=new Date(e);if(!Number.isNaN(t.getTime()))return t.toISOString()}return n().toISOString()}function T(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function c(e){if(typeof e=="string"&&e.trim().length!==0)return e}function I(e){return"eventType"in e}var ne="/api/mcp/events/v2/batch";var P="@waniwani/sdk",re=new Set([401,403]),ie=new Set([408,425,429,500,502,503,504]);function B(e){return new S(e)}var S=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(n){this.endpointUrl=ae(n.apiUrl,n.endpointPath??ne),this.flushIntervalMs=n.flushIntervalMs??1e3,this.maxBatchSize=n.maxBatchSize??20,this.maxBufferSize=n.maxBufferSize??1e3,this.maxRetries=n.maxRetries??3,this.retryBaseDelayMs=n.retryBaseDelayMs??200,this.retryMaxDelayMs=n.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=n.shutdownTimeoutMs??2e3,this.fetchFn=n.fetchFn??fetch,this.logger=n.logger??console,this.now=n.now??(()=>new Date),this.sleep=n.sleep??(t=>new Promise(r=>setTimeout(r,t))),this.apiKey=n.apiKey,this.sdkVersion=n.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs))}enqueue(n){if(this.isStopped||this.isShuttingDown){this.logger.warn("[WaniWani] Tracking transport is stopped, dropping event %s",n.id);return}if(this.buffer.length>=this.maxBufferSize){let t=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,t),this.logger.warn("[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)",t)}if(this.buffer.push(n),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(n){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let t=n?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(t)||t<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(t).then(()=>i)])===i?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let n=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(n)}}async sendBatchWithRetry(n){let t=0,r=n;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[WaniWani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(t>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(t)),t+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[WaniWani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(t>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.retryable,await this.sleep(this.backoffDelayMs(t)),t+=1;continue}}}async sendBatchOnce(n){let t;try{t=await this.fetchFn(this.endpointUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-WaniWani-SDK":P},body:JSON.stringify(this.makeBatchRequest(n))})}catch(s){return{kind:"retryable",reason:ce(s)}}if(re.has(t.status))return{kind:"auth",status:t.status};if(ie.has(t.status))return{kind:"retryable",reason:`HTTP ${t.status}`};if(!t.ok)return{kind:"permanent",reason:`HTTP ${t.status}`};let r=await oe(t);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(n,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.permanent}}makeBatchRequest(n){return{sentAt:this.now().toISOString(),source:{sdk:P,version:this.sdkVersion??"0.0.0"},events:n}}classifyRejectedEvents(n,t){let r=new Map(n.map(a=>[a.id,a])),i=[],s=[];for(let a of t){let o=r.get(a.eventId);if(o){if(se(a)){i.push(o);continue}s.push(o)}}return{retryable:i,permanent:s}}backoffDelayMs(n){let t=this.retryBaseDelayMs*2**n;return Math.min(t,this.retryMaxDelayMs)}stopTransportForAuthFailure(n,t){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",n,t+r)}};function se(e){if(e.retryable===!0)return!0;let n=e.code.toLowerCase();return n.includes("timeout")||n.includes("temporary")||n.includes("unavailable")||n.includes("rate_limit")||n.includes("transient")||n.includes("server")}async function oe(e){let n=await e.text();if(n)try{return JSON.parse(n)}catch{return}}function ae(e,n){let t=e.endsWith("/")?e:`${e}/`,r=n.startsWith("/")?n.slice(1):n;return`${t}${r}`}function ce(e){return e instanceof Error?e.message:String(e)}function F(e){let{apiUrl:n,apiKey:t,tracking:r}=e;function i(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}let s=t?B({apiUrl:n,apiKey:t,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0,a={async identify(o,u,m){i();let f=E({event:"user.identified",externalUserId:o,properties:u,meta:m});return s?.enqueue(f),{eventId:f.id}},async track(o){i();let u=E(o);return s?.enqueue(u),{eventId:u.id}},async flush(){i(),await s?.flush()},async shutdown(o){return i(),await s?.shutdown({timeoutMs:o?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return s&&ue(a,r.shutdownTimeoutMs),a}function ue(e,n){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let t=()=>{e.shutdown({timeoutMs:n})};process.once("beforeExit",t),process.once("SIGINT",t),process.once("SIGTERM",t)}function de(e){let t=e??w()??R(),r=t?.apiUrl??"https://app.waniwani.ai",i=t?.apiKey??process.env.WANIWANI_API_KEY,s={endpointPath:t?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:t?.tracking?.flushIntervalMs??1e3,maxBatchSize:t?.tracking?.maxBatchSize??20,maxBufferSize:t?.tracking?.maxBufferSize??1e3,maxRetries:t?.tracking?.maxRetries??3,retryBaseDelayMs:t?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:t?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:t?.tracking?.shutdownTimeoutMs??2e3},a={apiUrl:r,apiKey:i,tracking:s},o=F(a),u=b(a);return{...o,kb:u,_config:a}}export{y as WaniWaniError,K as defineConfig,de as waniwani};
1
+ var y=class extends Error{constructor(t,r){super(t);this.status=r;this.name="WaniWaniError"}};import{existsSync as j,readFileSync as V}from"fs";import{resolve as L}from"path";var O="waniwani.json",g;function w(){if(g!==void 0)return g;try{let e=L(process.cwd(),O);if(!j(e))return g=null,null;let n=V(e,"utf-8");return g=JSON.parse(n),g}catch{return g=null,null}}var k="__waniwani_config__";function K(e){return globalThis[k]=e,e}function R(){return globalThis[k]}var N="@waniwani/sdk";function b(e){let{apiUrl:n,apiKey:t}=e;function r(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}async function i(s,a,o){let u=r(),m=`${n.replace(/\/$/,"")}${a}`,f={Authorization:`Bearer ${u}`,"X-WaniWani-SDK":N},h={method:s,headers:f};o!==void 0&&(f["Content-Type"]="application/json",h.body=JSON.stringify(o));let d=await fetch(m,h);if(!d.ok){let U=await d.text().catch(()=>"");throw new y(U||`KB API error: HTTP ${d.status}`,d.status)}return(await d.json()).data}return{async ingest(s){return i("POST","/api/mcp/kb/ingest",{files:s})},async search(s,a){return i("POST","/api/mcp/kb/search",{query:s,...a})},async sources(){return i("GET","/api/mcp/kb/sources")}}}function v(e,n){for(let t of n){let r=e[t];if(typeof r=="string"&&r.length>0)return r}}var q=["waniwani/sessionId","openai/sessionId","openai/session","sessionId","conversationId","mcp-session-id"],z=["waniwani/requestId","openai/requestId","requestId","mcp/requestId"],Y=["waniwani/traceId","openai/traceId","traceId","mcp/traceId","openai/requestId","requestId"],$=["waniwani/userId","openai/userId","externalUserId","userId","actorId"],H=["correlationId","openai/requestId"];function x(e){return e?v(e,q):void 0}function _(e){return e?v(e,z):void 0}function M(e){return e?v(e,Y):void 0}function C(e){return e?v(e,$):void 0}function D(e){return e?v(e,H):void 0}var X=[{key:"openai/sessionId",source:"chatgpt"},{key:"openai/session",source:"chatgpt"}];function W(e){if(!e)return;let n=e["waniwani/source"];if(typeof n=="string"&&n.length>0)return n;for(let{key:t,source:r}of X){let i=e[t];if(typeof i=="string"&&i.length>0)return r}}var G="@waniwani/sdk";function E(e,n={}){let t=n.now??(()=>new Date),r=n.generateId??A,i=Q(e),s=T(e.meta),a=T(e.metadata),o=ee(e,s),u=c(e.eventId)??r(),m=te(e.timestamp,t),f=c(e.source)??W(s)??n.source??G,h=I(e)?{...e}:void 0,d={...a};return Object.keys(s).length>0&&(d.meta=s),h&&(d.rawLegacy=h),{id:u,type:"mcp.event",name:i,source:f,timestamp:m,correlation:o,properties:J(e,i),metadata:d,rawLegacy:h}}function A(){return typeof crypto<"u"&&typeof crypto.randomUUID=="function"?`evt_${crypto.randomUUID()}`:`evt_${Math.random().toString(36).slice(2,10)}_${Date.now().toString(36)}`}function J(e,n){if(!I(e))return T(e.properties);let t=Z(e,n),r=T(e.properties);return{...t,...r}}function Z(e,n){switch(n){case"tool.called":{let t={};return c(e.toolName)&&(t.name=e.toolName),c(e.toolType)&&(t.type=e.toolType),t}case"quote.succeeded":{let t={};return typeof e.quoteAmount=="number"&&(t.amount=e.quoteAmount),c(e.quoteCurrency)&&(t.currency=e.quoteCurrency),t}case"link.clicked":{let t={};return c(e.linkUrl)&&(t.url=e.linkUrl),t}case"purchase.completed":{let t={};return typeof e.purchaseAmount=="number"&&(t.amount=e.purchaseAmount),c(e.purchaseCurrency)&&(t.currency=e.purchaseCurrency),t}default:return{}}}function Q(e){return I(e)?e.eventType:e.event}function ee(e,n){let t=c(e.requestId)??_(n),r=c(e.sessionId)??x(n),i=c(e.traceId)??M(n),s=c(e.externalUserId)??C(n),a=c(e.correlationId)??D(n)??t,o={};return r&&(o.sessionId=r),i&&(o.traceId=i),t&&(o.requestId=t),a&&(o.correlationId=a),s&&(o.externalUserId=s),o}function te(e,n){if(e instanceof Date)return e.toISOString();if(typeof e=="string"){let t=new Date(e);if(!Number.isNaN(t.getTime()))return t.toISOString()}return n().toISOString()}function T(e){return!e||typeof e!="object"||Array.isArray(e)?{}:e}function c(e){if(typeof e=="string"&&e.trim().length!==0)return e}function I(e){return"eventType"in e}var ne="/api/mcp/events/v2/batch";var P="@waniwani/sdk",re=new Set([401,403]),ie=new Set([408,425,429,500,502,503,504]);function B(e){return new S(e)}var S=class{endpointUrl;flushIntervalMs;maxBatchSize;maxBufferSize;maxRetries;retryBaseDelayMs;retryMaxDelayMs;shutdownTimeoutMs;sdkVersion;fetchFn;logger;now;sleep;apiKey;buffer=[];flushTimer;flushScheduled=!1;flushScheduledTimer;flushInFlight;inFlightCount=0;isStopped=!1;isShuttingDown=!1;constructor(n){this.endpointUrl=ae(n.apiUrl,n.endpointPath??ne),this.flushIntervalMs=n.flushIntervalMs??1e3,this.maxBatchSize=n.maxBatchSize??20,this.maxBufferSize=n.maxBufferSize??1e3,this.maxRetries=n.maxRetries??3,this.retryBaseDelayMs=n.retryBaseDelayMs??200,this.retryMaxDelayMs=n.retryMaxDelayMs??2e3,this.shutdownTimeoutMs=n.shutdownTimeoutMs??2e3,this.fetchFn=n.fetchFn??fetch,this.logger=n.logger??console,this.now=n.now??(()=>new Date),this.sleep=n.sleep??(t=>new Promise(r=>setTimeout(r,t))),this.apiKey=n.apiKey,this.sdkVersion=n.sdkVersion,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>{this.flush()},this.flushIntervalMs))}enqueue(n){if(this.isStopped||this.isShuttingDown){this.logger.warn("[WaniWani] Tracking transport is stopped, dropping event %s",n.id);return}if(this.buffer.length>=this.maxBufferSize){let t=this.buffer.length-this.maxBufferSize+1;this.buffer.splice(0,t),this.logger.warn("[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)",t)}if(this.buffer.push(n),this.buffer.length>=this.maxBatchSize){this.flush();return}this.scheduleMicroFlush()}pendingEvents(){return this.buffer.length+this.inFlightCount}async flush(){return this.flushInFlight?this.flushInFlight:(this.flushInFlight=this.flushLoop().finally(()=>{this.flushInFlight=void 0}),this.flushInFlight)}async shutdown(n){this.isShuttingDown=!0,this.flushTimer&&(clearInterval(this.flushTimer),this.flushTimer=void 0),this.flushScheduledTimer&&(clearTimeout(this.flushScheduledTimer),this.flushScheduledTimer=void 0,this.flushScheduled=!1);let t=n?.timeoutMs??this.shutdownTimeoutMs,r=this.flush();if(!Number.isFinite(t)||t<=0)return await r,this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()};let i=Symbol("shutdown-timeout");return await Promise.race([r.then(()=>"flushed"),this.sleep(t).then(()=>i)])===i?(this.isStopped=!0,{timedOut:!0,pendingEvents:this.pendingEvents()}):(this.isStopped=!0,{timedOut:!1,pendingEvents:this.pendingEvents()})}scheduleMicroFlush(){this.flushScheduled||(this.flushScheduled=!0,this.flushScheduledTimer=setTimeout(()=>{this.flushScheduledTimer=void 0,this.flushScheduled=!1,this.flush()},0))}async flushLoop(){for(;this.buffer.length>0&&!this.isStopped;){let n=this.buffer.splice(0,this.maxBatchSize);await this.sendBatchWithRetry(n)}}async sendBatchWithRetry(n){let t=0,r=n;for(;r.length>0&&!this.isStopped;){this.inFlightCount=r.length;let i=await this.sendBatchOnce(r);switch(this.inFlightCount=0,i.kind){case"success":return;case"auth":this.stopTransportForAuthFailure(i.status,r.length);return;case"permanent":this.logger.error("[WaniWani] Dropping %d event(s) after permanent failure: %s",r.length,i.reason);return;case"retryable":if(t>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d event(s) after retry exhaustion: %s",r.length,i.reason);return}await this.sleep(this.backoffDelayMs(t)),t+=1;continue;case"partial":if(i.permanent.length>0&&this.logger.error("[WaniWani] Dropping %d event(s) rejected as permanent",i.permanent.length),i.retryable.length===0)return;if(t>=this.maxRetries){this.logger.error("[WaniWani] Dropping %d retryable event(s) after retry exhaustion",i.retryable.length);return}r=i.retryable,await this.sleep(this.backoffDelayMs(t)),t+=1;continue}}}async sendBatchOnce(n){let t;try{t=await this.fetchFn(this.endpointUrl,{method:"POST",headers:{"Content-Type":"application/json",Authorization:`Bearer ${this.apiKey}`,"X-WaniWani-SDK":P},body:JSON.stringify(this.makeBatchRequest(n))})}catch(s){return{kind:"retryable",reason:ce(s)}}if(re.has(t.status))return{kind:"auth",status:t.status};if(ie.has(t.status))return{kind:"retryable",reason:`HTTP ${t.status}`};if(!t.ok)return{kind:"permanent",reason:`HTTP ${t.status}`};let r=await oe(t);if(!r?.rejected||r.rejected.length===0)return{kind:"success"};let i=this.classifyRejectedEvents(n,r.rejected);return i.retryable.length===0&&i.permanent.length===0?{kind:"success"}:{kind:"partial",retryable:i.retryable,permanent:i.permanent}}makeBatchRequest(n){return{sentAt:this.now().toISOString(),source:{sdk:P,version:this.sdkVersion??"0.0.0"},events:n}}classifyRejectedEvents(n,t){let r=new Map(n.map(a=>[a.id,a])),i=[],s=[];for(let a of t){let o=r.get(a.eventId);if(o){if(se(a)){i.push(o);continue}s.push(o)}}return{retryable:i,permanent:s}}backoffDelayMs(n){let t=this.retryBaseDelayMs*2**n;return Math.min(t,this.retryMaxDelayMs)}stopTransportForAuthFailure(n,t){this.isStopped=!0;let r=this.buffer.length;this.buffer.splice(0,r),this.logger.error("[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)",n,t+r)}};function se(e){if(e.retryable===!0)return!0;let n=e.code.toLowerCase();return n.includes("timeout")||n.includes("temporary")||n.includes("unavailable")||n.includes("rate_limit")||n.includes("transient")||n.includes("server")}async function oe(e){let n=await e.text();if(n)try{return JSON.parse(n)}catch{return}}function ae(e,n){let t=e.endsWith("/")?e:`${e}/`,r=n.startsWith("/")?n.slice(1):n;return`${t}${r}`}function ce(e){return e instanceof Error?e.message:String(e)}function F(e){let{apiUrl:n,apiKey:t,tracking:r}=e;function i(){if(!t)throw new Error("WANIWANI_API_KEY is not set");return t}let s=t?B({apiUrl:n,apiKey:t,endpointPath:r.endpointPath,flushIntervalMs:r.flushIntervalMs,maxBatchSize:r.maxBatchSize,maxBufferSize:r.maxBufferSize,maxRetries:r.maxRetries,retryBaseDelayMs:r.retryBaseDelayMs,retryMaxDelayMs:r.retryMaxDelayMs,shutdownTimeoutMs:r.shutdownTimeoutMs}):void 0,a={async identify(o,u,m){i();let f=E({event:"user.identified",externalUserId:o,properties:u,meta:m});return s?.enqueue(f),{eventId:f.id}},async track(o){i();let u=E(o);return s?.enqueue(u),{eventId:u.id}},async flush(){i(),await s?.flush()},async shutdown(o){return i(),await s?.shutdown({timeoutMs:o?.timeoutMs??r.shutdownTimeoutMs})??{timedOut:!1,pendingEvents:0}}};return s&&ue(a,r.shutdownTimeoutMs),a}function ue(e,n){if(typeof process>"u"||typeof process.once!="function"||typeof process.on!="function")return;let t=()=>{e.shutdown({timeoutMs:n})};process.once("beforeExit",t),process.once("SIGINT",t),process.once("SIGTERM",t)}function de(e){let t=e??w()??R(),r=t?.apiUrl??"https://app.waniwani.ai",i=t?.apiKey??process.env.WANIWANI_API_KEY,s={endpointPath:t?.tracking?.endpointPath??"/api/mcp/events/v2/batch",flushIntervalMs:t?.tracking?.flushIntervalMs??1e3,maxBatchSize:t?.tracking?.maxBatchSize??20,maxBufferSize:t?.tracking?.maxBufferSize??1e3,maxRetries:t?.tracking?.maxRetries??3,retryBaseDelayMs:t?.tracking?.retryBaseDelayMs??200,retryMaxDelayMs:t?.tracking?.retryMaxDelayMs??2e3,shutdownTimeoutMs:t?.tracking?.shutdownTimeoutMs??2e3},a={apiUrl:r,apiKey:i,tracking:s},o=F(a),u=b(a);return{...o,kb:u,_config:a}}export{y as WaniWaniError,K as defineConfig,de as waniwani};
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/error.ts","../src/project-config.ts","../src/kb/client.ts","../src/mcp/server/utils.ts","../src/tracking/mapper.ts","../src/tracking/transport.ts","../src/tracking/index.ts","../src/waniwani.ts"],"sourcesContent":["// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n/**\n * Project-level configuration for WaniWani MCP projects.\n *\n * Mirrors the JSON Schema hosted at https://docs.waniwani.ai/waniwani.json.\n * The canonical config file is `waniwani.json` at the project root:\n *\n * ```json\n * {\n * \"$schema\": \"https://docs.waniwani.ai/waniwani.json\",\n * \"orgId\": \"...\",\n * \"projectId\": \"...\"\n * }\n * ```\n *\n * `waniwani()` and the CLI both read this file automatically — no\n * explicit import is required.\n */\nexport interface WaniWaniProjectConfig {\n\t/** URL of the JSON Schema for editor autocomplete. Ignored at runtime. */\n\t$schema?: string;\n\t/** WaniWani organization ID this project belongs to. */\n\torgId?: string;\n\t/** WaniWani MCP project ID. */\n\tprojectId?: string;\n\t/**\n\t * The base URL of the WaniWani API.\n\t * Defaults to `https://app.waniwani.ai`.\n\t */\n\tapiUrl?: string;\n\t/**\n\t * Local port the MCP listens on during `waniwani dev`. Overridden by\n\t * `--port`. Defaults to 3000.\n\t */\n\tdevPort?: number;\n}\n\n// ---------------------------------------------------------------------------\n// waniwani.json loader\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAME = \"waniwani.json\";\n\nlet _cached: WaniWaniProjectConfig | null | undefined;\n\n/**\n * Load `waniwani.json` from the current working directory.\n *\n * Returns `null` if the file doesn't exist or the runtime doesn't support\n * synchronous filesystem reads (e.g. edge / worker runtimes). Cached after\n * the first call.\n *\n * @internal\n */\nexport function loadProjectConfig(): WaniWaniProjectConfig | null {\n\tif (_cached !== undefined) {\n\t\treturn _cached;\n\t}\n\n\ttry {\n\t\tconst filePath = resolve(process.cwd(), CONFIG_FILENAME);\n\t\tif (!existsSync(filePath)) {\n\t\t\t_cached = null;\n\t\t\treturn null;\n\t\t}\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t_cached = JSON.parse(raw) as WaniWaniProjectConfig;\n\t\treturn _cached;\n\t} catch {\n\t\t_cached = null;\n\t\treturn null;\n\t}\n}\n\n/**\n * Reset the cached config. Test-only.\n *\n * @internal\n */\nexport function resetProjectConfigCache(): void {\n\t_cached = undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Legacy: defineConfig + globalThis registration\n// ---------------------------------------------------------------------------\n\nconst GLOBAL_KEY = \"__waniwani_config__\" as const;\n\n/**\n * Register a WaniWani project configuration on `globalThis`.\n *\n * @deprecated Create a `waniwani.json` at the project root instead. The SDK\n * and CLI both auto-load that file — no `defineConfig` call required.\n * See https://docs.waniwani.ai/waniwani.json for the schema.\n */\nexport function defineConfig(\n\tconfig: WaniWaniProjectConfig,\n): WaniWaniProjectConfig {\n\t(globalThis as Record<string, unknown>)[GLOBAL_KEY] = config;\n\treturn config;\n}\n\n/**\n * Retrieve the globally registered config (set by `defineConfig`).\n *\n * @deprecated Use `loadProjectConfig()` instead. `defineConfig` is going away.\n * @internal\n */\nexport function getGlobalConfig(): WaniWaniProjectConfig | undefined {\n\treturn (globalThis as Record<string, unknown>)[GLOBAL_KEY] as\n\t\t| WaniWaniProjectConfig\n\t\t| undefined;\n}\n","// KB Client — thin HTTP wrapper for knowledge base API\n\nimport { WaniWaniError } from \"../error.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tKbClient,\n\tKbIngestFile,\n\tKbIngestResult,\n\tKbSearchOptions,\n\tKbSource,\n\tSearchResult,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\n\nexport function createKbClient(config: InternalConfig): KbClient {\n\tconst { apiUrl, apiKey } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tasync function request<T>(\n\t\tmethod: \"GET\" | \"POST\",\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst key = requireApiKey();\n\t\tconst url = `${apiUrl.replace(/\\/$/, \"\")}${path}`;\n\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${key}`,\n\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t};\n\n\t\tconst init: RequestInit = { method, headers };\n\n\t\tif (body !== undefined) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\n\t\tconst response = await fetch(url, init);\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\tthrow new WaniWaniError(\n\t\t\t\ttext || `KB API error: HTTP ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\n\t\tconst json = (await response.json()) as { data: T };\n\t\treturn json.data;\n\t}\n\n\treturn {\n\t\tasync ingest(files: KbIngestFile[]): Promise<KbIngestResult> {\n\t\t\treturn request<KbIngestResult>(\"POST\", \"/api/mcp/kb/ingest\", {\n\t\t\t\tfiles,\n\t\t\t});\n\t\t},\n\n\t\tasync search(\n\t\t\tquery: string,\n\t\t\toptions?: KbSearchOptions,\n\t\t): Promise<SearchResult[]> {\n\t\t\treturn request<SearchResult[]>(\"POST\", \"/api/mcp/kb/search\", {\n\t\t\t\tquery,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tasync sources(): Promise<KbSource[]> {\n\t\t\treturn request<KbSource[]>(\"GET\", \"/api/mcp/kb/sources\");\n\t\t},\n\t};\n}\n","// ============================================================================\n// Meta key extraction helpers\n// ============================================================================\n\n/** Pick the first non-empty string value from `meta` matching the given keys. */\nfunction pickFirst(\n\tmeta: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n// --- Key lists (ordered by priority) ---\n\nconst SESSION_ID_KEYS = [\n\t\"waniwani/sessionId\",\n\t\"openai/sessionId\",\n\t\"openai/session\",\n\t\"sessionId\",\n\t\"conversationId\",\n\t\"mcp-session-id\",\n] as const;\n\nconst REQUEST_ID_KEYS = [\n\t\"waniwani/requestId\",\n\t\"openai/requestId\",\n\t\"requestId\",\n\t\"mcp/requestId\",\n] as const;\n\nconst TRACE_ID_KEYS = [\n\t\"waniwani/traceId\",\n\t\"openai/traceId\",\n\t\"traceId\",\n\t\"mcp/traceId\",\n\t\"openai/requestId\",\n\t\"requestId\",\n] as const;\n\nconst EXTERNAL_USER_ID_KEYS = [\n\t\"waniwani/userId\",\n\t\"openai/userId\",\n\t\"externalUserId\",\n\t\"userId\",\n\t\"actorId\",\n] as const;\n\nconst CORRELATION_ID_KEYS = [\"correlationId\", \"openai/requestId\"] as const;\n\nconst TURN_COUNT_KEYS = [\"waniwani/turnCount\"] as const;\n\n/** Meta key for flow execution path (nodesVisited, flowId). */\nexport const FLOW_META_KEY = \"waniwani/flow\" as const;\n\n// --- Extractors ---\n\nexport function extractSessionId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, SESSION_ID_KEYS) : undefined;\n}\n\nexport function extractRequestId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, REQUEST_ID_KEYS) : undefined;\n}\n\nexport function extractTraceId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, TRACE_ID_KEYS) : undefined;\n}\n\nexport function extractExternalUserId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, EXTERNAL_USER_ID_KEYS) : undefined;\n}\n\nexport function extractCorrelationId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, CORRELATION_ID_KEYS) : undefined;\n}\n\n/**\n * Number of user messages in the current chat session, as counted by the\n * WaniWani app before dispatching the MCP request. Useful for MCPs that\n * gate behavior on conversation length (e.g. compulsory email verification\n * after N turns) without each MCP having to track turn state itself.\n *\n * Forwarded as `waniwani/turnCount` (non-negative integer).\n */\nexport function extractTurnCount(\n\tmeta: Record<string, unknown> | undefined,\n): number | undefined {\n\tif (!meta) {\n\t\treturn undefined;\n\t}\n\tfor (const key of TURN_COUNT_KEYS) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"number\" && Number.isFinite(value) && value >= 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nconst SOURCE_SESSION_KEYS = [\n\t{ key: \"waniwani/sessionId\", source: \"chatbar\" },\n\t{ key: \"openai/sessionId\", source: \"chatgpt\" },\n\t{ key: \"openai/session\", source: \"chatgpt\" },\n] as const;\n\nexport function extractSource(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\tif (!meta) {\n\t\treturn undefined;\n\t}\n\t// Explicit source set by the caller (e.g. chatbar name)\n\tconst explicit = meta[\"waniwani/source\"];\n\tif (typeof explicit === \"string\" && explicit.length > 0) {\n\t\treturn explicit;\n\t}\n\t// Derive from session ID key\n\tfor (const { key, source } of SOURCE_SESSION_KEYS) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn source;\n\t\t}\n\t}\n\treturn undefined;\n}\n","import {\n\textractCorrelationId,\n\textractExternalUserId,\n\textractRequestId,\n\textractSessionId,\n\textractSource,\n\textractTraceId,\n} from \"../mcp/server/utils.js\";\nimport type { EventType, LegacyTrackEvent, TrackInput } from \"./@types.js\";\nimport type { V2CorrelationIds, V2EventEnvelope } from \"./v2-types.js\";\n\nconst DEFAULT_SOURCE = \"@waniwani/sdk\";\n\nexport interface MapTrackEventOptions {\n\tnow?: () => Date;\n\tgenerateId?: () => string;\n\tsource?: string;\n}\n\nexport function mapTrackEventToV2(\n\tinput: TrackInput,\n\toptions: MapTrackEventOptions = {},\n): V2EventEnvelope {\n\tconst now = options.now ?? (() => new Date());\n\tconst generateId = options.generateId ?? createEventId;\n\tconst eventName = resolveEventName(input);\n\tconst meta = toRecord(input.meta);\n\tconst metadata = toRecord(input.metadata);\n\tconst correlation = resolveCorrelationIds(input, meta);\n\tconst eventId = takeNonEmptyString(input.eventId) ?? generateId();\n\tconst timestamp = normalizeTimestamp(input.timestamp, now);\n\tconst source =\n\t\ttakeNonEmptyString(input.source) ??\n\t\textractSource(meta) ??\n\t\toptions.source ??\n\t\tDEFAULT_SOURCE;\n\tconst rawLegacy = isLegacyTrackEvent(input) ? { ...input } : undefined;\n\n\tconst mappedMetadata: Record<string, unknown> = {\n\t\t...metadata,\n\t};\n\tif (Object.keys(meta).length > 0) {\n\t\tmappedMetadata.meta = meta;\n\t}\n\tif (rawLegacy) {\n\t\tmappedMetadata.rawLegacy = rawLegacy;\n\t}\n\n\treturn {\n\t\tid: eventId,\n\t\ttype: \"mcp.event\",\n\t\tname: eventName,\n\t\tsource,\n\t\ttimestamp,\n\t\tcorrelation,\n\t\tproperties: mapProperties(input, eventName),\n\t\tmetadata: mappedMetadata,\n\t\trawLegacy,\n\t};\n}\n\nexport function createEventId(): string {\n\tif (\n\t\ttypeof crypto !== \"undefined\" &&\n\t\ttypeof crypto.randomUUID === \"function\"\n\t) {\n\t\treturn `evt_${crypto.randomUUID()}`;\n\t}\n\n\treturn `evt_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;\n}\n\nfunction mapProperties(\n\tinput: TrackInput,\n\teventName: EventType,\n): Record<string, unknown> {\n\tif (!isLegacyTrackEvent(input)) {\n\t\treturn toRecord(input.properties);\n\t}\n\n\tconst legacyProperties = mapLegacyProperties(input, eventName);\n\tconst explicitProperties = toRecord(input.properties);\n\treturn {\n\t\t...legacyProperties,\n\t\t...explicitProperties,\n\t};\n}\n\nfunction mapLegacyProperties(\n\tinput: LegacyTrackEvent,\n\teventName: EventType,\n): Record<string, unknown> {\n\tswitch (eventName) {\n\t\tcase \"tool.called\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.toolName)) {\n\t\t\t\tproperties.name = input.toolName;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.toolType)) {\n\t\t\t\tproperties.type = input.toolType;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"quote.succeeded\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.quoteAmount === \"number\") {\n\t\t\t\tproperties.amount = input.quoteAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.quoteCurrency)) {\n\t\t\t\tproperties.currency = input.quoteCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"link.clicked\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.linkUrl)) {\n\t\t\t\tproperties.url = input.linkUrl;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"purchase.completed\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.purchaseAmount === \"number\") {\n\t\t\t\tproperties.amount = input.purchaseAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.purchaseCurrency)) {\n\t\t\t\tproperties.currency = input.purchaseCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\nfunction resolveEventName(input: TrackInput): EventType {\n\tif (isLegacyTrackEvent(input)) {\n\t\treturn input.eventType;\n\t}\n\treturn input.event;\n}\n\nfunction resolveCorrelationIds(\n\tinput: TrackInput,\n\tmeta: Record<string, unknown>,\n): V2CorrelationIds {\n\tconst requestId =\n\t\ttakeNonEmptyString(input.requestId) ?? extractRequestId(meta);\n\n\tconst sessionId =\n\t\ttakeNonEmptyString(input.sessionId) ?? extractSessionId(meta);\n\n\tconst traceId = takeNonEmptyString(input.traceId) ?? extractTraceId(meta);\n\n\tconst externalUserId =\n\t\ttakeNonEmptyString(input.externalUserId) ?? extractExternalUserId(meta);\n\n\tconst correlationId =\n\t\ttakeNonEmptyString(input.correlationId) ??\n\t\textractCorrelationId(meta) ??\n\t\trequestId;\n\n\tconst correlation: V2CorrelationIds = {};\n\tif (sessionId) {\n\t\tcorrelation.sessionId = sessionId;\n\t}\n\tif (traceId) {\n\t\tcorrelation.traceId = traceId;\n\t}\n\tif (requestId) {\n\t\tcorrelation.requestId = requestId;\n\t}\n\tif (correlationId) {\n\t\tcorrelation.correlationId = correlationId;\n\t}\n\tif (externalUserId) {\n\t\tcorrelation.externalUserId = externalUserId;\n\t}\n\treturn correlation;\n}\n\nfunction normalizeTimestamp(\n\tinput: string | Date | undefined,\n\tnow: () => Date,\n): string {\n\tif (input instanceof Date) {\n\t\treturn input.toISOString();\n\t}\n\tif (typeof input === \"string\") {\n\t\tconst date = new Date(input);\n\t\tif (!Number.isNaN(date.getTime())) {\n\t\t\treturn date.toISOString();\n\t\t}\n\t}\n\treturn now().toISOString();\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn {};\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction takeNonEmptyString(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (value.trim().length === 0) {\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction isLegacyTrackEvent(input: TrackInput): input is LegacyTrackEvent {\n\treturn \"eventType\" in input;\n}\n","import type {\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nimport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\ntype FetchFn = (\n\tinput: URL | RequestInfo,\n\tinit?: RequestInit,\n) => Promise<Response>;\n\nconst DEFAULT_ENDPOINT_PATH = \"/api/mcp/events/v2/batch\";\nconst DEFAULT_FLUSH_INTERVAL_MS = 1_000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_BUFFER_SIZE = 1_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY_MS = 200;\nconst DEFAULT_RETRY_MAX_DELAY_MS = 2_000;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2_000;\nconst SDK_NAME = \"@waniwani/sdk\";\n\nconst AUTH_FAILURE_STATUS = new Set([401, 403]);\nconst RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);\n\ninterface Logger {\n\twarn: (message: string, ...args: unknown[]) => void;\n\terror: (message: string, ...args: unknown[]) => void;\n}\n\nexport interface V2TransportOptions {\n\tapiUrl: string;\n\tapiKey: string;\n\tendpointPath?: string;\n\tflushIntervalMs?: number;\n\tmaxBatchSize?: number;\n\tmaxBufferSize?: number;\n\tmaxRetries?: number;\n\tretryBaseDelayMs?: number;\n\tretryMaxDelayMs?: number;\n\tshutdownTimeoutMs?: number;\n\tsdkVersion?: string;\n\tfetchFn?: FetchFn;\n\tlogger?: Logger;\n\tnow?: () => Date;\n\tsleep?: (delayMs: number) => Promise<void>;\n}\n\nexport interface V2BatchTransport {\n\tenqueue: (event: V2EventEnvelope) => void;\n\tflush: () => Promise<void>;\n\tshutdown: (\n\t\toptions?: TrackingShutdownOptions,\n\t) => Promise<TrackingShutdownResult>;\n\tpendingEvents: () => number;\n}\n\ntype SendBatchResult =\n\t| { kind: \"success\" }\n\t| { kind: \"retryable\"; reason: string }\n\t| { kind: \"permanent\"; reason: string }\n\t| { kind: \"auth\"; status: number }\n\t| {\n\t\t\tkind: \"partial\";\n\t\t\tretryable: V2EventEnvelope[];\n\t\t\tpermanent: V2EventEnvelope[];\n\t };\n\nexport function createV2BatchTransport(\n\toptions: V2TransportOptions,\n): V2BatchTransport {\n\treturn new BatchingV2Transport(options);\n}\n\nclass BatchingV2Transport implements V2BatchTransport {\n\tprivate readonly endpointUrl: string;\n\tprivate readonly flushIntervalMs: number;\n\tprivate readonly maxBatchSize: number;\n\tprivate readonly maxBufferSize: number;\n\tprivate readonly maxRetries: number;\n\tprivate readonly retryBaseDelayMs: number;\n\tprivate readonly retryMaxDelayMs: number;\n\tprivate readonly shutdownTimeoutMs: number;\n\tprivate readonly sdkVersion?: string;\n\tprivate readonly fetchFn: FetchFn;\n\tprivate readonly logger: Logger;\n\tprivate readonly now: () => Date;\n\tprivate readonly sleep: (delayMs: number) => Promise<void>;\n\tprivate readonly apiKey: string;\n\n\tprivate readonly buffer: V2EventEnvelope[] = [];\n\tprivate flushTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate flushScheduled = false;\n\tprivate flushScheduledTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate flushInFlight: Promise<void> | undefined;\n\tprivate inFlightCount = 0;\n\tprivate isStopped = false;\n\tprivate isShuttingDown = false;\n\n\tconstructor(options: V2TransportOptions) {\n\t\tthis.endpointUrl = joinUrl(\n\t\t\toptions.apiUrl,\n\t\t\toptions.endpointPath ?? DEFAULT_ENDPOINT_PATH,\n\t\t);\n\t\tthis.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\t\tthis.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;\n\t\tthis.maxBufferSize = options.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n\t\tthis.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.retryBaseDelayMs =\n\t\t\toptions.retryBaseDelayMs ?? DEFAULT_RETRY_BASE_DELAY_MS;\n\t\tthis.retryMaxDelayMs =\n\t\t\toptions.retryMaxDelayMs ?? DEFAULT_RETRY_MAX_DELAY_MS;\n\t\tthis.shutdownTimeoutMs =\n\t\t\toptions.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n\t\tthis.fetchFn = options.fetchFn ?? fetch;\n\t\tthis.logger = options.logger ?? console;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.sleep =\n\t\t\toptions.sleep ??\n\t\t\t((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.sdkVersion = options.sdkVersion;\n\n\t\tif (this.flushIntervalMs > 0) {\n\t\t\tthis.flushTimer = setInterval(() => {\n\t\t\t\tvoid this.flush();\n\t\t\t}, this.flushIntervalMs);\n\t\t}\n\t}\n\n\tenqueue(event: V2EventEnvelope): void {\n\t\tif (this.isStopped || this.isShuttingDown) {\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking transport is stopped, dropping event %s\",\n\t\t\t\tevent.id,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.buffer.length >= this.maxBufferSize) {\n\t\t\tconst dropCount = this.buffer.length - this.maxBufferSize + 1;\n\t\t\tthis.buffer.splice(0, dropCount);\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)\",\n\t\t\t\tdropCount,\n\t\t\t);\n\t\t}\n\n\t\tthis.buffer.push(event);\n\n\t\tif (this.buffer.length >= this.maxBatchSize) {\n\t\t\tvoid this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scheduleMicroFlush();\n\t}\n\n\tpendingEvents(): number {\n\t\treturn this.buffer.length + this.inFlightCount;\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tif (this.flushInFlight) {\n\t\t\treturn this.flushInFlight;\n\t\t}\n\t\tthis.flushInFlight = this.flushLoop().finally(() => {\n\t\t\tthis.flushInFlight = undefined;\n\t\t});\n\t\treturn this.flushInFlight;\n\t}\n\n\tasync shutdown(\n\t\toptions?: TrackingShutdownOptions,\n\t): Promise<TrackingShutdownResult> {\n\t\tthis.isShuttingDown = true;\n\t\tif (this.flushTimer) {\n\t\t\tclearInterval(this.flushTimer);\n\t\t\tthis.flushTimer = undefined;\n\t\t}\n\t\tif (this.flushScheduledTimer) {\n\t\t\tclearTimeout(this.flushScheduledTimer);\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t}\n\n\t\tconst timeoutMs = options?.timeoutMs ?? this.shutdownTimeoutMs;\n\t\tconst flushPromise = this.flush();\n\n\t\tif (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n\t\t\tawait flushPromise;\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tconst timeoutSignal = Symbol(\"shutdown-timeout\");\n\t\tconst result = await Promise.race([\n\t\t\tflushPromise.then(() => \"flushed\" as const),\n\t\t\tthis.sleep(timeoutMs).then(() => timeoutSignal),\n\t\t]);\n\n\t\tif (result === timeoutSignal) {\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: true, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tthis.isStopped = true;\n\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t}\n\n\tprivate scheduleMicroFlush(): void {\n\t\tif (this.flushScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tthis.flushScheduled = true;\n\t\tthis.flushScheduledTimer = setTimeout(() => {\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t\tvoid this.flush();\n\t\t}, 0);\n\t}\n\n\tprivate async flushLoop(): Promise<void> {\n\t\twhile (this.buffer.length > 0 && !this.isStopped) {\n\t\t\tconst batch = this.buffer.splice(0, this.maxBatchSize);\n\t\t\tawait this.sendBatchWithRetry(batch);\n\t\t}\n\t}\n\n\tprivate async sendBatchWithRetry(batch: V2EventEnvelope[]): Promise<void> {\n\t\tlet attempt = 0;\n\t\tlet pendingBatch = batch;\n\n\t\twhile (pendingBatch.length > 0 && !this.isStopped) {\n\t\t\tthis.inFlightCount = pendingBatch.length;\n\t\t\tconst result = await this.sendBatchOnce(pendingBatch);\n\t\t\tthis.inFlightCount = 0;\n\n\t\t\tswitch (result.kind) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\treturn;\n\t\t\t\tcase \"auth\":\n\t\t\t\t\tthis.stopTransportForAuthFailure(result.status, pendingBatch.length);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"permanent\":\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after permanent failure: %s\",\n\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"retryable\":\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after retry exhaustion: %s\",\n\t\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase \"partial\":\n\t\t\t\t\tif (result.permanent.length > 0) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) rejected as permanent\",\n\t\t\t\t\t\t\tresult.permanent.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (result.retryable.length === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d retryable event(s) after retry exhaustion\",\n\t\t\t\t\t\t\tresult.retryable.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tpendingBatch = result.retryable;\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendBatchOnce(\n\t\tevents: V2EventEnvelope[],\n\t): Promise<SendBatchResult> {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await this.fetchFn(this.endpointUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(this.makeBatchRequest(events)),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: getErrorMessage(error),\n\t\t\t};\n\t\t}\n\n\t\tif (AUTH_FAILURE_STATUS.has(response.status)) {\n\t\t\treturn { kind: \"auth\", status: response.status };\n\t\t}\n\n\t\tif (RETRYABLE_STATUS.has(response.status)) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tkind: \"permanent\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tconst data = await parseJsonResponse<V2BatchResponse>(response);\n\t\tif (!data?.rejected || data.rejected.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\tconst partial = this.classifyRejectedEvents(events, data.rejected);\n\t\tif (partial.retryable.length === 0 && partial.permanent.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"partial\",\n\t\t\tretryable: partial.retryable,\n\t\t\tpermanent: partial.permanent,\n\t\t};\n\t}\n\n\tprivate makeBatchRequest(events: V2EventEnvelope[]): V2BatchRequest {\n\t\treturn {\n\t\t\tsentAt: this.now().toISOString(),\n\t\t\tsource: {\n\t\t\t\tsdk: SDK_NAME,\n\t\t\t\tversion: this.sdkVersion ?? \"0.0.0\",\n\t\t\t},\n\t\t\tevents,\n\t\t};\n\t}\n\n\tprivate classifyRejectedEvents(\n\t\tevents: V2EventEnvelope[],\n\t\trejected: V2BatchRejectedEvent[],\n\t): {\n\t\tretryable: V2EventEnvelope[];\n\t\tpermanent: V2EventEnvelope[];\n\t} {\n\t\tconst byId = new Map(events.map((event) => [event.id, event]));\n\t\tconst retryable: V2EventEnvelope[] = [];\n\t\tconst permanent: V2EventEnvelope[] = [];\n\n\t\tfor (const rejectedEvent of rejected) {\n\t\t\tconst event = byId.get(rejectedEvent.eventId);\n\t\t\tif (!event) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (isRetryableRejectedEvent(rejectedEvent)) {\n\t\t\t\tretryable.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpermanent.push(event);\n\t\t}\n\n\t\treturn { retryable, permanent };\n\t}\n\n\tprivate backoffDelayMs(attempt: number): number {\n\t\tconst rawDelay = this.retryBaseDelayMs * 2 ** attempt;\n\t\treturn Math.min(rawDelay, this.retryMaxDelayMs);\n\t}\n\n\tprivate stopTransportForAuthFailure(\n\t\tstatus: number,\n\t\trejectedCount: number,\n\t): void {\n\t\tthis.isStopped = true;\n\t\tconst buffered = this.buffer.length;\n\t\tthis.buffer.splice(0, buffered);\n\t\tthis.logger.error(\n\t\t\t\"[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)\",\n\t\t\tstatus,\n\t\t\trejectedCount + buffered,\n\t\t);\n\t}\n}\n\nfunction isRetryableRejectedEvent(\n\trejectedEvent: V2BatchRejectedEvent,\n): boolean {\n\tif (rejectedEvent.retryable === true) {\n\t\treturn true;\n\t}\n\tconst code = rejectedEvent.code.toLowerCase();\n\treturn (\n\t\tcode.includes(\"timeout\") ||\n\t\tcode.includes(\"temporary\") ||\n\t\tcode.includes(\"unavailable\") ||\n\t\tcode.includes(\"rate_limit\") ||\n\t\tcode.includes(\"transient\") ||\n\t\tcode.includes(\"server\")\n\t);\n}\n\nasync function parseJsonResponse<T>(\n\tresponse: Response,\n): Promise<T | undefined> {\n\tconst body = await response.text();\n\tif (!body) {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\treturn JSON.parse(body) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction joinUrl(apiUrl: string, endpointPath: string): string {\n\tconst normalizedBase = apiUrl.endsWith(\"/\") ? apiUrl : `${apiUrl}/`;\n\tconst normalizedPath = endpointPath.startsWith(\"/\")\n\t\t? endpointPath.slice(1)\n\t\t: endpointPath;\n\treturn `${normalizedBase}${normalizedPath}`;\n}\n\nfunction getErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\treturn String(error);\n}\n","// Tracking Module\n\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tTrackInput,\n\tTrackingClient,\n\tTrackingShutdownOptions,\n} from \"./@types.js\";\nimport { mapTrackEventToV2 } from \"./mapper.js\";\nimport { createV2BatchTransport } from \"./transport.js\";\n\n// Re-export types\nexport type {\n\tEventType,\n\tLegacyTrackEvent,\n\tLinkClickedProperties,\n\tPurchaseCompletedProperties,\n\tQuoteSucceededProperties,\n\tToolCalledProperties,\n\tTrackEvent,\n\tTrackInput,\n\tTrackingClient,\n\tTrackingConfig,\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nexport { createEventId, mapTrackEventToV2 } from \"./mapper.js\";\nexport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2CorrelationIds,\n\tV2EnvelopeType,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nexport function createTrackingClient(config: InternalConfig): TrackingClient {\n\tconst { apiUrl, apiKey, tracking } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tconst transport = apiKey\n\t\t? createV2BatchTransport({\n\t\t\t\tapiUrl,\n\t\t\t\tapiKey,\n\t\t\t\tendpointPath: tracking.endpointPath,\n\t\t\t\tflushIntervalMs: tracking.flushIntervalMs,\n\t\t\t\tmaxBatchSize: tracking.maxBatchSize,\n\t\t\t\tmaxBufferSize: tracking.maxBufferSize,\n\t\t\t\tmaxRetries: tracking.maxRetries,\n\t\t\t\tretryBaseDelayMs: tracking.retryBaseDelayMs,\n\t\t\t\tretryMaxDelayMs: tracking.retryMaxDelayMs,\n\t\t\t\tshutdownTimeoutMs: tracking.shutdownTimeoutMs,\n\t\t\t})\n\t\t: undefined;\n\n\tconst client: TrackingClient = {\n\t\tasync identify(\n\t\t\tuserId: string,\n\t\t\tproperties?: Record<string, unknown>,\n\t\t\tmeta?: Record<string, unknown>,\n\t\t): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2({\n\t\t\t\tevent: \"user.identified\",\n\t\t\t\texternalUserId: userId,\n\t\t\t\tproperties,\n\t\t\t\tmeta,\n\t\t\t});\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync track(event: TrackInput): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2(event);\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync flush(): Promise<void> {\n\t\t\trequireApiKey();\n\t\t\tawait transport?.flush();\n\t\t},\n\t\tasync shutdown(options?: TrackingShutdownOptions) {\n\t\t\trequireApiKey();\n\t\t\treturn (\n\t\t\t\t(await transport?.shutdown({\n\t\t\t\t\ttimeoutMs: options?.timeoutMs ?? tracking.shutdownTimeoutMs,\n\t\t\t\t})) ?? { timedOut: false, pendingEvents: 0 }\n\t\t\t);\n\t\t},\n\t};\n\n\tif (transport) {\n\t\tattachShutdownHooks(client, tracking.shutdownTimeoutMs);\n\t}\n\treturn client;\n}\n\nfunction attachShutdownHooks(\n\tclient: TrackingClient,\n\tdefaultTimeoutMs: number,\n): void {\n\tif (\n\t\ttypeof process === \"undefined\" ||\n\t\ttypeof process.once !== \"function\" ||\n\t\ttypeof process.on !== \"function\"\n\t) {\n\t\treturn;\n\t}\n\n\tconst shutdown = () => {\n\t\tvoid client.shutdown({ timeoutMs: defaultTimeoutMs });\n\t};\n\n\tprocess.once(\"beforeExit\", shutdown);\n\tprocess.once(\"SIGINT\", shutdown);\n\tprocess.once(\"SIGTERM\", shutdown);\n}\n","// WaniWani SDK - Main Entry\n\nimport { createKbClient } from \"./kb/client.js\";\nimport {\n\tgetGlobalConfig,\n\tloadProjectConfig,\n\ttype WaniWaniProjectConfig,\n} from \"./project-config.js\";\nimport { createTrackingClient } from \"./tracking/index.js\";\nimport type { WaniWaniClient, WaniWaniConfig } from \"./types.js\";\n\n/**\n * Create a WaniWani SDK client\n *\n * @param config - Configuration options. When omitted, reads `waniwani.json`\n * from the current working directory, then falls back to env vars.\n * @returns A fully typed WaniWani client\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani({ apiKey: \"...\" });\n *\n * // Next.js route handler\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function waniwani(\n\tconfig?: WaniWaniConfig | WaniWaniProjectConfig,\n): WaniWaniClient {\n\tconst projectConfig = config ?? loadProjectConfig() ?? getGlobalConfig();\n\tconst effective = projectConfig as\n\t\t| (WaniWaniConfig & WaniWaniProjectConfig)\n\t\t| undefined;\n\n\tconst apiUrl = effective?.apiUrl ?? \"https://app.waniwani.ai\";\n\tconst apiKey = effective?.apiKey ?? process.env.WANIWANI_API_KEY;\n\tconst trackingConfig = {\n\t\tendpointPath:\n\t\t\teffective?.tracking?.endpointPath ?? \"/api/mcp/events/v2/batch\",\n\t\tflushIntervalMs: effective?.tracking?.flushIntervalMs ?? 1_000,\n\t\tmaxBatchSize: effective?.tracking?.maxBatchSize ?? 20,\n\t\tmaxBufferSize: effective?.tracking?.maxBufferSize ?? 1_000,\n\t\tmaxRetries: effective?.tracking?.maxRetries ?? 3,\n\t\tretryBaseDelayMs: effective?.tracking?.retryBaseDelayMs ?? 200,\n\t\tretryMaxDelayMs: effective?.tracking?.retryMaxDelayMs ?? 2_000,\n\t\tshutdownTimeoutMs: effective?.tracking?.shutdownTimeoutMs ?? 2_000,\n\t};\n\n\tconst internalConfig = { apiUrl, apiKey, tracking: trackingConfig };\n\n\t// Compose client from modules\n\tconst trackingClient = createTrackingClient(internalConfig);\n\tconst kbClient = createKbClient(internalConfig);\n\n\treturn {\n\t\t...trackingClient,\n\t\tkb: kbClient,\n\t\t_config: internalConfig,\n\t};\n}\n"],"mappings":"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","names":["WaniWaniError","message","status","existsSync","readFileSync","resolve","CONFIG_FILENAME","_cached","loadProjectConfig","filePath","raw","GLOBAL_KEY","defineConfig","config","getGlobalConfig","SDK_NAME","createKbClient","config","apiUrl","apiKey","requireApiKey","request","method","path","body","key","url","headers","init","response","text","WaniWaniError","files","query","options","pickFirst","meta","keys","key","value","SESSION_ID_KEYS","REQUEST_ID_KEYS","TRACE_ID_KEYS","EXTERNAL_USER_ID_KEYS","CORRELATION_ID_KEYS","extractSessionId","meta","pickFirst","SESSION_ID_KEYS","extractRequestId","REQUEST_ID_KEYS","extractTraceId","TRACE_ID_KEYS","extractExternalUserId","EXTERNAL_USER_ID_KEYS","extractCorrelationId","CORRELATION_ID_KEYS","SOURCE_SESSION_KEYS","extractSource","meta","explicit","key","source","value","DEFAULT_SOURCE","mapTrackEventToV2","input","options","now","generateId","createEventId","eventName","resolveEventName","meta","toRecord","metadata","correlation","resolveCorrelationIds","eventId","takeNonEmptyString","timestamp","normalizeTimestamp","source","extractSource","rawLegacy","isLegacyTrackEvent","mappedMetadata","mapProperties","legacyProperties","mapLegacyProperties","explicitProperties","properties","requestId","extractRequestId","sessionId","extractSessionId","traceId","extractTraceId","externalUserId","extractExternalUserId","correlationId","extractCorrelationId","date","value","DEFAULT_ENDPOINT_PATH","SDK_NAME","AUTH_FAILURE_STATUS","RETRYABLE_STATUS","createV2BatchTransport","options","BatchingV2Transport","joinUrl","DEFAULT_ENDPOINT_PATH","delayMs","resolve","event","dropCount","timeoutMs","flushPromise","timeoutSignal","batch","attempt","pendingBatch","result","events","response","error","getErrorMessage","data","parseJsonResponse","partial","rejected","byId","retryable","permanent","rejectedEvent","isRetryableRejectedEvent","rawDelay","status","rejectedCount","buffered","code","body","apiUrl","endpointPath","normalizedBase","normalizedPath","createTrackingClient","config","apiUrl","apiKey","tracking","requireApiKey","transport","createV2BatchTransport","client","userId","properties","meta","mappedEvent","mapTrackEventToV2","event","options","attachShutdownHooks","defaultTimeoutMs","shutdown","waniwani","config","effective","loadProjectConfig","getGlobalConfig","apiUrl","apiKey","trackingConfig","internalConfig","trackingClient","createTrackingClient","kbClient","createKbClient"]}
1
+ {"version":3,"sources":["../src/error.ts","../src/project-config.ts","../src/kb/client.ts","../src/mcp/server/utils.ts","../src/tracking/mapper.ts","../src/tracking/transport.ts","../src/tracking/index.ts","../src/waniwani.ts"],"sourcesContent":["// WaniWani SDK - Errors\n\nexport class WaniWaniError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\tpublic status: number,\n\t) {\n\t\tsuper(message);\n\t\tthis.name = \"WaniWaniError\";\n\t}\n}\n","import { existsSync, readFileSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\n\n/**\n * Project-level configuration for WaniWani MCP projects.\n *\n * Mirrors the JSON Schema hosted at https://docs.waniwani.ai/waniwani.json.\n * The canonical config file is `waniwani.json` at the project root:\n *\n * ```json\n * {\n * \"$schema\": \"https://docs.waniwani.ai/waniwani.json\",\n * \"orgId\": \"...\",\n * \"projectId\": \"...\"\n * }\n * ```\n *\n * `waniwani()` and the CLI both read this file automatically — no\n * explicit import is required.\n */\nexport interface WaniWaniProjectConfig {\n\t/** URL of the JSON Schema for editor autocomplete. Ignored at runtime. */\n\t$schema?: string;\n\t/** WaniWani organization ID this project belongs to. */\n\torgId?: string;\n\t/** WaniWani MCP project ID. */\n\tprojectId?: string;\n\t/**\n\t * The base URL of the WaniWani API.\n\t * Defaults to `https://app.waniwani.ai`.\n\t */\n\tapiUrl?: string;\n\t/**\n\t * Local port the MCP listens on during `waniwani dev`. Overridden by\n\t * `--port`. Defaults to 3000.\n\t */\n\tdevPort?: number;\n}\n\n// ---------------------------------------------------------------------------\n// waniwani.json loader\n// ---------------------------------------------------------------------------\n\nconst CONFIG_FILENAME = \"waniwani.json\";\n\nlet _cached: WaniWaniProjectConfig | null | undefined;\n\n/**\n * Load `waniwani.json` from the current working directory.\n *\n * Returns `null` if the file doesn't exist or the runtime doesn't support\n * synchronous filesystem reads (e.g. edge / worker runtimes). Cached after\n * the first call.\n *\n * @internal\n */\nexport function loadProjectConfig(): WaniWaniProjectConfig | null {\n\tif (_cached !== undefined) {\n\t\treturn _cached;\n\t}\n\n\ttry {\n\t\tconst filePath = resolve(process.cwd(), CONFIG_FILENAME);\n\t\tif (!existsSync(filePath)) {\n\t\t\t_cached = null;\n\t\t\treturn null;\n\t\t}\n\t\tconst raw = readFileSync(filePath, \"utf-8\");\n\t\t_cached = JSON.parse(raw) as WaniWaniProjectConfig;\n\t\treturn _cached;\n\t} catch {\n\t\t_cached = null;\n\t\treturn null;\n\t}\n}\n\n/**\n * Reset the cached config. Test-only.\n *\n * @internal\n */\nexport function resetProjectConfigCache(): void {\n\t_cached = undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Legacy: defineConfig + globalThis registration\n// ---------------------------------------------------------------------------\n\nconst GLOBAL_KEY = \"__waniwani_config__\" as const;\n\n/**\n * Register a WaniWani project configuration on `globalThis`.\n *\n * @deprecated Create a `waniwani.json` at the project root instead. The SDK\n * and CLI both auto-load that file — no `defineConfig` call required.\n * See https://docs.waniwani.ai/waniwani.json for the schema.\n */\nexport function defineConfig(\n\tconfig: WaniWaniProjectConfig,\n): WaniWaniProjectConfig {\n\t(globalThis as Record<string, unknown>)[GLOBAL_KEY] = config;\n\treturn config;\n}\n\n/**\n * Retrieve the globally registered config (set by `defineConfig`).\n *\n * @deprecated Use `loadProjectConfig()` instead. `defineConfig` is going away.\n * @internal\n */\nexport function getGlobalConfig(): WaniWaniProjectConfig | undefined {\n\treturn (globalThis as Record<string, unknown>)[GLOBAL_KEY] as\n\t\t| WaniWaniProjectConfig\n\t\t| undefined;\n}\n","// KB Client — thin HTTP wrapper for knowledge base API\n\nimport { WaniWaniError } from \"../error.js\";\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tKbClient,\n\tKbIngestFile,\n\tKbIngestResult,\n\tKbSearchOptions,\n\tKbSource,\n\tSearchResult,\n} from \"./types.js\";\n\nconst SDK_NAME = \"@waniwani/sdk\";\n\nexport function createKbClient(config: InternalConfig): KbClient {\n\tconst { apiUrl, apiKey } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tasync function request<T>(\n\t\tmethod: \"GET\" | \"POST\",\n\t\tpath: string,\n\t\tbody?: unknown,\n\t): Promise<T> {\n\t\tconst key = requireApiKey();\n\t\tconst url = `${apiUrl.replace(/\\/$/, \"\")}${path}`;\n\n\t\tconst headers: Record<string, string> = {\n\t\t\tAuthorization: `Bearer ${key}`,\n\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t};\n\n\t\tconst init: RequestInit = { method, headers };\n\n\t\tif (body !== undefined) {\n\t\t\theaders[\"Content-Type\"] = \"application/json\";\n\t\t\tinit.body = JSON.stringify(body);\n\t\t}\n\n\t\tconst response = await fetch(url, init);\n\n\t\tif (!response.ok) {\n\t\t\tconst text = await response.text().catch(() => \"\");\n\t\t\tthrow new WaniWaniError(\n\t\t\t\ttext || `KB API error: HTTP ${response.status}`,\n\t\t\t\tresponse.status,\n\t\t\t);\n\t\t}\n\n\t\tconst json = (await response.json()) as { data: T };\n\t\treturn json.data;\n\t}\n\n\treturn {\n\t\tasync ingest(files: KbIngestFile[]): Promise<KbIngestResult> {\n\t\t\treturn request<KbIngestResult>(\"POST\", \"/api/mcp/kb/ingest\", {\n\t\t\t\tfiles,\n\t\t\t});\n\t\t},\n\n\t\tasync search(\n\t\t\tquery: string,\n\t\t\toptions?: KbSearchOptions,\n\t\t): Promise<SearchResult[]> {\n\t\t\treturn request<SearchResult[]>(\"POST\", \"/api/mcp/kb/search\", {\n\t\t\t\tquery,\n\t\t\t\t...options,\n\t\t\t});\n\t\t},\n\n\t\tasync sources(): Promise<KbSource[]> {\n\t\t\treturn request<KbSource[]>(\"GET\", \"/api/mcp/kb/sources\");\n\t\t},\n\t};\n}\n","// ============================================================================\n// Meta key extraction helpers\n// ============================================================================\n\n/** Pick the first non-empty string value from `meta` matching the given keys. */\nfunction pickFirst(\n\tmeta: Record<string, unknown>,\n\tkeys: readonly string[],\n): string | undefined {\n\tfor (const key of keys) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n// --- Key lists (ordered by priority) ---\n\nconst SESSION_ID_KEYS = [\n\t\"waniwani/sessionId\",\n\t\"openai/sessionId\",\n\t\"openai/session\",\n\t\"sessionId\",\n\t\"conversationId\",\n\t\"mcp-session-id\",\n] as const;\n\nconst REQUEST_ID_KEYS = [\n\t\"waniwani/requestId\",\n\t\"openai/requestId\",\n\t\"requestId\",\n\t\"mcp/requestId\",\n] as const;\n\nconst TRACE_ID_KEYS = [\n\t\"waniwani/traceId\",\n\t\"openai/traceId\",\n\t\"traceId\",\n\t\"mcp/traceId\",\n\t\"openai/requestId\",\n\t\"requestId\",\n] as const;\n\nconst EXTERNAL_USER_ID_KEYS = [\n\t\"waniwani/userId\",\n\t\"openai/userId\",\n\t\"externalUserId\",\n\t\"userId\",\n\t\"actorId\",\n] as const;\n\nconst CORRELATION_ID_KEYS = [\"correlationId\", \"openai/requestId\"] as const;\n\nconst TURN_COUNT_KEYS = [\"waniwani/turnCount\"] as const;\n\n/** Meta key for flow execution path (nodesVisited, flowId). */\nexport const FLOW_META_KEY = \"waniwani/flow\" as const;\n\n// --- Extractors ---\n\nexport function extractSessionId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, SESSION_ID_KEYS) : undefined;\n}\n\nexport function extractRequestId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, REQUEST_ID_KEYS) : undefined;\n}\n\nexport function extractTraceId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, TRACE_ID_KEYS) : undefined;\n}\n\nexport function extractExternalUserId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, EXTERNAL_USER_ID_KEYS) : undefined;\n}\n\nexport function extractCorrelationId(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\treturn meta ? pickFirst(meta, CORRELATION_ID_KEYS) : undefined;\n}\n\n/**\n * Number of user messages in the current chat session, as counted by the\n * WaniWani app before dispatching the MCP request. Useful for MCPs that\n * gate behavior on conversation length (e.g. compulsory email verification\n * after N turns) without each MCP having to track turn state itself.\n *\n * Forwarded as `waniwani/turnCount` (non-negative integer).\n */\nexport function extractTurnCount(\n\tmeta: Record<string, unknown> | undefined,\n): number | undefined {\n\tif (!meta) {\n\t\treturn undefined;\n\t}\n\tfor (const key of TURN_COUNT_KEYS) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"number\" && Number.isFinite(value) && value >= 0) {\n\t\t\treturn value;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nconst SOURCE_SESSION_KEYS = [\n\t{ key: \"openai/sessionId\", source: \"chatgpt\" },\n\t{ key: \"openai/session\", source: \"chatgpt\" },\n] as const;\n\nexport function extractSource(\n\tmeta: Record<string, unknown> | undefined,\n): string | undefined {\n\tif (!meta) {\n\t\treturn undefined;\n\t}\n\tconst explicit = meta[\"waniwani/source\"];\n\tif (typeof explicit === \"string\" && explicit.length > 0) {\n\t\treturn explicit;\n\t}\n\tfor (const { key, source } of SOURCE_SESSION_KEYS) {\n\t\tconst value = meta[key];\n\t\tif (typeof value === \"string\" && value.length > 0) {\n\t\t\treturn source;\n\t\t}\n\t}\n\treturn undefined;\n}\n","import {\n\textractCorrelationId,\n\textractExternalUserId,\n\textractRequestId,\n\textractSessionId,\n\textractSource,\n\textractTraceId,\n} from \"../mcp/server/utils.js\";\nimport type { EventType, LegacyTrackEvent, TrackInput } from \"./@types.js\";\nimport type { V2CorrelationIds, V2EventEnvelope } from \"./v2-types.js\";\n\nconst DEFAULT_SOURCE = \"@waniwani/sdk\";\n\nexport interface MapTrackEventOptions {\n\tnow?: () => Date;\n\tgenerateId?: () => string;\n\tsource?: string;\n}\n\nexport function mapTrackEventToV2(\n\tinput: TrackInput,\n\toptions: MapTrackEventOptions = {},\n): V2EventEnvelope {\n\tconst now = options.now ?? (() => new Date());\n\tconst generateId = options.generateId ?? createEventId;\n\tconst eventName = resolveEventName(input);\n\tconst meta = toRecord(input.meta);\n\tconst metadata = toRecord(input.metadata);\n\tconst correlation = resolveCorrelationIds(input, meta);\n\tconst eventId = takeNonEmptyString(input.eventId) ?? generateId();\n\tconst timestamp = normalizeTimestamp(input.timestamp, now);\n\tconst source =\n\t\ttakeNonEmptyString(input.source) ??\n\t\textractSource(meta) ??\n\t\toptions.source ??\n\t\tDEFAULT_SOURCE;\n\tconst rawLegacy = isLegacyTrackEvent(input) ? { ...input } : undefined;\n\n\tconst mappedMetadata: Record<string, unknown> = {\n\t\t...metadata,\n\t};\n\tif (Object.keys(meta).length > 0) {\n\t\tmappedMetadata.meta = meta;\n\t}\n\tif (rawLegacy) {\n\t\tmappedMetadata.rawLegacy = rawLegacy;\n\t}\n\n\treturn {\n\t\tid: eventId,\n\t\ttype: \"mcp.event\",\n\t\tname: eventName,\n\t\tsource,\n\t\ttimestamp,\n\t\tcorrelation,\n\t\tproperties: mapProperties(input, eventName),\n\t\tmetadata: mappedMetadata,\n\t\trawLegacy,\n\t};\n}\n\nexport function createEventId(): string {\n\tif (\n\t\ttypeof crypto !== \"undefined\" &&\n\t\ttypeof crypto.randomUUID === \"function\"\n\t) {\n\t\treturn `evt_${crypto.randomUUID()}`;\n\t}\n\n\treturn `evt_${Math.random().toString(36).slice(2, 10)}_${Date.now().toString(36)}`;\n}\n\nfunction mapProperties(\n\tinput: TrackInput,\n\teventName: EventType,\n): Record<string, unknown> {\n\tif (!isLegacyTrackEvent(input)) {\n\t\treturn toRecord(input.properties);\n\t}\n\n\tconst legacyProperties = mapLegacyProperties(input, eventName);\n\tconst explicitProperties = toRecord(input.properties);\n\treturn {\n\t\t...legacyProperties,\n\t\t...explicitProperties,\n\t};\n}\n\nfunction mapLegacyProperties(\n\tinput: LegacyTrackEvent,\n\teventName: EventType,\n): Record<string, unknown> {\n\tswitch (eventName) {\n\t\tcase \"tool.called\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.toolName)) {\n\t\t\t\tproperties.name = input.toolName;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.toolType)) {\n\t\t\t\tproperties.type = input.toolType;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"quote.succeeded\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.quoteAmount === \"number\") {\n\t\t\t\tproperties.amount = input.quoteAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.quoteCurrency)) {\n\t\t\t\tproperties.currency = input.quoteCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"link.clicked\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (takeNonEmptyString(input.linkUrl)) {\n\t\t\t\tproperties.url = input.linkUrl;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tcase \"purchase.completed\": {\n\t\t\tconst properties: Record<string, unknown> = {};\n\t\t\tif (typeof input.purchaseAmount === \"number\") {\n\t\t\t\tproperties.amount = input.purchaseAmount;\n\t\t\t}\n\t\t\tif (takeNonEmptyString(input.purchaseCurrency)) {\n\t\t\t\tproperties.currency = input.purchaseCurrency;\n\t\t\t}\n\t\t\treturn properties;\n\t\t}\n\t\tdefault:\n\t\t\treturn {};\n\t}\n}\n\nfunction resolveEventName(input: TrackInput): EventType {\n\tif (isLegacyTrackEvent(input)) {\n\t\treturn input.eventType;\n\t}\n\treturn input.event;\n}\n\nfunction resolveCorrelationIds(\n\tinput: TrackInput,\n\tmeta: Record<string, unknown>,\n): V2CorrelationIds {\n\tconst requestId =\n\t\ttakeNonEmptyString(input.requestId) ?? extractRequestId(meta);\n\n\tconst sessionId =\n\t\ttakeNonEmptyString(input.sessionId) ?? extractSessionId(meta);\n\n\tconst traceId = takeNonEmptyString(input.traceId) ?? extractTraceId(meta);\n\n\tconst externalUserId =\n\t\ttakeNonEmptyString(input.externalUserId) ?? extractExternalUserId(meta);\n\n\tconst correlationId =\n\t\ttakeNonEmptyString(input.correlationId) ??\n\t\textractCorrelationId(meta) ??\n\t\trequestId;\n\n\tconst correlation: V2CorrelationIds = {};\n\tif (sessionId) {\n\t\tcorrelation.sessionId = sessionId;\n\t}\n\tif (traceId) {\n\t\tcorrelation.traceId = traceId;\n\t}\n\tif (requestId) {\n\t\tcorrelation.requestId = requestId;\n\t}\n\tif (correlationId) {\n\t\tcorrelation.correlationId = correlationId;\n\t}\n\tif (externalUserId) {\n\t\tcorrelation.externalUserId = externalUserId;\n\t}\n\treturn correlation;\n}\n\nfunction normalizeTimestamp(\n\tinput: string | Date | undefined,\n\tnow: () => Date,\n): string {\n\tif (input instanceof Date) {\n\t\treturn input.toISOString();\n\t}\n\tif (typeof input === \"string\") {\n\t\tconst date = new Date(input);\n\t\tif (!Number.isNaN(date.getTime())) {\n\t\t\treturn date.toISOString();\n\t\t}\n\t}\n\treturn now().toISOString();\n}\n\nfunction toRecord(value: unknown): Record<string, unknown> {\n\tif (!value || typeof value !== \"object\" || Array.isArray(value)) {\n\t\treturn {};\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction takeNonEmptyString(value: unknown): string | undefined {\n\tif (typeof value !== \"string\") {\n\t\treturn undefined;\n\t}\n\tif (value.trim().length === 0) {\n\t\treturn undefined;\n\t}\n\treturn value;\n}\n\nfunction isLegacyTrackEvent(input: TrackInput): input is LegacyTrackEvent {\n\treturn \"eventType\" in input;\n}\n","import type {\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nimport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\ntype FetchFn = (\n\tinput: URL | RequestInfo,\n\tinit?: RequestInit,\n) => Promise<Response>;\n\nconst DEFAULT_ENDPOINT_PATH = \"/api/mcp/events/v2/batch\";\nconst DEFAULT_FLUSH_INTERVAL_MS = 1_000;\nconst DEFAULT_MAX_BATCH_SIZE = 20;\nconst DEFAULT_MAX_BUFFER_SIZE = 1_000;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_RETRY_BASE_DELAY_MS = 200;\nconst DEFAULT_RETRY_MAX_DELAY_MS = 2_000;\nconst DEFAULT_SHUTDOWN_TIMEOUT_MS = 2_000;\nconst SDK_NAME = \"@waniwani/sdk\";\n\nconst AUTH_FAILURE_STATUS = new Set([401, 403]);\nconst RETRYABLE_STATUS = new Set([408, 425, 429, 500, 502, 503, 504]);\n\ninterface Logger {\n\twarn: (message: string, ...args: unknown[]) => void;\n\terror: (message: string, ...args: unknown[]) => void;\n}\n\nexport interface V2TransportOptions {\n\tapiUrl: string;\n\tapiKey: string;\n\tendpointPath?: string;\n\tflushIntervalMs?: number;\n\tmaxBatchSize?: number;\n\tmaxBufferSize?: number;\n\tmaxRetries?: number;\n\tretryBaseDelayMs?: number;\n\tretryMaxDelayMs?: number;\n\tshutdownTimeoutMs?: number;\n\tsdkVersion?: string;\n\tfetchFn?: FetchFn;\n\tlogger?: Logger;\n\tnow?: () => Date;\n\tsleep?: (delayMs: number) => Promise<void>;\n}\n\nexport interface V2BatchTransport {\n\tenqueue: (event: V2EventEnvelope) => void;\n\tflush: () => Promise<void>;\n\tshutdown: (\n\t\toptions?: TrackingShutdownOptions,\n\t) => Promise<TrackingShutdownResult>;\n\tpendingEvents: () => number;\n}\n\ntype SendBatchResult =\n\t| { kind: \"success\" }\n\t| { kind: \"retryable\"; reason: string }\n\t| { kind: \"permanent\"; reason: string }\n\t| { kind: \"auth\"; status: number }\n\t| {\n\t\t\tkind: \"partial\";\n\t\t\tretryable: V2EventEnvelope[];\n\t\t\tpermanent: V2EventEnvelope[];\n\t };\n\nexport function createV2BatchTransport(\n\toptions: V2TransportOptions,\n): V2BatchTransport {\n\treturn new BatchingV2Transport(options);\n}\n\nclass BatchingV2Transport implements V2BatchTransport {\n\tprivate readonly endpointUrl: string;\n\tprivate readonly flushIntervalMs: number;\n\tprivate readonly maxBatchSize: number;\n\tprivate readonly maxBufferSize: number;\n\tprivate readonly maxRetries: number;\n\tprivate readonly retryBaseDelayMs: number;\n\tprivate readonly retryMaxDelayMs: number;\n\tprivate readonly shutdownTimeoutMs: number;\n\tprivate readonly sdkVersion?: string;\n\tprivate readonly fetchFn: FetchFn;\n\tprivate readonly logger: Logger;\n\tprivate readonly now: () => Date;\n\tprivate readonly sleep: (delayMs: number) => Promise<void>;\n\tprivate readonly apiKey: string;\n\n\tprivate readonly buffer: V2EventEnvelope[] = [];\n\tprivate flushTimer: ReturnType<typeof setInterval> | undefined;\n\tprivate flushScheduled = false;\n\tprivate flushScheduledTimer: ReturnType<typeof setTimeout> | undefined;\n\tprivate flushInFlight: Promise<void> | undefined;\n\tprivate inFlightCount = 0;\n\tprivate isStopped = false;\n\tprivate isShuttingDown = false;\n\n\tconstructor(options: V2TransportOptions) {\n\t\tthis.endpointUrl = joinUrl(\n\t\t\toptions.apiUrl,\n\t\t\toptions.endpointPath ?? DEFAULT_ENDPOINT_PATH,\n\t\t);\n\t\tthis.flushIntervalMs = options.flushIntervalMs ?? DEFAULT_FLUSH_INTERVAL_MS;\n\t\tthis.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH_SIZE;\n\t\tthis.maxBufferSize = options.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;\n\t\tthis.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n\t\tthis.retryBaseDelayMs =\n\t\t\toptions.retryBaseDelayMs ?? DEFAULT_RETRY_BASE_DELAY_MS;\n\t\tthis.retryMaxDelayMs =\n\t\t\toptions.retryMaxDelayMs ?? DEFAULT_RETRY_MAX_DELAY_MS;\n\t\tthis.shutdownTimeoutMs =\n\t\t\toptions.shutdownTimeoutMs ?? DEFAULT_SHUTDOWN_TIMEOUT_MS;\n\t\tthis.fetchFn = options.fetchFn ?? fetch;\n\t\tthis.logger = options.logger ?? console;\n\t\tthis.now = options.now ?? (() => new Date());\n\t\tthis.sleep =\n\t\t\toptions.sleep ??\n\t\t\t((delayMs) => new Promise((resolve) => setTimeout(resolve, delayMs)));\n\t\tthis.apiKey = options.apiKey;\n\t\tthis.sdkVersion = options.sdkVersion;\n\n\t\tif (this.flushIntervalMs > 0) {\n\t\t\tthis.flushTimer = setInterval(() => {\n\t\t\t\tvoid this.flush();\n\t\t\t}, this.flushIntervalMs);\n\t\t}\n\t}\n\n\tenqueue(event: V2EventEnvelope): void {\n\t\tif (this.isStopped || this.isShuttingDown) {\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking transport is stopped, dropping event %s\",\n\t\t\t\tevent.id,\n\t\t\t);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this.buffer.length >= this.maxBufferSize) {\n\t\t\tconst dropCount = this.buffer.length - this.maxBufferSize + 1;\n\t\t\tthis.buffer.splice(0, dropCount);\n\t\t\tthis.logger.warn(\n\t\t\t\t\"[WaniWani] Tracking buffer overflow, dropped %d oldest event(s)\",\n\t\t\t\tdropCount,\n\t\t\t);\n\t\t}\n\n\t\tthis.buffer.push(event);\n\n\t\tif (this.buffer.length >= this.maxBatchSize) {\n\t\t\tvoid this.flush();\n\t\t\treturn;\n\t\t}\n\n\t\tthis.scheduleMicroFlush();\n\t}\n\n\tpendingEvents(): number {\n\t\treturn this.buffer.length + this.inFlightCount;\n\t}\n\n\tasync flush(): Promise<void> {\n\t\tif (this.flushInFlight) {\n\t\t\treturn this.flushInFlight;\n\t\t}\n\t\tthis.flushInFlight = this.flushLoop().finally(() => {\n\t\t\tthis.flushInFlight = undefined;\n\t\t});\n\t\treturn this.flushInFlight;\n\t}\n\n\tasync shutdown(\n\t\toptions?: TrackingShutdownOptions,\n\t): Promise<TrackingShutdownResult> {\n\t\tthis.isShuttingDown = true;\n\t\tif (this.flushTimer) {\n\t\t\tclearInterval(this.flushTimer);\n\t\t\tthis.flushTimer = undefined;\n\t\t}\n\t\tif (this.flushScheduledTimer) {\n\t\t\tclearTimeout(this.flushScheduledTimer);\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t}\n\n\t\tconst timeoutMs = options?.timeoutMs ?? this.shutdownTimeoutMs;\n\t\tconst flushPromise = this.flush();\n\n\t\tif (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {\n\t\t\tawait flushPromise;\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tconst timeoutSignal = Symbol(\"shutdown-timeout\");\n\t\tconst result = await Promise.race([\n\t\t\tflushPromise.then(() => \"flushed\" as const),\n\t\t\tthis.sleep(timeoutMs).then(() => timeoutSignal),\n\t\t]);\n\n\t\tif (result === timeoutSignal) {\n\t\t\tthis.isStopped = true;\n\t\t\treturn { timedOut: true, pendingEvents: this.pendingEvents() };\n\t\t}\n\n\t\tthis.isStopped = true;\n\t\treturn { timedOut: false, pendingEvents: this.pendingEvents() };\n\t}\n\n\tprivate scheduleMicroFlush(): void {\n\t\tif (this.flushScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tthis.flushScheduled = true;\n\t\tthis.flushScheduledTimer = setTimeout(() => {\n\t\t\tthis.flushScheduledTimer = undefined;\n\t\t\tthis.flushScheduled = false;\n\t\t\tvoid this.flush();\n\t\t}, 0);\n\t}\n\n\tprivate async flushLoop(): Promise<void> {\n\t\twhile (this.buffer.length > 0 && !this.isStopped) {\n\t\t\tconst batch = this.buffer.splice(0, this.maxBatchSize);\n\t\t\tawait this.sendBatchWithRetry(batch);\n\t\t}\n\t}\n\n\tprivate async sendBatchWithRetry(batch: V2EventEnvelope[]): Promise<void> {\n\t\tlet attempt = 0;\n\t\tlet pendingBatch = batch;\n\n\t\twhile (pendingBatch.length > 0 && !this.isStopped) {\n\t\t\tthis.inFlightCount = pendingBatch.length;\n\t\t\tconst result = await this.sendBatchOnce(pendingBatch);\n\t\t\tthis.inFlightCount = 0;\n\n\t\t\tswitch (result.kind) {\n\t\t\t\tcase \"success\":\n\t\t\t\t\treturn;\n\t\t\t\tcase \"auth\":\n\t\t\t\t\tthis.stopTransportForAuthFailure(result.status, pendingBatch.length);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"permanent\":\n\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after permanent failure: %s\",\n\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t);\n\t\t\t\t\treturn;\n\t\t\t\tcase \"retryable\":\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) after retry exhaustion: %s\",\n\t\t\t\t\t\t\tpendingBatch.length,\n\t\t\t\t\t\t\tresult.reason,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t\tcase \"partial\":\n\t\t\t\t\tif (result.permanent.length > 0) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d event(s) rejected as permanent\",\n\t\t\t\t\t\t\tresult.permanent.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (result.retryable.length === 0) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tif (attempt >= this.maxRetries) {\n\t\t\t\t\t\tthis.logger.error(\n\t\t\t\t\t\t\t\"[WaniWani] Dropping %d retryable event(s) after retry exhaustion\",\n\t\t\t\t\t\t\tresult.retryable.length,\n\t\t\t\t\t\t);\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tpendingBatch = result.retryable;\n\t\t\t\t\tawait this.sleep(this.backoffDelayMs(attempt));\n\t\t\t\t\tattempt += 1;\n\t\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async sendBatchOnce(\n\t\tevents: V2EventEnvelope[],\n\t): Promise<SendBatchResult> {\n\t\tlet response: Response;\n\n\t\ttry {\n\t\t\tresponse = await this.fetchFn(this.endpointUrl, {\n\t\t\t\tmethod: \"POST\",\n\t\t\t\theaders: {\n\t\t\t\t\t\"Content-Type\": \"application/json\",\n\t\t\t\t\tAuthorization: `Bearer ${this.apiKey}`,\n\t\t\t\t\t\"X-WaniWani-SDK\": SDK_NAME,\n\t\t\t\t},\n\t\t\t\tbody: JSON.stringify(this.makeBatchRequest(events)),\n\t\t\t});\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: getErrorMessage(error),\n\t\t\t};\n\t\t}\n\n\t\tif (AUTH_FAILURE_STATUS.has(response.status)) {\n\t\t\treturn { kind: \"auth\", status: response.status };\n\t\t}\n\n\t\tif (RETRYABLE_STATUS.has(response.status)) {\n\t\t\treturn {\n\t\t\t\tkind: \"retryable\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tif (!response.ok) {\n\t\t\treturn {\n\t\t\t\tkind: \"permanent\",\n\t\t\t\treason: `HTTP ${response.status}`,\n\t\t\t};\n\t\t}\n\n\t\tconst data = await parseJsonResponse<V2BatchResponse>(response);\n\t\tif (!data?.rejected || data.rejected.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\tconst partial = this.classifyRejectedEvents(events, data.rejected);\n\t\tif (partial.retryable.length === 0 && partial.permanent.length === 0) {\n\t\t\treturn { kind: \"success\" };\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"partial\",\n\t\t\tretryable: partial.retryable,\n\t\t\tpermanent: partial.permanent,\n\t\t};\n\t}\n\n\tprivate makeBatchRequest(events: V2EventEnvelope[]): V2BatchRequest {\n\t\treturn {\n\t\t\tsentAt: this.now().toISOString(),\n\t\t\tsource: {\n\t\t\t\tsdk: SDK_NAME,\n\t\t\t\tversion: this.sdkVersion ?? \"0.0.0\",\n\t\t\t},\n\t\t\tevents,\n\t\t};\n\t}\n\n\tprivate classifyRejectedEvents(\n\t\tevents: V2EventEnvelope[],\n\t\trejected: V2BatchRejectedEvent[],\n\t): {\n\t\tretryable: V2EventEnvelope[];\n\t\tpermanent: V2EventEnvelope[];\n\t} {\n\t\tconst byId = new Map(events.map((event) => [event.id, event]));\n\t\tconst retryable: V2EventEnvelope[] = [];\n\t\tconst permanent: V2EventEnvelope[] = [];\n\n\t\tfor (const rejectedEvent of rejected) {\n\t\t\tconst event = byId.get(rejectedEvent.eventId);\n\t\t\tif (!event) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (isRetryableRejectedEvent(rejectedEvent)) {\n\t\t\t\tretryable.push(event);\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tpermanent.push(event);\n\t\t}\n\n\t\treturn { retryable, permanent };\n\t}\n\n\tprivate backoffDelayMs(attempt: number): number {\n\t\tconst rawDelay = this.retryBaseDelayMs * 2 ** attempt;\n\t\treturn Math.min(rawDelay, this.retryMaxDelayMs);\n\t}\n\n\tprivate stopTransportForAuthFailure(\n\t\tstatus: number,\n\t\trejectedCount: number,\n\t): void {\n\t\tthis.isStopped = true;\n\t\tconst buffered = this.buffer.length;\n\t\tthis.buffer.splice(0, buffered);\n\t\tthis.logger.error(\n\t\t\t\"[WaniWani] Auth failure (HTTP %d). Stopping tracking transport and dropping %d queued event(s)\",\n\t\t\tstatus,\n\t\t\trejectedCount + buffered,\n\t\t);\n\t}\n}\n\nfunction isRetryableRejectedEvent(\n\trejectedEvent: V2BatchRejectedEvent,\n): boolean {\n\tif (rejectedEvent.retryable === true) {\n\t\treturn true;\n\t}\n\tconst code = rejectedEvent.code.toLowerCase();\n\treturn (\n\t\tcode.includes(\"timeout\") ||\n\t\tcode.includes(\"temporary\") ||\n\t\tcode.includes(\"unavailable\") ||\n\t\tcode.includes(\"rate_limit\") ||\n\t\tcode.includes(\"transient\") ||\n\t\tcode.includes(\"server\")\n\t);\n}\n\nasync function parseJsonResponse<T>(\n\tresponse: Response,\n): Promise<T | undefined> {\n\tconst body = await response.text();\n\tif (!body) {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\treturn JSON.parse(body) as T;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction joinUrl(apiUrl: string, endpointPath: string): string {\n\tconst normalizedBase = apiUrl.endsWith(\"/\") ? apiUrl : `${apiUrl}/`;\n\tconst normalizedPath = endpointPath.startsWith(\"/\")\n\t\t? endpointPath.slice(1)\n\t\t: endpointPath;\n\treturn `${normalizedBase}${normalizedPath}`;\n}\n\nfunction getErrorMessage(error: unknown): string {\n\tif (error instanceof Error) {\n\t\treturn error.message;\n\t}\n\treturn String(error);\n}\n","// Tracking Module\n\nimport type { InternalConfig } from \"../types.js\";\nimport type {\n\tTrackInput,\n\tTrackingClient,\n\tTrackingShutdownOptions,\n} from \"./@types.js\";\nimport { mapTrackEventToV2 } from \"./mapper.js\";\nimport { createV2BatchTransport } from \"./transport.js\";\n\n// Re-export types\nexport type {\n\tEventType,\n\tLegacyTrackEvent,\n\tLinkClickedProperties,\n\tPurchaseCompletedProperties,\n\tQuoteSucceededProperties,\n\tToolCalledProperties,\n\tTrackEvent,\n\tTrackInput,\n\tTrackingClient,\n\tTrackingConfig,\n\tTrackingShutdownOptions,\n\tTrackingShutdownResult,\n} from \"./@types.js\";\nexport { createEventId, mapTrackEventToV2 } from \"./mapper.js\";\nexport type {\n\tV2BatchRejectedEvent,\n\tV2BatchRequest,\n\tV2BatchResponse,\n\tV2CorrelationIds,\n\tV2EnvelopeType,\n\tV2EventEnvelope,\n} from \"./v2-types.js\";\n\nexport function createTrackingClient(config: InternalConfig): TrackingClient {\n\tconst { apiUrl, apiKey, tracking } = config;\n\n\tfunction requireApiKey(): string {\n\t\tif (!apiKey) {\n\t\t\tthrow new Error(\"WANIWANI_API_KEY is not set\");\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\tconst transport = apiKey\n\t\t? createV2BatchTransport({\n\t\t\t\tapiUrl,\n\t\t\t\tapiKey,\n\t\t\t\tendpointPath: tracking.endpointPath,\n\t\t\t\tflushIntervalMs: tracking.flushIntervalMs,\n\t\t\t\tmaxBatchSize: tracking.maxBatchSize,\n\t\t\t\tmaxBufferSize: tracking.maxBufferSize,\n\t\t\t\tmaxRetries: tracking.maxRetries,\n\t\t\t\tretryBaseDelayMs: tracking.retryBaseDelayMs,\n\t\t\t\tretryMaxDelayMs: tracking.retryMaxDelayMs,\n\t\t\t\tshutdownTimeoutMs: tracking.shutdownTimeoutMs,\n\t\t\t})\n\t\t: undefined;\n\n\tconst client: TrackingClient = {\n\t\tasync identify(\n\t\t\tuserId: string,\n\t\t\tproperties?: Record<string, unknown>,\n\t\t\tmeta?: Record<string, unknown>,\n\t\t): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2({\n\t\t\t\tevent: \"user.identified\",\n\t\t\t\texternalUserId: userId,\n\t\t\t\tproperties,\n\t\t\t\tmeta,\n\t\t\t});\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync track(event: TrackInput): Promise<{ eventId: string }> {\n\t\t\trequireApiKey();\n\t\t\tconst mappedEvent = mapTrackEventToV2(event);\n\t\t\ttransport?.enqueue(mappedEvent);\n\t\t\treturn { eventId: mappedEvent.id };\n\t\t},\n\t\tasync flush(): Promise<void> {\n\t\t\trequireApiKey();\n\t\t\tawait transport?.flush();\n\t\t},\n\t\tasync shutdown(options?: TrackingShutdownOptions) {\n\t\t\trequireApiKey();\n\t\t\treturn (\n\t\t\t\t(await transport?.shutdown({\n\t\t\t\t\ttimeoutMs: options?.timeoutMs ?? tracking.shutdownTimeoutMs,\n\t\t\t\t})) ?? { timedOut: false, pendingEvents: 0 }\n\t\t\t);\n\t\t},\n\t};\n\n\tif (transport) {\n\t\tattachShutdownHooks(client, tracking.shutdownTimeoutMs);\n\t}\n\treturn client;\n}\n\nfunction attachShutdownHooks(\n\tclient: TrackingClient,\n\tdefaultTimeoutMs: number,\n): void {\n\tif (\n\t\ttypeof process === \"undefined\" ||\n\t\ttypeof process.once !== \"function\" ||\n\t\ttypeof process.on !== \"function\"\n\t) {\n\t\treturn;\n\t}\n\n\tconst shutdown = () => {\n\t\tvoid client.shutdown({ timeoutMs: defaultTimeoutMs });\n\t};\n\n\tprocess.once(\"beforeExit\", shutdown);\n\tprocess.once(\"SIGINT\", shutdown);\n\tprocess.once(\"SIGTERM\", shutdown);\n}\n","// WaniWani SDK - Main Entry\n\nimport { createKbClient } from \"./kb/client.js\";\nimport {\n\tgetGlobalConfig,\n\tloadProjectConfig,\n\ttype WaniWaniProjectConfig,\n} from \"./project-config.js\";\nimport { createTrackingClient } from \"./tracking/index.js\";\nimport type { WaniWaniClient, WaniWaniConfig } from \"./types.js\";\n\n/**\n * Create a WaniWani SDK client\n *\n * @param config - Configuration options. When omitted, reads `waniwani.json`\n * from the current working directory, then falls back to env vars.\n * @returns A fully typed WaniWani client\n *\n * @example\n * ```typescript\n * import { waniwani } from \"@waniwani/sdk\";\n * import { toNextJsHandler } from \"@waniwani/sdk/next-js\";\n *\n * const wani = waniwani({ apiKey: \"...\" });\n *\n * // Next.js route handler\n * export const { GET, POST } = toNextJsHandler(wani, {\n * chat: { systemPrompt: \"You are a helpful assistant.\" },\n * });\n * ```\n */\nexport function waniwani(\n\tconfig?: WaniWaniConfig | WaniWaniProjectConfig,\n): WaniWaniClient {\n\tconst projectConfig = config ?? loadProjectConfig() ?? getGlobalConfig();\n\tconst effective = projectConfig as\n\t\t| (WaniWaniConfig & WaniWaniProjectConfig)\n\t\t| undefined;\n\n\tconst apiUrl = effective?.apiUrl ?? \"https://app.waniwani.ai\";\n\tconst apiKey = effective?.apiKey ?? process.env.WANIWANI_API_KEY;\n\tconst trackingConfig = {\n\t\tendpointPath:\n\t\t\teffective?.tracking?.endpointPath ?? \"/api/mcp/events/v2/batch\",\n\t\tflushIntervalMs: effective?.tracking?.flushIntervalMs ?? 1_000,\n\t\tmaxBatchSize: effective?.tracking?.maxBatchSize ?? 20,\n\t\tmaxBufferSize: effective?.tracking?.maxBufferSize ?? 1_000,\n\t\tmaxRetries: effective?.tracking?.maxRetries ?? 3,\n\t\tretryBaseDelayMs: effective?.tracking?.retryBaseDelayMs ?? 200,\n\t\tretryMaxDelayMs: effective?.tracking?.retryMaxDelayMs ?? 2_000,\n\t\tshutdownTimeoutMs: effective?.tracking?.shutdownTimeoutMs ?? 2_000,\n\t};\n\n\tconst internalConfig = { apiUrl, apiKey, tracking: trackingConfig };\n\n\t// Compose client from modules\n\tconst trackingClient = createTrackingClient(internalConfig);\n\tconst kbClient = createKbClient(internalConfig);\n\n\treturn {\n\t\t...trackingClient,\n\t\tkb: kbClient,\n\t\t_config: internalConfig,\n\t};\n}\n"],"mappings":"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","names":["WaniWaniError","message","status","existsSync","readFileSync","resolve","CONFIG_FILENAME","_cached","loadProjectConfig","filePath","raw","GLOBAL_KEY","defineConfig","config","getGlobalConfig","SDK_NAME","createKbClient","config","apiUrl","apiKey","requireApiKey","request","method","path","body","key","url","headers","init","response","text","WaniWaniError","files","query","options","pickFirst","meta","keys","key","value","SESSION_ID_KEYS","REQUEST_ID_KEYS","TRACE_ID_KEYS","EXTERNAL_USER_ID_KEYS","CORRELATION_ID_KEYS","extractSessionId","meta","pickFirst","SESSION_ID_KEYS","extractRequestId","REQUEST_ID_KEYS","extractTraceId","TRACE_ID_KEYS","extractExternalUserId","EXTERNAL_USER_ID_KEYS","extractCorrelationId","CORRELATION_ID_KEYS","SOURCE_SESSION_KEYS","extractSource","meta","explicit","key","source","value","DEFAULT_SOURCE","mapTrackEventToV2","input","options","now","generateId","createEventId","eventName","resolveEventName","meta","toRecord","metadata","correlation","resolveCorrelationIds","eventId","takeNonEmptyString","timestamp","normalizeTimestamp","source","extractSource","rawLegacy","isLegacyTrackEvent","mappedMetadata","mapProperties","legacyProperties","mapLegacyProperties","explicitProperties","properties","requestId","extractRequestId","sessionId","extractSessionId","traceId","extractTraceId","externalUserId","extractExternalUserId","correlationId","extractCorrelationId","date","value","DEFAULT_ENDPOINT_PATH","SDK_NAME","AUTH_FAILURE_STATUS","RETRYABLE_STATUS","createV2BatchTransport","options","BatchingV2Transport","joinUrl","DEFAULT_ENDPOINT_PATH","delayMs","resolve","event","dropCount","timeoutMs","flushPromise","timeoutSignal","batch","attempt","pendingBatch","result","events","response","error","getErrorMessage","data","parseJsonResponse","partial","rejected","byId","retryable","permanent","rejectedEvent","isRetryableRejectedEvent","rawDelay","status","rejectedCount","buffered","code","body","apiUrl","endpointPath","normalizedBase","normalizedPath","createTrackingClient","config","apiUrl","apiKey","tracking","requireApiKey","transport","createV2BatchTransport","client","userId","properties","meta","mappedEvent","mapTrackEventToV2","event","options","attachShutdownHooks","defaultTimeoutMs","shutdown","waniwani","config","effective","loadProjectConfig","getGlobalConfig","apiUrl","apiKey","trackingConfig","internalConfig","trackingClient","createTrackingClient","kbClient","createKbClient"]}
@@ -321,6 +321,22 @@ type RegisteredTool = {
321
321
  register: (server: McpServer) => Promise<void>;
322
322
  };
323
323
 
324
+ /**
325
+ * Compact, JSON-serializable schema fragment for a single state field.
326
+ * Emitted inside interrupt/widget responses so the LLM gets focused, just-in-time
327
+ * guidance for the field it is being asked to write — without needing to consult
328
+ * a global schema dump.
329
+ */
330
+ type FieldSchemaInfo = {
331
+ type: "enum" | "string" | "number" | "boolean" | "object" | "array" | "unknown";
332
+ /** Allowed values for enum fields. */
333
+ values?: string[];
334
+ /** Field description from `.describe()` (or the parent group description if applicable). */
335
+ description?: string;
336
+ /** True when the underlying schema is optional/nullable/has a default. */
337
+ optional?: boolean;
338
+ };
339
+
324
340
  /**
325
341
  * Server-side flow state store.
326
342
  *
@@ -368,7 +384,11 @@ type WidgetSignal = {
368
384
  tool: string;
369
385
  /** Data to pass to the display tool */
370
386
  data: Record<string, unknown>;
371
- /** Description of what the widget does (for the AI's context) */
387
+ /**
388
+ * @deprecated The engine now emits a standardized instruction telling the AI
389
+ * to call the widget tool. Return any per-widget description from the widget
390
+ * tool's own response instead — that's where it belongs.
391
+ */
372
392
  description?: string;
373
393
  /**
374
394
  * Whether the user is expected to interact with the widget before the flow continues.
@@ -451,6 +471,11 @@ type TypedInterrupt<TState> = (fields: {
451
471
  */
452
472
  type TypedShowWidget<TState> = (tool: RegisteredTool | string, config: {
453
473
  data: Record<string, unknown>;
474
+ /**
475
+ * @deprecated The engine now emits a standardized instruction telling the AI
476
+ * to call the widget tool. Return any per-widget description from the widget
477
+ * tool's own response instead — that's where it belongs.
478
+ */
454
479
  description?: string;
455
480
  interactive?: boolean;
456
481
  field?: FieldPaths<TState>;
@@ -630,6 +655,12 @@ type InterruptQuestionData = {
630
655
  field: string;
631
656
  suggestions?: string[];
632
657
  context?: string;
658
+ /**
659
+ * JIT schema fragment for the field, derived from the flow's state schema.
660
+ * The LLM uses this to know the field's type, allowed values, and meaning
661
+ * at the exact moment it needs to fill it — without consulting a global dump.
662
+ */
663
+ fieldSchema?: FieldSchemaInfo;
633
664
  };
634
665
  type FlowInterruptContent = {
635
666
  status: "interrupt";
@@ -637,6 +668,8 @@ type FlowInterruptContent = {
637
668
  question?: string;
638
669
  field?: string;
639
670
  suggestions?: string[];
671
+ /** JIT schema fragment for the single-question shorthand. */
672
+ fieldSchema?: FieldSchemaInfo;
640
673
  /** Multi-question */
641
674
  questions?: InterruptQuestionData[];
642
675
  context?: string;
@@ -647,9 +680,18 @@ type FlowWidgetContent = {
647
680
  tool: string;
648
681
  /** Data to pass to the display tool */
649
682
  data: Record<string, unknown>;
683
+ /**
684
+ * Standardized instruction to the AI to call the widget tool. Auto-generated
685
+ * by the engine from the tool id; the value previously passed via
686
+ * `showWidget({ description })` is ignored.
687
+ */
650
688
  description?: string;
651
689
  /** Whether the widget expects user interaction before continuing */
652
690
  interactive?: boolean;
691
+ /** State field the widget fills, when declared via `showWidget({ field })`. */
692
+ field?: string;
693
+ /** JIT schema fragment for the field the widget fills. */
694
+ fieldSchema?: FieldSchemaInfo;
653
695
  };
654
696
  type FlowCompleteContent = {
655
697
  status: "complete";