@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.
Files changed (84) hide show
  1. package/dist/entities/activity/blocks.d.ts +30 -30
  2. package/dist/entities/activity/catalog.d.ts +63 -86
  3. package/dist/entities/activity/descriptor.d.ts +31 -32
  4. package/dist/entities/activity/index.d.ts +2 -2
  5. package/dist/entities/activity/preview.d.ts +7 -7
  6. package/dist/entities/activity/resolve.d.ts +37 -38
  7. package/dist/entities/activity/types.d.ts +64 -35
  8. package/dist/entities/ai-account/types.d.ts +3 -3
  9. package/dist/entities/ai-account/vendors.d.ts +25 -32
  10. package/dist/entities/ai-budget/types.d.ts +5 -5
  11. package/dist/entities/ai-conversation/types.d.ts +12 -12
  12. package/dist/entities/ai-profile/types.d.ts +14 -21
  13. package/dist/entities/ai-settings/types.d.ts +12 -13
  14. package/dist/entities/analytics/index.d.ts +4 -4
  15. package/dist/entities/analytics/source.d.ts +1 -1
  16. package/dist/entities/analytics/usage.d.ts +23 -28
  17. package/dist/entities/artifact/blocks.d.ts +54 -69
  18. package/dist/entities/artifact/markdown.d.ts +2 -3
  19. package/dist/entities/artifact/types.d.ts +46 -54
  20. package/dist/entities/assignment/types.d.ts +76 -90
  21. package/dist/entities/channel/index.d.ts +1 -1
  22. package/dist/entities/channel/signature.d.ts +8 -9
  23. package/dist/entities/communication/index.d.ts +1 -1
  24. package/dist/entities/contact/index.d.ts +1 -1
  25. package/dist/entities/custom-field-def/types.d.ts +14 -14
  26. package/dist/entities/external-identity/types.d.ts +45 -50
  27. package/dist/entities/interaction/index.d.ts +1 -1
  28. package/dist/entities/interaction/types.d.ts +44 -47
  29. package/dist/entities/live-lens/types.d.ts +39 -44
  30. package/dist/entities/mcp/types.d.ts +53 -57
  31. package/dist/entities/memory/alias.d.ts +7 -7
  32. package/dist/entities/memory/browse.d.ts +21 -21
  33. package/dist/entities/memory/ingest.d.ts +24 -28
  34. package/dist/entities/memory/item.d.ts +53 -58
  35. package/dist/entities/memory/kind.d.ts +25 -25
  36. package/dist/entities/memory/query.d.ts +49 -51
  37. package/dist/entities/playbook/actor.d.ts +17 -17
  38. package/dist/entities/playbook/assignment.d.ts +54 -63
  39. package/dist/entities/playbook/index.d.ts +2 -2
  40. package/dist/entities/playbook/labels.d.ts +4 -4
  41. package/dist/entities/playbook/trigger-vars.d.ts +17 -15
  42. package/dist/entities/playbook/types.d.ts +174 -303
  43. package/dist/entities/resource-reminder/types.d.ts +8 -9
  44. package/dist/entities/time-entry/duration.d.ts +11 -11
  45. package/dist/entities/time-entry/types.d.ts +59 -59
  46. package/dist/entities/time-entry/work-type.d.ts +21 -24
  47. package/dist/entities/topic/examples.d.ts +26 -27
  48. package/dist/entities/topic/scope.d.ts +11 -12
  49. package/dist/entities/topic/types.d.ts +26 -28
  50. package/dist/entities/user/types.d.ts +24 -28
  51. package/dist/entities/webhook/types.d.ts +6 -6
  52. package/dist/entities/work/activity.d.ts +36 -0
  53. package/dist/entities/work/index.d.ts +1 -0
  54. package/dist/entities/work/keys.d.ts +25 -26
  55. package/dist/entities/work/ladder.d.ts +48 -52
  56. package/dist/entities/work/types.d.ts +108 -114
  57. package/dist/index.cjs +6 -6
  58. package/dist/index.d.ts +29 -28
  59. package/dist/index.js +1416 -1258
  60. package/dist/platform/account.d.ts +34 -38
  61. package/dist/platform/ai-tools.d.ts +48 -52
  62. package/dist/platform/api.d.ts +9 -10
  63. package/dist/platform/author.d.ts +7 -9
  64. package/dist/platform/capabilities.d.ts +3 -3
  65. package/dist/platform/communication.d.ts +163 -204
  66. package/dist/platform/context.d.ts +8 -8
  67. package/dist/platform/identity.d.ts +16 -19
  68. package/dist/platform/kernel.d.ts +1 -1
  69. package/dist/platform/manifest.d.ts +2 -2
  70. package/dist/platform/media.d.ts +13 -15
  71. package/dist/platform/permission.d.ts +39 -0
  72. package/dist/platform/permission.test.d.ts +1 -0
  73. package/dist/platform/presence.d.ts +30 -30
  74. package/dist/platform/provider.d.ts +14 -15
  75. package/dist/platform/resource-source.d.ts +43 -45
  76. package/dist/platform/resource.d.ts +17 -23
  77. package/dist/platform/scope.d.ts +35 -61
  78. package/dist/platform/services.d.ts +4 -4
  79. package/dist/platform/storage.d.ts +1 -1
  80. package/dist/platform/sync-source.d.ts +196 -216
  81. package/dist/platform/transcript-cadence.d.ts +37 -41
  82. package/dist/platform/ui.d.ts +6 -6
  83. package/dist/text/endpoint.d.ts +11 -27
  84. package/package.json +3 -2
