@opencxh/domain 1.154.0 → 1.158.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.
Files changed (35) hide show
  1. package/dist/entities/activity/catalog.d.ts +23 -0
  2. package/dist/entities/ai-profile/types.d.ts +19 -6
  3. package/dist/entities/ai-settings/types.d.ts +27 -0
  4. package/dist/entities/ai-settings/types.test.d.ts +1 -0
  5. package/dist/entities/assignment/index.d.ts +1 -0
  6. package/dist/entities/assignment/types.d.ts +142 -0
  7. package/dist/entities/assignment/types.test.d.ts +1 -0
  8. package/dist/entities/live-lens/index.d.ts +1 -0
  9. package/dist/entities/live-lens/types.d.ts +81 -0
  10. package/dist/entities/live-lens/types.test.d.ts +1 -0
  11. package/dist/entities/playbook/actor.d.ts +20 -0
  12. package/dist/entities/playbook/assignment.d.ts +123 -0
  13. package/dist/entities/playbook/assignment.test.d.ts +1 -0
  14. package/dist/entities/playbook/index.d.ts +1 -0
  15. package/dist/entities/playbook/status.test.d.ts +1 -0
  16. package/dist/entities/playbook/trigger-vars.d.ts +34 -3
  17. package/dist/entities/playbook/types.d.ts +412 -33
  18. package/dist/entities/read-state/types.d.ts +1 -5
  19. package/dist/entities/user/types.d.ts +56 -0
  20. package/dist/index.cjs +8 -8
  21. package/dist/index.d.ts +6 -0
  22. package/dist/index.js +1022 -523
  23. package/dist/platform/ai-tools.d.ts +20 -0
  24. package/dist/platform/audio-gate.test.d.ts +1 -0
  25. package/dist/platform/context.d.ts +16 -0
  26. package/dist/platform/presence.d.ts +59 -0
  27. package/dist/platform/resource.d.ts +39 -0
  28. package/dist/platform/sdk.d.ts +6 -0
  29. package/dist/platform/tool-sources.test.d.ts +1 -0
  30. package/dist/platform/transcript-cadence.d.ts +93 -0
  31. package/dist/platform/transcript-cadence.test.d.ts +1 -0
  32. package/dist/platform/transcript-sanitize.d.ts +1 -0
  33. package/dist/platform/transcript-sanitize.test.d.ts +1 -0
  34. package/dist/platform/work-mode.test.d.ts +1 -0
  35. package/package.json +1 -1
