@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.
- 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 +64 -35
- 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 +5 -5
- package/dist/entities/ai-conversation/types.d.ts +12 -12
- package/dist/entities/ai-profile/types.d.ts +14 -21
- package/dist/entities/ai-settings/types.d.ts +12 -13
- 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 +46 -54
- package/dist/entities/assignment/types.d.ts +76 -90
- 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/contact/index.d.ts +1 -1
- 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 +24 -28
- package/dist/entities/memory/item.d.ts +53 -58
- package/dist/entities/memory/kind.d.ts +25 -25
- package/dist/entities/memory/query.d.ts +49 -51
- package/dist/entities/playbook/actor.d.ts +17 -17
- 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 +17 -15
- package/dist/entities/playbook/types.d.ts +174 -303
- package/dist/entities/resource-reminder/types.d.ts +8 -9
- package/dist/entities/time-entry/duration.d.ts +11 -11
- package/dist/entities/time-entry/types.d.ts +59 -59
- 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 +26 -28
- package/dist/entities/user/types.d.ts +24 -28
- package/dist/entities/webhook/types.d.ts +6 -6
- package/dist/entities/work/activity.d.ts +36 -0
- package/dist/entities/work/index.d.ts +1 -0
- 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 +108 -114
- package/dist/index.cjs +6 -6
- package/dist/index.d.ts +29 -28
- package/dist/index.js +1416 -1258
- package/dist/platform/account.d.ts +34 -38
- package/dist/platform/ai-tools.d.ts +48 -52
- package/dist/platform/api.d.ts +9 -10
- 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 -15
- package/dist/platform/resource-source.d.ts +43 -45
- package/dist/platform/resource.d.ts +17 -23
- package/dist/platform/scope.d.ts +35 -61
- package/dist/platform/services.d.ts +4 -4
- package/dist/platform/storage.d.ts +1 -1
- 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 -2
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
import { MemoryKindId, MemoryLink, MemorySubjectKey } from './item';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Writing is a **CAS upsert**, not a snapshot replace like analytics.
|
|
4
4
|
*
|
|
5
|
-
* Analytics
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* Analytics may throw a whole period away and write it again because a fact is recomputable from
|
|
6
|
+
* the source store. A delta-folded story is the accumulated outcome of N model calls and is not.
|
|
7
|
+
* On top of that, remove+insert is not atomic: a reader can observe "no item" on a subject that
|
|
8
|
+
* does have a story.
|
|
9
9
|
*/
|
|
10
10
|
export interface MemoryUpsertRequest {
|
|
11
11
|
organizationId: string;
|
|
12
|
-
/**
|
|
12
|
+
/** The writing app; the hub fills this from `req.source.app` and does not trust the body. */
|
|
13
13
|
source: string;
|
|
14
14
|
subject: MemorySubjectKey;
|
|
15
15
|
kind: MemoryKindId;
|
|
16
|
-
/** Default
|
|
16
|
+
/** Default with `cardinality: "single"`: `"<kind>:<subject>"`. */
|
|
17
17
|
sourceRef?: string;
|
|
18
18
|
title: string;
|
|
19
19
|
body: string;
|
|
20
20
|
occurredAt?: number;
|
|
21
|
-
/**
|
|
21
|
+
/** Watermark of the source event that caused this write. */
|
|
22
22
|
throughRef?: string;
|
|
23
23
|
throughAt?: number;
|
|
24
|
-
/**
|
|
24
|
+
/** A mismatch => `accepted: false` with `reason: "stale-version"`; the caller reschedules. */
|
|
25
25
|
expectedVersion?: number;
|
|
26
26
|
audienceRef?: string;
|
|
27
27
|
locale?: string;
|
|
28
|
-
/**
|
|
28
|
+
/** Omit it: the hub extracts keywords from title + body itself (one tokenizer, write and read). */
|
|
29
29
|
keywords?: string[];
|
|
30
30
|
tags?: string[];
|
|
31
31
|
links?: MemoryLink[];
|
|
@@ -33,14 +33,14 @@ export interface MemoryUpsertRequest {
|
|
|
33
33
|
export type MemoryUpsertRejection = "stale-version" | "stale-watermark" | "unknown-kind" | "kind-disabled" | "subject-kind-mismatch" | "not-owner";
|
|
34
34
|
export interface MemoryUpsertResult {
|
|
35
35
|
accepted: boolean;
|
|
36
|
-
/**
|
|
36
|
+
/** The version as it stands in the store now — also on a rejection, so the caller can reschedule. */
|
|
37
37
|
version: number;
|
|
38
38
|
itemId?: string;
|
|
39
39
|
reason?: MemoryUpsertRejection;
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
|
-
*
|
|
43
|
-
* `items`
|
|
42
|
+
* Explicitly destructive: throws away all items of (subject, kind) from this source and puts
|
|
43
|
+
* `items` back in their place. Only for **recomputable** kinds, e.g. a nightly re-fold.
|
|
44
44
|
*/
|
|
45
45
|
export interface MemoryReplaceRequest {
|
|
46
46
|
organizationId: string;
|
|
@@ -50,23 +50,19 @@ export interface MemoryReplaceRequest {
|
|
|
50
50
|
items: MemoryUpsertRequest[];
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* `
|
|
53
|
+
* Source-driven wiping. The **source** pushes this; the hub interprets no deletes: the mapping
|
|
54
|
+
* from a deleted contact to its interactions lives in comms, and `contacts:deleted` publishes only
|
|
55
|
+
* `{ id }` — without an `organizationId`.
|
|
56
56
|
*
|
|
57
|
-
*
|
|
57
|
+
* **There is no `source` field, and that was the bug.** It used to exist and the purge filtered on
|
|
58
|
+
* it, so only memory carrying that source was wiped. The server derived it from the *calling* app,
|
|
59
|
+
* but memory is written by the app that *records* it — `"context"` for a note, `ctx.app` for the
|
|
60
|
+
* `memory_remember` tool — never by the app deleting the subject. Net effect: the purge matched
|
|
61
|
+
* nothing, a deleted contact or conversation kept its memory. A retention leak, and invisible,
|
|
62
|
+
* because `{ removed: 0 }` is a valid answer.
|
|
58
63
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* `source: "communication"` en crm zou met `"crm"` wissen. Maar geheugen wordt geschreven door
|
|
62
|
-
* de app die het *vastlegt* — `"context"` voor een notitie, `ctx.app` voor de
|
|
63
|
-
* `memory_remember`-tool. Nooit door de app die het subject verwijdert.
|
|
64
|
-
*
|
|
65
|
-
* Netto matchte de purge dus niets: een verwijderd contact of gesprek liet zijn geheugen
|
|
66
|
-
* staan. Een retentie-lek, en niet zichtbaar, want `{ removed: 0 }` is een geldig antwoord.
|
|
67
|
-
*
|
|
68
|
-
* Een purge is subject-gescopeerd en niet bron-gescopeerd: "dit subject bestaat niet meer, haal
|
|
69
|
-
* alles erover weg". Dat de aanroeper een app moet zijn blijft de poort; het is geen filter.
|
|
64
|
+
* A purge is subject-scoped and not source-scoped: "this subject no longer exists, remove
|
|
65
|
+
* everything about it". That the caller has to be an app stays the gate; it is not a filter.
|
|
70
66
|
*/
|
|
71
67
|
export interface MemoryPurgeRequest {
|
|
72
68
|
organizationId: string;
|
|
@@ -1,114 +1,109 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* The memory layer (`apps/context`): one durable layer per **subject**, where the organisation
|
|
3
|
+
* itself decides per event what gets remembered.
|
|
4
4
|
*
|
|
5
|
-
* `Memory*`
|
|
6
|
-
* `context.collect` (
|
|
5
|
+
* `Memory*` and not `Context*`: `platform/context.ts` already exports `HostContext` and
|
|
6
|
+
* `context.collect` (ephemeral route context) from the same flat barrel.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
8
|
+
/** An existing scopeKey: `interaction:i_1`, `contact:c_9`. A later identity layer is just another prefix. */
|
|
9
9
|
export type MemorySubjectKey = string;
|
|
10
|
-
/** `comms.resolution` (app-
|
|
10
|
+
/** `comms.resolution` (app-declared) or `sales_besluitvormer` (org-owned). */
|
|
11
11
|
export type MemoryKindId = string;
|
|
12
12
|
/**
|
|
13
|
-
*
|
|
14
|
-
*
|
|
13
|
+
* A reference to something else — internal (`interaction:i_2`) or external (`hubspot:5591`,
|
|
14
|
+
* `clickup:86ab2`).
|
|
15
15
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* De oorspronkelijke reden (de http-client mangelde object-keys) geldt niet meer sinds 17-08-2026.
|
|
19
|
-
* De lijst blijft om een reden die altijd al sterker was: **dezelfde `rel` mag twee keer voorkomen**
|
|
20
|
-
* — twee gekoppelde bedrijven, drie betrokken interacties — en een map gooit er dan één weg.
|
|
16
|
+
* A **list**, never an object keyed by the relation: **the same `rel` may occur twice** — two
|
|
17
|
+
* linked companies, three involved interactions — and a map would throw one away.
|
|
21
18
|
*/
|
|
22
19
|
export interface MemoryLink {
|
|
23
20
|
rel: string;
|
|
24
21
|
target: string;
|
|
25
22
|
}
|
|
26
|
-
/**
|
|
23
|
+
/** Hard limit on the body. Memory is a summary; it is not a shadow copy of a mailbox. */
|
|
27
24
|
export declare const MAX_MEMORY_CHARS = 2000;
|
|
28
25
|
export interface MemoryItem {
|
|
29
26
|
id: string;
|
|
30
27
|
organizationId: string;
|
|
31
28
|
subject: MemorySubjectKey;
|
|
32
29
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* The file keys this item is findable under: the subject itself, plus the keys the owning app of
|
|
31
|
+
* that subject returned (`ScopeAuth.keys`). Stamped at write time.
|
|
35
32
|
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
* `
|
|
39
|
-
*
|
|
33
|
+
* This is what makes "tell me what was going on at this customer" one indexed query instead of a
|
|
34
|
+
* traversal. A case remembered on a conversation also carries `domain:vandijck.nl` and
|
|
35
|
+
* `company:co_1` through `Interaction.partyKeys`; the company file matches on those with its own
|
|
36
|
+
* handful of keys, however many employees that company has.
|
|
40
37
|
*
|
|
41
|
-
* **`keys`
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
* `
|
|
38
|
+
* **`keys` and not `subjects`**, because they are not: `domain:vandijck.nl` is no subject — no
|
|
39
|
+
* app owns that kind, so `authorizeIdentity` on it returns `allowed: false`. A name promising
|
|
40
|
+
* these are subjects invites exactly that mistake. Same word as `Interaction.partyKeys`,
|
|
41
|
+
* `Contact.keys` and `ScopeAuth.keys`: one concept, one term.
|
|
45
42
|
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
43
|
+
* Deliberately a snapshot and not resolved at read time: resolving on every query would mean
|
|
44
|
+
* every hit costs an authorization fan-out, which the engine cannot afford. The price is that a
|
|
45
|
+
* key recorded *later* no longer reaches this item; in practice `mailto:`/`domain:` already
|
|
46
|
+
* cover that almost always.
|
|
50
47
|
*
|
|
51
|
-
* `subject`
|
|
52
|
-
*
|
|
53
|
-
*
|
|
48
|
+
* `subject` stays the anchor and is therefore not one element among the others: the unique
|
|
49
|
+
* index, the folding, the wipe on a deleted contact and the authorization all four run over it,
|
|
50
|
+
* and those need exactly one.
|
|
54
51
|
*/
|
|
55
52
|
keys?: string[];
|
|
56
53
|
kind: MemoryKindId;
|
|
57
54
|
title: string;
|
|
58
|
-
/**
|
|
55
|
+
/** THE content: markdown, capped at {@link MAX_MEMORY_CHARS}. Never a nested object. */
|
|
59
56
|
body: string;
|
|
60
|
-
/**
|
|
57
|
+
/** Writing app (== `req.source.app`); playbook writes arrive as "ai". */
|
|
61
58
|
source: string;
|
|
62
|
-
/**
|
|
59
|
+
/** Identity within (subject, kind) for the source. With `cardinality: "single"` the fold key. */
|
|
63
60
|
sourceRef: string;
|
|
64
61
|
occurredAt: number;
|
|
65
62
|
/**
|
|
66
|
-
*
|
|
67
|
-
* retries
|
|
63
|
+
* Watermark: up to and including which source event this item is folded. Makes duplicate events
|
|
64
|
+
* and retries free — an older `throughAt` is refused instead of overwriting.
|
|
68
65
|
*/
|
|
69
66
|
throughRef?: string;
|
|
70
67
|
throughAt?: number;
|
|
71
|
-
/** Optimistic concurrency;
|
|
68
|
+
/** Optimistic concurrency; the hub does compare-and-swap on this field. */
|
|
72
69
|
version: number;
|
|
73
|
-
/**
|
|
70
|
+
/** Taken from the kind definition, not from the call — see `MemoryKindDefinition.visibility`. */
|
|
74
71
|
visibility: MemoryVisibility;
|
|
75
72
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* stale-permissive is een lek.
|
|
73
|
+
* Visibility ANCHOR (inbox id / OwnerScope key), **not a member list**: a snapshotted member list
|
|
74
|
+
* goes stale on every inbox join and reassignment, and stale-permissive is a leak.
|
|
79
75
|
*/
|
|
80
76
|
audienceRef?: string;
|
|
81
77
|
/**
|
|
82
|
-
*
|
|
78
|
+
* Language of the content. **Descriptive, not a filter** — a decision, not an omission.
|
|
83
79
|
*
|
|
84
|
-
* Retrieval
|
|
85
|
-
*
|
|
86
|
-
*
|
|
87
|
-
*
|
|
88
|
-
*
|
|
80
|
+
* Retrieval deliberately compares across language boundaries too: the embedding models here are
|
|
81
|
+
* multilingual, so a Dutch question should find an English case about the same problem. A hard
|
|
82
|
+
* language check once sat in `rank.ts`; it was never wired up and, had it been, would have
|
|
83
|
+
* skipped *every* item on a foreign-language query — because nothing sets this field, so
|
|
84
|
+
* everything sits on the default.
|
|
89
85
|
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
86
|
+
* If this is ever really set, keep it an *ordering* signal (material in your own language is
|
|
87
|
+
* easier to use) and not a visibility filter.
|
|
92
88
|
*/
|
|
93
89
|
locale: string;
|
|
94
|
-
/**
|
|
90
|
+
/** Denormalized terms: the only selective structural filter (there is no substring operator). */
|
|
95
91
|
keywords?: string[];
|
|
96
|
-
/**
|
|
92
|
+
/** Fast alternative keys, e.g. `tel:+31612345678` for the moment the phone rings. */
|
|
97
93
|
tags?: string[];
|
|
98
94
|
links?: MemoryLink[];
|
|
99
|
-
/** Model
|
|
95
|
+
/** Model the vector was made with; retrieval only compares within the same model. */
|
|
100
96
|
embeddingModel?: string;
|
|
101
|
-
/**
|
|
97
|
+
/** From a live pull (phase 3); never persisted. */
|
|
102
98
|
live?: boolean;
|
|
103
99
|
createdAt?: number;
|
|
104
100
|
updatedAt?: number;
|
|
105
101
|
}
|
|
106
102
|
/**
|
|
107
|
-
* `"audience"` =
|
|
108
|
-
*
|
|
109
|
-
* voorgekomen?").
|
|
103
|
+
* `"audience"` = only for whoever may see the subject. `"org"` = readable org-wide and therefore
|
|
104
|
+
* findable in the cross-subject path ("has this happened at another customer before?").
|
|
110
105
|
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
106
|
+
* Declared per **kind** and not per item: the cross-subject path cannot afford an authorization
|
|
107
|
+
* round trip per hit, so it filters structurally on this column.
|
|
113
108
|
*/
|
|
114
109
|
export type MemoryVisibility = "audience" | "org";
|
|
@@ -2,65 +2,65 @@ import { LocaleBundle } from '../analytics/dashboard';
|
|
|
2
2
|
import { OwnerScope } from '../scope/types';
|
|
3
3
|
import { MemoryKindId, MemoryVisibility } from './item';
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* What may be remembered, and under which rules.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
* - **app-
|
|
9
|
-
* - **org-
|
|
10
|
-
*
|
|
7
|
+
* Two sources, one type:
|
|
8
|
+
* - **app-declared** through `GET /provider/memory/describe` (the built-in defaults);
|
|
9
|
+
* - **org-owned** as a row in `memory_kind`. Template: `CustomFieldDef` — the organisation
|
|
10
|
+
* defines the key, a generic store keeps the value.
|
|
11
11
|
*
|
|
12
|
-
* `ownerScope` is
|
|
13
|
-
*
|
|
12
|
+
* `ownerScope` is what makes "everyone wants their own relevant data" possible: sales records
|
|
13
|
+
* different things from support, and a personal kind belongs to that user only.
|
|
14
14
|
*/
|
|
15
15
|
export interface MemoryKindDefinition {
|
|
16
|
-
/** Org-
|
|
16
|
+
/** Org-owned: `[a-z0-9_]+` (no dot — that reads as a nested form path). App-owned: `<app>.<name>`. */
|
|
17
17
|
id: MemoryKindId;
|
|
18
18
|
label: string;
|
|
19
19
|
description?: string;
|
|
20
|
-
/** Subject
|
|
20
|
+
/** Subject kinds this kind may land on, e.g. `["interaction"]` or `["contact"]`. */
|
|
21
21
|
subjectKinds: string[];
|
|
22
22
|
visibility: MemoryVisibility;
|
|
23
|
-
/** `"single"` =
|
|
23
|
+
/** `"single"` = one item per (subject, kind), folded onward. `"many"` = separate items. */
|
|
24
24
|
cardinality: "single" | "many";
|
|
25
|
-
/**
|
|
25
|
+
/** Ordering under the bundle's character budget. */
|
|
26
26
|
priority: number;
|
|
27
|
-
/**
|
|
27
|
+
/** May this kind take part in the cross-subject path? Only meaningful with `visibility: "org"`. */
|
|
28
28
|
crossSubjectSearchable: boolean;
|
|
29
|
-
/**
|
|
29
|
+
/** Embedding costs money; on or off per kind. */
|
|
30
30
|
embed: boolean;
|
|
31
31
|
retentionDays?: number;
|
|
32
|
-
/**
|
|
32
|
+
/** Rank multiplier: a resolved case beats a loose note. Defaults to 1. */
|
|
33
33
|
weight?: number;
|
|
34
34
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
35
|
+
* Who owns this kind. Decides both who may write and the **ordering** for a reader (own team
|
|
36
|
+
* first). Absent = the whole organisation.
|
|
37
37
|
*/
|
|
38
38
|
ownerScope?: OwnerScope;
|
|
39
|
-
/**
|
|
39
|
+
/** Who declared it. The org cannot widen an app kind, only switch it off. */
|
|
40
40
|
origin: "app" | "org";
|
|
41
41
|
enabled: boolean;
|
|
42
42
|
}
|
|
43
43
|
/**
|
|
44
|
-
* Bare payload
|
|
45
|
-
*
|
|
44
|
+
* Bare payload of `GET /provider/memory/describe` — **not** wrapped in `ResponseFactory`
|
|
45
|
+
* (template: `AnalyticsSourceDescription`).
|
|
46
46
|
*/
|
|
47
47
|
export interface MemorySourceDescription {
|
|
48
|
-
/**
|
|
48
|
+
/** The declaring app (== `manifest.name` == `req.source.app`). */
|
|
49
49
|
source: string;
|
|
50
50
|
kinds: MemoryKindDefinition[];
|
|
51
|
-
/**
|
|
51
|
+
/** Flat LIST, like {@link LocaleBundle}; see there why that shape stays. */
|
|
52
52
|
locales?: LocaleBundle;
|
|
53
|
-
/**
|
|
53
|
+
/** Phase 3: which subjects/kinds this source can deliver live. */
|
|
54
54
|
live?: {
|
|
55
55
|
subjectKinds: string[];
|
|
56
56
|
kinds: MemoryKindId[];
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
/**
|
|
59
|
+
/** The composed catalog the hub delivers to the frontend (`GET /kinds`). */
|
|
60
60
|
export interface MemoryKindCatalog {
|
|
61
|
-
/** App-
|
|
61
|
+
/** App-declared plus org-owned, mixed. */
|
|
62
62
|
kinds: MemoryKindDefinition[];
|
|
63
|
-
/**
|
|
63
|
+
/** Sources that took part in the fan-out (for "who provides this?"). */
|
|
64
64
|
sources: string[];
|
|
65
65
|
locales: LocaleBundle;
|
|
66
66
|
}
|
|
@@ -1,71 +1,71 @@
|
|
|
1
1
|
import { MemoryItem, MemoryKindId, MemorySubjectKey } from './item';
|
|
2
|
-
/**
|
|
2
|
+
/** Bounds of a bundle's character budget. Out of range is clamped, not refused. */
|
|
3
3
|
export declare const MIN_MEMORY_BUDGET = 500;
|
|
4
4
|
export declare const MAX_MEMORY_BUDGET = 12000;
|
|
5
5
|
export declare const DEFAULT_MEMORY_BUDGET = 4000;
|
|
6
6
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* One query contract for the file panel, the briefing while the phone rings, and every AI tool. No
|
|
8
|
+
* second code path: the difference between those three is `budget`, not code.
|
|
9
9
|
*/
|
|
10
10
|
export interface MemoryQuery {
|
|
11
|
-
/**
|
|
11
|
+
/** Omit = cross-subject search ("similar cases"). */
|
|
12
12
|
subject?: MemorySubjectKey;
|
|
13
|
-
/** Extra subjects
|
|
13
|
+
/** Extra subjects belonging to the same picture, e.g. the contact next to the interaction. */
|
|
14
14
|
alsoSubjects?: MemorySubjectKey[];
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* Also items not *on* the subject but belonging to it: everything carrying the same file key
|
|
17
|
+
* (`MemoryItem.keys`).
|
|
18
18
|
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
19
|
+
* This is the answer to "what was going on at this company": a case sits on the conversation, and
|
|
20
|
+
* the company file finds it through `domain:`/`company:` instead of through a list of fifty
|
|
21
|
+
* employee subjects. One extra `$or` branch on an indexed array column, no second query and no
|
|
22
|
+
* extra authorization fan-out.
|
|
23
23
|
*
|
|
24
|
-
* **
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
* (`effectiveVisibility`)
|
|
24
|
+
* **Only `visibility: "org"` is inherited.** The authorization of this path is the single check
|
|
25
|
+
* on the anchor; an inherited item is by definition not authorized on that anchor. A case from
|
|
26
|
+
* someone's personal mailbox is already narrowed to `audience` at write time
|
|
27
|
+
* (`effectiveVisibility`) and therefore stays out of a colleague's customer file.
|
|
28
28
|
*
|
|
29
|
-
*
|
|
29
|
+
* Requires an anchor (`subject`); without one this is the cross-subject path.
|
|
30
30
|
*/
|
|
31
31
|
related?: boolean;
|
|
32
32
|
/** Default true. */
|
|
33
33
|
resolveAliases?: boolean;
|
|
34
34
|
kinds?: MemoryKindId[];
|
|
35
35
|
tags?: string[];
|
|
36
|
-
/**
|
|
36
|
+
/** Free text. Absent = purely structural ordering (priority x recency x weight), so no embedding call. */
|
|
37
37
|
text?: string;
|
|
38
38
|
since?: number;
|
|
39
39
|
limit?: number;
|
|
40
|
-
/**
|
|
40
|
+
/** This is how you say "at OTHER customers". */
|
|
41
41
|
excludeSubjects?: MemorySubjectKey[];
|
|
42
42
|
format?: "items" | "bundle" | "both";
|
|
43
|
-
/**
|
|
43
|
+
/** Clamped to [{@link MIN_MEMORY_BUDGET}, {@link MAX_MEMORY_BUDGET}]. */
|
|
44
44
|
budget?: number;
|
|
45
|
-
/**
|
|
45
|
+
/** Phase 3: query live sources too, with a hard timeout and their own sub-budget. */
|
|
46
46
|
live?: boolean;
|
|
47
47
|
}
|
|
48
48
|
/**
|
|
49
|
-
* Subject
|
|
50
|
-
*
|
|
49
|
+
* Subject kinds for which "the file" includes the **relations**: everything carrying the same file
|
|
50
|
+
* key, even when it was remembered on another subject.
|
|
51
51
|
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
52
|
+
* With a customer or a person the party *is* the question — "what was going on at Van Dijck" is
|
|
53
|
+
* about the company, while the cases sit on the conversations. With a conversation it is not: there
|
|
54
|
+
* you want the thread itself, because in a bounded bundle the whole customer history crowds out
|
|
55
|
+
* exactly what is going on right now.
|
|
56
56
|
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
57
|
+
* Here and not in the tool or the panel, because there are two of them: the assistant and the file
|
|
58
|
+
* panel should mean the same thing by "this customer's file". Two copies of that rule is exactly
|
|
59
|
+
* the kind of rule that drifts apart.
|
|
60
60
|
*/
|
|
61
61
|
export declare const RELATED_SUBJECT_KINDS: readonly ["contact", "company"];
|
|
62
|
-
/**
|
|
62
|
+
/** Should {@link MemoryQuery.related} default to on for this subject? */
|
|
63
63
|
export declare function relatedByDefault(subject: MemorySubjectKey | undefined): boolean;
|
|
64
64
|
export interface MemoryHit {
|
|
65
65
|
item: MemoryItem;
|
|
66
66
|
score: number;
|
|
67
67
|
snippet: string;
|
|
68
|
-
/**
|
|
68
|
+
/** Raw material for a confidence gate (phase 4): where did the score come from? */
|
|
69
69
|
matched: {
|
|
70
70
|
keyword: number;
|
|
71
71
|
semantic: number;
|
|
@@ -75,39 +75,37 @@ export interface MemoryHit {
|
|
|
75
75
|
export interface MemoryQueryResult {
|
|
76
76
|
hits: MemoryHit[];
|
|
77
77
|
bundle?: string;
|
|
78
|
-
/**
|
|
78
|
+
/** How many rows the structural pre-filter yielded. */
|
|
79
79
|
scanned: number;
|
|
80
|
-
/** Scan
|
|
80
|
+
/** Scan cap hit OR hits dropped from the bundle — never truncate silently. */
|
|
81
81
|
truncated?: boolean;
|
|
82
|
-
/** `false` = keyword-only (
|
|
82
|
+
/** `false` = keyword-only (no embedding account, or not a single comparable vector). */
|
|
83
83
|
semantic: boolean;
|
|
84
|
-
/** Hits
|
|
84
|
+
/** Hits that did not survive the re-authorization of the top-K. */
|
|
85
85
|
withheld?: number;
|
|
86
86
|
/**
|
|
87
|
-
*
|
|
87
|
+
* Candidates dropped on the cross-subject path by the absolute similarity threshold.
|
|
88
88
|
*
|
|
89
|
-
*
|
|
90
|
-
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
89
|
+
* The difference with an empty result: `belowThreshold: 7` means "there was material, but nothing
|
|
90
|
+
* really resembled it" and `undefined` means "there was nothing". Without this counter the
|
|
91
|
+
* threshold cannot be calibrated — you only see that nothing comes back, not whether it sits too
|
|
92
|
+
* high.
|
|
93
93
|
*/
|
|
94
94
|
belowThreshold?: number;
|
|
95
95
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
96
|
+
* Candidates skipped by a model or language mismatch. Without this counter a model switch
|
|
97
|
+
* degrades completely silently: `cosineSimilarity` returns 0 on a dimension mismatch, so after
|
|
98
|
+
* flipping `isDefaultEmbedding` *every* existing item scores 0 and retrieval quietly falls back
|
|
99
|
+
* to keyword.
|
|
100
100
|
*/
|
|
101
101
|
vectorSkipped?: number;
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
104
|
-
* gevonden".
|
|
103
|
+
* Why nothing came back, when that has a structural reason instead of "nothing found".
|
|
105
104
|
*
|
|
106
|
-
* `"no-cross-subject-kinds"` is
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
* niets vergelijkbaars bestaat.
|
|
105
|
+
* `"no-cross-subject-kinds"` is the important one: searching at *other* customers reads only
|
|
106
|
+
* memory kinds declared both `visibility: "org"` and `crossSubjectSearchable`. If there is not a
|
|
107
|
+
* single one, an empty result is not a search outcome but a configuration fact — and the caller
|
|
108
|
+
* should know that instead of concluding nothing comparable exists.
|
|
111
109
|
*/
|
|
112
110
|
reason?: "no-cross-subject-kinds" | "no-subject-access";
|
|
113
111
|
}
|