@@ -1,150 +1,136 @@
1
1
  import { ResourceRef } from '../../platform/resource';
2
- import { Activity } from '../activity/types';
2
+ import { FeedActivity } from '../activity/types';
3
3
  import { WaitingOn } from '../playbook/types';
4
4
  /**
5
- * Een **opdracht**: werk dat aan een agent is toegewezen en zo lang leeft als het werk.
5
+ * An **assignment**: work handed to an agent, living as long as the work does.
6
6
  *
7
- * **Waarom dit een entity is.** Het was er geen: `plans/unify-ai.md` besliste "een opdracht *is*
8
- * een run met een procedure, een agent als actor en een lange levensduur". Dat is verdedigbaar
9
- * gedacht, en de code liet zien wat het kostte:
7
+ * **Why this is an entity.** It was not one — an assignment used to be a run with a procedure, an
8
+ * agent as actor and a long lifetime. Defensible, and the code showed what it cost: every
9
+ * procedure run was cut short at `{ status: "done" }`, so an assignment was structurally **one
10
+ * model turn**; the thread shared no memory with the agent, so what a person wrote in it never
11
+ * reached the agent and the other way round; `retry` meant "redo the whole assignment from the
12
+ * original trigger", sensible for a workflow of seconds and nonsense for work running for days;
13
+ * and "what kind of thing is this" had to be derived from the execution shape.
10
14
  *
11
- * - `resume-playbook.ts` kortte elke procedure-run af op `{ status: "done" }`, dus een opdracht was
12
- * structureel **één modelbeurt**;
13
- * - de draad (een `AIConversation` op `playbook-run:<id>`) deelde géén geheugen met de agent:
14
- * `runProcedure` gaf bewust geen `conversationId` mee, dus wat een mens in de draad zei bereikte
15
- * de agent nooit en omgekeerd;
16
- * - `retry` betekende "doe de hele opdracht opnieuw vanaf de originele trigger" — zinnig voor een
17
- * workflow van seconden, onzin voor werk dat dagen liep;
18
- * - en "wat voor ding is dit" moest uit de *uitvoeringsvorm* worden afgeleid
19
- * (`decider.kind === "procedure"`), dus de vorm deed dienst als producttype.
20
- *
21
- * Nu is een opdracht wat hij is, en een run wat die is: **één beurt**. De opdracht leeft, de runs
22
- * zijn wat hij deed.
15
+ * Now an assignment is what it is, and a run is what it is: **one turn**.
23
16
  */
24
17
  /**
25
- * Waar een opdracht in verkeert.
18
+ * The state an assignment is in.
26
19
  *
27
- * Bewust dezelfde woorden als de eindstanden van een run, en bewust minder: een opdracht kan niet
28
- * `failed` zijn. Een gefaalde beurt is een gefaalde run — de opdracht staat dan nog open, want er
29
- * is niets afgehandeld. Dat verschil is precies waarom dit een eigen vocabulaire is.
20
+ * Deliberately the same words as a run's end states, and deliberately fewer: an assignment cannot
21
+ * be `failed`. A failed turn is a failed run — the assignment is then still open, because nothing
22
+ * was handled. That difference is exactly why this is its own vocabulary.
30
23
  */
31
24
  export type AssignmentStatus = "open" | "waiting" | "done" | "escalated";
32
25
  export declare const TERMINAL_ASSIGNMENT_STATUSES: readonly ["done", "escalated"];
33
26
  export declare function isAssignmentTerminal(status: AssignmentStatus | string): boolean;
