@memberjunction/core-entities 5.48.0 → 5.49.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/engines/PermissionEngine.d.ts +14 -0
- package/dist/engines/PermissionEngine.d.ts.map +1 -1
- package/dist/engines/PermissionEngine.js +30 -34
- package/dist/engines/PermissionEngine.js.map +1 -1
- package/dist/engines/conversations.d.ts +128 -0
- package/dist/engines/conversations.d.ts.map +1 -1
- package/dist/engines/conversations.js +152 -2
- package/dist/engines/conversations.js.map +1 -1
- package/dist/generated/entity_subclasses.d.ts +432 -53
- package/dist/generated/entity_subclasses.d.ts.map +1 -1
- package/dist/generated/entity_subclasses.js +651 -82
- package/dist/generated/entity_subclasses.js.map +1 -1
- package/package.json +5 -5
|
@@ -58,6 +58,20 @@ export declare class PermissionEngine extends BaseEngine<PermissionEngine> {
|
|
|
58
58
|
* Powers the Sharing Center "User Access Report" view.
|
|
59
59
|
*/
|
|
60
60
|
GetAllUserPermissions(user: UserInfo): Promise<NormalizedPermission[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Runs `invoke` against every registered provider in parallel and concatenates the results,
|
|
63
|
+
* isolating per-provider failures so one broken domain degrades to "contributes nothing"
|
|
64
|
+
* rather than rejecting the whole aggregate.
|
|
65
|
+
*
|
|
66
|
+
* The `Promise.resolve().then(...)` wrapper is load-bearing and NOT ceremony: `allSettled`
|
|
67
|
+
* only isolates *rejections*, but a mapper that throws SYNCHRONOUSLY (the classic case being a
|
|
68
|
+
* provider whose method is `undefined`, e.g. a hollow stub) throws inside `.map()` — before
|
|
69
|
+
* `allSettled` is ever called — and rejects the entire call. Deferring the invocation into a
|
|
70
|
+
* promise body converts that synchronous throw into a rejection `allSettled` can contain.
|
|
71
|
+
* Defense in depth: `instantiateProviders` should prevent hollow providers from being
|
|
72
|
+
* installed at all, but this fan-out must not be the thing that fails if one ever slips in.
|
|
73
|
+
*/
|
|
74
|
+
private fanOutAcrossProviders;
|
|
61
75
|
/**
|
|
62
76
|
* Aggregate every permission this user has granted to *other* users across all
|
|
63
77
|
* registered providers. Powers the end-user Sharing Center's "Shared by me" tab.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionEngine.d.ts","sourceRoot":"","sources":["../../src/engines/PermissionEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EAEV,iBAAiB,EAGjB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EAErB,sBAAsB,EAGtB,QAAQ,EACX,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAoB1E;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAW1D,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,+BAA+B,qBAAqB,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBACa,gBAAiB,SAAQ,UAAU,CAAC,gBAAgB,CAAC;IAC9D,WAAkB,QAAQ,IAAI,gBAAgB,CAE7C;IAED,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,UAAU,CAAkD;IAEvD,MAAM,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAchH;;;OAGG;IACH,OAAO,CAAC,oBAAoB;
|
|
1
|
+
{"version":3,"file":"PermissionEngine.d.ts","sourceRoot":"","sources":["../../src/engines/PermissionEngine.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,UAAU,EAEV,iBAAiB,EAGjB,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EAErB,sBAAsB,EAGtB,QAAQ,EACX,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EAAE,wBAAwB,EAAE,MAAM,gCAAgC,CAAC;AAoB1E;;;;;;GAMG;AACH,eAAO,MAAM,uBAAuB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAW1D,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,+BAA+B,qBAAqB,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,qBACa,gBAAiB,SAAQ,UAAU,CAAC,gBAAgB,CAAC;IAC9D,WAAkB,QAAQ,IAAI,gBAAgB,CAE7C;IAED,OAAO,CAAC,QAAQ,CAAkC;IAClD,OAAO,CAAC,UAAU,CAAkD;IAEvD,MAAM,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAchH;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAmC5B;;;;OAIG;IACU,eAAe,CACxB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,qBAAqB,CAAC;IAYjC;;;;OAIG;IACU,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAInF;;;;;;;;;;;;OAYG;YACW,qBAAqB;IAmBnC;;;;;;;OAOG;IACU,2BAA2B,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAI5F;;;;OAIG;IACU,4BAA4B,CAAC,OAAO,EAAE,QAAQ,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAI7F;;;OAGG;IACU,sBAAsB,CAC/B,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAMlC,iDAAiD;IACjD,IAAW,OAAO,IAAI,wBAAwB,EAAE,CAE/C;IAED,6FAA6F;IACtF,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,sBAAsB,GAAG,SAAS;IAI1E;;;;;;OAMG;IACI,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,iBAAiB,GAAG,MAAM,EAAE;IAInF;;;;OAIG;WACW,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAIvD,wEAAwE;IACjE,uBAAuB,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,sBAAsB,CAAC,GAAG,IAAI;IAIpF;;;;;;;;;;;;;;;;OAgBG;IACU,gBAAgB,CACzB,IAAI,EAAE,QAAQ,EACd,UAAU,EAAE,MAAM,EAClB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,MAAM,EAAE,gBAAgB,GACzB,OAAO,CAAC,IAAI,CAAC;IAOhB;;;;;;;;;OASG;IACU,gBAAgB,CAAC,MAAM,GAAE,qBAA0B,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;IAkDlG;;;OAGG;IACH,OAAO,CAAC,uBAAuB;IAS/B;;;;OAIG;IACH,OAAO,CAAC,sBAAsB;CAMjC"}
|
|
@@ -100,13 +100,19 @@ let PermissionEngine = class PermissionEngine extends BaseEngine {
|
|
|
100
100
|
const activeDomains = this._domains.filter((d) => d.IsActive);
|
|
101
101
|
for (const domain of activeDomains) {
|
|
102
102
|
try {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
// TryCreateInstance (NOT CreateInstance) because CreateInstance never returns null
|
|
104
|
+
// for an unregistered key — it falls back to the anchor base class. Using the
|
|
105
|
+
// explicit-result API is what makes the "skip unresolvable domains" branch below
|
|
106
|
+
// actually reachable, instead of installing a method-less abstract-base stub as a
|
|
107
|
+
// live provider (which then poisons every fan-out in this engine).
|
|
108
|
+
const resolution = MJGlobal.Instance.ClassFactory.TryCreateInstance(PermissionProviderBase, domain.ProviderClassName);
|
|
109
|
+
if (resolution.Resolved && resolution.Instance) {
|
|
110
|
+
this._providers.set(domain.Name, resolution.Instance);
|
|
106
111
|
}
|
|
107
112
|
else {
|
|
108
113
|
LogError(`PermissionEngine: no provider registered for ProviderClassName '${domain.ProviderClassName}' ` +
|
|
109
|
-
`(domain '${domain.Name}'). Domain will be skipped until a provider is registered
|
|
114
|
+
`(domain '${domain.Name}'). Domain will be skipped until a provider is registered. ` +
|
|
115
|
+
`${resolution.Reason ?? ''}`);
|
|
110
116
|
}
|
|
111
117
|
}
|
|
112
118
|
catch (err) {
|
|
@@ -141,8 +147,24 @@ let PermissionEngine = class PermissionEngine extends BaseEngine {
|
|
|
141
147
|
* Powers the Sharing Center "User Access Report" view.
|
|
142
148
|
*/
|
|
143
149
|
async GetAllUserPermissions(user) {
|
|
150
|
+
return this.fanOutAcrossProviders('GetAllUserPermissions', (p) => p.GetUserResources(user));
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Runs `invoke` against every registered provider in parallel and concatenates the results,
|
|
154
|
+
* isolating per-provider failures so one broken domain degrades to "contributes nothing"
|
|
155
|
+
* rather than rejecting the whole aggregate.
|
|
156
|
+
*
|
|
157
|
+
* The `Promise.resolve().then(...)` wrapper is load-bearing and NOT ceremony: `allSettled`
|
|
158
|
+
* only isolates *rejections*, but a mapper that throws SYNCHRONOUSLY (the classic case being a
|
|
159
|
+
* provider whose method is `undefined`, e.g. a hollow stub) throws inside `.map()` — before
|
|
160
|
+
* `allSettled` is ever called — and rejects the entire call. Deferring the invocation into a
|
|
161
|
+
* promise body converts that synchronous throw into a rejection `allSettled` can contain.
|
|
162
|
+
* Defense in depth: `instantiateProviders` should prevent hollow providers from being
|
|
163
|
+
* installed at all, but this fan-out must not be the thing that fails if one ever slips in.
|
|
164
|
+
*/
|
|
165
|
+
async fanOutAcrossProviders(callerName, invoke) {
|
|
144
166
|
const providers = Array.from(this._providers.values());
|
|
145
|
-
const settled = await Promise.allSettled(providers.map((p) =>
|
|
167
|
+
const settled = await Promise.allSettled(providers.map((p) => Promise.resolve().then(() => invoke(p))));
|
|
146
168
|
const results = [];
|
|
147
169
|
settled.forEach((outcome, i) => {
|
|
148
170
|
const provider = providers[i];
|
|
@@ -151,7 +173,7 @@ let PermissionEngine = class PermissionEngine extends BaseEngine {
|
|
|
151
173
|
results.push(...outcome.value);
|
|
152
174
|
}
|
|
153
175
|
else {
|
|
154
|
-
LogError(`PermissionEngine
|
|
176
|
+
LogError(`PermissionEngine.${callerName}: provider '${provider?.DomainName}' threw: ${outcome.reason}`);
|
|
155
177
|
}
|
|
156
178
|
});
|
|
157
179
|
return results;
|
|
@@ -165,20 +187,7 @@ let PermissionEngine = class PermissionEngine extends BaseEngine {
|
|
|
165
187
|
* Providers are queried in parallel; failures are logged and skipped.
|
|
166
188
|
*/
|
|
167
189
|
async GetPermissionsGrantedByUser(grantor) {
|
|
168
|
-
|
|
169
|
-
const settled = await Promise.allSettled(providers.map((p) => p.GetPermissionsGrantedByUser(grantor)));
|
|
170
|
-
const results = [];
|
|
171
|
-
settled.forEach((outcome, i) => {
|
|
172
|
-
const provider = providers[i];
|
|
173
|
-
if (outcome.status === 'fulfilled') {
|
|
174
|
-
if (outcome.value?.length)
|
|
175
|
-
results.push(...outcome.value);
|
|
176
|
-
}
|
|
177
|
-
else {
|
|
178
|
-
LogError(`PermissionEngine.GetPermissionsGrantedByUser: provider '${provider.DomainName}' threw: ${outcome.reason}`);
|
|
179
|
-
}
|
|
180
|
-
});
|
|
181
|
-
return results;
|
|
190
|
+
return this.fanOutAcrossProviders('GetPermissionsGrantedByUser', (p) => p.GetPermissionsGrantedByUser(grantor));
|
|
182
191
|
}
|
|
183
192
|
/**
|
|
184
193
|
* Aggregate every permission where this user is the grantee AND someone else is the
|
|
@@ -186,20 +195,7 @@ let PermissionEngine = class PermissionEngine extends BaseEngine {
|
|
|
186
195
|
* access. Providers are queried in parallel; failures are logged and skipped.
|
|
187
196
|
*/
|
|
188
197
|
async GetPermissionsSharedWithUser(grantee) {
|
|
189
|
-
|
|
190
|
-
const settled = await Promise.allSettled(providers.map((p) => p.GetPermissionsSharedWithUser(grantee)));
|
|
191
|
-
const results = [];
|
|
192
|
-
settled.forEach((outcome, i) => {
|
|
193
|
-
const provider = providers[i];
|
|
194
|
-
if (outcome.status === 'fulfilled') {
|
|
195
|
-
if (outcome.value?.length)
|
|
196
|
-
results.push(...outcome.value);
|
|
197
|
-
}
|
|
198
|
-
else {
|
|
199
|
-
LogError(`PermissionEngine.GetPermissionsSharedWithUser: provider '${provider.DomainName}' threw: ${outcome.reason}`);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
return results;
|
|
198
|
+
return this.fanOutAcrossProviders('GetPermissionsSharedWithUser', (p) => p.GetPermissionsSharedWithUser(grantee));
|
|
203
199
|
}
|
|
204
200
|
/**
|
|
205
201
|
* Get every grantee's permissions on a specific resource within a domain.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PermissionEngine.js","sourceRoot":"","sources":["../../src/engines/PermissionEngine.ts"],"names":[],"mappings":";;;;;;AAAA,OAAO,EACH,UAAU,EAGV,QAAQ,EACR,WAAW,EAMX,qBAAqB,EACrB,sBAAsB,EACtB,kBAAkB,EAClB,OAAO,GAEV,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAIlD;;;;GAIG;AACH,MAAM,wBAAwB,GAA6B;IACvD,oBAAoB,EAAE,CAAC,wBAAwB,CAAC;IAChD,mBAAmB,EAAE,CAAC,uBAAuB,CAAC;IAC9C,uBAAuB,EAAE,CAAC,2BAA2B,EAAE,oCAAoC,CAAC;IAC5F,sBAAsB,EAAE,CAAC,0BAA0B,CAAC;IACpD,sBAAsB,EAAE,CAAC,0BAA0B,CAAC;IACpD,wBAAwB,EAAE,CAAC,4BAA4B,CAAC;IACxD,sBAAsB,EAAE,CAAC,0BAA0B,CAAC;IACpD,sBAAsB,EAAE,CAAC,0BAA0B,CAAC;IACpD,mBAAmB,EAAE,CAAC,uBAAuB,CAAC;IAC9C,sBAAsB,EAAE,CAAC,0BAA0B,CAAC;CACvD,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAA2B;IAC3D,oBAAoB,EAAE,sBAAsB;IAC5C,mBAAmB,EAAE,2BAA2B;IAChD,uBAAuB,EAAE,wBAAwB;IACjD,sBAAsB,EAAE,yBAAyB;IACjD,sBAAsB,EAAE,wBAAwB;IAChD,wBAAwB,EAAE,yBAAyB;IACnD,sBAAsB,EAAE,mBAAmB;IAC3C,sBAAsB,EAAE,iCAAiC;IACzD,mBAAmB,EAAE,8BAA8B;IACnD,sBAAsB,EAAE,kBAAkB;CAC7C,CAAC;AAEF,wFAAwF;AACxF,MAAM,CAAC,MAAM,+BAA+B,GAAG,kBAAkB,CAAC;AAElE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAEI,IAAM,gBAAgB,GAAtB,MAAM,gBAAiB,SAAQ,UAA4B;IAA3D;;QAKK,aAAQ,GAA+B,EAAE,CAAC;QAC1C,eAAU,GAAwC,IAAI,GAAG,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"PermissionEngine.js","sourceRoot":"","sources":["../../src/engines/PermissionEngine.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseEngine, BaseEntityEvent, IMetadataProvider, UserInfo } from "@memberjunction/core";
|
|
2
|
+
import { ChatMessage } from "@memberjunction/ai";
|
|
2
3
|
import { Observable } from "rxjs";
|
|
3
4
|
import { MJConversationEntity, MJConversationDetailEntity, MJConversationDetailEntityType, MJAIAgentRunEntity, MJAIAgentRunEntityType, MJConversationDetailRatingEntityType, MJConversationDetailArtifactEntityType, MJProjectEntity } from "../generated/entity_subclasses.js";
|
|
4
5
|
/**
|
|
@@ -46,6 +47,56 @@ export interface CreateConversationOptions {
|
|
|
46
47
|
*/
|
|
47
48
|
linkedRecordId?: string | null;
|
|
48
49
|
}
|
|
50
|
+
/**
|
|
51
|
+
* Metadata stamped on messages returned by {@link ConversationEngine.GetAgentContextWindow}.
|
|
52
|
+
*
|
|
53
|
+
* Type-locality tradeoff: the agent framework's `AgentChatMessageMetadata` (in
|
|
54
|
+
* `@memberjunction/ai-core-plus`) carries these same optional fields, but this package
|
|
55
|
+
* cannot import from ai-core-plus — ai-core-plus depends on core-entities, so importing it
|
|
56
|
+
* here would create a package cycle. This structural type is therefore defined locally and
|
|
57
|
+
* kept assignment-compatible with `AgentChatMessageMetadata`; if you add a field here,
|
|
58
|
+
* mirror it there.
|
|
59
|
+
*/
|
|
60
|
+
export type ConversationContextMetadata = {
|
|
61
|
+
/** `ConversationDetail.Sequence` of the row this message came from */
|
|
62
|
+
sequence?: number;
|
|
63
|
+
/** ID of the `ConversationDetail` row this message came from */
|
|
64
|
+
conversationDetailId?: string;
|
|
65
|
+
/** True only on the synthetic first message carrying the persisted conversation summary */
|
|
66
|
+
isConversationSummary?: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* On the summary message: the boundary row's `Sequence`. The summary covers every row
|
|
69
|
+
* with `Sequence` below this value; the boundary row itself and everything after are
|
|
70
|
+
* included raw in the window.
|
|
71
|
+
*/
|
|
72
|
+
summaryBoundarySequence?: number;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* A conversation message shaped for agent context assembly — the return element of
|
|
76
|
+
* {@link ConversationEngine.GetAgentContextWindow}.
|
|
77
|
+
*/
|
|
78
|
+
export type ConversationContextMessage = ChatMessage<ConversationContextMetadata>;
|
|
79
|
+
/**
|
|
80
|
+
* The minimal row shape {@link ConversationEngine.AssembleContextWindow} needs —
|
|
81
|
+
* satisfied by full `MJConversationDetailEntity` instances AND by
|
|
82
|
+
* `ResultType: 'simple'` RunView rows selecting {@link ConversationWindowFields}.
|
|
83
|
+
* Server-side callers use the latter to load fresh per request (entity RLS via
|
|
84
|
+
* contextUser, per-request provider) without instantiating entities or touching the
|
|
85
|
+
* engine's process-global cache.
|
|
86
|
+
*/
|
|
87
|
+
export interface ConversationWindowSourceRow {
|
|
88
|
+
ID: string;
|
|
89
|
+
Sequence: number;
|
|
90
|
+
Role: string | null;
|
|
91
|
+
Message: string | null;
|
|
92
|
+
SummaryOfEarlierConversation: string | null;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* The Fields list a fresh per-request load must select to satisfy
|
|
96
|
+
* {@link ConversationWindowSourceRow} — single-sourced so server callers cannot drift
|
|
97
|
+
* from the assembler's requirements.
|
|
98
|
+
*/
|
|
99
|
+
export declare const ConversationWindowFields: readonly (keyof ConversationWindowSourceRow)[];
|
|
49
100
|
export interface SharedByInfo {
|
|
50
101
|
/** Grantor user ID. Null when the share predates the `SharedByUserID` column. */
|
|
51
102
|
UserID: string | null;
|
|
@@ -449,6 +500,83 @@ export declare class ConversationEngine extends BaseEngine<ConversationEngine> {
|
|
|
449
500
|
* Returns true if conversation details are cached for the given conversation.
|
|
450
501
|
*/
|
|
451
502
|
HasCachedDetails(conversationId: string): boolean;
|
|
503
|
+
/**
|
|
504
|
+
* Builds the agent-facing context window for a conversation.
|
|
505
|
+
*
|
|
506
|
+
* When a persisted cross-turn summary exists (the row with the highest `Sequence`
|
|
507
|
+
* whose `SummaryOfEarlierConversation` is non-null), the window is
|
|
508
|
+
* `[summary-as-message, boundary row (raw), ...tail (raw)]` — the summary covers every
|
|
509
|
+
* row with `Sequence` below the boundary's, so there is no gap and no overlap. When no
|
|
510
|
+
* summary exists, the window is all messages, optionally capped to the most recent
|
|
511
|
+
* `maxTailMessages` (parity with the legacy sliding-window history load).
|
|
512
|
+
*
|
|
513
|
+
* Served from the per-conversation `_detailCache`: the first touch of a conversation
|
|
514
|
+
* pays one `GetConversationComplete` query; every subsequent call is a warm in-memory
|
|
515
|
+
* slice kept current by the engine's entity-event handlers.
|
|
516
|
+
*
|
|
517
|
+
* @param conversationId - The conversation to build the window for
|
|
518
|
+
* @param contextUser - User context for the (cold-miss) cache load
|
|
519
|
+
* @param options.excludeDetailIds - Detail rows to omit — e.g. the in-flight
|
|
520
|
+
* agent-response placeholder row created before the agent executes
|
|
521
|
+
* @param options.maxTailMessages - With NO summary boundary present: cap the window to
|
|
522
|
+
* the most recent N messages. Ignored when a boundary exists — the summary already
|
|
523
|
+
* covers everything before it, and cutting into the post-boundary tail would create
|
|
524
|
+
* a coverage gap.
|
|
525
|
+
* @returns Messages in chronological order, each stamped with
|
|
526
|
+
* {@link ConversationContextMetadata}
|
|
527
|
+
*/
|
|
528
|
+
GetAgentContextWindow(conversationId: string, contextUser: UserInfo, options?: {
|
|
529
|
+
excludeDetailIds?: string[];
|
|
530
|
+
maxTailMessages?: number;
|
|
531
|
+
}): Promise<ConversationContextMessage[]>;
|
|
532
|
+
/**
|
|
533
|
+
* Loads a conversation's window source rows FRESH — one RunView through the given
|
|
534
|
+
* provider with the contextUser (entity RLS applies), never touching this engine's
|
|
535
|
+
* process-global detail cache. The single source of the fresh-load query shape
|
|
536
|
+
* (entity name, filter, order, fields) for every server-side caller: the agent
|
|
537
|
+
* resolver's history loader and the cross-turn compaction pass both consume this,
|
|
538
|
+
* so the two can never drift apart. THROWS on load failure — servers must fail
|
|
539
|
+
* loudly rather than proceed against an empty history.
|
|
540
|
+
*
|
|
541
|
+
* @param conversationId - The conversation whose detail rows to load
|
|
542
|
+
* @param contextUser - The requesting user (entity RLS is applied under this user)
|
|
543
|
+
* @param provider - Optional per-request metadata provider; falls back to the global default
|
|
544
|
+
* @returns The conversation's rows in Sequence order, shaped for {@link AssembleContextWindow}
|
|
545
|
+
* @throws When the underlying RunView reports failure (never returns a silent empty set)
|
|
546
|
+
*/
|
|
547
|
+
static LoadWindowRowsFresh(conversationId: string, contextUser: UserInfo, provider?: IMetadataProvider): Promise<ConversationWindowSourceRow[]>;
|
|
548
|
+
/**
|
|
549
|
+
* Pure window assembly over already-loaded rows: exclude → sort by Sequence →
|
|
550
|
+
* summary-boundary fold → tail cap. Extracted from {@link GetAgentContextWindow} so
|
|
551
|
+
* SERVER-SIDE callers (agent resolver, cross-turn compaction) can load rows fresh
|
|
552
|
+
* per request — via {@link LoadWindowRowsFresh} with the per-request provider and
|
|
553
|
+
* contextUser, applying entity RLS and never populating this engine's
|
|
554
|
+
* process-global detail cache — and still share the exact same fold math the
|
|
555
|
+
* cache-backed client path uses.
|
|
556
|
+
*
|
|
557
|
+
* Accepts the minimal {@link ConversationWindowSourceRow} shape, satisfied by full
|
|
558
|
+
* entities AND `ResultType: 'simple'` rows selecting {@link ConversationWindowFields}.
|
|
559
|
+
*/
|
|
560
|
+
static AssembleContextWindow(details: ReadonlyArray<ConversationWindowSourceRow>, options?: {
|
|
561
|
+
excludeDetailIds?: string[];
|
|
562
|
+
maxTailMessages?: number;
|
|
563
|
+
}): ConversationContextMessage[];
|
|
564
|
+
/**
|
|
565
|
+
* Finds the summary boundary: the detail with the HIGHEST `Sequence` whose
|
|
566
|
+
* `SummaryOfEarlierConversation` is non-blank. Older summaries below it simply stop
|
|
567
|
+
* being selected (recursive-summary pattern). Returns undefined when no summary exists.
|
|
568
|
+
*/
|
|
569
|
+
private static findSummaryBoundary;
|
|
570
|
+
/** Wraps the boundary row's persisted summary as the window's synthetic first message. */
|
|
571
|
+
private static buildSummaryMessage;
|
|
572
|
+
/** Converts a raw ConversationDetail row into a context message with sequence metadata. */
|
|
573
|
+
private static detailToContextMessage;
|
|
574
|
+
/**
|
|
575
|
+
* Maps `ConversationDetail.Role` values to ChatMessage roles. Mirrors the mapping the
|
|
576
|
+
* server resolver has always used for history assembly ('AI'/'Agent' → assistant,
|
|
577
|
+
* unknown → user).
|
|
578
|
+
*/
|
|
579
|
+
private static mapDetailRoleToChatRole;
|
|
452
580
|
/**
|
|
453
581
|
* Adds a detail (message) to the cached list for a conversation.
|
|
454
582
|
* If no cache entry exists, this is a no-op (caller should LoadConversationDetails first).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/engines/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA4B,eAAe,EAAE,iBAAiB,EAA0D,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/engines/conversations.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,12 @@ import { ResourcePermissionEngine } from "../custom/ResourcePermissions/Resource
|
|
|
9
9
|
* are folded into the list by `LoadConversations` using this ID.
|
|
10
10
|
*/
|
|
11
11
|
const CONVERSATIONS_RESOURCE_TYPE_ID = '81D4BC3D-9FEB-EF11-B01A-286B35C04427';
|
|
12
|
+
/**
|
|
13
|
+
* The Fields list a fresh per-request load must select to satisfy
|
|
14
|
+
* {@link ConversationWindowSourceRow} — single-sourced so server callers cannot drift
|
|
15
|
+
* from the assembler's requirements.
|
|
16
|
+
*/
|
|
17
|
+
export const ConversationWindowFields = ['ID', 'Sequence', 'Role', 'Message', 'SummaryOfEarlierConversation'];
|
|
12
18
|
/**
|
|
13
19
|
* Helper: parse a raw ConversationDetailComplete row into typed arrays.
|
|
14
20
|
*/
|
|
@@ -1010,6 +1016,146 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1010
1016
|
const key = NormalizeUUID(conversationId);
|
|
1011
1017
|
return this._detailCache.has(key);
|
|
1012
1018
|
}
|
|
1019
|
+
// ========================================================================
|
|
1020
|
+
// AGENT CONTEXT WINDOW (cross-turn compaction assembly layer)
|
|
1021
|
+
// ========================================================================
|
|
1022
|
+
/**
|
|
1023
|
+
* Builds the agent-facing context window for a conversation.
|
|
1024
|
+
*
|
|
1025
|
+
* When a persisted cross-turn summary exists (the row with the highest `Sequence`
|
|
1026
|
+
* whose `SummaryOfEarlierConversation` is non-null), the window is
|
|
1027
|
+
* `[summary-as-message, boundary row (raw), ...tail (raw)]` — the summary covers every
|
|
1028
|
+
* row with `Sequence` below the boundary's, so there is no gap and no overlap. When no
|
|
1029
|
+
* summary exists, the window is all messages, optionally capped to the most recent
|
|
1030
|
+
* `maxTailMessages` (parity with the legacy sliding-window history load).
|
|
1031
|
+
*
|
|
1032
|
+
* Served from the per-conversation `_detailCache`: the first touch of a conversation
|
|
1033
|
+
* pays one `GetConversationComplete` query; every subsequent call is a warm in-memory
|
|
1034
|
+
* slice kept current by the engine's entity-event handlers.
|
|
1035
|
+
*
|
|
1036
|
+
* @param conversationId - The conversation to build the window for
|
|
1037
|
+
* @param contextUser - User context for the (cold-miss) cache load
|
|
1038
|
+
* @param options.excludeDetailIds - Detail rows to omit — e.g. the in-flight
|
|
1039
|
+
* agent-response placeholder row created before the agent executes
|
|
1040
|
+
* @param options.maxTailMessages - With NO summary boundary present: cap the window to
|
|
1041
|
+
* the most recent N messages. Ignored when a boundary exists — the summary already
|
|
1042
|
+
* covers everything before it, and cutting into the post-boundary tail would create
|
|
1043
|
+
* a coverage gap.
|
|
1044
|
+
* @returns Messages in chronological order, each stamped with
|
|
1045
|
+
* {@link ConversationContextMetadata}
|
|
1046
|
+
*/
|
|
1047
|
+
async GetAgentContextWindow(conversationId, contextUser, options) {
|
|
1048
|
+
await this.Config(false, contextUser);
|
|
1049
|
+
const cache = await this.LoadConversationDetails(conversationId, contextUser);
|
|
1050
|
+
return ConversationEngine.AssembleContextWindow(cache.Details, options);
|
|
1051
|
+
}
|
|
1052
|
+
/**
|
|
1053
|
+
* Loads a conversation's window source rows FRESH — one RunView through the given
|
|
1054
|
+
* provider with the contextUser (entity RLS applies), never touching this engine's
|
|
1055
|
+
* process-global detail cache. The single source of the fresh-load query shape
|
|
1056
|
+
* (entity name, filter, order, fields) for every server-side caller: the agent
|
|
1057
|
+
* resolver's history loader and the cross-turn compaction pass both consume this,
|
|
1058
|
+
* so the two can never drift apart. THROWS on load failure — servers must fail
|
|
1059
|
+
* loudly rather than proceed against an empty history.
|
|
1060
|
+
*
|
|
1061
|
+
* @param conversationId - The conversation whose detail rows to load
|
|
1062
|
+
* @param contextUser - The requesting user (entity RLS is applied under this user)
|
|
1063
|
+
* @param provider - Optional per-request metadata provider; falls back to the global default
|
|
1064
|
+
* @returns The conversation's rows in Sequence order, shaped for {@link AssembleContextWindow}
|
|
1065
|
+
* @throws When the underlying RunView reports failure (never returns a silent empty set)
|
|
1066
|
+
*/
|
|
1067
|
+
static async LoadWindowRowsFresh(conversationId, contextUser, provider) {
|
|
1068
|
+
const rv = provider ? RunView.FromMetadataProvider(provider) : new RunView();
|
|
1069
|
+
const rows = await rv.RunView({
|
|
1070
|
+
EntityName: 'MJ: Conversation Details',
|
|
1071
|
+
ExtraFilter: `ConversationID='${conversationId}'`,
|
|
1072
|
+
OrderBy: 'Sequence ASC',
|
|
1073
|
+
Fields: [...ConversationWindowFields],
|
|
1074
|
+
ResultType: 'simple',
|
|
1075
|
+
}, contextUser);
|
|
1076
|
+
if (!rows.Success) {
|
|
1077
|
+
throw new Error(`Failed to load conversation window for conversation ${conversationId}: ${rows.ErrorMessage}`);
|
|
1078
|
+
}
|
|
1079
|
+
return rows.Results || [];
|
|
1080
|
+
}
|
|
1081
|
+
/**
|
|
1082
|
+
* Pure window assembly over already-loaded rows: exclude → sort by Sequence →
|
|
1083
|
+
* summary-boundary fold → tail cap. Extracted from {@link GetAgentContextWindow} so
|
|
1084
|
+
* SERVER-SIDE callers (agent resolver, cross-turn compaction) can load rows fresh
|
|
1085
|
+
* per request — via {@link LoadWindowRowsFresh} with the per-request provider and
|
|
1086
|
+
* contextUser, applying entity RLS and never populating this engine's
|
|
1087
|
+
* process-global detail cache — and still share the exact same fold math the
|
|
1088
|
+
* cache-backed client path uses.
|
|
1089
|
+
*
|
|
1090
|
+
* Accepts the minimal {@link ConversationWindowSourceRow} shape, satisfied by full
|
|
1091
|
+
* entities AND `ResultType: 'simple'` rows selecting {@link ConversationWindowFields}.
|
|
1092
|
+
*/
|
|
1093
|
+
static AssembleContextWindow(details, options) {
|
|
1094
|
+
const excluded = new Set((options?.excludeDetailIds || []).map(id => NormalizeUUID(id)));
|
|
1095
|
+
const ordered = details
|
|
1096
|
+
.filter(d => !excluded.has(NormalizeUUID(d.ID)))
|
|
1097
|
+
.sort((a, b) => a.Sequence - b.Sequence);
|
|
1098
|
+
const boundary = ConversationEngine.findSummaryBoundary(ordered);
|
|
1099
|
+
if (boundary) {
|
|
1100
|
+
const tail = ordered.filter(d => d.Sequence >= boundary.Sequence);
|
|
1101
|
+
return [ConversationEngine.buildSummaryMessage(boundary), ...tail.map(d => ConversationEngine.detailToContextMessage(d))];
|
|
1102
|
+
}
|
|
1103
|
+
const all = ordered.map(d => ConversationEngine.detailToContextMessage(d));
|
|
1104
|
+
const cap = options?.maxTailMessages;
|
|
1105
|
+
return cap && all.length > cap ? all.slice(-cap) : all;
|
|
1106
|
+
}
|
|
1107
|
+
/**
|
|
1108
|
+
* Finds the summary boundary: the detail with the HIGHEST `Sequence` whose
|
|
1109
|
+
* `SummaryOfEarlierConversation` is non-blank. Older summaries below it simply stop
|
|
1110
|
+
* being selected (recursive-summary pattern). Returns undefined when no summary exists.
|
|
1111
|
+
*/
|
|
1112
|
+
static findSummaryBoundary(orderedDetails) {
|
|
1113
|
+
for (let i = orderedDetails.length - 1; i >= 0; i--) {
|
|
1114
|
+
const summary = orderedDetails[i].SummaryOfEarlierConversation;
|
|
1115
|
+
if (summary && summary.trim().length > 0) {
|
|
1116
|
+
return orderedDetails[i];
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
return undefined;
|
|
1120
|
+
}
|
|
1121
|
+
/** Wraps the boundary row's persisted summary as the window's synthetic first message. */
|
|
1122
|
+
static buildSummaryMessage(boundary) {
|
|
1123
|
+
return {
|
|
1124
|
+
role: 'user',
|
|
1125
|
+
content: boundary.SummaryOfEarlierConversation || '',
|
|
1126
|
+
metadata: {
|
|
1127
|
+
isConversationSummary: true,
|
|
1128
|
+
summaryBoundarySequence: boundary.Sequence,
|
|
1129
|
+
sequence: boundary.Sequence
|
|
1130
|
+
}
|
|
1131
|
+
};
|
|
1132
|
+
}
|
|
1133
|
+
/** Converts a raw ConversationDetail row into a context message with sequence metadata. */
|
|
1134
|
+
static detailToContextMessage(detail) {
|
|
1135
|
+
return {
|
|
1136
|
+
role: ConversationEngine.mapDetailRoleToChatRole(detail.Role),
|
|
1137
|
+
content: detail.Message || '',
|
|
1138
|
+
metadata: {
|
|
1139
|
+
sequence: detail.Sequence,
|
|
1140
|
+
conversationDetailId: detail.ID
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
/**
|
|
1145
|
+
* Maps `ConversationDetail.Role` values to ChatMessage roles. Mirrors the mapping the
|
|
1146
|
+
* server resolver has always used for history assembly ('AI'/'Agent' → assistant,
|
|
1147
|
+
* unknown → user).
|
|
1148
|
+
*/
|
|
1149
|
+
static mapDetailRoleToChatRole(role) {
|
|
1150
|
+
const r = (role || '').toLowerCase();
|
|
1151
|
+
if (r === 'assistant' || r === 'agent' || r === 'ai') {
|
|
1152
|
+
return 'assistant';
|
|
1153
|
+
}
|
|
1154
|
+
if (r === 'system') {
|
|
1155
|
+
return 'system';
|
|
1156
|
+
}
|
|
1157
|
+
return 'user';
|
|
1158
|
+
}
|
|
1013
1159
|
/**
|
|
1014
1160
|
* Adds a detail (message) to the cached list for a conversation.
|
|
1015
1161
|
* If no cache entry exists, this is a no-op (caller should LoadConversationDetails first).
|
|
@@ -1356,8 +1502,12 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1356
1502
|
// New detail from local event — append the entity
|
|
1357
1503
|
cached.Details.push(entity);
|
|
1358
1504
|
}
|
|
1359
|
-
|
|
1360
|
-
|
|
1505
|
+
else {
|
|
1506
|
+
// Remote NEW-row save: we can't hydrate an entity from event data alone,
|
|
1507
|
+
// and the warm cache would keep serving without this row forever (loads
|
|
1508
|
+
// short-circuit on a cache hit) — evict so the next load re-queries.
|
|
1509
|
+
this.removeDetailCache(conversationId);
|
|
1510
|
+
}
|
|
1361
1511
|
}
|
|
1362
1512
|
else if (action === 'delete') {
|
|
1363
1513
|
cached.Details = cached.Details.filter(d => !UUIDsEqual(d.ID, id));
|