@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,415 +1,395 @@
1
1
  /**
2
- * Data van buiten naar binnen halen, één keer geregeld.
2
+ * Pulling outside data in, settled once.
3
3
  *
4
- * Het platform ingest vandaag op vier onafhankelijke manieren — een cron-sync per provider-app,
5
- * webhooks, een event-driven verrijkingsjob en losse backfills — en elk van die vier herbouwt
6
- * zelfstandig dezelfde zes dingen: cursor, batching, locking, idempotentie, foutafhandeling en
7
- * observability. Dat is te zien: `cursorKey()` staat drie keer los in de repo,
8
- * `microsoft_sync_state` groeit per sub-sync een kolom, en een sync die stilvalt is een
9
- * `console.error` die niemand ziet.
4
+ * The platform ingests in four independent ways today — a cron sync per provider app, webhooks, an
5
+ * event-driven enrichment job and loose backfills — and each of the four rebuilds the same six
6
+ * things: cursor, batching, locking, idempotency, error handling and observability. It shows:
7
+ * `cursorKey()` sits in the repo three times, `microsoft_sync_state` grows a column per sub-sync,
8
+ * and a sync that stalls is a `console.error` nobody sees.
10
9
  *
11
- * De splitsing die dat oplost is de industriestandaard (Singer/Airbyte, Nango, Fivetran): **een
12
- * smal bron-contract met een dikke generieke runtime.** De bron weet hoe je één pagina uit
13
- * Microsoft Graph of Asana haalt; de runtime weet hoe je cursors checkpoint, batches
14
- * doorschakelt, backoff doet en er een run-log van bijhoudt. Dit bestand is dat smalle contract.
10
+ * The split that fixes that is the industry standard (Singer/Airbyte, Nango, Fivetran): **a narrow
11
+ * source contract with a thick generic runtime.** The source knows how to fetch one page from
12
+ * Microsoft Graph or Asana; the runtime knows how to checkpoint cursors, chain batches, back off
13
+ * and keep a run log. This file is that narrow contract.
15
14
  *
16
- * Drie endpoints, in de vorm van `memory-source` en `analytics-source` — dus een providerrol en
17
- * geen servicebus-sleutel, want dit draait server-side zonder gebruiker erbij:
15
+ * Three endpoints, shaped like `memory-source` and `analytics-source` — so a provider role and not
16
+ * a service-bus key, because this runs server-side with no user present:
18
17
  *
19
- * | Rol | Endpoint | Antwoord |
18
+ * | Role | Endpoint | Answer |
20
19
  * |---|---|---|
21
20
  * | `sync-source` | `GET /provider/sync/describe` | {@link SyncSourceDescribe} |
22
21
  * | `sync-source` | `POST /provider/sync/pull` | {@link SyncPullResponse} |
23
22
  * | `sync-target` | `POST /provider/sync/land` | {@link SyncLandResponse} |
24
23
  *
25
- * Bron en doel zijn **losse rollen**: `apps/microsoft` levert records en landt niets,
26
- * `apps/work` landt records en levert niets. Een app mag beide zijn, maar hoeft dat niet.
24
+ * Source and target are **separate roles**: `apps/microsoft` delivers records and lands none,
25
+ * `apps/work` lands records and delivers none. An app may be both, but need not be.
27
26
  */
28
- /** De providerrol-groepen, als constante zodat een typefout niet stil een lege lijst oplevert. */
27
+ /** The provider role groups, as constants so a typo does not silently yield an empty list. */
29
28
  export declare const SYNC_SOURCE_PROVIDER_GROUP = "sync-source";
30
29
  export declare const SYNC_TARGET_PROVIDER_GROUP = "sync-target";
31
30
  /**
32
- * Continu of eenmalig — en dat is een echt verschil, geen label.
31
+ * Continuous or one-off — a real difference, not a label.
33
32
  *
34
- * `continuous` draait op een tik en moet dus goedkoop kunnen niksdoen (een cursor die zegt
35
- * "niets nieuws"). `once` wordt door een mens gestart, mag duur zijn, en moet **uitputtend**
36
- * zijn: een migratie die 95% ophaalt is geen migratie. Die eis is precies waarom niet elke bron
37
- * beide kan — zie {@link SyncSourceDefinition.transport}.
33
+ * `continuous` runs on a tick and so has to be able to do nothing cheaply (a cursor saying
34
+ * "nothing new"). `once` is started by a person, may be expensive, and has to be **exhaustive**: a
35
+ * migration that fetches 95% is not a migration. That requirement is exactly why not every source
36
+ * can do both — see {@link SyncSourceDefinition.transport}.
38
37
  */
39
38
  export type SyncMode = "continuous" | "once";
