@opencxh/domain 1.125.0 → 1.127.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.
@@ -1 +1,2 @@
1
1
  export * from './types';
2
+ export * from './vendors';
@@ -3,6 +3,12 @@ export interface AiAccount {
3
3
  organizationId: string;
4
4
  vendor: string;
5
5
  apiKey: string;
6
+ /**
7
+ * API-host voor dit account. Alleen in te vullen bij vendors zonder vaste
8
+ * host (self-hosted: Ollama, vLLM, een eigen gateway) — bij de rest komt de
9
+ * host uit `AI_VENDORS` en wordt dit veld genegeerd.
10
+ */
11
+ baseUrl?: string;
6
12
  isDefaultLlm?: boolean;
7
13
  isDefaultStt?: boolean;
8
14
  isDefaultTts?: boolean;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * De AI-vendors waar de ai-app een adapter voor heeft.
3
+ *
4
+ * Eén bron voor zowel de UI (accountformulier, tabellabels) als de server
5
+ * (default-model, API-host). Eerder stonden die lijsten los van elkaar en
6
+ * liepen ze uiteen — de UI noemde vendors waarvoor geen adapter bestond.
7
+ *
8
+ * Deze catalogus beschrijft *wat* een vendor is. *Hoe* je ertegen praat (welke
9
+ * adapterklasse, welk model-filter) staat server-side in `providers/registry`,
10
+ * die zich tegen deze lijst verantwoordt: een vendor die hier `"chat"` claimt
11
+ * en daar geen chat-adapter heeft, is een testfout.
12
+ *
13
+ * `id` is wat er in `AiAccount.vendor` belandt; die string is dus onderdeel van
14
+ * het opslagformaat en mag niet zomaar hernoemd worden.
15
+ */
16
+ export type AiVendorCapability = "chat" | "transcription" | "embedding";
17
+ export interface AiVendor {
18
+ id: string;
19
+ label: string;
20
+ /** Wat deze vendor kan. Bepaalt waar hij in de UI als optie verschijnt. */
21
+ capabilities: readonly AiVendorCapability[];
22
+ /**
23
+ * Model waarmee een aanroep draait die zelf geen model meekrijgt. Afwezig
24
+ * voor vendors zonder chat-adapter.
25
+ */
26
+ defaultModel?: string;
27
+ /**
28
+ * Vaste API-host. Afwezig betekent dat de user er zelf een moet invullen
29
+ * op het account (`AiAccount.baseUrl`) — dat is het geval bij self-hosted
30
+ * runtimes en eigen gateways.
31
+ */
32
+ baseUrl?: string;
33
+ }
34
+ export declare const AI_VENDORS: readonly AiVendor[];
35
+ export declare function findAiVendor(vendorId: string): AiVendor | undefined;
36
+ /** Leesbare naam voor een opgeslagen vendor-id; valt terug op de id zelf. */
37
+ export declare function aiVendorLabel(vendorId: string): string;
38
+ /**
39
+ * Default-model voor een vendor. De fallback is bewust een bestaand model en
40
+ * geen lege string: callers gebruiken dit juist omdat ze zelf geen keuze
41
+ * hebben.
42
+ */
43
+ export declare function aiVendorDefaultModel(vendorId: string, fallback?: string): string;
44
+ /** Vendors die deze capability aanbieden — voor capability-gefilterde keuzelijsten. */
45
+ export declare function aiVendorsWith(capability: AiVendorCapability): AiVendor[];
46
+ /** True wanneer de user zelf een host moet invullen (vendor heeft er geen vaste). */
47
+ export declare function aiVendorNeedsBaseUrl(vendorId: string): boolean;
@@ -5,3 +5,4 @@ export * from './source';
5
5
  export * from './report';
6
6
  export * from './dashboard';
7
7
  export * from './period';
8
+ export * from './usage';
@@ -0,0 +1,56 @@
1
+ import { MetricDefinition } from './metric';
2
+ /**
3
+ * Gedeeld vocabulaire voor verbruik — wat een app aan externe middelen opmaakt.
4
+ *
5
+ * Bewust géén centrale usage-tabel. Apps bezitten hun eigen ruwe rijen (dat is
6
+ * hoe de rest van de analytics-keten al werkt) en rollen die zelf op. Een
7
+ * centrale tabel zou apps die de data al hebben — een voip-app heeft z'n
8
+ * gesprekken al staan — dwingen alles dubbel weg te schrijven.
9
+ *
10
+ * Wat hier wél gedeeld wordt is de *taal*: dezelfde assen en dezelfde
11
+ * metric-naamgeving, zodat AI-tokens en belminuten naast elkaar in één
12
+ * dashboard passen en je ze op `provider` tegen elkaar kunt afzetten.
13
+ *
14
+ * Een app die verbruik wil rapporteren:
15
+ * 1. rolt z'n eigen rijen op met `FactAccumulator` (platform-api)
16
+ * 2. emit op de assen hieronder + de core-dimensie `provider`
17
+ * 3. declareert `usageMetrics("voip", [{ unit: "call_minutes", ... }])`
18
+ * in z'n `/analytics/describe`
19
+ */
20
+ /**
21
+ * Waar het verbruik in wordt uitgedrukt. Open string — een app mag een eigen
22
+ * eenheid declareren; deze staan hier omdat ze naar verwachting terugkomen.
23
+ */
24
+ export type UsageUnit = "input_tokens" | "output_tokens" | "cached_input_tokens" | "requests" | "seconds" | "bytes" | (string & {});
25
+ /**
26
+ * Verbruiksdimensies die meerdere apps delen. Aanvullend op de core-dimensie
27
+ * `provider` (welke leverancier), die hiervoor al bestond.
28
+ *
29
+ * `resource` — het specifieke ding dat verbruikt is: een model-id bij AI, een
30
+ * lijn of nummer bij voip, een bucket bij opslag.
31
+ * `origin` — de functie die het verbruik veroorzaakte: een gesprek, een
32
+ * playbook, een samenvatting. Beantwoordt "waar gaat het geld heen", wat een
33
+ * andere vraag is dan "bij wie".
34
+ */
35
+ export declare const USAGE_DIMENSIONS: {
36
+ id: string;
37
+ label: string;
38
+ labelSource: "raw";
39
+ }[];
40
+ export declare const USAGE_DIMENSION_IDS: string[];
41
+ /** Metric-id volgens de gedeelde conventie: `<app>.usage.<unit>`. */
42
+ export declare function usageMetricId(app: string, unit: UsageUnit): string;
43
+ export interface UsageMetricSpec {
44
+ unit: UsageUnit;
45
+ label: string;
46
+ /** Default `count` — tokens, requests en bytes zijn allemaal tellingen. */
47
+ valueType?: MetricDefinition["valueType"];
48
+ /** Extra assen bovenop provider/resource/origin/time. */
49
+ extraDimensions?: string[];
50
+ }
51
+ /**
52
+ * Bouwt de `MetricDefinition`s voor een app z'n verbruiksmetrics, met overal
53
+ * dezelfde assen. Scheelt elke app dezelfde zes regels boilerplate en zorgt dat
54
+ * de definities niet per app uiteenlopen.
55
+ */
56
+ export declare function usageMetrics(app: string, specs: UsageMetricSpec[]): MetricDefinition[];
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=140;function g(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/\s+/g," ").trim()}function M(e){const t=e.trim();return t.length<=c?t:t.slice(0,c-1).trimEnd()+"…"}function i(e){if(!e||e<0)return"";const t=Math.floor(e/60),n=Math.floor(e%60);return`${t}:${n.toString().padStart(2,"0")}`}function C(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||g(t.body??"");return t.subject?`${t.subject} — ${n}`:n}case"CHAT_MESSAGE_SENT":case"CHAT_MESSAGE_RECEIVED":return e.payload.text??"";case"CHAT_RENAMED":return`Hernoemd naar "${e.payload.newName}"`;case"CHAT_MEMBER_JOINED":return`${e.payload.members.map(t=>t.name).join(", ")} toegevoegd`;case"CHAT_MEMBER_LEFT":return`${e.payload.members.map(t=>t.name).join(", ")} verlaten`;case"CHAT_CALL_STARTED":return"Gesprek gestart";case"CHAT_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${i(e.payload.duration)})`:""}`;case"CHAT_EVENT":return e.payload.text??e.payload.eventType;case"VOICE_CALL_STARTED":case"VIDEO_CALL_STARTED":return"Gesprek gestart";case"VOICE_CALL_ANSWERED":case"VIDEO_CALL_ANSWERED":return"Gesprek aangenomen";case"VOICE_CALL_HOLD":case"VIDEO_CALL_HOLD":return"In de wacht";case"VOICE_CALL_UNHOLD":case"VIDEO_CALL_UNHOLD":return"Hervat";case"VOICE_CALL_ENDED":case"VIDEO_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${i(e.payload.duration)})`:""}`;case"VOICE_CALL_MISSED":case"VIDEO_CALL_MISSED":return"Gemiste oproep";case"VOICE_CALL_FAILED":case"VIDEO_CALL_FAILED":return"Gesprek mislukt";case"VOICE_CALL_VOICEMAIL":return e.payload.transcription?.trim()?e.payload.transcription:"Voicemail ontvangen";case"TRANSCRIPT_ADDED":return(e.payload.segments??[]).map(t=>t.text).join(" ");case"AI_MESSAGE_ADDED":return e.payload.output?.text??e.payload.input?.text??"";case"AI_ACTION_PROPOSED":return`Voorstel wacht op goedkeuring (${e.payload.actions?.length??0} actie(s))`;case"PLAYBOOK_STARTED":return`Playbook gestart${e.payload.playbookName?`: ${e.payload.playbookName}`:""}`;case"PLAYBOOK_COMPLETED":return"Playbook afgerond";case"PLAYBOOK_ESCALATED":return"Playbook geëscaleerd naar een mens";case"MEETING_SCHEDULED":return`Vergadering gepland: ${e.payload.title}`;case"MEETING_STARTED":return`Vergadering gestart: ${e.payload.title}`;case"MEETING_ENDED":return`Vergadering beëindigd${e.payload.duration?` (${i(e.payload.duration)})`:""}`;case"MEETING_PARTICIPANT_JOINED":return`${e.payload.name} deelgenomen`;case"MEETING_PARTICIPANT_LEFT":return`${e.payload.name} verlaten`;case"COMMENT_ADDED":return e.payload.text??"";case"FILE_UPLOADED":return`Bestand: ${e.payload.fileName}`;case"INTERACTION_CREATED":return"Interactie aangemaakt";case"INTERACTION_STATUS_CHANGED":return`Status: ${e.payload.fromStatus} → ${e.payload.toStatus}`;case"INTERACTION_ASSIGNED":return"Interactie toegewezen";default:return""}}function h(e){return{activityId:e.id,type:e.type,snippet:M(C(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function U(e,t,n=[]){const r=e.createdAt;if(!r)return[];const s=new Set(n);return e.author.type==="user"&&e.author.id&&s.add(e.author.id),Object.entries(t).filter(([a,o])=>o>=r&&!s.has(a)).map(([a])=>a)}const P=[{id:"agent",label:"Agent",labelSource:"user"},{id:"team",label:"Team",labelSource:"team"},{id:"inbox",label:"Inbox",labelSource:"inbox"},{id:"channel",label:"Kanaal",labelSource:"channel"},{id:"provider",label:"Provider",labelSource:"raw"},{id:"status",label:"Status",labelSource:"raw"},{id:"handledBy",label:"Afhandelaar",labelSource:"raw"},{id:"time",label:"Tijd",labelSource:"raw"}];function $(e){const t=[];for(const n of Object.keys(e))for(const r of Object.keys(e[n]))t.push({lang:n,key:r,value:e[n][r]});return t}function l(e){return e<10?`0${e}`:`${e}`}function A(e,t){const n=new Date(e),r=`${n.getUTCFullYear()}-${l(n.getUTCMonth()+1)}-${l(n.getUTCDate())}`;return t==="day"?r:`${r}T${l(n.getUTCHours())}`}function f(e,t){const n=new Date(e);return n.setUTCMinutes(0,0,0),t==="day"&&n.setUTCHours(0),n.getTime()}const k=36e5,G=864e5;function V(e,t,n){const r=n==="hour"?k:G,s=[];for(let a=f(e,n);a<=t;a+=r)s.push(A(a,n));return s}function H(e){return(e.startsWith("tool:")?e.slice(5):e).replace(/__/g," · ").replace(/\./g," · ").replace(/_/g," ")}const w=[{name:"text",type:"string"},{name:"subject",type:"string"},{name:"from",type:"string"},{name:"type",type:"string"},{name:"direction",type:"string"},{name:"channelId",type:"string"},{name:"interactionId",type:"string"},{name:"activityId",type:"string"},{name:"authorType",type:"string"},{name:"authorName",type:"string"},{name:"textRaw",type:"string"}],F={topics:[{name:"topic",type:"string"},{name:"confidence",type:"number"}],question:[{name:"answer",type:"boolean"},{name:"confidence",type:"number"}],extract:[]};function I(e){switch(e.kind){case"user":return`user:${e.userId}`;case"team":return`team:${e.teamId}`;case"org":return"org"}}function x(e){if(typeof e!="string")return;const t=e.trim();if(t==="org")return{kind:"org"};const n=t.indexOf(":");if(n<=0)return;const r=t.slice(0,n),s=t.slice(n+1).trim();if(s){if(r==="user")return{kind:"user",userId:s};if(r==="team")return{kind:"team",teamId:s}}}function _(e){return I(e)}function B(e){switch(e.kind){case"personal":return{kind:"user",userId:e.userId};case"team":return{kind:"team",teamId:e.teamId};case"org":return{kind:"org"}}}function j(e){return _(e)}const S=["done","escalated","failed","timed_out","stopped"],T=["awaiting_approval","awaiting_reply"];function m(e){return S.includes(e)}function W(e){return m(e)}function K(e){return T.includes(e)}const Y=new Set(["mail","message"]);function R(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function q(e,t,n,r="html"){if(!e)return n;const s=R(e,t);return s?`${n}${r==="text"?`
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const d=140;function A(e){return e.replace(/<style[\s\S]*?<\/style>/gi," ").replace(/<script[\s\S]*?<\/script>/gi," ").replace(/<[^>]+>/g," ").replace(/&nbsp;/g," ").replace(/&amp;/g,"&").replace(/&lt;/g,"<").replace(/&gt;/g,">").replace(/&quot;/g,'"').replace(/&#39;/g,"'").replace(/\s+/g," ").trim()}function k(e){const t=e.trim();return t.length<=d?t:t.slice(0,d-1).trimEnd()+"…"}function l(e){if(!e||e<0)return"";const t=Math.floor(e/60),n=Math.floor(e%60);return`${t}:${n.toString().padStart(2,"0")}`}function V(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||A(t.body??"");return t.subject?`${t.subject} — ${n}`:n}case"CHAT_MESSAGE_SENT":case"CHAT_MESSAGE_RECEIVED":return e.payload.text??"";case"CHAT_RENAMED":return`Hernoemd naar "${e.payload.newName}"`;case"CHAT_MEMBER_JOINED":return`${e.payload.members.map(t=>t.name).join(", ")} toegevoegd`;case"CHAT_MEMBER_LEFT":return`${e.payload.members.map(t=>t.name).join(", ")} verlaten`;case"CHAT_CALL_STARTED":return"Gesprek gestart";case"CHAT_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${l(e.payload.duration)})`:""}`;case"CHAT_EVENT":return e.payload.text??e.payload.eventType;case"VOICE_CALL_STARTED":case"VIDEO_CALL_STARTED":return"Gesprek gestart";case"VOICE_CALL_ANSWERED":case"VIDEO_CALL_ANSWERED":return"Gesprek aangenomen";case"VOICE_CALL_HOLD":case"VIDEO_CALL_HOLD":return"In de wacht";case"VOICE_CALL_UNHOLD":case"VIDEO_CALL_UNHOLD":return"Hervat";case"VOICE_CALL_ENDED":case"VIDEO_CALL_ENDED":return`Gesprek beëindigd${e.payload.duration?` (${l(e.payload.duration)})`:""}`;case"VOICE_CALL_MISSED":case"VIDEO_CALL_MISSED":return"Gemiste oproep";case"VOICE_CALL_FAILED":case"VIDEO_CALL_FAILED":return"Gesprek mislukt";case"VOICE_CALL_VOICEMAIL":return e.payload.transcription?.trim()?e.payload.transcription:"Voicemail ontvangen";case"TRANSCRIPT_ADDED":return(e.payload.segments??[]).map(t=>t.text).join(" ");case"AI_MESSAGE_ADDED":return e.payload.output?.text??e.payload.input?.text??"";case"AI_ACTION_PROPOSED":return`Voorstel wacht op goedkeuring (${e.payload.actions?.length??0} actie(s))`;case"PLAYBOOK_STARTED":return`Playbook gestart${e.payload.playbookName?`: ${e.payload.playbookName}`:""}`;case"PLAYBOOK_COMPLETED":return"Playbook afgerond";case"PLAYBOOK_ESCALATED":return"Playbook geëscaleerd naar een mens";case"MEETING_SCHEDULED":return`Vergadering gepland: ${e.payload.title}`;case"MEETING_STARTED":return`Vergadering gestart: ${e.payload.title}`;case"MEETING_ENDED":return`Vergadering beëindigd${e.payload.duration?` (${l(e.payload.duration)})`:""}`;case"MEETING_PARTICIPANT_JOINED":return`${e.payload.name} deelgenomen`;case"MEETING_PARTICIPANT_LEFT":return`${e.payload.name} verlaten`;case"COMMENT_ADDED":return e.payload.text??"";case"FILE_UPLOADED":return`Bestand: ${e.payload.fileName}`;case"INTERACTION_CREATED":return"Interactie aangemaakt";case"INTERACTION_STATUS_CHANGED":return`Status: ${e.payload.fromStatus} → ${e.payload.toStatus}`;case"INTERACTION_ASSIGNED":return"Interactie toegewezen";default:return""}}function G(e){return{activityId:e.id,type:e.type,snippet:k(V(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function $(e,t,n=[]){const r=e.createdAt;if(!r)return[];const a=new Set(n);return e.author.type==="user"&&e.author.id&&a.add(e.author.id),Object.entries(t).filter(([s,i])=>i>=r&&!a.has(s)).map(([s])=>s)}const w=[{id:"agent",label:"Agent",labelSource:"user"},{id:"team",label:"Team",labelSource:"team"},{id:"inbox",label:"Inbox",labelSource:"inbox"},{id:"channel",label:"Kanaal",labelSource:"channel"},{id:"provider",label:"Provider",labelSource:"raw"},{id:"status",label:"Status",labelSource:"raw"},{id:"handledBy",label:"Afhandelaar",labelSource:"raw"},{id:"time",label:"Tijd",labelSource:"raw"}];function H(e){const t=[];for(const n of Object.keys(e))for(const r of Object.keys(e[n]))t.push({lang:n,key:r,value:e[n][r]});return t}function c(e){return e<10?`0${e}`:`${e}`}function I(e,t){const n=new Date(e),r=`${n.getUTCFullYear()}-${c(n.getUTCMonth()+1)}-${c(n.getUTCDate())}`;return t==="day"?r:`${r}T${c(n.getUTCHours())}`}function S(e,t){const n=new Date(e);return n.setUTCMinutes(0,0,0),t==="day"&&n.setUTCHours(0),n.getTime()}const B=36e5,x=864e5;function F(e,t,n){const r=n==="hour"?B:x,a=[];for(let s=S(e,n);s<=t;s+=r)a.push(I(s,n));return a}const b=[{id:"resource",label:"Resource",labelSource:"raw"},{id:"origin",label:"Herkomst",labelSource:"raw"}],_=b.map(e=>e.id);function m(e,t){return`${e}.usage.${t}`}function v(e,t){return t.map(n=>({id:m(e,n.unit),label:n.label,category:"Verbruik",valueType:n.valueType??"count",aggregation:"sum",supportedDimensions:["provider",..._,...n.extraDimensions??[],"time"]}))}function W(e){return(e.startsWith("tool:")?e.slice(5):e).replace(/__/g," · ").replace(/\./g," · ").replace(/_/g," ")}const j=[{name:"text",type:"string"},{name:"subject",type:"string"},{name:"from",type:"string"},{name:"type",type:"string"},{name:"direction",type:"string"},{name:"channelId",type:"string"},{name:"interactionId",type:"string"},{name:"activityId",type:"string"},{name:"authorType",type:"string"},{name:"authorName",type:"string"},{name:"textRaw",type:"string"}],K={topics:[{name:"topic",type:"string"},{name:"confidence",type:"number"}],question:[{name:"answer",type:"boolean"},{name:"confidence",type:"number"}],extract:[]};function T(e){switch(e.kind){case"user":return`user:${e.userId}`;case"team":return`team:${e.teamId}`;case"org":return"org"}}function Y(e){if(typeof e!="string")return;const t=e.trim();if(t==="org")return{kind:"org"};const n=t.indexOf(":");if(n<=0)return;const r=t.slice(0,n),a=t.slice(n+1).trim();if(a){if(r==="user")return{kind:"user",userId:a};if(r==="team")return{kind:"team",teamId:a}}}function R(e){return T(e)}function q(e){switch(e.kind){case"personal":return{kind:"user",userId:e.userId};case"team":return{kind:"team",teamId:e.teamId};case"org":return{kind:"org"}}}function z(e){return R(e)}const N=["done","escalated","failed","timed_out","stopped"],M=["awaiting_approval","awaiting_reply"];function D(e){return N.includes(e)}function J(e){return D(e)}function Q(e){return M.includes(e)}const u=[{id:"openai",label:"OpenAI",capabilities:["chat","transcription","embedding"],defaultModel:"gpt-5-mini",baseUrl:"https://api.openai.com/v1"},{id:"gemini",label:"Google Gemini",capabilities:["chat","transcription","embedding"],defaultModel:"gemini-3.5-flash",baseUrl:"https://generativelanguage.googleapis.com/v1beta"},{id:"anthropic",label:"Anthropic",capabilities:["chat"],defaultModel:"claude-haiku-4-5",baseUrl:"https://api.anthropic.com/v1"},{id:"elevenlabs",label:"ElevenLabs",capabilities:["transcription"],baseUrl:"https://api.elevenlabs.io/v1"},{id:"moonshot",label:"Moonshot (Kimi)",capabilities:["chat"],defaultModel:"kimi-k3",baseUrl:"https://api.moonshot.ai/v1"},{id:"deepseek",label:"DeepSeek",capabilities:["chat"],defaultModel:"deepseek-v4-flash",baseUrl:"https://api.deepseek.com/v1"},{id:"groq",label:"Groq",capabilities:["chat"],defaultModel:"openai/gpt-oss-120b",baseUrl:"https://api.groq.com/openai/v1"},{id:"mistral",label:"Mistral",capabilities:["chat"],defaultModel:"mistral-large-latest",baseUrl:"https://api.mistral.ai/v1"},{id:"openrouter",label:"OpenRouter",capabilities:["chat"],defaultModel:"openrouter/auto",baseUrl:"https://openrouter.ai/api/v1"},{id:"ollama",label:"Ollama (self-hosted)",capabilities:["chat"],defaultModel:"llama3.3"},{id:"openai-compatible",label:"OpenAI-compatible (eigen endpoint)",capabilities:["chat"],defaultModel:"default"}];function o(e){return u.find(t=>t.id===e)}function X(e){return o(e)?.label??e}function Z(e,t="gpt-5-mini"){return o(e)?.defaultModel??t}function ee(e){return u.filter(t=>t.capabilities.includes(e))}function te(e){const t=o(e);return!!t&&!t.baseUrl}const ne=new Set(["mail","message"]);function O(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function re(e,t,n,r="html"){if(!e)return n;const a=O(e,t);return a?`${n}${r==="text"?`
2
2
 
3
- `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${s.body}`:n}function z(e){return e.endpoints??[]}const v=e=>!!e.assignedUserId||!!e.assignedInboxId,J=e=>e.status==="closed",Q=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,X=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function Z(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const ee=[/<blockquote/i,/class="?gmail_quote/i,/id="?[^"]*divRplyFwdMsg/i,/id="?[^"]*mail-editor-reference-message-container/i,/-{3,}\s*Original Message\s*-{3,}/i,/\n_{5,}\s*\n/,/\bOn\b[\s\S]{0,200}?\bwrote:/i],u=/^\s*(?:>+\s*)?(?:from|van|sent|verzonden|to|aan|cc|subject|onderwerp|date|datum)\s*:/i,te=/^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;function ne(e){const t=e.split(`
4
- `);for(let n=0;n<t.length;n++)if(te.test(t[n])||u.test(t[n])&&t.slice(n+1,n+4).some(r=>u.test(r)))return t.slice(0,n).join(`
3
+ `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${a.body}`:n}function ae(e){return e.endpoints??[]}const se=e=>!!e.assignedUserId||!!e.assignedInboxId,ie=e=>e.status==="closed",oe=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,le=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function ce(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const ue=[/<blockquote/i,/class="?gmail_quote/i,/id="?[^"]*divRplyFwdMsg/i,/id="?[^"]*mail-editor-reference-message-container/i,/-{3,}\s*Original Message\s*-{3,}/i,/\n_{5,}\s*\n/,/\bOn\b[\s\S]{0,200}?\bwrote:/i],p=/^\s*(?:>+\s*)?(?:from|van|sent|verzonden|to|aan|cc|subject|onderwerp|date|datum)\s*:/i,de=/^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;function pe(e){const t=e.split(`
4
+ `);for(let n=0;n<t.length;n++)if(de.test(t[n])||p.test(t[n])&&t.slice(n+1,n+4).some(r=>p.test(r)))return t.slice(0,n).join(`
5
5
  `).trim();return e}function E(e){let t=e;return t=t.replace(/<(script|style)[\s\S]*?<\/\1>/gi,""),t=t.replace(/<br\s*\/?>/gi,`
6
6
  `),t=t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi,`
7
- `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/<[^>]*$/,""),t}function d(e){return e.replace(/&nbsp;/gi," ").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(Number(n))).replace(/&amp;/gi,"&")}function p(e){return e.replace(/\r/g,"").replace(/[ \t]+/g," ").replace(/ *\n */g,`
7
+ `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/<[^>]*$/,""),t}function g(e){return e.replace(/&nbsp;/gi," ").replace(/&lt;/gi,"<").replace(/&gt;/gi,">").replace(/&quot;/gi,'"').replace(/&#39;/gi,"'").replace(/&#(\d+);/g,(t,n)=>String.fromCharCode(Number(n))).replace(/&amp;/gi,"&")}function f(e){return e.replace(/\r/g,"").replace(/[ \t]+/g," ").replace(/ *\n */g,`
8
8
  `).replace(/\n{3,}/g,`
9
9
 
10
- `).trim()}function re(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const a of ee){const o=e.search(a);o>=0&&o<t&&(t=o)}const n=e.slice(0,t);let r=p(d(E(n)));return r||(r=p(d(E(e)))),ne(r)||r}const se=/(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i,ae=/\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;function oe(e,t){return!!(e&&se.test(e)||t&&ae.test(t))}const b=3,N=600;function ie(e,t){return e>=b||t>=N}function D(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(o=>!n.has(o.intent)).map(o=>ce(o,r[o.intent]));if(!e.extraIntents||e.extraIntents.length===0)return s;const a=new Set(s.map(o=>o.intent));for(const o of e.extraIntents)a.has(o.intent)||(s.push(o),a.add(o.intent));return s}function y(e,t){const n={};for(const r of e.intents){if(!r.togglable)continue;const s=t?.[r.intent];n[r.intent]=s??r.defaultEnabled??!0}return n}function le(e,t){const n=y(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function ce(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function ue(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const a of D(r,s))n.push({channel:r,description:s,capability:a})}return n}function Ee(e,t){return t.filter(n=>n.capability.intent===e)}function L(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function de(e,t){return L(e.scheme,t)}function pe(e,t,n){const r=[];for(const s of e)for(const a of n)a.capability.intent===t&&a.capability.targetSchemes.includes(s.scheme)&&r.push({channelIntent:a,endpoint:s});return r}var O=(e=>(e.MAILTO="mailto",e.SIP="sip",e.TEL="tel",e.WEBHOOK="webhook",e.USERNAME="username",e.ID="id",e.CUSTOM="custom",e.URL="url",e.TELEGRAM="telegram",e.WHATSAPP="whatsapp",e.MESSENGER="messenger",e.INSTAGRAM="instagram",e.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e.CALENDAR="calendar",e))(O||{});const ge="message_window",Ae="message_templates",fe={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},Ie=(e,t)=>({intent:e,...t}),_e="folder_management",Se="remote_search",Te={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},me=9e4,Re="installation-id";exports.CLASSIFY_VARS=F;exports.CORE_DIMENSIONS=P;exports.CommunicationScheme=O;exports.FEATURE_FOLDER_MANAGEMENT=_e;exports.FEATURE_REMOTE_SEARCH=Se;exports.INSTALLATION_HEADER=Re;exports.InteractionParticipantRole=fe;exports.PAUSED_RUN_STATUSES=T;exports.PRESENCE_ONLINE_WINDOW_MS=me;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=Ae;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=ge;exports.SIGNATURE_INTENTS=Y;exports.SUMMARY_MIN_LAST_CHARS=N;exports.SUMMARY_MIN_MESSAGES=b;exports.TERMINAL_RUN_STATUSES=S;exports.TRIGGER_VARS=w;exports.UNSUPPORTED=Te;exports.actingIdentityKey=_;exports.actorKey=j;exports.applySignature=q;exports.buildActivityPreview=h;exports.buildChannelIntents=ue;exports.cleanMessageText=re;exports.defineIntent=Ie;exports.disabledIntentsFromCapabilities=le;exports.filterByEndpoint=de;exports.filterByIntent=Ee;exports.filterByTargetScheme=L;exports.flattenLocales=$;exports.floorToPeriod=f;exports.formatActingIdentity=I;exports.formatPeriod=A;exports.getActivitySeenUserIds=U;exports.getContactEndpoints=z;exports.getShortTitle=X;exports.getUrgencyScore=Q;exports.humanizeAction=H;exports.isAssigned=v;exports.isClosed=J;exports.isInteractionUnseen=Z;exports.isLikelyBulk=oe;exports.isPaused=K;exports.isRetryable=W;exports.isTerminal=m;exports.isThreadLongEnough=ie;exports.matchContactToIntents=pe;exports.parseActingIdentity=x;exports.periodsInRange=V;exports.playbookActor=B;exports.resolveAccountCapabilities=y;exports.resolveChannelIntents=D;exports.resolveSignature=R;exports.stripHtml=g;
10
+ `).trim()}function Ee(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const s of ue){const i=e.search(s);i>=0&&i<t&&(t=i)}const n=e.slice(0,t);let r=f(g(E(n)));return r||(r=f(g(E(e)))),pe(r)||r}const ge=/(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i,fe=/\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;function Ae(e,t){return!!(e&&ge.test(e)||t&&fe.test(t))}const h=3,y=600;function Ie(e,t){return e>=h||t>=y}function L(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},a=t.intents.filter(i=>!n.has(i.intent)).map(i=>be(i,r[i.intent]));if(!e.extraIntents||e.extraIntents.length===0)return a;const s=new Set(a.map(i=>i.intent));for(const i of e.extraIntents)s.has(i.intent)||(a.push(i),s.add(i.intent));return a}function C(e,t){const n={};for(const r of e.intents){if(!r.togglable)continue;const a=t?.[r.intent];n[r.intent]=a??r.defaultEnabled??!0}return n}function Se(e,t){const n=C(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function be(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function _e(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const a=t[r.providerId];if(a)for(const s of L(r,a))n.push({channel:r,description:a,capability:s})}return n}function me(e,t){return t.filter(n=>n.capability.intent===e)}function U(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function Te(e,t){return U(e.scheme,t)}function Re(e,t,n){const r=[];for(const a of e)for(const s of n)s.capability.intent===t&&s.capability.targetSchemes.includes(a.scheme)&&r.push({channelIntent:s,endpoint:a});return r}var P=(e=>(e.MAILTO="mailto",e.SIP="sip",e.TEL="tel",e.WEBHOOK="webhook",e.USERNAME="username",e.ID="id",e.CUSTOM="custom",e.URL="url",e.TELEGRAM="telegram",e.WHATSAPP="whatsapp",e.MESSENGER="messenger",e.INSTAGRAM="instagram",e.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e.CALENDAR="calendar",e))(P||{});const Ne="message_window",Me="message_templates",De={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},Oe=(e,t)=>({intent:e,...t}),he="folder_management",ye="remote_search",Le={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},Ce=9e4,Ue="installation-id";exports.AI_VENDORS=u;exports.CLASSIFY_VARS=K;exports.CORE_DIMENSIONS=w;exports.CommunicationScheme=P;exports.FEATURE_FOLDER_MANAGEMENT=he;exports.FEATURE_REMOTE_SEARCH=ye;exports.INSTALLATION_HEADER=Ue;exports.InteractionParticipantRole=De;exports.PAUSED_RUN_STATUSES=M;exports.PRESENCE_ONLINE_WINDOW_MS=Ce;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=Me;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=Ne;exports.SIGNATURE_INTENTS=ne;exports.SUMMARY_MIN_LAST_CHARS=y;exports.SUMMARY_MIN_MESSAGES=h;exports.TERMINAL_RUN_STATUSES=N;exports.TRIGGER_VARS=j;exports.UNSUPPORTED=Le;exports.USAGE_DIMENSIONS=b;exports.USAGE_DIMENSION_IDS=_;exports.actingIdentityKey=R;exports.actorKey=z;exports.aiVendorDefaultModel=Z;exports.aiVendorLabel=X;exports.aiVendorNeedsBaseUrl=te;exports.aiVendorsWith=ee;exports.applySignature=re;exports.buildActivityPreview=G;exports.buildChannelIntents=_e;exports.cleanMessageText=Ee;exports.defineIntent=Oe;exports.disabledIntentsFromCapabilities=Se;exports.filterByEndpoint=Te;exports.filterByIntent=me;exports.filterByTargetScheme=U;exports.findAiVendor=o;exports.flattenLocales=H;exports.floorToPeriod=S;exports.formatActingIdentity=T;exports.formatPeriod=I;exports.getActivitySeenUserIds=$;exports.getContactEndpoints=ae;exports.getShortTitle=le;exports.getUrgencyScore=oe;exports.humanizeAction=W;exports.isAssigned=se;exports.isClosed=ie;exports.isInteractionUnseen=ce;exports.isLikelyBulk=Ae;exports.isPaused=Q;exports.isRetryable=J;exports.isTerminal=D;exports.isThreadLongEnough=Ie;exports.matchContactToIntents=Re;exports.parseActingIdentity=Y;exports.periodsInRange=F;exports.playbookActor=q;exports.resolveAccountCapabilities=C;exports.resolveChannelIntents=L;exports.resolveSignature=O;exports.stripHtml=A;exports.usageMetricId=m;exports.usageMetrics=v;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- function E(e) {
1
+ function f(e) {
2
2
  return e.replace(/<style[\s\S]*?<\/style>/gi, " ").replace(/<script[\s\S]*?<\/script>/gi, " ").replace(/<[^>]+>/g, " ").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\s+/g, " ").trim();
3
3
  }
4
- function g(e) {
4
+ function b(e) {
5
5
  const t = e.trim();
6
6
  return t.length <= 140 ? t : t.slice(0, 139).trimEnd() + "…";
7
7
  }
@@ -10,11 +10,11 @@ function i(e) {
10
10
  const t = Math.floor(e / 60), n = Math.floor(e % 60);
11
11
  return `${t}:${n.toString().padStart(2, "0")}`;
12
12
  }
13
- function f(e) {
13
+ function A(e) {
14
14
  switch (e.type) {
15
15
  case "EMAIL_RECEIVED":
16
16
  case "EMAIL_SENT": {
17
- const t = e.payload, n = t.bodySnippet?.trim() || E(t.body ?? "");
17
+ const t = e.payload, n = t.bodySnippet?.trim() || f(t.body ?? "");
18
18
  return t.subject ? `${t.subject} — ${n}` : n;
19
19
  }
20
20
  case "CHAT_MESSAGE_SENT":
@@ -91,23 +91,23 @@ function f(e) {
91
91
  return "";
92
92
  }
93
93
  }
94
- function H(e) {
94
+ function j(e) {
95
95
  return {
96
96
  activityId: e.id,
97
97
  type: e.type,
98
- snippet: g(f(e)),
98
+ snippet: b(A(e)),
99
99
  authorName: e.author?.name ?? "",
100
100
  direction: e.direction,
101
101
  createdAt: e.createdAt ?? Date.now()
102
102
  };
103
103
  }
104
- function V(e, t, n = []) {
104
+ function v(e, t, n = []) {
105
105
  const r = e.createdAt;
106
106
  if (!r) return [];
107
- const s = new Set(n);
108
- return e.author.type === "user" && e.author.id && s.add(e.author.id), Object.entries(t).filter(([a, o]) => o >= r && !s.has(a)).map(([a]) => a);
107
+ const a = new Set(n);
108
+ return e.author.type === "user" && e.author.id && a.add(e.author.id), Object.entries(t).filter(([s, o]) => o >= r && !a.has(s)).map(([s]) => s);
109
109
  }
110
- const x = [
110
+ const W = [
111
111
  { id: "agent", label: "Agent", labelSource: "user" },
112
112
  { id: "team", label: "Team", labelSource: "team" },
113
113
  { id: "inbox", label: "Inbox", labelSource: "inbox" },
@@ -117,7 +117,7 @@ const x = [
117
117
  { id: "handledBy", label: "Afhandelaar", labelSource: "raw" },
118
118
  { id: "time", label: "Tijd", labelSource: "raw" }
119
119
  ];
120
- function B(e) {
120
+ function K(e) {
121
121
  const t = [];
122
122
  for (const n of Object.keys(e))
123
123
  for (const r of Object.keys(e[n]))
@@ -127,25 +127,47 @@ function B(e) {
127
127
  function l(e) {
128
128
  return e < 10 ? `0${e}` : `${e}`;
129
129
  }
130
- function A(e, t) {
130
+ function m(e, t) {
131
131
  const n = new Date(e), r = `${n.getUTCFullYear()}-${l(n.getUTCMonth() + 1)}-${l(n.getUTCDate())}`;
132
132
  return t === "day" ? r : `${r}T${l(n.getUTCHours())}`;
133
133
  }
134
- function _(e, t) {
134
+ function I(e, t) {
135
135
  const n = new Date(e);
136
136
  return n.setUTCMinutes(0, 0, 0), t === "day" && n.setUTCHours(0), n.getTime();
137
137
  }
138
- const I = 36e5, T = 864e5;
139
- function F(e, t, n) {
140
- const r = n === "hour" ? I : T, s = [];
141
- for (let a = _(e, n); a <= t; a += r)
142
- s.push(A(a, n));
143
- return s;
138
+ const _ = 36e5, S = 864e5;
139
+ function q(e, t, n) {
140
+ const r = n === "hour" ? _ : S, a = [];
141
+ for (let s = I(e, n); s <= t; s += r)
142
+ a.push(m(s, n));
143
+ return a;
144
+ }
145
+ const T = [
146
+ { id: "resource", label: "Resource", labelSource: "raw" },
147
+ { id: "origin", label: "Herkomst", labelSource: "raw" }
148
+ ], D = T.map((e) => e.id);
149
+ function N(e, t) {
150
+ return `${e}.usage.${t}`;
151
+ }
152
+ function Y(e, t) {
153
+ return t.map((n) => ({
154
+ id: N(e, n.unit),
155
+ label: n.label,
156
+ category: "Verbruik",
157
+ valueType: n.valueType ?? "count",
158
+ aggregation: "sum",
159
+ supportedDimensions: [
160
+ "provider",
161
+ ...D,
162
+ ...n.extraDimensions ?? [],
163
+ "time"
164
+ ]
165
+ }));
144
166
  }
145
- function j(e) {
167
+ function z(e) {
146
168
  return (e.startsWith("tool:") ? e.slice(5) : e).replace(/__/g, " · ").replace(/\./g, " · ").replace(/_/g, " ");
147
169
  }
148
- const W = [
170
+ const X = [
149
171
  { name: "text", type: "string" },
150
172
  { name: "subject", type: "string" },
151
173
  { name: "from", type: "string" },
@@ -157,7 +179,7 @@ const W = [
157
179
  { name: "authorType", type: "string" },
158
180
  { name: "authorName", type: "string" },
159
181
  { name: "textRaw", type: "string" }
160
- ], K = {
182
+ ], J = {
161
183
  topics: [
162
184
  { name: "topic", type: "string" },
163
185
  { name: "confidence", type: "number" }
@@ -168,7 +190,7 @@ const W = [
168
190
  ],
169
191
  extract: []
170
192
  };
171
- function S(e) {
193
+ function h(e) {
172
194
  switch (e.kind) {
173
195
  case "user":
174
196
  return `user:${e.userId}`;
@@ -178,22 +200,22 @@ function S(e) {
178
200
  return "org";
179
201
  }
180
202
  }
181
- function Y(e) {
203
+ function Q(e) {
182
204
  if (typeof e != "string") return;
183
205
  const t = e.trim();
184
206
  if (t === "org") return { kind: "org" };
185
207
  const n = t.indexOf(":");
186
208
  if (n <= 0) return;
187
- const r = t.slice(0, n), s = t.slice(n + 1).trim();
188
- if (s) {
189
- if (r === "user") return { kind: "user", userId: s };
190
- if (r === "team") return { kind: "team", teamId: s };
209
+ const r = t.slice(0, n), a = t.slice(n + 1).trim();
210
+ if (a) {
211
+ if (r === "user") return { kind: "user", userId: a };
212
+ if (r === "team") return { kind: "team", teamId: a };
191
213
  }
192
214
  }
193
- function m(e) {
194
- return S(e);
215
+ function M(e) {
216
+ return h(e);
195
217
  }
196
- function q(e) {
218
+ function Z(e) {
197
219
  switch (e.kind) {
198
220
  case "personal":
199
221
  return { kind: "user", userId: e.userId };
@@ -203,44 +225,148 @@ function q(e) {
203
225
  return { kind: "org" };
204
226
  }
205
227
  }
206
- function z(e) {
207
- return m(e);
228
+ function ee(e) {
229
+ return M(e);
230
+ }
231
+ const R = ["done", "escalated", "failed", "timed_out", "stopped"], O = ["awaiting_approval", "awaiting_reply"];
232
+ function L(e) {
233
+ return R.includes(e);
234
+ }
235
+ function te(e) {
236
+ return L(e);
237
+ }
238
+ function ne(e) {
239
+ return O.includes(e);
240
+ }
241
+ const g = [
242
+ {
243
+ id: "openai",
244
+ label: "OpenAI",
245
+ capabilities: ["chat", "transcription", "embedding"],
246
+ defaultModel: "gpt-5-mini",
247
+ baseUrl: "https://api.openai.com/v1"
248
+ },
249
+ {
250
+ id: "gemini",
251
+ label: "Google Gemini",
252
+ capabilities: ["chat", "transcription", "embedding"],
253
+ defaultModel: "gemini-3.5-flash",
254
+ baseUrl: "https://generativelanguage.googleapis.com/v1beta"
255
+ },
256
+ {
257
+ id: "anthropic",
258
+ label: "Anthropic",
259
+ capabilities: ["chat"],
260
+ defaultModel: "claude-haiku-4-5",
261
+ baseUrl: "https://api.anthropic.com/v1"
262
+ },
263
+ {
264
+ id: "elevenlabs",
265
+ label: "ElevenLabs",
266
+ capabilities: ["transcription"],
267
+ baseUrl: "https://api.elevenlabs.io/v1"
268
+ },
269
+ // --- Chat Completions-vendors ------------------------------------------
270
+ // Alles hieronder draait op één adapter (`OpenAiCompatibleProvider`). Een
271
+ // nieuwe toevoegen is een rij hier + een regel in `providers/registry`.
272
+ //
273
+ // `defaultModel` is bewust een alias waar de vendor er een aanbiedt
274
+ // (`mistral-large-latest`, `openrouter/auto`): losse model-ids verlopen,
275
+ // aliassen niet. Waar dat niet kan, staat het nieuwste vlaggenschip.
276
+ {
277
+ id: "moonshot",
278
+ label: "Moonshot (Kimi)",
279
+ capabilities: ["chat"],
280
+ defaultModel: "kimi-k3",
281
+ baseUrl: "https://api.moonshot.ai/v1"
282
+ },
283
+ {
284
+ id: "deepseek",
285
+ label: "DeepSeek",
286
+ capabilities: ["chat"],
287
+ defaultModel: "deepseek-v4-flash",
288
+ baseUrl: "https://api.deepseek.com/v1"
289
+ },
290
+ {
291
+ id: "groq",
292
+ label: "Groq",
293
+ capabilities: ["chat"],
294
+ defaultModel: "openai/gpt-oss-120b",
295
+ baseUrl: "https://api.groq.com/openai/v1"
296
+ },
297
+ {
298
+ id: "mistral",
299
+ label: "Mistral",
300
+ capabilities: ["chat"],
301
+ defaultModel: "mistral-large-latest",
302
+ baseUrl: "https://api.mistral.ai/v1"
303
+ },
304
+ {
305
+ id: "openrouter",
306
+ label: "OpenRouter",
307
+ capabilities: ["chat"],
308
+ // De Auto Router kiest zelf een model per prompt, dus dit verloopt niet
309
+ // mee met de wekelijks wisselende slugs in de catalogus.
310
+ defaultModel: "openrouter/auto",
311
+ baseUrl: "https://openrouter.ai/api/v1"
312
+ },
313
+ // Self-hosted: geen vaste host, de user vult er zelf een in op het account.
314
+ // Praten allemaal Chat Completions, dus dezelfde adapter.
315
+ {
316
+ id: "ollama",
317
+ label: "Ollama (self-hosted)",
318
+ capabilities: ["chat"],
319
+ defaultModel: "llama3.3"
320
+ },
321
+ {
322
+ id: "openai-compatible",
323
+ label: "OpenAI-compatible (eigen endpoint)",
324
+ capabilities: ["chat"],
325
+ // vLLM, LM Studio, een eigen gateway — model en host komen van de user.
326
+ defaultModel: "default"
327
+ }
328
+ ];
329
+ function c(e) {
330
+ return g.find((t) => t.id === e);
331
+ }
332
+ function re(e) {
333
+ return c(e)?.label ?? e;
208
334
  }
209
- const b = ["done", "escalated", "failed", "timed_out", "stopped"], N = ["awaiting_approval", "awaiting_reply"];
210
- function D(e) {
211
- return b.includes(e);
335
+ function ae(e, t = "gpt-5-mini") {
336
+ return c(e)?.defaultModel ?? t;
212
337
  }
213
- function X(e) {
214
- return D(e);
338
+ function se(e) {
339
+ return g.filter((t) => t.capabilities.includes(e));
215
340
  }
216
- function J(e) {
217
- return N.includes(e);
341
+ function oe(e) {
342
+ const t = c(e);
343
+ return !!t && !t.baseUrl;
218
344
  }
219
- const Q = /* @__PURE__ */ new Set(["mail", "message"]);
220
- function R(e, t) {
345
+ const ie = /* @__PURE__ */ new Set(["mail", "message"]);
346
+ function y(e, t) {
221
347
  const n = e.settings?.signatures?.[t];
222
348
  return !n || !n.enabled || !n.body || n.body.trim() === "" ? null : n;
223
349
  }
224
- function Z(e, t, n, r = "html") {
350
+ function le(e, t, n, r = "html") {
225
351
  if (!e) return n;
226
- const s = R(e, t);
227
- return s ? `${n}${r === "text" ? `
352
+ const a = y(e, t);
353
+ return a ? `${n}${r === "text" ? `
228
354
 
229
- ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${s.body}` : n;
355
+ ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${a.body}` : n;
230
356
  }
231
- function v(e) {
357
+ function ce(e) {
232
358
  return e.endpoints ?? [];
233
359
  }
234
- const ee = (e) => !!e.assignedUserId || !!e.assignedInboxId, te = (e) => e.status === "closed", ne = (e) => ({
360
+ const ue = (e) => !!e.assignedUserId || !!e.assignedInboxId, de = (e) => e.status === "closed", pe = (e) => ({
235
361
  urgent: 10,
236
362
  high: 5,
237
363
  normal: 2,
238
364
  low: 1
239
- })[e.priority] || 0, re = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
240
- function se(e, t) {
365
+ })[e.priority] || 0, Ee = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
366
+ function ge(e, t) {
241
367
  return e.lastActivityAt ? !(e.seenBy ?? []).includes(t) : !1;
242
368
  }
243
- const L = [
369
+ const C = [
244
370
  /<blockquote/i,
245
371
  /class="?gmail_quote/i,
246
372
  // Gmail quote container
@@ -253,107 +379,107 @@ const L = [
253
379
  // Outlook underscore separator before "From:"
254
380
  /\bOn\b[\s\S]{0,200}?\bwrote:/i
255
381
  // Gmail "On <date> <name> wrote:"
256
- ], u = /^\s*(?:>+\s*)?(?:from|van|sent|verzonden|to|aan|cc|subject|onderwerp|date|datum)\s*:/i, O = /^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;
257
- function y(e) {
382
+ ], u = /^\s*(?:>+\s*)?(?:from|van|sent|verzonden|to|aan|cc|subject|onderwerp|date|datum)\s*:/i, U = /^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;
383
+ function k(e) {
258
384
  const t = e.split(`
259
385
  `);
260
386
  for (let n = 0; n < t.length; n++)
261
- if (O.test(t[n]) || u.test(t[n]) && t.slice(n + 1, n + 4).some((r) => u.test(r)))
387
+ if (U.test(t[n]) || u.test(t[n]) && t.slice(n + 1, n + 4).some((r) => u.test(r)))
262
388
  return t.slice(0, n).join(`
263
389
  `).trim();
264
390
  return e;
265
391
  }
266
- function c(e) {
392
+ function d(e) {
267
393
  let t = e;
268
394
  return t = t.replace(/<(script|style)[\s\S]*?<\/\1>/gi, ""), t = t.replace(/<br\s*\/?>/gi, `
269
395
  `), t = t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi, `
270
396
  `), t = t.replace(/<[^>]+>/g, ""), t = t.replace(/<[^>]*$/, ""), t;
271
397
  }
272
- function d(e) {
398
+ function p(e) {
273
399
  return e.replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/&#(\d+);/g, (t, n) => String.fromCharCode(Number(n))).replace(/&amp;/gi, "&");
274
400
  }
275
- function p(e) {
401
+ function E(e) {
276
402
  return e.replace(/\r/g, "").replace(/[ \t]+/g, " ").replace(/ *\n */g, `
277
403
  `).replace(/\n{3,}/g, `
278
404
 
279
405
  `).trim();
280
406
  }
281
- function ae(e) {
407
+ function fe(e) {
282
408
  if (typeof e != "string" || !e) return "";
283
409
  let t = e.length;
284
- for (const a of L) {
285
- const o = e.search(a);
410
+ for (const s of C) {
411
+ const o = e.search(s);
286
412
  o >= 0 && o < t && (t = o);
287
413
  }
288
414
  const n = e.slice(0, t);
289
- let r = p(d(c(n)));
290
- return r || (r = p(d(c(e)))), y(r) || r;
415
+ let r = E(p(d(n)));
416
+ return r || (r = E(p(d(e)))), k(r) || r;
291
417
  }
292
- const C = /(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i, M = /\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;
293
- function oe(e, t) {
294
- return !!(e && C.test(e) || t && M.test(t));
418
+ const P = /(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i, $ = /\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;
419
+ function be(e, t) {
420
+ return !!(e && P.test(e) || t && $.test(t));
295
421
  }
296
- const h = 3, P = 600;
297
- function ie(e, t) {
298
- return e >= h || t >= P;
422
+ const V = 3, G = 600;
423
+ function Ae(e, t) {
424
+ return e >= V || t >= G;
299
425
  }
300
- function U(e, t) {
301
- const n = new Set(e.disabledIntents ?? []), r = e.intentOverrides ?? {}, s = t.intents.filter((o) => !n.has(o.intent)).map((o) => k(o, r[o.intent]));
302
- if (!e.extraIntents || e.extraIntents.length === 0) return s;
303
- const a = new Set(s.map((o) => o.intent));
426
+ function w(e, t) {
427
+ const n = new Set(e.disabledIntents ?? []), r = e.intentOverrides ?? {}, a = t.intents.filter((o) => !n.has(o.intent)).map((o) => x(o, r[o.intent]));
428
+ if (!e.extraIntents || e.extraIntents.length === 0) return a;
429
+ const s = new Set(a.map((o) => o.intent));
304
430
  for (const o of e.extraIntents)
305
- a.has(o.intent) || (s.push(o), a.add(o.intent));
306
- return s;
431
+ s.has(o.intent) || (a.push(o), s.add(o.intent));
432
+ return a;
307
433
  }
308
- function $(e, t) {
434
+ function H(e, t) {
309
435
  const n = {};
310
436
  for (const r of e.intents) {
311
437
  if (!r.togglable) continue;
312
- const s = t?.[r.intent];
313
- n[r.intent] = s ?? r.defaultEnabled ?? !0;
438
+ const a = t?.[r.intent];
439
+ n[r.intent] = a ?? r.defaultEnabled ?? !0;
314
440
  }
315
441
  return n;
316
442
  }
317
- function le(e, t) {
318
- const n = $(e, t);
443
+ function me(e, t) {
444
+ const n = H(e, t);
319
445
  return Object.entries(n).filter(([, r]) => !r).map(([r]) => r);
320
446
  }
321
- function k(e, t) {
447
+ function x(e, t) {
322
448
  return t ? {
323
449
  intent: t.intent ?? e.intent,
324
450
  targetSchemes: t.targetSchemes ?? e.targetSchemes,
325
451
  transport: t.transport ?? e.transport
326
452
  } : e;
327
453
  }
328
- function ue(e, t) {
454
+ function Ie(e, t) {
329
455
  const n = [];
330
456
  for (const r of e) {
331
457
  if (!r.enabled) continue;
332
- const s = t[r.providerId];
333
- if (s)
334
- for (const a of U(r, s))
335
- n.push({ channel: r, description: s, capability: a });
458
+ const a = t[r.providerId];
459
+ if (a)
460
+ for (const s of w(r, a))
461
+ n.push({ channel: r, description: a, capability: s });
336
462
  }
337
463
  return n;
338
464
  }
339
- function ce(e, t) {
465
+ function _e(e, t) {
340
466
  return t.filter((n) => n.capability.intent === e);
341
467
  }
342
- function w(e, t) {
468
+ function B(e, t) {
343
469
  return t.filter((n) => n.capability.targetSchemes.includes(e));
344
470
  }
345
- function de(e, t) {
346
- return w(e.scheme, t);
471
+ function Se(e, t) {
472
+ return B(e.scheme, t);
347
473
  }
348
- function pe(e, t, n) {
474
+ function Te(e, t, n) {
349
475
  const r = [];
350
- for (const s of e)
351
- for (const a of n)
352
- a.capability.intent === t && a.capability.targetSchemes.includes(s.scheme) && r.push({ channelIntent: a, endpoint: s });
476
+ for (const a of e)
477
+ for (const s of n)
478
+ s.capability.intent === t && s.capability.targetSchemes.includes(a.scheme) && r.push({ channelIntent: s, endpoint: a });
353
479
  return r;
354
480
  }
355
- var G = /* @__PURE__ */ ((e) => (e.MAILTO = "mailto", e.SIP = "sip", e.TEL = "tel", e.WEBHOOK = "webhook", e.USERNAME = "username", e.ID = "id", e.CUSTOM = "custom", e.URL = "url", e.TELEGRAM = "telegram", e.WHATSAPP = "whatsapp", e.MESSENGER = "messenger", e.INSTAGRAM = "instagram", e.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e.CALENDAR = "calendar", e))(G || {});
356
- const Ee = "message_window", ge = "message_templates", fe = {
481
+ var F = /* @__PURE__ */ ((e) => (e.MAILTO = "mailto", e.SIP = "sip", e.TEL = "tel", e.WEBHOOK = "webhook", e.USERNAME = "username", e.ID = "id", e.CUSTOM = "custom", e.URL = "url", e.TELEGRAM = "telegram", e.WHATSAPP = "whatsapp", e.MESSENGER = "messenger", e.INSTAGRAM = "instagram", e.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e.CALENDAR = "calendar", e))(F || {});
482
+ const De = "message_window", Ne = "message_templates", he = {
357
483
  // E-mail
358
484
  FROM: "from",
359
485
  TO: "to",
@@ -370,59 +496,69 @@ const Ee = "message_window", ge = "message_templates", fe = {
370
496
  // Voice/conference
371
497
  HOST: "host",
372
498
  PARTICIPANT: "participant"
373
- }, Ae = (e, t) => ({ intent: e, ...t }), _e = "folder_management", Ie = "remote_search", Te = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, Se = 9e4, me = "installation-id";
499
+ }, Me = (e, t) => ({ intent: e, ...t }), Re = "folder_management", Oe = "remote_search", Le = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, ye = 9e4, Ce = "installation-id";
374
500
  export {
375
- K as CLASSIFY_VARS,
376
- x as CORE_DIMENSIONS,
377
- G as CommunicationScheme,
378
- _e as FEATURE_FOLDER_MANAGEMENT,
379
- Ie as FEATURE_REMOTE_SEARCH,
380
- me as INSTALLATION_HEADER,
381
- fe as InteractionParticipantRole,
382
- N as PAUSED_RUN_STATUSES,
383
- Se as PRESENCE_ONLINE_WINDOW_MS,
384
- ge as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
385
- Ee as PROVIDER_FEATURE_MESSAGE_WINDOW,
386
- Q as SIGNATURE_INTENTS,
387
- P as SUMMARY_MIN_LAST_CHARS,
388
- h as SUMMARY_MIN_MESSAGES,
389
- b as TERMINAL_RUN_STATUSES,
390
- W as TRIGGER_VARS,
391
- Te as UNSUPPORTED,
392
- m as actingIdentityKey,
393
- z as actorKey,
394
- Z as applySignature,
395
- H as buildActivityPreview,
396
- ue as buildChannelIntents,
397
- ae as cleanMessageText,
398
- Ae as defineIntent,
399
- le as disabledIntentsFromCapabilities,
400
- de as filterByEndpoint,
401
- ce as filterByIntent,
402
- w as filterByTargetScheme,
403
- B as flattenLocales,
404
- _ as floorToPeriod,
405
- S as formatActingIdentity,
406
- A as formatPeriod,
407
- V as getActivitySeenUserIds,
408
- v as getContactEndpoints,
409
- re as getShortTitle,
410
- ne as getUrgencyScore,
411
- j as humanizeAction,
412
- ee as isAssigned,
413
- te as isClosed,
414
- se as isInteractionUnseen,
415
- oe as isLikelyBulk,
416
- J as isPaused,
417
- X as isRetryable,
418
- D as isTerminal,
419
- ie as isThreadLongEnough,
420
- pe as matchContactToIntents,
421
- Y as parseActingIdentity,
422
- F as periodsInRange,
423
- q as playbookActor,
424
- $ as resolveAccountCapabilities,
425
- U as resolveChannelIntents,
426
- R as resolveSignature,
427
- E as stripHtml
501
+ g as AI_VENDORS,
502
+ J as CLASSIFY_VARS,
503
+ W as CORE_DIMENSIONS,
504
+ F as CommunicationScheme,
505
+ Re as FEATURE_FOLDER_MANAGEMENT,
506
+ Oe as FEATURE_REMOTE_SEARCH,
507
+ Ce as INSTALLATION_HEADER,
508
+ he as InteractionParticipantRole,
509
+ O as PAUSED_RUN_STATUSES,
510
+ ye as PRESENCE_ONLINE_WINDOW_MS,
511
+ Ne as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
512
+ De as PROVIDER_FEATURE_MESSAGE_WINDOW,
513
+ ie as SIGNATURE_INTENTS,
514
+ G as SUMMARY_MIN_LAST_CHARS,
515
+ V as SUMMARY_MIN_MESSAGES,
516
+ R as TERMINAL_RUN_STATUSES,
517
+ X as TRIGGER_VARS,
518
+ Le as UNSUPPORTED,
519
+ T as USAGE_DIMENSIONS,
520
+ D as USAGE_DIMENSION_IDS,
521
+ M as actingIdentityKey,
522
+ ee as actorKey,
523
+ ae as aiVendorDefaultModel,
524
+ re as aiVendorLabel,
525
+ oe as aiVendorNeedsBaseUrl,
526
+ se as aiVendorsWith,
527
+ le as applySignature,
528
+ j as buildActivityPreview,
529
+ Ie as buildChannelIntents,
530
+ fe as cleanMessageText,
531
+ Me as defineIntent,
532
+ me as disabledIntentsFromCapabilities,
533
+ Se as filterByEndpoint,
534
+ _e as filterByIntent,
535
+ B as filterByTargetScheme,
536
+ c as findAiVendor,
537
+ K as flattenLocales,
538
+ I as floorToPeriod,
539
+ h as formatActingIdentity,
540
+ m as formatPeriod,
541
+ v as getActivitySeenUserIds,
542
+ ce as getContactEndpoints,
543
+ Ee as getShortTitle,
544
+ pe as getUrgencyScore,
545
+ z as humanizeAction,
546
+ ue as isAssigned,
547
+ de as isClosed,
548
+ ge as isInteractionUnseen,
549
+ be as isLikelyBulk,
550
+ ne as isPaused,
551
+ te as isRetryable,
552
+ L as isTerminal,
553
+ Ae as isThreadLongEnough,
554
+ Te as matchContactToIntents,
555
+ Q as parseActingIdentity,
556
+ q as periodsInRange,
557
+ Z as playbookActor,
558
+ H as resolveAccountCapabilities,
559
+ w as resolveChannelIntents,
560
+ y as resolveSignature,
561
+ f as stripHtml,
562
+ N as usageMetricId,
563
+ Y as usageMetrics
428
564
  };
@@ -51,6 +51,14 @@ export interface AiToolDescriptor extends AiTool {
51
51
  param: string;
52
52
  kind: string;
53
53
  };
54
+ /**
55
+ * `"interactive"` = deze tool heeft een kijkende gebruiker nodig, omdat hij aflevert
56
+ * op een client-oppervlak (bijvoorbeeld een SSE naar een geopend venster) in plaats
57
+ * van iets te persisteren. Onbemande runs krijgen zulke tools niet aangeboden:
58
+ * anders rapporteert het model succes voor iets dat nergens landt. Afwezig = werkt
59
+ * overal.
60
+ */
61
+ surface?: "interactive";
54
62
  }
55
63
  /**
56
64
  * Body van `POST /provider/ai-tools/invoke`. Vervangt vijf inline structurele
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.125.0",
3
+ "version": "1.127.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",