34
27
  /**
35
- * De scope-soort waaronder de draad van een opdracht leeft.
36
- *
37
- * Was `playbook-run:<id>`, want de draad hing aan een run. Hij hangt nu aan de opdracht, en dat is
38
- * het hele punt: een draad die stopt zodra één beurt klaar is, is geen draad.
28
+ * The scope kind an assignment's thread lives under. It hangs on the assignment, not on a
29
+ * single run — a thread that stops when one turn finishes is no thread.
39
30
  *
40
- * Staat in domain omdat het een **contract tussen client en server** is: de client bouwt de
41
- * sleutel om de draad te openen, de server autoriseert hem. Twee plekken die het voorvoegsel los
42
- * uitschrijven lopen bij de eerste typefout stil uiteen — en het gevolg daarvan is "geen toegang
43
- * tot deze scope", niet een foutmelding die het zegt.
31
+ * In domain because it is a **contract between client and server**: the client builds the key to
32
+ * open the thread, the server authorizes it. Two places spelling the prefix out separately drift
33
+ * apart silently on the first typo — and the result of that is "no access to this scope", not an
34
+ * error message saying so.
44
35
  */
45
36
  export declare const ASSIGNMENT_SCOPE_KIND = "assignment";
46
37
  export declare function assignmentScopeKey(assignmentId: string): string;
47
38
  export interface Assignment {
48
39
  id: string;
49
40
  organizationId: string;
50
- /** De agent die dit doet: het `userId` van een gebruiker met `type: "agent"`. */
41
+ /** The agent doing this: the `userId` of a user with `type: "agent"`. */
51
42
  agentId: string;
52
- /** De definitie waar deze opdracht uit voortkomt (een playbook met een procedure). */
43
+ /** The definition this assignment came from (a playbook with a procedure). */
53
44
  jobId: string;
54
45
  /**
55
- * De versie waarmee hij begon — puur voor de audit.
46
+ * The version it started on — purely for the audit.
56
47
  *
57
- * Elke **beurt** pint zijn eigen procedure (`run.decider`), dus een verbeterde prose komt bij de
58
- * volgende beurt aan zonder een lopende beurt van opdracht te laten wisselen. Eeuwig pinnen zou
59
- * betekenen dat een correctie nooit aankomt; niet pinnen dat een beurt halverwege verandert.
48
+ * Every **turn** pins its own procedure (`run.decider`), so improved prose arrives on the next
49
+ * turn without a running turn switching instructions mid-flight. Pinning forever would mean a
50
+ * correction never arrives; not pinning would mean a turn changes halfway.
60
51
  */
61
52
  jobVersion: number;
62
- /** Waar deze opdracht over gaat. Afwezig = werk zonder resource (een rollup, een klus). */
53
+ /** What this assignment is about. Absent = work without a resource (a rollup, a chore). */
63
54
  subjectKind?: string;
64
55
  subjectId?: string;
65
56
  status: AssignmentStatus;
66
57
  /**
67
- * Waarop de **opdracht** wacht tussen twee beurten.
58
+ * What the **assignment** is waiting on between two turns.
68
59
  *
69
- * Bewust hier en niet op de run, want het zijn twee verschillende soorten pauze:
60
+ * Deliberately here and not on the run, because they are two different kinds of pause:
70
61
  *
71
- * - een **workflow** pauzeert *midden in een flow* en hervat op stap N+1 met dezelfde vars — de
72
- * cursor (`run.resumeAtStep`) is essentieel;
73
- * - een **opdracht** heeft geen cursor. "Hervatten" is *nog een beurt nemen*, met de draad als
74
- * geheugen.
62
+ * - a **workflow** pauses *mid-flow* and resumes at step N+1 with the same vars — the cursor
63
+ * (`run.resumeAtStep`) is essential;
64
+ * - an **assignment** has no cursor. "Resuming" is *taking another turn*, with the thread as
65
+ * memory.
75
66
  *
76
- * Dat die twee in één veld geperst zaten is waarom elke procedure-run `resumeAtStep: 0` opsloeg.
67
+ * Squeezing those two into one field is why every procedure run stored `resumeAtStep: 0`.
77
68
  */
78
69
  waitingOn?: WaitingOn | null;
79
- /** De soort van `waitingOn` als platte kolom — de store kan niet in een genest object zoeken. */
70
+ /** The kind of `waitingOn` as a flat column — the store cannot search inside a nested object. */
80
71
  waitingOnKind?: WaitingOn["on"] | null;
81
72
  /**
82
- * Hoeveel beurten deze opdracht al heeft gehad.
83
- *
84
- * Was `run.rounds`, wat het aantal keer parkeren *binnen één run* telde. Op dit niveau is het de
85
- * teller die telt: een agent die met een mens heen en weer blijft pingpongen moet een plafond
86
- * hebben.
73
+ * How many turns this assignment has had — counted at assignment level, because an agent
74
+ * ping-ponging with a human needs a ceiling.
87
75
  */
88
76
  turns: number;
89
- /** De draad: agent én mens schrijven hierin. Dit is de toestand van de opdracht. */
77
+ /** The thread: agent and human both write in it. This is the assignment's state. */
90
78
  conversationId: string;
91
- /** Lijst-index, geen autorisatie — zie `PlaybookRun.visibleTo`. */
79
+ /** List index, not authorization — see `PlaybookRun.visibleTo`. */
92
80
  visibleTo?: string[];
93
- /** Waar deze opdracht over gaat, in mensentaal. Voor de lijst. */
81
+ /** What this assignment is about, in human words. For the list. */
94
82
  title?: string;
95
83
  /**
96
- * Waaróm de opdracht in deze stand staat, als er iets uit te leggen is.
84
+ * *Why* the assignment is in this state, when there is something to explain.
97
85
  *
98
- * `decideAssignmentState` rekende dit al uit en niets bewaarde het, dus je zag `escalated` zonder
99
- * uitleg — precies de vraag ("waarom heeft hij dit overgedragen?") die dit ontwerp zou moeten
100
- * kunnen beantwoorden. De reden van de agent zelf staat hier, en zijn woorden in de draad.
86
+ * `decideAssignmentState` already computed this and nothing stored it, so you saw `escalated`
87
+ * without an explanation — exactly the question ("why did it hand this over?") this design should
88
+ * be able to answer. The agent's own reason goes here, and its words in the thread.
101
89
  */
102
90
  statusReason?: string;
103
91
  createdBy: string;
104
92
  lastActivityAt?: number;
105
93
  }