40
39
  /**
41
- * Hoe de records feitelijk binnenkomen.
40
+ * How the records actually arrive.
42
41
  *
43
- * Dit staat in het contract omdat het de verwachtingen bepaalt die de runtime en de UI mogen
44
- * hebben, niet als documentatie:
42
+ * This sits in the contract because it sets the expectations the runtime and the UI may have, not
43
+ * as documentation:
45
44
  *
46
- * - **`native`** — de app praat zelf met de API van de leverancier. Kent delta-tokens en
47
- * pagination, dus geschikt voor `continuous` én `once`.
48
- * - **`tool`** — de app gaat via de AI-tool/MCP-laag. Prima voor de lange staart aan systemen
49
- * waarvoor een eigen connector niet loont, maar een MCP-zoektool antwoordt "top-N relevant"
50
- * en niet "alles sinds X", en het protocol kent geen change-token. Een `tool`-bron die
51
- * `once` claimt belooft volledigheid die hij niet kan leveren.
52
- * - **`file`** — een geüpload bestand (CSV). Per definitie `once`, en per definitie volledig.
45
+ * - **`native`** — the app talks to the vendor's API itself. Knows delta tokens and pagination,
46
+ * so suitable for `continuous` as well as `once`.
47
+ * - **`tool`** — the app goes through the AI tool/MCP layer. Fine for the long tail of systems
48
+ * where a dedicated connector does not pay off, but an MCP search tool answers "top-N relevant"
49
+ * and not "everything since X", and the protocol has no change token. A `tool` source claiming
50
+ * `once` promises completeness it cannot deliver.
51
+ * - **`file`** — an uploaded file (CSV). `once` by definition, and complete by definition.
53
52
  */
54
53
  export type SyncTransport = "native" | "tool" | "file";
55
- /** Eén ding dat een app van buiten kan halen. */
56
54
  /**
57
- * Eén instelling van een koppeling, zoals de bron hem declareert.
55
+ * One setting of a connection, as the source declares it.
58
56
  *
59
- * Bewust geen ui-kit-types hier: `packages/domain` heeft geen UI-afhankelijkheden, en het
60
- * formulier mapt deze declaratie zelf op zijn eigen velden.
57
+ * Deliberately no ui-kit types here: `packages/domain` has no UI dependencies, and the form maps
58
+ * this declaration onto its own fields itself.
61
59
  */
62
60
  export interface SyncSettingsField {
63
- /** Sleutel in het settings-object dat de bron bij `pull` terugkrijgt. */
61
+ /** Key in the settings object the source gets back on `pull`. */
64
62
  key: string;
65
63
  label: string;
66
64
  type: "text" | "number" | "boolean" | "select";
67
65
  required?: boolean;
68
- /** Alleen bij `type: "select"`. */
66
+ /** Only with `type: "select"`. */
69
67
  options?: {
70
68
  value: string;
71
69
  label: string;
72
70
  }[];
73
- /** Uitleg onder het veld. Voor "leeg = ..."-gevallen, die anders raadwerk zijn. */
71
+ /** Explanation under the field. For "empty = ..." cases, which are guesswork otherwise. */
74
72
  help?: string;
75
73
  /**
76
- * Hint dat deze waarde een resource in een andere app aanwijst (`"work_project"`).
74
+ * Hint that this value points at a resource in another app (`"work_project"`).
77
75
  *
78
- * Vandaag rendert het formulier hem als tekstveld; de hint staat er zodat een picker later
79
- * toegevoegd kan worden zonder dat de bron verandert. Een hint en geen belofte.
76
+ * Today the form renders it as a text field; the hint is here so a picker can be added later
77
+ * without the source changing. A hint, not a promise.
80
78
  */
81
79
  resource?: string;
82
80
  }
