@opencxh/domain 1.171.0 → 1.172.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/entities/activity/blocks.d.ts +30 -30
- package/dist/entities/activity/catalog.d.ts +63 -86
- package/dist/entities/activity/descriptor.d.ts +31 -32
- package/dist/entities/activity/index.d.ts +2 -2
- package/dist/entities/activity/preview.d.ts +7 -7
- package/dist/entities/activity/resolve.d.ts +37 -38
- package/dist/entities/activity/types.d.ts +34 -34
- package/dist/entities/ai-account/types.d.ts +3 -3
- package/dist/entities/ai-account/vendors.d.ts +25 -32
- package/dist/entities/ai-budget/types.d.ts +12 -8
- package/dist/entities/ai-conversation/types.d.ts +12 -12
- package/dist/entities/ai-profile/types.d.ts +21 -26
- package/dist/entities/ai-settings/types.d.ts +12 -13
- package/dist/entities/analytics/dashboard.d.ts +1 -1
- package/dist/entities/analytics/index.d.ts +4 -4
- package/dist/entities/analytics/source.d.ts +1 -1
- package/dist/entities/analytics/usage.d.ts +23 -28
- package/dist/entities/artifact/blocks.d.ts +54 -69
- package/dist/entities/artifact/markdown.d.ts +2 -3
- package/dist/entities/artifact/types.d.ts +47 -55
- package/dist/entities/assignment/types.d.ts +74 -88
- package/dist/entities/calendar-event/types.d.ts +1 -1
- package/dist/entities/channel/index.d.ts +1 -1
- package/dist/entities/channel/signature.d.ts +8 -9
- package/dist/entities/communication/index.d.ts +1 -1
- package/dist/entities/communication/message-template.d.ts +1 -1
- package/dist/entities/company/types.d.ts +1 -1
- package/dist/entities/contact/index.d.ts +1 -1
- package/dist/entities/contact/provider.d.ts +2 -1
- package/dist/entities/contact/types.d.ts +1 -10
- package/dist/entities/custom-field-def/types.d.ts +14 -14
- package/dist/entities/external-identity/types.d.ts +45 -50
- package/dist/entities/interaction/index.d.ts +1 -1
- package/dist/entities/interaction/types.d.ts +44 -47
- package/dist/entities/live-lens/types.d.ts +39 -44
- package/dist/entities/mcp/types.d.ts +53 -57
- package/dist/entities/memory/alias.d.ts +7 -7
- package/dist/entities/memory/browse.d.ts +21 -21
- package/dist/entities/memory/ingest.d.ts +26 -17
- package/dist/entities/memory/item.d.ts +53 -58
- package/dist/entities/memory/kind.d.ts +26 -26
- package/dist/entities/memory/query.d.ts +49 -51
- package/dist/entities/playbook/actor.d.ts +20 -19
- package/dist/entities/playbook/assignment.d.ts +54 -63
- package/dist/entities/playbook/index.d.ts +2 -2
- package/dist/entities/playbook/labels.d.ts +4 -4
- package/dist/entities/playbook/trigger-vars.d.ts +14 -15
- package/dist/entities/playbook/types.d.ts +173 -303
- package/dist/entities/resource-reminder/types.d.ts +8 -9
- package/dist/entities/scope/actor.d.ts +16 -0
- package/dist/entities/{note → scope}/index.d.ts +1 -0
- package/dist/entities/scope/scope.test.d.ts +1 -0
- package/dist/entities/scope/types.d.ts +53 -0
- package/dist/entities/task/types.d.ts +1 -1
- package/dist/entities/time-entry/duration.d.ts +11 -11
- package/dist/entities/time-entry/types.d.ts +60 -60
- package/dist/entities/time-entry/work-type.d.ts +21 -24
- package/dist/entities/topic/examples.d.ts +26 -27
- package/dist/entities/topic/scope.d.ts +11 -12
- package/dist/entities/topic/types.d.ts +28 -28
- package/dist/entities/user/types.d.ts +24 -28
- package/dist/entities/webhook/types.d.ts +6 -6
- package/dist/entities/work/keys.d.ts +25 -26
- package/dist/entities/work/ladder.d.ts +48 -52
- package/dist/entities/work/types.d.ts +109 -115
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +29 -33
- package/dist/index.js +1391 -1254
- package/dist/platform/account.d.ts +34 -38
- package/dist/platform/ai-tools.d.ts +48 -52
- package/dist/platform/api.d.ts +9 -8
- package/dist/platform/author.d.ts +7 -9
- package/dist/platform/capabilities.d.ts +3 -3
- package/dist/platform/communication.d.ts +163 -204
- package/dist/platform/context.d.ts +8 -8
- package/dist/platform/identity.d.ts +16 -19
- package/dist/platform/kernel.d.ts +1 -1
- package/dist/platform/manifest.d.ts +2 -2
- package/dist/platform/media.d.ts +13 -15
- package/dist/platform/permission.d.ts +39 -0
- package/dist/platform/permission.test.d.ts +1 -0
- package/dist/platform/presence.d.ts +30 -30
- package/dist/platform/provider.d.ts +14 -76
- package/dist/platform/resource-source.d.ts +43 -45
- package/dist/platform/resource.d.ts +17 -23
- package/dist/platform/scope.d.ts +36 -62
- package/dist/platform/services.d.ts +4 -4
- package/dist/platform/settings.d.ts +0 -14
- package/dist/platform/storage.d.ts +2 -2
- package/dist/platform/sync-source.d.ts +196 -216
- package/dist/platform/transcript-cadence.d.ts +37 -41
- package/dist/platform/ui.d.ts +6 -6
- package/dist/text/endpoint.d.ts +11 -27
- package/package.json +3 -5
- package/dist/entities/note/types.d.ts +0 -14
- package/dist/entities/shopify/index.d.ts +0 -1
- package/dist/entities/shopify/types.d.ts +0 -79
- package/dist/platform/lifecycle.d.ts +0 -13
- package/dist/platform/reactive.d.ts +0 -13
- package/dist/platform/sdk.d.ts +0 -182
- package/dist/platform/service-registry.d.ts +0 -13
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import { ActivityChannelKind, ActivityShape } from './catalog';
|
|
2
1
|
import { ActivityBlock } from './blocks';
|
|
2
|
+
import { ActivityChannelKind, ActivityShape } from './catalog';
|
|
3
3
|
import { ActivityTypeDescriptor } from './descriptor';
|
|
4
4
|
import { Activity } from './types';
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* One answer about an activity type, whether it is ours or an app's.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* tweedeling die dit hele traject wilde vermijden.
|
|
8
|
+
* The built-in catalog and a declared descriptor describe the same thing in a different shape: the
|
|
9
|
+
* first uses functions (which do not survive an HTTP hop), the second translation keys. This layer
|
|
10
|
+
* flattens that difference, so the feed, the inbox list and the playbook builder do not each grow
|
|
11
|
+
* their own "is it ours or not?" branch — exactly the split this whole effort wanted to avoid.
|
|
13
12
|
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
13
|
+
* Built-in wins on a collision. An app claiming `EMAIL_RECEIVED` therefore cannot hijack the mail
|
|
14
|
+
* view.
|
|
16
15
|
*/
|
|
17
16
|
export interface ResolvedActivityType {
|
|
18
17
|
type: string;
|
|
@@ -24,65 +23,65 @@ export interface ResolvedActivityType {
|
|
|
24
23
|
carriesText: boolean;
|
|
25
24
|
triggerable: boolean;
|
|
26
25
|
component?: string;
|
|
27
|
-
/** True
|
|
26
|
+
/** True for the 41 types the platform brings itself. */
|
|
28
27
|
builtIn: boolean;
|
|
29
|
-
/**
|
|
28
|
+
/** The key for the name in pickers; present as soon as a type is triggerable. */
|
|
30
29
|
displayNameKey?: string;
|
|
31
30
|
}
|
|
32
|
-
/**
|
|
31
|
+
/** Translation function for keys from a supplied locale bundle. */
|
|
33
32
|
export type Translate = (key: string, params?: Record<string, string>) => string;
|
|
34
33
|
/**
|
|
35
|
-
*
|
|
34
|
+
* A lookup over the built-in types plus whatever apps declared.
|
|
36
35
|
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* Deliberately an explicit object and not a module-global registry: on the server every request
|
|
37
|
+
* lives in its own module instance, so a registered list would never be hit there — and that would
|
|
38
|
+
* be invisible.
|
|
40
39
|
*/
|
|
41
40
|
export declare class ActivityTypeRegistry {
|
|
42
41
|
private readonly translate;
|
|
43
42
|
private readonly declared;
|
|
44
43
|
constructor(descriptors?: ActivityTypeDescriptor[], translate?: Translate);
|
|
45
44
|
get(type: string): ResolvedActivityType | undefined;
|
|
46
|
-
/**
|
|
45
|
+
/** Everything that may be offered as a trigger, built-in and declared. */
|
|
47
46
|
triggerable(): ResolvedActivityType[];
|
|
48
47
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
48
|
+
* The timeline line. Built-in comes from the catalog, declared from `text` plus the supplied
|
|
49
|
+
* translation. Without usable text it falls back to the de-underscored type name — the same line
|
|
50
|
+
* the feed always showed for unknown types.
|
|
52
51
|
*/
|
|
53
52
|
timelineText(activity: Activity, authorName: string): string;
|
|
54
|
-
/**
|
|
53
|
+
/** The line for the inbox list. Empty when the type has nothing to show. */
|
|
55
54
|
snippet(activity: Activity): string;
|
|
56
|
-
/**
|
|
55
|
+
/** The descriptor as it arrived; needed to store `text` as a key. */
|
|
57
56
|
descriptor(type: string): ActivityTypeDescriptor | undefined;
|
|
58
|
-
/**
|
|
57
|
+
/** A type's declarative blocks, when it has any. */
|
|
59
58
|
blocks(type: string): ActivityBlock[] | undefined;
|
|
60
59
|
/**
|
|
61
|
-
*
|
|
62
|
-
*
|
|
60
|
+
* The same semantic questions as the standalone accessors in `catalog.ts`, but for types an app
|
|
61
|
+
* declared as well.
|
|
63
62
|
*
|
|
64
|
-
*
|
|
65
|
-
* `ACTIVITY_CATALOG`,
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
63
|
+
* That difference is why these methods exist. `isMessageType(type)` reads only
|
|
64
|
+
* `ACTIVITY_CATALOG`, so a declared type with `shape: "message"` returned `false` there — a
|
|
65
|
+
* helpdesk app could bring a message kind that then did not count as a conversation, was not
|
|
66
|
+
* replyable and never landed in the analytics. Anyone with a registry at hand should use these;
|
|
67
|
+
* anyone without falls back to the built-in ones.
|
|
69
68
|
*
|
|
70
|
-
*
|
|
69
|
+
* The rule itself lives in one place: {@link isMessageShape} and the fields of
|
|
71
70
|
* {@link ResolvedActivityType}.
|
|
72
71
|
*/
|
|
73
72
|
isMessage(type: string): boolean;
|
|
74
|
-
/**
|
|
73
|
+
/** Can a person reply to this? Notes cannot — those go nowhere. */
|
|
75
74
|
isReplyable(type: string): boolean;
|
|
76
|
-
/**
|
|
75
|
+
/** Readable content for a model: messages, notes and transcripts. */
|
|
77
76
|
carriesText(type: string): boolean;
|
|
78
|
-
/**
|
|
77
|
+
/** Counts as an inbound or outbound message in the analytics rollup. */
|
|
79
78
|
countsAs(type: string): "inbound_message" | "outbound_message" | undefined;
|
|
80
|
-
/** `mail` | `chat` | `voice` | `video`,
|
|
79
|
+
/** `mail` | `chat` | `voice` | `video`, or `undefined` for a lifecycle event. */
|
|
81
80
|
channelKind(type: string): ActivityChannelKind | undefined;
|
|
82
|
-
/** Transcript-
|
|
81
|
+
/** Transcript-like artifact: supplied content carrying readable text. */
|
|
83
82
|
isTranscript(type: string): boolean;
|
|
84
|
-
/**
|
|
83
|
+
/** The translator belonging to this registry, for resolving block texts. */
|
|
85
84
|
get translator(): Translate;
|
|
86
85
|
}
|
|
87
|
-
/**
|
|
86
|
+
/** The empty registry: built-in types only. For code that does not (yet) load a catalog. */
|
|
88
87
|
export declare const BUILT_IN_ONLY: ActivityTypeRegistry;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ActingIdentity } from '../../platform/identity';
|
|
2
|
-
import { AIMessageInput, AIMessageOutput } from '../ai-message/types';
|
|
3
2
|
import { TranscriptSegment } from '../../platform/media';
|
|
3
|
+
import { AIMessageInput, AIMessageOutput } from '../ai-message/types';
|
|
4
4
|
export type CallStatus = "new" | "connecting" | "ringing" | "connected" | "held" | "ended" | "failed";
|
|
5
5
|
export type CallDirection = "inbound" | "outbound";
|
|
6
6
|
export type CallType = "audio" | "video" | "data" | "screen-share";
|
|
@@ -11,9 +11,9 @@ export interface Attachment {
|
|
|
11
11
|
mimeType: string;
|
|
12
12
|
size?: number;
|
|
13
13
|
url?: string;
|
|
14
|
-
/** Content-ID
|
|
14
|
+
/** Content-ID for inline (cid:) references in the body — bare, without `<>`. */
|
|
15
15
|
contentId?: string;
|
|
16
|
-
/** True
|
|
16
|
+
/** True when the attachment belongs inline in the body (cid:), not as a separate chip. */
|
|
17
17
|
isInline?: boolean;
|
|
18
18
|
}
|
|
19
19
|
export interface BaseActivity {
|
|
@@ -24,12 +24,12 @@ export interface BaseActivity {
|
|
|
24
24
|
providerId: string;
|
|
25
25
|
author: {
|
|
26
26
|
type: "user" | "contact" | "system" | "app";
|
|
27
|
-
/** Provider/account
|
|
27
|
+
/** Provider/account id (Microsoft user id, SIP account id, etc.). */
|
|
28
28
|
id?: string;
|
|
29
29
|
name: string;
|
|
30
30
|
/**
|
|
31
|
-
* Resolved
|
|
32
|
-
*
|
|
31
|
+
* Resolved hint to a Contact entity. Set by the provider sync through an endpoint lookup. The
|
|
32
|
+
* UI may navigate straight there without an extra lookup.
|
|
33
33
|
*/
|
|
34
34
|
contactId?: string;
|
|
35
35
|
};
|
|
@@ -37,12 +37,12 @@ export interface BaseActivity {
|
|
|
37
37
|
externalIds?: string[];
|
|
38
38
|
attachments?: Attachment[];
|
|
39
39
|
/**
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
40
|
+
* The activity was written optimistically and awaits confirmation from the provider sync. Sync
|
|
41
|
+
* sets it to `false` once the canonical version is fetched. The UI can show a "sending…"
|
|
42
|
+
* indicator while true.
|
|
43
43
|
*/
|
|
44
44
|
pending?: boolean;
|
|
45
|
-
/** Epoch ms —
|
|
45
|
+
/** Epoch ms — matches the DB model and Interaction.createdAt. */
|
|
46
46
|
createdAt?: number;
|
|
47
47
|
updatedAt?: number;
|
|
48
48
|
}
|
|
@@ -166,10 +166,10 @@ export type FileUploadedPayload = {
|
|
|
166
166
|
fileId?: string;
|
|
167
167
|
};
|
|
168
168
|
/**
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
169
|
+
* Every status `Interaction.status` can take, `snoozed` included. That one was missing, leaving
|
|
170
|
+
* the timeline with a gap exactly where it surprises most: snoozing and waking were the only two
|
|
171
|
+
* status changes leaving no trace, so a conversation jumped out of and back into the list without
|
|
172
|
+
* anything saying why.
|
|
173
173
|
*/
|
|
174
174
|
export type InteractionStatus = "open" | "pending" | "closed" | "snoozed";
|
|
175
175
|
export type InteractionStatusChangedPayload = {
|
|
@@ -206,7 +206,7 @@ export type AIActionProposedPayload = {
|
|
|
206
206
|
params?: Record<string, unknown>;
|
|
207
207
|
}[];
|
|
208
208
|
status: "pending" | "approved" | "rejected";
|
|
209
|
-
/**
|
|
209
|
+
/** Who the proposal was made on behalf of, so the card can show "on behalf of Finance". */
|
|
210
210
|
actor?: ActingIdentity;
|
|
211
211
|
};
|
|
212
212
|
/** Playbook lifecycle marker in the interaction timeline (system-authored). */
|
|
@@ -215,7 +215,7 @@ export type PlaybookLifecyclePayload = {
|
|
|
215
215
|
playbookId?: string;
|
|
216
216
|
playbookName?: string;
|
|
217
217
|
status?: string;
|
|
218
|
-
/**
|
|
218
|
+
/** Who the run acted for; saves the UI from fetching the playbook again. */
|
|
219
219
|
actor?: ActingIdentity;
|
|
220
220
|
};
|
|
221
221
|
export type Activity = (BaseActivity & {
|
|
@@ -343,34 +343,34 @@ export type Activity = (BaseActivity & {
|
|
|
343
343
|
payload: InteractionAssignedPayload;
|
|
344
344
|
});
|
|
345
345
|
/**
|
|
346
|
-
*
|
|
346
|
+
* An activity of **any type at all**, including a type an app declared.
|
|
347
347
|
*
|
|
348
|
-
* `Activity` is
|
|
349
|
-
*
|
|
350
|
-
* `ACTIVITY_TYPE_AUTHORING.md`
|
|
351
|
-
*
|
|
348
|
+
* `Activity` is a closed union over the 41 built-in types, deliberately: only that way does
|
|
349
|
+
* TypeScript know after `activity.type === "EMAIL_RECEIVED"` that `payload.from` exists. But
|
|
350
|
+
* `ACTIVITY_TYPE_AUTHORING.md` invites apps to bring their own type, and such a row does not fit
|
|
351
|
+
* that union — hence the ~40 `as Activity` casts across the repo.
|
|
352
352
|
*
|
|
353
|
-
*
|
|
354
|
-
* `Record<string, unknown>`
|
|
355
|
-
*
|
|
356
|
-
*
|
|
357
|
-
*
|
|
353
|
+
* An open arm *inside* `Activity` does not fix it: `payload` would become a union with
|
|
354
|
+
* `Record<string, unknown>` everywhere, removing exactly the narrowing the union exists for. Hence
|
|
355
|
+
* a name of its own. Code that also handles declared types — the feed, the inbox line, the
|
|
356
|
+
* analytics rollup — types on `AnyActivity` and asks the catalog what the thing is; code reading
|
|
357
|
+
* into a payload stays on `Activity` and narrows.
|
|
358
358
|
*/
|
|
359
359
|
export type AnyActivity = Activity | (BaseActivity & {
|
|
360
360
|
type: string;
|
|
361
361
|
payload: Record<string, unknown>;
|
|
362
362
|
});
|
|
363
363
|
/**
|
|
364
|
-
* Type
|
|
364
|
+
* Type guards for the two payload shapes the UI reads into directly.
|
|
365
365
|
*
|
|
366
|
-
*
|
|
367
|
-
* ("is
|
|
368
|
-
*
|
|
369
|
-
* `activity.payload.text`
|
|
370
|
-
*
|
|
366
|
+
* These are deliberately type names and not a catalog question. The catalog answers *semantics*
|
|
367
|
+
* ("is this a message", "which channel") and that never belongs as a type list inside a component.
|
|
368
|
+
* These two do something else: they **narrow the type**, so `activity.payload.from` and
|
|
369
|
+
* `activity.payload.text` exist afterwards. A `channelKindOf(...) === "mail"` cannot do that — it
|
|
370
|
+
* returns a boolean and TypeScript still does not know which payload is there.
|
|
371
371
|
*
|
|
372
|
-
*
|
|
373
|
-
*
|
|
372
|
+
* They live here, next to the union, so one place knows the names instead of a repetition per
|
|
373
|
+
* component.
|
|
374
374
|
*/
|
|
375
375
|
export declare function isEmailActivity(activity: Activity): activity is Extract<Activity, {
|
|
376
376
|
type: "EMAIL_RECEIVED" | "EMAIL_SENT";
|
|
@@ -4,9 +4,9 @@ export interface AiAccount {
|
|
|
4
4
|
vendor: string;
|
|
5
5
|
apiKey: string;
|
|
6
6
|
/**
|
|
7
|
-
* API
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* API host for this account. Only fillable for vendors without a fixed host (self-hosted:
|
|
8
|
+
* Ollama, vLLM, a custom gateway) — for the rest the host comes from `AI_VENDORS` and this field
|
|
9
|
+
* is ignored.
|
|
10
10
|
*/
|
|
11
11
|
baseUrl?: string;
|
|
12
12
|
isDefaultLlm?: boolean;
|
|
@@ -1,61 +1,54 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The AI vendors the ai app has an adapter for.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* One source for both the UI (account form, table labels) and the server (default model, API
|
|
5
|
+
* host). Those lists used to be separate and drifted apart — the UI named vendors no adapter
|
|
6
|
+
* existed for.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
* en daar geen chat-adapter heeft, is een testfout.
|
|
8
|
+
* This catalog describes *what* a vendor is. *How* you talk to it (which adapter class, which
|
|
9
|
+
* model filter) lives server-side in `providers/registry`, which answers to this list: a vendor
|
|
10
|
+
* claiming `"chat"` here without a chat adapter there is a test failure.
|
|
12
11
|
*
|
|
13
|
-
* `id` is
|
|
14
|
-
*
|
|
12
|
+
* `id` is what ends up in `AiAccount.vendor`, so that string is part of the storage format and
|
|
13
|
+
* must not be renamed casually.
|
|
15
14
|
*/
|
|
16
15
|
export type AiVendorCapability = "chat" | "transcription" | "embedding";
|
|
17
16
|
export interface AiVendor {
|
|
18
17
|
id: string;
|
|
19
18
|
label: string;
|
|
20
|
-
/**
|
|
19
|
+
/** What this vendor can do. Decides where it appears as an option in the UI. */
|
|
21
20
|
capabilities: readonly AiVendorCapability[];
|
|
22
|
-
/**
|
|
23
|
-
* Model waarmee een aanroep draait die zelf geen model meekrijgt. Afwezig
|
|
24
|
-
* voor vendors zonder chat-adapter.
|
|
25
|
-
*/
|
|
21
|
+
/** Model a call runs on when it carries none itself. Absent for vendors without a chat adapter. */
|
|
26
22
|
defaultModel?: string;
|
|
27
23
|
/**
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
* runtimes en eigen gateways.
|
|
24
|
+
* Fixed API host. Absent means the user has to fill one in on the account (`AiAccount.baseUrl`) —
|
|
25
|
+
* the case for self-hosted runtimes and custom gateways.
|
|
31
26
|
*/
|
|
32
27
|
baseUrl?: string;
|
|
33
28
|
/**
|
|
34
|
-
*
|
|
29
|
+
* Which attachment kinds this vendor accepts directly.
|
|
35
30
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
31
|
+
* Deliberately not a value in `capabilities`: `findAdapterGaps()` maps every capability it does
|
|
32
|
+
* not know to `"embedding"`, so a vendor claiming `"attachments"` there would be reported as a
|
|
33
|
+
* missing embedding adapter.
|
|
39
34
|
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
* mogelijkheden, en een gok levert een 400 midden in een gesprek op.
|
|
35
|
+
* Absent or empty = text only. The right default for the Chat Completions vendors: they share one
|
|
36
|
+
* adapter but not one set of capabilities, and a guess yields a 400 mid-conversation.
|
|
43
37
|
*/
|
|
44
38
|
attachmentKinds?: readonly ("image" | "pdf")[];
|
|
45
39
|
}
|
|
46
40
|
export declare const AI_VENDORS: readonly AiVendor[];
|
|
47
41
|
export declare function findAiVendor(vendorId: string): AiVendor | undefined;
|
|
48
|
-
/**
|
|
42
|
+
/** Readable name for a stored vendor id; falls back to the id itself. */
|
|
49
43
|
export declare function aiVendorLabel(vendorId: string): string;
|
|
50
44
|
/**
|
|
51
|
-
* Default
|
|
52
|
-
*
|
|
53
|
-
* hebben.
|
|
45
|
+
* Default model for a vendor. The fallback is deliberately an existing model and not an empty
|
|
46
|
+
* string: callers use this precisely because they have no choice of their own.
|
|
54
47
|
*/
|
|
55
48
|
export declare function aiVendorDefaultModel(vendorId: string, fallback?: string): string;
|
|
56
|
-
/** Vendors
|
|
49
|
+
/** Vendors offering this capability — for capability-filtered pickers. */
|
|
57
50
|
export declare function aiVendorsWith(capability: AiVendorCapability): AiVendor[];
|
|
58
|
-
/** True
|
|
51
|
+
/** True when the user has to fill in a host themselves (the vendor has no fixed one). */
|
|
59
52
|
export declare function aiVendorNeedsBaseUrl(vendorId: string): boolean;
|
|
60
|
-
/**
|
|
53
|
+
/** Does this vendor accept this attachment kind? Text always works — that is prompt. */
|
|
61
54
|
export declare function aiVendorAcceptsAttachment(vendorId: string, kind: "image" | "pdf" | "text"): boolean;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { OwnerScope } from '../
|
|
1
|
+
import { OwnerScope } from '../scope/types';
|
|
2
2
|
/**
|
|
3
3
|
* A ceiling on what may be spent on AI in one period.
|
|
4
4
|
*
|
|
@@ -18,8 +18,12 @@ export interface AiBudget {
|
|
|
18
18
|
* tenant, including the ones attributed to nobody (kb indexing, transcription,
|
|
19
19
|
* playbooks running as the organisation). That unattributed spend is real and
|
|
20
20
|
* it has to land somewhere.
|
|
21
|
+
*
|
|
22
|
+
* Named `ownerScope` like every other scoped entity. It was `scope` — the one
|
|
23
|
+
* deviation from the platform's convention, where `scope` is reserved for OAuth
|
|
24
|
+
* scopes, an unrelated concept that happens to share the word.
|
|
21
25
|
*/
|
|
22
|
-
|
|
26
|
+
ownerScope: OwnerScope;
|
|
23
27
|
/**
|
|
24
28
|
* Warn here. Absent = never warn, only block. Both absent is allowed and means
|
|
25
29
|
* the budget does nothing — the UI says so rather than pretending it is armed.
|
|
@@ -57,7 +61,7 @@ export type AiBudgetClearable = "softLimitNanos" | "hardLimitNanos";
|
|
|
57
61
|
* what an ambiguous sentinel (`0`? that is a real limit) never could.
|
|
58
62
|
*/
|
|
59
63
|
export interface AiBudgetInput {
|
|
60
|
-
|
|
64
|
+
ownerScope?: OwnerScope;
|
|
61
65
|
softLimitNanos?: number;
|
|
62
66
|
hardLimitNanos?: number;
|
|
63
67
|
period?: AiBudgetPeriod;
|
|
@@ -118,12 +122,12 @@ export interface AiMyUsage {
|
|
|
118
122
|
*/
|
|
119
123
|
export declare function aiBudgetState(budget: Pick<AiBudget, "softLimitNanos" | "hardLimitNanos" | "enabled">, spentNanos: number): AiBudgetState;
|
|
120
124
|
/**
|
|
121
|
-
* "
|
|
125
|
+
* "No ceiling" has two shapes and they have to mean the same thing.
|
|
122
126
|
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
+
* A cleared ceiling is written as `null` — `undefined` in a `$set` leaves the field standing
|
|
128
|
+
* instead of emptying it. On read-back `null` is therefore a real value, and `x !== undefined`
|
|
129
|
+
* would read it as the number zero: a ceiling of zero blocks immediately, without anyone setting
|
|
130
|
+
* it.
|
|
127
131
|
*/
|
|
128
132
|
export declare function aiBudgetLimit(value: number | null | undefined): number | undefined;
|
|
129
133
|
/**
|
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { AssignmentStatus } from '../assignment/types';
|
|
2
2
|
export type AIConversationVisibility = "shared" | "personal";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* That this thread belongs to an **agent assignment**, and where it should appear.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
6
|
+
* An assignment's thread lives on `assignment:<id>` and not on the subject — exactly why an
|
|
7
|
+
* assignment can survive several turns while the human questions about the same conversation keep
|
|
8
|
+
* their own thread. The downside was that there was no entrance to it except the agent page: the
|
|
9
|
+
* work was there, but not visible where it was about.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* **
|
|
13
|
-
*
|
|
11
|
+
* So `conversation/resolve` also delivers an assignment's thread under the scope of its
|
|
12
|
+
* **subject**, with this stamp on it. Not stored — it is the state right now, read on fetch,
|
|
13
|
+
* exactly like the assignment's own status.
|
|
14
14
|
*/
|
|
15
15
|
export interface AIConversationAgentThread {
|
|
16
16
|
assignmentId: string;
|
|
17
|
-
/**
|
|
17
|
+
/** The `userId` of the agent doing this assignment. */
|
|
18
18
|
agentId: string;
|
|
19
19
|
status: AssignmentStatus;
|
|
20
|
-
/**
|
|
20
|
+
/** The scope this thread is delivered under: the assignment's subject. */
|
|
21
21
|
subjectScopeKey: string;
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
@@ -44,8 +44,8 @@ export interface AIConversation {
|
|
|
44
44
|
createdAt: number;
|
|
45
45
|
updatedAt: number;
|
|
46
46
|
/**
|
|
47
|
-
*
|
|
48
|
-
*
|
|
47
|
+
* Set on fetch, never stored: this thread hangs off an agent assignment about the requested
|
|
48
|
+
* scope. Present = read along, not join in (see {@link AIConversationAgentThread}).
|
|
49
49
|
*/
|
|
50
50
|
agent?: AIConversationAgentThread;
|
|
51
51
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OrgTeamScope } from '../scope/types';
|
|
2
2
|
export interface PredefinedPrompt {
|
|
3
3
|
name: string;
|
|
4
4
|
prompt: string;
|
|
@@ -7,20 +7,22 @@ export interface AIProfile<T extends Record<string, any> = Record<string, any>>
|
|
|
7
7
|
id: string;
|
|
8
8
|
organizationId: string;
|
|
9
9
|
/**
|
|
10
|
-
* Who may use this profile. Company-wide (`org`) or bound to a `team`; profiles
|
|
11
|
-
*
|
|
12
|
-
*
|
|
10
|
+
* Who may use this profile. Company-wide (`org`) or bound to a `team`; profiles are
|
|
11
|
+
* never personal. The server resolves one on create (`normalizeProfileScope`), so it is
|
|
12
|
+
* always present.
|
|
13
|
+
*
|
|
14
|
+
* "Never personal" was prose while the type allowed it. `OrgTeamScope` states it.
|
|
13
15
|
*/
|
|
14
|
-
ownerScope:
|
|
16
|
+
ownerScope: OrgTeamScope;
|
|
15
17
|
name: string;
|
|
16
18
|
description?: string;
|
|
17
19
|
accountId: string;
|
|
18
20
|
model: string;
|
|
19
21
|
providerConfig?: T;
|
|
20
22
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
23
|
+
* This brain's base instruction. Optional: a profile that only exposes tools needs none. It was
|
|
24
|
+
* documented as required while the schema made it optional, so every reader had to write `?? ""`
|
|
25
|
+
* without the type explaining why.
|
|
24
26
|
*/
|
|
25
27
|
systemPrompt?: string;
|
|
26
28
|
contextIds?: string[];
|
|
@@ -28,27 +30,20 @@ export interface AIProfile<T extends Record<string, any> = Record<string, any>>
|
|
|
28
30
|
/** Namespaced tool names this profile may use (default: none enabled). */
|
|
29
31
|
enabledTools?: string[];
|
|
30
32
|
/**
|
|
31
|
-
* Per-tool confirmation policy, stored as a LIST (not a keyed map).
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* camel↔snake, en dat verminkte een genamespacede toolnaam (`communication__compose_email` →
|
|
35
|
-
* `communication_ComposeEmail`) zodat hij niet meer matchte met `enabledTools`. **Die transform
|
|
36
|
-
* is 17-08-2026 verwijderd**, dus een map zou nu werken.
|
|
37
|
-
*
|
|
38
|
-
* De lijst blijft staan: hij zit op bestaande profielrijen met een formulier eroverheen, en een
|
|
39
|
-
* migratie zou alleen een prettigere vorm opleveren. `enabledTools` ernaast is ook een lijst.
|
|
33
|
+
* Per-tool confirmation policy, stored as a LIST (not a keyed map). It sits on existing profile
|
|
34
|
+
* rows with a form on top, and a migration to a map would only yield a nicer shape;
|
|
35
|
+
* `enabledTools` next to it is a list too.
|
|
40
36
|
*
|
|
41
|
-
* Under autonomy "suggest", write tools are held for approval; reads run freely.
|
|
42
|
-
*
|
|
37
|
+
* Under autonomy "suggest", write tools are held for approval; reads run freely. A tool absent
|
|
38
|
+
* from this list defaults to "write" (safe).
|
|
43
39
|
*
|
|
44
|
-
* **
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
* die je met een gesprek in de chat verwacht af te dwingen, doet dus niets.
|
|
40
|
+
* **Where this does and does not apply.** Enforced in `playbook/ai/gate.ts`, and thereby on
|
|
41
|
+
* every autonomous run (workflow or procedure). The interactive assistant does **nothing** with
|
|
42
|
+
* it: there is no confirmation step there. So a `write` marking you expect to enforce in a chat
|
|
43
|
+
* conversation does nothing.
|
|
49
44
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
45
|
+
* That is an open gap and not a design choice: a confirmation step in the chat is UI plus an
|
|
46
|
+
* extra round, and should be a decision rather than a field that silently does nothing.
|
|
52
47
|
*/
|
|
53
48
|
toolPolicy?: {
|
|
54
49
|
name: string;
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* What an organisation configures for the AI app as a whole — not per profile, not per user.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* One field so far, deliberately not a `Record<string, unknown>`: a setting nobody can name is a
|
|
5
|
+
* setting nobody can find.
|
|
6
6
|
*/
|
|
7
7
|
export interface AiOrgSettings {
|
|
8
8
|
/**
|
|
9
|
-
*
|
|
9
|
+
* May the AI app listen in on conversations (speech to text)?
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* The switch that did not exist: installing the AI app turned transcription on for *every*
|
|
12
|
+
* conversation in the organisation, a technical accident acting as policy.
|
|
13
13
|
*/
|
|
14
14
|
listeningEnabled?: boolean;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
17
|
+
* May anything listen in?
|
|
18
18
|
*
|
|
19
|
-
* **
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* **Absent = yes**, the right side here: the switch is new, and an organisation that never touched
|
|
20
|
+
* it should not suddenly find itself without transcription. Off is therefore an explicit choice
|
|
21
|
+
* and not an empty row.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* niet.
|
|
23
|
+
* One place, so the client (which skips the upload) and the server (which refuses it) cannot each
|
|
24
|
+
* have their own default — then one would listen while the other thinks not.
|
|
26
25
|
*/
|
|
27
26
|
export declare function listeningAllowed(settings: AiOrgSettings | null | undefined): boolean;
|