@@ -51,6 +51,18 @@ interface ActivityTypeInfo<T extends ActivityType = ActivityType> {
51
51
  carriesText?: boolean;
52
52
  /** Door de engine zelf geschreven; mag geen nieuwe playbook-run openen. */
53
53
  playbookAuthored?: boolean;
54
+ /**
55
+ * Betekent dit type dat er daadwerkelijk contact tot stand kwam — dat er gesproken is?
56
+ *
57
+ * Nodig omdat "is hier echt iets uitgewisseld" niet uit berichten alleen te bepalen valt: een
58
+ * aangenomen telefoongesprek is een volwaardige casus met nul berichten, en een gemiste oproep
59
+ * is het tegenovergestelde. Een eigen vlag en niet afgeleid van `shape`, want beide zijn
60
+ * `event` — het verschil zit in de betekenis, niet in de vorm.
61
+ *
62
+ * Bewust hier en niet als lijstje in de app die de vraag stelt: dat is precies hoe "is dit een
63
+ * bericht?" zes keer los kwam te staan.
64
+ */
65
+ connected?: boolean;
54
66
  /**
55
67
  * Verschijnt in de trigger-keuzelijst van playbooks en webhooks.
56
68
  *
@@ -111,6 +123,17 @@ export declare function carriesText(type: string): boolean;
111
123
  export declare function messageCountsAs(type: string): "inbound_message" | "outbound_message" | undefined;
112
124
  /** Door de playbook-engine zelf geschreven; mag geen nieuwe run openen. */
113
125
  export declare function isPlaybookAuthoredType(type: string): boolean;
126
+ /** Kwam er met dit type daadwerkelijk contact tot stand? Zie {@link ActivityTypeInfo.connected}. */
127
+ export declare function isConnectedType(type: string): boolean;
128
+ /**
129
+ * Is dit een transcript-achtig artefact — meegeleverde inhoud die leesbare tekst draagt?
130
+ *
131
+ * Het onderscheid met een bestand zit al in de catalogus (`FILE_UPLOADED` draagt geen tekst), dus
132
+ * dit is geen nieuwe lijst maar een combinatie van twee bestaande antwoorden. Zo telt het
133
+ * transcript van een externe provider mee zodra die app zijn type declareert, zonder dat hier een
134
+ * naam bij hoeft.
135
+ */
136
+ export declare function isTranscriptType(type: string): boolean;
114
137
  /** De inboxlijst-regel, of `""` wanneer het type er geen heeft. */
115
138
  export declare function activitySnippet(activity: Activity): string;
116
139
  /**
@@ -8,16 +8,21 @@ export interface AIProfile<T extends Record<string, any> = Record<string, any>>
8
8
  organizationId: string;
9
9
  /**
10
10
  * Who may use this profile. Company-wide (`org`) or bound to a `team`; profiles
11
- * are never personal. Optional only during the migration window - the server
12
- * defaults new profiles to `org` and backfills existing ones.
11
+ * are never personal. The server resolves one on create (`normalizeProfileScope`),
12
+ * so it is always present.
13
13
  */
14
- ownerScope?: OwnerScope;
14
+ ownerScope: OwnerScope;
15
15
  name: string;
16
16
  description?: string;
17
17
  accountId: string;
18
18
  model: string;
19
19
  providerConfig?: T;
20
- systemPrompt: string;
20
+ /**
21
+ * De basisinstructie van dit brein. Optioneel: een profiel dat alleen tools ontsluit heeft
22
+ * er geen nodig. Stond hier als verplicht terwijl het schema hem optioneel maakte, waardoor
23
+ * elke lezer `?? ""` moest schrijven zonder dat het type dat verklaarde.
24
+ */
25
+ systemPrompt?: string;
21
26
  contextIds?: string[];
22
27
  predefinedPrompts?: PredefinedPrompt[];
23
28
  /** Namespaced tool names this profile may use (default: none enabled). */
@@ -31,8 +36,16 @@ export interface AIProfile<T extends Record<string, any> = Record<string, any>>
31
36
  * which the transform leaves untouched.
32
37
  *
33
38
  * 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.
39
+ * A tool absent from this list defaults to "write" (safe).
40
+ *
41
+ * **Waar dit wél en níet geldt.** Afgedwongen in `playbook/ai/gate.ts`, en daarmee op elke
42
+ * autonome run (workflow of procedure). De interactieve assistent doet er **niets** mee: daar
43
+ * bestaat geen bevestigingsstap, en er is ook geen `requiresConfirmation` om aan te haken —
44
+ * die stond hier als belofte terwijl er nergens een implementatie was. Een `write`-markering
45
+ * die je met een gesprek in de chat verwacht af te dwingen, doet dus niets.
46
+ *
47
+ * Dat is een openstaand gat en geen ontwerpkeuze: een bevestigingsstap in de chat is UI plus
48
+ * een extra ronde, en hoort een besluit te zijn in plaats van een veld dat stil niets doet.
36
49
  */
37
50
  toolPolicy?: {
38
51
  name: string;
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Wat een organisatie voor de AI-app als geheel instelt — niet per profiel, niet per gebruiker.
3
+ *
4
+ * Eén veld tot nu toe, en dat is met opzet geen `Record<string, unknown>`: een instelling die
5
+ * niemand kan opnoemen is een instelling die niemand kan vinden.
6
+ */
7
+ export interface AiOrgSettings {
8
+ /**
9
+ * Mag de AI-app meeluisteren met gesprekken (spraak naar tekst)?
10
+ *
11
+ * Dit is de knop die er niet was: de AI-app installeren zette transcriptie aan voor élk
12
+ * gesprek in de organisatie, wat een technische toevalligheid was die als beleid gold.
13
+ */
14
+ listeningEnabled?: boolean;
15
+ }
16
+ /**
17
+ * Mag er meegeluisterd worden?
18
+ *
19
+ * **Afwezig = ja**, en dat is hier de juiste kant: de knop is nieuw, en een organisatie die
20
+ * hem nooit heeft aangeraakt hoort niet plotseling zonder transcriptie te zitten. Uit is dus
21
+ * een expliciete keuze en niet een lege rij.
22
+ *
23
+ * Eén plek, zodat de client (die de upload overslaat) en de server (die hem weigert) niet
24
+ * ieder hun eigen default kunnen hebben — dan zou de een luisteren terwijl de ander denkt van
25
+ * niet.
26
+ */
27
+ export declare function listeningAllowed(settings: AiOrgSettings | null | undefined): boolean;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,142 @@
1
+ import { ResourceRef } from '../../platform/resource';
2
+ import { WaitingOn } from '../playbook/types';
3
+ /**
4
+ * Een **opdracht**: werk dat aan een agent is toegewezen en zo lang leeft als het werk.
5
+ *
6
+ * **Waarom dit een entity is.** Het was er geen: `plans/unify-ai.md` besliste "een opdracht *is*
7
+ * een run met een procedure, een agent als actor en een lange levensduur". Dat is verdedigbaar
8
+ * gedacht, en de code liet zien wat het kostte:
9
+ *
10
+ * - `resume-playbook.ts` kortte elke procedure-run af op `{ status: "done" }`, dus een opdracht was
11
+ * structureel **één modelbeurt**;
12
+ * - de draad (een `AIConversation` op `playbook-run:<id>`) deelde géén geheugen met de agent:
13
+ * `runProcedure` gaf bewust geen `conversationId` mee, dus wat een mens in de draad zei bereikte
14
+ * de agent nooit en omgekeerd;
15
+ * - `retry` betekende "doe de hele opdracht opnieuw vanaf de originele trigger" — zinnig voor een
16
+ * workflow van seconden, onzin voor werk dat dagen liep;
17
+ * - en "wat voor ding is dit" moest uit de *uitvoeringsvorm* worden afgeleid
18
+ * (`decider.kind === "procedure"`), dus de vorm deed dienst als producttype.
19
+ *
20
+ * Nu is een opdracht wat hij is, en een run wat die is: **één beurt**. De opdracht leeft, de runs
21
+ * zijn wat hij deed.
22
+ */
23
+ /**
24
+ * Waar een opdracht in verkeert.
25
+ *
26
+ * Bewust dezelfde woorden als de eindstanden van een run, en bewust minder: een opdracht kan niet
27
+ * `failed` zijn. Een gefaalde beurt is een gefaalde run — de opdracht staat dan nog open, want er
28
+ * is niets afgehandeld. Dat verschil is precies waarom dit een eigen vocabulaire is.
29
+ */
30
+ export type AssignmentStatus = "open" | "waiting" | "done" | "escalated";
31
+ export declare const TERMINAL_ASSIGNMENT_STATUSES: readonly ["done", "escalated"];
32
+ export declare function isAssignmentTerminal(status: AssignmentStatus | string): boolean;
33
+ /**
34
+ * De scope-soort waaronder de draad van een opdracht leeft.
35
+ *
36
+ * Was `playbook-run:<id>`, want de draad hing aan een run. Hij hangt nu aan de opdracht, en dat is
37
+ * het hele punt: een draad die stopt zodra één beurt klaar is, is geen draad.
38
+ *
39
+ * Staat in domain omdat het een **contract tussen client en server** is: de client bouwt de
40
+ * sleutel om de draad te openen, de server autoriseert hem. Twee plekken die het voorvoegsel los
41
+ * uitschrijven lopen bij de eerste typefout stil uiteen — en het gevolg daarvan is "geen toegang
42
+ * tot deze scope", niet een foutmelding die het zegt.
43
+ */
44
+ export declare const ASSIGNMENT_SCOPE_KIND = "assignment";
45
+ export declare function assignmentScopeKey(assignmentId: string): string;
46
+ export interface Assignment {
47
+ id: string;
48
+ organizationId: string;
49
+ /** De agent die dit doet: het `userId` van een gebruiker met `type: "agent"`. */
50
+ agentId: string;
51
+ /** De definitie waar deze opdracht uit voortkomt (een playbook met een procedure). */
52
+ jobId: string;
53
+ /**
54
+ * De versie waarmee hij begon — puur voor de audit.
55
+ *
56
+ * Elke **beurt** pint zijn eigen procedure (`run.decider`), dus een verbeterde prose komt bij de
57
+ * volgende beurt aan zonder een lopende beurt van opdracht te laten wisselen. Eeuwig pinnen zou
58
+ * betekenen dat een correctie nooit aankomt; niet pinnen dat een beurt halverwege verandert.
59
+ */
60
+ jobVersion: number;
61
+ /** Waar deze opdracht over gaat. Afwezig = werk zonder resource (een rollup, een klus). */
62
+ subjectKind?: string;
63
+ subjectId?: string;
64
+ status: AssignmentStatus;
65
+ /**
66
+ * Waarop de **opdracht** wacht tussen twee beurten.
67
+ *
68
+ * Bewust hier en niet op de run, want het zijn twee verschillende soorten pauze:
69
+ *
70
+ * - een **workflow** pauzeert *midden in een flow* en hervat op stap N+1 met dezelfde vars — de
71
+ * cursor (`run.resumeAtStep`) is essentieel;
72
+ * - een **opdracht** heeft geen cursor. "Hervatten" is *nog een beurt nemen*, met de draad als
73
+ * geheugen.
74
+ *
75
+ * Dat die twee in één veld geperst zaten is waarom elke procedure-run `resumeAtStep: 0` opsloeg.
76
+ */
77
+ waitingOn?: WaitingOn | null;
78
+ /** De soort van `waitingOn` als platte kolom — de store kan niet in een genest object zoeken. */
79
+ waitingOnKind?: WaitingOn["on"] | null;
80
+ /**
81
+ * Hoeveel beurten deze opdracht al heeft gehad.
82
+ *
83
+ * Was `run.rounds`, wat het aantal keer parkeren *binnen één run* telde. Op dit niveau is het de
84
+ * teller die telt: een agent die met een mens heen en weer blijft pingpongen moet een plafond
85
+ * hebben.
86
+ */
87
+ turns: number;
88
+ /** De draad: agent én mens schrijven hierin. Dit is de toestand van de opdracht. */
89
+ conversationId: string;
90
+ /** Lijst-index, geen autorisatie — zie `PlaybookRun.visibleTo`. */
91
+ visibleTo?: string[];
92
+ /** Waar deze opdracht over gaat, in mensentaal. Voor de lijst. */
93
+ title?: string;
94
+ createdBy: string;
95
+ lastActivityAt?: number;
96
+ }
97
+ /** Het onderwerp van een opdracht als één verwijzing, of `undefined`. */
98
+ export declare function assignmentSubject(assignment: {
99
+ subjectKind?: string;
100
+ subjectId?: string;
101
+ }): ResourceRef | undefined;
102
+ /**
103
+ * Hoeveel beurten een opdracht mag nemen voordat een mens het overneemt.
104
+ *
105
+ * Niet oneindig, want elke beurt is een modelaanroep en een agent kan met de beste bedoelingen
106
+ * blijven vragen. Bij het plafond escaleert hij, en dat is te zien in de draad — een opdracht die
107
+ * stil ophoudt zou de vraag "waarom doet hij niets meer" onbeantwoordbaar maken.
108
+ */
109
+ export declare const MAX_ASSIGNMENT_TURNS = 12;
110
+ /**
111
+ * Wat een beurt als volgende zet koos. Dit is wat de control-tools vastleggen.
112
+ *
113
+ * Eén union, want de vier uitkomsten sluiten elkaar uit: je wacht op iets, je bent klaar, of je
114
+ * geeft het over aan een mens.
115
+ */
116
+ export type AssignmentControl = {
117
+ kind: "wait";
118
+ on: WaitingOn;
119
+ } | {
120
+ kind: "done";
121
+ note?: string;
122
+ } | {
123
+ kind: "escalate";
124
+ reason: string;
125
+ };
126
+ /**
127
+ * Waar de opdracht na deze beurt in verkeert.
128
+ *
129
+ * Puur, zodat de regel op één plek staat en zonder Bridge te testen is — dezelfde vorm als
130
+ * `decideFinalState` voor een run.
131
+ *
132
+ * **Geen control-call betekent escaleren**, niet stil openblijven. Dat is de regel die
133
+ * `decideFinalState` al toepast op een run die wacht zonder geldige reden ("liever luid falen dan
134
+ * stil parkeren"): een agent die zijn beurt afmaakt zonder te zeggen wat hij wil, laat een mens
135
+ * met een opdracht zitten waarvan niemand weet of er nog iets gebeurt.
136
+ */
137
+ export declare function decideAssignmentState(control: AssignmentControl | undefined, turnsSoFar: number, maxTurns?: number): {
138
+ status: AssignmentStatus;
139
+ waitingOn: WaitingOn | null;
140
+ turns: number;
141
+ reason?: string;
142
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export * from './types';
@@ -0,0 +1,81 @@
1
+ import { OwnerScope } from '../contact/types';
2
+ import { ConditionStep, ForEachStep, LookupStep, ParallelStep, Step } from '../playbook/types';
3
+ /**
4
+ * Een **leesbaan**: wat er meeleest tijdens een levend gesprek, en aan wie het iets toont.
5
+ *
6
+ * **Waarom dit een eigen entity is en geen playbook met een `stream`-trigger.** Dat was het:
7
+ * `PlaybookTrigger` had een arm `{ kind: "stream" }` met opzet zonder velden, en zo'n rij deed
8
+ * daarna niets van wat een playbook doet — geen run, geen job, geen lock, geen goedkeuringspoort,
9
+ * geen `finalizeRun`. `live-assist` bouwde met de hand een `RunContext` en riep `executeFlow`
10
+ * direct aan.
11
+ *
12
+ * De prijs stond in de velden: `autonomy`, `agentId`, `procedure`, `stats`, `lastRunAt` en
13
+ * `debounceMs` waren voor zo'n rij allemaal betekenisloos, en `steps` mocht maar vier van de tien
14
+ * staptypes bevatten. Iedere lezer van `Playbook` moest dus impliciet weten of hij naar een
15
+ * automatisering of naar een leesbaan keek, met twee functies in domain als vangnet en een
16
+ * tabel-scan-plus-cache als selectie.
17
+ *
18
+ * Definities horen gescheiden te zijn naar hun **runtime**, niet naar hun editor. Een leesbaan
19
+ * heeft geen run, dus is het geen playbook.
20
+ *
21
+ * Wat hij deelt met een playbook is de **staptaal** en de executor — niet de tabel.
22
+ */
23
+ /**
24
+ * Wat een leesbaan mag doen: opzoeken, kiezen, en dat desnoods parallel of per item.
25
+ *
26
+ * Dit is een **echt subtype** van {@link Step} en geen lijst met toegestane namen. Alles wat
27
+ * schrijft (`action`), een model kost (`classify`, `generate`, `agent`) of kan pauzeren
28
+ * (`wait-for-*`) valt erbuiten — en pauzeren kán hier ook niet, want er is geen rij om op te
29
+ * hervatten.
30
+ *
31
+ * Dat het een type is en geen controle is de winst: een leesbaan die zou schrijven is nu een
32
+ * typefout bij het bouwen, in plaats van een validatie die pas bij het opslaan (of erger: bij de
33
+ * eerste ronde, tijdens een telefoongesprek) iets zegt.
34
+ *
35
+ * Wie tijdens een gesprek iets wíl laten schrijven, doet dat met een playbook op een
36
+ * `activity`-trigger. Die heeft een run, een eigenaar en een goedkeuringspoort.
37
+ */
38
+ export type ReadStep = LookupStep | ConditionStep | ForEachStep<ReadStep> | ParallelStep<ReadStep>;
39
+ /** De staptypes die een leesbaan mag bevatten — de runtime-tegenhanger van {@link ReadStep}. */
40
+ export declare const READ_STEP_TYPES: readonly string[];
41
+ export interface LiveLens {
42
+ id: string;
43
+ organizationId: string;
44
+ /** Voor wie deze baan loopt. De werkmodus selecteert de team-banen; zie {@link selectLenses}. */
45
+ ownerScope: OwnerScope;
46
+ name: string;
47
+ description?: string;
48
+ enabled: boolean;
49
+ /** Wat er opgezocht wordt. Alleen lezen — zie {@link ReadStep}. */
50
+ steps: ReadStep[];
51
+ createdBy: string;
52
+ }
53
+ /**
54
+ * Waarom deze stappenlijst geen leesbaan mag zijn, of `null` als hij het mag.
55
+ *
56
+ * Nodig ondanks {@link ReadStep}, want een `POST` levert JSON en daar helpt een type niet: dit is
57
+ * de poort bij het **opslaan**. Hij draaide voorheen bij élke ronde, elke acht seconden per lopend
58
+ * gesprek, om een eigenschap te controleren die bij het opslaan al vaststond.
59
+ *
60
+ * Een reden en geen boolean, omdat een geweigerde baan anders stil niets doet — precies de klasse
61
+ * fout die "ik heb hem aangezet en er komt niets" onbeantwoordbaar maakt.
62
+ */
63
+ export declare function readStepError(steps: readonly Step[]): string | null;
64
+ /**
65
+ * De leesbanen die bij déze medewerker op dít moment horen.
66
+ *
67
+ * De werkmodus is wat een team-baan selecteert: zonder die keuze zouden bij iemand die in Sales
68
+ * én Support zit beide baansets tegelijk meelezen — het probleem waarvoor de werkmodus bestaat.
69
+ * Persoonlijke en org-brede banen staan er los van.
70
+ *
71
+ * Merk op dat de *acting identity* van een ronde altijd de medewerker zelf is, ook bij een team-
72
+ * of org-brede baan: hij ziet tijdens zijn eigen gesprek nooit meer dan hij mag zien. De eigenaar
73
+ * bepaalt hier alleen wélke banen er lopen.
74
+ *
75
+ * Geen `skipped`-uitkomst meer: die bestond om een baan te melden die niet mocht meelezen, en dat
76
+ * kan niet meer bestaan — `readStepError` weigert hem bij het opslaan.
77
+ */
78
+ export declare function selectLenses<T extends Pick<LiveLens, "enabled" | "ownerScope">>(lenses: readonly T[], lens: {
79
+ userId: string;
80
+ teamId?: string;
81
+ }): T[];
@@ -0,0 +1 @@
1
+ export {};
@@ -9,6 +9,26 @@ import { OwnerScope } from '../contact/types';
9
9
  * functie is die vertaling, en de enige plek waar hij hoort.
10
10
  */
11
11
  export declare function playbookActor(scope: OwnerScope): ActingIdentity;
12
+ /**
13
+ * Namens wie deze run handelt.
14
+ *
15
+ * **Twee verschillende vragen, en dat is de hele reden dat deze functie naast
16
+ * {@link playbookActor} bestaat:**
17
+ *
18
+ * - *wie handelt* — de agent, als er één is aangewezen;
19
+ * - *wie mag dit beheren* — `ownerScope`, en dat blijft van mensen.
20
+ *
21
+ * Die twee op één veld laten leunen leek aantrekkelijk, want een agent is een gebruiker en
22
+ * `ownerScope: { kind: "personal", userId }` levert precies de juiste actor op. Maar
23
+ * `ownerScope` bepaalt óók wie mag bewerken, en dan kon geen enkel mens de playbook van een
24
+ * agent nog aanpassen — de agent zelf logt immers nooit in.
25
+ *
26
+ * Zonder `agentId` verandert er niets: dan is de actor die van de scope, precies zoals eerst.
27
+ */
28
+ export declare function runActor(playbook: {
29
+ agentId?: string | null;
30
+ ownerScope: OwnerScope;
31
+ }): ActingIdentity;
12
32
  /**
13
33
  * Stabiele sleutel om actors te groeperen. Tien playbooks van hetzelfde team horen
14
34
  * één autorisatie-call te kosten, niet tien.
@@ -0,0 +1,123 @@
1
+ import { LocaleBundle } from '../analytics/dashboard';
2
+ import { PlaybookTrigger } from './types';
3
+ /**
4
+ * Toewijzen als wekker — het oppervlak waarop een **opdracht** bestaat.
5
+ *
6
+ * **Herzien:** een opdracht *is* wél een entiteit — zie {@link Assignment}. Hier stond "een
7
+ * opdracht is geen entiteit: het is een run met een procedure", en dat brak op drie punten: een
8
+ * procedure-run werd altijd afgekort tot één beurt, de draad ernaast deelde geen geheugen met de
9
+ * agent, en "wat voor ding is dit" moest uit de uitvoeringsvorm worden afgeleid.
10
+ *
11
+ * Wat deze module doet verandert daar niet door: de handeling die het werk begint bestaat al in
12
+ * elke app die werk kent — **je wijst iets aan iemand toe.** Wijs je het aan een gebruiker met
13
+ * `type: "agent"` toe, dan is dat het startsignaal, en dan zet de engine er een opdracht neer.
14
+ *
15
+ * Daarom is dit een federatie en geen lijst in de ai-app. Alleen comms weet dat een gesprek
16
+ * toegewezen kan worden en dat `INTERACTION_ASSIGNED` dat aankondigt; alleen een taken-app weet
17
+ * dat van een taak. De hub hoort dat niet te weten — precies de scheiding die de
18
+ * geheugen-catalogus (`memory-source`) en de analytics-catalogus al maken. Mal:
19
+ * `apps/context/server/src/memory/catalog.ts`.
20
+ *
21
+ * **Er komt geen nieuw wekmechanisme.** Een toewijzing is al een activity, dus de bestaande
22
+ * `activity`-poort levert hem af; deze module voegt alleen de vraag toe die de engine zelf niet
23
+ * kan beantwoorden: *"is dit aan míjn agent toegewezen?"*
24
+ */
25
+ /**
26
+ * Eén soort werk dat aan een agent toegewezen kan worden, gedeclareerd door de app die het
27
+ * bezit.
28
+ */
29
+ export interface AssignmentTargetKind {
30
+ /**
31
+ * `<app>.<ding>` — het voorvoegsel is de eigenaar, dezelfde conventie als `memory_kind` en
32
+ * de dossiersleutels. Twee apps kunnen zo nooit dezelfde soort claimen.
33
+ */
34
+ id: string;
35
+ label: string;
36
+ description?: string;
37
+ /**
38
+ * De activity-types waarmee deze app "dit is toegewezen" aankondigt.
39
+ *
40
+ * Gedeclareerd en niet geraden: de engine ziet alleen een type-string langskomen, en welk
41
+ * type een toewijzing ís weet alleen de bron. Een geraden voorvoegsel zou bij de eerste app
42
+ * die zijn eigen soort meebrengt stil het verkeerde type pakken.
43
+ */
44
+ activityTypes: string[];
45
+ /**
46
+ * Waar op de activity het userId van de toegewezene staat, als punt-pad
47
+ * (`"payload.userId"`).
48
+ *
49
+ * Een pad en geen vaste sleutel, omdat de payload van de bron is. Comms zet `payload.userId`;
50
+ * een andere app mag `payload.assignee.id` zetten zonder dat de hub verandert.
51
+ */
52
+ assigneePath: string;
53
+ /** Gezet door de hub na de fan-out, niet door de bron. */
54
+ origin?: "app";
55
+ }
56
+ /**
57
+ * Bare payload van `GET /provider/assignment/describe` — niet in `ResponseFactory` verpakt
58
+ * (mal: `MemorySourceDescription`).
59
+ */
60
+ export interface AssignmentSourceDescription {
61
+ /** De declarerende app (== `manifest.name` == `req.source.app`). */
62
+ source: string;
63
+ targets: AssignmentTargetKind[];
64
+ /** Vlakke LIJST: keys met een punt worden gemangeld. */
65
+ locales?: LocaleBundle;
66
+ }
67
+ /** De samengestelde catalogus die de hub aan de builder levert. */
68
+ export interface AssignmentCatalog {
69
+ targets: AssignmentTargetKind[];
70
+ /** Bronnen die meededen aan de fan-out (voor "wie levert dit?"). */
71
+ sources: string[];
72
+ locales: LocaleBundle;
73
+ }
74
+ /**
75
+ * De waarde op een punt-pad, of `undefined`.
76
+ *
77
+ * Eigen mini-lezer en niet `resolveRef` uit de engine: die woont in de ai-app en kent de
78
+ * `$`-conventie, terwijl hier een kaal pad uit een app-declaratie gelezen wordt. Stopt bij het
79
+ * eerste niet-object, zodat een pad dat langs `null` loopt `undefined` geeft en niet gooit.
80
+ */
81
+ export declare function valueAtPath(source: unknown, path: string): unknown;
82
+ /**
83
+ * De doelsoort die dit activity-type aankondigt, en de doelsoort met dit id.
84
+ *
85
+ * Twee lezers, één lijst: de engine komt binnen met een type ("wat is dit?"), de builder en de
86
+ * validatie met een id ("bestaat deze soort?").
87
+ */
88
+ export declare function targetForActivityType(catalog: Pick<AssignmentCatalog, "targets">, activityType: string): AssignmentTargetKind | undefined;
89
+ export declare function targetById(catalog: Pick<AssignmentCatalog, "targets">, id: string | undefined): AssignmentTargetKind | undefined;
90
+ /** Is dit een trigger die op een toewijzing wacht? */
91
+ export declare function isAssignmentTrigger(trigger: PlaybookTrigger | undefined): trigger is Extract<PlaybookTrigger, {
92
+ kind: "assignment";
93
+ }>;
94
+ /**
95
+ * Waarom deze toewijzing géén run van deze playbook start, of `null` als hij dat wél doet.
96
+ *
97
+ * Een reden en geen boolean, om dezelfde reden als `triggerMismatch`: een trigger die niet
98
+ * matcht laat geen run achter, dus er is geen trace om in te kijken. "Ik heb dit gesprek aan de
99
+ * agent gegeven en er gebeurt niets" moet te beantwoorden zijn.
100
+ *
101
+ * Puur, en dat is waar de vier poorten hier staan in plaats van in de job:
102
+ *
103
+ * 1. **Geen agent** ⇒ nooit. "Toegewezen aan wie?" heeft dan geen antwoord, en zonder deze
104
+ * poort zou zo'n playbook op *elke* toewijzing van *iedereen* vuren.
105
+ * 2. **Onbekende doelsoort** ⇒ nooit, met naam en al. Dat is het geval "de app die dit
106
+ * declareerde is uitgeschakeld of antwoordt niet", en dat mag geen stilte zijn.
107
+ * 3. **Aan iemand anders toegewezen** ⇒ niet deze playbook. Dit is de kernvergelijking.
108
+ * 4. **De agent wees het zichzelf toe** ⇒ niet opnieuw. Zonder deze poort is een procedure die
109
+ * zelf toewijst (`update_interaction`) een lus die zichzelf blijft wekken: de
110
+ * lifecycle-activity draagt geen `runId`, dus de bestaande loop-demping ziet hem niet.
111
+ */
112
+ export declare function assignmentMismatch(input: {
113
+ trigger: PlaybookTrigger | undefined;
114
+ /** `Playbook.agentId` — het userId van de agent die deze playbook uitvoert. */
115
+ agentId?: string | null;
116
+ /** De doelsoort uit de catalogus die bij deze playbook hoort, indien gevonden. */
117
+ target?: AssignmentTargetKind;
118
+ activityType: string;
119
+ /** Aan wie dit toegewezen werd, gelezen via `target.assigneePath`. */
120
+ assigneeUserId?: string;
121
+ /** Wie de toewijzing deed, als het een gebruiker was. */
122
+ authorUserId?: string;
123
+ }): string | null;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,4 +1,5 @@
1
1
  export * from './labels';
2
2
  export * from './trigger-vars';
3
3
  export * from './actor';
4
+ export * from './assignment';
4
5
  export * from './types';
@@ -0,0 +1 @@
1
+ export {};
@@ -52,22 +52,53 @@ export declare const TRIGGER_VARS: readonly [{
52
52
  }, {
53
53
  readonly name: "companyId";
54
54
  readonly type: "string";
55
+ }, {
56
+ readonly name: "assigneeUserId";
57
+ readonly type: "string";
55
58
  }];
56
- /** De variabelen die een classify-stap oplevert, per mode. */
59
+ /**
60
+ * Wat een classify-stap per mode **oplevert** — het contract dat `runClassify` nakomt.
61
+ *
62
+ * Staat hier als type en niet alleen als lijst, omdat {@link CLASSIFY_VARS} eruit hoort te volgen.
63
+ * Die lijst was uiteengelopen met de werkelijkheid, precies zoals `TRIGGER_VARS` dat eerder was:
64
+ * `question` bood `confidence` aan (bestaat niet, de adapter geeft `answer` + `reasoning`) en
65
+ * `topics` miste `topicId` en `reasoning`. Een pad dat de picker aanbiedt en dat `undefined`
66
+ * oplevert is stil kapot — vandaar dat de adapter er in `ai/classify.test.ts` tegen wordt gehouden.
67
+ */
68
+ export interface ClassifyTopicsResult {
69
+ topicId: string;
70
+ /** Naam van het onderwerp, of `null` bij `"none"`. */
71
+ topic: string | null;
72
+ confidence: number;
73
+ reasoning?: string;
74
+ }
75
+ export interface ClassifyQuestionResult {
76
+ answer: boolean;
77
+ reasoning?: string;
78
+ }
79
+ /** De zelf-gedefinieerde velden uit `ClassifyStep.fields`; de vorm is van de auteur. */
80
+ export type ClassifyExtractResult = Record<string, unknown>;
81
+ /** De variabelen die een classify-stap oplevert, per mode. Volgt de types hierboven. */
57
82
  export declare const CLASSIFY_VARS: {
58
83
  readonly topics: readonly [{
84
+ readonly name: "topicId";
85
+ readonly type: "string";
86
+ }, {
59
87
  readonly name: "topic";
60
88
  readonly type: "string";
61
89
  }, {
62
90
  readonly name: "confidence";
63
91
  readonly type: "number";
92
+ }, {
93
+ readonly name: "reasoning";
94
+ readonly type: "string";
64
95
  }];
65
96
  readonly question: readonly [{
66
97
  readonly name: "answer";
67
98
  readonly type: "boolean";
68
99
  }, {
69
- readonly name: "confidence";
70
- readonly type: "number";
100
+ readonly name: "reasoning";
101
+ readonly type: "string";
71
102
  }];
72
103
  readonly extract: readonly [];
73
104
  };