83
81
  export interface SyncSourceDefinition {
84
82
  /**
85
- * Stabiel en app-genamespaced: `microsoft.todo`, `asana.tasks`, `csv.contacts`.
83
+ * Stable and app-namespaced: `microsoft.todo`, `asana.tasks`, `csv.contacts`.
86
84
  *
87
- * Dit is een **opgeslagen verwijzing** — `SyncConnection.sourceId` bewaart hem — dus
88
- * hernoemen breekt bestaande koppelingen. Punt als scheidingsteken, zoals `MemoryKindId`
89
- * voor app-eigen kinds.
85
+ * A **stored reference** — `SyncConnection.sourceId` keeps it — so renaming breaks existing
86
+ * connections. Dot as separator, like `MemoryKindId` for app-owned kinds.
90
87
  */
91
88
  id: string;
92
89
  label: string;
93
90
  description?: string;
94
91
  /**
95
- * De scope-soorten die deze bron landt: `work_item`, `interaction`, `company`, `contact`.
92
+ * The scope kinds this source lands: `work_item`, `interaction`, `company`, `contact`.
96
93
  *
97
- * Hetzelfde vocabulaire als `ScopeDescribe.kinds`, en dat is geen cosmetische keuze — de
98
- * runtime routeert een record naar de bezittende app met `findScopeOwner(kind)`. Een soort
99
- * die geen scope-eigenaar heeft, kan dus niet geland worden, en dat merk je bij het
100
- * registreren in plaats van halverwege een run.
94
+ * The same vocabulary as `ScopeDescribe.kinds`, and not a cosmetic choice — the runtime routes a
95
+ * record to the owning app with `findScopeOwner(kind)`. A kind without a scope owner cannot be
96
+ * landed, and you notice that while registering instead of halfway a run.
101
97
  */
102
98
  kinds: string[];
103
- /** Welke modi deze bron ondersteunt. Leeg is zinloos; minstens één. */
99
+ /** Which modes this source supports. Empty is pointless; at least one. */
104
100
  modes: SyncMode[];
105
101
  transport: SyncTransport;
106
102
  /**
107
- * De `providerId` van de {@link ManagedAccount} die deze bron nodig heeft.
103
+ * The `providerId` of the {@link ManagedAccount} this source needs.
108
104
  *
109
- * Afwezig = geen credential nodig (een bestand-upload). Aanwezig betekent dat een koppeling
110
- * niet werkt zonder gekozen account, en dat de UI dat vóór de eerste run kan zeggen in
111
- * plaats van een mislukte run te laten zien.
105
+ * Absent = no credential needed (a file upload). Present means a connection does not work
106
+ * without a chosen account, and the UI can say so before the first run instead of showing a
107
+ * failed one.
112
108
  */
113
109
  accountProviderId?: string;
114
110
  /**
115
- * Instellingen die per koppeling verschillen en dus niet in code horen.
111
+ * Settings that differ per connection and therefore do not belong in code.
116
112
  *
117
- * Declaratief, zodat een connector-app **geen frontend nodig heeft** om configureerbaar te
118
- * zijn: het Koppelingen-formulier rendert deze velden. Dat is wat `apps/asana` (nul
119
- * frontend-bestanden) mogelijk houdt.
113
+ * Declarative, so a connector app needs **no frontend** to be configurable: the Connections form
114
+ * renders these fields. That is what keeps `apps/asana` (zero frontend files) possible.
120
115
  *
121
- * Plat gehouden met opzet. Een genest schema zou een mapping-DSL worden, en die is eerder
122
- * afgewezen: datumformaten en enums verschillen per bron, en fouten verschuiven dan van `tsc`
123
- * naar runtime.
116
+ * Kept flat on purpose. A nested schema would turn into a mapping DSL, which was rejected
117
+ * earlier: date formats and enums differ per source, and errors then move from `tsc` to runtime.
124
118
  */
125
119
  settingsSchema?: SyncSettingsField[];
126
120
  /**
127
- * Alleen bij `transport: "tool"`: de catalogus-sleutel waarop de connector-picker
128
- * voorfiltert (`"asana"`, `"linear"`).
121
+ * Only with `transport: "tool"`: the catalog key the connector picker pre-filters on
122
+ * (`"asana"`, `"linear"`).
129
123
  *
130
- * Een **hint**, geen resolutie: hij bepaalt welke van de MCP-verbindingen van de
131
- * organisatie als kandidaat worden voorgesteld. Wat de koppeling daarna bewaart is het
132
- * connector-**id** ({@link SyncConnection.connectorId}). Zonder hint krijgt de gebruiker
133
- * simpelweg de volledige lijst.
124
+ * A **hint**, not a resolution: it decides which of the organisation's MCP connections are
125
+ * offered as candidates. What the connection stores afterwards is the connector **id**
126
+ * ({@link SyncConnection.connectorId}). Without a hint the user simply gets the full list.
134
127
  */
135
128
  connectorHint?: string;
136
129
  /**
137
- * Alleen bij `transport: "tool"`: deze bron kan alleen draaien op een gedeeld
138
- * organisatie-credential.
130
+ * Only with `transport: "tool"`: this source can only run on a shared organisation credential.
139
131
  *
140
- * Waarom dit bestaat: een onbemande ronde (cron, sync) heeft geen handelende gebruiker, dus
141
- * een connector met `credentialScope: "per-user"` levert daar per definitie nul tools — en dat
142
- * zag je vroeger alleen als "de sync doet niets". Met deze vlag weigert het opslaan van een
143
- * `continuous`-koppeling meteen, met een uitleg.
132
+ * Why this exists: an unattended round (cron, sync) has no acting user, so a connector with
133
+ * `credentialScope: "per-user"` yields zero tools there by definition — which used to show up
134
+ * only as "the sync does nothing". With this flag, saving a `continuous` connection is refused
135
+ * right away, with an explanation.
144
136
  *
145
- * Alleen voor `continuous`: een `once`-koppeling start een mens, en dan is er wél een
146
- * handelende gebruiker wiens token gebruikt kan worden.
137
+ * Only for `continuous`: a `once` connection is started by a person, and then there *is* an
138
+ * acting user whose token can be used.
147
139
  */
148
140
  requiresCredentialScope?: "shared";
149
141
  /**
150
- * Aanbevolen minimale tijd tussen twee rondes, in ms. De organisatie mag hem overschrijven.
142
+ * Recommended minimum time between two rounds, in ms. The organisation may override it.
151
143
  *
152
- * Een **interval** en geen cron-expressie, omdat dat is wat er daadwerkelijk uitvoerbaar is:
153
- * `JobOptions.schedule` staat vast bij registratie, dus een cron-string per koppeling zou
154
- * nooit geëvalueerd worden — een veld dat belooft wat het niet doet. De runtime tikt op een
155
- * grof raster en poort daarop.
144
+ * An **interval** and not a cron expression, because that is what is actually executable:
145
+ * `JobOptions.schedule` is fixed at registration, so a per-connection cron string would never be
146
+ * evaluated — a field promising what it does not do. The runtime ticks on a coarse grid and
147
+ * gates on this.
156
148
  *
157
- * Bij de bron en niet in de runtime, omdat alleen de bron weet wat zijn API verdraagt: een
158
- * delta-feed mag elke minuut, een lijst-endpoint dat elke keer alles teruggeeft niet.
149
+ * On the source and not in the runtime, because only the source knows what its API tolerates: a
150
+ * delta feed may run every minute, a list endpoint returning everything every time may not.
159
151
  */
160
152
  defaultIntervalMs?: number;
161
153
  /**
162
- * Hoe vaak de bron zijn cursor moet negeren en alles opnieuw lezen (ms).
154
+ * How often the source should ignore its cursor and read everything again (ms).
163
155
  *
164
- * De reconcile-sweep uit het Stripe/Shopify-patroon, en in deze codebase al bewezen als
165
- * `CHAT_FULL_SWEEP_INTERVAL_MS`: cursors rusten op de aanname dat de leverancier een
166
- * `updatedAt` bumpt voor alles wat telt, en dat houdt voor nieuwe records maar is
167
- * onbetrouwbaar voor edits en verwijderingen. Afwezig = nooit vegen (juist voor een
168
- * delta-feed die verwijderingen zelf meldt).
156
+ * The reconcile sweep from the Stripe/Shopify pattern, already proven in this codebase as
157
+ * `CHAT_FULL_SWEEP_INTERVAL_MS`: cursors rest on the assumption that the vendor bumps an
158
+ * `updatedAt` for everything that counts, which holds for new records but is unreliable for
159
+ * edits and deletions. Absent = never sweep (right for a delta feed that reports deletions).
169
160
  */
170
161
  fullSweepIntervalMs?: number;
171
162
  }
