@opencxh/domain 1.18.0 → 1.19.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,4 +1,4 @@
1
- import { Capability, Uri } from '../../platform/communication';
1
+ import { IntentCapability, Uri } from '../../platform/communication';
2
2
  export interface Channel {
3
3
  id: string;
4
4
  organizationId: string;
@@ -12,7 +12,9 @@ export interface Channel {
12
12
  ownerId: string;
13
13
  externalIds?: string[];
14
14
  settings: Record<string, any>;
15
- capabilities?: Capability[];
15
+ disabledIntents?: string[];
16
+ intentOverrides?: Record<string, Partial<IntentCapability>>;
17
+ extraIntents?: IntentCapability[];
16
18
  enabled: boolean;
17
19
  verified: boolean;
18
20
  }
@@ -1,6 +1,2 @@
1
1
  import { Contact, ContactEndpoint } from './types';
2
- /**
3
- * Returns the contact's communication endpoints, synthesising them from
4
- * legacy phone/email fields when `endpoints` is not populated yet.
5
- */
6
2
  export declare function getContactEndpoints(contact: Contact): ContactEndpoint[];
@@ -1,2 +1,3 @@
1
1
  export * from './types';
2
2
  export * from './endpoints';
3
+ export * from './provider';
@@ -0,0 +1,13 @@
1
+ import { CommunicationScheme } from '../../platform/communication';
2
+ import { Contact, OwnerScope } from './types';
3
+ export interface ContactSearchQuery {
4
+ text?: string;
5
+ scope?: OwnerScope[];
6
+ limit?: number;
7
+ }
8
+ export interface ContactProvider {
9
+ id: string;
10
+ searchContacts(q: ContactSearchQuery): Promise<Contact[]>;
11
+ resolveByEndpoint(scheme: CommunicationScheme, resource: string): Promise<Contact | null>;
12
+ listContacts?(scope: OwnerScope): Promise<Contact[]>;
13
+ }
@@ -5,17 +5,29 @@ export interface ContactEndpoint {
5
5
  label?: string;
6
6
  primary?: boolean;
7
7
  }
8
- /**
9
- * Data model for a single Contact.
10
- */
8
+ export type OwnerScope = {
9
+ kind: "personal";
10
+ userId: string;
11
+ } | {
12
+ kind: "team";
13
+ teamId: string;
14
+ } | {
15
+ kind: "org";
16
+ organizationId: string;
17
+ };
18
+ export interface ContactSource {
19
+ providerId: string;
20
+ externalId?: string;
21
+ readOnly?: boolean;
22
+ }
11
23
  export interface Contact {
12
24
  id: string;
13
25
  organizationId: string;
26
+ ownerScope: OwnerScope;
27
+ source: ContactSource;
14
28
  firstName: string;
15
29
  lastName?: string;
16
30
  company?: string;
17
- email?: string;
18
- phone?: string;
19
31
  address?: string;
20
32
  endpoints?: ContactEndpoint[];
21
33
  }
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});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.VIBER="viber",e.SMS="sms",e.FAX="fax",e.TEAMS="teams",e))(o||{});function h(e){if(e.endpoints&&e.endpoints.length>0)return e.endpoints;const s=[];return e.phone&&s.push({scheme:o.TEL,resource:e.phone,primary:!0}),e.email&&s.push({scheme:o.MAILTO,resource:e.email,primary:!e.phone}),s}const g=e=>!!e.assignedUserId||!!e.assignedInboxId,b=e=>e.status==="closed",T=e=>({urgent:10,high:5,normal:2,low:1})[e.priority]||0,v=(e,s=30)=>e.title.length<=s?e.title:`${e.title.substring(0,s)}...`,E={[o.TEL]:["call"],[o.SIP]:["call"],[o.MAILTO]:["mail"],[o.SMS]:["message"],[o.WHATSAPP]:["message"],[o.TELEGRAM]:["message"],[o.VIBER]:["message"],[o.TEAMS]:["call","message","video"],[o.FAX]:["message"]},p={call:"transport",video:"transport",message:"transport",mail:"compose"},u=["call","mail","message","video"];function a(e,s){if(!e.supportedSchemes.includes(s))return[];const n=e.capabilities.filter(t=>t.scheme===s&&t.value===!0&&u.includes(t.key));return n.length>0?n.map(t=>t.key):E[s]??[]}function d(e){const s=new Set;for(const n of e.supportedSchemes)for(const t of a(e,n))s.add(t);return Array.from(s)}function f(e,s,n){return e.capabilities.find(r=>r.scheme===s&&r.key===n&&r.value===!0)?.dispatch??p[n]}function A(e,s){return Object.entries(e).map(([n,t])=>({providerId:n,description:t,intents:d(t)}))}function y(e,s){const n=[];for(const t of e){if(!t.enabled)continue;const r=s[t.providerId];if(r){if(t.capabilities&&t.capabilities.length>0){for(const i of t.capabilities){if(!i.value||!u.includes(i.key))continue;const c=i.scheme,l=i.key;n.push({channel:t,description:r,scheme:c,intent:l,dispatch:i.dispatch??p[l]})}continue}for(const i of r.supportedSchemes){const c=a(r,i);for(const l of c)n.push({channel:t,description:r,scheme:i,intent:l,dispatch:f(r,i,l)})}}}return n}function I(e,s){return s.filter(n=>n.intents.includes(e))}function S(e,s){return s.filter(n=>n.description.supportedSchemes.includes(e.scheme))}function F(e,s,n){const t=[];for(const r of e)for(const i of n)i.description.supportedSchemes.includes(r.scheme)&&a(i.description,r.scheme).includes(s)&&t.push({capability:i,endpoint:r});return t}function M(e,s,n){const t=[];for(const r of e)for(const i of n)i.scheme===r.scheme&&i.intent===s&&t.push({capability:i,endpoint:r});return t}exports.CommunicationScheme=o;exports.buildChannelCapabilities=y;exports.buildResolvedCapabilities=A;exports.dispatchForCapability=f;exports.getContactEndpoints=h;exports.getShortTitle=v;exports.getUrgencyScore=T;exports.intentsForProvider=d;exports.intentsForScheme=a;exports.isAssigned=g;exports.isClosed=b;exports.resolveContactChannelCapabilities=M;exports.resolveForContact=F;exports.resolveForEndpoint=S;exports.resolveForIntent=I;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function c(t){return t.endpoints??[]}const u=t=>!!t.assignedUserId||!!t.assignedInboxId,d=t=>t.status==="closed",g=t=>({urgent:10,high:5,normal:2,low:1})[t.priority]||0,p=(t,n=30)=>t.title?.length<=n?t.title:`${t.title.substring(0,n)}...`;function l(t,n){const s=new Set(t.disabledIntents??[]),i=t.intentOverrides??{},e=n.intents.filter(r=>!s.has(r.intent)).map(r=>I(r,i[r.intent]));if(!t.extraIntents||t.extraIntents.length===0)return e;const o=new Set(e.map(r=>r.intent));for(const r of t.extraIntents)o.has(r.intent)||(e.push(r),o.add(r.intent));return e}function I(t,n){return n?{intent:n.intent??t.intent,targetSchemes:n.targetSchemes??t.targetSchemes,transport:n.transport??t.transport}:t}function y(t,n){const s=[];for(const i of t){if(!i.enabled)continue;const e=n[i.providerId];if(e)for(const o of l(i,e))s.push({channel:i,description:e,capability:o})}return s}function b(t,n){return n.filter(s=>s.capability.intent===t)}function a(t,n){return n.filter(s=>s.capability.targetSchemes.includes(t))}function h(t,n){return a(t.scheme,n)}function T(t,n,s){const i=[];for(const e of t)for(const o of s)o.capability.intent===n&&o.capability.targetSchemes.includes(e.scheme)&&i.push({channelIntent:o,endpoint:e});return i}var f=(t=>(t.MAILTO="mailto",t.SIP="sip",t.TEL="tel",t.WEBHOOK="webhook",t.USERNAME="username",t.ID="id",t.CUSTOM="custom",t.URL="url",t.TELEGRAM="telegram",t.WHATSAPP="whatsapp",t.VIBER="viber",t.SMS="sms",t.FAX="fax",t.TEAMS="teams",t))(f||{});exports.CommunicationScheme=f;exports.buildChannelIntents=y;exports.filterByEndpoint=h;exports.filterByIntent=b;exports.filterByTargetScheme=a;exports.getContactEndpoints=c;exports.getShortTitle=p;exports.getUrgencyScore=g;exports.isAssigned=u;exports.isClosed=d;exports.matchContactToIntents=T;exports.resolveChannelIntents=l;
package/dist/index.js CHANGED
@@ -1,125 +1,66 @@
1
- var o = /* @__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.VIBER = "viber", e.SMS = "sms", e.FAX = "fax", e.TEAMS = "teams", e))(o || {});
2
- function g(e) {
3
- if (e.endpoints && e.endpoints.length > 0)
4
- return e.endpoints;
5
- const s = [];
6
- return e.phone && s.push({ scheme: o.TEL, resource: e.phone, primary: !0 }), e.email && s.push({ scheme: o.MAILTO, resource: e.email, primary: !e.phone }), s;
1
+ function c(t) {
2
+ return t.endpoints ?? [];
7
3
  }
8
- const T = (e) => !!e.assignedUserId || !!e.assignedInboxId, E = (e) => e.status === "closed", b = (e) => ({
4
+ const d = (t) => !!t.assignedUserId || !!t.assignedInboxId, p = (t) => t.status === "closed", g = (t) => ({
9
5
  urgent: 10,
10
6
  high: 5,
11
7
  normal: 2,
12
8
  low: 1
13
- })[e.priority] || 0, A = (e, s = 30) => e.title.length <= s ? e.title : `${e.title.substring(0, s)}...`, d = {
14
- [o.TEL]: ["call"],
15
- [o.SIP]: ["call"],
16
- [o.MAILTO]: ["mail"],
17
- [o.SMS]: ["message"],
18
- [o.WHATSAPP]: ["message"],
19
- [o.TELEGRAM]: ["message"],
20
- [o.VIBER]: ["message"],
21
- [o.TEAMS]: ["call", "message", "video"],
22
- [o.FAX]: ["message"]
23
- }, u = {
24
- call: "transport",
25
- video: "transport",
26
- message: "transport",
27
- mail: "compose"
28
- }, p = ["call", "mail", "message", "video"];
29
- function a(e, s) {
30
- if (!e.supportedSchemes.includes(s)) return [];
31
- const n = e.capabilities.filter(
32
- (t) => t.scheme === s && t.value === !0 && p.includes(t.key)
33
- );
34
- return n.length > 0 ? n.map((t) => t.key) : d[s] ?? [];
9
+ })[t.priority] || 0, I = (t, n = 30) => t.title?.length <= n ? t.title : `${t.title.substring(0, n)}...`;
10
+ function f(t, n) {
11
+ const r = new Set(t.disabledIntents ?? []), i = t.intentOverrides ?? {}, e = n.intents.filter((s) => !r.has(s.intent)).map((s) => l(s, i[s.intent]));
12
+ if (!t.extraIntents || t.extraIntents.length === 0) return e;
13
+ const o = new Set(e.map((s) => s.intent));
14
+ for (const s of t.extraIntents)
15
+ o.has(s.intent) || (e.push(s), o.add(s.intent));
16
+ return e;
35
17
  }
36
- function f(e) {
37
- const s = /* @__PURE__ */ new Set();
38
- for (const n of e.supportedSchemes)
39
- for (const t of a(e, n)) s.add(t);
40
- return Array.from(s);
18
+ function l(t, n) {
19
+ return n ? {
20
+ intent: n.intent ?? t.intent,
21
+ targetSchemes: n.targetSchemes ?? t.targetSchemes,
22
+ transport: n.transport ?? t.transport
23
+ } : t;
41
24
  }