106
94
  /**
107
- * Wekt deze activity een opdracht die op een antwoord wacht?
108
- *
109
- * **Vier regels, van hard naar zacht.**
95
+ * Does this activity wake an assignment that is waiting for an answer?
110
96
  *
111
- * 1. **De klant antwoordt** (`inbound`): wekt altijd. Een klant kan niemand @-noemen, en zijn
112
- * antwoord is per definitie het antwoord waar de opdracht op wachtte.
113
- * 2. **De agent zelf schreef dit**: nooit. Zonder deze poort is de vraag die de agent stelt het
114
- * signaal waarop hij wakker wordt — een lus die pas bij het beurtplafond stopt.
115
- * 3. **De agent is genoemd**: wekt, ongeacht waarop hij wacht. Een vermelding is de expliciete
116
- * vraag die de engine zelf niet kan beantwoorden: *is dit aan míjn agent gericht?*
117
- * 4. **Een interne notitie terwijl hij op een antwoord wacht** (`waitingOn === "reply"`): wekt.
97
+ * **Four rules, hard to soft.**
118
98
  *
119
- * Regel 4 is nieuw en vervangt "alleen als hij genoemd is" voor dit ene geval. **Als een agent
120
- * expliciet om een antwoord heeft gevraagd, ís het volgende wat een collega schrijft dat antwoord** —
121
- * dan iemand dwingen zijn eigen agent te @-noemen op de vraag die die agent net zelf stelde is een
122
- * ritueel, niet een signaal. De oude vrees ("twee collega's die overleggen kosten elk een beurt")
123
- * blijft gedekt door de rand eromheen: `waitingOn` moet `"reply"` zijn, en zodra hij op een taak
124
- * wacht of nog bezig is, is de vermelding weer de enige weg naar binnen.
99
+ * 1. **The customer replies** (`inbound`): always wakes. A customer cannot @-mention anyone, and
100
+ * their reply is by definition the answer the assignment was waiting for.
101
+ * 2. **The agent wrote this itself**: never. Without this gate, the question the agent asks is the
102
+ * signal it wakes on — a loop that stops only at the turn ceiling.
103
+ * 3. **The agent is mentioned**: wakes, whatever it is waiting on. A mention is the explicit answer
104
+ * to the question the engine cannot answer itself: *is this addressed to my agent?*
105
+ * 4. **An internal note while it waits for a reply** (`waitingOn === "reply"`): wakes. If an agent
106
+ * explicitly asked for an answer, the next thing a colleague writes *is* that answer; forcing
107
+ * someone to @-mention their own agent on the question it just asked is a ritual, not a signal.
108
+ * The worry it replaces ("two colleagues conferring cost a turn each") stays covered by the
109
+ * edge: `waitingOn` has to be `"reply"`, and once it waits on a task or is still busy, a mention
110
+ * is again the only way in.
125
111
  *
126
- * @param waitingOn Waarop de opdracht wacht. Weggelaten = alleen de regels 1-3 (het gedrag van
127
- * vóór regel 4), zodat een beller die het niet weet nooit te ruim wekt.
112
+ * @param waitingOn What the assignment waits on. Omitted = rules 1-3 only, so a caller that does
113
+ * not know never wakes too eagerly.
128
114
  */
