@opencxh/domain 1.221.0 → 1.224.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/activity/catalog.d.ts +17 -0
- package/dist/entities/activity/resolve.d.ts +2 -0
- package/dist/entities/activity/types.d.ts +17 -0
- package/dist/entities/assignment/types.d.ts +42 -4
- package/dist/entities/reaction/index.d.ts +1 -0
- package/dist/entities/reaction/summarize.test.d.ts +1 -0
- package/dist/entities/reaction/types.d.ts +98 -0
- package/dist/entities/work/activity.d.ts +9 -0
- package/dist/entities/work/keys.d.ts +9 -0
- package/dist/index.cjs +14 -14
- package/dist/index.d.ts +1 -0
- package/dist/index.js +878 -806
- package/dist/platform/communication.d.ts +25 -0
- package/dist/platform/documents.d.ts +9 -0
- package/dist/platform/permission.d.ts +1 -1
- package/dist/platform/resource-source.d.ts +24 -0
- package/package.json +1 -1
|
@@ -99,6 +99,23 @@ export declare function channelKindOf(type: string): ActivityChannelKind | undef
|
|
|
99
99
|
export declare function isMessageShape(shape: ActivityShape | undefined): boolean;
|
|
100
100
|
/** See {@link isMessageShape}. For the built-in types. */
|
|
101
101
|
export declare function isMessageType(type: string): boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Does an answer of this kind fold into the list under the message it answers, instead of
|
|
104
|
+
* taking its own place in the timeline?
|
|
105
|
+
*
|
|
106
|
+
* Only notes. A mail carrying a `parentId` is still a message that was really sent, at a real
|
|
107
|
+
* moment, and burying it under the thing it answered would take it out of the chronology the
|
|
108
|
+
* rest of the conversation is read in — and out of view entirely while the thread is folded.
|
|
109
|
+
* An internal note has no such life of its own: it exists *about* that message.
|
|
110
|
+
*
|
|
111
|
+
* Keyed on the shape, so the rule has exactly one definition and an app-declared note kind
|
|
112
|
+
* behaves the same — see {@link ActivityTypeRegistry.nestsUnderParent}. `parentId` itself stays
|
|
113
|
+
* set on every reply regardless: it is a true fact about the row, and mail threading, the
|
|
114
|
+
* provider `In-Reply-To` and the reply notification all read it.
|
|
115
|
+
*/
|
|
116
|
+
export declare function isNoteShape(shape: ActivityShape | undefined): boolean;
|
|
117
|
+
/** See {@link isNoteShape}. For the built-in types. */
|
|
118
|
+
export declare function nestsUnderParent(type: string): boolean;
|
|
102
119
|
/** Can a human reply to it? Not to notes — those go nowhere. */
|
|
103
120
|
export declare function isReplyableType(type: string): boolean;
|
|
104
121
|
/**
|
|
@@ -70,6 +70,8 @@ export declare class ActivityTypeRegistry {
|
|
|
70
70
|
* {@link ResolvedActivityType}.
|
|
71
71
|
*/
|
|
72
72
|
isMessage(type: string): boolean;
|
|
73
|
+
/** Folds into the list under the message it answers. See {@link isNoteShape}. */
|
|
74
|
+
nestsUnderParent(type: string): boolean;
|
|
73
75
|
/** Can a person reply to this? Notes cannot — those go nowhere. */
|
|
74
76
|
isReplyable(type: string): boolean;
|
|
75
77
|
/** Readable content for a model: messages, notes and transcripts. */
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ActingIdentity } from '../../platform/identity';
|
|
2
2
|
import { TranscriptSegment } from '../../platform/media';
|
|
3
3
|
import { AIMessageInput, AIMessageOutput } from '../ai-message/types';
|
|
4
|
+
import { ReactionSummary } from '../reaction/types';
|
|
4
5
|
export type CallStatus = "new" | "connecting" | "ringing" | "connected" | "held" | "ended" | "failed";
|
|
5
6
|
export type CallDirection = "inbound" | "outbound";
|
|
6
7
|
export type CallType = "audio" | "video" | "data" | "screen-share";
|
|
@@ -34,6 +35,14 @@ export interface BaseActivity {
|
|
|
34
35
|
contactId?: string;
|
|
35
36
|
};
|
|
36
37
|
direction: "inbound" | "outbound" | "internal" | "none";
|
|
38
|
+
/**
|
|
39
|
+
* The activity this one answers, if any — the in-app thread, one level deep.
|
|
40
|
+
*
|
|
41
|
+
* Not `EmailPayload.threadId`: that is the provider's own conversation id and groups a whole
|
|
42
|
+
* mail thread. This points at one activity, and an internal note can hang off a customer's
|
|
43
|
+
* mail with it. A reply to a reply stores the *top* parent, so the feed never nests twice.
|
|
44
|
+
*/
|
|
45
|
+
parentId?: string;
|
|
37
46
|
externalIds?: string[];
|
|
38
47
|
attachments?: Attachment[];
|
|
39
48
|
/**
|
|
@@ -42,6 +51,14 @@ export interface BaseActivity {
|
|
|
42
51
|
* indicator while true.
|
|
43
52
|
*/
|
|
44
53
|
pending?: boolean;
|
|
54
|
+
/**
|
|
55
|
+
* The emoji on this message, as the list and get routes project them.
|
|
56
|
+
*
|
|
57
|
+
* Read-only and **never stored on the activity row**: reactions are their own rows, keyed
|
|
58
|
+
* by (resource, actor, emoji), so two people reacting at once cannot race on one column.
|
|
59
|
+
* The server joins them on in one bulk query per page; a writer that sets this is ignored.
|
|
60
|
+
*/
|
|
61
|
+
reactions?: ReactionSummary[];
|
|
45
62
|
/** Epoch ms — matches the DB model and Interaction.createdAt. */
|
|
46
63
|
createdAt?: number;
|
|
47
64
|
updatedAt?: number;
|
|
@@ -128,17 +128,55 @@ export interface Assignment {
|
|
|
128
128
|
* signal it wakes on — a loop that stops only at the turn ceiling.
|
|
129
129
|
* 3. **The agent is mentioned**: wakes, whatever it is waiting on. A mention is the explicit answer
|
|
130
130
|
* to the question the engine cannot answer itself: *is this addressed to my agent?*
|
|
131
|
-
* 4. **An
|
|
131
|
+
* 4. **An answer in the agent's own thread**: wakes, whatever it is waiting on. A note hanging
|
|
132
|
+
* under the message the agent itself wrote is as addressed to it as its name would be — more
|
|
133
|
+
* so, really: you had to aim at that message to write it. This is the case the mention rule
|
|
134
|
+
* was standing in for before threads existed, and it deliberately widens the earlier choice
|
|
135
|
+
* that a mention stays required while waiting on a task. Only for its *own* thread: a reply
|
|
136
|
+
* under a colleague's note is two people talking, and still costs no turn.
|
|
137
|
+
* 5. **An internal note while it waits for a reply** (`waitingOn === "reply"`): wakes. If an agent
|
|
132
138
|
* explicitly asked for an answer, the next thing a colleague writes *is* that answer; forcing
|
|
133
139
|
* someone to @-mention their own agent on the question it just asked is a ritual, not a signal.
|
|
134
140
|
* The worry it replaces ("two colleagues conferring cost a turn each") stays covered by the
|
|
135
|
-
* edge: `waitingOn` has to be `"reply"`, and once it waits on a task or is still busy, a
|
|
136
|
-
* is again the only way in.
|
|
141
|
+
* edge: `waitingOn` has to be `"reply"`, and once it waits on a task or is still busy, a
|
|
142
|
+
* mention or its own thread is again the only way in.
|
|
137
143
|
*
|
|
138
144
|
* @param waitingOn What the assignment waits on. Omitted = rules 1-3 only, so a caller that does
|
|
139
145
|
* not know never wakes too eagerly.
|
|
146
|
+
* @param parentAuthorId Who wrote the activity this one answers, when it answers one. Resolved by
|
|
147
|
+
* the caller: the activity carries only `parentId`, and this function stays pure.
|
|
140
148
|
*/
|
|
141
|
-
export declare function wakesAssignment(activity: FeedActivity, agentId: string, waitingOn?: WaitingOn["on"]): boolean;
|
|
149
|
+
export declare function wakesAssignment(activity: FeedActivity, agentId: string, waitingOn?: WaitingOn["on"], parentAuthorId?: string): boolean;
|
|
150
|
+
/**
|
|
151
|
+
* Does this reaction wake an assignment?
|
|
152
|
+
*
|
|
153
|
+
* A reaction is the cheapest possible answer: the agent asks "shall I send this?" and you put
|
|
154
|
+
* a ✅ on it without typing a word. That is worth having, because the alternative — typing
|
|
155
|
+
* "ja" and @-mentioning your own agent — is the friction that makes people not answer at all.
|
|
156
|
+
*
|
|
157
|
+
* **Its own function, not a rule inside {@link wakesAssignment}.** A reaction is not a
|
|
158
|
+
* `FeedActivity` and never becomes one: activities are real rows on a timeline, and
|
|
159
|
+
* manufacturing a fake one to reuse a signature would put a lie in the feed's own vocabulary.
|
|
160
|
+
*
|
|
161
|
+
* **Three gates, all narrow.** It must be a reaction that was *put there* (taking one away
|
|
162
|
+
* answers nothing); it must sit on a message this agent itself wrote (that is what makes it
|
|
163
|
+
* addressed — a reaction on a colleague's note is between colleagues); and the reactor must
|
|
164
|
+
* not be the agent. `waitingOn` does not come into it: the same standing as a mention, because
|
|
165
|
+
* the aiming is just as explicit.
|
|
166
|
+
*
|
|
167
|
+
* Which emoji it is, deliberately does not matter here. There is no 👍-means-yes table: the
|
|
168
|
+
* turn is told which emoji and from whom, and reading that is what a model is for. A fixed
|
|
169
|
+
* set would be a configuration that differs per team and grows by commit.
|
|
170
|
+
*/
|
|
171
|
+
export declare function reactionWakesAssignment(input: {
|
|
172
|
+
/** The author of the activity that was reacted on. Only a `user` author can be an agent. */
|
|
173
|
+
targetAuthorId?: string;
|
|
174
|
+
/** Who reacted. */
|
|
175
|
+
reactorId?: string;
|
|
176
|
+
agentId: string;
|
|
177
|
+
/** `true` put it there, `false` took it away. */
|
|
178
|
+
on: boolean;
|
|
179
|
+
}): boolean;
|
|
142
180
|
/** The subject of an assignment as one reference, or `undefined`. */
|
|
143
181
|
export declare function assignmentSubject(assignment: {
|
|
144
182
|
subjectKind?: string;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './types';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who reacted.
|
|
3
|
+
*
|
|
4
|
+
* Not a bare `userId`, and not `MutationAuthor`: a reaction can come from outside the
|
|
5
|
+
* organisation. A Teams chat is synced with the customer in it, and their 👍 has to land
|
|
6
|
+
* somewhere — as a `contact`, with the name the provider gave. Mirrors `BaseActivity.author`,
|
|
7
|
+
* which already carries that same four-way distinction.
|
|
8
|
+
*
|
|
9
|
+
* `id` is the user id, the app name, or the provider's id for a contact; absent for `system`.
|
|
10
|
+
*/
|
|
11
|
+
export interface ReactionActor {
|
|
12
|
+
type: "user" | "contact" | "app" | "system";
|
|
13
|
+
id?: string;
|
|
14
|
+
/** Shown on hover. Kept on the row because an external reactor has no record to look up. */
|
|
15
|
+
name?: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* One emoji, put on one resource, by one actor.
|
|
19
|
+
*
|
|
20
|
+
* Addressed by (resourceType, resourceId) — the same `ResourceRef` shape read-state and the
|
|
21
|
+
* attribute store use — so an activity, a work comment, a file or a knowledge article all
|
|
22
|
+
* reuse this without the store learning what any of them are.
|
|
23
|
+
*
|
|
24
|
+
* A row per (resource, actor, emoji), never an array on the resource: two people reacting at
|
|
25
|
+
* the same moment would otherwise race on one column, and the unique index makes both the
|
|
26
|
+
* toggle and a re-sync from a provider idempotent.
|
|
27
|
+
*/
|
|
28
|
+
export interface Reaction {
|
|
29
|
+
id: string;
|
|
30
|
+
organizationId: string;
|
|
31
|
+
resourceType: string;
|
|
32
|
+
resourceId: string;
|
|
33
|
+
actor: ReactionActor;
|
|
34
|
+
/**
|
|
35
|
+
* The literal character, e.g. `"👍"` — never a shortcode and never a provider's name for it.
|
|
36
|
+
* One vocabulary means no mapping table in the core; the six legacy Teams names are
|
|
37
|
+
* translated at the edge, in the app that speaks to Graph.
|
|
38
|
+
*/
|
|
39
|
+
emoji: string;
|
|
40
|
+
createdAt?: number;
|
|
41
|
+
}
|
|
42
|
+
/** One chip: an emoji, how many put it there, and who — for the hover. */
|
|
43
|
+
export interface ReactionSummary {
|
|
44
|
+
emoji: string;
|
|
45
|
+
count: number;
|
|
46
|
+
actors: ReactionActor[];
|
|
47
|
+
}
|
|
48
|
+
/** `POST /reaction/:type/:id` body. `on` absent means "flip whatever it is now". */
|
|
49
|
+
export interface ToggleReactionRequest {
|
|
50
|
+
emoji: string;
|
|
51
|
+
on?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Who to record it for, instead of the caller.
|
|
54
|
+
*
|
|
55
|
+
* **Honoured for an app-to-app caller only**, and ignored outright for a user — otherwise
|
|
56
|
+
* anyone could put an emoji on a message in a colleague's name. It exists for provider
|
|
57
|
+
* sync: a reaction pulled out of Teams belongs to the person who left it there, not to the
|
|
58
|
+
* app that carried it across.
|
|
59
|
+
*/
|
|
60
|
+
actor?: ReactionActor;
|
|
61
|
+
}
|
|
62
|
+
/** Reactions per resource id, as the list endpoints hand them to a feed. */
|
|
63
|
+
export type ReactionsByResource = Record<string, ReactionSummary[]>;
|
|
64
|
+
/** The one thing that just happened, as opposed to the state that resulted from it. */
|
|
65
|
+
export interface ReactionChange {
|
|
66
|
+
emoji: string;
|
|
67
|
+
actor: ReactionActor;
|
|
68
|
+
/** `true` put it there, `false` took it away. */
|
|
69
|
+
on: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* The `reactions:changed` event, as apps receive it (`communication:reactions:changed`).
|
|
73
|
+
*
|
|
74
|
+
* Carries **both** the resulting chip set and the delta, because the two consumers need
|
|
75
|
+
* different things. A feed re-renders from `reactions` — a client that only got deltas and
|
|
76
|
+
* missed one would keep a wrong count forever. A server-side listener needs `change`: "who
|
|
77
|
+
* just did what" cannot be derived from a state it never saw the previous version of.
|
|
78
|
+
*
|
|
79
|
+
* `organizationId` rides along because an event handler has no request: every other
|
|
80
|
+
* subscriber in this platform reads the tenant from the payload and gives up without it.
|
|
81
|
+
*/
|
|
82
|
+
export interface ReactionsChangedEvent {
|
|
83
|
+
organizationId: string;
|
|
84
|
+
resourceType: string;
|
|
85
|
+
resourceId: string;
|
|
86
|
+
reactions: ReactionSummary[];
|
|
87
|
+
change: ReactionChange;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Rows to chips.
|
|
91
|
+
*
|
|
92
|
+
* Most-used first so the chip that carries the most agreement sits nearest the message, with
|
|
93
|
+
* ties broken by which emoji appeared first — a stable order, so a chip does not jump when
|
|
94
|
+
* someone else reacts. Pure: this is the one piece of the feature worth a test.
|
|
95
|
+
*/
|
|
96
|
+
export declare function summarizeReactions(rows: Reaction[]): ReactionSummary[];
|
|
97
|
+
/** Did this user put this emoji there? The chip's own state, asked without a second query. */
|
|
98
|
+
export declare function hasReacted(summary: ReactionSummary, userId: string | undefined): boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ReactionSummary } from '../reaction/types';
|
|
1
2
|
import { WorkStatusCategory } from './types';
|
|
2
3
|
/**
|
|
3
4
|
* The activity feed under a work item.
|
|
@@ -36,6 +37,8 @@ export interface WorkActivity {
|
|
|
36
37
|
* "the customer answered" and would wake every waiting assignment on the item.
|
|
37
38
|
*/
|
|
38
39
|
direction: "internal";
|
|
40
|
+
/** The activity this one answers — same one-level thread as comms' `BaseActivity.parentId`. */
|
|
41
|
+
parentId?: string;
|
|
39
42
|
/** `html` is the composer's markup for a comment; `text` stays the plain reading of it. */
|
|
40
43
|
payload: {
|
|
41
44
|
text?: string;
|
|
@@ -46,6 +49,12 @@ export interface WorkActivity {
|
|
|
46
49
|
toStatus?: string;
|
|
47
50
|
toCategory?: WorkStatusCategory;
|
|
48
51
|
};
|
|
52
|
+
/**
|
|
53
|
+
* The emoji on this comment, as the list route projects them. Read-only and never stored
|
|
54
|
+
* on the row — see `BaseActivity.reactions`, which this mirrors, and the generic store in
|
|
55
|
+
* the communication app that both read from.
|
|
56
|
+
*/
|
|
57
|
+
reactions?: ReactionSummary[];
|
|
49
58
|
createdAt?: number;
|
|
50
59
|
updatedAt?: number;
|
|
51
60
|
}
|
|
@@ -10,8 +10,17 @@ import { WorkItem, WorkProject } from './types';
|
|
|
10
10
|
/** The scope kinds `apps/work` claims through `/provider/scope/describe`. */
|
|
11
11
|
export declare const WORK_ITEM_SCOPE_KIND = "work_item";
|
|
12
12
|
export declare const WORK_PROJECT_SCOPE_KIND = "work_project";
|
|
13
|
+
/**
|
|
14
|
+
* A row in an item's feed, addressable on its own.
|
|
15
|
+
*
|
|
16
|
+
* Claimed so the generic stores can hang something off a single comment — reactions today,
|
|
17
|
+
* whatever is resource-agnostic tomorrow. It inherits its item's access exactly the way
|
|
18
|
+
* comms' `activity:` inherits its conversation's: a comment is not a second boundary.
|
|
19
|
+
*/
|
|
20
|
+
export declare const WORK_ACTIVITY_SCOPE_KIND = "work_activity";
|
|
13
21
|
export declare function workItemScopeKey(itemId: string): string;
|
|
14
22
|
export declare function workProjectScopeKey(projectId: string): string;
|
|
23
|
+
export declare function workActivityScopeKey(activityId: string): string;
|
|
15
24
|
/**
|
|
16
25
|
* The human key: `SAL-142`, or `SAL-142-1` for a subitem.
|
|
17
26
|
*
|