@memberjunction/server 6.1.3 → 6.1.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/README.md +30 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/auth/magicLink/MagicLinkService.d.ts +17 -15
- package/dist/auth/magicLink/MagicLinkService.d.ts.map +1 -1
- package/dist/auth/magicLink/MagicLinkService.js +49 -35
- package/dist/auth/magicLink/MagicLinkService.js.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.d.ts +14 -33
- package/dist/auth/magicLink/magicLinkCore.d.ts.map +1 -1
- package/dist/auth/magicLink/magicLinkCore.js +20 -52
- package/dist/auth/magicLink/magicLinkCore.js.map +1 -1
- package/dist/config.d.ts +53 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +23 -0
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +11 -23
- package/dist/context.js.map +1 -1
- package/dist/generic/CacheInvalidationResolver.d.ts +87 -3
- package/dist/generic/CacheInvalidationResolver.d.ts.map +1 -1
- package/dist/generic/CacheInvalidationResolver.js +118 -3
- package/dist/generic/CacheInvalidationResolver.js.map +1 -1
- package/dist/generic/DeleteOptionsInput.d.ts +8 -1
- package/dist/generic/DeleteOptionsInput.d.ts.map +1 -1
- package/dist/generic/DeleteOptionsInput.js +1 -1
- package/dist/generic/DeleteOptionsInput.js.map +1 -1
- package/dist/generic/ResolverBase.d.ts +4 -0
- package/dist/generic/ResolverBase.d.ts.map +1 -1
- package/dist/generic/ResolverBase.js +10 -3
- package/dist/generic/ResolverBase.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -47
- package/dist/index.js.map +1 -1
- package/dist/postgresqlCompat.d.ts +8 -0
- package/dist/postgresqlCompat.d.ts.map +1 -0
- package/dist/postgresqlCompat.js +19 -0
- package/dist/postgresqlCompat.js.map +1 -0
- package/dist/postgresqlPoolSettings.d.ts +108 -0
- package/dist/postgresqlPoolSettings.d.ts.map +1 -0
- package/dist/postgresqlPoolSettings.js +134 -0
- package/dist/postgresqlPoolSettings.js.map +1 -0
- package/dist/resolvers/AdhocQueryResolver.d.ts +10 -20
- package/dist/resolvers/AdhocQueryResolver.d.ts.map +1 -1
- package/dist/resolvers/AdhocQueryResolver.js +54 -174
- package/dist/resolvers/AdhocQueryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts +44 -6
- package/dist/resolvers/RealtimeClientSessionResolver.d.ts.map +1 -1
- package/dist/resolvers/RealtimeClientSessionResolver.js +90 -34
- package/dist/resolvers/RealtimeClientSessionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +2 -1
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/resolvers/adhocTimeout.d.ts +7 -0
- package/dist/resolvers/adhocTimeout.d.ts.map +1 -0
- package/dist/resolvers/adhocTimeout.js +12 -0
- package/dist/resolvers/adhocTimeout.js.map +1 -0
- package/dist/services/TaskGraphActionRunner.d.ts +13 -8
- package/dist/services/TaskGraphActionRunner.d.ts.map +1 -1
- package/dist/services/TaskGraphActionRunner.js +16 -15
- package/dist/services/TaskGraphActionRunner.js.map +1 -1
- package/package.json +92 -92
- package/src/__tests__/AdhocQueryResolver.bugs.test.ts +12 -35
- package/src/__tests__/AdhocQueryResolver.pagination.test.ts +6 -2
- package/src/__tests__/AdhocQueryResolver.scopeGuard.test.ts +3 -3
- package/src/__tests__/DeleteOptionsInput.wireShape.test.ts +62 -0
- package/src/__tests__/RealtimeClientSessionResolver.test.ts +216 -2
- package/src/__tests__/TaskGraphActionRunner.durablePayload.test.ts +129 -0
- package/src/__tests__/adhocTimeoutClamp.test.ts +26 -0
- package/src/__tests__/cacheInvalidationRecordData.test.ts +177 -0
- package/src/__tests__/magicLink.test.ts +15 -76
- package/src/__tests__/magicLinkService.consume.test.ts +224 -0
- package/src/__tests__/postgresqlPoolSettings.test.ts +131 -0
- package/src/__tests__/translateBracketsToPG.test.ts +20 -0
- package/src/auth/magicLink/MagicLinkService.ts +55 -36
- package/src/auth/magicLink/magicLinkCore.ts +19 -56
- package/src/config.ts +23 -0
- package/src/context.ts +11 -24
- package/src/generic/CacheInvalidationResolver.ts +139 -3
- package/src/generic/DeleteOptionsInput.ts +9 -2
- package/src/generic/ResolverBase.ts +11 -3
- package/src/index.ts +55 -48
- package/src/postgresqlCompat.ts +21 -0
- package/src/postgresqlPoolSettings.ts +213 -0
- package/src/resolvers/AdhocQueryResolver.ts +64 -194
- package/src/resolvers/RealtimeClientSessionResolver.ts +93 -35
- package/src/resolvers/RunAIAgentResolver.ts +4 -1
- package/src/resolvers/adhocTimeout.ts +12 -0
- package/src/services/TaskGraphActionRunner.ts +16 -15
- package/dist/resolvers/adhoc-query-helpers.d.ts +0 -27
- package/dist/resolvers/adhoc-query-helpers.d.ts.map +0 -1
- package/dist/resolvers/adhoc-query-helpers.js +0 -42
- package/dist/resolvers/adhoc-query-helpers.js.map +0 -1
- package/src/__tests__/adhoc-query-helpers.test.ts +0 -50
- package/src/resolvers/adhoc-query-helpers.ts +0 -50
|
@@ -148,6 +148,18 @@ export declare class UploadRealtimeRecordingResult {
|
|
|
148
148
|
export declare class RealtimeClientSessionResolver extends ResolverBase {
|
|
149
149
|
private readonly clientSessionService;
|
|
150
150
|
private readonly sessionManager;
|
|
151
|
+
/**
|
|
152
|
+
* The reason a `Save()`/`Delete()` returned false, for a log line. `CompleteMessage` renders
|
|
153
|
+
* `Message`, `Error` and every `Errors[]` entry (validation errors included, since `Save()`
|
|
154
|
+
* runs `Validate()` and records its errors there), so there is no separate `Validate()` call.
|
|
155
|
+
* When a subclass refused the write without recording anything, say so explicitly instead of
|
|
156
|
+
* "unknown error", which is what hid MJ#4791.
|
|
157
|
+
*
|
|
158
|
+
* Only a NULL `LatestResult` proves the refusal happened before the provider (nothing was
|
|
159
|
+
* registered at all). A SUCCESS `LatestResult` is an earlier save on the same instance — the
|
|
160
|
+
* refusal registered nothing — so its text must never be reported as this failure's reason.
|
|
161
|
+
*/
|
|
162
|
+
private describeSaveFailure;
|
|
151
163
|
/**
|
|
152
164
|
* Start a client-direct realtime voice session targeting `targetAgentId`.
|
|
153
165
|
*
|
|
@@ -712,6 +724,14 @@ export declare class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
712
724
|
* queries (which scan junctions across ALL details) still surface the artifact. This is the
|
|
713
725
|
* least-invasive correct anchor — no fake visible message, no orphaned artifact.
|
|
714
726
|
*
|
|
727
|
+
* TWO PRINCIPALS, deliberately: the lookup (`findLatestSessionDetailID`) and the junction row
|
|
728
|
+
* itself (`saveDetailArtifactJunction`, `MJ: Conversation Detail Artifacts`) run as `runUser`
|
|
729
|
+
* (elevated for a scoped-anonymous caller) — that junction entity is outside an anonymous
|
|
730
|
+
* caller's relay grants. The hidden ANCHOR `Conversation Detail` this may need to create runs as
|
|
731
|
+
* `callerUser` instead: like every other `MJ: Conversation Details` write, the System user is
|
|
732
|
+
* refused because it is not the conversation's owner (see {@link persistDirectActionTurn},
|
|
733
|
+
* issue #4791) — only the actual caller can create it.
|
|
734
|
+
*
|
|
715
735
|
* Strictly best-effort: every failure path logs and returns; a relayed tool call NEVER fails
|
|
716
736
|
* because history linking did.
|
|
717
737
|
*/
|
|
@@ -778,12 +798,6 @@ export declare class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
778
798
|
* than blocking the start.
|
|
779
799
|
*/
|
|
780
800
|
private resolveWidgetVoiceCapSeconds;
|
|
781
|
-
/**
|
|
782
|
-
* Persists a single transcript turn as a `Conversation Detail` stamped with the session's
|
|
783
|
-
* conversation, the mapped role, the turn text, the session id, and the owning user.
|
|
784
|
-
*
|
|
785
|
-
* @returns The boolean save result (logs `CompleteMessage` on failure).
|
|
786
|
-
*/
|
|
787
801
|
/**
|
|
788
802
|
* Stamps `RecordingStartedAt` (the recording `t0` alignment origin) + `RecordingMedia` on a
|
|
789
803
|
* just-started session when the browser captured WITH consent. Best-effort: a parse/save failure
|
|
@@ -794,6 +808,13 @@ export declare class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
794
808
|
* @param recordingStartedAt ISO-8601 recording start timestamp from the browser.
|
|
795
809
|
*/
|
|
796
810
|
private stampRecordingStart;
|
|
811
|
+
/**
|
|
812
|
+
* Persists a single transcript turn as a `Conversation Detail` stamped with the session's
|
|
813
|
+
* conversation, the mapped role, the turn text, the session id, and the owning user.
|
|
814
|
+
*
|
|
815
|
+
* @returns The boolean save result. On failure it logs the session, the write user and the
|
|
816
|
+
* reason from {@link describeSaveFailure}.
|
|
817
|
+
*/
|
|
797
818
|
private persistTranscriptTurn;
|
|
798
819
|
/**
|
|
799
820
|
* CORRECTION persistence (the client transcript's `ReplacesPrevious` marker — e.g.
|
|
@@ -814,6 +835,23 @@ export declare class RealtimeClientSessionResolver extends ResolverBase {
|
|
|
814
835
|
* Stored as stable facts (Role: 'AI', HiddenToUser: true, ExternalID: callId, CompletionTime: durationMs,
|
|
815
836
|
* Message: JSON-serialized tool facts) so that session review can reconstruct the action card dynamically
|
|
816
837
|
* on load (Option 1) without polluting user-facing speech chat bubbles.
|
|
838
|
+
*
|
|
839
|
+
* Writes as the CALLER, never the elevated run user (issue #4791). Owning the session
|
|
840
|
+
* (`loadOwnedActiveSession`'s gate has already run on every path here) does NOT imply owning
|
|
841
|
+
* the conversation: the conversation id was client-supplied at session start. What authorizes
|
|
842
|
+
* this write is the save itself, evaluated for the caller — the conversation owner gate in
|
|
843
|
+
* `MJConversationDetailEntityExtended`, plus the caller's own entity permission and Create RLS
|
|
844
|
+
* on `MJ: Conversation Details` (for the magic-link role that RLS scopes by the invite's
|
|
845
|
+
* `ScopeResourceID`, and the role belongs to a downstream app, not MJ core). That is why
|
|
846
|
+
* writing as the caller is safe even though the conversation id originated client-side.
|
|
847
|
+
* Elevating to the System user would bypass RLS entirely, and the owner gate refuses it anyway
|
|
848
|
+
* because the System user is not the conversation owner. Measured on MJ#4791 (before the
|
|
849
|
+
* core-entities fix that now records the denial): `Save()` returned `false` with a NULL `LatestResult` and an empty
|
|
850
|
+
* `ResultHistory` — indistinguishable from any other silent failure. Elevating this write is
|
|
851
|
+
* what silently dropped both hidden tool-execution turns of a real magic-link session; the
|
|
852
|
+
* denial itself still happens today, only its silence was fixed separately.
|
|
853
|
+
* `ResolveScopedAnonymousRunUser` elevates AI-**run**-entity writes only (prompt runs, agent
|
|
854
|
+
* runs, the relayed dispatch itself) — never this one.
|
|
817
855
|
*/
|
|
818
856
|
private persistDirectActionTurn;
|
|
819
857
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RealtimeClientSessionResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RealtimeClientSessionResolver.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"RealtimeClientSessionResolver.d.ts","sourceRoot":"","sources":["../../src/resolvers/RealtimeClientSessionResolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -266,6 +266,30 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
266
266
|
this.clientSessionService = new RealtimeClientSessionService();
|
|
267
267
|
this.sessionManager = new SessionManager(this.clientSessionService);
|
|
268
268
|
}
|
|
269
|
+
/**
|
|
270
|
+
* The reason a `Save()`/`Delete()` returned false, for a log line. `CompleteMessage` renders
|
|
271
|
+
* `Message`, `Error` and every `Errors[]` entry (validation errors included, since `Save()`
|
|
272
|
+
* runs `Validate()` and records its errors there), so there is no separate `Validate()` call.
|
|
273
|
+
* When a subclass refused the write without recording anything, say so explicitly instead of
|
|
274
|
+
* "unknown error", which is what hid MJ#4791.
|
|
275
|
+
*
|
|
276
|
+
* Only a NULL `LatestResult` proves the refusal happened before the provider (nothing was
|
|
277
|
+
* registered at all). A SUCCESS `LatestResult` is an earlier save on the same instance — the
|
|
278
|
+
* refusal registered nothing — so its text must never be reported as this failure's reason.
|
|
279
|
+
*/
|
|
280
|
+
describeSaveFailure(entity) {
|
|
281
|
+
const latest = entity.LatestResult;
|
|
282
|
+
if (!latest) {
|
|
283
|
+
return `no failure detail recorded (LatestResult null; ResultHistory length ${entity.ResultHistory.length}) ` +
|
|
284
|
+
'— the write was refused before reaching the provider';
|
|
285
|
+
}
|
|
286
|
+
if (latest.Success) {
|
|
287
|
+
return 'no failure detail recorded (latest result entry is a prior success; ' +
|
|
288
|
+
`ResultHistory length ${entity.ResultHistory.length})`;
|
|
289
|
+
}
|
|
290
|
+
const detail = latest.CompleteMessage?.trim();
|
|
291
|
+
return detail || `no failure detail recorded (LatestResult empty; ResultHistory length ${entity.ResultHistory.length})`;
|
|
292
|
+
}
|
|
269
293
|
/**
|
|
270
294
|
* Start a client-direct realtime voice session targeting `targetAgentId`.
|
|
271
295
|
*
|
|
@@ -412,7 +436,9 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
412
436
|
// Junction-link any artifacts the delegated run produced into the session's conversation
|
|
413
437
|
// history (best-effort) — so chat, session review, and resume carryover can all see them.
|
|
414
438
|
// Runs as `runUser`: the junction entity is not among an anonymous caller's relay grants.
|
|
415
|
-
|
|
439
|
+
// The hidden anchor detail it may need to create, though, is written by the CALLER (#4791)
|
|
440
|
+
// — same reason `persistDirectActionTurn` is: the System user is not the conversation owner.
|
|
441
|
+
await this.linkDelegatedArtifactsToConversation(session, Artifacts, runUser, contextUser, provider);
|
|
416
442
|
await this.sessionManager.Heartbeat(agentSessionId, contextUser, provider);
|
|
417
443
|
return ResultJson;
|
|
418
444
|
}
|
|
@@ -463,7 +489,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
463
489
|
};
|
|
464
490
|
session.Config_ = JSON.stringify(next);
|
|
465
491
|
if (!(await session.Save())) {
|
|
466
|
-
LogError(`RealtimeClientSessionResolver.updatePendingFeedbackRunID save failed
|
|
492
|
+
LogError(`RealtimeClientSessionResolver.updatePendingFeedbackRunID save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
|
|
467
493
|
}
|
|
468
494
|
}
|
|
469
495
|
/**
|
|
@@ -1367,7 +1393,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1367
1393
|
session.Config_ = JSON.stringify(config);
|
|
1368
1394
|
const saved = await session.Save();
|
|
1369
1395
|
if (!saved) {
|
|
1370
|
-
LogError(`RealtimeClientSessionResolver.persistObservabilityRunIDs save failed
|
|
1396
|
+
LogError(`RealtimeClientSessionResolver.persistObservabilityRunIDs save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
|
|
1371
1397
|
}
|
|
1372
1398
|
}
|
|
1373
1399
|
/**
|
|
@@ -1451,7 +1477,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1451
1477
|
const saved = await row.Save();
|
|
1452
1478
|
if (!saved) {
|
|
1453
1479
|
LogError(`SaveSessionChannelState: save failed for session ${agentSessionID} / channel ${channelID}: ` +
|
|
1454
|
-
`${row
|
|
1480
|
+
`${this.describeSaveFailure(row)}`);
|
|
1455
1481
|
}
|
|
1456
1482
|
return saved;
|
|
1457
1483
|
}
|
|
@@ -1675,7 +1701,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1675
1701
|
artifact.UserID = contextUser.ID;
|
|
1676
1702
|
artifact.Visibility = 'Always';
|
|
1677
1703
|
if (!(await artifact.Save())) {
|
|
1678
|
-
const message = `SaveSessionChannelArtifact: artifact save failed: ${artifact
|
|
1704
|
+
const message = `SaveSessionChannelArtifact: artifact save failed for session ${session.ID}: ${this.describeSaveFailure(artifact)}`;
|
|
1679
1705
|
LogError(message);
|
|
1680
1706
|
return { Success: false, ErrorMessage: message, ConversationDetailLinked: false };
|
|
1681
1707
|
}
|
|
@@ -1686,7 +1712,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1686
1712
|
version.Content = contentJson;
|
|
1687
1713
|
version.UserID = contextUser.ID;
|
|
1688
1714
|
if (!(await version.Save())) {
|
|
1689
|
-
const message = `SaveSessionChannelArtifact: artifact version save failed
|
|
1715
|
+
const message = `SaveSessionChannelArtifact: artifact version save failed for session ${session.ID} (artifact ${artifact.ID}): ${this.describeSaveFailure(version)}`;
|
|
1690
1716
|
LogError(message);
|
|
1691
1717
|
return { Success: false, ErrorMessage: message, ArtifactID: artifact.ID, ConversationDetailLinked: false };
|
|
1692
1718
|
}
|
|
@@ -1736,21 +1762,29 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1736
1762
|
* queries (which scan junctions across ALL details) still surface the artifact. This is the
|
|
1737
1763
|
* least-invasive correct anchor — no fake visible message, no orphaned artifact.
|
|
1738
1764
|
*
|
|
1765
|
+
* TWO PRINCIPALS, deliberately: the lookup (`findLatestSessionDetailID`) and the junction row
|
|
1766
|
+
* itself (`saveDetailArtifactJunction`, `MJ: Conversation Detail Artifacts`) run as `runUser`
|
|
1767
|
+
* (elevated for a scoped-anonymous caller) — that junction entity is outside an anonymous
|
|
1768
|
+
* caller's relay grants. The hidden ANCHOR `Conversation Detail` this may need to create runs as
|
|
1769
|
+
* `callerUser` instead: like every other `MJ: Conversation Details` write, the System user is
|
|
1770
|
+
* refused because it is not the conversation's owner (see {@link persistDirectActionTurn},
|
|
1771
|
+
* issue #4791) — only the actual caller can create it.
|
|
1772
|
+
*
|
|
1739
1773
|
* Strictly best-effort: every failure path logs and returns; a relayed tool call NEVER fails
|
|
1740
1774
|
* because history linking did.
|
|
1741
1775
|
*/
|
|
1742
|
-
async linkDelegatedArtifactsToConversation(session, artifacts,
|
|
1776
|
+
async linkDelegatedArtifactsToConversation(session, artifacts, runUser, callerUser, provider) {
|
|
1743
1777
|
if (!artifacts || artifacts.length === 0 || !session.ConversationID) {
|
|
1744
1778
|
return;
|
|
1745
1779
|
}
|
|
1746
1780
|
try {
|
|
1747
|
-
const detailID = (await this.findLatestSessionDetailID(session,
|
|
1748
|
-
(await this.createHiddenSessionAnchorDetail(session,
|
|
1781
|
+
const detailID = (await this.findLatestSessionDetailID(session, runUser, provider)) ??
|
|
1782
|
+
(await this.createHiddenSessionAnchorDetail(session, callerUser, provider));
|
|
1749
1783
|
if (!detailID) {
|
|
1750
1784
|
return; // anchor unavailable — logged in the helpers
|
|
1751
1785
|
}
|
|
1752
1786
|
for (const artifact of artifacts) {
|
|
1753
|
-
await this.saveDetailArtifactJunction(detailID, artifact.ArtifactVersionID,
|
|
1787
|
+
await this.saveDetailArtifactJunction(detailID, artifact.ArtifactVersionID, runUser, provider);
|
|
1754
1788
|
}
|
|
1755
1789
|
}
|
|
1756
1790
|
catch (error) {
|
|
@@ -1781,22 +1815,22 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1781
1815
|
* {@link linkDelegatedArtifactsToConversation}). Returns the new detail's id, or `null`
|
|
1782
1816
|
* (logged) when the save fails.
|
|
1783
1817
|
*/
|
|
1784
|
-
async createHiddenSessionAnchorDetail(session,
|
|
1785
|
-
const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY,
|
|
1818
|
+
async createHiddenSessionAnchorDetail(session, callerUser, provider) {
|
|
1819
|
+
const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, callerUser);
|
|
1786
1820
|
detail.NewRecord();
|
|
1787
1821
|
detail.ConversationID = session.ConversationID;
|
|
1788
1822
|
detail.Role = 'AI';
|
|
1789
1823
|
detail.HiddenToUser = true;
|
|
1790
1824
|
detail.Message = 'Artifacts produced during a realtime session (system anchor).';
|
|
1791
1825
|
detail.AgentSessionID = session.ID;
|
|
1792
|
-
// Attribute
|
|
1793
|
-
//
|
|
1826
|
+
// Attribute to the SESSION owner — the caller writing this row, whose ownership
|
|
1827
|
+
// `loadOwnedActiveSession` already proved.
|
|
1794
1828
|
detail.UserID = session.UserID;
|
|
1795
1829
|
if (await detail.Save()) {
|
|
1796
1830
|
return detail.ID;
|
|
1797
1831
|
}
|
|
1798
1832
|
LogError(`ExecuteRealtimeSessionTool: hidden anchor detail save failed for session ${session.ID}: ` +
|
|
1799
|
-
`${detail
|
|
1833
|
+
`${this.describeSaveFailure(detail)}`);
|
|
1800
1834
|
return null;
|
|
1801
1835
|
}
|
|
1802
1836
|
/**
|
|
@@ -1812,7 +1846,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1812
1846
|
const saved = await junction.Save();
|
|
1813
1847
|
if (!saved) {
|
|
1814
1848
|
LogError(`RealtimeClientSessionResolver: artifact junction save failed for detail ${conversationDetailID} / version ${artifactVersionID}: ` +
|
|
1815
|
-
`${junction
|
|
1849
|
+
`${this.describeSaveFailure(junction)}`);
|
|
1816
1850
|
}
|
|
1817
1851
|
return saved;
|
|
1818
1852
|
}
|
|
@@ -1841,7 +1875,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1841
1875
|
row.LastActiveAt = new Date();
|
|
1842
1876
|
if (!(await row.Save())) {
|
|
1843
1877
|
LogError(`SaveSessionChannelArtifact: LastActiveAt stamp failed for session ${agentSessionID} / channel ${channelID}: ` +
|
|
1844
|
-
`${row
|
|
1878
|
+
`${this.describeSaveFailure(row)}`);
|
|
1845
1879
|
}
|
|
1846
1880
|
}
|
|
1847
1881
|
catch (error) {
|
|
@@ -1974,12 +2008,6 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
1974
2008
|
const minutes = elevation?.widget.VoiceMaxSessionMinutes;
|
|
1975
2009
|
return minutes && minutes > 0 ? minutes * 60 : undefined;
|
|
1976
2010
|
}
|
|
1977
|
-
/**
|
|
1978
|
-
* Persists a single transcript turn as a `Conversation Detail` stamped with the session's
|
|
1979
|
-
* conversation, the mapped role, the turn text, the session id, and the owning user.
|
|
1980
|
-
*
|
|
1981
|
-
* @returns The boolean save result (logs `CompleteMessage` on failure).
|
|
1982
|
-
*/
|
|
1983
2011
|
/**
|
|
1984
2012
|
* Stamps `RecordingStartedAt` (the recording `t0` alignment origin) + `RecordingMedia` on a
|
|
1985
2013
|
* just-started session when the browser captured WITH consent. Best-effort: a parse/save failure
|
|
@@ -2001,9 +2029,16 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2001
2029
|
session.RecordingStartedAt = startedAt;
|
|
2002
2030
|
session.RecordingMedia = 'Audio';
|
|
2003
2031
|
if (!(await session.Save())) {
|
|
2004
|
-
LogError(`RealtimeClientSessionResolver.stampRecordingStart save failed
|
|
2032
|
+
LogError(`RealtimeClientSessionResolver.stampRecordingStart save failed for session ${session.ID}: ${this.describeSaveFailure(session)}`);
|
|
2005
2033
|
}
|
|
2006
2034
|
}
|
|
2035
|
+
/**
|
|
2036
|
+
* Persists a single transcript turn as a `Conversation Detail` stamped with the session's
|
|
2037
|
+
* conversation, the mapped role, the turn text, the session id, and the owning user.
|
|
2038
|
+
*
|
|
2039
|
+
* @returns The boolean save result. On failure it logs the session, the write user and the
|
|
2040
|
+
* reason from {@link describeSaveFailure}.
|
|
2041
|
+
*/
|
|
2007
2042
|
async persistTranscriptTurn(session, role, text, contextUser, provider, utteranceStartMs, utteranceEndMs) {
|
|
2008
2043
|
const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, contextUser);
|
|
2009
2044
|
detail.NewRecord();
|
|
@@ -2020,7 +2055,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2020
2055
|
}
|
|
2021
2056
|
const saved = await detail.Save();
|
|
2022
2057
|
if (!saved) {
|
|
2023
|
-
LogError(`RealtimeClientSessionResolver.persistTranscriptTurn save failed
|
|
2058
|
+
LogError(`RealtimeClientSessionResolver.persistTranscriptTurn save failed for session ${session.ID} (write user ${contextUser.ID}): ${this.describeSaveFailure(detail)}`);
|
|
2024
2059
|
}
|
|
2025
2060
|
return saved;
|
|
2026
2061
|
}
|
|
@@ -2051,8 +2086,13 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2051
2086
|
// ⚠️ ELEVATING INSTEAD WAS TRIED AND IS A TRAP. `ResolveScopedAnonymousRunUser` returns
|
|
2052
2087
|
// `UserCache.GetSystemUser()`, which on this deployment resolves to the unconfigured
|
|
2053
2088
|
// placeholder `not.set@nowhere.com` ("Configured provisioning user not found; falling back
|
|
2054
|
-
// to an Owner").
|
|
2055
|
-
//
|
|
2089
|
+
// to an Owner"). That user EXISTS — it is not a missing/invalid principal — and even carries
|
|
2090
|
+
// full CRUD on `MJ: Conversation Details`. It is refused anyway:
|
|
2091
|
+
// `MJConversationDetailEntityExtended`'s owner gate accepts only the conversation's OWNER or
|
|
2092
|
+
// an Edit/Owner Resource Permission grantee, and the System user is neither. Measured on
|
|
2093
|
+
// MJ#4791 (before the core-entities fix that now records the denial): `Save()` → `false`,
|
|
2094
|
+
// `LatestResult` null — indistinguishable from a permission denial, and just as silent. The
|
|
2095
|
+
// denial itself still happens; that fix only made it stop being silent.
|
|
2056
2096
|
const writeUser = contextUser;
|
|
2057
2097
|
const rv = RunView.FromMetadataProvider(provider);
|
|
2058
2098
|
const result = await rv.RunView({
|
|
@@ -2070,8 +2110,8 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2070
2110
|
//
|
|
2071
2111
|
// `ResultType: 'entity_object'` hands back hydrated entities, but they do not carry the
|
|
2072
2112
|
// context user the way `GetEntityObject(entity, user)` does — so `Save()` ran with no
|
|
2073
|
-
// principal and failed with an EMPTY message ("unknown error"), which is what
|
|
2074
|
-
//
|
|
2113
|
+
// principal and failed with an EMPTY message ("unknown error"), which is what an empty-message
|
|
2114
|
+
// failure here looked like before the re-load. The insert path beside this one has
|
|
2075
2115
|
// always used `GetEntityObject`; this now matches it, which is also why they now succeed
|
|
2076
2116
|
// and fail for the same reasons.
|
|
2077
2117
|
const previous = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, writeUser);
|
|
@@ -2097,7 +2137,7 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2097
2137
|
LogError(`RealtimeClientSessionResolver.replacePreviousTranscriptTurn save failed for session `
|
|
2098
2138
|
+ `${session.ID} — the ${mappedRole} turn keeps its PREVIOUS, shorter text and the `
|
|
2099
2139
|
+ `transcript now understates what was said: `
|
|
2100
|
-
+ `${
|
|
2140
|
+
+ `${this.describeSaveFailure(previous)}`);
|
|
2101
2141
|
}
|
|
2102
2142
|
return saved;
|
|
2103
2143
|
}
|
|
@@ -2114,14 +2154,30 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2114
2154
|
* Stored as stable facts (Role: 'AI', HiddenToUser: true, ExternalID: callId, CompletionTime: durationMs,
|
|
2115
2155
|
* Message: JSON-serialized tool facts) so that session review can reconstruct the action card dynamically
|
|
2116
2156
|
* on load (Option 1) without polluting user-facing speech chat bubbles.
|
|
2157
|
+
*
|
|
2158
|
+
* Writes as the CALLER, never the elevated run user (issue #4791). Owning the session
|
|
2159
|
+
* (`loadOwnedActiveSession`'s gate has already run on every path here) does NOT imply owning
|
|
2160
|
+
* the conversation: the conversation id was client-supplied at session start. What authorizes
|
|
2161
|
+
* this write is the save itself, evaluated for the caller — the conversation owner gate in
|
|
2162
|
+
* `MJConversationDetailEntityExtended`, plus the caller's own entity permission and Create RLS
|
|
2163
|
+
* on `MJ: Conversation Details` (for the magic-link role that RLS scopes by the invite's
|
|
2164
|
+
* `ScopeResourceID`, and the role belongs to a downstream app, not MJ core). That is why
|
|
2165
|
+
* writing as the caller is safe even though the conversation id originated client-side.
|
|
2166
|
+
* Elevating to the System user would bypass RLS entirely, and the owner gate refuses it anyway
|
|
2167
|
+
* because the System user is not the conversation owner. Measured on MJ#4791 (before the
|
|
2168
|
+
* core-entities fix that now records the denial): `Save()` returned `false` with a NULL `LatestResult` and an empty
|
|
2169
|
+
* `ResultHistory` — indistinguishable from any other silent failure. Elevating this write is
|
|
2170
|
+
* what silently dropped both hidden tool-execution turns of a real magic-link session; the
|
|
2171
|
+
* denial itself still happens today, only its silence was fixed separately.
|
|
2172
|
+
* `ResolveScopedAnonymousRunUser` elevates AI-**run**-entity writes only (prompt runs, agent
|
|
2173
|
+
* runs, the relayed dispatch itself) — never this one.
|
|
2117
2174
|
*/
|
|
2118
2175
|
async persistDirectActionTurn(session, callId, toolName, argsJson, resultJson, durationMs, contextUser, provider) {
|
|
2119
2176
|
if (!session.ConversationID) {
|
|
2120
2177
|
return false;
|
|
2121
2178
|
}
|
|
2122
2179
|
try {
|
|
2123
|
-
const
|
|
2124
|
-
const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, writeUser);
|
|
2180
|
+
const detail = await provider.GetEntityObject(CONVERSATION_DETAIL_ENTITY, contextUser);
|
|
2125
2181
|
detail.NewRecord();
|
|
2126
2182
|
detail.ConversationID = session.ConversationID;
|
|
2127
2183
|
detail.Role = 'AI';
|
|
@@ -2159,13 +2215,13 @@ let RealtimeClientSessionResolver = class RealtimeClientSessionResolver extends
|
|
|
2159
2215
|
detail.Message = JSON.stringify(factPayload);
|
|
2160
2216
|
const saved = await detail.Save();
|
|
2161
2217
|
if (!saved) {
|
|
2162
|
-
LogError(`RealtimeClientSessionResolver.persistDirectActionTurn save failed
|
|
2218
|
+
LogError(`RealtimeClientSessionResolver.persistDirectActionTurn save failed for session ${session.ID} (tool '${toolName}', call ${callId ?? 'n/a'}, write user ${contextUser.ID}): ${this.describeSaveFailure(detail)}`);
|
|
2163
2219
|
}
|
|
2164
2220
|
return saved;
|
|
2165
2221
|
}
|
|
2166
2222
|
catch (error) {
|
|
2167
2223
|
const message = error instanceof Error ? error.message : String(error);
|
|
2168
|
-
LogError(`RealtimeClientSessionResolver.persistDirectActionTurn unexpected error: ${message}`);
|
|
2224
|
+
LogError(`RealtimeClientSessionResolver.persistDirectActionTurn unexpected error for session ${session.ID}: ${message}`);
|
|
2169
2225
|
return false;
|
|
2170
2226
|
}
|
|
2171
2227
|
}
|