@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
@@ -1,9 +1,11 @@
1
1
  import { ActingIdentity } from '../../platform/identity';
2
+ import { ResourceRef } from '../../platform/resource';
2
3
  import { OwnerScope } from '../contact/types';
3
4
  /**
4
- * Playbooks — event-driven automation with AI steps. See docs/PLAYBOOKS.md.
5
- * Fase 0 covers the deterministic subset; AI steps are typed here but the
6
- * executor rejects them until Fase 2.
5
+ * Playbooks — event-driven automation met AI-stappen. Zie docs/PLAYBOOKS.md.
6
+ *
7
+ * Twee uitvoeringsvormen, één contract: zie {@link Decider}. Alle staptypes in
8
+ * {@link Step} worden uitgevoerd; er is geen soort meer die de executor weigert.
7
9
  */
8
10
  export type Autonomy = "suggest" | "auto";
9
11
  export type PlaybookTrigger = {
@@ -29,6 +31,23 @@ export type PlaybookTrigger = {
29
31
  debounceMs?: number;
30
32
  } | {
31
33
  kind: "manual";
34
+ }
35
+ /**
36
+ * Iemand wijst werk aan de agent van deze playbook toe — de wekker van een **opdracht**.
37
+ *
38
+ * Geen nieuw mechanisme: een toewijzing is al een activity, dus dezelfde poort levert hem af.
39
+ * Wat deze soort toevoegt is de vraag die de engine niet zelf kan beantwoorden — *is dit aan
40
+ * míjn agent?* — en het antwoord staat in `Playbook.agentId`, niet hier. Eén plek die de
41
+ * agent benoemt, want dat veld bepaalt óók namens wie de run handelt; zou het hier nóg een
42
+ * keer staan, dan kon een playbook op de toewijzing aan de één reageren namens de ander.
43
+ *
44
+ * Wélke soorten werk toegewezen kunnen worden is **gedeclareerd** door de app die ze bezit
45
+ * (`assignment-source`-federatie, zie `./assignment.ts`) en staat daarom als losse id in
46
+ * `targetKind` in plaats van als union hier.
47
+ */
48
+ | {
49
+ kind: "assignment";
50
+ targetKind: string;
32
51
  };
33
52
  export interface Guardrails {
34
53
  maxSteps?: number;
@@ -56,11 +75,19 @@ export interface ConditionStep {
56
75
  /** Gate: what to do when the condition is false. "then" = the steps that follow. */
57
76
  onFalse: "stop" | "escalate" | "continue";
58
77
  }
59
- export interface ForEachStep {
78
+ /**
79
+ * Generiek over wat er in de body mag staan, zodat een beperkte staptaal een **echt subtype** kan
80
+ * zijn in plaats van een runtime-regel. Zie `ReadStep` in `../live-lens/types`: die laat hier
81
+ * alleen leesbare stappen in, en dan is "een leesbaan kan niet schrijven" een typefout in plaats
82
+ * van een controle die iemand kan vergeten aan te roepen.
83
+ *
84
+ * De default houdt elk bestaand gebruik (`ForEachStep` zonder parameter) ongewijzigd.
85
+ */
86
+ export interface ForEachStep<S = Step> {
60
87
  type: "for-each";
61
88
  in: string;
62
89
  as: string;
63
- body: Step[];
90
+ body: S[];
64
91
  }
65
92
  export interface LookupStep {
66
93
  type: "lookup";
@@ -75,13 +102,47 @@ export interface ActionStep {
75
102
  action: string;
76
103
  params: Record<string, unknown>;
77
104
  autonomy?: Autonomy;
105
+ /**
106
+ * Var waarin het resultaat van de actie landt, als je het nodig hebt.
107
+ *
108
+ * Bestond niet, en daardoor was er geen manier om te wachten op iets dat je zelf net had
109
+ * aangemaakt: een `create_task` kon haar id nergens laten, dus `waitingOn: { on: "task" }` was
110
+ * niet in te gaan. Dezelfde vorm als `LookupStep.as`, en dezelfde parsing — twee antwoorden op
111
+ * "wat ís het resultaat van een tool" zou de volgende duplicatie zijn.
112
+ *
113
+ * Onder autonomie `suggest` wordt de actie vastgehouden en draait ze pas na goedkeuring; het
114
+ * resultaat landt dan bij het hervatten. Tot dat moment bestaat de var niet, en een stap die
115
+ * er dan al naar verwijst faalt luid (zie `engine/utils.ts`) in plaats van met leegte verder
116
+ * te gaan.
117
+ */
118
+ as?: string;
78
119
  }
79
- /** AI steps — typed in Fase 0, executed from Fase 2. */
120
+ /**
121
+ * Wacht tot deze taak af is.
122
+ *
123
+ * De tegenhanger van `wait-for-reply`, voor het geval uit de support-procedure: "moet een
124
+ * collega iets doen, maak een taak en wacht tot die af is". `taskId` is een gewone verwijzing,
125
+ * dus `$newTask.id` na een `create_task` met een `as`.
126
+ */
127
+ export interface WaitForTaskStep {
128
+ type: "wait-for-task";
129
+ /** `$ref` of letterlijk id van de taak om op te wachten. */
130
+ taskId: string;
131
+ }
132
+ /**
133
+ * Een enkele classificatie met gestructureerde uitvoer.
134
+ *
135
+ * **`profileId` en niet `accountId` + `model`.** Dit was de enige AI-stap die zijn eigen account
136
+ * en model droeg, terwijl de kernkeuze van dit systeem is dat een `AIProfile` het herbruikbare
137
+ * brein is en AI-stappen daarnaar verwijzen (`docs/PLAYBOOKS.md` §2). Wat die uitzondering kostte:
138
+ * sjablonen hadden `$default`/`$defaultModel`-placeholders nodig die de installer moest
139
+ * vervangen, en een modelwissel raakte elke classify-stap van elke playbook afzonderlijk.
140
+ */
80
141
  export interface ClassifyStep {
81
142
  type: "classify";
82
143
  as: string;
83
- accountId: string;
84
- model: string;
144
+ /** Het brein dat classificeert. Een goedkoop, snel profiel is hier de bedoeling. */
145
+ profileId: string;
85
146
  mode: "topics" | "question" | "extract";
86
147
  topicIds?: string[];
87
148
  question?: string;
@@ -116,22 +177,147 @@ export interface GenerateStep {
116
177
  /** Plafond op de output van deze stap. Zie `ClassifyStep.maxTokens`. */
117
178
  maxTokens?: number;
118
179
  }
180
+ /**
181
+ * Een agentische beurt **binnen een flow**.
182
+ *
183
+ * Draagt een {@link Procedure} in plaats van diens velden nóg een keer: dit had
184
+ * `goal`/`toolsOverride`/`guardrails`/`maxTokens` als eigen velden, wat exact dezelfde vijf
185
+ * dingen waren onder twee namen — en `runAgentStep` was een adapter die `goal` op `text` en
186
+ * `toolsOverride` op `tools` afbeeldde. Compositie in plaats van een tweede naamgeving.
187
+ *
188
+ * `profileId` staat hiér en niet in de procedure: een workflow heeft geen agent, dus het brein
189
+ * moet uit de stap komen. Bij een **opdracht** komt het brein van de agent
190
+ * (`User.profileId`) en draagt de procedure er dus geen.
191
+ */
119
192
  export interface AgentStep {
120
193
  type: "agent";
194
+ /** Het brein voor deze stap. */
121
195
  profileId: string;
122
- goal: string;
123
- guardrails: Guardrails;
124
- toolsOverride?: string[];
196
+ /** Wat er moet gebeuren, en waarbinnen. */
197
+ procedure: Procedure;
125
198
  autonomy?: Autonomy;
126
199
  as?: string;
127
- /** Plafond op de output **per turn** van de loop. Zie `ClassifyStep.maxTokens`. */
128
- maxTokens?: number;
129
200
  }
130
201
  export interface WaitForReplyStep {
131
202
  type: "wait-for-reply";
132
203
  deadline?: number;
133
204
  }
134
- export type Step = ConditionStep | ForEachStep | LookupStep | ActionStep | ClassifyStep | GenerateStep | AgentStep | WaitForReplyStep;
205
+ /**
206
+ * Meerdere takken tegelijk, en pas verder als ze allemaal klaar zijn.
207
+ *
208
+ * Bestaat voor één concreet probleem: stappen liepen strikt sequentieel, en drie lookups van
209
+ * elk ~1200 ms achter elkaar passen niet in een cadans van seconden (live assist). Sequentieel
210
+ * kost de som, parallel kost de langzaamste.
211
+ *
212
+ * **Alleen lezen.** Een tak mag niet pauzeren — geen goedkeuring, geen wachten op een antwoord.
213
+ * Dat is geen tijdelijke beperking maar een grens die het begrip eenvoudig houdt: een run die
214
+ * halverwege een fan-out pauzeert zou moeten onthouden welke takken al klaar waren en waar hij
215
+ * verder moet, en dat is een tweede toestandsmachine naast `waitingOn`. Wie iets wil schrijven
216
+ * doet dat in een stap ná de `parallel`.
217
+ *
218
+ * Elke tak schrijft naar zijn eigen `as`-namen; twee takken die dezelfde naam vullen is een
219
+ * fout, want wie er wint hangt dan van de timing af.
220
+ */
221
+ export interface ParallelStep<S = Step> {
222
+ type: "parallel";
223
+ /** Elke tak is een eigen rijtje stappen. Zie {@link ForEachStep} over de parameter. */
224
+ branches: S[][];
225
+ /**
226
+ * Hoeveel takken er tegelijk mogen lopen. Leeg = alle.
227
+ *
228
+ * Een plafond bestaat omdat een tak een tool-call is: tien takken tegelijk zijn tien
229
+ * gelijktijdige aanroepen naar andere apps.
230
+ */
231
+ maxConcurrent?: number;
232
+ /**
233
+ * Wat een falende tak betekent. Default `"fail"`: de stap faalt.
234
+ *
235
+ * `"continue"` laat de andere takken staan en gaat door — dat is wat een leesoppervlak wil
236
+ * (één kapotte baan mag de andere twee niet wissen), en precies waarom het een keuze is en
237
+ * geen aanname.
238
+ */
239
+ onBranchError?: "fail" | "continue";
240
+ }
241
+ export type Step = ConditionStep | ForEachStep | ParallelStep | LookupStep | ActionStep | ClassifyStep | GenerateStep | AgentStep | WaitForReplyStep | WaitForTaskStep;
242
+ /**
243
+ * Een **procedure**: hoe een scenario aangepakt wordt, als prose met guardrails.
244
+ *
245
+ * Dit is de tweede uitvoeringsvorm naast `Step[]`, en {@link AgentStep} is er de
246
+ * variant-binnen-een-flow van (die *bevat* er een, en herhaalt zijn velden niet).
247
+ *
248
+ * **Geen `profileId`.** Dat stond hier, en het was de tweede brein-verwijzing in het systeem:
249
+ * `User.profileId` bestaat al — verplicht bij het aanmaken van een agent, met als doc "het brein
250
+ * van een agent" — en de engine las hem nooit. Twee velden voor één feit, zonder dat iets ze aan
251
+ * elkaar relateerde: `agentId: Alice` met het brein van Bob werd zonder klacht opgeslagen, en de
252
+ * tijdlijn zei dan Alice terwijl het model, de tools en de prompt van Bob waren.
253
+ *
254
+ * Nu is er één antwoord op "met welk brein": bij een opdracht dat van de agent, bij een
255
+ * agent-stap in een workflow dat van de stap. De procedure beschrijft het scenario, niet wie het
256
+ * uitvoert.
257
+ *
258
+ * `tools` is **narrowing-only**: het moet een deelverzameling zijn van `enabledTools` van het
259
+ * brein dat hem uitvoert. Het profiel is de identiteit (één per agent), de procedure het scenario
260
+ * (veel per agent), en een scenario mag nooit méér mogen dan de identiteit die het uitvoert.
261
+ */
262
+ export interface Procedure {
263
+ /** De prose zelf — wat er moet gebeuren, in de woorden van wie het opschreef. */
264
+ text: string;
265
+ /** Toegestane tools. Narrowing-only t.o.v. `enabledTools` van het brein. */
266
+ tools?: string[];
267
+ guardrails?: Guardrails;
268
+ /** Plafond per beurt. Zie `ClassifyStep.maxTokens`. */
269
+ maxTokens?: number;
270
+ }
271
+ /**
272
+ * Wie kiest de volgende zet.
273
+ *
274
+ * Twee uitvoeringsvormen, één contract. Beide lopen door dezelfde toolruntime en dezelfde
275
+ * goedkeuringspoort; alleen de manier waarop de volgende zet gekozen wordt verschilt:
276
+ *
277
+ * - **workflow** — deterministisch, `Step[]`, gebouwd op de canvas. Routing, triage,
278
+ * achtergrondautomatisering. Een gratis, testbare poort in plaats van een prompt.
279
+ * - **procedure** — prose, een model kiest. Voor werk waarvan de stappen geen takken in een
280
+ * boom zijn (antwoorden *én* een taak maken, of eerst vragen en daarna alsnog antwoorden).
281
+ *
282
+ * Verkeerd om gebruikt faalt elke vorm op zijn eigen manier: een procedure als workflow
283
+ * verspreidt logica over blokken tot het onbeheersbaar is, een workflow als procedure ruilt een
284
+ * testbare conditie voor een onbetrouwbare prompt (en kost een modelaanroep per bericht).
285
+ */
286
+ export type Decider = {
287
+ kind: "workflow";
288
+ steps: Step[];
289
+ } | {
290
+ kind: "procedure";
291
+ procedure: Procedure;
292
+ };
293
+ /**
294
+ * **Eén veld, met de soort erin.** Dit was een paar losse kolommen — `steps: Step[]` naast
295
+ * `procedure?: Procedure | null` — met een functie (`deciderOf`) die er een union van maakte
296
+ * volgens de regel "procedure aanwezig ⇒ procedure".
297
+ *
298
+ * Wat die vorm kostte:
299
+ *
300
+ * - een rij met **beide** gevuld was representeerbaar, en betekende stil "de steps zijn dood";
301
+ * - elk schrijfpad moest de onuitgesproken regel kennen: `crud/update.ts` moest expliciet `null`
302
+ * schrijven om "de procedure eruit halen" te laten werken, en `run-playbook.ts` had een
303
+ * conditionele spread om de kolom weg te laten;
304
+ * - de frontend had de regel nóg een keer (`!!pb.procedure` als soort-filter);
305
+ * - en het is al een keer stil fout gegaan: `fromView` droeg `procedure` niet mee, waardoor élke
306
+ * procedure die je opende en opsloeg zichzelf tot een lege workflow maakte.
307
+ *
308
+ * Met `kind` in de kolom is de soort een feit in plaats van een gevolgtrekking, en kan er geen
309
+ * derde toestand bestaan. De editor houdt zijn platte vorm — dat is wat `toView`/`fromView` doen.
310
+ */
311
+ export declare const EMPTY_WORKFLOW: Decider;
312
+ /**
313
+ * De stappen van deze decider, of een lege lijst als het een procedure is.
314
+ *
315
+ * Twee lezers-helpers en géén `deciderOf` terug: het verschil is dat deze de soort *lezen* in
316
+ * plaats van hem te *raden* uit welke velden gevuld zijn.
317
+ */
318
+ export declare function stepsOf(decider: Decider | undefined): Step[];
319
+ /** De procedure van deze decider, of `undefined` als het een workflow is. */
320
+ export declare function procedureOf(decider: Decider | undefined): Procedure | undefined;
135
321
  export interface Playbook {
136
322
  id: string;
137
323
  organizationId: string;
@@ -141,7 +327,20 @@ export interface Playbook {
141
327
  enabled: boolean;
142
328
  autonomy: Autonomy;
143
329
  trigger: PlaybookTrigger;
144
- steps: Step[];
330
+ /** Wie de volgende zet kiest: stappen of prose. Zie {@link Decider}. */
331
+ decider: Decider;
332
+ /**
333
+ * De agent die dit uitvoert: het `userId` van een gebruiker met `type: "agent"`.
334
+ *
335
+ * Bepaalt **namens wie** de run handelt (`runActor`), en daarmee: wiens naam er in de tijdlijn
336
+ * staat, welke interacties hij mag raken, en van wiens budget het afgaat. Bewust náást
337
+ * `ownerScope` en niet erin — dat veld bepaalt wie de playbook mag *beheren*, en dat blijven
338
+ * mensen. Een agent logt nooit in, dus als beide op één veld leunden kon niemand hem meer
339
+ * aanpassen.
340
+ *
341
+ * Leeg = de actor volgt de scope, precies zoals voorheen.
342
+ */
343
+ agentId?: string | null;
145
344
  /** Bumped on every change; runs pin to the version they started on. */
146
345
  version: number;
147
346
  /** Actor of autonomous actions. */
@@ -158,19 +357,106 @@ export interface Playbook {
158
357
  * de `PLAYBOOK_STARTED`-activity dekt "hij is begonnen" al. (Die status bestond,
159
358
  * werd nooit geproduceerd, en had wél een label in de UI.)
160
359
  */
161
- export type RunStatus = "running" | "awaiting_approval" | "awaiting_reply" | "done" | "escalated" | "failed" | "timed_out" | "stopped";
360
+ export type RunStatus = "running" | "waiting" | "done" | "escalated" | "failed" | "stopped";
361
+ /**
362
+ * Waaróp een wachtende run wacht.
363
+ *
364
+ * Dit was eerder statusinformatie: `awaiting_approval`, `awaiting_reply` en `timed_out` waren
365
+ * drie statussen die alle drie "wacht op X" betekenden, en de reden zat in de naam. Daardoor
366
+ * groeide de statusset met elke nieuwe reden mee — een run die op een taak wacht zou
367
+ * `awaiting_task` hebben gevraagd, een run die op een moment wacht `awaiting_time`, en dan
368
+ * moest elke lezer van `status` opnieuw worden bijgewerkt.
369
+ *
370
+ * De reden van het wachten is **data**, geen status. `status: "waiting"` zegt dát er gewacht
371
+ * wordt; dit veld zegt waarop, en wie het signaal levert leest alleen dit.
372
+ *
373
+ * `timed_out` staat er niet bij: een verstreken deadline is geen manier van wachten maar een
374
+ * uitkomst, en landt via `decideFinalState` op een eindstand (zie {@link TerminalLifecycle}).
375
+ *
376
+ * Er is ook geen `{ on: "time" }`. Die stond hier, zonder dat iets hem schreef of las: het
377
+ * wachten-op-een-moment dat wél bestaat is de timeout-job die bij `{ on: "reply" }` wordt
378
+ * ingeplanned, en die leest de deadline uit de job en niet uit dit veld. Een lid dat door niets
379
+ * geproduceerd wordt is een belofte aan elke lezer die hem moet afhandelen.
380
+ */
381
+ export type WaitingOn = {
382
+ on: "approval";
383
+ }
384
+ /** De klant antwoordt, of een collega beantwoordt de interne vraag van de run. */
385
+ | {
386
+ on: "reply";
387
+ interactionId: string;
388
+ } | {
389
+ on: "task";
390
+ taskId: string;
391
+ };
162
392
  /** Eindstanden: hier komt een run niet meer vanzelf uit. */
163
- export declare const TERMINAL_RUN_STATUSES: readonly ["done", "escalated", "failed", "timed_out", "stopped"];
164
- /** Wacht deze run op iets externs? */
165
- export declare const PAUSED_RUN_STATUSES: readonly ["awaiting_approval", "awaiting_reply"];
393
+ export declare const TERMINAL_RUN_STATUSES: readonly ["done", "escalated", "failed", "stopped"];
394
+ /**
395
+ * **Tweede vocabulaire, expliciet.** Waarom een run eindigde, zoals de tijdlijn het benoemt —
396
+ * niet hetzelfde als de status die op de run staat.
397
+ *
398
+ * Twee redenen zijn geen `RunStatus`, en dat is geen omissie:
399
+ *
400
+ * - **`rejected`** — een afgewezen voorstel landt als status `"stopped"`, maar de tijdlijn moet
401
+ * "afgewezen" kunnen zeggen.
402
+ * - **`timed_out`** — een run waarvan de deadline verstreek landt als `"escalated"` (er moet een
403
+ * mens iets mee), maar "geen antwoord binnen de deadline" is een andere mededeling dan
404
+ * "geëscaleerd". Dit was eerder een eigen status; hij is verhuisd naar dit vocabulaire, want
405
+ * het is een *reden* en geen toestand waar een run in kan verkeren.
406
+ *
407
+ * Dat dit vocabulaire rijker is dan de statusset is precies waarom het apart bestaat: de
408
+ * tijdlijn vertelt een mens wát er gebeurde, `status` vertelt de machine wat er nog te doen is.
409
+ */
410
+ export type TerminalLifecycle = RunStatus | "rejected" | "timed_out";
411
+ /**
412
+ * Is deze run klaar — en daarmee ook: mag hij opnieuw gedraaid worden?
413
+ *
414
+ * Die tweede vraag had een eigen functie (`isRetryable`) die niets anders deed dan deze
415
+ * aanroepen. Twee namen voor één regel betekent dat een lezer moet uitzoeken of er een verschil
416
+ * is; dat er geen was, was niet te zien zonder de body te openen.
417
+ *
418
+ * De regel zelf staat hier dus één keer: een retry mag op precies de eindstanden. Een lopende of
419
+ * mens-gepauzeerde run blijft met rust, zodat een retry nooit over live uitvoering heen loopt of
420
+ * een openstaand voorstel overschrijft.
421
+ */
166
422
  export declare function isTerminal(status: RunStatus | string): boolean;
167
423
  /**
168
- * Statussen waarvan een run opnieuw gestart mag worden. Een lopende of
169
- * mens-gepauzeerde run blijft met rust, zodat een retry nooit over live uitvoering
170
- * heen loopt of een openstaand voorstel overschrijft — vandaar: precies de eindstanden.
424
+ * Wacht deze run op iets externs?
425
+ *
426
+ * Eén statusvergelijking en niet meer een lijst: er was een `PAUSED_RUN_STATUSES` met twee
427
+ * leden omdat de reden van het wachten ín de status zat. Nu staat de reden in
428
+ * {@link WaitingOn} en is "wacht" één toestand — dus een nieuwe reden (een taak, een moment)
429
+ * verandert deze functie niet meer.
171
430
  */
172
- export declare function isRetryable(status: RunStatus | string): boolean;
173
431
  export declare function isPaused(status: RunStatus | string): boolean;
432
+ /**
433
+ * Loopt deze run nog — mag `finalizeRun` er een uitkomst over schrijven?
434
+ *
435
+ * "In de lucht" is precies "niet in een eindstand": `running` of `waiting`. De lock-retry in
436
+ * `jobs/context.ts` herschedulet tot vijf keer, dus zonder deze check kan een late job met een
437
+ * verouderde uitkomst over een al afgeronde run schrijven.
438
+ */
439
+ export declare function isInFlight(status: RunStatus | string): boolean;
440
+ /**
441
+ * Het onderwerp van een run als één verwijzing, of `undefined` als hij er geen heeft.
442
+ *
443
+ * Zo'n run bestaat: een org-brede playbook die een rollup doet heeft geen resource om over te
444
+ * gaan. Dat is een geldige toestand en geen ontbrekend veld.
445
+ */
446
+ export declare function subjectOf(run: {
447
+ subjectKind?: string;
448
+ subjectId?: string;
449
+ }): ResourceRef | undefined;
450
+ /** De scope-sleutel van het onderwerp, klaar voor `authorizeScope`. */
451
+ export declare function subjectKeyOf(run: {
452
+ subjectKind?: string;
453
+ subjectId?: string;
454
+ }): string | undefined;
455
+ /** Het interactie-id van deze run, als zijn onderwerp een gesprek is. */
456
+ export declare function runInteractionId(run: {
457
+ subjectKind?: string;
458
+ subjectId?: string;
459
+ }): string | undefined;
174
460
  export interface RunStepTrace {
175
461
  path: string;
176
462
  type: string;
@@ -178,22 +464,83 @@ export interface RunStepTrace {
178
464
  note?: string;
179
465
  ms?: number;
180
466
  }
467
+ /**
468
+ * Wat deze run in beweging zette.
469
+ *
470
+ * Was `createdBy: string`, en dat veld droeg de auteur van de **playbook** — niet wie de run
471
+ * startte. Dus "wie heeft dit gedaan" had geen antwoord, terwijl `run/start.ts` het userId van de
472
+ * mens die op start drukte al meestuurde in de job-data, waar niemand het las.
473
+ */
474
+ export type StartedBy =
475
+ /** Een mens drukte op start (`POST /playbook-run/start`). */
476
+ {
477
+ kind: "user";
478
+ id: string;
479
+ }
480
+ /** De trigger vuurde: een activity, een toewijzing, een afgeronde taak. */
481
+ | {
482
+ kind: "trigger";
483
+ id?: string;
484
+ };
181
485
  export interface PlaybookRun {
182
486
  id: string;
183
487
  organizationId: string;
184
488
  playbookId: string;
185
489
  playbookVersion: number;
186
- interactionId?: string;
490
+ /**
491
+ * De opdracht waarvan dit een **beurt** is, als er een is.
492
+ *
493
+ * Afwezig = een losstaande uitvoering (een automatisering die vuurt en klaar is). Aanwezig = één
494
+ * zet in werk dat langer leeft dan deze run, met een draad als geheugen.
495
+ */
496
+ assignmentId?: string;
497
+ /**
498
+ * Waar deze run **over gaat**, in twee platte kolommen.
499
+ *
500
+ * Dit was `interactionId?: string`, en dat leek onschuldig — het veld was immers optioneel. Maar
501
+ * vier paden leunden er zwijgend op alsof het verplicht was: goedkeuren gooide `400` zonder
502
+ * interactie, de lijst filterde op `audience` (leeg voor een org-actor, dus onvindbaar), de
503
+ * tijdlijn deed niets, en de taak-wekker eiste een gesprek. Precies het geval waar de
504
+ * toewijs-federatie voor bestaat — werk dat géén gesprek is — viel dus in dat gat.
505
+ *
506
+ * **Twee kolommen en geen genest object**, om dezelfde reden als {@link WaitingOn} zijn
507
+ * `waitingOnKind` heeft: de store kan niet in een genest object zoeken, en hier moet op gezocht
508
+ * worden (de runs van dit gesprek, de runs van deze taak). `subjectOf` maakt er een
509
+ * {@link ResourceRef} van waar je hem als geheel nodig hebt.
510
+ */
511
+ subjectKind?: string;
512
+ subjectId?: string;
187
513
  status: RunStatus;
514
+ /**
515
+ * De aanleiding: wélk signaal deze run startte, plus de invoer zoals die er tóen uitzag.
516
+ *
517
+ * **`vars` hier is niet hetzelfde als `vars.trigger` hieronder, en dat is met opzet.** Dit veld
518
+ * is de *onveranderlijke invoer*; `vars` is de *werkkopie* die tijdens de run muteert — stappen
519
+ * schrijven erin, en resume-on-reply overschrijft `vars.trigger` met het nieuwe bericht. Een
520
+ * retry moet schoon herrekenen vanaf het oorspronkelijke signaal, en dat kan alleen als dat
521
+ * signaal ergens bewaard is gebleven.
522
+ *
523
+ * Het veld heette `event`, wat suggereerde dat het de rauwe activity was; het zijn de
524
+ * trigger-vars. Dezelfde vorm als `vars.trigger` betekent hier dus niet dezelfde betekenis.
525
+ */
188
526
  trigger: {
189
527
  activityType?: string;
190
528
  activityId?: string;
191
- event: Record<string, unknown>;
529
+ vars: Record<string, unknown>;
192
530
  };
193
531
  vars: Record<string, unknown>;
194
532
  trace: RunStepTrace[];
195
- /** Snapshot of the playbook steps at run start — version-pinned resume (docs 11.4). */
196
- steps?: Step[];
533
+ /**
534
+ * De uitvoeringsvorm, vastgepind bij de start: de playbook bijstellen mag een lopende of
535
+ * geparkeerde run niet halverwege van gedrag of opdracht laten wisselen (docs 11.4).
536
+ */
537
+ decider: Decider;
538
+ /**
539
+ * De agent die deze run uitvoert, vastgepind — en daarmee ook **met welk brein**: dat is
540
+ * `User.profileId` van deze agent. Afwezig bij een workflow zonder agent; dan draagt een
541
+ * agent-stap zijn eigen `profileId`.
542
+ */
543
+ agentId?: string | null;
197
544
  /**
198
545
  * Namens wie deze run handelt, vastgepind bij de start — net als `steps`. Anders
199
546
  * wisselt een lopende of geparkeerde run stilletjes van identiteit zodra iemand de
@@ -209,19 +556,51 @@ export interface PlaybookRun {
209
556
  pending?: {
210
557
  action: string;
211
558
  params: Record<string, unknown>;
559
+ as?: string;
212
560
  }[] | null;
213
561
  /** The AI_ACTION_PROPOSED activity created for the current pending batch (for resolve on approve/reject). */
214
562
  proposalActivityId?: string | null;
215
563
  /**
216
- * Index van de stap waarop de flow pauzeerde — voor zowel `awaiting_approval` als
217
- * `awaiting_reply`. (Was `resumeIndex` naast `pending.stepIndex`: twee velden voor
564
+ * Waarop deze run wacht, als `status === "waiting"`. `null` wanneer het veld gewist is (de
565
+ * store schrijft `null`), en afwezig bij een run die niet wacht.
566
+ *
567
+ * Vervangt de drie `awaiting_*`/`timed_out`-statussen: de reden van het wachten is data, dus
568
+ * een nieuwe reden kost een lid in {@link WaitingOn} en niet een status die elke lezer moet
569
+ * kennen.
570
+ */
571
+ waitingOn?: WaitingOn | null;
572
+ /**
573
+ * De **soort** van {@link WaitingOn}, als platte kolom — puur om op te kunnen queryen.
574
+ *
575
+ * `waitingOn` is een genest object en de store kan daar niet in zoeken, dus elke wekker deed
576
+ * `status: "waiting"` en filterde daarna in het geheugen op `run.waitingOn?.on`
577
+ * (`jobs/run-playbook.ts`, `triggers/from_tasks.ts`). Dat is goedkoop bij tien wachtende runs
578
+ * en niet bij tienduizend. Afgeleid en op één plek geschreven (`finalizeRun`), zodat hij niet
579
+ * uit de pas kan lopen met het object ernaast.
580
+ */
581
+ waitingOnKind?: WaitingOn["on"] | null;
582
+ /**
583
+ * Index van de stap waarop de flow pauzeerde — voor elke vorm van wachten.
584
+ * (Was `resumeIndex` naast `pending.stepIndex`: twee velden voor
218
585
  * hetzelfde feit, in twee takken geschreven en in twee takken gelezen.)
586
+ *
587
+ * Heette `pausedAt`, wat een tijdstip suggereerde tussen velden die dat écht zijn
588
+ * (`lastStepAt`, `lastRunAt`, `deadline`, `until`). Het is een stap-index.
219
589
  */
220
- pausedAt?: number | null;
590
+ resumeAtStep?: number | null;
221
591
  rounds?: number;
222
- /** Denormalized interaction audience for efficient list-filtering. */
223
- audience?: string[];
224
- createdBy: string;
592
+ /**
593
+ * Wie deze run in zijn lijst ziet — een **index**, geen autorisatie.
594
+ *
595
+ * Heette `audience`, en dat woord suggereerde een rechtenlijst; hij werd ook zo gebruikt in de
596
+ * losse tak van `authorizeRunScope` terwijl de interactie-tak hem juist bewust negeerde (de
597
+ * denormalisatie verouderd zodra een gesprek van inbox wisselt). Autorisatie komt nu van het
598
+ * onderwerp of van de scope van de definitie; dit veld is er alleen om "mijn runs" te kunnen
599
+ * queryen.
600
+ */
601
+ visibleTo?: string[];
602
+ /** Wie deze run in beweging zette. Zie {@link StartedBy}. */
603
+ startedBy: StartedBy;
225
604
  approvedBy?: string | null;
226
605
  result?: {
227
606
  ok: boolean;
@@ -3,11 +3,7 @@
3
3
  * so read-state (and, later, an audit log) can attach to any resource -
4
4
  * interactions, activities, files, kb articles - without per-type schemas.
5
5
  */
6
- /** Polymorphic reference to any resource that can carry read-state. */
7
- export interface ResourceRef {
8
- type: string;
9
- id: string;
10
- }
6
+ export type { ResourceRef } from '../../platform/resource';
11
7
  /**
12
8
  * A single user's high-water read mark on one resource. `lastReadAt` is the
13
9
  * epoch (ms) up to which this user has read the resource; whether they have
@@ -1,8 +1,64 @@
1
+ /**
2
+ * Mens of agent.
3
+ *
4
+ * Een agent is **geen apart begrip naast de gebruiker** maar een gebruiker met een ander type.
5
+ * Dat is een keuze met een concreet doel: hij krijgt een echt `userId`, dus hij loopt door
6
+ * `authorizeIdentity` op het fail-closed pad, hij tekent zijn activities met zijn eigen naam in
7
+ * plaats van als "system", hij kan lid van een team zijn (`getReadableTeamIds` werkt), en hij
8
+ * verbruikt van zijn eigen per-user-budget. Elk van die dingen zou anders een uitzondering zijn
9
+ * geweest, en elke uitzondering is een plek waar een audit hem kwijtraakt.
10
+ *
11
+ * Afwezig betekent `"user"`: bestaande rijen zijn mensen, en de mirror wordt niet gebackfilld.
12
+ */
13
+ export type UserType = "user" | "agent";
14
+ /**
15
+ * Wat een ander oppervlak van een gebruiker mag weten om hem te **tónen**.
16
+ *
17
+ * Los van {@link User}, en dat is het punt: een naam opzoeken is iets anders dan een gebruiker
18
+ * beheren. De beheerroute geeft het hele record plus zijn rollen (een extra RPC naar
19
+ * `system.rbac`) — dat is precies wat je niet wil als je alleen een auteursnaam in een tijdlijn
20
+ * zet: het kost een call die niemand nodig heeft en het deelt e-mail en rollen met een app die
21
+ * daar niets mee te maken heeft.
22
+ *
23
+ * `type` zit er wél in: een tijdlijn of een deelnemerslijst wil een agent kunnen markeren.
24
+ */
25
+ export interface UserProfile {
26
+ id: string;
27
+ name: string;
28
+ displayName?: string;
29
+ avatarUrl?: string;
30
+ type?: UserType;
31
+ /**
32
+ * Het brein van een agent, als dit een agent is — zie {@link User.profileId}.
33
+ *
34
+ * Hoort bij dit compacte record en niet alleen bij het volledige: "is dit een agent" staat er
35
+ * al (`type`), en "met welk brein" is de directe vervolgvraag van elke beller die er werk aan
36
+ * geeft. Zonder dit veld moest de ai-app de beheerroute gebruiken — het hele record plus een
37
+ * RPC naar `system.rbac` — om één id te weten.
38
+ */
39
+ profileId?: string;
40
+ }
41
+ /** Is deze gebruiker een agent? Afwezig type = mens. */
42
+ export declare function isAgentUser(user: {
43
+ type?: UserType | string;
44
+ }): boolean;
1
45
  export interface User {
2
46
  id: string;
3
47
  name: string;
4
48
  email: string;
5
49
  availableOrganizations: string[];
50
+ /**
51
+ * Mens of agent. Zie {@link UserType}.
52
+ *
53
+ * Staat op de **lokale mirror** en niet in `system.users`: dat is het platform-record en dat
54
+ * kennen we niet. Alles wat "is dit een mens" moet weten leest dit veld.
55
+ */
56
+ type?: UserType;
57
+ /**
58
+ * Het brein van een agent: het `AIProfile` waarmee hij werkt. Alleen gezet als
59
+ * `type === "agent"`.
60
+ */
61
+ profileId?: string;
6
62
  /** Mirrored from system.users. */
7
63
  firstName?: string;
8
64
  lastName?: string;