129
- export declare function wakesAssignment(activity: Activity, agentId: string, waitingOn?: WaitingOn["on"]): boolean;
130
- /** Het onderwerp van een opdracht als één verwijzing, of `undefined`. */
115
+ export declare function wakesAssignment(activity: FeedActivity, agentId: string, waitingOn?: WaitingOn["on"]): boolean;
116
+ /** The subject of an assignment as one reference, or `undefined`. */
131
117
  export declare function assignmentSubject(assignment: {
132
118
  subjectKind?: string;
133
119
  subjectId?: string;
134
120
  }): ResourceRef | undefined;
135
121
  /**
136
- * Hoeveel beurten een opdracht mag nemen voordat een mens het overneemt.
122
+ * How many turns an assignment may take before a human takes over.
137
123
  *
138
- * Niet oneindig, want elke beurt is een modelaanroep en een agent kan met de beste bedoelingen
139
- * blijven vragen. Bij het plafond escaleert hij, en dat is te zien in de draad — een opdracht die
140
- * stil ophoudt zou de vraag "waarom doet hij niets meer" onbeantwoordbaar maken.
124
+ * Not unlimited, because every turn is a model call and an agent can keep asking with the best
125
+ * intentions. At the ceiling it escalates, and that is visible in the thread — an assignment that
126
+ * silently stops would make "why is it doing nothing any more" unanswerable.
141
127
  */
142
128
  export declare const MAX_ASSIGNMENT_TURNS = 12;
143
129
  /**
144
- * Wat een beurt als volgende zet koos. Dit is wat de control-tools vastleggen.
130
+ * What a turn chose as its next move. This is what the control tools record.
145
131
  *
146
- * Eén union, want de vier uitkomsten sluiten elkaar uit: je wacht op iets, je bent klaar, of je
147
- * geeft het over aan een mens.
132
+ * One union, because the outcomes are mutually exclusive: you wait for something, you are done, or
133
+ * you hand it to a human.
148
134
  */