172
163
  /**
173
- * Bare payload van `GET /provider/sync/describe` — **niet** in `ResponseFactory` verpakt
174
- * (mal: {@link MemorySourceDescription} en `AnalyticsSourceDescription`).
164
+ * Bare payload of `GET /provider/sync/describe` — **not** wrapped in `ResponseFactory` (template:
165
+ * {@link MemorySourceDescription} and `AnalyticsSourceDescription`).
175
166
  */
176
167
  export interface SyncSourceDescribe {
177
- /** De declarerende app (== `manifest.name` == `req.source.app`). */
168
+ /** The declaring app (== `manifest.name` == `req.source.app`). */
178
169
  source: string;
179
170
  sources: SyncSourceDefinition[];
180
171
  }
181
172
  /**
182
- * Eén record uit een bronsysteem, klaar om geland te worden.
173
+ * One record from a source system, ready to be landed.
183
174
  *
184
- * De bron doet de mapping naar de vorm die de doel-app verwacht. Dat is bewust: de runtime kent
185
- * geen enkel veld van geen enkele leverancier, en zodra hij dat wél zou doen is hij niet meer
186
- * generiek. Zie {@link SyncRecord.data}.
175
+ * The source does the mapping into the shape the target app expects. Deliberately so: the runtime
176
+ * knows no field of any vendor, and the moment it did it would stop being generic. See
177
+ * {@link SyncRecord.data}.
187
178
  */