42
- function h(e, s, n) {
43
- return e.capabilities.find(
44
- (r) => r.scheme === s && r.key === n && r.value === !0
45
- )?.dispatch ?? u[n];
46
- }
47
- function I(e, s) {
48
- return Object.entries(e).map(([n, t]) => ({
49
- providerId: n,
50
- description: t,
51
- intents: f(t)
52
- }));
53
- }
54
- function v(e, s) {
55
- const n = [];
56
- for (const t of e) {
57
- if (!t.enabled) continue;
58
- const r = s[t.providerId];
59
- if (r) {
60
- if (t.capabilities && t.capabilities.length > 0) {
61
- for (const i of t.capabilities) {
62
- if (!i.value || !p.includes(i.key)) continue;
63
- const c = i.scheme, l = i.key;
64
- n.push({
65
- channel: t,
66
- description: r,
67
- scheme: c,
68
- intent: l,
69
- dispatch: i.dispatch ?? u[l]
70
- });
71
- }
72
- continue;
73
- }
74
- for (const i of r.supportedSchemes) {
75
- const c = a(r, i);
76
- for (const l of c)
77
- n.push({
78
- channel: t,
79
- description: r,
80
- scheme: i,
81
- intent: l,
82
- dispatch: h(r, i, l)
83
- });
84
- }
85
- }
25
+ function b(t, n) {
26
+ const r = [];
27
+ for (const i of t) {
28
+ if (!i.enabled) continue;
29
+ const e = n[i.providerId];
30
+ if (e)
31
+ for (const o of f(i, e))
32
+ r.push({ channel: i, description: e, capability: o });
86
33
  }
87
- return n;
34
+ return r;
88
35
  }
89
- function y(e, s) {
90
- return s.filter((n) => n.intents.includes(e));
36
+ function y(t, n) {
37
+ return n.filter((r) => r.capability.intent === t);
91
38
  }
92
- function M(e, s) {
93
- return s.filter((n) => n.description.supportedSchemes.includes(e.scheme));
39
+ function a(t, n) {
40
+ return n.filter((r) => r.capability.targetSchemes.includes(t));
94
41
  }
95
- function S(e, s, n) {
96
- const t = [];
97
- for (const r of e)
98
- for (const i of n)
99
- i.description.supportedSchemes.includes(r.scheme) && a(i.description, r.scheme).includes(s) && t.push({ capability: i, endpoint: r });
100
- return t;
42
+ function E(t, n) {
43
+ return a(t.scheme, n);
101
44
  }
102
- function F(e, s, n) {
103
- const t = [];
104
- for (const r of e)
105
- for (const i of n)
106
- i.scheme === r.scheme && i.intent === s && t.push({ capability: i, endpoint: r });
107
- return t;
45
+ function T(t, n, r) {
46
+ const i = [];
47
+ for (const e of t)
48
+ for (const o of r)
49
+ o.capability.intent === n && o.capability.targetSchemes.includes(e.scheme) && i.push({ channelIntent: o, endpoint: e });
50
+ return i;
108
51
  }
52
+ var u = /* @__PURE__ */ ((t) => (t.MAILTO = "mailto", t.SIP = "sip", t.TEL = "tel", t.WEBHOOK = "webhook", t.USERNAME = "username", t.ID = "id", t.CUSTOM = "custom", t.URL = "url", t.TELEGRAM = "telegram", t.WHATSAPP = "whatsapp", t.VIBER = "viber", t.SMS = "sms", t.FAX = "fax", t.TEAMS = "teams", t))(u || {});
109
53
  export {
110
- o as CommunicationScheme,
111
- v as buildChannelCapabilities,
112
- I as buildResolvedCapabilities,
113
- h as dispatchForCapability,
114
- g as getContactEndpoints,
115
- A as getShortTitle,
116
- b as getUrgencyScore,
117
- f as intentsForProvider,
118
- a as intentsForScheme,
119
- T as isAssigned,
120
- E as isClosed,
121
- F as resolveContactChannelCapabilities,
122
- S as resolveForContact,
123
- M as resolveForEndpoint,
124
- y as resolveForIntent
54
+ u as CommunicationScheme,
55
+ b as buildChannelIntents,
56
+ E as filterByEndpoint,
57
+ y as filterByIntent,
58
+ a as filterByTargetScheme,
59
+ c as getContactEndpoints,
60
+ I as getShortTitle,
61
+ g as getUrgencyScore,
62
+ d as isAssigned,
63
+ p as isClosed,
64
+ T as matchContactToIntents,
65
+ f as resolveChannelIntents
125
66
  };
@@ -1,60 +1,48 @@
1
1
  import { Channel } from '../entities/channel/types';
2
2
  import { ContactEndpoint } from '../entities/contact/types';
3
- import { CommsAccount, CommunicationDispatch, CommunicationScheme, ProviderDescription, Uri } from './communication';
3
+ import { CommunicationScheme, IntentCapability, ProviderDescription, Uri } from './communication';
4
+ /**
5
+ * Reference list of intents that have built-in UI defaults (icons, labels,
6
+ * default composers). Intent itself is an open string — providers may
7
+ * declare anything. Unknown intents render as generic actions.
8
+ */
4
9
  export type CommunicationIntent = "call" | "mail" | "message" | "video";
5
- export interface ResolvedCapability {
6
- providerId: string;
7
- description: ProviderDescription;
8
- intents: CommunicationIntent[];
9
- }
10
- export interface ChannelCapability {
10
+ /**
11
+ * A channel's effective intent — the resolved IntentCapability for this
12
+ * specific channel, ready for routing/UI decisions.
13
+ */
14
+ export interface ChannelIntent {
11
15
  channel: Channel;
12
16
  description: ProviderDescription;
13
- scheme: CommunicationScheme;
14
- intent: CommunicationIntent;
15
- dispatch: CommunicationDispatch;
17
+ capability: IntentCapability;
16
18
  }
17
- export interface ResolvedCapabilityMatch {
18
- capability: ResolvedCapability;
19
+ export interface ChannelIntentMatch {
20
+ channelIntent: ChannelIntent;
19
21
  endpoint: ContactEndpoint;
20
22
  }
21
- export interface ChannelCapabilityMatch {
22
- capability: ChannelCapability;
23
- endpoint: ContactEndpoint;
24
- }
25
- /**
26
- * Returns which intents a provider offers for a given scheme.
27
- * Capability rows whose `key` is a known user-intent (`call|mail|message|video`)
28
- * are treated as explicit declarations; anything else is ignored. Empty
29
- * declared list falls back to a conventional default per scheme.
30
- */
31
- export declare function intentsForScheme(description: ProviderDescription, scheme: CommunicationScheme): CommunicationIntent[];
32
- export declare function intentsForProvider(description: ProviderDescription): CommunicationIntent[];
33
23
  /**
34
- * Returns how a given (scheme, intent) on this provider should be dispatched.
35
- * Honours an explicit `dispatch` on the capability row; otherwise falls back
36
- * to the intent default (mail=compose, rest=transport).
24
+ * Pure resolution of a channel's effective intents.
25
+ *
26
+ * Algorithm:
27
+ * 1. Start from `description.intents` (provider defaults)
28
+ * 2. Drop entries whose `intent` is in `channel.disabledIntents`
29
+ * 3. Shallow-merge `channel.intentOverrides[intent]` over the provider default
30
+ * 4. Append `channel.extraIntents` (deduped on `intent`)
31
+ *
32
+ * No fallback heuristics, no scheme-based defaults — provider declares the
33
+ * source of truth, channel patches it.
37
34
  */
38
- export declare function dispatchForCapability(description: ProviderDescription, scheme: CommunicationScheme, intent: CommunicationIntent): CommunicationDispatch;
39
- export declare function buildResolvedCapabilities(descriptions: Record<string, ProviderDescription>, _accounts?: CommsAccount[]): ResolvedCapability[];
35
+ export declare function resolveChannelIntents(channel: Channel, description: ProviderDescription): IntentCapability[];
40
36
  /**
41
- * Builds (channel × scheme × intent) capabilities.
42
- *
43
- * Precedence:
44
- * 1. `channel.capabilities` when present — the precise truth for *this*
45
- * specific channel (e.g. one Microsoft account exposes only Teams,
46
- * another only mailto). Each capability row maps directly to one
47
- * ChannelCapability.
48
- * 2. Fallback: provider-level `description.capabilities` × `supportedSchemes`
49
- * — coarse default for backends that don't yet declare per-channel.
37
+ * Builds the full set of (channel × intent) entries across all channels —
38
+ * the source for the starter palette.
50
39
  */
51
- export declare function buildChannelCapabilities(channels: Channel[], descriptions: Record<string, ProviderDescription>): ChannelCapability[];
52
- export declare function resolveForIntent(intent: CommunicationIntent, capabilities: ResolvedCapability[]): ResolvedCapability[];
53
- export declare function resolveForEndpoint(uri: Uri, capabilities: ResolvedCapability[]): ResolvedCapability[];
54
- export declare function resolveForContact(endpoints: ContactEndpoint[], intent: CommunicationIntent, capabilities: ResolvedCapability[]): ResolvedCapabilityMatch[];
40
+ export declare function buildChannelIntents(channels: Channel[], descriptions: Record<string, ProviderDescription>): ChannelIntent[];
41
+ export declare function filterByIntent(intent: string, items: ChannelIntent[]): ChannelIntent[];
42
+ export declare function filterByTargetScheme(scheme: CommunicationScheme, items: ChannelIntent[]): ChannelIntent[];
43
+ export declare function filterByEndpoint(uri: Uri, items: ChannelIntent[]): ChannelIntent[];
55
44
  /**
56
- * Channel-aware variant: matches contact endpoints against (channel × intent)
57
- * capabilities. Each match identifies *exactly* which channel will send and
58
- * how. Drives both contact-detail buttons and contact-named palette rows.
45
+ * Cross-product of contact endpoints × channel intents for one specific
46
+ * intent. One match = one row in the starter palette / contact buttons.
59
47
  */
60
- export declare function resolveContactChannelCapabilities(endpoints: ContactEndpoint[], intent: CommunicationIntent, capabilities: ChannelCapability[]): ChannelCapabilityMatch[];
48
+ export declare function matchContactToIntents(endpoints: ContactEndpoint[], intent: string, items: ChannelIntent[]): ChannelIntentMatch[];
@@ -41,18 +41,6 @@ export interface ITransportProvider {
41
41
  handleAction(sessionId: string, actionId: string, ...args: any[]): void;
42
42
  }
43
43
  export type CommunicationDispatch = "transport" | "compose";
44
- export interface Capability {
45
- scheme: string;
46
- key: string;
47
- value: boolean;
48
- /**
49
- * How this (scheme,intent) is delivered. `transport` goes through
50
- * ITransportProvider.createSession (live SIP/Teams call). `compose`
51
- * goes through the server-side compose endpoint with a channelId
52
- * (mail, async messaging). Defaults to intent: mail=compose, rest=transport.
53
- */
54
- dispatch?: CommunicationDispatch;
55
- }
56
44
  export declare enum CommunicationScheme {
57
45
  MAILTO = "mailto",
58
46
  SIP = "sip",
@@ -69,11 +57,63 @@ export declare enum CommunicationScheme {
69
57
  FAX = "fax",
70
58
  TEAMS = "teams"
71
59
  }
60
+ /**
61
+ * Account selector for `transport: session` intents. Tells the frontend
62
+ * which `CommsAccount` should service this intent on this channel.
63
+ * - `protocol` only: pick the first registered account with this protocol
64
+ * (acceptable when there is one UA per provider per tenant)
65
+ * - `accountId` set: pin to a specific account (multi-trunk / multi-tenant)
66
+ */
67
+ export interface AccountSelector {
68
+ protocol: string;
69
+ accountId?: string;
70
+ }
71
+ export type TransportConfig = {
72
+ kind: "compose";
73
+ /** Optional federated composer resource as "<app>:<resource>". */
74
+ composer?: string;
75
+ /**
76
+ * When true, comms-app `interaction/compose` skips its auto-create
77
+ * and lets the provider own interaction lifecycle (e.g. Teams chat
78
+ * upserts on a chatId externalId).
79
+ */
80
+ ownsInteraction?: boolean;
81
+ } | {
82
+ kind: "session";
83
+ accountSelector: AccountSelector;
84
+ ui: {
85
+ canvas: `${string}:${string}`;
86
+ actionTray?: `${string}:${string}`;
87
+ };
88
+ };
89
+ /**
90
+ * One user-facing intent a provider/channel can perform on a target.
91
+ * `intent` is an open string — known values (call/mail/message/video) get
92
+ * default UI/labels; new ones (`schedule`, `transfer`, `record`) render as
93
+ * generic actions until a provider declares specialised UI.
94
+ */
95
+ export interface IntentCapability {
96
+ intent: string;
97
+ targetSchemes: CommunicationScheme[];
98
+ transport: TransportConfig;
99
+ }
100
+ /**
101
+ * Provider-level feature flag (no routing relevance). Used for things
102
+ * like `call_hold`, `call_forward`, `message_reactions` that providers
103
+ * advertise to feature-detect, but which are not user-pickable intents.
104
+ */
105
+ export interface ProviderFeature {
106
+ name: string;
107
+ scheme?: CommunicationScheme;
108
+ value: boolean;
109
+ }
72
110
  export interface ProviderDescription {
73
111
  id?: string;
74
112
  displayName: string;
75
- capabilities: Capability[];
76
- supportedSchemes: CommunicationScheme[];
113
+ /** User-intents this provider offers by default. Channels inherit. */
114
+ intents: IntentCapability[];
115
+ /** Provider feature flags — separate from routing. */
116
+ features: ProviderFeature[];
77
117
  }
78
118
  export interface PersonalAuthOption {
79
119
  id: string;
@@ -16,6 +16,18 @@ export interface ComposePayload {
16
16
  channelUri?: Uri;
17
17
  recipientUri: Uri;
18
18
  subject?: string;
19
+ /**
20
+ * The user-intent picked in the starter / contact buttons. Lets a
21
+ * provider compose-handler dispatch on intent — e.g. a Microsoft
22
+ * mailbox with intent `message` routes to Teams chat, intent `mail`
23
+ * routes to Outlook send-mail. Open string for forward-compat.
24
+ */
25
+ intent?: string;
26
+ /**
27
+ * Channel-specific extras — template_id, attachments, location, etc.
28
+ * Provider compose-handlers may read this; defaults ignore it.
29
+ */
30
+ extra?: Record<string, any>;
19
31
  }
20
32
  export interface ComposeResponse {
21
33
  messageId: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opencxh/domain",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",