149
135
  export type AssignmentControl = {
150
136
  kind: "wait";
@@ -157,15 +143,15 @@ export type AssignmentControl = {
157
143
  reason: string;
158
144
  };
159
145
  /**
160
- * Waar de opdracht na deze beurt in verkeert.
146
+ * The state the assignment is in after this turn.
161
147
  *
162
- * Puur, zodat de regel op één plek staat en zonder Bridge te testen is — dezelfde vorm als
163
- * `decideFinalState` voor een run.
148
+ * Pure, so the rule sits in one place and is testable without Bridge — the same shape as
149
+ * `decideFinalState` for a run.
164
150
  *
165
- * **Geen control-call betekent escaleren**, niet stil openblijven. Dat is de regel die
166
- * `decideFinalState` al toepast op een run die wacht zonder geldige reden ("liever luid falen dan
167
- * stil parkeren"): een agent die zijn beurt afmaakt zonder te zeggen wat hij wil, laat een mens
168
- * met een opdracht zitten waarvan niemand weet of er nog iets gebeurt.
151
+ * **No control call means escalate**, not silently stay open. That is the rule `decideFinalState`
152
+ * already applies to a run waiting without a valid reason ("fail loudly rather than park
153
+ * silently"): an agent finishing its turn without saying what it wants leaves a human with an
154
+ * assignment nobody knows is still going anywhere.
169
155
  */
170
156
  export declare function decideAssignmentState(control: AssignmentControl | undefined, turnsSoFar: number, maxTurns?: number): {
171
157
  status: AssignmentStatus;
@@ -1,2 +1,2 @@
1
- export * from './types';
2
1
  export * from './signature';
2
+ export * from './types';
@@ -7,8 +7,8 @@ export interface ChannelSignature {
7
7
  }
8
8
  export type ChannelSignaturesMap = Partial<Record<CommunicationIntent | string, ChannelSignature>>;
9
9
  /**
10
- * Intents die een handtekening dragen. Enkel deze verschijnen in de
11
- * signature-editor en worden door `applySignature` van een delimiter voorzien.
10
+ * Intents that carry a signature. Only these appear in the signature editor and are given a
11
+ * delimiter by `applySignature`.
12
12
  */
13
13
  export declare const SIGNATURE_INTENTS: Set<string>;
14
14
  /**
@@ -21,12 +21,11 @@ export declare const SIGNATURE_INTENTS: Set<string>;
21
21
  */
22
22
  export declare function resolveSignature(channel: Channel, intent: CommunicationIntent | string): ChannelSignature | null;
23
23
  /**
24
- * Append de handtekening voor dit channel + intent aan een uitgaande body.
25
- * Retourneert de body ongewijzigd wanneer er geen (enabled, niet-lege)
26
- * handtekening is. Centrale plek voor de delimiter-logica zodat elke provider
27
- * dezelfde opmaak gebruikt i.p.v. 'm per send-pad te dupliceren:
28
- * - mail (html) -> RFC-style "-- " scheider
29
- * - message (html) -> dubbele <br>
30
- * - text -> dubbele newline (plain-text varianten / timeline)
24
+ * Append the signature for this channel + intent to an outgoing body. Returns the body unchanged
25
+ * when there is no (enabled, non-empty) signature. One place for the delimiter logic so every
26
+ * provider uses the same formatting instead of duplicating it per send path:
27
+ * - mail (html) -> RFC-style "-- " separator
28
+ * - message (html) -> double <br>
29
+ * - text -> double newline (plain-text variants / timeline)
31
30
  */
32
31
  export declare function applySignature(channel: Channel | undefined, intent: CommunicationIntent | string, body: string, format?: "html" | "text"): string;
@@ -1,2 +1,2 @@
1
- export * from './types';
2
1
  export * from './message-template';
2
+ export * from './types';
@@ -1,3 +1,3 @@
1
- export * from './types';
2
1
  export * from './endpoints';
3
2
  export * from './provider';
3
+ export * from './types';
@@ -1,16 +1,16 @@
1
1
  /**
2
- * De soorten aangepaste velden.
2
+ * The kinds of custom field.
3
3
  *
4
- * Begon als `text | select`, want dat was alles wat een gesprek nodig had. Werkitems vragen
5
- * er meer: een dealwaarde moet als bedrag getoond worden, een doorlooptijd als duur, en een
6
- * klant als verwijzing naar het bedrijvenregister in plaats van als vrije tekst.
4
+ * Started as `text | select`, because that was all a conversation needed. Work items ask for more:
5
+ * a deal value should show as an amount, a lead time as a duration, and a customer as a reference
6
+ * into the company register instead of as free text.
7
7
  *
8
- * Bewust **geen** `multi_select`: {@link CustomFieldDef.multiValued} bestaat al, wordt al
9
- * opgeslagen en betekent precies dat. Twee manieren om één ding te zeggen levert een
10
- * renderer op met een tak die niemand onderhoudt.
8
+ * Deliberately **no** `multi_select`: {@link CustomFieldDef.multiValued} already exists, is
9
+ * already stored, and means exactly that. Two ways to say one thing yields a renderer with a
10
+ * branch nobody maintains.
11
11
  */
12
12
  export type CustomFieldType = "text" | "textarea" | "number" | "currency" | "date" | "duration" | "select" | "user" | "checkbox"
13
- /** Verwijst naar een resource elders in het platform; zie {@link CustomFieldDef.refKinds}. */
13
+ /** Points at a resource elsewhere in the platform; see {@link CustomFieldDef.refKinds}. */
14
14
  | "resource_ref";
15
15
  /** A selectable option for a `select` field. `label` falls back to `value` when absent. */
16
16
  export interface CustomFieldOption {
@@ -39,17 +39,17 @@ export interface CustomFieldDef {
39
39
  /** Empty/absent = all teams; otherwise only shown when the resource belongs to one of these teams. */
40
40
  teamIds?: string[];
41
41
  /**
42
- * Voor `resource_ref`: welke scope-soorten gekozen mogen worden (`"company"`,
43
- * `"contact"`, `"work_item"`). Leeg = alles wat de kiezer aanbiedt.
42
+ * For `resource_ref`: which scope kinds may be chosen (`"company"`, `"contact"`, `"work_item"`).
43
+ * Empty = whatever the picker offers.
44
44
  */
45
45
  refKinds?: string[];
46
46
  /**
47
- * Welke work-projecten dit veld aanzetten. Leeg/afwezig = elk project, net als bij
47
+ * Which work projects switch this field on. Empty/absent = every project, as with
48
48
  * {@link CustomFieldDef.teamIds}.
49
49
  *
50
- * Bestaat omdat een velddefinitie platformbreed is maar een veldenset dat niet is: twee
51
- * projecten mogen allebei een "Bedrag" kennen met een eigen betekenis. Additief, dus
52
- * bestaande definities houden een lege lijst en blijven overal gelden.
50
+ * Exists because a field definition is platform-wide while a field set is not: two projects may
51
+ * both know an "Amount" with a meaning of their own. Additive, so existing definitions keep an
52
+ * empty list and stay valid everywhere.
53
53
  */
54
54
  projectIds?: string[];
55
55
  /**
@@ -1,107 +1,102 @@
1
1
  /**
2
- * Wie is "Asana-gebruiker 1203847" hier?
2
+ * Who is "Asana user 1203847" here?
3
3
  *
4
- * Deze laag bestond niet, en dat is precies waarom `apps/asana` zijn `assignees` leeg laat: een
5
- * Asana-assignee is een gid, en die één-op-één overnemen zou werk toewijzen aan een id dat in dit
6
- * platform niemand is — werk op naam van een spook. De enige twee opties waren "veld weglaten" of
7
- * "per app een hack", en elke volgende connector die assignees, auteurs, reviewers of commenters
8
- * synct loopt er opnieuw tegenaan.
4
+ * This layer did not exist, which is exactly why `apps/asana` leaves its `assignees` empty: an
5
+ * Asana assignee is a gid, and copying it one-to-one would assign work to an id that is nobody in
6
+ * this platform — work in a ghost's name. The only two options were "leave the field out" or "a
7
+ * hack per app", and every next connector syncing assignees, authors, reviewers or commenters runs
8
+ * into it again.
9
9
  *
10
- * ## Waarom dit een eigen store is en geen veld
10
+ * An external identity is **not** the same as the four things that resemble it:
11
11
  *
12
- * Een externe identiteit is **niet** hetzelfde als de vier dingen die er op lijken:
13
- *
14
- * | Lijkt erop | Is | Waarom niet hetzelfde |
12
+ * | Resembles it | Is | Why it differs |
15
13
  * |---|---|---|
16
- * | `externalId` op een record | dedupe-as voor één rij | zegt niets over personen |
17
- * | `ManagedAccount.metadata.externalUserId` | de identiteit van een *verbonden account* | één per account, en alleen van wie zelf verbond |
18
- * | `ManagedAccount.userId` | van wie is dit credential | eigendom, geen mapping |
19
- * | `Contact` / shadow contact | een persoon *buiten* de organisatie | een assignee is iemand *binnen* de organisatie, in een extern systeem |
20
- *
21
- * De vraag hier is de vijfde: welke platform-gebruiker hóórt bij dit externe account. Die heeft
22
- * een eigen levensduur (je koppelt hem één keer, hij geldt voor elke connector-ronde daarna) en
23
- * een eigen beheer-oppervlak ("3 van de 12 Asana-gebruikers gekoppeld").
14
+ * | `externalId` on a record | dedupe axis for one row | says nothing about people |
15
+ * | `ManagedAccount.metadata.externalUserId` | the identity of a *connected account* | one per account, and only of whoever connected themselves |
16
+ * | `ManagedAccount.userId` | whose credential this is | ownership, not a mapping |
17
+ * | `Contact` / shadow contact | a person *outside* the organisation | an assignee is someone *inside* the organisation, in an external system |
24
18
  *
25
- * ## Waarom in `apps/user`
19
+ * The question here is the fifth: which platform user *belongs* to this external account. It has a
20
+ * lifetime of its own (you link it once, it holds for every connector round after) and an admin
21
+ * surface of its own ("3 of the 12 Asana users linked").
26
22
  *
27
- * Bij de app die gebruikers bezit. De e-mail-bootstrap heeft de tenant-roster nodig en die staat
28
- * daar; en niet-sync-consumenten (AI-tools, webhooks, companion-apps) kunnen hem net zo goed
29
- * gebruiken. In de sync-pijplijn zetten zou hem onbruikbaar maken buiten sync.
23
+ * It lives in `apps/user`, with the app that owns users: the email bootstrap needs the tenant
24
+ * roster, which is there, and non-sync consumers (AI tools, webhooks, companion apps) can use it
25
+ * just as well. Putting it in the sync pipeline would make it unusable outside sync.
30
26
  */
31
27
  /**
32
- * Hoe zeker weten we dat deze koppeling klopt?
28
+ * How sure are we this link is right?
33
29
  *
34
- * De drie waarden zijn geen kwaliteitslabel maar een **beslissingsas**: alleen bij `confirmed` en
35
- * `email-match` mag er een `userId` aan hangen, en een `unmatched`-rij bestaat juist om zichtbaar
36
- * te maken dat er iets te koppelen valt.
30
+ * The three values are not a quality label but a **decision axis**: only `confirmed` and
31
+ * `email-match` may carry a `userId`, and an `unmatched` row exists precisely to make visible that
32
+ * there is something to link.
37
33
  */
38
34
  export type ExternalIdentityConfidence =
39
- /** Een mens heeft dit gekoppeld. Wordt nooit door een bootstrap overschreven. */
35
+ /** A person linked this. Never overwritten by a bootstrap. */
40
36
  "confirmed"
41
- /** Het externe adres matchte exact op het adres van een actieve gebruiker. */
37
+ /** The external address matched an active user's address exactly. */
42
38
  | "email-match"
43
- /** Bekend extern account, geen platform-gebruiker gevonden. `userId` is leeg. */
39
+ /** Known external account, no platform user found. `userId` is empty. */
44
40
  | "unmatched";
45
41
  export interface ExternalIdentity {
46
42
  id: string;
47
43
  organizationId: string;
48
44
  /**
49
- * Welk extern systeem. De catalogus-sleutel van de connector (`"asana"`) of de app-namespace
50
- * (`"microsoft"`) — hetzelfde vocabulaire als `SyncSourceDefinition.connectorHint`.
45
+ * Which external system. The connector's catalog key (`"asana"`) or the app namespace
46
+ * (`"microsoft"`) — the same vocabulary as `SyncSourceDefinition.connectorHint`.
51
47
  *
52
- * Bewust niet het connector-**id**: twee organisatie-verbindingen naar hetzelfde product delen
53
- * dezelfde gebruikers, en dan zou je de mapping twee keer moeten doen.
48
+ * Deliberately not the connector **id**: two organisation connections to the same product share
49
+ * the same users, and you would then have to do the mapping twice.
54
50
  */
55
51
  connectorKey: string;
56
- /** Het id van deze persoon in dat systeem: een Asana-gid, een Graph-`oid`, een Slack-user-id. */
52
+ /** This person's id in that system: an Asana gid, a Graph `oid`, a Slack user id. */
57
53
  externalId: string;
58
- /** De platform-gebruiker. Leeg bij `confidence: "unmatched"`. */
54
+ /** The platform user. Empty with `confidence: "unmatched"`. */
59
55
  userId?: string;
60
- /** Wat het externe systeem als adres gaf. Bewaard zodat een latere bootstrap kan matchen. */
56
+ /** The address the external system gave. Kept so a later bootstrap can match. */
61
57
  externalEmail?: string;
62
- /** Wat het externe systeem als naam gaf. Alleen om de beheerlijst leesbaar te maken. */
58
+ /** The name the external system gave. Only to keep the admin list readable. */
63
59
  externalName?: string;
64
60
  confidence: ExternalIdentityConfidence;
65
61
  createdAt?: number;
66
62
  updatedAt?: number;
67
63
  }
68
- /** Eén extern account waarover een connector een vraag stelt. */
64
+ /** One external account a connector is asking about. */
69
65
  export interface ExternalIdentityQuery {
70
66
  externalId: string;
71
- /** Als het externe systeem een adres geeft. Zonder dit kan de bootstrap niets matchen. */
67
+ /** When the external system gives an address. Without it the bootstrap can match nothing. */
72
68
  email?: string;
73
69
  name?: string;
74
70
  }
75
71
  /**
76
- * `POST /external-identity/resolve` — **batch**, want één sync-pagina draagt er tientallen.
72
+ * `POST /external-identity/resolve` — **batched**, because one sync page carries dozens.
77
73
  *
78
- * Per record vragen zou per pagina tientallen app-grenzen kosten, en dat is precies de fout die
79
- * `landRecords` al vermijdt door per soort te groeperen.
74
+ * Asking per record would cost dozens of app boundary crossings per page, exactly the mistake
75
+ * `landRecords` already avoids by grouping per kind.
80
76
  */
81
77
  export interface ResolveExternalIdentitiesRequest {
82
78
  connectorKey: string;
83
79
  externals: ExternalIdentityQuery[];
84
80
  }
85
- /** Wat de resolve teruggeeft per extern id. `userId` afwezig = niets om aan toe te wijzen. */
81
+ /** What the resolve returns per external id. `userId` absent = nothing to assign to. */
86
82
  export interface ResolvedExternalIdentity {
87
83
  userId?: string;
88
84
  confidence: ExternalIdentityConfidence;
89
85
  }
90
86
  export interface ResolveExternalIdentitiesResponse {
91
87
  /**
92
- * Op `externalId`. Elk gevraagd id staat erin, ook de niet-gematchte — een ontbrekende sleutel
93
- * en "geen match" zijn twee verschillende dingen, en de beller mag ze niet hoeven raden.
88
+ * Keyed on `externalId`. Every requested id is in there, the unmatched ones included — a missing
89
+ * key and "no match" are two different things, and the caller must not have to guess.
94
90
  *
95
- * Een gewone map is hier veilig: sinds de camelCase-transform op antwoorden verwijderd is
96
- * (17-08-2026) reizen sleutels ongeschonden, dus een gid met een punt of een streep erin komt
97
- * heel aan.
91
+ * A plain map is safe here: keys travel intact, so a gid with a dot or a dash in it arrives
92
+ * whole.
98
93
  */
99
94
  resolved: Record<string, ResolvedExternalIdentity>;
100
95
  }
101
- /** `POST /external-identity/link` — een mens wijst de koppeling aan. */
96
+ /** `POST /external-identity/link` — a person points out the link. */
102
97
  export interface LinkExternalIdentityRequest {
103
98
  connectorKey: string;
104
99
  externalId: string;
105
- /** Leeg = ontkoppelen; de rij blijft bestaan als `unmatched`. */
100
+ /** Empty = unlink; the row stays as `unmatched`. */
106
101
  userId?: string;
107
102
  }
@@ -1,3 +1,3 @@
1
- export * from './types';
2
1
  export * from './logic';
3
2
  export * from './seen';
3
+ export * from './types';