188
179
  export interface SyncRecord {
189
180
  /**
190
- * Het stabiele id in het bronsysteem, genamespaced door de bron zelf
191
- * (`asana_task:12345`, `graph-todo:AAMk…`).
181
+ * The stable id in the source system, namespaced by the source itself (`asana_task:12345`,
182
+ * `graph-todo:AAMk…`).
192
183
  *
193
- * Dit is de dedupe-as: de doel-app upsert hierop via zijn
194
- * `upsert_by_external_provider_id`-route, dus dezelfde run twee keer draaien schrijft één
195
- * rij. "Stabiel" is de hele eis — een id dat per pagina verandert maakt van elke sync een
196
- * duplicaat-fabriek.
184
+ * This is the dedupe axis: the target app upserts on it through its
185
+ * `upsert_by_external_provider_id` route, so running the same run twice writes one row.
186
+ * "Stable" is the whole requirement — an id that changes per page turns every sync into a
187
+ * duplicate factory.
197
188
  */
198
189
  externalId: string;
199
190
  /**
200
- * Ids die de **container** aanwijzen en niet deze rij: de lijst, het project, het board.
191
+ * Ids pointing at the **container** and not at this row: the list, the project, the board.
201
192
  *
202
- * Ze worden wél bewaard (uitgaande dispatch heeft ze nodig) maar mogen nooit meedoen in de
203
- * dedupe. Dit veld bestaat omdat het al één keer fout is gegaan: Microsoft Graph stuurt
204
- * naast het taak-id ook `graph-todolist:<id>`, dat voor élke taak in die lijst hetzelfde is,
205
- * en zoeken daarop klapte de hele lijst op één rij (`task/external-ids.ts`). Daar is het
206
- * met een prefix-lijst gerepareerd; hier zegt de bron het zelf, zodat die lijst niet hoeft
207
- * te groeien.
193
+ * They are stored (outgoing dispatch needs them) but must never take part in the dedupe. This
194
+ * field exists because it went wrong once: next to the task id Microsoft Graph also sends
195
+ * `graph-todolist:<id>`, identical for every task in that list, and searching on it collapsed
196
+ * the whole list onto one row (`task/external-ids.ts`). Repaired there with a prefix list; here
197
+ * the source says it itself, so that list does not have to grow.
208
198
  */
209
199
  containerExternalIds?: string[];
210
- /** De scope-soort, uit {@link SyncSourceDefinition.kinds}. Bepaalt naar welke app dit gaat. */
200
+ /** The scope kind, from {@link SyncSourceDefinition.kinds}. Decides which app this goes to. */
211
201
  kind: string;
212
202
  /**
213
- * De velden voor de doel-app.
203
+ * The fields for the target app.
214
204
  *
215
- * **Vaste veldnamen, nooit een map met een extern id als sleutel.**
216
- *
217
- * Hier stond de sleutel-mangeling van de SDK-client als reden. Die gold voor dit veld
218
- * **nooit**: een `SyncRecord` reist van bron-app naar doel-app over `Bridge.rpc` en komt nooit
219
- * bij een browser langs. (En sinds 17-08-2026 mangelt de client helemaal niets meer.)
220
- *
221
- * De regel blijft, om de reden die er werkelijk is: de doel-app moet de velden kunnen *typen*.
222
- * `{ "asana_gid_123": {...} }` is voor `upsertWorkItemFromExternal` niet te lezen zonder eerst
223
- * te weten wat de bron erin stopte, en dan verschuift elke fout van `tsc` naar runtime.
205
+ * **Fixed field names, never a map keyed by an external id.** The target app has to be able to
206
+ * *type* the fields: `{ "asana_gid_123": {...} }` cannot be read by `upsertWorkItemFromExternal`
207
+ * without first knowing what the source put in there, and then every error moves from `tsc` to
208
+ * runtime.
224
209
  */
225
210
  data: Record<string, unknown>;
226
211
  /**
227
- * Dit record is bij de bron verwijderd.
212
+ * This record was deleted at the source.
228
213
  *
229
- * Alleen zinvol als de bron dat kán weten: een delta-feed meldt verwijderingen, een
230
- * lijst-endpoint laat ze simpelweg weg. Afwezigheid uit een lijst is géén verwijdering —
231
- * dat verschil niet respecteren betekent dat een gefilterde of gepagineerde respons je
232
- * halve administratie opruimt.
214
+ * Only meaningful when the source *can* know: a delta feed reports deletions, a list endpoint
215
+ * simply leaves them out. Absence from a list is **not** a deletion — not respecting that
216
+ * difference means a filtered or paginated response clears out half your administration.
233
217
  */
234
218
  deleted?: boolean;
235
219
  }
236
- /** `POST /provider/sync/pull` — haal één pagina op. */
220
+ /** `POST /provider/sync/pull` — fetch one page. */
237
221
  export interface SyncPullRequest {
238
222
  sourceId: string;
239
- /** Zodat de bron kan loggen en zijn eigen per-koppeling state kan vinden als hij die heeft. */
223
+ /** So the source can log and find its own per-connection state, if it has any. */
240
224
  connectionId: string;
241
- /** De {@link ManagedAccount} om mee te praten. Afwezig bij `transport: "file"`. */
225
+ /** The {@link ManagedAccount} to talk with. Absent with `transport: "file"`. */
242
226
  accountId?: string;
243
227
  /**
244
- * Bij `transport: "tool"`: het id van de MCP-connector die deze koppeling mag gebruiken,
245
- * gekozen tóen de koppeling werd geconfigureerd.
228
+ * With `transport: "tool"`: the id of the MCP connector this connection may use, chosen when the
229
+ * connection was configured.
246
230
  *
247
- * Hiermee is de toolnaam een pure string — `mcpToolPrefix(connectorId) + toolnaam` — en is
248
- * er per pagina géén resolutie-RPC nodig. Eerder zocht een bron zijn connector op
249
- * catalogus-sleutel op, en dat kon bij twee kandidaten (een org-rij naast een persoonlijke,
250
- * of twee gebruikers met elk hun eigen) geen winnaar kiezen; een app kon zelfs het prefix
251
- * van iemands persoonlijke connector krijgen.
231
+ * This makes the tool name a pure string — `mcpToolPrefix(connectorId) + tool name` — with no
232
+ * resolution RPC per page. A source used to look its connector up by catalog key, which could
233
+ * not pick a winner with two candidates (an org row next to a personal one, or two users each
234
+ * with their own); an app could even end up with the prefix of somebody's personal connector.
252
235
  */
253
236
  connectorId?: string;
254
237
  /**
255
- * De instellingen van deze koppeling, volgens {@link SyncSourceDefinition.settingsSchema}.
238
+ * The settings of this connection, per {@link SyncSourceDefinition.settingsSchema}.
256
239
  *
257
- * Een **object** hier en een JSON-string in de opslag. Let op: dat is NIET omdat de
258
- * SDK-client uitgaande sleutels zou mangelen — dat doet hij niet, de verzendkant is
259
- * `removeUndefined` + `JSON.stringify`. Alleen *antwoorden* worden gecamelCase't. De string in
260
- * de opslag is er om dezelfde reden als de cursor: één schemaveld dat elke vorm moet kunnen
261
- * dragen.
240
+ * An **object** here and a JSON string in storage — for the same reason as the cursor: one
241
+ * schema field that has to be able to carry any shape. Not because of key mangling; the client
242
+ * does not transform outgoing keys.
262
243
  */
263
244
  settings?: Record<string, unknown>;
264
245
  /**
265
- * Opaak, exact zoals de bron hem vorige keer teruggaf. `undefined` = vanaf het begin.
246
+ * Opaque, exactly as the source returned it last time. `undefined` = from the start.
266
247
  *
267
- * **De runtime kijkt er nooit in.** Een Graph `deltaLink`, een tijdstempel, een
268
- * pagina-token — het maakt niet uit, en juist daarom hoeft er geen kolom per sub-sync te
269
- * bestaan. Dat `microsoft_sync_state` vandaag `todoDeltaLink` náást `chatCursors` náást
270
- * `chatFullSweepAt` heeft, is precies wat een opaak veld voorkomt.
248
+ * **The runtime never looks inside.** A Graph `deltaLink`, a timestamp, a page token — it does
249
+ * not matter, and that is precisely why no column per sub-sync has to exist. That
250
+ * `microsoft_sync_state` today carries `todoDeltaLink` next to `chatCursors` next to
251
+ * `chatFullSweepAt` is exactly what an opaque field prevents.
271
252
  */
272
253
  cursor?: unknown;
273
- /** Negeer de cursor en lees alles opnieuw — de reconcile-sweep. */
254
+ /** Ignore the cursor and read everything again — the reconcile sweep. */
274
255
  fullSweep?: boolean;
275
- /** Maximum aantal records in dit antwoord. De bron mag minder geven, nooit meer. */
256
+ /** Maximum number of records in this answer. The source may give fewer, never more. */
276
257
  limit: number;
277
258
  }
