@memberjunction/core-entities 6.2.0-edge.4 → 6.2.0-edge.5
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/.tsbuildinfo +1 -1
- package/dist/custom/ConversationWriteAccess.d.ts +5 -0
- package/dist/custom/ConversationWriteAccess.d.ts.map +1 -1
- package/dist/custom/ConversationWriteAccess.js +6 -6
- package/dist/custom/ConversationWriteAccess.js.map +1 -1
- package/dist/custom/MJConversationDetailEntityExtended.d.ts +46 -4
- package/dist/custom/MJConversationDetailEntityExtended.d.ts.map +1 -1
- package/dist/custom/MJConversationDetailEntityExtended.js +185 -28
- package/dist/custom/MJConversationDetailEntityExtended.js.map +1 -1
- package/dist/custom/MJThemeEntityExtended.d.ts +13 -0
- package/dist/custom/MJThemeEntityExtended.d.ts.map +1 -0
- package/dist/custom/MJThemeEntityExtended.js +37 -0
- package/dist/custom/MJThemeEntityExtended.js.map +1 -0
- package/dist/engines/InstanceConfigEngine.d.ts.map +1 -1
- package/dist/engines/InstanceConfigEngine.js +13 -3
- package/dist/engines/InstanceConfigEngine.js.map +1 -1
- package/dist/engines/PermissionEngine.d.ts +5 -4
- package/dist/engines/PermissionEngine.d.ts.map +1 -1
- package/dist/engines/PermissionEngine.js +7 -5
- package/dist/engines/PermissionEngine.js.map +1 -1
- package/dist/engines/QueryEngine.d.ts +81 -6
- package/dist/engines/QueryEngine.d.ts.map +1 -1
- package/dist/engines/QueryEngine.js +283 -4
- package/dist/engines/QueryEngine.js.map +1 -1
- package/dist/engines/UserInfoEngine.d.ts +48 -2
- package/dist/engines/UserInfoEngine.d.ts.map +1 -1
- package/dist/engines/UserInfoEngine.js +65 -8
- package/dist/engines/UserInfoEngine.js.map +1 -1
- package/dist/engines/conversations.d.ts +19 -1
- package/dist/engines/conversations.d.ts.map +1 -1
- package/dist/engines/conversations.js +125 -22
- package/dist/engines/conversations.js.map +1 -1
- package/dist/generated/entities/__mj.d.ts +1762 -44
- package/dist/generated/entities/__mj.d.ts.map +1 -1
- package/dist/generated/entities/__mj.js +2231 -18
- package/dist/generated/entities/__mj.js.map +1 -1
- package/dist/generated/remote_operations.d.ts +17 -5
- package/dist/generated/remote_operations.d.ts.map +1 -1
- package/dist/generated/remote_operations.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/package.json +6 -5
|
@@ -44,14 +44,20 @@ function sameId(a, b) {
|
|
|
44
44
|
* Helper: parse a raw ConversationDetailComplete row into typed arrays.
|
|
45
45
|
*/
|
|
46
46
|
export function ParseConversationDetailComplete(queryResult) {
|
|
47
|
+
const rawArtifacts = queryResult.ArtifactsJSON
|
|
48
|
+
? JSON.parse(queryResult.ArtifactsJSON)
|
|
49
|
+
: [];
|
|
50
|
+
const artifacts = rawArtifacts.slice().sort((a, b) => {
|
|
51
|
+
const aTime = ToEpochMs(a.VersionCreatedAt) ?? ToEpochMs(a.__mj_CreatedAt) ?? 0;
|
|
52
|
+
const bTime = ToEpochMs(b.VersionCreatedAt) ?? ToEpochMs(b.__mj_CreatedAt) ?? 0;
|
|
53
|
+
return aTime - bTime;
|
|
54
|
+
});
|
|
47
55
|
return {
|
|
48
56
|
...queryResult,
|
|
49
57
|
agentRuns: queryResult.AgentRunsJSON
|
|
50
58
|
? JSON.parse(queryResult.AgentRunsJSON)
|
|
51
59
|
: [],
|
|
52
|
-
artifacts
|
|
53
|
-
? JSON.parse(queryResult.ArtifactsJSON)
|
|
54
|
-
: [],
|
|
60
|
+
artifacts,
|
|
55
61
|
ratings: queryResult.RatingsJSON
|
|
56
62
|
? JSON.parse(queryResult.RatingsJSON)
|
|
57
63
|
: []
|
|
@@ -113,12 +119,63 @@ function detailIdInFilter(detailIds) {
|
|
|
113
119
|
function idInFilter(ids) {
|
|
114
120
|
return `ID IN (${quoteIdList(ids)})`;
|
|
115
121
|
}
|
|
116
|
-
/**
|
|
122
|
+
/**
|
|
123
|
+
* Priority tier for choosing which `AIAgentRun` represents a `ConversationDetail` in the UI
|
|
124
|
+
* (gear icon -> "View Agent Run", token/cost stats, duration):
|
|
125
|
+
* - Tier 0: Root primary orchestrating run (`ParentRunID == null` and `ContinuationDepth == 0`)
|
|
126
|
+
* - Tier 1: Root post-workflow continuation synthesis run (`ParentRunID == null` and `ContinuationDepth > 0`)
|
|
127
|
+
* - Tier 2: Child / sub-agent run (`ParentRunID != null`)
|
|
128
|
+
*/
|
|
129
|
+
function agentRunPriorityTier(run) {
|
|
130
|
+
if (run.ParentRunID) {
|
|
131
|
+
return 2;
|
|
132
|
+
}
|
|
133
|
+
return (run.ContinuationDepth ?? 0) > 0 ? 1 : 0;
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Returns true when `candidate` should replace `existing` as the representative agent run
|
|
137
|
+
* for a single `ConversationDetailID`. Lower tier wins (primary root run beats continuation
|
|
138
|
+
* synthesis run, which beats child sub-agent run); within the same tier, a strictly newer run
|
|
139
|
+
* wins (so user retries of a message replace the earlier run while DESC-ordered queries keep
|
|
140
|
+
* the first row when timestamps are tied or omitted in test fixtures).
|
|
141
|
+
*/
|
|
142
|
+
export function ShouldReplaceDetailAgentRun(existing, candidate) {
|
|
143
|
+
if (!existing) {
|
|
144
|
+
return true;
|
|
145
|
+
}
|
|
146
|
+
if (existing.ID && candidate.ID && UUIDsEqual(existing.ID, candidate.ID)) {
|
|
147
|
+
return true;
|
|
148
|
+
}
|
|
149
|
+
const existingTier = agentRunPriorityTier(existing);
|
|
150
|
+
const candidateTier = agentRunPriorityTier(candidate);
|
|
151
|
+
if (candidateTier !== existingTier) {
|
|
152
|
+
return candidateTier < existingTier;
|
|
153
|
+
}
|
|
154
|
+
const existingTime = ToEpochMs(existing.__mj_CreatedAt ?? existing.StartedAt) ?? 0;
|
|
155
|
+
const candidateTime = ToEpochMs(candidate.__mj_CreatedAt ?? candidate.StartedAt) ?? 0;
|
|
156
|
+
if (candidateTime !== existingTime) {
|
|
157
|
+
return candidateTime > existingTime;
|
|
158
|
+
}
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
function selectPreferredDetailAgentRun(runs) {
|
|
162
|
+
let best;
|
|
163
|
+
for (const run of runs) {
|
|
164
|
+
if (ShouldReplaceDetailAgentRun(best, run)) {
|
|
165
|
+
best = run;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return best;
|
|
169
|
+
}
|
|
170
|
+
/** Keys agent runs by the detail they belong to, preferring the primary root orchestrating run. */
|
|
117
171
|
function groupAgentRunsByDetailId(runs) {
|
|
118
172
|
const byDetailId = new Map();
|
|
119
173
|
for (const run of runs) {
|
|
120
174
|
if (run.ConversationDetailID) {
|
|
121
|
-
byDetailId.
|
|
175
|
+
const existing = byDetailId.get(run.ConversationDetailID);
|
|
176
|
+
if (ShouldReplaceDetailAgentRun(existing, run)) {
|
|
177
|
+
byDetailId.set(run.ConversationDetailID, run);
|
|
178
|
+
}
|
|
122
179
|
}
|
|
123
180
|
}
|
|
124
181
|
return byDetailId;
|
|
@@ -1036,14 +1093,29 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1036
1093
|
throw new Error('Conversation not found');
|
|
1037
1094
|
}
|
|
1038
1095
|
}
|
|
1096
|
+
const previousValues = {};
|
|
1097
|
+
for (const key of Object.keys(updates)) {
|
|
1098
|
+
previousValues[key] = conversation[key];
|
|
1099
|
+
}
|
|
1039
1100
|
this.mergeDataOntoRecord(conversation, updates);
|
|
1101
|
+
// Emit immediately after mutating the shared cached entity so Angular subscribers
|
|
1102
|
+
// (e.g. ConversationListComponent) run change detection synchronously rather than
|
|
1103
|
+
// seeing an uncommitted mutation during the async Save() round-trip (NG0100).
|
|
1104
|
+
this._conversations$.next([...this._conversations$.value]);
|
|
1040
1105
|
this._selfMutating = true;
|
|
1041
1106
|
try {
|
|
1042
1107
|
const saved = await conversation.Save();
|
|
1043
1108
|
if (!saved) {
|
|
1109
|
+
this.mergeDataOntoRecord(conversation, previousValues);
|
|
1110
|
+
this._conversations$.next([...this._conversations$.value]);
|
|
1044
1111
|
throw new Error(conversation.LatestResult?.Message || 'Failed to update conversation');
|
|
1045
1112
|
}
|
|
1046
1113
|
}
|
|
1114
|
+
catch (err) {
|
|
1115
|
+
this.mergeDataOntoRecord(conversation, previousValues);
|
|
1116
|
+
this._conversations$.next([...this._conversations$.value]);
|
|
1117
|
+
throw err;
|
|
1118
|
+
}
|
|
1047
1119
|
finally {
|
|
1048
1120
|
this._selfMutating = false;
|
|
1049
1121
|
}
|
|
@@ -1531,14 +1603,16 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1531
1603
|
if (!row.ID)
|
|
1532
1604
|
continue;
|
|
1533
1605
|
const parsed = ParseConversationDetailComplete(row);
|
|
1534
|
-
// Agent runs
|
|
1535
|
-
|
|
1536
|
-
|
|
1606
|
+
// Agent runs (prefer primary root orchestrating run over continuation synthesis or child runs)
|
|
1607
|
+
const agentRunData = selectPreferredDetailAgentRun(parsed.agentRuns);
|
|
1608
|
+
if (agentRunData) {
|
|
1537
1609
|
const agentRun = await md.GetEntityObject('MJ: AI Agent Runs', contextUser);
|
|
1538
1610
|
agentRun.LoadFromData({
|
|
1539
1611
|
ID: agentRunData.ID,
|
|
1540
1612
|
AgentID: agentRunData.AgentID,
|
|
1541
1613
|
Agent: agentRunData.Agent,
|
|
1614
|
+
ParentRunID: agentRunData.ParentRunID ?? null,
|
|
1615
|
+
ContinuationDepth: agentRunData.ContinuationDepth ?? 0,
|
|
1542
1616
|
Status: agentRunData.Status,
|
|
1543
1617
|
__mj_CreatedAt: agentRunData.__mj_CreatedAt,
|
|
1544
1618
|
__mj_UpdatedAt: agentRunData.__mj_UpdatedAt,
|
|
@@ -1600,6 +1674,7 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1600
1674
|
{
|
|
1601
1675
|
EntityName: 'MJ: Conversation Detail Artifacts',
|
|
1602
1676
|
ExtraFilter: `${byDetail} AND Direction='Output'`,
|
|
1677
|
+
OrderBy: '__mj_CreatedAt ASC',
|
|
1603
1678
|
ResultType: 'simple'
|
|
1604
1679
|
}
|
|
1605
1680
|
], contextUser);
|
|
@@ -1670,6 +1745,14 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1670
1745
|
list.push(mergeArtifactJSON(junction, version, artifact));
|
|
1671
1746
|
byDetailId.set(junction.ConversationDetailID, list);
|
|
1672
1747
|
}
|
|
1748
|
+
for (const [detailId, list] of byDetailId) {
|
|
1749
|
+
list.sort((a, b) => {
|
|
1750
|
+
const aTime = ToEpochMs(a.VersionCreatedAt) ?? ToEpochMs(a.__mj_CreatedAt) ?? 0;
|
|
1751
|
+
const bTime = ToEpochMs(b.VersionCreatedAt) ?? ToEpochMs(b.__mj_CreatedAt) ?? 0;
|
|
1752
|
+
return aTime - bTime;
|
|
1753
|
+
});
|
|
1754
|
+
byDetailId.set(detailId, list);
|
|
1755
|
+
}
|
|
1673
1756
|
return byDetailId;
|
|
1674
1757
|
}
|
|
1675
1758
|
/** Reads the artifact rows behind a window's artifact versions. */
|
|
@@ -1756,12 +1839,14 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1756
1839
|
// check for `LoadFromData` before calling it, and hydrate a real entity when
|
|
1757
1840
|
// we only have plain data. Mirrors the pattern in
|
|
1758
1841
|
// `conversation-chat-area.component.ts#onMessageComplete` (line ~909).
|
|
1759
|
-
|
|
1760
|
-
|
|
1842
|
+
const agentRunData = selectPreferredDetailAgentRun(parsed.agentRuns);
|
|
1843
|
+
if (agentRunData) {
|
|
1761
1844
|
const mergeFields = {
|
|
1762
1845
|
ID: agentRunData.ID,
|
|
1763
1846
|
AgentID: agentRunData.AgentID,
|
|
1764
1847
|
Agent: agentRunData.Agent,
|
|
1848
|
+
ParentRunID: agentRunData.ParentRunID ?? null,
|
|
1849
|
+
ContinuationDepth: agentRunData.ContinuationDepth ?? 0,
|
|
1765
1850
|
Status: agentRunData.Status,
|
|
1766
1851
|
__mj_CreatedAt: agentRunData.__mj_CreatedAt,
|
|
1767
1852
|
__mj_UpdatedAt: agentRunData.__mj_UpdatedAt,
|
|
@@ -1771,15 +1856,17 @@ export class ConversationEngine extends BaseEngine {
|
|
|
1771
1856
|
ConversationDetailID: agentRunData.ConversationDetailID
|
|
1772
1857
|
};
|
|
1773
1858
|
const existingRun = existing.AgentRunsByDetailId.get(row.ID);
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1859
|
+
if (ShouldReplaceDetailAgentRun(existingRun, agentRunData)) {
|
|
1860
|
+
const existingRunIsEntity = !!existingRun && typeof existingRun.LoadFromData === 'function';
|
|
1861
|
+
if (existingRunIsEntity) {
|
|
1862
|
+
existingRun.LoadFromData(mergeFields);
|
|
1863
|
+
}
|
|
1864
|
+
else {
|
|
1865
|
+
// Either no existing run, or existing value is plain JSON — hydrate fresh.
|
|
1866
|
+
const newRun = await md.GetEntityObject('MJ: AI Agent Runs', contextUser);
|
|
1867
|
+
newRun.LoadFromData(existingRun ? { ...existingRun, ...mergeFields } : agentRunData);
|
|
1868
|
+
existing.AgentRunsByDetailId.set(row.ID, newRun);
|
|
1869
|
+
}
|
|
1783
1870
|
}
|
|
1784
1871
|
}
|
|
1785
1872
|
// Replace artifacts per-detail (plain data, cheap to replace)
|
|
@@ -2523,7 +2610,9 @@ export class ConversationEngine extends BaseEngine {
|
|
|
2523
2610
|
return this._detailCache.get(key)?.AgentRunsByDetailId ?? new Map();
|
|
2524
2611
|
}
|
|
2525
2612
|
/**
|
|
2526
|
-
* Adds or updates an agent run in the cache for a specific detail
|
|
2613
|
+
* Adds or updates an agent run in the cache for a specific detail, honoring
|
|
2614
|
+
* {@link ShouldReplaceDetailAgentRun} so a continuation/sub-agent run never overwrites
|
|
2615
|
+
* the primary root orchestrating run for that detail.
|
|
2527
2616
|
*
|
|
2528
2617
|
* @param conversationId - The conversation ID
|
|
2529
2618
|
* @param detailId - The detail ID the agent run is associated with
|
|
@@ -2532,8 +2621,22 @@ export class ConversationEngine extends BaseEngine {
|
|
|
2532
2621
|
SetAgentRunForDetail(conversationId, detailId, agentRun) {
|
|
2533
2622
|
const key = NormalizeUUID(conversationId);
|
|
2534
2623
|
const cached = this._detailCache.get(key);
|
|
2535
|
-
if (cached) {
|
|
2536
|
-
|
|
2624
|
+
if (!cached) {
|
|
2625
|
+
return;
|
|
2626
|
+
}
|
|
2627
|
+
let targetDetailKey = detailId;
|
|
2628
|
+
let existing = cached.AgentRunsByDetailId.get(detailId);
|
|
2629
|
+
if (!existing) {
|
|
2630
|
+
for (const [cachedDetailId, cachedRun] of cached.AgentRunsByDetailId) {
|
|
2631
|
+
if (UUIDsEqual(cachedDetailId, detailId)) {
|
|
2632
|
+
targetDetailKey = cachedDetailId;
|
|
2633
|
+
existing = cachedRun;
|
|
2634
|
+
break;
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2638
|
+
if (ShouldReplaceDetailAgentRun(existing, agentRun)) {
|
|
2639
|
+
cached.AgentRunsByDetailId.set(targetDetailKey, agentRun);
|
|
2537
2640
|
}
|
|
2538
2641
|
}
|
|
2539
2642
|
// ========================================================================
|