@opencxh/domain 1.90.0 → 1.92.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,2 +1,3 @@
1
1
  export * from './types';
2
2
  export * from './preview';
3
+ export * from './seen';
@@ -0,0 +1,10 @@
1
+ import { ReadReceipts } from '../read-state/types';
2
+ import { Activity } from './types';
3
+ /**
4
+ * Users who have seen this activity, derived from high-water read marks: a user
5
+ * has seen the activity when their lastReadAt is at or after the activity's
6
+ * createdAt. Excludes the activity's own author (user-authored) and any
7
+ * explicitly excluded ids - typically the viewing user, whose own receipt we
8
+ * never render.
9
+ */
10
+ export declare function getActivitySeenUserIds(activity: Activity, receipts: ReadReceipts, excludeUserIds?: string[]): string[];
@@ -3,7 +3,7 @@ import { TranscriptSegment } from '../../platform/media';
3
3
  export type CallStatus = "new" | "connecting" | "ringing" | "connected" | "held" | "ended" | "failed";
4
4
  export type CallDirection = "inbound" | "outbound";
5
5
  export type CallType = "audio" | "video" | "data" | "screen-share";
6
- export type ActivityType = "VOICE_CALL_STARTED" | "VOICE_CALL_ANSWERED" | "VOICE_CALL_HOLD" | "VOICE_CALL_UNHOLD" | "VOICE_CALL_ENDED" | "VOICE_CALL_MISSED" | "VOICE_CALL_VOICEMAIL" | "VIDEO_CALL_STARTED" | "VIDEO_CALL_ANSWERED" | "VIDEO_CALL_HOLD" | "VIDEO_CALL_UNHOLD" | "VIDEO_CALL_ENDED" | "VIDEO_CALL_MISSED" | "EMAIL_RECEIVED" | "EMAIL_SENT" | "CHAT_MESSAGE_SENT" | "CHAT_MESSAGE_RECEIVED" | "CHAT_MEMBER_JOINED" | "CHAT_MEMBER_LEFT" | "CHAT_RENAMED" | "CHAT_CALL_STARTED" | "CHAT_CALL_ENDED" | "CHAT_EVENT" | "AI_MESSAGE_ADDED" | "MEETING_SCHEDULED" | "MEETING_STARTED" | "MEETING_ENDED" | "MEETING_PARTICIPANT_JOINED" | "MEETING_PARTICIPANT_LEFT" | "COMMENT_ADDED" | "FILE_UPLOADED" | "INTERACTION_CREATED" | "INTERACTION_STATUS_CHANGED" | "INTERACTION_ASSIGNED" | "VOICE_CALL_FAILED" | "VIDEO_CALL_FAILED" | "TRANSCRIPT_ADDED";
6
+ export type ActivityType = "VOICE_CALL_STARTED" | "VOICE_CALL_ANSWERED" | "VOICE_CALL_HOLD" | "VOICE_CALL_UNHOLD" | "VOICE_CALL_ENDED" | "VOICE_CALL_MISSED" | "VOICE_CALL_VOICEMAIL" | "VIDEO_CALL_STARTED" | "VIDEO_CALL_ANSWERED" | "VIDEO_CALL_HOLD" | "VIDEO_CALL_UNHOLD" | "VIDEO_CALL_ENDED" | "VIDEO_CALL_MISSED" | "EMAIL_RECEIVED" | "EMAIL_SENT" | "CHAT_MESSAGE_SENT" | "CHAT_MESSAGE_RECEIVED" | "CHAT_MEMBER_JOINED" | "CHAT_MEMBER_LEFT" | "CHAT_RENAMED" | "CHAT_CALL_STARTED" | "CHAT_CALL_ENDED" | "CHAT_EVENT" | "AI_MESSAGE_ADDED" | "AI_ACTION_PROPOSED" | "PLAYBOOK_STARTED" | "PLAYBOOK_COMPLETED" | "PLAYBOOK_ESCALATED" | "MEETING_SCHEDULED" | "MEETING_STARTED" | "MEETING_ENDED" | "MEETING_PARTICIPANT_JOINED" | "MEETING_PARTICIPANT_LEFT" | "COMMENT_ADDED" | "FILE_UPLOADED" | "INTERACTION_CREATED" | "INTERACTION_STATUS_CHANGED" | "INTERACTION_ASSIGNED" | "VOICE_CALL_FAILED" | "VIDEO_CALL_FAILED" | "TRANSCRIPT_ADDED";
7
7
  export interface Attachment {
8
8
  id: string;
9
9
  filename: string;
@@ -183,6 +183,29 @@ export type AIMessageAddedPayload = {
183
183
  accountId?: string;
184
184
  model?: string;
185
185
  };
186
+ /**
187
+ * A playbook proposes one or more actions for a human to approve/reject (autonomy:
188
+ * suggest). Rendered inline in the interaction timeline; approve/reject call the ai
189
+ * app's `playbook-run` endpoints. System-authored, so it does not re-trigger playbooks.
190
+ */
191
+ export type AIActionProposedPayload = {
192
+ runId: string;
193
+ playbookId?: string;
194
+ playbookName?: string;
195
+ /** The held actions, e.g. { action: "tool:mcp__jira__create_issue", params }. */
196
+ actions: {
197
+ action: string;
198
+ params?: Record<string, unknown>;
199
+ }[];
200
+ status: "pending" | "approved" | "rejected";
201
+ };
202
+ /** Playbook lifecycle marker in the interaction timeline (system-authored). */
203
+ export type PlaybookLifecyclePayload = {
204
+ runId: string;
205
+ playbookId?: string;
206
+ playbookName?: string;
207
+ status?: string;
208
+ };
186
209
  export type Activity = (BaseActivity & {
187
210
  type: "VOICE_CALL_STARTED";
188
211
  payload: VoiceCallPayload;
@@ -264,6 +287,18 @@ export type Activity = (BaseActivity & {
264
287
  }) | (BaseActivity & {
265
288
  type: "AI_MESSAGE_ADDED";
266
289
  payload: AIMessageAddedPayload;
290
+ }) | (BaseActivity & {
291
+ type: "AI_ACTION_PROPOSED";
292
+ payload: AIActionProposedPayload;
293
+ }) | (BaseActivity & {
294
+ type: "PLAYBOOK_STARTED";
295
+ payload: PlaybookLifecyclePayload;
296
+ }) | (BaseActivity & {
297
+ type: "PLAYBOOK_COMPLETED";
298
+ payload: PlaybookLifecyclePayload;
299
+ }) | (BaseActivity & {
300
+ type: "PLAYBOOK_ESCALATED";
301
+ payload: PlaybookLifecyclePayload;
267
302
  }) | (BaseActivity & {
268
303
  type: "MEETING_SCHEDULED";
269
304
  payload: MeetingPayload;
@@ -22,6 +22,22 @@ export interface AIProfile<T extends Record<string, any> = Record<string, any>>
22
22
  predefinedPrompts?: PredefinedPrompt[];
23
23
  /** Namespaced tool names this profile may use (default: none enabled). */
24
24
  enabledTools?: string[];
25
+ /**
26
+ * Per-tool confirmation policy. Stored as a LIST (not a keyed map) on purpose:
27
+ * namespaced tool names contain "__", and the SDK client transforms every
28
+ * object KEY between camelCase/snake_case — which mangles map keys (e.g.
29
+ * `communication__compose_email` -> `communication_ComposeEmail`) so they no
30
+ * longer match `enabledTools`. As a list the tool name lives in a string VALUE,
31
+ * which the transform leaves untouched.
32
+ *
33
+ * Under autonomy "suggest", write tools are held for approval; reads run freely.
34
+ * A tool absent from this list defaults to "write" (safe). Also usable by the
35
+ * interactive assistant's requiresConfirmation flow.
36
+ */
37
+ toolPolicy?: {
38
+ name: string;
39
+ policy: "read" | "write";
40
+ }[];
25
41
  /**
26
42
  * Whether the acting user's PERSONAL MCP tools (their own per-user connections)
27
43
  * are available on this profile, on top of the admin-curated `enabledTools`.
@@ -6,6 +6,11 @@ export interface ChannelSignature {
6
6
  updatedAt?: number;
7
7
  }
8
8
  export type ChannelSignaturesMap = Partial<Record<CommunicationIntent | string, ChannelSignature>>;
9
+ /**
10
+ * Intents die een handtekening dragen. Enkel deze verschijnen in de
11
+ * signature-editor en worden door `applySignature` van een delimiter voorzien.
12
+ */
13
+ export declare const SIGNATURE_INTENTS: Set<string>;
9
14
  /**
10
15
  * Resolve the signature a provider should append for an outbound message
11
16
  * on this channel + intent. Returns null when no signature is configured,
@@ -15,3 +20,13 @@ export type ChannelSignaturesMap = Partial<Record<CommunicationIntent | string,
15
20
  * resulting activity/interaction.
16
21
  */
17
22
  export declare function resolveSignature(channel: Channel, intent: CommunicationIntent | string): ChannelSignature | null;
23
+ /**
24
+ * Append de handtekening voor dit channel + intent aan een uitgaande body.
25
+ * Retourneert de body ongewijzigd wanneer er geen (enabled, niet-lege)
26
+ * handtekening is. Centrale plek voor de delimiter-logica zodat elke provider
27
+ * dezelfde opmaak gebruikt i.p.v. 'm per send-pad te dupliceren:
28
+ * - mail (html) -> RFC-style "-- " scheider
29
+ * - message (html) -> dubbele <br>
30
+ * - text -> dubbele newline (plain-text varianten / timeline)
31
+ */
32
+ export declare function applySignature(channel: Channel | undefined, intent: CommunicationIntent | string, body: string, format?: "html" | "text"): string;
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,154 @@
1
+ import { OwnerScope } from '../contact/types';
2
+ /**
3
+ * Playbooks — event-driven automation with AI steps. See docs/PLAYBOOKS.md.
4
+ * Fase 0 covers the deterministic subset; AI steps are typed here but the
5
+ * executor rejects them until Fase 2.
6
+ */
7
+ export type Autonomy = "suggest" | "auto";
8
+ export type PlaybookTrigger = {
9
+ kind: "activity";
10
+ activityTypes: string[];
11
+ /** Channel kinds: "mail" | "chat" | "voice" (derived from the activity type). */
12
+ channels?: string[];
13
+ /** Specific channel ids (e.g. per mailbox). Matched against activity.channelId. */
14
+ channelIds?: string[];
15
+ filter?: unknown;
16
+ } | {
17
+ kind: "manual";
18
+ };
19
+ export interface Guardrails {
20
+ maxSteps?: number;
21
+ maxToolCalls?: number;
22
+ policyCaps?: Record<string, unknown>;
23
+ }
24
+ export interface ConditionStep {
25
+ type: "condition";
26
+ /** JSONLogic expression evaluated against the run vars. */
27
+ if: unknown;
28
+ /** Gate: what to do when the condition is false. "then" = the steps that follow. */
29
+ onFalse: "stop" | "escalate" | "continue";
30
+ }
31
+ export interface ForEachStep {
32
+ type: "for-each";
33
+ in: string;
34
+ as: string;
35
+ body: Step[];
36
+ }
37
+ export interface LookupStep {
38
+ type: "lookup";
39
+ /** "tool:<namespaced>" (Fase 1) or a local source ("interactions"/"tasks"). */
40
+ source: string;
41
+ filter?: unknown;
42
+ as: string;
43
+ }
44
+ export interface ActionStep {
45
+ type: "action";
46
+ /** Local action ("task.create"/"interaction.set"/"notify.user") or "tool:<namespaced>" (Fase 1). */
47
+ action: string;
48
+ params: Record<string, unknown>;
49
+ autonomy?: Autonomy;
50
+ }
51
+ /** AI steps — typed in Fase 0, executed from Fase 2. */
52
+ export interface ClassifyStep {
53
+ type: "classify";
54
+ as: string;
55
+ accountId: string;
56
+ model: string;
57
+ mode: "topics" | "question" | "extract";
58
+ topicIds?: string[];
59
+ question?: string;
60
+ fields?: unknown;
61
+ instruction?: string;
62
+ }
63
+ export interface GenerateStep {
64
+ type: "generate";
65
+ profileId: string;
66
+ goal?: string;
67
+ kind: "reply" | "compose" | "note";
68
+ as?: string;
69
+ }
70
+ export interface AgentStep {
71
+ type: "agent";
72
+ profileId: string;
73
+ goal: string;
74
+ guardrails: Guardrails;
75
+ toolsOverride?: string[];
76
+ autonomy?: Autonomy;
77
+ as?: string;
78
+ }
79
+ export interface WaitForReplyStep {
80
+ type: "wait-for-reply";
81
+ deadline?: number;
82
+ }
83
+ export type Step = ConditionStep | ForEachStep | LookupStep | ActionStep | ClassifyStep | GenerateStep | AgentStep | WaitForReplyStep;
84
+ export interface Playbook {
85
+ id: string;
86
+ organizationId: string;
87
+ ownerScope: OwnerScope;
88
+ name: string;
89
+ description?: string;
90
+ enabled: boolean;
91
+ autonomy: Autonomy;
92
+ trigger: PlaybookTrigger;
93
+ steps: Step[];
94
+ /** Bumped on every change; runs pin to the version they started on. */
95
+ version: number;
96
+ /** Actor of autonomous actions. */
97
+ createdBy: string;
98
+ lastRunAt?: number;
99
+ stats?: {
100
+ runs: number;
101
+ proposals: number;
102
+ autoActions: number;
103
+ };
104
+ }
105
+ export type RunStatus = "pending" | "running" | "awaiting_approval" | "awaiting_reply" | "done" | "escalated" | "failed" | "timed_out" | "stopped";
106
+ export interface RunStepTrace {
107
+ path: string;
108
+ type: string;
109
+ ok: boolean;
110
+ note?: string;
111
+ ms?: number;
112
+ }
113
+ export interface PlaybookRun {
114
+ id: string;
115
+ organizationId: string;
116
+ playbookId: string;
117
+ playbookVersion: number;
118
+ interactionId?: string;
119
+ status: RunStatus;
120
+ trigger: {
121
+ activityType?: string;
122
+ activityId?: string;
123
+ event: Record<string, unknown>;
124
+ };
125
+ vars: Record<string, unknown>;
126
+ trace: RunStepTrace[];
127
+ /** Snapshot of the playbook steps at run start — version-pinned resume (docs 11.4). */
128
+ steps?: Step[];
129
+ /** Held action(s) awaiting human approval (autonomy: suggest) — a batch per paused step. */
130
+ pending?: {
131
+ stepIndex: number;
132
+ actions: {
133
+ action: string;
134
+ params: Record<string, unknown>;
135
+ }[];
136
+ };
137
+ /** 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;
144
+ rounds?: number;
145
+ /** Denormalized interaction audience for efficient list-filtering. */
146
+ audience?: string[];
147
+ createdBy: string;
148
+ approvedBy?: string;
149
+ result?: {
150
+ ok: boolean;
151
+ error?: string;
152
+ };
153
+ lastStepAt?: number;
154
+ }
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Generic read-tracking primitives. Addressing is polymorphic via ResourceRef
3
+ * so read-state (and, later, an audit log) can attach to any resource -
4
+ * interactions, activities, files, kb articles - without per-type schemas.
5
+ */
6
+ /** Polymorphic reference to any resource that can carry read-state. */
7
+ export interface ResourceRef {
8
+ type: string;
9
+ id: string;
10
+ }
11
+ /**
12
+ * A single user's high-water read mark on one resource. `lastReadAt` is the
13
+ * epoch (ms) up to which this user has read the resource; whether they have
14
+ * seen a given child item is derived by comparing it against the item's time.
15
+ */
16
+ export interface ReadState {
17
+ id: string;
18
+ resourceType: string;
19
+ resourceId: string;
20
+ userId: string;
21
+ organizationId: string;
22
+ lastReadAt: number;
23
+ createdAt?: number;
24
+ updatedAt?: number;
25
+ }
26
+ /** Derived map of userId -> lastReadAt for a single resource. */
27
+ export type ReadReceipts = Record<string, number>;
package/dist/index.cjs CHANGED
@@ -1 +1,8 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function i(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 A(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function o(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 I(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||i(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?` (${o(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?` (${o(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"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?` (${o(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 _(e){return{activityId:e.id,type:e.type,snippet:A(I(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function T(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function g(e){return e.endpoints??[]}const D=e=>!!e.assignedUserId||!!e.assignedInboxId,S=e=>e.status==="closed",f=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,N=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function R(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}function u(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(a=>!n.has(a.intent)).map(a=>O(a,r[a.intent]));if(!e.extraIntents||e.extraIntents.length===0)return s;const E=new Set(s.map(a=>a.intent));for(const a of e.extraIntents)E.has(a.intent)||(s.push(a),E.add(a.intent));return s}function c(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 L(e,t){const n=c(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function O(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function C(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const E of u(r,s))n.push({channel:r,description:s,capability:E})}return n}function M(e,t){return t.filter(n=>n.capability.intent===e)}function d(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function b(e,t){return d(e.scheme,t)}function P(e,t,n){const r=[];for(const s of e)for(const E of n)E.capability.intent===t&&E.capability.targetSchemes.includes(s.scheme)&&r.push({channelIntent:E,endpoint:s});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 G="message_window",U="message_templates",V={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},m=(e,t)=>({intent:e,...t}),y="folder_management",H="remote_search",h={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},$=9e4,F="installation-id";exports.CommunicationScheme=p;exports.FEATURE_FOLDER_MANAGEMENT=y;exports.FEATURE_REMOTE_SEARCH=H;exports.INSTALLATION_HEADER=F;exports.InteractionParticipantRole=V;exports.PRESENCE_ONLINE_WINDOW_MS=$;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=U;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=G;exports.UNSUPPORTED=h;exports.buildActivityPreview=_;exports.buildChannelIntents=C;exports.defineIntent=m;exports.disabledIntentsFromCapabilities=L;exports.filterByEndpoint=b;exports.filterByIntent=M;exports.filterByTargetScheme=d;exports.getContactEndpoints=g;exports.getShortTitle=N;exports.getUrgencyScore=f;exports.isAssigned=D;exports.isClosed=S;exports.isInteractionUnseen=R;exports.matchContactToIntents=P;exports.resolveAccountCapabilities=c;exports.resolveChannelIntents=u;exports.resolveSignature=T;exports.stripHtml=i;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const l=140;function p(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 T(e){const t=e.trim();return t.length<=l?t:t.slice(0,l-1).trimEnd()+"…"}function E(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 f(e){switch(e.type){case"EMAIL_RECEIVED":case"EMAIL_SENT":{const t=e.payload,n=t.bodySnippet?.trim()||p(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?` (${E(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?` (${E(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?` (${E(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 S(e){return{activityId:e.id,type:e.type,snippet:T(f(e)),authorName:e.author?.name??"",direction:e.direction,createdAt:e.createdAt??Date.now()}}function N(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 D=new Set(["mail","message"]);function d(e,t){const n=e.settings?.signatures?.[t];return!n||!n.enabled||!n.body||n.body.trim()===""?null:n}function O(e,t,n,r="html"){if(!e)return n;const s=d(e,t);return s?`${n}${r==="text"?`
2
+
3
+ `:t==="mail"?"<br><br>-- <br>":"<br><br>"}${s.body}`:n}function R(e){return e.endpoints??[]}const L=e=>!!e.assignedUserId||!!e.assignedInboxId,C=e=>e.status==="closed",b=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,M=(e,t=30)=>e.title?.length<=t?e.title:`${e.title.substring(0,t)}...`;function m(e,t){return e.lastActivityAt?!(e.seenBy??[]).includes(t):!1}const P=[/<blockquote/i,/class="?gmail_quote/i,/id="?[^"]*divRplyFwdMsg/i,/-{3,}\s*Original Message\s*-{3,}/i,/\n_{5,}\s*\n/,/\bOn\b[\s\S]{0,200}?\bwrote:/i];function i(e){let t=e;return t=t.replace(/<(script|style)[\s\S]*?<\/\1>/gi,""),t=t.replace(/<br\s*\/?>/gi,`
4
+ `),t=t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi,`
5
+ `),t=t.replace(/<[^>]+>/g,""),t=t.replace(/<[^>]*$/,""),t}function c(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 u(e){return e.replace(/\r/g,"").replace(/[ \t]+/g," ").replace(/ *\n */g,`
6
+ `).replace(/\n{3,}/g,`
7
+
8
+ `).trim()}function y(e){if(typeof e!="string"||!e)return"";let t=e.length;for(const s of P){const a=e.search(s);a>=0&&a<t&&(t=a)}const n=e.slice(0,t);let r=u(c(i(n)));return r||(r=u(c(i(e)))),r}function A(e,t){const n=new Set(e.disabledIntents??[]),r=e.intentOverrides??{},s=t.intents.filter(o=>!n.has(o.intent)).map(o=>U(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 g(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 h(e,t){const n=g(e,t);return Object.entries(n).filter(([,r])=>!r).map(([r])=>r)}function U(e,t){return t?{intent:t.intent??e.intent,targetSchemes:t.targetSchemes??e.targetSchemes,transport:t.transport??e.transport}:e}function G(e,t){const n=[];for(const r of e){if(!r.enabled)continue;const s=t[r.providerId];if(s)for(const a of A(r,s))n.push({channel:r,description:s,capability:a})}return n}function V(e,t){return t.filter(n=>n.capability.intent===e)}function I(e,t){return t.filter(n=>n.capability.targetSchemes.includes(e))}function $(e,t){return I(e.scheme,t)}function H(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 _=(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))(_||{});const F="message_window",k="message_templates",B={FROM:"from",TO:"to",CC:"cc",BCC:"bcc",ORGANIZER:"organizer",PRESENTER:"presenter",ATTENDEE:"attendee",OWNER:"owner",MEMBER:"member",GUEST:"guest",HOST:"host",PARTICIPANT:"participant"},x=(e,t)=>({intent:e,...t}),w="folder_management",W="remote_search",j={ok:!1,code:"UNSUPPORTED",message:"Provider does not support this op"},K=9e4,q="installation-id";exports.CommunicationScheme=_;exports.FEATURE_FOLDER_MANAGEMENT=w;exports.FEATURE_REMOTE_SEARCH=W;exports.INSTALLATION_HEADER=q;exports.InteractionParticipantRole=B;exports.PRESENCE_ONLINE_WINDOW_MS=K;exports.PROVIDER_FEATURE_MESSAGE_TEMPLATES=k;exports.PROVIDER_FEATURE_MESSAGE_WINDOW=F;exports.SIGNATURE_INTENTS=D;exports.UNSUPPORTED=j;exports.applySignature=O;exports.buildActivityPreview=S;exports.buildChannelIntents=G;exports.cleanMessageText=y;exports.defineIntent=x;exports.disabledIntentsFromCapabilities=h;exports.filterByEndpoint=$;exports.filterByIntent=V;exports.filterByTargetScheme=I;exports.getActivitySeenUserIds=N;exports.getContactEndpoints=R;exports.getShortTitle=M;exports.getUrgencyScore=b;exports.isAssigned=L;exports.isClosed=C;exports.isInteractionUnseen=m;exports.matchContactToIntents=H;exports.resolveAccountCapabilities=g;exports.resolveChannelIntents=A;exports.resolveSignature=d;exports.stripHtml=p;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './entities/activity';
2
+ export * from './entities/playbook';
2
3
  export * from './entities/kb';
3
4
  export * from './entities/topic';
4
5
  export * from './entities/ai-account';
@@ -16,11 +17,13 @@ export * from './entities/inbox';
16
17
  export * from './entities/interaction';
17
18
  export * from './entities/task';
18
19
  export * from './entities/note';
20
+ export * from './entities/read-state';
19
21
  export * from './entities/team';
20
22
  export * from './entities/organization';
21
23
  export * from './entities/shopify';
22
24
  export * from './entities/transcript';
23
25
  export * from './entities/user';
26
+ export * from './text/message';
24
27
  export * from './platform/ai-tools';
25
28
  export * from './platform/api';
26
29
  export * from './platform/common';
package/dist/index.js CHANGED
@@ -1,21 +1,21 @@
1
- function l(e) {
1
+ function p(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 u(e) {
4
+ function i(e) {
5
5
  const t = e.trim();
6
6
  return t.length <= 140 ? t : t.slice(0, 139).trimEnd() + "…";
7
7
  }
8
- function E(e) {
8
+ function l(e) {
9
9
  if (!e || e < 0) return "";
10
- const t = Math.floor(e / 60), n = Math.floor(e % 60);
11
- return `${t}:${n.toString().padStart(2, "0")}`;
10
+ const t = Math.floor(e / 60), r = Math.floor(e % 60);
11
+ return `${t}:${r.toString().padStart(2, "0")}`;
12
12
  }
13
- function p(e) {
13
+ function d(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() || l(t.body ?? "");
18
- return t.subject ? `${t.subject} — ${n}` : n;
17
+ const t = e.payload, r = t.bodySnippet?.trim() || p(t.body ?? "");
18
+ return t.subject ? `${t.subject} — ${r}` : r;
19
19
  }
20
20
  case "CHAT_MESSAGE_SENT":
21
21
  case "CHAT_MESSAGE_RECEIVED":
@@ -29,7 +29,7 @@ function p(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 ? ` (${E(e.payload.duration)})` : ""}`;
32
+ return `Gesprek beëindigd${e.payload.duration ? ` (${l(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 p(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 ? ` (${E(e.payload.duration)})` : ""}`;
49
+ return `Gesprek beëindigd${e.payload.duration ? ` (${l(e.payload.duration)})` : ""}`;
50
50
  case "VOICE_CALL_MISSED":
51
51
  case "VIDEO_CALL_MISSED":
52
52
  return "Gemiste oproep";
@@ -59,12 +59,20 @@ function p(e) {
59
59
  return (e.payload.segments ?? []).map((t) => t.text).join(" ");
60
60
  case "AI_MESSAGE_ADDED":
61
61
  return e.payload.output?.text ?? e.payload.input?.text ?? "";
62
+ case "AI_ACTION_PROPOSED":
63
+ return `Voorstel wacht op goedkeuring (${e.payload.actions?.length ?? 0} actie(s))`;
64
+ case "PLAYBOOK_STARTED":
65
+ return `Playbook gestart${e.payload.playbookName ? `: ${e.payload.playbookName}` : ""}`;
66
+ case "PLAYBOOK_COMPLETED":
67
+ return "Playbook afgerond";
68
+ case "PLAYBOOK_ESCALATED":
69
+ return "Playbook geëscaleerd naar een mens";
62
70
  case "MEETING_SCHEDULED":
63
71
  return `Vergadering gepland: ${e.payload.title}`;
64
72
  case "MEETING_STARTED":
65
73
  return `Vergadering gestart: ${e.payload.title}`;
66
74
  case "MEETING_ENDED":
67
- return `Vergadering beëindigd${e.payload.duration ? ` (${E(e.payload.duration)})` : ""}`;
75
+ return `Vergadering beëindigd${e.payload.duration ? ` (${l(e.payload.duration)})` : ""}`;
68
76
  case "MEETING_PARTICIPANT_JOINED":
69
77
  return `${e.payload.name} deelgenomen`;
70
78
  case "MEETING_PARTICIPANT_LEFT":
@@ -83,89 +91,141 @@ function p(e) {
83
91
  return "";
84
92
  }
85
93
  }
86
- function _(e) {
94
+ function S(e) {
87
95
  return {
88
96
  activityId: e.id,
89
97
  type: e.type,
90
- snippet: u(p(e)),
98
+ snippet: i(d(e)),
91
99
  authorName: e.author?.name ?? "",
92
100
  direction: e.direction,
93
101
  createdAt: e.createdAt ?? Date.now()
94
102
  };
95
103
  }
96
- function T(e, t) {
97
- const n = e.settings?.signatures?.[t];
98
- return !n || !n.enabled || !n.body || n.body.trim() === "" ? null : n;
104
+ function N(e, t, r = []) {
105
+ const n = e.createdAt;
106
+ if (!n) return [];
107
+ const s = new Set(r);
108
+ return e.author.type === "user" && e.author.id && s.add(e.author.id), Object.entries(t).filter(([a, o]) => o >= n && !s.has(a)).map(([a]) => a);
99
109
  }
100
- function g(e) {
110
+ const L = /* @__PURE__ */ new Set(["mail", "message"]);
111
+ function A(e, t) {
112
+ const r = e.settings?.signatures?.[t];
113
+ return !r || !r.enabled || !r.body || r.body.trim() === "" ? null : r;
114
+ }
115
+ function O(e, t, r, n = "html") {
116
+ if (!e) return r;
117
+ const s = A(e, t);
118
+ return s ? `${r}${n === "text" ? `
119
+
120
+ ` : t === "mail" ? "<br><br>-- <br>" : "<br><br>"}${s.body}` : r;
121
+ }
122
+ function R(e) {
101
123
  return e.endpoints ?? [];
102
124
  }
103
- const D = (e) => !!e.assignedUserId || !!e.assignedInboxId, f = (e) => e.status === "closed", L = (e) => ({
125
+ const b = (e) => !!e.assignedUserId || !!e.assignedInboxId, C = (e) => e.status === "closed", M = (e) => ({
104
126
  urgent: 10,
105
127
  high: 5,
106
128
  normal: 2,
107
129
  low: 1
108
- })[e.priority] || 0, N = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
109
- function S(e, t) {
130
+ })[e.priority] || 0, m = (e, t = 30) => e.title?.length <= t ? e.title : `${e.title.substring(0, t)}...`;
131
+ function P(e, t) {
110
132
  return e.lastActivityAt ? !(e.seenBy ?? []).includes(t) : !1;
111
133
  }
112
- function d(e, t) {
113
- const n = new Set(e.disabledIntents ?? []), r = e.intentOverrides ?? {}, s = t.intents.filter((a) => !n.has(a.intent)).map((a) => A(a, r[a.intent]));
134
+ const g = [
135
+ /<blockquote/i,
136
+ /class="?gmail_quote/i,
137
+ // Gmail quote container
138
+ /id="?[^"]*divRplyFwdMsg/i,
139
+ // Outlook web reply/forward header
140
+ /-{3,}\s*Original Message\s*-{3,}/i,
141
+ /\n_{5,}\s*\n/,
142
+ // Outlook underscore separator before "From:"
143
+ /\bOn\b[\s\S]{0,200}?\bwrote:/i
144
+ // Gmail "On <date> <name> wrote:"
145
+ ];
146
+ function E(e) {
147
+ let t = e;
148
+ return t = t.replace(/<(script|style)[\s\S]*?<\/\1>/gi, ""), t = t.replace(/<br\s*\/?>/gi, `
149
+ `), t = t.replace(/<\/(p|div|li|tr|h[1-6]|ul|ol|table)>/gi, `
150
+ `), t = t.replace(/<[^>]+>/g, ""), t = t.replace(/<[^>]*$/, ""), t;
151
+ }
152
+ function u(e) {
153
+ return e.replace(/&nbsp;/gi, " ").replace(/&lt;/gi, "<").replace(/&gt;/gi, ">").replace(/&quot;/gi, '"').replace(/&#39;/gi, "'").replace(/&#(\d+);/g, (t, r) => String.fromCharCode(Number(r))).replace(/&amp;/gi, "&");
154
+ }
155
+ function c(e) {
156
+ return e.replace(/\r/g, "").replace(/[ \t]+/g, " ").replace(/ *\n */g, `
157
+ `).replace(/\n{3,}/g, `
158
+
159
+ `).trim();
160
+ }
161
+ function $(e) {
162
+ if (typeof e != "string" || !e) return "";
163
+ let t = e.length;
164
+ for (const s of g) {
165
+ const a = e.search(s);
166
+ a >= 0 && a < t && (t = a);
167
+ }
168
+ const r = e.slice(0, t);
169
+ let n = c(u(E(r)));
170
+ return n || (n = c(u(E(e)))), n;
171
+ }
172
+ function _(e, t) {
173
+ const r = new Set(e.disabledIntents ?? []), n = e.intentOverrides ?? {}, s = t.intents.filter((o) => !r.has(o.intent)).map((o) => T(o, n[o.intent]));
114
174
  if (!e.extraIntents || e.extraIntents.length === 0) return s;
115
- const o = new Set(s.map((a) => a.intent));
116
- for (const a of e.extraIntents)
117
- o.has(a.intent) || (s.push(a), o.add(a.intent));
175
+ const a = new Set(s.map((o) => o.intent));
176
+ for (const o of e.extraIntents)
177
+ a.has(o.intent) || (s.push(o), a.add(o.intent));
118
178
  return s;
119
179
  }
120
- function c(e, t) {
121
- const n = {};
122
- for (const r of e.intents) {
123
- if (!r.togglable) continue;
124
- const s = t?.[r.intent];
125
- n[r.intent] = s ?? r.defaultEnabled ?? !0;
180
+ function I(e, t) {
181
+ const r = {};
182
+ for (const n of e.intents) {
183
+ if (!n.togglable) continue;
184
+ const s = t?.[n.intent];
185
+ r[n.intent] = s ?? n.defaultEnabled ?? !0;
126
186
  }
127
- return n;
187
+ return r;
128
188
  }
129
- function R(e, t) {
130
- const n = c(e, t);
131
- return Object.entries(n).filter(([, r]) => !r).map(([r]) => r);
189
+ function h(e, t) {
190
+ const r = I(e, t);
191
+ return Object.entries(r).filter(([, n]) => !n).map(([n]) => n);
132
192
  }
133
- function A(e, t) {
193
+ function T(e, t) {
134
194
  return t ? {
135
195
  intent: t.intent ?? e.intent,
136
196
  targetSchemes: t.targetSchemes ?? e.targetSchemes,
137
197
  transport: t.transport ?? e.transport
138
198
  } : e;
139
199
  }
140
- function O(e, t) {
141
- const n = [];
142
- for (const r of e) {
143
- if (!r.enabled) continue;
144
- const s = t[r.providerId];
200
+ function y(e, t) {
201
+ const r = [];
202
+ for (const n of e) {
203
+ if (!n.enabled) continue;
204
+ const s = t[n.providerId];
145
205
  if (s)
146
- for (const o of d(r, s))
147
- n.push({ channel: r, description: s, capability: o });
206
+ for (const a of _(n, s))
207
+ r.push({ channel: n, description: s, capability: a });
148
208
  }
149
- return n;
209
+ return r;
150
210
  }
151
- function C(e, t) {
152
- return t.filter((n) => n.capability.intent === e);
211
+ function V(e, t) {
212
+ return t.filter((r) => r.capability.intent === e);
153
213
  }
154
- function i(e, t) {
155
- return t.filter((n) => n.capability.targetSchemes.includes(e));
214
+ function f(e, t) {
215
+ return t.filter((r) => r.capability.targetSchemes.includes(e));
156
216
  }
157
- function M(e, t) {
158
- return i(e.scheme, t);
217
+ function G(e, t) {
218
+ return f(e.scheme, t);
159
219
  }
160
- function b(e, t, n) {
161
- const r = [];
220
+ function H(e, t, r) {
221
+ const n = [];
162
222
  for (const s of e)
163
- for (const o of n)
164
- o.capability.intent === t && o.capability.targetSchemes.includes(s.scheme) && r.push({ channelIntent: o, endpoint: s });
165
- return r;
223
+ for (const a of r)
224
+ a.capability.intent === t && a.capability.targetSchemes.includes(s.scheme) && n.push({ channelIntent: a, endpoint: s });
225
+ return n;
166
226
  }
167
- var I = /* @__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))(I || {});
168
- const P = "message_window", V = "message_templates", G = {
227
+ var D = /* @__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))(D || {});
228
+ const U = "message_window", k = "message_templates", x = {
169
229
  // E-mail
170
230
  FROM: "from",
171
231
  TO: "to",
@@ -182,33 +242,37 @@ const P = "message_window", V = "message_templates", G = {
182
242
  // Voice/conference
183
243
  HOST: "host",
184
244
  PARTICIPANT: "participant"
185
- }, H = (e, t) => ({ intent: e, ...t }), $ = "folder_management", y = "remote_search", U = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, m = 9e4, h = "installation-id";
245
+ }, F = (e, t) => ({ intent: e, ...t }), w = "folder_management", B = "remote_search", W = { ok: !1, code: "UNSUPPORTED", message: "Provider does not support this op" }, j = 9e4, K = "installation-id";
186
246
  export {
187
- I as CommunicationScheme,
188
- $ as FEATURE_FOLDER_MANAGEMENT,
189
- y as FEATURE_REMOTE_SEARCH,
190
- h as INSTALLATION_HEADER,
191
- G as InteractionParticipantRole,
192
- m as PRESENCE_ONLINE_WINDOW_MS,
193
- V as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
194
- P as PROVIDER_FEATURE_MESSAGE_WINDOW,
195
- U as UNSUPPORTED,
196
- _ as buildActivityPreview,
197
- O as buildChannelIntents,
198
- H as defineIntent,
199
- R as disabledIntentsFromCapabilities,
200
- M as filterByEndpoint,
201
- C as filterByIntent,
202
- i as filterByTargetScheme,
203
- g as getContactEndpoints,
204
- N as getShortTitle,
205
- L as getUrgencyScore,
206
- D as isAssigned,
207
- f as isClosed,
208
- S as isInteractionUnseen,
209
- b as matchContactToIntents,
210
- c as resolveAccountCapabilities,
211
- d as resolveChannelIntents,
212
- T as resolveSignature,
213
- l as stripHtml
247
+ D as CommunicationScheme,
248
+ w as FEATURE_FOLDER_MANAGEMENT,
249
+ B as FEATURE_REMOTE_SEARCH,
250
+ K as INSTALLATION_HEADER,
251
+ x as InteractionParticipantRole,
252
+ j as PRESENCE_ONLINE_WINDOW_MS,
253
+ k as PROVIDER_FEATURE_MESSAGE_TEMPLATES,
254
+ U as PROVIDER_FEATURE_MESSAGE_WINDOW,
255
+ L as SIGNATURE_INTENTS,
256
+ W as UNSUPPORTED,
257
+ O as applySignature,
258
+ S as buildActivityPreview,
259
+ y as buildChannelIntents,
260
+ $ as cleanMessageText,
261
+ F as defineIntent,
262
+ h as disabledIntentsFromCapabilities,
263
+ G as filterByEndpoint,
264
+ V as filterByIntent,
265
+ f as filterByTargetScheme,
266
+ N as getActivitySeenUserIds,
267
+ R as getContactEndpoints,
268
+ m as getShortTitle,
269
+ M as getUrgencyScore,
270
+ b as isAssigned,
271
+ C as isClosed,
272
+ P as isInteractionUnseen,
273
+ H as matchContactToIntents,
274
+ I as resolveAccountCapabilities,
275
+ _ as resolveChannelIntents,
276
+ A as resolveSignature,
277
+ p as stripHtml
214
278
  };
@@ -7,6 +7,13 @@
7
7
  */
8
8
  /** How recently a user's last device activity must be to count as online (ms). */
9
9
  export declare const PRESENCE_ONLINE_WINDOW_MS = 90000;
10
+ /**
11
+ * Effective presence status. `offline` is derived from connectivity (stale
12
+ * `lastSeenAt`) and always wins; the rest are set by the user or a provider.
13
+ */
14
+ export type PresenceStatus = "available" | "away" | "busy" | "dnd" | "out_of_office" | "offline";
15
+ /** Statuses a user (or provider) can set — `offline` is derived, not settable. */
16
+ export type SettablePresenceStatus = Exclude<PresenceStatus, "offline">;
10
17
  /** One tenant user with their derived online status, as returned by GET /presence. */
11
18
  export interface PresenceEntry {
12
19
  userId: string;
@@ -16,10 +23,23 @@ export interface PresenceEntry {
16
23
  lastSeenAt: number;
17
24
  /** `now - lastSeenAt < PRESENCE_ONLINE_WINDOW_MS` at the time of the read. */
18
25
  online: boolean;
26
+ /** Effective status: `offline` when not connected, else set/derived. */
27
+ status: PresenceStatus;
28
+ /** Optional free-text status message (e.g. "Back at 15:00"). */
29
+ message?: string;
19
30
  }
20
- /** SSE payload pushed on an offline->online transition (event `presence:changed`). */
31
+ /** SSE payload pushed when a user's presence changes (event `presence:changed`). */
21
32
  export interface PresenceChange {
22
33
  userId: string;
23
34
  online: boolean;
24
35
  lastSeenAt: number;
36
+ status: PresenceStatus;
37
+ message?: string;
38
+ }
39
+ /** Request body for setting one's own presence status. */
40
+ export interface PresenceStatusInput {
41
+ status: SettablePresenceStatus;
42
+ message?: string;
43
+ /** Unix ms after which the status auto-clears (falls back to derived). */
44
+ expiresAt?: number;
25
45
  }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Clean an inbound message body for display/LLM use: cut the quoted reply /
3
+ * forwarded thread, strip HTML, decode common entities, collapse whitespace.
4
+ * Without this the raw HTML + full Gmail/Outlook quote leaks the OLD message into
5
+ * classify/extract/agent (or history views) instead of the sender's new content.
6
+ * Pure, dependency-free (lives in domain so both server apps can share it).
7
+ */
8
+ export declare function cleanMessageText(raw: unknown): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.90.0",
3
+ "version": "1.92.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",