278
- /** Antwoord op `POST /provider/sync/pull`. Wél in `ResponseFactory` verpakt. */
259
+ /** Answer to `POST /provider/sync/pull`. This one *is* wrapped in `ResponseFactory`. */
279
260
  export interface SyncPullResponse {
280
261
  records: SyncRecord[];
281
262
  /**
282
- * De cursor ná deze pagina. De runtime bewaart hem **pas nadat de records geland zijn**.
263
+ * The cursor after this page. The runtime stores it **only once the records have landed**.
283
264
  *
284
- * Dat is de Singer/Airbyte-regel en de reden dat crash-herstel werkt: valt de boel om
285
- * tussen pull en land, dan wordt de batch herhaald (idempotent, want upsert op
286
- * `externalId`) in plaats van overgeslagen. Andersom — cursor eerst — verlies je records
287
- * stil, en stil is hier het probleem.
265
+ * That is the Singer/Airbyte rule and the reason crash recovery works: if things fall over
266
+ * between pull and land, the batch is repeated (idempotent, because upsert on `externalId`)
267
+ * instead of skipped. The other way round — cursor first — loses records silently, and silent is
268
+ * the problem here.
288
269
  */
289
270
  cursor?: unknown;
290
- /** Geen data meer na deze pagina. De runtime schakelt dan niet door. */
271
+ /** No more data after this page. The runtime then does not chain on. */
291
272
  done: boolean;
292
273
  /**
293
- * De bron is gerate-limit; wacht minstens zo lang voor de volgende poging.
274
+ * The source is rate-limited; wait at least this long before the next attempt.
294
275
  *
295
- * In milliseconden, zoals elke andere duur in dit platform. Een bron die dit vult in
296
- * plaats van te gooien, houdt de run `partial` in plaats van `failed` — het is uitstel,
297
- * geen fout.
276
+ * In milliseconds, like every other duration in this platform. A source that fills this in
277
+ * instead of throwing keeps the run `partial` rather than `failed` — it is a delay, not an
278
+ * error.
298
279
  */
299
280
  retryAfterMs?: number;
300
281
  }
301
- /** `POST /provider/sync/land` — schrijf deze records weg. Alleen app-callers. */
282
+ /** `POST /provider/sync/land` — write these records. App callers only. */
302
283
  export interface SyncLandRequest {
303
- /** Waar ze vandaan komen, voor `source`/attributie op de gelande rij. */
284
+ /** Where they came from, for `source`/attribution on the landed row. */
304
285
  sourceId: string;
305
286
  records: SyncRecord[];
306
287
  }
307
- /** Wat er met één record gebeurde. */
288
+ /** What happened to one record. */
308
289
  export interface SyncLandOutcome {
309
290
  externalId: string;
310
291
  /**
311
- * `created` en `updated` zijn beide "geschreven"; `unchanged` bestaat apart omdat een
312
- * idempotente resync die niets veranderde géén SSE-ruis mag maken en niet als werk mag
313
- * tellen (het patroon uit `lib/upsert-dedup.ts`). `failed` is één rij, niet de batch.
292
+ * `created` and `updated` are both "written"; `unchanged` exists separately because an
293
+ * idempotent resync that changed nothing must not make SSE noise and must not count as work
294
+ * (the pattern from `lib/upsert-dedup.ts`). `failed` is one row, not the batch.
314
295
  */
315
296
  result: "created" | "updated" | "unchanged" | "deleted" | "failed";
316
- /** De scopeKey van de gelande rij, zodat de run-log naar het resultaat kan linken. */
297
+ /** The scopeKey of the landed row, so the run log can link to the result. */
317
298
  scopeKey?: string;
318
- /** Alleen bij `failed`. Kort genoeg voor een tabelregel. */
299
+ /** Only on `failed`. Short enough for a table row. */
319
300
  error?: string;
320
301
  }
