v6.2.0-edge.1: AI usage analytics, personal folders, and cache-friendly Loop agents
AI usage analytics with a trustworthy cost basis, personal conversation folders, a multi-select conversation sidebar, and Loop agents that keep provider prompt caches warm across iterations. Plus the Betty LLM provider, the Opus 5.5 / GPT-6 / Grok 4.7 catalog refresh, and fixes to durable entity actions, magic links and Data Explorer views.
Upgrade Notes apply: six schema migrations apply on mj migrate, cache-invalidation broadcasts no longer carry row data by default, and prompt model selection now enforces the runner’s model type.
Edge builds are prereleases.
MemberJunction 6.x is released under the Business Source License 1.1; 5.x and earlier stay ISC. What the licence grants, and what needs certification, is in the licence section of the v6 upgrade guide.
New Features
Section titled “New Features”- AI usage analytics on a cost basis you can trust (
@memberjunction/ai-prompts,@memberjunction/ai-agents,@memberjunction/ai-core-plus,@memberjunction/core-entities,@memberjunction/ng-dashboards,@memberjunction/ng-query-viewer,@memberjunction/server): Cost reporting was wrong in both directions and could not be sliced by user or agent run. Prompt runs now recordUserID, cost columns sharedecimal(19,8)precision, and a newvwAIUsageFactsview gives one row per prompt run with time buckets, attribution dimensions and flags such asIsPricedandSourceKind. Eight saved queries in theAIcategory group every cost by currency beside its priced/unpriced counts, and the seven analytics dashboards read them, rendering an unpriced cost as an em dash instead of$0.00. Parallel runs now record every arm as its ownParallelChildrow; previously the losing arms were never recorded. The rules are inguides/AI_USAGE_AND_COST_ANALYTICS_GUIDE.md. - Personal conversation folders (
@memberjunction/core-entities,@memberjunction/server,@memberjunction/ng-conversations,@memberjunction/ng-core-entity-forms):Project.OwnerUserIDlets a folder belong to one user; NULL keeps it shared with the environment, as every existing folder is. The sidebar and Assign Project picker show shared folders plus the user’s own, and a row-level-security filter on the UI role’s read permission keeps a personal folder’s name unreadable through any reader for users whose every read grant is filtered. A personal folder can later be shared; a shared folder cannot be taken private. - Conversation sidebar multi-select, bulk actions and sorting (
@memberjunction/ng-conversations,@memberjunction/ng-resource-permissions,@memberjunction/core-entities): Ctrl/Cmd-click, Shift-click, hover checkboxes and touch long-press select conversations; a selection bar and a single right-click menu offer Pin, Move to folder, Share and Delete across the selection, and dragging a selection moves it into a folder. Date/Name sorting applies to every section, the search box gains a clear button, andmj-resource-share-dialogcan share several resources at once through a newContextsinput. - Host rules for multi-person chats (
@memberjunction/ng-conversations,@memberjunction/conversations-runtime,@memberjunction/server,@memberjunction/ai-agents):mj-conversation-chat-areagains opt-in inputs —AgentReplyMode,AllowedAgentIDs,MentionPeople,AgentHistoryFrom,AgentTurnHandler,AutoNameConversation— and acomposerExtraslot, so a host can run a chat among several people without forking the chat area. Every default keeps existing behaviour. - Chat-wide search in Explorer (
@memberjunction/ng-conversations,@memberjunction/ng-explorer-core,@memberjunction/core-entities): The cross-entity search panel was never mounted on any shipped surface. It is now reached from a “Search all of Chat for …” row under the conversation list, returns only conversations and artifacts the user can already see, and message search — which could never match — works on SQL Server and PostgreSQL. - Betty LLM provider (
@memberjunction/ai-betty): A newBettyLLMdriver for the MJ-native Betty assistant, alongside the unchanged@memberjunction/ai-betty-bot. It sends the latest user turn and passes earlier turns as context, where the legacy provider silently discarded follow-ups. RequiresBETTY_API_BASE_URLandAI_VENDOR_API_KEY__BETTYLLM. - Decision model type (
@memberjunction/ai,@memberjunction/core-entities): Adds theDecisionAI model type and the abstractBaseDecisionprimitive for models that answer typed questions (Likelihood, Choice, Score) with a probability per answer. No drivers ship yet. - MJAPI reloads metadata on
SIGHUP(@memberjunction/server): SendingSIGHUPhard-reloads metadata from the database without a restart.
Improvements
Section titled “Improvements”- Loop agents keep provider prompt caches warm (
@memberjunction/ai-agents,@memberjunction/ai-prompts,@memberjunction/ai-anthropic,@memberjunction/ai): The Loop system prompt rendered date/time, scratchpad and payload at its tail, which broke the provider’s prefix cache every iteration. That state now travels as a trailing<mj-runtime-state>message, carried per provider according to a newPrefixPromptCachesetting in the model-configuration cascade, which gains a vendor layer (AIVendor.Configuration). Measured on Sage from the third iteration, cached input rose to 66–96% across Anthropic, OpenAI, xAI, Gemini and Cerebras models (from 0–65%), and prompt cost per million tokens fell 37–80%. - Agents stop retrying tools that cannot succeed (
@memberjunction/ai-agents): An action that returnedSuccess: falsewas reported as successful, so agents retried unconfigured tools indefinitely. A run-scoped circuit breaker now disables an action after a fatal configuration error, after two identical failing calls, or after five consecutive failures, and tells the model why. - AI model catalog refresh (
@memberjunction/aiengine,@memberjunction/ai-deepinfra,@memberjunction/ai-siliconflow,@memberjunction/ai-elevenlabs): Adds Claude Opus 5.5, Grok 4.7, GPT-6 Sol and Luna, Xiaomi MiMo V2.6 Pro and Flash, and ElevenLabs Eleven v4 and v4 Turbo, plus new DeepInfra and SiliconFlow drivers serving GLM-5.3-Flash. - Records load their IsA subtype with fewer round trips (
@memberjunction/core): An entity can opt in (UseForLoadedRecordson itsSubtypeSelector, or a resolver’sResolveLoadHint) to predicting a record’s subtype from cached data, cutting a browser record open from three round trips to two and a 100-row entityRunViewfrom 201 to 101. - Business-user vocabulary (
@memberjunction/core,@memberjunction/ng-entity-viewer,@memberjunction/ng-dashboards,@memberjunction/ng-resource-permissions): Default data-browsing surfaces use the entity’s own plural (“No Contacts to display”) through the newEntityInfo.DisplayNamePlural, and Data Explorer and the Sharing Center no longer show the word “entity” or raw domain names. - Grid exports match what the grid shows (
@memberjunction/ng-entity-viewer): Exports now use the on-screen columns, order and headers instead of the host-declared column list, and “Send Message” appears only for entities that support communication. - Accessible names for form controls (
@memberjunction/ng-ui-components):mj-combobox,mj-switch,mj-numeric-input,mj-datepickerandmj-page-searchgainAriaLabel,AriaLabelledBy,AriaDescribedByandInputId, reaching each control’s secondary buttons too, so they no longer announce as unnamed controls (WCAG 2.1 4.1.2). - Test-harness data bridges are capped (
@memberjunction/react-test-harness): A generated component’s query returned 205,802 rows and exhausted the host process heap. Data-bridge calls now clampMaxRowstodataCaps(default 1000), and each call’s rows returned and total are reported indataAccess. - Generated forms with many related panels no longer peg the CPU (
@memberjunction/ng-base-forms): Resolving form contributions was quadratic in an entity’s related-entity panels and ran on every change-detection pass; it is now linear. - Public APIs renamed to PascalCase without breaking consumers (most packages): Public members and exported functions gain PascalCase names; every old name remains as a
@deprecatedforwarding stub, so existing code compiles and behaves identically.
Bug Fixes
Section titled “Bug Fixes”- Durable entity actions ran without their parameters (
@memberjunction/actions,@memberjunction/actions-base,@memberjunction/task-graph,@memberjunction/server): Since 6.1.0, an entity action withRunMode = 'Durable'stored its parameters as an array that the dispatcher silently dropped, so every durable binding ran with no inputs (for exampleCommon.LogActivityfailing withTypeCode is required.). Parameters now arrive by name, a task whose payload is not a name → value object or not valid JSON fails with a message naming the task, redaction uses the liveActionParamdefinitions, and the Workflows run view shows why a failed step failed. - Magic links failed on hosts whose API login is not
db_owner(@memberjunction/server): Redemption ran a rawUPDATEthe data roles are not granted, so every link — including every MJ Forms public link — answered 410 “already redeemed”. It now runs through a grantedspConsumeMagicLinkInviteprocedure on SQL Server, and a database failure reports HTTP 500 instead of 410. - Cache-invalidation events exposed rows to users who could not read them (
@memberjunction/server,@memberjunction/core,@memberjunction/core-entities): ThecacheInvalidationsubscription reaches every connected client and carried the full saved row, bypassing row-level security. Row data is now sent only for allowlisted entities, and consumers re-read the record as the signed-in user through the newResolveEntityEventRow. - Saving a default Data Explorer view with a filter did nothing (
@memberjunction/ng-entity-viewer): Configuring a Smart or Traditional filter on the unsaved default view and clicking Save created nothing and showed no error. It now opens the name prompt, suggesting a name from the filter, and saves a view; the quick-save path also stopped discarding smart filters. - Opening a saved view in its own tab froze Explorer (
@memberjunction/ng-explorer-core): The tab comparedMJ: User ViewswithUser Viewsexactly, treated every emission as a change and reloaded endlessly. Dynamic views of different entities also stopped sharing one tab and cache entry. - Ctrl/Cmd+/ never opened the command palette (
@memberjunction/ng-explorer-core,@memberjunction/ng-whiteboard): A duplicate@HostListenersilently replaced the handler. The palette now opens, lists only the user’s own apps, and its search row opens Search Results; the whiteboard’s Sees dropdown closes on an outside click again. - A
Whileloop with an unevaluable condition reported success (@memberjunction/ai-agents): A malformed condition produced a green, zero-iteration loop with the cause recorded nowhere. The loop now fails with the evaluator’s message, and loop step errors render their text instead of[object Object]. - Prompts could select a model of the wrong type (
@memberjunction/ai-prompts): A prompt with noAIModelTypeIDcould run an embeddings or realtime model with a highPowerRank.AIPromptRunnernow selects only LLMs. - Foreign-key picker reopened after being dismissed (
@memberjunction/ng-base-forms): A slow lookup reopened the dropdown after Escape or blur, and nothing could then close it. Dismissal now retires in-flight lookups, an outside press closes the panel, and Escape no longer also closes the hosting dialog. - Required fields flagged an empty string as missing (
@memberjunction/ng-base-forms): A cleared NOT NULL text field showed the red required underline for a value the save accepts. Onlynullorundefinedis now treated as empty. RunQueryfetched every row when onlyMaxRowswas set (@memberjunction/generic-database-provider): WithoutStartRow, the whole result crossed the network and was trimmed in memory — aMaxRows=10call returned 479 rows’ worth.MaxRowsalone now pages at the database.- 5.51 clients failed to delete against a 6.1 server (
@memberjunction/server):DeleteOptionsInput.SkipRecordChangesbecame required in 6.1.0, so older clients sending delete options failed schema validation. It is optional again, defaulting tofalse. - Numeric CHECK constraints produced no value list (
@memberjunction/codegen-lib,@memberjunction/core): On SQL Server, a numeric or single-valueIN (...)CHECK constraint lost both its validation and its dropdown. CodeGen now captures them, and validation compares numeric values numerically. - Provider and driver instances leaked or were rebuilt on every call (
@memberjunction/communication-engine,@memberjunction/storage,@memberjunction/auth-providers): Communication providers were rebuilt on every send, and replaced storage drivers and auth providers were dropped without releasing their SDK clients and socket pools. They are now cached or disposed. - My Profile dialog clipped its bottom controls (
@memberjunction/ng-explorer-core): On short windows the notification settings and Sign out were cut off with nothing to scroll; the settings now scroll in one region and the footer stays pinned. - Folder save failures showed a generic message (
@memberjunction/ng-conversations): The New/Edit Folder dialog now reports why a save was refused instead of “Failed to save project”. - Hover-only row actions were unreachable by keyboard (
@memberjunction/ng-entity-viewer,@memberjunction/ng-dashboards): The view selector and home dashboard revealed row actions on:hoveralone; they now also appear on keyboard focus.
Upgrade Notes
Section titled “Upgrade Notes”- Six schema migrations apply on
mj migrate:Project.OwnerUserIDwith its row-level-security filter on the UI role’s read ofMJ: Projects;AIVendor.Configuration;AIPromptRun.UserID,decimal(19,8)cost precision, analytics indexes andvwAIUsageFacts;spConsumeMagicLinkInvite; the repoint of the duplicateSTTmodel type ontoSpeech to Text; and the release metadata sync. - Cache-invalidation broadcasts carry no row data by default:
cacheSettings.recordDataBroadcastEntitiesdefaults to[], so every remote save of anAutoRefreshengine entity triggers a full reload of each matching engine config rather than an in-place update. List entities every signed-in user may read to restore in-place updates for them, or['*']to restore the previous behaviour wholesale. - Prompt model selection enforces the LLM type: a prompt whose
AIModelTypeIDnames a non-LLM type, or an explicit model override naming a model of another type, now fails with an error naming both types instead of running. Prompt model bindings pointing at another type are skipped and logged. No shipped prompt is affected. - Durable tasks queued before this release fail loudly: tasks still carrying the old array payload now fail with
Task <id> has an InputPayload that is an array…instead of running without inputs. Re-trigger the source save, or use the failed step’s Edit input & retry in the Workflows run view. BeforeAgentTurnfires on every route: it now fires once per turn before any reply row exists, carries the resolved agent and route, and a cancel leaves nothing behind. Previously it fired only on the conversation manager’s route, after that route’s placeholder row was saved.- Regenerated types narrow for numeric CHECK constraints: on SQL Server, regenerating against a schema with a numeric or single-value
IN (...)CHECK now types that property as a literal union (for example1 | 2 | 3) instead ofnumber. MJ’s own schema has none. - Removed
AIPromptRunnerfailover helpers: the unused protected methodsbuildFailoverCandidatesandcreateCandidatesFromModelsare gone; a subclass that overrode or called them must drop that code. - Mixed-version clients: metadata fields such as
EntityInfo.spCreatenow serialize under their PascalCase names. A client older than its server drops them; same-version deployments are unaffected.