@opencxh/domain 1.172.1 → 1.173.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/blocks.d.ts +30 -30
- package/dist/entities/activity/catalog.d.ts +63 -86
- package/dist/entities/activity/descriptor.d.ts +31 -32
- package/dist/entities/activity/index.d.ts +2 -2
- package/dist/entities/activity/preview.d.ts +7 -7
- package/dist/entities/activity/resolve.d.ts +37 -38
- package/dist/entities/activity/types.d.ts +64 -35
- package/dist/entities/ai-account/types.d.ts +3 -3
- package/dist/entities/ai-account/vendors.d.ts +25 -32
- package/dist/entities/ai-budget/types.d.ts +5 -5
- package/dist/entities/ai-conversation/types.d.ts +12 -12
- package/dist/entities/ai-profile/types.d.ts +14 -21
- package/dist/entities/ai-settings/types.d.ts +12 -13
- package/dist/entities/analytics/index.d.ts +4 -4
- package/dist/entities/analytics/source.d.ts +1 -1
- package/dist/entities/analytics/usage.d.ts +23 -28
- package/dist/entities/artifact/blocks.d.ts +54 -69
- package/dist/entities/artifact/markdown.d.ts +2 -3
- package/dist/entities/artifact/types.d.ts +46 -54
- package/dist/entities/assignment/types.d.ts +76 -90
- package/dist/entities/channel/index.d.ts +1 -1
- package/dist/entities/channel/signature.d.ts +8 -9
- package/dist/entities/communication/index.d.ts +1 -1
- package/dist/entities/contact/index.d.ts +1 -1
- package/dist/entities/custom-field-def/types.d.ts +14 -14
- package/dist/entities/external-identity/types.d.ts +45 -50
- package/dist/entities/interaction/index.d.ts +1 -1
- package/dist/entities/interaction/types.d.ts +44 -47
- package/dist/entities/live-lens/types.d.ts +39 -44
- package/dist/entities/mcp/types.d.ts +53 -57
- package/dist/entities/memory/alias.d.ts +7 -7
- package/dist/entities/memory/browse.d.ts +21 -21
- package/dist/entities/memory/ingest.d.ts +24 -28
- package/dist/entities/memory/item.d.ts +53 -58
- package/dist/entities/memory/kind.d.ts +25 -25
- package/dist/entities/memory/query.d.ts +49 -51
- package/dist/entities/playbook/actor.d.ts +17 -17
- package/dist/entities/playbook/assignment.d.ts +54 -63
- package/dist/entities/playbook/index.d.ts +2 -2
- package/dist/entities/playbook/labels.d.ts +4 -4
- package/dist/entities/playbook/trigger-vars.d.ts +17 -15
- package/dist/entities/playbook/types.d.ts +174 -303
- package/dist/entities/resource-reminder/types.d.ts +8 -9
- package/dist/entities/time-entry/duration.d.ts +11 -11
- package/dist/entities/time-entry/types.d.ts +59 -59
- package/dist/entities/time-entry/work-type.d.ts +21 -24
- package/dist/entities/topic/examples.d.ts +26 -27
- package/dist/entities/topic/scope.d.ts +11 -12
- package/dist/entities/topic/types.d.ts +26 -28
- package/dist/entities/user/types.d.ts +24 -28
- package/dist/entities/webhook/types.d.ts +6 -6
- package/dist/entities/work/activity.d.ts +36 -0
- package/dist/entities/work/index.d.ts +1 -0
- package/dist/entities/work/keys.d.ts +25 -26
- package/dist/entities/work/ladder.d.ts +48 -52
- package/dist/entities/work/types.d.ts +108 -114
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +29 -28
- package/dist/index.js +1416 -1258
- package/dist/platform/account.d.ts +34 -38
- package/dist/platform/ai-tools.d.ts +48 -52
- package/dist/platform/api.d.ts +9 -10
- package/dist/platform/author.d.ts +7 -9
- package/dist/platform/capabilities.d.ts +3 -3
- package/dist/platform/communication.d.ts +163 -204
- package/dist/platform/context.d.ts +8 -8
- package/dist/platform/identity.d.ts +16 -19
- package/dist/platform/kernel.d.ts +1 -1
- package/dist/platform/manifest.d.ts +2 -2
- package/dist/platform/media.d.ts +13 -15
- package/dist/platform/permission.d.ts +39 -0
- package/dist/platform/permission.test.d.ts +1 -0
- package/dist/platform/presence.d.ts +30 -30
- package/dist/platform/provider.d.ts +14 -15
- package/dist/platform/resource-source.d.ts +43 -45
- package/dist/platform/resource.d.ts +17 -23
- package/dist/platform/scope.d.ts +35 -61
- package/dist/platform/services.d.ts +4 -4
- package/dist/platform/storage.d.ts +1 -1
- package/dist/platform/sync-source.d.ts +196 -216
- package/dist/platform/transcript-cadence.d.ts +37 -41
- package/dist/platform/ui.d.ts +6 -6
- package/dist/text/endpoint.d.ts +11 -27
- package/package.json +3 -2
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
* gebruikt.
|
|
2
|
+
* A personal "come back to this later" marker on a resource, per (resourceType, resourceId,
|
|
3
|
+
* userId) - the same ResourceRef shape read-state uses.
|
|
5
4
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* Unlike a snooze this does not touch the resource itself: the team keeps seeing the conversation
|
|
6
|
+
* in its list, only your attention stack skips it.
|
|
8
7
|
*/
|
|
9
8
|
export interface ResourceReminder {
|
|
10
9
|
id: string;
|
|
@@ -12,12 +11,12 @@ export interface ResourceReminder {
|
|
|
12
11
|
resourceId: string;
|
|
13
12
|
userId: string;
|
|
14
13
|
organizationId: string;
|
|
15
|
-
/** Epoch
|
|
14
|
+
/** Epoch ms at which the item may come back into view. */
|
|
16
15
|
remindAt: number;
|
|
17
16
|
/**
|
|
18
|
-
* Epoch
|
|
19
|
-
*
|
|
20
|
-
*
|
|
17
|
+
* Epoch ms at which it was parked. Decides whether later activity lifts the reminder - an
|
|
18
|
+
* explicit field and not the framework `updatedAt`, so the expiry rule stays deterministic and
|
|
19
|
+
* testable.
|
|
21
20
|
*/
|
|
22
21
|
parkedAt: number;
|
|
23
22
|
createdAt?: number;
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
import { TimeEntry, TimeRounding } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The elapsed time of an entry, in seconds.
|
|
4
4
|
*
|
|
5
|
-
* In `domain`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* In `domain` and not in the app server, because client and server have to give the same answer:
|
|
6
|
+
* the server stamps the duration on stop, the panel counts it up every second in the meantime. If
|
|
7
|
+
* the panel counted from a zero point of its own, the visible clock would drift from what is
|
|
8
|
+
* eventually booked as soon as a tab sleeps.
|
|
9
9
|
*/
|
|
10
10
|
export declare function elapsedSeconds(entry: Pick<TimeEntry, "accumulatedSeconds" | "runningSince">, now: number): number;
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
12
|
+
* The booked duration belonging to a measured one.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Rounded up and with a minimum of one unit: whoever picks "per 15 minutes" wants a three-minute
|
|
15
|
+
* job to become a quarter of an hour and not zero. With `exact` the floor is one minute, for the
|
|
16
|
+
* same reason — an entry of zero seconds is not an entry.
|
|
17
17
|
*/
|
|
18
18
|
export declare function applyRounding(rawSeconds: number, rounding: TimeRounding | undefined): number;
|
|
19
|
-
/** `0:45` / `2:05` —
|
|
19
|
+
/** `0:45` / `2:05` — hours and minutes, like the day totals in the panel. */
|
|
20
20
|
export declare function formatHm(totalSeconds: number): string;
|
|
21
|
-
/** `12:34`
|
|
21
|
+
/** `12:34` under the hour, `1:02:03` above it — the running clock. */
|
|
22
22
|
export declare function formatClock(totalSeconds: number): string;
|
|
@@ -1,70 +1,71 @@
|
|
|
1
1
|
import { OwnerScope } from '../scope/types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Where a time entry stands in its life.
|
|
4
4
|
*
|
|
5
|
-
* `review` is
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* `review` is deliberately a **server** state and not something living only in the panel: the
|
|
6
|
+
* design lets you round off, pick a work type and update a note after stopping, before the entry
|
|
7
|
+
* counts. If that intermediate step lived only in the client, a closed tab would be exactly the
|
|
8
|
+
* moment the worked hour evaporates. Now the panel simply finds the entry again on the next visit,
|
|
9
|
+
* review card and all.
|
|
10
10
|
*
|
|
11
|
-
* `logged` is
|
|
11
|
+
* `logged` is the only state counting towards totals and reporting.
|
|
12
12
|
*/
|
|
13
13
|
export type TimeEntryStatus = "running" | "paused" | "review" | "logged";
|
|
14
|
-
/**
|
|
14
|
+
/** How measured time is translated into a bookable duration. */
|
|
15
15
|
export type TimeRounding = "exact" | "15" | "30";
|
|
16
|
-
/**
|
|
16
|
+
/** Entered by hand or actually clocked — visible in the reporting. */
|
|
17
17
|
export type TimeEntrySource = "timer" | "manual";
|
|
18
18
|
export interface TimeEntry {
|
|
19
19
|
id: string;
|
|
20
20
|
organizationId: string;
|
|
21
|
-
/**
|
|
21
|
+
/** Who wrote the time. An entry always belongs to exactly one person. */
|
|
22
22
|
userId: string;
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* What the time is booked on: `"interaction:abc"`, `"task:42"`, `"company:xyz"`. Absent =
|
|
25
|
+
* general work without an object.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Deliberately an opaque key and not an `interactionId`: the app owning the kind answers the
|
|
28
|
+
* access question (`assertScopeAccess`), so time tracking needs to know no other app at all to
|
|
29
|
+
* be able to write time against it.
|
|
30
30
|
*/
|
|
31
31
|
scopeKey?: string;
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
32
|
+
/** Human label at write time ("Gesprek · RE: EYLO"), so a list stays readable without
|
|
33
|
+
* querying the source app per row. */
|
|
34
34
|
scopeLabel?: string;
|
|
35
|
-
/**
|
|
35
|
+
/** File keys of the resource, flattened at write time — see the `keys` convention. */
|
|
36
36
|
keys?: string[];
|
|
37
|
-
/**
|
|
37
|
+
/** Ownership as the source app sees it; decides who sees the entry in a team view. */
|
|
38
38
|
ownerScope?: OwnerScope;
|
|
39
39
|
status: TimeEntryStatus;
|
|
40
|
-
/** Epoch ms,
|
|
40
|
+
/** Epoch ms, set by the server. The client only works out what time it is now. */
|
|
41
41
|
startedAt: number;
|
|
42
|
-
/** Epoch ms;
|
|
42
|
+
/** Epoch ms; set as soon as the timer stops. Absent while it runs or is paused. */
|
|
43
43
|
endedAt?: number;
|
|
44
44
|
/**
|
|
45
|
-
*
|
|
45
|
+
* Seconds already banked before the current segment.
|
|
46
46
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
47
|
+
* Pausing must not produce a second row — the design promises one entry per job, with a pause
|
|
48
|
+
* button in it. So on every pause the server adds the elapsed segment here and releases
|
|
49
|
+
* `runningSince`; resuming only sets `runningSince` again. Elapsed time is therefore always
|
|
50
|
+
* `accumulatedSeconds + (runningSince ? now - runningSince : 0)`, and that answer is the same on
|
|
51
|
+
* every device.
|
|
52
52
|
*/
|
|
53
53
|
accumulatedSeconds: number;
|
|
54
|
-
/** Epoch ms
|
|
54
|
+
/** Epoch ms the running segment started at. Absent = paused or stopped. */
|
|
55
55
|
runningSince?: number;
|
|
56
|
-
/**
|
|
56
|
+
/** What the clock actually measured. Stays when rounding pulls the booked duration up. */
|
|
57
57
|
rawSeconds?: number;
|
|
58
58
|
/**
|
|
59
|
-
*
|
|
59
|
+
* The booked duration in seconds — *this* is what counts in totals and invoicing.
|
|
60
60
|
*
|
|
61
|
-
*
|
|
62
|
-
* later
|
|
61
|
+
* Separate from `rawSeconds` because rounding is a user's choice and not a measurement: whoever
|
|
62
|
+
* asks later "where does that quarter of an hour come from" must still be able to see the two
|
|
63
|
+
* minutes that were measured.
|
|
63
64
|
*/
|
|
64
65
|
durationSeconds?: number;
|
|
65
66
|
rounding?: TimeRounding;
|
|
66
67
|
billable: boolean;
|
|
67
|
-
/**
|
|
68
|
+
/** Refers to `WorkType.key`. */
|
|
68
69
|
workType?: string;
|
|
69
70
|
note?: string;
|
|
70
71
|
source: TimeEntrySource;
|
|
@@ -72,35 +73,34 @@ export interface TimeEntry {
|
|
|
72
73
|
updatedAt?: number;
|
|
73
74
|
}
|
|
74
75
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
76
|
+
* A manageable work type instead of a list in code: which types an organisation knows differs per
|
|
77
|
+
* company, and the type decides whether something is billable by default.
|
|
77
78
|
*/
|
|
78
79
|
export interface WorkType {
|
|
79
80
|
id: string;
|
|
80
81
|
organizationId: string;
|
|
81
82
|
/**
|
|
82
|
-
*
|
|
83
|
+
* Stable key that lands on the time entry.
|
|
83
84
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
* twee betekenissen erin.
|
|
85
|
+
* Unique within the organisation and not within the scope, even though a type can belong to a
|
|
86
|
+
* team: `TimeEntry.workType` stores only this key and the reporting groups on it. Two teams each
|
|
87
|
+
* with their own `installatie` would come out as one row there, carrying two meanings.
|
|
88
88
|
*/
|
|
89
89
|
key: string;
|
|
90
90
|
label: string;
|
|
91
|
-
/**
|
|
91
|
+
/** Default for `TimeEntry.billable`; overridable per entry. */
|
|
92
92
|
defaultBillable: boolean;
|
|
93
|
-
/**
|
|
93
|
+
/** Included as a dimension in the reporting. */
|
|
94
94
|
reportable?: boolean;
|
|
95
95
|
order?: number;
|
|
96
96
|
archived?: boolean;
|
|
97
97
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
98
|
+
* Whose type this is: the whole organisation or one team. Decides who sees it in their time
|
|
99
|
+
* panel and who may manage it.
|
|
100
100
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
101
|
+
* No `personal`, for the same reason as with `Topic`: a type only one person knows makes the
|
|
102
|
+
* booked hour unreadable for the colleague taking over the week. Absent counts as org-wide —
|
|
103
|
+
* which is what every type effectively was before this field.
|
|
104
104
|
*/
|
|
105
105
|
ownerScope?: Extract<OwnerScope, {
|
|
106
106
|
kind: "org";
|
|
@@ -111,10 +111,10 @@ export interface WorkType {
|
|
|
111
111
|
updatedAt?: number;
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
*
|
|
114
|
+
* What the admin screen sends to the server.
|
|
115
115
|
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
116
|
+
* Without an `id` it is a new type. `key` is deliberately absent: the server derives it from the
|
|
117
|
+
* name and it is fixed afterwards, because booked hours refer to it.
|
|
118
118
|
*/
|
|
119
119
|
export interface SaveWorkTypeRequest {
|
|
120
120
|
id?: string;
|
|
@@ -125,13 +125,13 @@ export interface SaveWorkTypeRequest {
|
|
|
125
125
|
archived?: boolean;
|
|
126
126
|
ownerScope?: WorkType["ownerScope"];
|
|
127
127
|
}
|
|
128
|
-
/**
|
|
128
|
+
/** A candidate the user can clock on, offered in the panel's empty state. */
|
|
129
129
|
export interface TimeTarget {
|
|
130
130
|
scopeKey?: string;
|
|
131
|
-
/**
|
|
131
|
+
/** Kind label above the title ("GESPREK OP JE SCHERM", "TAAK"). */
|
|
132
132
|
kindLabel: string;
|
|
133
133
|
title: string;
|
|
134
|
-
/** Lucide
|
|
134
|
+
/** Lucide icon name. */
|
|
135
135
|
icon: string;
|
|
136
136
|
keys?: string[];
|
|
137
137
|
}
|
|
@@ -142,14 +142,14 @@ export interface StartTimerRequest {
|
|
|
142
142
|
workType?: string;
|
|
143
143
|
note?: string;
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
145
|
+
/** What can still be adjusted when saving an entry. */
|
|
146
146
|
export interface SaveTimeEntryRequest {
|
|
147
147
|
id: string;
|
|
148
148
|
rounding?: TimeRounding;
|
|
149
149
|
workType?: string;
|
|
150
150
|
billable?: boolean;
|
|
151
151
|
note?: string;
|
|
152
|
-
/**
|
|
152
|
+
/** Book against a different object than the timer started on. */
|
|
153
153
|
scopeKey?: string;
|
|
154
154
|
scopeLabel?: string;
|
|
155
155
|
keys?: string[];
|
|
@@ -158,18 +158,18 @@ export interface ManualTimeEntryRequest {
|
|
|
158
158
|
scopeKey?: string;
|
|
159
159
|
scopeLabel?: string;
|
|
160
160
|
keys?: string[];
|
|
161
|
-
/** Epoch ms;
|
|
161
|
+
/** Epoch ms; the day the entry falls on. */
|
|
162
162
|
startedAt: number;
|
|
163
163
|
durationSeconds: number;
|
|
164
164
|
workType?: string;
|
|
165
165
|
billable?: boolean;
|
|
166
166
|
note?: string;
|
|
167
167
|
}
|
|
168
|
-
/**
|
|
168
|
+
/** What the panel fetches in one go: the running timer plus the day around it. */
|
|
169
169
|
export interface TimeDaySummary {
|
|
170
|
-
/**
|
|
170
|
+
/** The entry running, paused or awaiting review right now — there is at most one. */
|
|
171
171
|
active?: TimeEntry;
|
|
172
|
-
/**
|
|
172
|
+
/** Finished entries of the requested day, ascending on start time. */
|
|
173
173
|
entries: TimeEntry[];
|
|
174
174
|
totalSeconds: number;
|
|
175
175
|
billableSeconds: number;
|
|
@@ -1,44 +1,41 @@
|
|
|
1
1
|
import { WorkType } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The key belonging to a name: lowercase, hyphens, no accents.
|
|
4
4
|
*
|
|
5
|
-
* In `domain`
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* rapportage.
|
|
5
|
+
* In `domain` and not in the app server because the admin screen shows it while you type — what
|
|
6
|
+
* the user sees there has to be exactly what the server writes later, or the form promises
|
|
7
|
+
* `installatie-op-locatie` and the reporting says `installatieoplocatie`.
|
|
9
8
|
*
|
|
10
|
-
* `normalize("NFD")` plus
|
|
11
|
-
*
|
|
9
|
+
* `normalize("NFD")` plus stripping the combining marks, so "Béta" becomes `beta` and not `b-ta`:
|
|
10
|
+
* a readable key is half the point.
|
|
12
11
|
*/
|
|
13
12
|
export declare function workTypeKey(label: string): string;
|
|
14
13
|
/**
|
|
15
|
-
*
|
|
14
|
+
* The same key, but guaranteed still free within the organisation.
|
|
16
15
|
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* een sleutel die hij nooit heeft gezien.
|
|
16
|
+
* Collisions are real: the key is org-unique while types are managed per team, so two teams both
|
|
17
|
+
* creating "Installatie" end up here. The second gets `installatie-2` instead of an error — an
|
|
18
|
+
* admin picking a name should not trip over a key they never saw.
|
|
21
19
|
*
|
|
22
|
-
* `taken`
|
|
23
|
-
*
|
|
20
|
+
* `taken` also holds archived keys: those still sit on booked hours, so reusing one would silently
|
|
21
|
+
* shift old work under a new type.
|
|
24
22
|
*/
|
|
25
23
|
export declare function uniqueWorkTypeKey(label: string, taken: Iterable<string>): string;
|
|
26
24
|
/**
|
|
27
|
-
*
|
|
25
|
+
* Does this user see this type?
|
|
28
26
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
27
|
+
* A type without a scope is org-wide: rows are older than the field, and filtering them out would
|
|
28
|
+
* cost an existing organisation its whole list.
|
|
31
29
|
*/
|
|
32
30
|
export declare function isWorkTypeVisible(workType: Pick<WorkType, "ownerScope">, teamIds: string[]): boolean;
|
|
33
31
|
/**
|
|
34
|
-
*
|
|
32
|
+
* What the panel shows: not archived, in the right order.
|
|
35
33
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
* heeft goedgekeurd.
|
|
34
|
+
* Without a team argument, deliberately: the server already cut team visibility off before the
|
|
35
|
+
* list reaches the client (that is a permission question). If the client filtered on `teamIds`
|
|
36
|
+
* again here it would have to know the user's memberships — and with an empty list, as it has one
|
|
37
|
+
* now, it would filter out precisely the team types the server just approved.
|
|
41
38
|
*/
|
|
42
39
|
export declare function activeWorkTypes(workTypes: WorkType[]): WorkType[];
|
|
43
|
-
/**
|
|
40
|
+
/** The order of the chips: `order` first, ties alphabetical so it stays stable. */
|
|
44
41
|
export declare function compareWorkTypes(a: WorkType, b: WorkType): number;
|
|
@@ -1,46 +1,45 @@
|
|
|
1
1
|
import { Topic, TopicExampleCandidate } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* The shape in which two examples are "the same".
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
5
|
+
* For comparing only, never for storing: what the admin sees is the original text. Ten copies of
|
|
6
|
+
* "Ik wil mijn factuur" teach the classifier nothing while crowding out an example that would, so
|
|
7
|
+
* capitals, punctuation and double spaces must not hide that.
|
|
8
8
|
*/
|
|
9
9
|
export declare function normalizeExample(text: string): string;
|
|
10
|
-
/**
|
|
10
|
+
/** Truncate on a whole sentence where possible, otherwise hard at the limit. */
|
|
11
11
|
export declare function truncateExample(text: string): string;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* Add a suggestion. Returns the new candidate list, or `null` when there was nothing to change —
|
|
14
|
+
* the caller then stores no write.
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
* 1.
|
|
18
|
-
* 2.
|
|
19
|
-
*
|
|
20
|
-
* 3.
|
|
21
|
-
*
|
|
16
|
+
* Three reasons to return `null`, all three ordinary:
|
|
17
|
+
* 1. Empty text. A conversation without an inbound message yields no example.
|
|
18
|
+
* 2. It is already in `examples`. The admin already judged this; suggesting it again is noise, and
|
|
19
|
+
* after one rejection it would come back on every next assignment.
|
|
20
|
+
* 3. It is already among the candidates. It *is* **updated** when the new report is a correction:
|
|
21
|
+
* that is the stronger signal and belongs at the top.
|
|
22
22
|
*/
|
|
23
23
|
export declare function addExampleCandidate(topic: Pick<Topic, "examples" | "exampleCandidates">, candidate: TopicExampleCandidate): TopicExampleCandidate[] | null;
|
|
24
24
|
/**
|
|
25
|
-
*
|
|
25
|
+
* Accept a suggestion: from the candidate list into `examples`.
|
|
26
26
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
27
|
+
* Runs in the **editor**, not on the server — accepting is an edit of the topic and travels with
|
|
28
|
+
* the normal save. That saves an endpoint and keeps "what the admin sees" and "what gets stored"
|
|
29
|
+
* one thing.
|
|
30
30
|
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* in de UI vermeld.
|
|
31
|
+
* On a full `examples` the **oldest** falls out (first in the list). Not unavoidably right — the
|
|
32
|
+
* first examples are often typed with care — but the alternative is putting a choice to the admin
|
|
33
|
+
* while they are doing something else. The cap is stated in the UI.
|
|
35
34
|
*/
|
|
36
35
|
export declare function acceptExampleCandidate(topic: Pick<Topic, "examples" | "exampleCandidates">, text: string): Pick<Topic, "examples" | "exampleCandidates">;
|
|
37
36
|
/**
|
|
38
|
-
*
|
|
37
|
+
* Throw a suggestion away.
|
|
39
38
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
39
|
+
* Rejecting is **not** remembered. If the exact same sentence shows up again on a manual
|
|
40
|
+
* assignment, it is back. Deliberately no third list of rejected texts: that is twice the state
|
|
41
|
+
* for a case that only occurs with a generic opening line ("Hallo, ik heb een vraag") — and then
|
|
42
|
+
* coming back is not strange either. If it still bothers you, the real fix is to accept it, after
|
|
43
|
+
* which it is never suggested again.
|
|
45
44
|
*/
|
|
46
45
|
export declare function rejectExampleCandidate(topic: Pick<Topic, "exampleCandidates">, text: string): TopicExampleCandidate[];
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
import { Topic } from './types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* May this topic be offered on a conversation in an inbox of this team?
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* `apps/communication/server/src/utils/scope.ts`),
|
|
8
|
-
*
|
|
9
|
-
* niet door te scrollen.
|
|
5
|
+
* Mind the word *offered*. This is a **picker filter**, not authorization: the read right is
|
|
6
|
+
* already handled by the server (`buildScopeQuery` in
|
|
7
|
+
* `apps/communication/server/src/utils/scope.ts`), so whatever arrives here the user may see
|
|
8
|
+
* anyway. What this filter adds is relevance — Sales need not scroll past Support's topics.
|
|
10
9
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* So it must **never** be used to look up an already assigned topic. A conversation that changes
|
|
11
|
+
* team keeps its label; looking that up in the filtered list would leave the label empty while a
|
|
12
|
+
* topic is very much set.
|
|
14
13
|
*
|
|
15
|
-
* `personal`
|
|
16
|
-
*
|
|
14
|
+
* `personal` always drops out: that scope is refused at write time, and if a row ever does arrive
|
|
15
|
+
* that way, it does not belong in a shared picker.
|
|
17
16
|
*/
|
|
18
17
|
export declare function topicOfferedForTeam(topic: Topic, teamId: string | undefined): boolean;
|
|
19
|
-
/** {@link topicOfferedForTeam} over
|
|
18
|
+
/** {@link topicOfferedForTeam} over a list. */
|
|
20
19
|
export declare function topicsForTeam(topics: Topic[], teamId: string | undefined): Topic[];
|
|
@@ -1,47 +1,45 @@
|
|
|
1
1
|
import { OrgTeamScope } from '../scope/types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* A topic is a **label** on a conversation: what is this about. Nothing more.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* It once also had a knowledge side (`knowledgeSources`: urls and kb articles "belonging to this
|
|
6
|
+
* topic"). That was removed because nobody read it — the form filled it, the entity stored it, and
|
|
7
|
+
* no prompt or search did anything with it. What it promised the knowledge base now does better
|
|
8
|
+
* (hybrid RAG over *all* articles) and the memory layer alongside it (`memory_find_similar`, from
|
|
9
|
+
* real traffic). See `docs/TOPICS.md`.
|
|
10
10
|
*/
|
|
11
11
|
export interface Topic {
|
|
12
12
|
id: string;
|
|
13
13
|
organizationId: string;
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* Dit stond hier als proza terwijl het type `personal` toestond, dus handhaafde niets het
|
|
19
|
-
* behalve de schrijfvalidatie. `OrgTeamScope` zegt het nu in het type.
|
|
15
|
+
* Whose topic this is: `org` or `team`. A topic only one person knows cannot be read by a
|
|
16
|
+
* colleague taking over the conversation. `OrgTeamScope` says so in the type, so it is not only
|
|
17
|
+
* the write validation enforcing it.
|
|
20
18
|
*/
|
|
21
19
|
ownerScope: OrgTeamScope;
|
|
22
20
|
name: string;
|
|
23
21
|
description: string;
|
|
24
22
|
/**
|
|
25
|
-
*
|
|
26
|
-
* classifier
|
|
23
|
+
* Example sentences the classification prompt receives. The only field that sharpens the
|
|
24
|
+
* classifier, and therefore the only one worth maintaining.
|
|
27
25
|
*/
|
|
28
26
|
examples: string[];
|
|
29
27
|
/**
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* Suggested examples, not accepted yet. Grows by itself: every time a **person** puts this topic
|
|
29
|
+
* on a conversation or corrects it, the opening sentence lands here.
|
|
32
30
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
31
|
+
* Deliberately next to `examples` and not in it: the classification prompt is where one bad
|
|
32
|
+
* example shifts the behaviour of *every* following conversation, and there is no undo on a
|
|
33
|
+
* prompt that sat crooked for three weeks. `examples` stays the admin's; this is a suggestion.
|
|
36
34
|
*/
|
|
37
35
|
exampleCandidates?: TopicExampleCandidate[];
|
|
38
36
|
}
|
|
39
37
|
/**
|
|
40
|
-
*
|
|
38
|
+
* One suggested example.
|
|
41
39
|
*
|
|
42
|
-
* `corrected`
|
|
43
|
-
*
|
|
44
|
-
*
|
|
40
|
+
* `corrected` distinguishes "someone put a topic on a conversation that had none" from "someone
|
|
41
|
+
* changed the topic" — the second is a corrected mistake and thereby the most informative case
|
|
42
|
+
* there is. The UI puts those on top.
|
|
45
43
|
*/
|
|
46
44
|
export interface TopicExampleCandidate {
|
|
47
45
|
text: string;
|
|
@@ -50,14 +48,14 @@ export interface TopicExampleCandidate {
|
|
|
50
48
|
corrected: boolean;
|
|
51
49
|
}
|
|
52
50
|
/**
|
|
53
|
-
*
|
|
51
|
+
* How many examples a topic may carry.
|
|
54
52
|
*
|
|
55
|
-
*
|
|
56
|
-
* (`apps/ai/server/src/playbook/ai/classify.ts`),
|
|
57
|
-
* per
|
|
53
|
+
* The whole list goes into the system prompt as JSON on *every* classification
|
|
54
|
+
* (`apps/ai/server/src/playbook/ai/classify.ts`), so unlimited growth makes classifying more
|
|
55
|
+
* expensive per conversation until it no longer fits.
|
|
58
56
|
*/
|
|
59
57
|
export declare const MAX_TOPIC_EXAMPLES = 20;
|
|
60
|
-
/**
|
|
58
|
+
/** Same for the candidate list: a suggestion bucket that fills up is one nobody reviews. */
|
|
61
59
|
export declare const MAX_TOPIC_CANDIDATES = 20;
|
|
62
|
-
/**
|
|
60
|
+
/** An example is an opening sentence, not a transcript. */
|
|
63
61
|
export declare const MAX_TOPIC_EXAMPLE_CHARS = 300;
|