321
- /** Antwoord op `POST /provider/sync/land`. Wél in `ResponseFactory` verpakt. */
302
+ /** Answer to `POST /provider/sync/land`. This one *is* wrapped in `ResponseFactory`. */
322
303
  export interface SyncLandResponse {
323
304
  outcomes: SyncLandOutcome[];
324
305
  }
325
306
  /**
326
- * Bare payload van `GET /provider/sync/land-describe`: welke soorten deze app kan landen.
307
+ * Bare payload of `GET /provider/sync/land-describe`: which kinds this app can land.
327
308
  *
328
- * Apart van `ScopeDescribe`, ook al overlappen de soorten meestal: een app kan een soort
329
- * autoriseren zonder er een ingest-route voor te hebben. Dat verschil zichtbaar maken is
330
- * goedkoper dan een run die halverwege een 404 vindt.
309
+ * Separate from `ScopeDescribe`, even though the kinds usually overlap: an app can authorize a
310
+ * kind without having an ingest route for it. Making that difference visible is cheaper than a run
311
+ * that finds a 404 halfway.
331
312
  */
332
313
  export interface SyncTargetDescribe {
333
314
  source: string;
334
315
  kinds: string[];
335
316
  }
336
- /** Hoe gezond een koppeling is. Wat de lijst als badge toont. */
317
+ /** How healthy a connection is. What the list shows as a badge. */
337
318
  export type SyncHealth =
338
- /** Laatste run geslaagd. */
319
+ /** Last run succeeded. */
339
320
  "ok"
340
- /** Laatste run deels geslaagd, of gerate-limit. Loopt nog, maar niet schoon. */
321
+ /** Last run partly succeeded, or was rate-limited. Still running, but not cleanly. */
341
322
  | "degraded"
342
- /** Laatste run gefaald — meestal een verlopen credential. Vraagt een mens. */
323
+ /** Last run failed — usually an expired credential. Needs a person. */
343
324
  | "broken"
344
- /** Nooit gedraaid, of een verplicht account ontbreekt. Nog geen fout. */
325
+ /** Never ran, or a required account is missing. Not an error yet. */
345
326
  | "unconfigured";
346
327
  /**
347
- * Eén geconfigureerde koppeling: deze bron, met dit account, in deze organisatie.
328
+ * One configured connection: this source, with this account, in this organisation.
348
329
  *
349
- * De credential zit hier **niet** in — `accountId` verwijst naar de canonieke
350
- * {@link ManagedAccount}-store. Een tweede plek waar tokens leven is een tweede plek waar ze
351
- * verlopen zonder dat iemand het weet.
330
+ * The credential is **not** in here — `accountId` points at the canonical
331
+ * {@link ManagedAccount} store. A second place where tokens live is a second place where they
332
+ * expire without anyone knowing.
352
333
  */
353
334
  export interface SyncConnection {
354
335
  id: string;
355
336
  organizationId: string;
356
337
  /** → {@link SyncSourceDefinition.id} */
357
338
  sourceId: string;
358
- /** → {@link ManagedAccount.id}. Afwezig bij een bron zonder credential. */
339
+ /** → {@link ManagedAccount.id}. Absent for a source without a credential. */
359
340
  accountId?: string;
360
341
  /**
361
- * Bij `transport: "tool"`: de gekozen MCP-connector. Expliciet, niet run-time geraden —
362
- * zo is in de UI te zien welke verbinding deze koppeling gebruikt, en verandert het antwoord
363
- * niet doordat iemand anders dezelfde catalogus-entry toevoegt.
342
+ * With `transport: "tool"`: the chosen MCP connector. Explicit, not guessed at run time — so the
343
+ * UI shows which connection this one uses, and the answer does not change because somebody else
344
+ * adds the same catalog entry.
364
345
  *
365
- * Is de rij weg of uitgezet, dan gaat de koppeling naar `health: "unconfigured"` en faalt de
366
- * run luid — nooit een stille lege pagina waarop de cursor doorschuift.
346
+ * If the row is gone or disabled, the connection goes to `health: "unconfigured"` and the run
347
+ * fails loudly — never a silent empty page the cursor advances over.
367
348
  */
368
349
  connectorId?: string;
369
350
  /**
370
- * De ingevulde instellingen, geparsed. In de opslag staat hij als JSON-string, om dezelfde
371
- * reden als de cursor: één schemaveld dat elke vorm moet kunnen dragen. **Niet** vanwege
372
- * sleutel-mangeling — de client transformeert uitgaande sleutels niet.
351
+ * The filled-in settings, parsed. In storage it is a JSON string, for the same reason as the
352
+ * cursor: one schema field that has to be able to carry any shape.
373
353
  */
374
354
  settings?: Record<string, unknown>;
375
- /** Vrije naam; afwezig = het label van de bron. */
355
+ /** Free-form name; absent = the source's label. */
376
356
  label?: string;
377
357
  mode: SyncMode;
378
358
  enabled: boolean;
379
359
  /**
380
- * Minimale tijd tussen twee rondes, in ms.
360
+ * Minimum time between two rounds, in ms.
381
361
  *
382
- * Bij het aanmaken gekopieerd uit {@link SyncSourceDefinition.defaultIntervalMs}, zodat de
383
- * tik hem kan lezen zonder per koppeling de bron-catalogus te bevragen — en zodat het
384
- * effectieve interval te zien en te wijzigen is in plaats van ergens in code te zitten.
362
+ * Copied from {@link SyncSourceDefinition.defaultIntervalMs} on creation, so the tick can read
363
+ * it without querying the source catalog per connection — and so the effective interval is
364
+ * visible and changeable instead of buried in code.
385
365
  */
386
366
  intervalMs?: number;
387
- /** Opaak, van de bron. Zie {@link SyncPullRequest.cursor}. */
367
+ /** Opaque, from the source. See {@link SyncPullRequest.cursor}. */
388
368
  cursor?: unknown;
389
- /** Epoch ms van de laatste volledige veegronde. */
369
+ /** Epoch ms of the last full sweep. */
390
370
  lastFullSweepAt?: number;
391
371
  health: SyncHealth;
392
372
  lastRunAt?: number;
393
- /** De fout van de laatste gefaalde run, zodat de lijst hem kan tonen zonder een run te lezen. */
373
+ /** The error of the last failed run, so the list can show it without reading a run. */
394
374
  lastError?: string;
395
375
  createdBy?: string;
396
376
  createdAt?: number;
397
377
  updatedAt?: number;
398
378
  }
