@opencxh/domain 1.123.0 → 1.125.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,3 +1,4 @@
1
+ import { ActingIdentity } from '../../platform/identity';
1
2
  import { AIMessageInput, AIMessageOutput } from '../ai-message/types';
2
3
  import { TranscriptSegment } from '../../platform/media';
3
4
  export type CallStatus = "new" | "connecting" | "ringing" | "connected" | "held" | "ended" | "failed";
@@ -198,6 +199,8 @@ export type AIActionProposedPayload = {
198
199
  params?: Record<string, unknown>;
199
200
  }[];
200
201
  status: "pending" | "approved" | "rejected";
202
+ /** Namens wie het voorstel is gedaan, zodat de kaart "namens Finance" kan tonen. */
203
+ actor?: ActingIdentity;
201
204
  };
202
205
  /** Playbook lifecycle marker in the interaction timeline (system-authored). */
203
206
  export type PlaybookLifecyclePayload = {
@@ -205,6 +208,8 @@ export type PlaybookLifecyclePayload = {
205
208
  playbookId?: string;
206
209
  playbookName?: string;
207
210
  status?: string;
211
+ /** Namens wie de run handelde; voorkomt dat de UI de playbook opnieuw moet ophalen. */
212
+ actor?: ActingIdentity;
208
213
  };
209
214
  export type Activity = (BaseActivity & {
210
215
  type: "VOICE_CALL_STARTED";
@@ -0,0 +1,16 @@
1
+ import { ActingIdentity } from '../../platform/identity';
2
+ import { OwnerScope } from '../contact/types';
3
+ /**
4
+ * `ownerScope` **is** de actor van een playbook: er is geen apart `runAs`-veld.
5
+ * Zichtbaarheid, werkgebied en toeschrijving volgen dus uit hetzelfde veld.
6
+ *
7
+ * Aan de bewaarkant heet de persoonlijke variant `personal` (zoals bij elke andere
8
+ * entity), aan de uitvoerkant `user` (zoals elke andere acting identity). Deze
9
+ * functie is die vertaling, en de enige plek waar hij hoort.
10
+ */
11
+ export declare function playbookActor(scope: OwnerScope): ActingIdentity;
12
+ /**
13
+ * Stabiele sleutel om actors te groeperen. Tien playbooks van hetzelfde team horen
14
+ * één autorisatie-call te kosten, niet tien.
15
+ */
16
+ export declare function actorKey(actor: ActingIdentity): string;
@@ -1 +1,4 @@
1
+ export * from './labels';
2
+ export * from './trigger-vars';
3
+ export * from './actor';
1
4
  export * from './types';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Leesbare naam van een actie/tool voor een mens: `tool:communication__update_interaction`
3
+ * wordt `communication · update interaction`.
4
+ *
5
+ * Gedeeld, want de proposal-kaart in comms had deze helper al terwijl de run-timeline in
6
+ * de AI-app de rauwe id printte — dus daar las je letterlijk
7
+ * `voorstel: tool:mcp__jira__create_issue`.
8
+ */
9
+ export declare function humanizeAction(action: string): string;
@@ -0,0 +1,61 @@
1
+ /**
2
+ * De variabelen die de engine onder `vars.trigger` zet bij een activity-trigger.
3
+ *
4
+ * Contract tussen `buildTriggerVars` (server) en de variabelen-picker (builder). Die
5
+ * twee waren uiteengelopen: de picker bood `trigger.fromEmail` en `trigger.channel`
6
+ * aan — die bestaan niet — en miste `subject`, `textRaw`, `direction`, `authorType` en
7
+ * `activityId`. Elk pad dat hij aanbood leverde dus `undefined` op. Eén lijst, aan
8
+ * beide kanten geïmporteerd, kan niet opnieuw uiteenlopen.
9
+ */
10
+ export declare const TRIGGER_VARS: readonly [{
11
+ readonly name: "text";
12
+ readonly type: "string";
13
+ }, {
14
+ readonly name: "subject";
15
+ readonly type: "string";
16
+ }, {
17
+ readonly name: "from";
18
+ readonly type: "string";
19
+ }, {
20
+ readonly name: "type";
21
+ readonly type: "string";
22
+ }, {
23
+ readonly name: "direction";
24
+ readonly type: "string";
25
+ }, {
26
+ readonly name: "channelId";
27
+ readonly type: "string";
28
+ }, {
29
+ readonly name: "interactionId";
30
+ readonly type: "string";
31
+ }, {
32
+ readonly name: "activityId";
33
+ readonly type: "string";
34
+ }, {
35
+ readonly name: "authorType";
36
+ readonly type: "string";
37
+ }, {
38
+ readonly name: "authorName";
39
+ readonly type: "string";
40
+ }, {
41
+ readonly name: "textRaw";
42
+ readonly type: "string";
43
+ }];
44
+ /** De variabelen die een classify-stap oplevert, per mode. */
45
+ export declare const CLASSIFY_VARS: {
46
+ readonly topics: readonly [{
47
+ readonly name: "topic";
48
+ readonly type: "string";
49
+ }, {
50
+ readonly name: "confidence";
51
+ readonly type: "number";
52
+ }];
53
+ readonly question: readonly [{
54
+ readonly name: "answer";
55
+ readonly type: "boolean";
56
+ }, {
57
+ readonly name: "confidence";
58
+ readonly type: "number";
59
+ }];
60
+ readonly extract: readonly [];
61
+ };
@@ -1,3 +1,4 @@
1
+ import { ActingIdentity } from '../../platform/identity';
1
2
  import { OwnerScope } from '../contact/types';
2
3
  /**
3
4
  * Playbooks — event-driven automation with AI steps. See docs/PLAYBOOKS.md.
@@ -19,7 +20,6 @@ export type PlaybookTrigger = {
19
20
  export interface Guardrails {
20
21
  maxSteps?: number;
21
22
  maxToolCalls?: number;
22
- policyCaps?: Record<string, unknown>;
23
23
  }
24
24
  export interface ConditionStep {
25
25
  type: "condition";
@@ -102,7 +102,24 @@ export interface Playbook {
102
102
  autoActions: number;
103
103
  };
104
104
  }
105
- export type RunStatus = "pending" | "running" | "awaiting_approval" | "awaiting_reply" | "done" | "escalated" | "failed" | "timed_out" | "stopped";
105
+ /**
106
+ * Levenscyclus van een run. Geen `pending`: runs worden ingevoegd als `running` en
107
+ * de `PLAYBOOK_STARTED`-activity dekt "hij is begonnen" al. (Die status bestond,
108
+ * werd nooit geproduceerd, en had wél een label in de UI.)
109
+ */
110
+ export type RunStatus = "running" | "awaiting_approval" | "awaiting_reply" | "done" | "escalated" | "failed" | "timed_out" | "stopped";
111
+ /** Eindstanden: hier komt een run niet meer vanzelf uit. */
112
+ export declare const TERMINAL_RUN_STATUSES: readonly ["done", "escalated", "failed", "timed_out", "stopped"];
113
+ /** Wacht deze run op iets externs? */
114
+ export declare const PAUSED_RUN_STATUSES: readonly ["awaiting_approval", "awaiting_reply"];
115
+ export declare function isTerminal(status: RunStatus | string): boolean;
116
+ /**
117
+ * Statussen waarvan een run opnieuw gestart mag worden. Een lopende of
118
+ * mens-gepauzeerde run blijft met rust, zodat een retry nooit over live uitvoering
119
+ * heen loopt of een openstaand voorstel overschrijft — vandaar: precies de eindstanden.
120
+ */
121
+ export declare function isRetryable(status: RunStatus | string): boolean;
122
+ export declare function isPaused(status: RunStatus | string): boolean;
106
123
  export interface RunStepTrace {
107
124
  path: string;
108
125
  type: string;
@@ -126,29 +143,38 @@ export interface PlaybookRun {
126
143
  trace: RunStepTrace[];
127
144
  /** Snapshot of the playbook steps at run start — version-pinned resume (docs 11.4). */
128
145
  steps?: Step[];
129
- /** Held action(s) awaiting human approval (autonomy: suggest) — a batch per paused step. */
146
+ /**
147
+ * Namens wie deze run handelt, vastgepind bij de start — net als `steps`. Anders
148
+ * wisselt een lopende of geparkeerde run stilletjes van identiteit zodra iemand de
149
+ * playbook van "Alleen ik" naar "Team Finance" zet. `resume`/`retry` lezen dit veld,
150
+ * niet `playbook.ownerScope`.
151
+ */
152
+ actor?: ActingIdentity;
153
+ /**
154
+ * Held action(s) awaiting human approval (autonomy: suggest) — a batch per paused
155
+ * step. `null` wanneer het veld gewist is; de store schrijft `null`, dus dat staat
156
+ * ook in het type in plaats van dat elke lezer drie toestanden moet aankunnen.
157
+ */
130
158
  pending?: {
131
- stepIndex: number;
132
- actions: {
133
- action: string;
134
- params: Record<string, unknown>;
135
- }[];
136
- };
159
+ action: string;
160
+ params: Record<string, unknown>;
161
+ }[] | null;
137
162
  /** The AI_ACTION_PROPOSED activity created for the current pending batch (for resolve on approve/reject). */
138
- proposalActivityId?: string;
139
- /** Parked run (awaiting_reply): where to continue + correlation + round counter. */
140
- awaitFor?: {
141
- interactionId?: string;
142
- };
143
- resumeIndex?: number;
163
+ proposalActivityId?: string | null;
164
+ /**
165
+ * Index van de stap waarop de flow pauzeerde — voor zowel `awaiting_approval` als
166
+ * `awaiting_reply`. (Was `resumeIndex` naast `pending.stepIndex`: twee velden voor
167
+ * hetzelfde feit, in twee takken geschreven en in twee takken gelezen.)
168
+ */
169
+ pausedAt?: number | null;
144
170
  rounds?: number;
145
171
  /** Denormalized interaction audience for efficient list-filtering. */
146
172
  audience?: string[];
147
173
  createdBy: string;
148
- approvedBy?: string;
174
+ approvedBy?: string | null;
149
175
  result?: {
150
176
  ok: boolean;
151
177
  error?: string;
152
- };
178
+ } | null;
153
179
  lastStepAt?: number;
154
180
  }
package/dist/index.cjs CHANGED
@@ -1,10 +1,10 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const c=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 D(e){const t=e.trim();return t.length<=c?t:t.slice(0,c-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 O(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 L(e){return{activityId:e.id,type:e.type,snippet:D(O(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function M(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 C=[{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 m(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 i(e){return e<10?`0${e}`:`${e}`}function g(e,t){const n=new Date(e),r=`${n.getUTCFullYear()}-${i(n.getUTCMonth()+1)}-${i(n.getUTCDate())}`;return t==="day"?r:`${r}T${i(n.getUTCHours())}`}function _(e,t){const n=new Date(e);return n.setUTCMinutes(0,0,0),t==="day"&&n.setUTCHours(0),n.getTime()}const h=36e5,y=864e5;function P(e,t,n){const r=n==="hour"?h:y,s=[];for(let a=_(e,n);a<=t;a+=r)s.push(g(a,n));return s}const U=new Set(["mail","message"]);function I(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function $(e,t,n,r="html"){if(!e)return n;const s=I(e,t);return s?`${n}${r==="text"?`
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"?`
2
2
 
3
- `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${s.body}`:n}function G(e){return e.endpoints??[]}const H=e=>!!e.assignedUserId||!!e.assignedInboxId,V=e=>e.status==="closed",k=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,w=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function B(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const F=[/<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,x=/^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;function j(e){const t=e.split(`
4
- `);for(let n=0;n<t.length;n++)if(x.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>"}${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(`
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
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,`
8
8
  `).replace(/\n{3,}/g,`
9
9
 
10
- `).trim()}function W(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const a of F){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)))),j(r)||r}const Y=/(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i,K=/\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;function q(e,t){return!!(e&&Y.test(e)||t&&K.test(t))}const T=3,f=600;function z(e,t){return e>=T||t>=f}function S(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(o=>!n.has(o.intent)).map(o=>Q(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 b(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 J(e,t){const n=b(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function Q(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function X(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const a of S(r,s))n.push({channel:r,description:s,capability:a})}return n}function Z(e,t){return t.filter(n=>n.capability.intent===e)}function N(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function v(e,t){return N(e.scheme,t)}function ee(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 R=(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))(R||{});const te="message_window",ne="message_templates",re={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},se=(e,t)=>({intent:e,...t}),ae="folder_management",oe="remote_search",le={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},ie=9e4,ce="installation-id";exports.CORE_DIMENSIONS=C;exports.CommunicationScheme=R;exports.FEATURE_FOLDER_MANAGEMENT=ae;exports.FEATURE_REMOTE_SEARCH=oe;exports.INSTALLATION_HEADER=ce;exports.InteractionParticipantRole=re;exports.PRESENCE_ONLINE_WINDOW_MS=ie;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=ne;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=te;exports.SIGNATURE_INTENTS=U;exports.SUMMARY_MIN_LAST_CHARS=f;exports.SUMMARY_MIN_MESSAGES=T;exports.UNSUPPORTED=le;exports.applySignature=$;exports.buildActivityPreview=L;exports.buildChannelIntents=X;exports.cleanMessageText=W;exports.defineIntent=se;exports.disabledIntentsFromCapabilities=J;exports.filterByEndpoint=v;exports.filterByIntent=Z;exports.filterByTargetScheme=N;exports.flattenLocales=m;exports.floorToPeriod=_;exports.formatPeriod=g;exports.getActivitySeenUserIds=M;exports.getContactEndpoints=G;exports.getShortTitle=w;exports.getUrgencyScore=k;exports.isAssigned=H;exports.isClosed=V;exports.isInteractionUnseen=B;exports.isLikelyBulk=q;exports.isThreadLongEnough=z;exports.matchContactToIntents=ee;exports.periodsInRange=P;exports.resolveAccountCapabilities=b;exports.resolveChannelIntents=S;exports.resolveSignature=I;exports.stripHtml=A;
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;
package/dist/index.d.ts CHANGED
@@ -38,6 +38,7 @@ export * from './platform/capabilities';
38
38
  export * from './platform/context';
39
39
  export * from './platform/communication';
40
40
  export * from './platform/federated';
41
+ export * from './platform/identity';
41
42
  export * from './platform/intents';
42
43
  export * from './platform/kernel';
43
44
  export * from './platform/lifecycle';
package/dist/index.js CHANGED
@@ -1,20 +1,20 @@
1
- function p(e) {
1
+ function E(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
4
  function g(e) {
5
5
  const t = e.trim();
6
6
  return t.length <= 140 ? t : t.slice(0, 139).trimEnd() + "…";
7
7
  }
8
- function l(e) {
8
+ function i(e) {
9
9
  if (!e || e < 0) return "";
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 A(e) {
13
+ function f(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() || p(t.body ?? "");
17
+ const t = e.payload, n = t.bodySnippet?.trim() || E(t.body ?? "");
18
18
  return t.subject ? `${t.subject} — ${n}` : n;
19
19
  }
20
20
  case "CHAT_MESSAGE_SENT":
@@ -29,7 +29,7 @@ function A(e) {
29
29
  case "CHAT_CALL_STARTED":
30
30
  return "Gesprek gestart";
31
31
  case "CHAT_CALL_ENDED":
32
- return `Gesprek beëindigd${e.payload.duration ? ` (${l(e.payload.duration)})` : ""}`;
32
+ return `Gesprek beëindigd${e.payload.duration ? ` (${i(e.payload.duration)})` : ""}`;
33
33
  case "CHAT_EVENT":
34
34
  return e.payload.text ?? e.payload.eventType;
35
35
  case "VOICE_CALL_STARTED":
@@ -46,7 +46,7 @@ function A(e) {
46
46
  return "Hervat";
47
47
  case "VOICE_CALL_ENDED":
48
48
  case "VIDEO_CALL_ENDED":
49
- return `Gesprek beëindigd${e.payload.duration ? ` (${l(e.payload.duration)})` : ""}`;
49
+ return `Gesprek beëindigd${e.payload.duration ? ` (${i(e.payload.duration)})` : ""}`;
50
50
  case "VOICE_CALL_MISSED":
51
51
  case "VIDEO_CALL_MISSED":
52
52
  return "Gemiste oproep";
@@ -72,7 +72,7 @@ function A(e) {
72
72
  case "MEETING_STARTED":
73
73
  return `Vergadering gestart: ${e.payload.title}`;
74
74
  case "MEETING_ENDED":
75
- return `Vergadering beëindigd${e.payload.duration ? ` (${l(e.payload.duration)})` : ""}`;
75
+ return `Vergadering beëindigd${e.payload.duration ? ` (${i(e.payload.duration)})` : ""}`;
76
76
  case "MEETING_PARTICIPANT_JOINED":
77
77
  return `${e.payload.name} deelgenomen`;
78
78
  case "MEETING_PARTICIPANT_LEFT":
@@ -91,23 +91,23 @@ function A(e) {
91
91
  return "";
92
92
  }
93
93
  }
94
- function $(e) {
94
+ function H(e) {
95
95
  return {
96
96
  activityId: e.id,
97
97
  type: e.type,
98
- snippet: g(A(e)),
98
+ snippet: g(f(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 U(e, t, n = []) {
104
+ function V(e, t, n = []) {
105
105
  const r = e.createdAt;
106
106
  if (!r) return [];
107
107
  const s = new Set(n);
108
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);
109
109
  }
110
- const H = [
110
+ const x = [
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,56 +117,130 @@ const H = [
117
117
  { id: "handledBy", label: "Afhandelaar", labelSource: "raw" },
118
118
  { id: "time", label: "Tijd", labelSource: "raw" }
119
119
  ];
120
- function G(e) {
120
+ function B(e) {
121
121
  const t = [];
122
122
  for (const n of Object.keys(e))
123
123
  for (const r of Object.keys(e[n]))
124
124
  t.push({ lang: n, key: r, value: e[n][r] });
125
125
  return t;
126
126
  }
127
- function i(e) {
127
+ function l(e) {
128
128
  return e < 10 ? `0${e}` : `${e}`;
129
129
  }
130
- function f(e, t) {
131
- const n = new Date(e), r = `${n.getUTCFullYear()}-${i(n.getUTCMonth() + 1)}-${i(n.getUTCDate())}`;
132
- return t === "day" ? r : `${r}T${i(n.getUTCHours())}`;
130
+ function A(e, t) {
131
+ const n = new Date(e), r = `${n.getUTCFullYear()}-${l(n.getUTCMonth() + 1)}-${l(n.getUTCDate())}`;
132
+ return t === "day" ? r : `${r}T${l(n.getUTCHours())}`;
133
133
  }
134
134
  function _(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 T = 36e5, I = 864e5;
139
- function V(e, t, n) {
140
- const r = n === "hour" ? T : I, s = [];
138
+ const I = 36e5, T = 864e5;
139
+ function F(e, t, n) {
140
+ const r = n === "hour" ? I : T, s = [];
141
141
  for (let a = _(e, n); a <= t; a += r)
142
- s.push(f(a, n));
142
+ s.push(A(a, n));
143
143
  return s;
144
144
  }
145
- const w = /* @__PURE__ */ new Set(["mail", "message"]);
146
- function S(e, t) {
145
+ function j(e) {
146
+ return (e.startsWith("tool:") ? e.slice(5) : e).replace(/__/g, " · ").replace(/\./g, " · ").replace(/_/g, " ");
147
+ }
148
+ const W = [
149
+ { name: "text", type: "string" },
150
+ { name: "subject", type: "string" },
151
+ { name: "from", type: "string" },
152
+ { name: "type", type: "string" },
153
+ { name: "direction", type: "string" },
154
+ { name: "channelId", type: "string" },
155
+ { name: "interactionId", type: "string" },
156
+ { name: "activityId", type: "string" },
157
+ { name: "authorType", type: "string" },
158
+ { name: "authorName", type: "string" },
159
+ { name: "textRaw", type: "string" }
160
+ ], K = {
161
+ topics: [
162
+ { name: "topic", type: "string" },
163
+ { name: "confidence", type: "number" }
164
+ ],
165
+ question: [
166
+ { name: "answer", type: "boolean" },
167
+ { name: "confidence", type: "number" }
168
+ ],
169
+ extract: []
170
+ };
171
+ function S(e) {
172
+ switch (e.kind) {
173
+ case "user":
174
+ return `user:${e.userId}`;
175
+ case "team":
176
+ return `team:${e.teamId}`;
177
+ case "org":
178
+ return "org";
179
+ }
180
+ }
181
+ function Y(e) {
182
+ if (typeof e != "string") return;
183
+ const t = e.trim();
184
+ if (t === "org") return { kind: "org" };
185
+ const n = t.indexOf(":");
186
+ 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 };
191
+ }
192
+ }
193
+ function m(e) {
194
+ return S(e);
195
+ }
196
+ function q(e) {
197
+ switch (e.kind) {
198
+ case "personal":
199
+ return { kind: "user", userId: e.userId };
200
+ case "team":
201
+ return { kind: "team", teamId: e.teamId };
202
+ case "org":
203
+ return { kind: "org" };
204
+ }
205
+ }
206
+ function z(e) {
207
+ return m(e);
208
+ }
209
+ const b = ["done", "escalated", "failed", "timed_out", "stopped"], N = ["awaiting_approval", "awaiting_reply"];
210
+ function D(e) {
211
+ return b.includes(e);
212
+ }
213
+ function X(e) {
214
+ return D(e);
215
+ }
216
+ function J(e) {
217
+ return N.includes(e);
218
+ }
219
+ const Q = /* @__PURE__ */ new Set(["mail", "message"]);
220
+ function R(e, t) {
147
221
  const n = e.settings?.signatures?.[t];
148
222
  return !n || !n.enabled || !n.body || n.body.trim() === "" ? null : n;
149
223
  }
150
- function k(e, t, n, r = "html") {
224
+ function Z(e, t, n, r = "html") {
151
225
  if (!e) return n;
152
- const s = S(e, t);
226
+ const s = R(e, t);
153
227
  return s ? `${n}${r === "text" ? `
154
228
 
155
229
  ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${s.body}` : n;
156
230
  }
157
- function x(e) {
231
+ function v(e) {
158
232
  return e.endpoints ?? [];
159
233
  }
160
- const B = (e) => !!e.assignedUserId || !!e.assignedInboxId, F = (e) => e.status === "closed", j = (e) => ({
234
+ const ee = (e) => !!e.assignedUserId || !!e.assignedInboxId, te = (e) => e.status === "closed", ne = (e) => ({
161
235
  urgent: 10,
162
236
  high: 5,
163
237
  normal: 2,
164
238
  low: 1
165
- })[e.priority] || 0, W = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
166
- function K(e, t) {
239
+ })[e.priority] || 0, re = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
240
+ function se(e, t) {
167
241
  return e.lastActivityAt ? !(e.seenBy ?? []).includes(t) : !1;
168
242
  }
169
- const b = [
243
+ const L = [
170
244
  /<blockquote/i,
171
245
  /class="?gmail_quote/i,
172
246
  // Gmail quote container
@@ -179,12 +253,12 @@ const b = [
179
253
  // Outlook underscore separator before "From:"
180
254
  /\bOn\b[\s\S]{0,200}?\bwrote:/i
181
255
  // Gmail "On <date> <name> wrote:"
182
- ], u = /^\s*(?:>+\s*)?(?:from|van|sent|verzonden|to|aan|cc|subject|onderwerp|date|datum)\s*:/i, D = /^\s*(?:on|op)\b.{0,200}?\b(?:wrote|schreef|geschreven)\s*:?\s*$/i;
183
- function N(e) {
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) {
184
258
  const t = e.split(`
185
259
  `);
186
260
  for (let n = 0; n < t.length; n++)
187
- if (D.test(t[n]) || u.test(t[n]) && t.slice(n + 1, n + 4).some((r) => u.test(r)))
261
+ if (O.test(t[n]) || u.test(t[n]) && t.slice(n + 1, n + 4).some((r) => u.test(r)))
188
262
  return t.slice(0, n).join(`
189
263
  `).trim();
190
264
  return e;
@@ -195,43 +269,43 @@ function c(e) {
195
269
  `), t = t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi, `
196
270
  `), t = t.replace(/<[^>]+>/g, ""), t = t.replace(/<[^>]*$/, ""), t;
197
271
  }
198
- function E(e) {
272
+ function d(e) {
199
273
  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, "&");
200
274
  }
201
- function d(e) {
275
+ function p(e) {
202
276
  return e.replace(/\r/g, "").replace(/[ \t]+/g, " ").replace(/ *\n */g, `
203
277
  `).replace(/\n{3,}/g, `
204
278
 
205
279
  `).trim();
206
280
  }
207
- function Y(e) {
281
+ function ae(e) {
208
282
  if (typeof e != "string" || !e) return "";
209
283
  let t = e.length;
210
- for (const a of b) {
284
+ for (const a of L) {
211
285
  const o = e.search(a);
212
286
  o >= 0 && o < t && (t = o);
213
287
  }
214
288
  const n = e.slice(0, t);
215
- let r = d(E(c(n)));
216
- return r || (r = d(E(c(e)))), N(r) || r;
289
+ let r = p(d(c(n)));
290
+ return r || (r = p(d(c(e)))), y(r) || r;
217
291
  }
218
- const L = /(^|[._+-])(no-?reply|noreply|donotreply|do-not-reply|newsletter|nieuwsbrief|mailer|mailing|notifications?|bounce|postmaster|marketing)@/i, O = /\b(unsubscribe|afmelden|uitschrijven|opt[\s-]?out|manage (your )?preferences|geen (e-?mails?|nieuwsbrief|berichten) meer)\b/i;
219
- function q(e, t) {
220
- return !!(e && L.test(e) || t && O.test(t));
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));
221
295
  }
222
- const R = 3, m = 600;
223
- function X(e, t) {
224
- return e >= R || t >= m;
296
+ const h = 3, P = 600;
297
+ function ie(e, t) {
298
+ return e >= h || t >= P;
225
299
  }
226
- function C(e, t) {
227
- const n = new Set(e.disabledIntents ?? []), r = e.intentOverrides ?? {}, s = t.intents.filter((o) => !n.has(o.intent)).map((o) => h(o, r[o.intent]));
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]));
228
302
  if (!e.extraIntents || e.extraIntents.length === 0) return s;
229
303
  const a = new Set(s.map((o) => o.intent));
230
304
  for (const o of e.extraIntents)
231
305
  a.has(o.intent) || (s.push(o), a.add(o.intent));
232
306
  return s;
233
307
  }
234
- function M(e, t) {
308
+ function $(e, t) {
235
309
  const n = {};
236
310
  for (const r of e.intents) {
237
311
  if (!r.togglable) continue;
@@ -240,46 +314,46 @@ function M(e, t) {
240
314
  }
241
315
  return n;
242
316
  }
243
- function z(e, t) {
244
- const n = M(e, t);
317
+ function le(e, t) {
318
+ const n = $(e, t);
245
319
  return Object.entries(n).filter(([, r]) => !r).map(([r]) => r);
246
320
  }
247
- function h(e, t) {
321
+ function k(e, t) {
248
322
  return t ? {
249
323
  intent: t.intent ?? e.intent,
250
324
  targetSchemes: t.targetSchemes ?? e.targetSchemes,
251
325
  transport: t.transport ?? e.transport
252
326
  } : e;
253
327
  }
254
- function J(e, t) {
328
+ function ue(e, t) {
255
329
  const n = [];
256
330
  for (const r of e) {
257
331
  if (!r.enabled) continue;
258
332
  const s = t[r.providerId];
259
333
  if (s)
260
- for (const a of C(r, s))
334
+ for (const a of U(r, s))
261
335
  n.push({ channel: r, description: s, capability: a });
262
336
  }
263
337
  return n;
264
338
  }
265
- function Q(e, t) {
339
+ function ce(e, t) {
266
340
  return t.filter((n) => n.capability.intent === e);
267
341
  }
268
- function P(e, t) {
342
+ function w(e, t) {
269
343
  return t.filter((n) => n.capability.targetSchemes.includes(e));
270
344
  }
271
- function Z(e, t) {
272
- return P(e.scheme, t);
345
+ function de(e, t) {
346
+ return w(e.scheme, t);
273
347
  }
274
- function v(e, t, n) {
348
+ function pe(e, t, n) {
275
349
  const r = [];
276
350
  for (const s of e)
277
351
  for (const a of n)
278
352
  a.capability.intent === t && a.capability.targetSchemes.includes(s.scheme) && r.push({ channelIntent: a, endpoint: s });
279
353
  return r;
280
354
  }
281
- var y = /* @__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))(y || {});
282
- const ee = "message_window", te = "message_templates", ne = {
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 = {
283
357
  // E-mail
284
358
  FROM: "from",
285
359
  TO: "to",
@@ -296,46 +370,59 @@ const ee = "message_window", te = "message_templates", ne = {
296
370
  // Voice/conference
297
371
  HOST: "host",
298
372
  PARTICIPANT: "participant"
299
- }, re = (e, t) => ({ intent: e, ...t }), se = "folder_management", ae = "remote_search", oe = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, le = 9e4, ie = "installation-id";
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";
300
374
  export {
301
- H as CORE_DIMENSIONS,
302
- y as CommunicationScheme,
303
- se as FEATURE_FOLDER_MANAGEMENT,
304
- ae as FEATURE_REMOTE_SEARCH,
305
- ie as INSTALLATION_HEADER,
306
- ne as InteractionParticipantRole,
307
- le as PRESENCE_ONLINE_WINDOW_MS,
308
- te as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
309
- ee as PROVIDER_FEATURE_MESSAGE_WINDOW,
310
- w as SIGNATURE_INTENTS,
311
- m as SUMMARY_MIN_LAST_CHARS,
312
- R as SUMMARY_MIN_MESSAGES,
313
- oe as UNSUPPORTED,
314
- k as applySignature,
315
- $ as buildActivityPreview,
316
- J as buildChannelIntents,
317
- Y as cleanMessageText,
318
- re as defineIntent,
319
- z as disabledIntentsFromCapabilities,
320
- Z as filterByEndpoint,
321
- Q as filterByIntent,
322
- P as filterByTargetScheme,
323
- G as flattenLocales,
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,
324
404
  _ as floorToPeriod,
325
- f as formatPeriod,
326
- U as getActivitySeenUserIds,
327
- x as getContactEndpoints,
328
- W as getShortTitle,
329
- j as getUrgencyScore,
330
- B as isAssigned,
331
- F as isClosed,
332
- K as isInteractionUnseen,
333
- q as isLikelyBulk,
334
- X as isThreadLongEnough,
335
- v as matchContactToIntents,
336
- V as periodsInRange,
337
- M as resolveAccountCapabilities,
338
- C as resolveChannelIntents,
339
- S as resolveSignature,
340
- p as stripHtml
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
341
428
  };
@@ -31,8 +31,39 @@ export interface AiToolResult {
31
31
  content: string;
32
32
  isError?: boolean;
33
33
  }
34
- /** Returned by an app's `GET /provider/ai-tools/describe`. */
35
- export type AiToolDescriptor = AiTool;
34
+ /**
35
+ * Returned by an app's `GET /provider/ai-tools/describe`.
36
+ *
37
+ * Beide extra velden zijn optioneel en additief: een tool die ze niet zet gedraagt
38
+ * zich exact als voorheen. Ze bestaan zodat generieke lagen (de suggest-gate, de
39
+ * scope-gate in `registerAiToolProvider`) een tool kunnen beoordelen **zonder hem
40
+ * te kennen** — nu zit die kennis in `AIProfile.toolPolicy` met "onbekend = write".
41
+ */
42
+ export interface AiToolDescriptor extends AiTool {
43
+ /** Muteert deze tool iets? Bepaalt de suggest-gate. Afwezig = onbekend. */
44
+ access?: "read" | "write";
45
+ /**
46
+ * De resource waarop deze tool werkt, zodat een generieke gate hem kan
47
+ * autoriseren: `{ param: "interactionId", kind: "interaction" }` betekent
48
+ * "autoriseer `interaction:<args.interactionId>` voor de acting identity".
49
+ */
50
+ scope?: {
51
+ param: string;
52
+ kind: string;
53
+ };
54
+ }
55
+ /**
56
+ * Body van `POST /provider/ai-tools/invoke`. Vervangt vijf inline structurele
57
+ * types (de beller in de ai-app plus de vier ontvangende apps).
58
+ *
59
+ * De acting identity zit hier **niet** in: die reist als header mee
60
+ * (`ACTING_IDENTITY_HEADER` in `@opencxh/platform-api`). Gemeten in fase 0 dat dat
61
+ * werkt vanuit een request, een job én een subscriber — dus geen tweede kanaal.
62
+ */
63
+ export interface AiToolInvokeRequest {
64
+ tool: string;
65
+ arguments?: Record<string, unknown>;
66
+ }
36
67
  /** One step in the assistant's tool-use trace, persisted on the message. */
37
68
  export interface ToolTraceEntry {
38
69
  call: AiToolCall;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Namens wie een actie gebeurt.
3
+ *
4
+ * Orthogonaal aan *wie* er belt (dat is `Caller` in `@opencxh/platform-api`): een
5
+ * app kan bellen namens een gebruiker, namens een team, of namens de organisatie.
6
+ * Een app-to-app call draagt van zichzelf geen bruikbare identiteit — de auth die
7
+ * de runtime meestuurt is die van de oorspronkelijke beller, niet van degene
8
+ * namens wie gehandeld wordt. Vandaar dit eigen, expliciete kanaal.
9
+ *
10
+ * `MutationAuthor` wordt hieruit *afgeleid* (attributie is een projectie van
11
+ * identiteit), niet parallel bijgehouden. Zie `resolveAuthor`.
12
+ */
13
+ export type ActingIdentity = {
14
+ kind: "user";
15
+ userId: string;
16
+ } | {
17
+ kind: "team";
18
+ teamId: string;
19
+ } | {
20
+ kind: "org";
21
+ };
22
+ /**
23
+ * Compacte, header-veilige vorm: `"user:<id>"` | `"team:<id>"` | `"org"`.
24
+ * ASCII, geen JSON — een header hoort geen gestructureerde payload te dragen, en
25
+ * dit blijft leesbaar in logs.
26
+ */
27
+ export declare function formatActingIdentity(actor: ActingIdentity): string;
28
+ /**
29
+ * Leest de compacte vorm terug. Strikt: een onbekende soort, een ontbrekende of
30
+ * lege ref levert `undefined` — nooit een gok en nooit een half-gevulde identiteit
31
+ * zoals `{ kind: "user", userId: "" }`, want die zou als geldig door een gate
32
+ * glippen.
33
+ *
34
+ * Round-trip: `parseActingIdentity(formatActingIdentity(a))` is diep gelijk aan `a`.
35
+ */
36
+ export declare function parseActingIdentity(raw: string | null | undefined): ActingIdentity | undefined;
37
+ /**
38
+ * Stabiele sleutel om identiteiten te vergelijken of te dedupliceren (bijvoorbeeld
39
+ * "één autorisatie-call per distincte actor"). Gelijk aan de wire-vorm; apart
40
+ * genoemd omdat de bedoeling anders is dan transporteren.
41
+ */
42
+ export declare function actingIdentityKey(actor: ActingIdentity): string;
@@ -1,10 +1,4 @@
1
- /**
2
- * Cross-app scope-authorization contract. A resource is addressed by an opaque
3
- * scopeKey ("<kind>:<ref>", e.g. "interaction:abc"); the app that owns that kind
4
- * answers "may this user access it?" over the `scope` provider role. Consumed by
5
- * the AI live-assistant (conversation access) and, from fase 2, the attribute store.
6
- * Implementation: authorizeScope / assertScopeAccess in @opencxh/platform-api.
7
- */
1
+ import { ActingIdentity } from './identity';
8
2
  /** Access decision + whether the scope anchor is team-shared + the SSE audience. */
9
3
  export interface ScopeAuth {
10
4
  allowed: boolean;
@@ -14,9 +8,27 @@ export interface ScopeAuth {
14
8
  /** GET /provider/scope/describe response: the scopeKey prefixes an app owns. */
15
9
  export interface ScopeDescribe {
16
10
  kinds: string[];
11
+ /**
12
+ * Welke soorten acting identity deze provider daadwerkelijk evalueert.
13
+ * Afwezig = alleen het klassieke `userId`-contract.
14
+ *
15
+ * Dit is een fail-closed schakelaar, geen documentatie: een provider die
16
+ * `actor` negeert ziet bij een team-actor een lege `userId` en zou dat als
17
+ * "trusted system caller" lezen — dus volledige toegang. `authorizeIdentity`
18
+ * weigert daarom een `team`-actor zolang de eigenaar `"team"` niet declareert.
19
+ */
20
+ actorKinds?: ActingIdentity["kind"][];
17
21
  }
18
- /** POST /provider/scope/authorize body. userId omitted = trusted system caller. */
22
+ /**
23
+ * POST /provider/scope/authorize body.
24
+ *
25
+ * `userId` blijft voor back-compat: bestaande providers lezen alleen dat veld, en
26
+ * een `user`-actor vult het mee zodat zij ongewijzigd blijven werken. `userId`
27
+ * afwezig = trusted system caller (ruim!), dus zie de waarschuwing bij
28
+ * {@link ScopeDescribe.actorKinds}.
29
+ */
19
30
  export interface ScopeAuthorizeRequest {
20
31
  scopeKey: string;
21
32
  userId?: string;
33
+ actor?: ActingIdentity;
22
34
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.123.0",
3
+ "version": "1.125.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",