Static ReadonlyAuthorizesTrue when the action authorizes its caller itself, so a public entry point such as the
RunAction mutation may run it for an authenticated user without an MJ: Action Authorizations link. Set it only on an action that either checks an authorization the caller
must hold, or reads and writes only with the caller's own permissions. Leave it false for an
action that acts with more authority than its caller, or that reaches data or sends messages
the caller chooses but could not otherwise reach.
The engine reads the marker only from the class that runs, never from its parent classes. A subclass that overrides a marked action must declare the marker again.
Honours an audience (RunActionParams.Audience): every reader must pass the same scope-permission gate
the caller does, the search keeps only results every reader may read (SearchParams.Audience), and
SourceCounts — counted before that filtering — are left out of the output.
ProtectedInternalInternal method that must be implemented by derived action classes. This is where the actual action logic should be implemented.
The action execution parameters including typed context
Promise resolving to the action result
Executes the action with the provided parameters.
The action execution parameters including context
Promise resolving to the action result
__Scoped_Search— scope-aware universal search for AI agents.Enforces the calling agent's
SearchScopeAccesssetting and restricts the requested scope against the agent'sMJ: AI Agent Search Scopesrows (Phase IN 'AgentInvoked','Both'). Delegates the actual search toSearchEngine.Search()withScopeIDs: [resolvedScopeID].The SKILL PRINCIPAL (
AISkillID, optional) is resolved and permission-checked alongside the agent. Inside an agent run it is bound to the RUN:Context.ActiveSkillIDs(stamped by BaseAgent) must contain a named skill, and a lone active skill becomes the principal when none is named — so the model steering a Loop agent cannot widen retrieval by naming a skill it never activated. Otherwise:AISkill.SearchScopeAccesscan deny a scope the user's roles allow, or grant one they do not, and it binds asPrincipals.SkillIDfor a dimension's expansion query. A value that is not a UUID, or will not load, is refused rather than dropped.Agent identity is resolved from (in order):
AgentIDinput parameter (most common — passed by the agent executor).params.Context?.AgentID(when the agent executor stamps context).params.Context?.agentID(lowercased variant used in some execution paths).Enforcement rules (Section 5 of plans/search-scopes-rag-plus.md):
SearchScopeAccess='None'→ rejects withACCESS_DENIED.SearchScopeAccess='Assigned':ScopeIDsupplied: must match one of the agent's active AgentInvoked/Both rows.IsDefault=1row (falling back to lowest Priority).NO_DEFAULT_SCOPE.SearchScopeAccess='All':ScopeIDsupplied: used as-is.Multi-tenant
SearchContext: A tenant and secondary dimensions assemble aSearchContextthat is threaded intoSearchParams.SearchContext. The engine renders the values into every scope-level Nunjucks template (MetadataFilter, ExtraFilter, UserSearchString, FolderPath) at search time — so one scope definition serves many tenants. The same tenant goes into the permission decision (ResolveEffectivePermission), so tenant-scoped grants and denies apply to exactly the search that runs.PrimaryScopeRecordID(string) — primary tenant key (e.g. OrganizationID). Available in templates as{{ context.PrimaryScopeRecordID }}.SecondaryScopes(a JSON object string, or an object) — flat object of additional dimensions. Each value must bestring | number | boolean | string[]. Available in templates as{{ context.SecondaryScopes.<key> }}. Input that is not valid JSON, not an object, or holds an unsupported value is REFUSED (INVALID_PARAM) — never dropped, which would search without the dimension.INSIDE AN AGENT RUN THE RUN'S SCOPE IS AUTHORITATIVE. BaseAgent stamps the run's validated scope on every dispatch (
RunActionParams.RunScope); the two inputs are model-written there, so they are bound to the run asAISkillIDis:PrimaryScopeRecordID→ the run's tenant (none when the run is unscoped);PrimaryScopeRecordIDthat is not the run's (case-insensitive), or any value when the run has no tenant → refused withINVALID_PARAMand aForbiddensearch-log row. The host sets a run's tenant (ExecuteAgentParams.PrimaryScope*); the model never does;SecondaryScopesmay restate a dimension the run sets only with an equal value (refused otherwise, as above); a dimension the run does not set is added, bounded by the scope's own dimension trust rules. Outside an agent run (noRunScope, e.g. a direct call) both inputs are used as given.SearchContextis included only when a tenant or a secondary dimension applies — none preserves the original "no per-call tenant filter" behavior. Seeguides/SEARCH_SCOPES_AND_RAG_GUIDE.md§10 for the full multi-tenant model and template-rendering details.Example: Agent tool call
Example: Per-tenant scoped call (a direct call outside an agent run; inside a run scoped to `<org-uuid>`, omit
PrimaryScopeRecordID— the run's tenant applies)