399
- /** Waardoor een run begon. */
379
+ /** What started a run. */
400
380
  export type SyncRunTrigger = "cron" | "manual" | "webhook";
401
381
  /**
402
- * `partial` bestaat naast `done` en `failed` omdat "12 van de 500 rijen deden het niet" geen
403
- * van beide is: de koppeling werkt, de cursor mag opschuiven, en er is toch iets om te melden.
404
- * Zonder die derde staat wordt het óf een fout die de sync stilzet óf een succes dat de fouten
405
- * verzwijgt.
382
+ * `partial` exists next to `done` and `failed` because "12 of the 500 rows did not make it" is
383
+ * neither: the connection works, the cursor may advance, and there is still something to report.
384
+ * Without that third state it becomes either an error that halts the sync or a success that hides
385
+ * the failures.
406
386
  */
407
387
  export type SyncRunStatus = "running" | "done" | "partial" | "failed";
408
388
  /**
409
- * Wat er tijdens één ronde gebeurde.
389
+ * What happened during one round.
410
390
  *
411
- * Bewust dezelfde vorm als `PlaybookRun`: het is dezelfde vraag ("wat deed het systeem
412
- * ongevraagd, en ging het goed?"), dus dezelfde velden en dezelfde schermen.
391
+ * Deliberately the same shape as `PlaybookRun`: it is the same question ("what did the system do
392
+ * unasked, and did it go well?"), so the same fields and the same screens.
413
393
  */
414
394
  export interface SyncRun {
415
395
  id: string;
@@ -420,28 +400,28 @@ export interface SyncRun {
420
400
  status: SyncRunStatus;
421
401
  startedAt: number;
422
402
  finishedAt?: number;
423
- /** Hoeveel pagina's deze run gelezen heeft. Verraadt een bron die niet opschiet. */
403
+ /** How many pages this run read. Gives away a source that is not making progress. */
424
404
  pages: number;
425
405
  scanned: number;
426
- /** `created` + `updated`. Expliciet niet `unchanged`, anders lijkt een lege resync werk. */
406
+ /** `created` + `updated`. Explicitly not `unchanged`, or an empty resync looks like work. */
427
407
  written: number;
428
- /** `unchanged` — de gezonde uitkomst van een idempotente resync. */
408
+ /** `unchanged` — the healthy outcome of an idempotent resync. */
429
409
  skipped: number;
430
410
  failed: number;
431
- /** Was dit een veegronde? Verklaart waarom `scanned` ineens veel hoger is. */
411
+ /** Was this a sweep? Explains why `scanned` is suddenly much higher. */
432
412
  fullSweep?: boolean;
433
- /** De fout die de hele run stopte. Bij `partial` leeg — die staan in {@link SyncRun.errors}. */
413
+ /** The error that stopped the whole run. Empty on `partial` — see {@link SyncRun.errors}. */
434
414
  error?: string;
435
415
  /**
436
- * De eerste N mislukte rijen, met hun `externalId`.
416
+ * The first N failed rows, with their `externalId`.
437
417
  *
438
- * Begrensd en niet volledig: een bron waarvan élke rij faalt zou anders een run-rij van
439
- * megabytes opleveren. Het doel is debuggen ("welke rij, en waarom"), niet boekhouden.
418
+ * Bounded and not complete: a source where every row fails would otherwise produce a run row of
419
+ * megabytes. The goal is debugging ("which row, and why"), not bookkeeping.
440
420
  */
441
421
  errors?: {
442
422
  externalId: string;
443
423
  message: string;
444
424
  }[];
445
425
  }
446
- /** Hoeveel mislukte rijen een {@link SyncRun} onthoudt. */
426
+ /** How many failed rows a {@link SyncRun} remembers. */
447
427
  export declare const SYNC_RUN_MAX_ERRORS = 20;