Skip to content

Generated file

This page is generated from the GraphQL SDL in hadron-server/src/api/graphql/schema/typeDefs.ts. To refresh it, run npm run docs:graphql from the root of this repo.

Code paths below are not local. The descriptions come from doc comments written inside hadron-server, so a path like src/api/assetPublic.ts is relative to the hadron-server repository — there is no such file here. They point at the implementation and are volatile; the durable statement is the description itself.

Convention: ID or URN

Across the GraphQL API, fields that take an entity reference (memoryId, agentId, appId, orgId, a ref argument, or id on an entity-keyed op) accept either the entity's database ID or its URN. URNs may be passed bare (e.g. acme:family-mealplan) or with the optional canonical hrn:<type>: prefix (e.g. hrn:mem:acme:family-mealplan); the legacy urn:<type>: prefix is also accepted on input. All shapes resolve to the same entity, with identical authorization and behavior. The closing line of every in-scope field's description — "Accepts the entity's ID or URN." — calls out which fields participate.

# These two queries are equivalent:
query { memory(ref: "cm5x...kqp") { name } }
query { memory(ref: "acme:family-mealplan") { name } }

GraphQL API Reference

Table of Contents * [Query](#query) * [Mutation](#mutation) * [Objects](#objects) * [AccessGrant](#accessgrant) * [ActionTicket](#actionticket) * [ActionTicketsPage](#actionticketspage) * [AddMemoryMemberPayload](#addmemorymemberpayload) * [Agent](#agent) * [AgentAIConfig](#agentaiconfig) * [AgentImport](#agentimport) * [AgentMemoryItem](#agentmemoryitem) * [AgentOrgGrant](#agentorggrant) * [AgentSchedule](#agentschedule) * [AgentSchedulesPage](#agentschedulespage) * [AgentSubscription](#agentsubscription) * [AgentWebhook](#agentwebhook) * [AgentWebhookCredentials](#agentwebhookcredentials) * [AgentWebhooksPage](#agentwebhookspage) * [AgentsPage](#agentspage) * [AiConfigTestKnob](#aiconfigtestknob) * [AiModelPrice](#aimodelprice) * [AiModelPriceList](#aimodelpricelist) * [AiProviderEndpoint](#aiproviderendpoint) * [AiServiceConfig](#aiserviceconfig) * [AiServiceConfigTestError](#aiserviceconfigtesterror) * [AiServiceConfigTestResult](#aiserviceconfigtestresult) * [AiServiceConfigTestUsed](#aiserviceconfigtestused) * [AiServiceConfigsPage](#aiserviceconfigspage) * [App](#app) * [AppAIConfig](#appaiconfig) * [AppAgent](#appagent) * [AppCreated](#appcreated) * [AppKey](#appkey) * [AppKeyCreated](#appkeycreated) * [AppMember](#appmember) * [AppRun](#apprun) * [AppRunsPage](#apprunspage) * [AppsPage](#appspage) * [Asset](#asset) * [AssetDownloadUrl](#assetdownloadurl) * [AssetListResult](#assetlistresult) * [AssetPutHeader](#assetputheader) * [AssetsPage](#assetspage) * [AuthContext](#authcontext) * [BeginAssetUploadResult](#beginassetuploadresult) * [CalendarAttendee](#calendarattendee) * [CalendarEvent](#calendarevent) * [CalendarEventTime](#calendareventtime) * [CalendarInfo](#calendarinfo) * [CalendarPerson](#calendarperson) * [CastWorkerPreview](#castworkerpreview) * [Channel](#channel) * [ChannelReadState](#channelreadstate) * [ChannelsPage](#channelspage) * [Chat](#chat) * [ChatMessage](#chatmessage) * [ChatPromptResult](#chatpromptresult) * [ChatResponseResult](#chatresponseresult) * [ChatStartResult](#chatstartresult) * [ConnectionGrant](#connectiongrant) * [ConnectionGrantsPage](#connectiongrantspage) * [CreateMemorySharePayload](#creatememorysharepayload) * [CreditBalance](#creditbalance) * [CreditLedgerEntry](#creditledgerentry) * [CreditLedgerEntryList](#creditledgerentrylist) * [DashboardWidget](#dashboardwidget) * [DashboardWidgetData](#dashboardwidgetdata) * [DashboardWidgetPreset](#dashboardwidgetpreset) * [DeleteMemorySharePayload](#deletememorysharepayload) * [DeleteTeamRolePayload](#deleteteamrolepayload) * [DriveFile](#drivefile) * [DriveFileExport](#drivefileexport) * [DriveFilePage](#drivefilepage) * [Edge](#edge) * [EdgesPage](#edgespage) * [EditorLockResult](#editorlockresult) * [EffectiveAccess](#effectiveaccess) * [EffectiveActionPolicy](#effectiveactionpolicy) * [EmailDraftInfo](#emaildraftinfo) * [ExchangeConnection](#exchangeconnection) * [ExchangeFolder](#exchangefolder) * [ExchangeMessage](#exchangemessage) * [FindNodesResult](#findnodesresult) * [GlobalSearchHit](#globalsearchhit) * [GlobalSearchResult](#globalsearchresult) * [GraphEdge](#graphedge) * [GraphEdgesPage](#graphedgespage) * [GraphNode](#graphnode) * [GraphNodesPage](#graphnodespage) * [HomeAssistantInstance](#homeassistantinstance) * [HomeAssistantInstancesPage](#homeassistantinstancespage) * [HomeAssistantOp](#homeassistantop) * [ImpersonationInfo](#impersonationinfo) * [ImpersonationSession](#impersonationsession) * [ImpersonationSessionList](#impersonationsessionlist) * [ImportNodeResult](#importnoderesult) * [InstallAgentIntoAppPayload](#installagentintoapppayload) * [InstallationPolicy](#installationpolicy) * [JoinAppPayload](#joinapppayload) * [LeaveAppPayload](#leaveapppayload) * [McpServer](#mcpserver) * [McpServerTool](#mcpservertool) * [McpServersPage](#mcpserverspage) * [MemoriesPage](#memoriespage) * [Memory](#memory) * [MemoryConfig](#memoryconfig) * [MemoryConfigApplyEntry](#memoryconfigapplyentry) * [MemoryConfigApplyResult](#memoryconfigapplyresult) * [MemoryConfigTemplate](#memoryconfigtemplate) * [MemoryConfigTemplatePayload](#memoryconfigtemplatepayload) * [MemoryConfigTemplateRule](#memoryconfigtemplaterule) * [MemoryConfigTemplatesPage](#memoryconfigtemplatespage) * [MemoryConfigWarning](#memoryconfigwarning) * [MemoryMember](#memorymember) * [MemoryOwnerTransferPreview](#memoryownertransferpreview) * [MemoryProvisioning](#memoryprovisioning) * [MemoryShare](#memoryshare) * [MemorySubscription](#memorysubscription) * [MemoryTransferBlocker](#memorytransferblocker) * [MemoryTransferDependent](#memorytransferdependent) * [MemoryValidationFinding](#memoryvalidationfinding) * [MemoryValidationResult](#memoryvalidationresult) * [MemoryValidationSkippedCheck](#memoryvalidationskippedcheck) * [Node](#node) * [NodeAuthorship](#nodeauthorship) * [NodeBatchResult](#nodebatchresult) * [NodeContentValidationReport](#nodecontentvalidationreport) * [NodeContentValidationStatus](#nodecontentvalidationstatus) * [NodeExportResult](#nodeexportresult) * [NodeHit](#nodehit) * [NodeMoveLinkWarning](#nodemovelinkwarning) * [NodeRevision](#noderevision) * [NodeRevisionEditor](#noderevisioneditor) * [NodeRoleRule](#noderolerule) * [NodeRoleRulePayload](#noderolerulepayload) * [NodeRoleRuleSourceTemplate](#noderolerulesourcetemplate) * [NodeVectorMeta](#nodevectormeta) * [ObjectList](#objectlist) * [OrgMember](#orgmember) * [Organization](#organization) * [OrganizationsPage](#organizationspage) * [OwnTeamChatReadState](#ownteamchatreadstate) * [Passage](#passage) * [PolicyLayerVerdict](#policylayerverdict) * [PrincipalGrant](#principalgrant) * [PrincipalGrantsPage](#principalgrantspage) * [PublicInvitation](#publicinvitation) * [PublicInvitationOrganization](#publicinvitationorganization) * [PublicInvitationSender](#publicinvitationsender) * [PublicOrganization](#publicorganization) * [PublicResourceRef](#publicresourceref) * [PublicResourceRefPage](#publicresourcerefpage) * [QuotaCounter](#quotacounter) * [RegisterDefaults](#registerdefaults) * [RegisterEntriesPage](#registerentriespage) * [RegisterEntry](#registerentry) * [RegisterViewChannel](#registerviewchannel) * [RegisterViewRow](#registerviewrow) * [ReleaseWorkerPayload](#releaseworkerpayload) * [ReleasedFromUser](#releasedfromuser) * [RemoveMemoryMemberPayload](#removememorymemberpayload) * [ResendSender](#resendsender) * [ResendSendersPage](#resendsenderspage) * [ResolvedAiConfig](#resolvedaiconfig) * [RevisionResult](#revisionresult) * [ScheduleBusyInterval](#schedulebusyinterval) * [ScheduleFreeBusy](#schedulefreebusy) * [Scope](#scope) * [ScopeExplanation](#scopeexplanation) * [ScopeMemoryEntry](#scopememoryentry) * [ScopesPage](#scopespage) * [SearchReplaceFieldResult](#searchreplacefieldresult) * [SearchReplaceNodeResult](#searchreplacenoderesult) * [SearchReplaceResult](#searchreplaceresult) * [SearchReplaceSkip](#searchreplaceskip) * [SearchReplaceSkipCounts](#searchreplaceskipcounts) * [SearchScopeInfo](#searchscopeinfo) * [Secret](#secret) * [SecretsPage](#secretspage) * [ServerInfo](#serverinfo) * [Session](#session) * [SkillExportPlanEntry](#skillexportplanentry) * [SkillExportReason](#skillexportreason) * [SkillFinding](#skillfinding) * [SkillOrphan](#skillorphan) * [SkillPlan](#skillplan) * [SkillPlanEntry](#skillplanentry) * [SkillPlanScopeSelection](#skillplanscopeselection) * [SkillPlanUnrecognized](#skillplanunrecognized) * [SkillSelectionResult](#skillselectionresult) * [SlackConnection](#slackconnection) * [SlackConnectionsPage](#slackconnectionspage) * [SpecCorpusMintReport](#speccorpusmintreport) * [SpecMintFinding](#specmintfinding) * [SpecOpenQuestion](#specopenquestion) * [SpecOpenQuestionGroup](#specopenquestiongroup) * [SpecReference](#specreference) * [SpecRenumberMove](#specrenumbermove) * [SpecRenumberResult](#specrenumberresult) * [SpecRenumberRewrite](#specrenumberrewrite) * [SpecTextCitation](#spectextcitation) * [StartImpersonationResult](#startimpersonationresult) * [SummarizationRequest](#summarizationrequest) * [TeamAttentionChannel](#teamattentionchannel) * [TeamAttentionPage](#teamattentionpage) * [TeamAttentionPageItem](#teamattentionpageitem) * [TeamAttentionPreviewPage](#teamattentionpreviewpage) * [TeamAttentionResult](#teamattentionresult) * [TeamAttentionSwitchoverChannel](#teamattentionswitchoverchannel) * [TeamAttentionSwitchoverPreview](#teamattentionswitchoverpreview) * [TeamAttentionSwitchoverResult](#teamattentionswitchoverresult) * [TeamAttentionSwitchoverWorker](#teamattentionswitchoverworker) * [TeamAttentionWorker](#teamattentionworker) * [TeamChatMessage](#teamchatmessage) * [TeamChatMessagesPage](#teamchatmessagespage) * [TeamCollectionsPayload](#teamcollectionspayload) * [TeamRole](#teamrole) * [TeamRolesPage](#teamrolespage) * [TeamWorkItem](#teamworkitem) * [TeamWorkItemsPage](#teamworkitemspage) * [UninstallAgentFromAppPayload](#uninstallagentfromapppayload) * [UpdateMemoryMemberRolePayload](#updatememorymemberrolepayload) * [UpdateMemoryShareRolePayload](#updatememorysharerolepayload) * [UrnResolution](#urnresolution) * [UsageActivityBucket](#usageactivitybucket) * [UsageEvent](#usageevent) * [UsageSummaryEntry](#usagesummaryentry) * [User](#user) * [UserApiKey](#userapikey) * [UserApiKeyCreateResult](#userapikeycreateresult) * [UserInvitation](#userinvitation) * [UsersPage](#userspage) * [Worker](#worker) * [WorkerContinuity](#workercontinuity) * [WorkerContinuitySession](#workercontinuitysession) * [WorkersPage](#workerspage) * [Inputs](#inputs) * [AgentFilter](#agentfilter) * [AiServiceConfigFilter](#aiserviceconfigfilter) * [AiServiceConfigTestOverride](#aiserviceconfigtestoverride) * [AppFilter](#appfilter) * [AssetFilter](#assetfilter) * [BeginAssetUploadInput](#beginassetuploadinput) * [BeginAssetUploadV2Input](#beginassetuploadv2input) * [CalendarEventTimeInput](#calendareventtimeinput) * [ChannelFilter](#channelfilter) * [CompleteAssetUploadInput](#completeassetuploadinput) * [CreateAgentScheduleInput](#createagentscheduleinput) * [CreateAgentWebhookInput](#createagentwebhookinput) * [CreateAssetReferenceNodeInput](#createassetreferencenodeinput) * [CreateChannelInput](#createchannelinput) * [CreateMemoryConfigTemplateInput](#creatememoryconfigtemplateinput) * [CreateNodeInput](#createnodeinput) * [CreateNodeRoleRuleInput](#createnoderoleruleinput) * [CreateRegisterEntryInput](#createregisterentryinput) * [CreateScopeInput](#createscopeinput) * [DashboardWidgetInput](#dashboardwidgetinput) * [EdgeFilter](#edgefilter) * [EdgeInput](#edgeinput) * [GrantCreditsInput](#grantcreditsinput) * [ImpersonationSessionFilter](#impersonationsessionfilter) * [ImportNodeInput](#importnodeinput) * [MemoryConfigTemplateFilter](#memoryconfigtemplatefilter) * [MemoryFilter](#memoryfilter) * [MergeMemoriesInput](#mergememoriesinput) * [MergeNodesInput](#mergenodesinput) * [MergeUsersInput](#mergeusersinput) * [MintActionTicketsInput](#mintactionticketsinput) * [NodeEdgeInput](#nodeedgeinput) * [NodeFilter](#nodefilter) * [NodeInput](#nodeinput) * [NodePropertySort](#nodepropertysort) * [NodeWhereInput](#nodewhereinput) * [OrganizationFilter](#organizationfilter) * [PublicAgentFilter](#publicagentfilter) * [RegisterDefaultsInput](#registerdefaultsinput) * [RegisterEntryFilter](#registerentryfilter) * [ScopeFilter](#scopefilter) * [SearchReplaceInNodesInput](#searchreplaceinnodesinput) * [SelectedSkillFilePlanInput](#selectedskillfileplaninput) * [SessionInput](#sessioninput) * [SetAgentAssetUploadEnabledInput](#setagentassetuploadenabledinput) * [SetAiModelPriceInput](#setaimodelpriceinput) * [SetQuotaLimitInput](#setquotalimitinput) * [SkillFileFactsInput](#skillfilefactsinput) * [SkillPlanInput](#skillplaninput) * [SpecSearchReplaceInput](#specsearchreplaceinput) * [TriggerAppRunInput](#triggerappruninput) * [UpdateAgentScheduleInput](#updateagentscheduleinput) * [UpdateChannelInput](#updatechannelinput) * [UpdateMemoryConfigTemplateInput](#updatememoryconfigtemplateinput) * [UpdateNodeInput](#updatenodeinput) * [UpdateNodeRoleRuleInput](#updatenoderoleruleinput) * [UpdateRegisterEntryInput](#updateregisterentryinput) * [UpdateScopeInput](#updatescopeinput) * [UsageEventInput](#usageeventinput) * [UserFilter](#userfilter) * [Enums](#enums) * [AccessSource](#accesssource) * [AgentType](#agenttype) * [AgentVisibility](#agentvisibility) * [AiApiStyle](#aiapistyle) * [AiConfigOwnerType](#aiconfigownertype) * [AiConfigTestKeySource](#aiconfigtestkeysource) * [AiConfigTestValueSource](#aiconfigtestvaluesource) * [AppMembershipRole](#appmembershiprole) * [AppMemoryKind](#appmemorykind) * [AppRunSortField](#apprunsortfield) * [AppRunStatus](#apprunstatus) * [AppRunTriggerKind](#appruntriggerkind) * [AppType](#apptype) * [AssetScanStatus](#assetscanstatus) * [CalendarEventResponse](#calendareventresponse) * [ChannelKind](#channelkind) * [ChatScope](#chatscope) * [ContentValidator](#contentvalidator) * [CorpusState](#corpusstate) * [CreateUserPermission](#createuserpermission) * [DashboardWidgetType](#dashboardwidgettype) * [DriveExportFormat](#driveexportformat) * [DriveFileKind](#drivefilekind) * [EmailFlag](#emailflag) * [EmbeddingSource](#embeddingsource) * [FindNodesMode](#findnodesmode) * [IdentifyUserMethod](#identifyusermethod) * [ImportNodeStatus](#importnodestatus) * [MemoryClass](#memoryclass) * [MemoryConfigApplyOutcome](#memoryconfigapplyoutcome) * [MemoryConfigTemplateOwnerType](#memoryconfigtemplateownertype) * [MemoryMemberRole](#memorymemberrole) * [MemoryShareRole](#memorysharerole) * [MemoryTransferHandling](#memorytransferhandling) * [MemoryValidationCheck](#memoryvalidationcheck) * [MemoryVisibility](#memoryvisibility) * [NodeAuthorshipKind](#nodeauthorshipkind) * [NodeContentValidationOutcome](#nodecontentvalidationoutcome) * [NodeContentValidationState](#nodecontentvalidationstate) * [NodeExportEncoding](#nodeexportencoding) * [NodeExportFormat](#nodeexportformat) * [NodeExportSection](#nodeexportsection) * [NodeField](#nodefield) * [NodeMergeField](#nodemergefield) * [NodeMoveLinkStatus](#nodemovelinkstatus) * [NodeRoleRuleRefState](#noderolerulerefstate) * [NodeRoleWriters](#noderolewriters) * [NodeSort](#nodesort) * [NodeTextField](#nodetextfield) * [NodeWhereCast](#nodewherecast) * [NodeWhereColumn](#nodewherecolumn) * [PrincipalType](#principaltype) * [RegisterDisclosure](#registerdisclosure) * [RegisterNamedBy](#registernamedby) * [RegisterOwnerType](#registerownertype) * [RegisterRole](#registerrole) * [Role](#role) * [ScopeOwnerType](#scopeownertype) * [SearchEntityType](#searchentitytype) * [SearchField](#searchfield) * [SearchGranularity](#searchgranularity) * [SearchReplaceSkipReason](#searchreplaceskipreason) * [SkillExportAction](#skillexportaction) * [SkillPlanIntent](#skillplanintent) * [SortDirection](#sortdirection) * [SpecMintFindingKind](#specmintfindingkind) * [SpecReferenceKind](#specreferencekind) * [SpecReferenceProblem](#specreferenceproblem) * [SpecRewriteStatus](#specrewritestatus) * [SpecTextField](#spectextfield) * [SubscriptionType](#subscriptiontype) * [SyncStatus](#syncstatus) * [UploadIntent](#uploadintent) * [UsageActivityBucketSize](#usageactivitybucketsize) * [WorkerContinuityStatus](#workercontinuitystatus) * [Scalars](#scalars) * [Boolean](#boolean) * [DateTime](#datetime) * [Float](#float) * [ID](#id) * [Int](#int) * [JSON](#json) * [String](#string)

Query

Field Argument Type Description
resolveUrn UrnResolution Resolve a fully-qualified, `hrn::`-prefixed Hadron URN (legacy `urn:` scheme also accepted) to the ids needed to reach its canonical page. Returns null when the URN is unresolvable (not found) OR the caller lacks access. Unlike the dedicated per-kind queries — which throw `Forbidden` on no-access — this query deliberately collapses both not-found and no-access to null, so a redirect resolver can 404 uniformly without disclosing which case it was. Per-kind access uses the SAME authorization rules as the dedicated queries (memory/org/agent: org membership; app: org ADMIN; node: memoryAccessFilter), only with the throw replaced by a null return. The `hrn::` prefix (legacy `urn::` also accepted) is the dispatch key — bare URNs without a type prefix are ambiguous across kinds (`root:slug` could be a memory or an agent) and resolve to null.
urn String!
effectiveAccess EffectiveAccess! issue #323: resolve the effective access a single user has to a single resource, with the grants that confer it. The authoritative backing for the 'hadron access check <user> <resource>' audit command — one resolver that reuses the server's authorization rules instead of re-deriving them client-side. 'user' (the subject) accepts a User ID or a hrn:user:<handle> URN (spec cor:urn:010:01). 'resource' is dispatched on URN type the same way resolveUrn dispatches: a memory, node, app, agent, org, or user MUST be a fully-qualified hrn:<type>: URN (a bare id is ambiguous across those kinds). A bare id with no scheme prefix is treated as an AiServiceConfig id — the only URN-less resource kind. Authorization (who may CALL this): the same visibility bar already enforced on the underlying grant tables — a platform ADMIN/OWNER, an ADMIN/OWNER of the resource's owning org, or (for a strict-owner memory) the memory's principal. Everyone else gets FORBIDDEN, NOT a silent empty result, so the command can't probe access it isn't allowed to audit. A subject with no access is a SUCCESS with an empty grants list (all capabilities false, role null) — not NOT_FOUND. An unresolvable or unknown resource ref is NOT_FOUND.
user ID!
resource ID!
node Node The uniform single-node read (#473) — subsumes the former nodeById(id:) and node(loc:, memory:) split. 'ref' accepts, in dispatch order: 1. a primary key (the unambiguous read — the old nodeById), 2. a fully-qualified node URN (`hrn:node:::`, legacy `urn:` scheme accepted), 3. a bare loc — scoped by 'memoryRef' (an ID or URN) when given; unscoped, it resolves across every readable memory and a cross-memory loc collision is REJECTED with extensions.code AMBIGUOUS_NODE_LOC (listing the candidate memoryIds) rather than silently returning one (#335). An unprefixed 3+-segment ref whose first two segments name a readable memory is treated as form 2 (a full URN); pass 'memoryRef' to force loc interpretation. raw: true skips Mustache template compilation. Soft-deleted nodes do not resolve. Access: the caller's readable-memory set (same gate the old queries used); denied and missing are both null.
ref ID!
memoryRef ID
raw Boolean
nodeExport NodeExportResult! Render one node to a portable, self-contained file (spec cor:api). full: true (default) returns the CANONICAL form — byte-identical to "hadron node export <urn>", all sections, round-trippable via "hadron node import". Requires MD or JSON; sections is ignored. full: false returns a PRESENTATION render of the selected sections (default: all); not guaranteed round-trippable. Auth: same node-read gate as node(ref:). (HTML/PDF + asUrl delivery land later; the result shape is forward-compatible.)
id ID!
format NodeExportFormat
full Boolean
sections [NodeExportSection!]
findNodes FindNodesResult! Unified node search (cor:api:090) — the single node list + retrieval surface (it replaced the removed `nodes` filter/list and `nodeSearch` rank queries in PR3). Omit `query` for a filtered list in deterministic order (subsumes `nodes`); pass `query` to rank by `mode` (keyword | vector | hybrid | regex). Lexical modes honor boolean operators (`(a OR b) AND c`, quoted phrases, NOT/-) — operator words are UPPERCASE-ONLY, so natural phrases like 'not sure' search literally — and `fields` as a ranking weight-mask. Malformed boolean syntax (unmatched parenthesis, unterminated quote) DEGRADES to a literal phrase search flagged `degraded: 'literal_fallback'` instead of erroring; only mode: regex fails loudly on bad syntax. `filter` is the structured, AND-combined filter context (SYSTEM memory class excluded by default; explicit wins). Returns a scored-hit envelope. Access-scoped identically to the per-kind node queries. orgId (optional) narrows the accessible scope to a single organization the caller is a member of; a non-member orgId returns an empty envelope (total 0, no existence disclosure). Combined with filter.isRunnable: true this is the canonical "the caller's runnable task nodes in one org" query (there is no separate myTasks surface — a task is simply an isRunnable node).
query String
mode FindNodesMode
filter NodeFilter
fields [NodeField!]
sort NodeSort
sortProperty NodePropertySort #719 — order by a properties/data JSON path; overrides sort when present.
expand Int
granularity SearchGranularity
orgId ID
limit Int
offset Int
minScore Float Optional absolute relevance floor (cor:api:090 / #497): drop ranked hits whose score is below this value. Scores are NOT comparable across modes, so the meaningful scale differs per mode (keyword ts_rank is roughly 0..a-few; vector cosine similarity is 0..1; hybrid is a small RRF fraction) — pick a floor for the mode in use. Ignored on the no-query browse list (unscored) and never trims expand-neighbours. A floor returns fewer hits, never weaker ones.
scope String Spec 049 (D-2026-09-13-002): search under a named scope — a scope id, a bare scope name (needs appRef), 'app' (the App's attached memories; needs appRef), or 'global' (the active organization's view — orgId, or your single membership). The scope's memories are intersected with your access (a lens never grants; the rest is disclosed as scope.droppedCount) and filter.memoryIds narrows WITHIN it. Omitted: the pre-049 behaviour — your accessible set, optionally narrowed by orgId.
appRef ID Spec 049: the App context for a bare scope name or for scope: 'app' (ID or URN).
object JSON #745 — read one object by its id (the object store's flat projection of a node). Returns a JSON object { id, type, ...fields } or null if not found/inaccessible. The object store is the legible sugar surface over structured storage (an object IS a node); see createObject / findObjects.
ref ID!
findObjects ObjectList! #745 — query a collection. Returns objects of the given type (the collection / node objectType) in the memory, projected flat. 'match' is an equality shorthand ({ field: value }) desugared to a where predicate with each field's cast inferred from the memory schema; 'where' is the full #719 predicate (ANDed with match); 'sort' is a single-field shorthand desugared to a property-path sort.
memoryRef String! Memory ID or fully-qualified URN (hrn:memory:...) — relative refs are rejected (spec 022).
type String!
match JSON Equality shorthand: { field: value, ... } becomes an AND of eq predicates (cast inferred from the schema).
where JSON Full #719 structured predicate over the object's fields (ANDed with match).
sort JSON Single-field sort: a map of one field to the direction string 'asc' or 'desc', e.g. { fundingUsd: 'desc' }.
limit Int
offset Int
globalSearch GlobalSearchResult! Cross-entity global search across organizations, memories, nodes, agents, apps, AiServiceConfigs, and users — one ranked, flat list of hits. By default the query shape is smart-sniffed: a PK-shaped input does an exact id lookup, a URN-shaped input matches URNs, anything else matches name + URN. Pass `fields` to force specific fields (overrides the sniff) and `entityTypes` to restrict the kinds searched. Every hit is access-scoped to the caller exactly as the per-kind queries are. Distinct from `findNodes`, which is the node-only, vector-aware retrieval surface. orgId (optional) narrows the org-anchored slices to a single organization: node/memory hits are confined to that org's memories and organization / agent / app / aiServiceConfig hits to that org. (The user slice is NOT org-scoped — users aren't org-owned; they stay gated by the caller's user-visibility as usual.) Omit for the cross-org union (default). A caller who is not a member of orgId gets an empty page (total: 0) — no org-existence disclosure; membership-gated for everyone, no admin bypass. Paged in the repo standard idiom: limit (default 50, capped 200) + offset (default 0), with total in the envelope. NOTE: total is the ranked-match count WITHIN the per-entity candidate bound (a fixed cap per entity type), not an unbounded COUNT — deep paging past that bound is not surfaced. See GlobalSearchResult.total.
query String!
entityTypes [SearchEntityType!]
fields [SearchField!]
orgId ID
limit Int
offset Int
scope String Spec 049 (D-2026-09-13-002): run the memory + node slices under a named scope — a scope id, a bare name (needs appRef), 'app' (needs appRef), or 'global' (orgId, or your single membership). See findNodes.scope; the result's scope field discloses what applied.
appRef ID Spec 049: the App context for a bare scope name or scope: 'app' (ID or URN).
edge Edge The uniform single-edge read (#473) — edges are first-class, loc-addressed peers of nodes (spec 037). 'ref' accepts a primary key or a fully-qualified edge URN (`hrn:edge:::`; the edge's memory is its SOURCE node's memory). Access: the edge's memory must be in the caller's readable set; denied and missing are both null.
ref ID!
edges EdgesPage! Uniform paginated edge list (#473). Scope: edges whose (denormalized, source-side) memory the caller can read, AND-narrowed by the filter. orgId follows cor:api:100:01 (member -> scope, non-member -> empty page, no disclosure, no admin bypass). Deterministic loc-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter EdgeFilter
orgId ID
limit Int
offset Int
nodeBatch NodeBatchResult! Batch read (spec cor:api:040) — the full node projection (select any Node fields, including content + edges) for MANY nodes in one call, eliminating the N+1 of one node(ref:) per node (e.g. 'spec lint --all'). Provide EITHER 'refs' (explicit set, returned in input order) OR 'memory' + 'locPrefix' (subtree, loc order) — not both. Each entry of 'refs' is a primary key OR a fully-qualified node URN (cor:api:140), so a URN-holding caller batches in ONE call instead of resolving each ref first. The split on a bad ref is by KIND, not by luck: a ref whose SHAPE is wrong errors the call — unqualified / relative (UrnNotQualifiedError) or a URN of the wrong entity type, e.g. 'hrn:mem:...' (BAD_USER_INPUT) — while a well-formed ref that names nothing the caller may read comes back in 'unavailable'. A caller mistake stays loud instead of hiding among denials. Per-node access is applied independently AFTER resolution: denied or missing refs come back in 'unavailable' and never fail the call. Bounded by hard caps — over the node-count cap throws BAD_USER_INPUT; over the response-size cap returns a partial result with 'truncated: true' and the dropped refs in 'omitted' (never a silent short read). Node content is returned raw — Mustache templates are NOT compiled (unlike the single-node 'node' read), since this is a bulk source read for lint / audit / migration and compiling per node would re-introduce the N+1 it eliminates.
refs [ID!]
memory ID
locPrefix String
skillPlan SkillPlan! #1177 section 3 — the SKILL SEAM: the server judges, the client does I/O. One surface serving skill lint, skill status and skill export. The client sends what it can see of each file on disk; the server returns a drift CLASS and the lint findings per declared node, plus the rendered body for anything the client is to write. A QUERY, not a mutation, and that is the contract. Nothing here writes to a node — publishing is one-way, and the only writer in the feature is the client's own filesystem. So status and lint cannot write by construction rather than by promise. The client cannot name a drift class: the vocabulary is the server's, so it cannot drift between clients. A node failing lint is never given a rendered body, so a client cannot write a file the server judged broken.
input SkillPlanInput!
selectedSkillFilePlan SkillPlan! Selected individual skill files only. Plugin bundles cannot use node selectors.
input SelectedSkillFilePlanInput!
nodeUsage ⚠️ [UsageEvent!]! DEPRECATED: use usageEvents(loc: <loc>). Carried for source-compat; there are no known callers (checked across portal / CLI / docs at #802). Usage events for a node loc, most recent first. The migration is a rename, not a rewrite — usageEvents' loc argument is the SAME verbatim exact nodeLoc match under the same gate, so nodeUsage(loc: "x", limit: n) becomes usageEvents(loc: "x", limit: n) with identical results and identical access scoping: nodeUsage(loc: 'cor:acl:010:01', limit: 20) usageEvents(loc: 'cor:acl:010:01', limit: 20) Do NOT migrate to usageEvents(nodeId: <loc>): nodeId is shape-classified, and a hierarchical loc with 3+ colon-separated segments (like the spec citation above) qualifies as node-URN shape and routes to ref resolution instead — NODE_NOT_FOUND, or another node's events (PR-986 review). The loc argument exists precisely so the migration needs no shape caveats. usageEvents is a strict superset: nodeId accepts a PK or a fully-qualified node URN (memory-precise — a bare loc cannot disambiguate the same loc in two memories), plus type filtering, memoryId scoping and offset paging. It is also the surface that gets extended; nodeUsage will not grow arguments. Access control is identical and unchanged (#802) — see usageEvents below. Deprecated for redundancy, NOT because this field is unsafe.

⚠️ DEPRECATED

Use usageEvents(loc: <loc>) — identical exact-match loc semantics and access scoping, plus PK/URN refs via nodeId, type filtering, memoryId scoping and paging (#802).
loc String!
limit Int
usageEvents [UsageEvent!]! Usage events (most recent first), optionally filtered by type, memoryId, or nodeId. Access control (#250): results are always scoped to memories the caller may read (org membership / subscriptions / public / owned personal+private per spec 034; ADMIN/OWNER see all EXCEPT another user's personal/private memories and, per spec 047, another user's user-owned memories). A memoryId the caller can't read returns [] (no existence leak); the global form (no memoryId) is bounded to the readable set, never cross-tenant. memoryId is a memory identifier — accepts the memory's ID or URN. Results are scoped on the denormalized UsageEvent.memoryId column (#796), so an event stays attributed to its memory after its node is deleted. nodeId is a node token accepting a PK, a fully-qualified URN, or a bare loc (no memoryId needed): - PK / URN → resolved to a single node; scoped via its exact nodeId FK (memory-precise). - bare loc → exact nodeLoc match, bounded to readable memories; not existence-checked. Shape-classified: a loc with 3+ colon-separated segments qualifies as node-URN shape and takes the PK/URN branch instead — use loc for those. (This used to read "like nodeUsage but access-scoped" — a comment that named an ungated sibling without anyone following the pointer back. nodeUsage was gated in #802 and deprecated as redundant in the same issue.) loc — the VERBATIM exact nodeLoc filter (never shape-sniffed, so every loc works, hierarchical spec citations included), bounded to readable memories; not existence-checked. The migration target for the deprecated nodeUsage(loc:) above — same match, same gate (PR-986 review, Codex P2). Mutually exclusive with nodeId (BAD_USER_INPUT when both are passed).
type String
memoryId ID
nodeId ID
loc String
limit Int
offset Int
nodeRevisions [NodeRevision!]! Revision history for a node, most recent first. Accepts the node's ID or URN. Access-gated on the node's memory: a node whose memory the caller can't read returns an empty list (no existence disclosure).
nodeRef ID!
limit Int
nodeRevision NodeRevision A single node-revision snapshot by its id (#617 Display). Access-gated on the snapshot's node memory (read): null when the revision does not exist OR the caller cannot read the node's current memory OR the memory the snapshot was captured in (per-snapshot gating mirrors nodeRevisions — a moved node keeps its id, so a snapshot may belong to a memory the caller cannot read). A soft-deleted node is also null (its history drops out of every read surface). Null, never an error, so there is no existence disclosure.
revisionId ID!
memoryUsageSummary [UsageSummaryEntry!]! Aggregated usage counts per node and type for a memory. Accepts the entity's ID or URN.
memoryRef ID!
usageActivity [UsageActivityBucket!]! #796 — read/write activity counts, bucketed by time. Default scope is the caller's OWN events across everything they touched; pass memoryRefs to switch to all users' events attributed to ANY of those memories (#805), each of which requires read access. The result is the UNION as a single read/write series, not a per-memory breakdown; since every event carries exactly one memory, the union equals the sum of the separate scopes with no double-counting. A ref the caller cannot read — or that does not exist — is silently dropped from the union rather than failing the query, so the result never reveals which of the named memories exist. Passing an empty list therefore yields an empty result, NOT the caller's own activity. A malformed ref is still an error. At most 20 refs. Only non-empty buckets are returned — zero-fill the window client-side. Counts only events of type read and write. Window bounds are ISO-8601 strings: from is INCLUSIVE, to is EXCLUSIVE, so adjacent windows tile without double-counting. Defaults to the 7 days ending now; a span longer than 366 days is clamped by moving from forward.
from String Window start, inclusive. Default: to minus 7 days.
to String Window end, exclusive. Default: now.
bucket UsageActivityBucketSize
memoryRefs [ID!] Switch scope to all users of these memories, unioned (each ID or URN; max 20).
timeZone String IANA zone deciding where bucket boundaries fall. Default UTC.
sessions [Session!]! Sessions VISIBLE to the caller, newest first — not only the caller's own. The scope is authorization-derived (`sessionScopeFilter`): a platform ADMIN/OWNER sees every session, an App key sees its own App's, and an ordinary user sees those of Apps in any org they belong to **plus** every session attributed to them (the self branch is identity-scoped, so it does not apply through impersonation). So a client must not present these as "my activity" — many rows may be attributed to other users or Apps. Unfiltered, this is also the GENERAL row of every `type`, worker-bound or not (#1034), so it is equally not a list of worker sessions. workerRef (#974): only sessions bound to that Worker (by id), newest first - the 'taken right now' / 'last driven by' read (cor:agt:020:03). That filter is what narrows this to worker sessions.
type String
repo String
customerId String
workerRef String
limit Int
offset Int
session Session
id ID!
user User The uniform single-user read (#473). 'ref' accepts a primary key or a `hrn:user:` URN. Resolves for the user themselves, a platform ADMIN/OWNER, or a caller sharing at least one organization with the target (the resolveUrn user gate); anyone else gets null — denied and missing are indistinguishable, preserving #384's anti-enumeration posture. PII fields (name/email) stay gated by the User field resolvers.
ref ID!
users UsersPage! Uniform paginated user list (#473) — replaces the admin-only users list and searchUsers. Platform ADMIN/OWNER: every live user, filter.query optional. Everyone else: filter.query is REQUIRED (blank/omitted -> empty page) and matching is enumeration-safe per cor:acl:070:02 — handle / githubUsername by substring, email by exact match, name only where the viewer may see it (self/admin/co-member). Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter UserFilter
limit Int
offset Int
me User Current authenticated user
authContext AuthContext The resolved principal for the credential on THIS request (issue #562). Credential-type-agnostic: works for personal API keys, JWT sessions, and App keys alike — unlike me, which is null for a valid App credential. Returns null when the presented credential does not resolve (identically for revoked / never-existed / malformed — never a token oracle). See the AuthContext type for the full security posture.
impersonationSession ImpersonationSession One admin-impersonation audit record by id. Resolvable to the session's admin, an org ADMIN/OWNER of its org, or a platform admin; anyone else gets null (denied and missing are indistinguishable).
ref ID!
impersonationSessions ImpersonationSessionList! Uniform paginated admin-impersonation audit list (createdAt-desc). Platform admins may omit orgId (unscoped); everyone else must name an org they are a live ADMIN/OWNER of. filter.activeOnly keeps only currently-live sessions. limit default 50 / cap 200.
orgId String
filter ImpersonationSessionFilter
limit Int
offset Int
organizations OrganizationsPage! Uniform paginated organization list (#473) — replaces organizations + myOrganizations. Scope: the caller's memberships; platform ADMIN/OWNER get every live org (the established admin unscoped reach) unless filter.memberOnly restricts the list to their own memberships (the old myOrganizations view, as a slice-as-filter). Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter OrganizationFilter
limit Int
offset Int
organization Organization Fetch an Organization (org member or platform ADMIN). 'ref' accepts the entity's ID or URN.
ref ID!
publicOrganization PublicOrganization cor:acl:080:02 — the sanitized PUBLIC view of a DISCOVERABLE organization, safe for a non-member. Returns non-null only when the org exposes a public footprint (a PUBLIC memory/agent, or listedOnMarketplace); otherwise null (denied is indistinguishable from not-found — anti-enumeration). Never exposes members, private memories, apps, or credentials. 'ref' accepts the org ID or URN.
ref ID!
marketplaceOrganizations PublicResourceRefPage! cor:acl:080:04 — browse the marketplace catalogue: resources opted-in via listedOnMarketplace AND publicly accessible. Public (no membership required); sanitized flat refs only (never members / private data / credentials). One paginated query per resource type; ordered by (name, id) for stable paging. - marketplaceOrganizations: listedOnMarketplace orgs. - marketplaceAgents: listed + visibility=PUBLIC agents. - marketplaceMemories: listed + public-readable memories. Drill into an org's public footprint via publicOrganization(ref:).
limit Int
offset Int
marketplaceAgents PublicResourceRefPage!
limit Int
offset Int
marketplaceMemories PublicResourceRefPage!
limit Int
offset Int
invitation PublicInvitation Invitation lookup (public to the slug holder). Returns only invite-page display fields; accepting the slug still performs its own authorization.
slug String!
memory Memory Fetch a single Memory (org member, shared-read gate, or platform ADMIN). Missing and unreadable refs both return null without an error, so a caller cannot use this field to test whether a private memory exists. Malformed refs still fail as caller errors, and operational faults still propagate. 'ref' accepts the entity's ID or URN.
ref ID!
memories MemoriesPage! Uniform paginated memory list (#473) — replaces myMemories, publicMemories, and orgSystemMemories. Default scope: the caller's union — (1) their OWN personal/private memories (always; they are user-owned and show in every org context), (2) memories their member orgs own, (3) memories their member orgs subscribe to. Per-user agent memories (userMemoryOfAgentId) are excluded. App-key callers get the union of their installed Agents' memory items. filter.visibility: PUBLIC selects the public marketplace slice instead (every PUBLIC memory — the old publicMemories). filter.memoryClasses restricts to exactly the listed classes; omitted, the noisy agent system class is hidden by default (pass it explicitly to surface system memories, e.g. the old orgSystemMemories = orgId + memoryClasses: [system]). orgId follows cor:api:100:01: member -> the "active organization" view (own personal/private + that org's owned + subscribed memories); non-member -> empty page, no existence disclosure, no admin bypass. Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter MemoryFilter
orgId ID
limit Int
offset Int
dashboardWidgets [DashboardWidget!]! #772 — the caller's placed dashboard widgets, ordered by position. Empty for a non-user (App-key) caller.
dashboardWidgetData [DashboardWidgetData!]! #797 — data for each of the caller's PLACED widgets, in position order, one entry per placement built from its own config. Empty for a non-user caller.
dashboardWidgetPresets [DashboardWidgetPreset!]! #806 — the caller's saved widget configurations, name-ordered (id tiebreak), optionally narrowed to one widget type. Empty for a non-user caller; never another user's presets.
type DashboardWidgetType Return only presets for this widget type. Omit for all of them.
exchangeConnections [ExchangeConnection!]! Exchange connections for an org. Accepts the entity's ID or URN.
orgId ID!
slackConnection SlackConnection Fetch one Slack connection by id (spec 043). Returns null when the row does not exist OR the caller is not a member of its org — no existence disclosure.
ref ID!
slackConnections SlackConnectionsPage! Uniform paginated Slack-connection list (cor:api:120). Default scope: the caller's member orgs. orgId follows cor:api:100:01 (member -> that org, non-member -> empty page, no disclosure, no admin bypass). Team-name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
orgId ID
limit Int
offset Int
mcpServer McpServer Fetch one registered external MCP server by id. Returns null when the row does not exist OR the caller is not a member of its org — no existence disclosure.
ref ID!
mcpServers McpServersPage! Uniform paginated MCP-server list (cor:api:120). Default scope: the caller's member orgs. orgId follows cor:api:100:01 (member -> that org, non-member -> empty page, no disclosure, no admin bypass). Slug-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
orgId ID
limit Int
offset Int
mcpServerTools [McpServerTool!] Live tools/list passthrough for one registered server (org member) — what authors browse to pick data.tools entries. Applies the same admissibility filter as run-tool materialization, so what it returns IS what a run can call: a disabled server errors with MCP_SERVER_DISABLED rather than listing uncallable tools. Null when the row does not exist or the caller is not a member of its org; errors with MCP_TOOL_* codes when the conduit or the external server fails.
ref ID!
secret Secret Fetch one secret's inspectable half (never the value). Null when the row does not exist OR the caller is not entitled to its owner scope — no existence disclosure. Entitlement: user -> that user; org -> org member; app -> app owner or org member; memory -> memory READ.
ref ID!
secrets SecretsPage! Paginated secret list for ONE owner scope (the hadron secret ls surface). ownerRef accepts ID or URN (cor:api:140) and defaults to the caller for ownerType user. An unresolvable ref or a non-entitled owner yields an empty page — no disclosure. Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
ownerType String!
ownerRef ID
limit Int
offset Int
homeAssistantInstance HomeAssistantInstance Fetch one registered Home Assistant instance (org member). Null when the row does not exist OR the caller is not a member of its org — no existence disclosure.
ref ID!
homeAssistantInstances HomeAssistantInstancesPage! Uniform paginated Home Assistant instance list (cor:api:120). Default scope: the caller's member orgs. orgId follows cor:api:100:01 (member -> that org, non-member -> empty page, no disclosure, no admin bypass). Slug-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
orgId ID
limit Int
offset Int
resendSender ResendSender Fetch one sanitized BYO Resend sender config; null for missing or inaccessible.
ref ID!
resendSenders ResendSendersPage! Uniform paginated BYO Resend sender list scoped to the caller's member orgs.
orgId ID
limit Int
offset Int
homeAssistantInstanceOps [HomeAssistantOp!] The ops one registered instance exposes (the closed catalog filtered by the row's allowlist) — what authors browse to pick data.tools entries; what this returns IS what a run can call. Null when the row does not exist or the caller is not a member of its org; a disabled instance errors with HA_INSTANCE_DISABLED.
ref ID!
exchangeMessages [ExchangeMessage!]!
connectionId ID!
top Int
filter String Deprecated: raw provider filters are gone; only 'isRead eq false' still translates. Use unreadOnly.
unreadOnly Boolean
exchangeFolders [ExchangeFolder!]!
connectionId ID!
calendars [CalendarInfo!]! Calendars the connected account can see. Grant scope: calendar.read.
connectionRef ID!
calendarEvents [CalendarEvent!]! Events in a time window (recurring events expand to instances, ordered chronologically). start/end are RFC 3339; calendarId defaults to the account's primary calendar. Grant scope: calendar.read.
connectionRef ID!
calendarId String
start String
end String
query String Free-text search over event fields.
top Int
calendarEvent CalendarEvent! One event by id. Grant scope: calendar.read.
connectionRef ID!
eventId ID!
calendarId String
calendarSchedule [ScheduleFreeBusy!]! Free/busy intervals for one or more schedules (calendar ids or addresses the account can read; defaults to the account's primary calendar). Structurally capped at yes/no busy windows — never event contents. Grant scope: calendar.freebusy.
connectionRef ID!
start String!
end String!
schedules [String!]
driveFiles DriveFilePage! Browse or search the connected drive. With no search criteria, lists the children of folderId (default: the drive root), folders first. Any of query/nameContains/mimeType/modifiedAfter switches to a search, most recently modified first. Grant scope: drive.read.
connectionRef ID!
folderId ID
query String Full-text search over file content.
nameContains String
mimeType String Exact MIME type filter.
modifiedAfter String RFC 3339 lower bound on last modification.
top Int
pageToken String
driveFile DriveFile! File metadata by id. Grant scope: drive.read.
connectionRef ID!
fileId ID!
driveFileExport DriveFileExport! Export a drive file as text: native documents convert (Docs export natively to MARKDOWN; spreadsheets to TEXT as CSV; presentations to TEXT), text files return verbatim. Binary files are refused (typed validation error). Size-capped with a typed file_too_large — never truncated. Grant scope: drive.read.
connectionRef ID!
fileId ID!
format DriveExportFormat
apps AppsPage! Uniform paginated app list (#473) — replaces the org-ADMIN apps(orgId!) and the member myApps. Scope: Apps in the caller's member orgs (App doesn't carry a userId, so org membership is the routing); platform ADMIN/OWNER unscoped get every live App. orgId follows cor:api:100:01 (member -> scope, non-member -> empty page, no disclosure, no admin bypass). Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only. filter.ownedByMe (#782) narrows to the caller's own user-owned (org-less) Apps for every caller including platform admins.
filter AppFilter
orgId ID
limit Int
offset Int
app App Fetch an App (member of the App's org, or platform ADMIN — the myApps exposure bar; #473 relaxed this from org ADMIN for read parity with the list). 'ref' accepts the entity's ID or URN.
ref ID!
myApp App The App authenticated by the current AppKey (null for JWT users without an App)
agent Agent The uniform single-agent read (#473) — the first top-level Agent query; previously Agents were reachable only via Organization.agents / App.agents nesting. Gate mirrors resolveUrn's agent branch: member of the Agent's org or platform ADMIN/OWNER; denied throws Forbidden, missing is null. 'ref' accepts the entity's ID or URN.
ref ID!
agents AgentsPage! Uniform paginated agent list (#473). Scope: Agents owned by the caller's member orgs; platform ADMIN/OWNER unscoped get every live Agent. orgId follows cor:api:100:01 (member -> scope, non-member -> empty page, no disclosure, no admin bypass). filter narrows by type / visibility. filter.ownedByMe (#782) narrows to the caller's own user-owned (org-less) agents for every caller including platform admins. Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter AgentFilter
orgId ID
limit Int
offset Int
publicAgents AgentsPage! #551 — the public-agent marketplace slice: every live PUBLIC agent, readable by ANY caller (no membership required), so an org can discover foreign public agents to subscribe to (008/009 AgentOrgGrant; hadron-portal#486). Kept SEPARATE from agents() on purpose: agents() stays bounded to the caller's own orgs, and the cross-org widening is this explicit, paginated surface rather than a value in a filter. filter.type narrows by AgentType; filter.visibility is IGNORED (the slice is PUBLIC by definition). Field-level exposure: a PUBLIC agent publishes its OWN definition (systemPrompt / systemMemoryId / memoryItems / imports) to every caller — that is what a marketplace listing is (#551). The fields that surface OTHER tenants (apps / appAgents / appCount / orgGrants / importedBy) are scoped to the caller's own access and come back empty for an outsider (#552, #757). Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter PublicAgentFilter
limit Int
offset Int
myAgentSubscriptions [AgentSubscription!]! 005-agent-subscription FR-005: AgentSubscriptions held by the authenticated user. Read-only in v1.
agentSubscriptions [AgentSubscription!]! 005-agent-subscription FR-022: list all AgentSubscriptions for an Agent. Authorized for ADMIN/OWNER of the Agent's owning org. Accepts the entity's ID or URN.
agentRef ID!
appNodes [Node!]! All nodes accessible by an App: the union of every installed agent's system memory (read-only) and its knowledge memoryItems (#524). Accepts the entity's ID or URN. An optional NodeFilter pushes node-level predicates server-side — e.g. filter: { isRunnable: true } to list the App's runnable task nodes (#525). The App's memory scope is authoritative, so the filter's memory-scope fields (memoryIds / memoryClasses) are rejected with BAD_USER_INPUT rather than silently ignored — use findNodes for cross-memory scoping. Only node-level predicates (nodeType, tags, isRunnable, role, locPrefix, createdBy, updatedAfter/Before, includeDeleted) apply. Unlike findNodes, system-memory nodes are NOT excluded by default.
appRef ID!
filter NodeFilter
limit Int
memoryNodes GraphNodesPage! Slim, paginated node list for rendering a memory's graph view (issue #466). Returns ONLY the fields a node-link graph draws (id, loc, name, nodeType, tags) — no content / abstract / data / tokens / edges — so the payload is a fraction of the full node fragment. The graph view loads this to completion (page by page) and renders nodes, THEN loads memoryEdges to draw the links. memoryRef accepts the memory's ID or URN; a memory the caller can't read returns an empty page (total 0, no existence disclosure). Paged via limit (default 1000, hard-capped 2000) + offset; total is the memory's full node count so the client can show "N of M" and know when it has paged them all. limit: 0 returns an empty page with just the total — the cheap "how big is this memory" node count. Deterministic loc-ascending order so offset paging never drops/duplicates.
memoryRef ID!
limit Int
offset Int
memoryEdges GraphEdgesPage! Slim, paginated edge list for a memory's graph view (issue #466) — phase two after memoryNodes. Returns endpoint IDs only (sourceId/targetId); the client already holds the nodes by the time it draws edges, so it maps id -> node itself. Scoped by the edge's denormalized memoryId (edges whose SOURCE is in the memory); a cross-memory edge may reference a target outside this memory, which the client drops as a dangling endpoint. Same access gate, paging, and deterministic loc-ascending order as memoryNodes.
memoryRef ID!
limit Int
offset Int
appAIConfig AppAIConfig Decrypted AI config for an App (org ADMIN of the App's org). Accepts the entity's ID or URN.
appRef ID!
appKeys [AppKey!]! List keys for an App (org ADMIN of the App's org). Accepts the entity's ID or URN.
appRef ID!
myUserApiKeys [UserApiKey!]! 025-oauth-for-mcp FR-004: list the calling User's API keys (active + revoked, createdAt DESC). Powers Phase 3's portal revocation UI (SC-006). Rejected with UNAUTHENTICATED for AppKey-resolved callers (no user in context). PR-137 review delta D5 — Query not present in PR 137.
agentAIConfig AgentAIConfig Agent AI config (org ADMIN) — returns the decrypted API key so the portal backend can make LLM calls on behalf of the user. 036-ai-service-config: registry-backed (the Agent's config named 'default'). Prefer resolveAIConfig for new callers. Accepts the entity's ID or URN.
agentRef ID!
aiServiceConfig AiServiceConfig The uniform single-config read (#473). 'ref' is the config's ID — AiServiceConfig is the one URN-less entity (matching resolveUrn / effectiveAccess dispatch). Masked: never returns key material, only hasApiKey + apiKeyPreview (resolveAIConfig is the privileged decrypted read). Auth mirrors the list: platform ADMIN/OWNER for HADRON_SERVER-owned configs, org ADMIN of the owning org otherwise; denied throws Forbidden, missing is null.
ref ID!
aiServiceConfigs AiServiceConfigsPage! 036-ai-service-config: masked, paginated list of AI configs (#473). Never returns key material — only hasApiKey + apiKeyPreview. filter names the owning entity. Auth: platform ADMIN/OWNER for HADRON_SERVER owners; org ADMIN of the owning org for ORGANIZATION / APP / AGENT owners. filter omitted entirely = the platform ADMIN/OWNER cross-owner view of every config (non-admin callers must filter). Name-ascending order (id tiebreak); limit default 50 / cap 200; limit: 0 -> count only.
filter AiServiceConfigFilter
limit Int
offset Int
scope Scope Spec 049 Phase 2 — the uniform single-scope read (cor:api:120). 'ref' is the scope's ID (a Scope has no URN yet). Null when missing OR when you may not read it — no existence disclosure. Read gate: an org scope to that org's members, an App scope to the App's participants, an Agent scope to whoever may read the Agent.
ref ID!
memoryConfig MemoryConfig! #1325 — a memory's config, for the memory's managers only. For anyone else, and for a missing memory, the same MEMORY_NOT_FOUND.
memoryRef ID!
memoryConfigTemplate MemoryConfigTemplate #1325 part (c) — one template you manage, by id; null for anything else, including a missing or deleted one.
ref ID!
memoryConfigTemplates MemoryConfigTemplatesPage! #1325 part (c) — the templates you manage, name-ascending. Default page 50, at most 200; limit 0 returns the count only.
filter MemoryConfigTemplateFilter
limit Int
offset Int
scopes ScopesPage! Spec 049 Phase 2 — paginated list of the scopes you may read, optionally narrowed by owner, by an App's resolution context, by name, and by the cor:api:100:01 orgId (member-gated, no admin bypass; non-member ⇒ empty page). Name-ascending (id tiebreak); limit default 50 / cap 200; limit: 0 ⇒ count only.
filter ScopeFilter
orgId ID
limit Int
offset Int
scopeExplain ScopeExplanation Spec 049 Phase 2 — explain a scope for YOU. Pass scopeRef, or a bare name plus appRef (a name is unique only per owner, so it resolves in an App's context: App › Agent › organization, never unioned — two installed Agents each owning the name is SCOPE_NAME_AMBIGUOUS and you choose by scopeRef). Returns the readable memories in order, the dropped COUNT, the shadowed scopes, and with `loc` which memory would win for that address.
scopeRef ID
name String
appRef ID
loc String
channel Channel Spec 049 Phase 4 — the uniform single-Channel read. 'ref' is the Channel's id or its ADDRESS — the chat root's node URN, which is `Channel.chatRootUrn` and what `createChannel(memoryRef, loc)` composes to (#1171). Null when missing, when the ref names nothing, OR when you may not read its host memory — identically, so this is never an existence oracle.
ref ID!
channels ChannelsPage! Spec 049 Phase 4 — Channels whose host memory you may read, narrowed by an App (its host memories' Channels) or by one memory, and by the cor:api:100:01 orgId. Loc-ascending (id tiebreak); limit default 50 / cap 200.
filter ChannelFilter
orgId ID
limit Int
offset Int
registerEntry RegisterEntry Spec 049 Phase 5 — one register entry. Null when missing OR when you may neither read its Channel's host memory nor stand on the row's owner side.
ref ID!
registerEntries RegisterEntriesPage! Spec 049 Phase 5 — register entries you may read (§8.6), narrowed by owner (an App or an organization), Channel, or attendee (a Worker or an Agent), and by the cor:api:100:01 orgId. A filter naming something foreign returns the same empty page as one naming nothing. Oldest first; limit default 50 / cap 200.
filter RegisterEntryFilter
orgId ID
limit Int
offset Int
attendeeRegister [RegisterViewRow!]! Spec 049 Phase 6 — an attendee's RESOLVED register: the rows that apply to a Worker (workerRef) or to an Agent in an App (agentRef + appRef), one per Channel, with YOUR standing on each host memory. The relay reads this instead of a configured appRef. Gated on App participation (the same gate as the Worker's briefing); a foreign or nonexistent attendee is an empty list.
workerRef ID
agentRef ID
appRef ID
channelReadState ChannelReadState Spec 049 Phase 7 — an attendee's cursor on a Channel (attendeeRef: a Worker, or an Agent with appRef). Same gate as attendeeRegister; lastSeenSeq 0 when the attendee has never checkpointed there; null when unreadable or unknown. channelRef: the Channel's id or its address (`Channel.chatRootUrn`, #1171).
attendeeRef ID!
appRef ID
channelRef ID!
teamAttention TeamAttentionResult! #1353 v1 attention query, retired by #1384. This field always returns UPGRADE_REQUIRED; use teamAttentionPage for bounded polling. Pass an existing v1 since token to teamAttentionPage to carry its watermark forward. Neither operation changes read state.
appRef ID!
since String
teamAttentionPage TeamAttentionPage! #1384 paged (v2) attention poll — the bounded successor to teamAttention (v1 answers UPGRADE_REQUIRED). Each call decrypts at most scanLimit rows and emits at most limit Worker/Channel pairs; continue with page while nextPage is present. The final page alone carries adoptableSince — adopt it only after EVERY listed nudge was accepted; on any partial failure retain the prior watermark and restart the scan (duplicates, never loss). An existing v1 since token starts a scan, carrying the watermark forward. Never changes read state.
appRef ID!
since String
page String
limit Int
scanLimit Int
teamAttentionPreviewPage TeamAttentionPreviewPage! #1384 paged switchover preview — same bounded scan as teamAttentionPage, full-backlog accounting. The apply proof exists ONLY on the final page of a completed preview; confirmTeamAttentionSwitchover (apply) is unchanged.
appRef ID!
page String
limit Int
scanLimit Int
teamAttentionSwitchoverPreview TeamAttentionSwitchoverPreview! Preview the explicit baseline switchover for the operator's current live Workers. Read-only: inspect the exact cursors, heads and unread counts, then pass proof to confirmTeamAttentionSwitchover within ten minutes.
appRef ID!
resolveAIConfig ResolvedAiConfig! 036-ai-service-config: resolve a config name for an execution context and return the DECRYPTED credentials (privileged; successor to agentAIConfig/appAIConfig). Walk: App -> Agent -> Org (of the App, else of the Agent) -> HadronServer; disabled configs are skipped. When name is omitted, 'default' is resolved. When an explicit name misses, resolution falls back to 'default'; if that also misses, errors with NoAiConfigAvailableError. Consumers (webhooks, scheduled tasks, the portal chatbot) reference configs by name only — pass the name plus the execution context, never credentials. Auth: a named App uses its own owner/org ADMIN gate, including strict owner-only access for a user-owned App. A named Agent must be installed in that App. Without an App, an org Agent requires org ADMIN; a host-only context requires platform ADMIN/OWNER. A user-owned Agent is strictly owner-only, including when an App is also named. With no effective org, its owner can decrypt that Agent's own config or a user-owned App's config; a host fallback is unavailable through this query. A user-owned App's owner needs the outer source's own authority to decrypt an installed Agent or org config; its App ownership alone grants only its App config. A HADRON_SERVER fallback can back a server-side call, but only a platform ADMIN/OWNER may receive its plaintext key. Other callers get a typed refusal naming the resolved config and HADRON_SERVER level. appRef/agentRef accept the entity's ID or URN.
name String
appRef ID
agentRef ID
aiProviderEndpoints [AiProviderEndpoint!]! #1373: the known endpoints for a provider — the server's single suggestion list, so the portal and the CLI render the same choices and neither hardcodes a URL. Empty for an unknown provider and for bedrock (its endpoint comes from the region). Public reference data (vendor URLs), so it needs no auth.
provider String!
resolveAiServiceConfigs [AiServiceConfig!]! 036-ai-service-config: the MASKED set of configs RESOLVABLE in an execution context — every distinct config name a chat in this context could select. Populates a config picker in the chatbot UI. Same walk as resolveAIConfig (App -> Agent -> Org (of the App, else of the Agent) -> HadronServer), but returns ALL names instead of resolving one: configs are deduped by name with the innermost owner winning, so each entry is the row resolveAIConfig would return for that name. Only the Agent named here contributes — sibling Agents installed in the same App are not consulted. Disabled configs are skipped. Never returns key material (hasApiKey + apiKeyPreview only). Auth: scoped to one App's chat context. A non-admin caller MUST pass an appRef, be a member of that App, and (when an agentRef is given) the Agent must be installed in that App. Because the result is masked (no key material), App membership — not org admin — is the bar, unlike resolveAIConfig and the aiServiceConfigs management list. Platform ADMIN/OWNER are always allowed and may omit appRef. appRef/agentRef accept the entity's ID or URN.
appRef ID
agentRef ID
myChats [Chat!]! Saved chats for the current user in their personal memory for an agent. Newest first. Accepts the entity's ID or URN.
agentRef ID!
limit Int
offset Int
chatHistory [ChatMessage!]! Message history for a saved chat. Returns plain role/content pairs suitable for feeding back into an LLM. limit bounds the transcript to its MOST RECENT messages (default 100), and they are returned oldest-first. Stated because it was not: the window used to be taken from the other end, so a chat longer than the limit returned its opening forever (#1111), and a client author had no written contract to check that against. Contract: cor:cht:050:01. beforeSeq (#1116) pages BACKWARD: only messages with seq strictly LESS than it are considered, and the window then returns the newest "limit" of those -- the page immediately preceding the cursor, still oldest-first. It is the mirror of teamChatMessages' sinceSeq: pass the OLDEST seq you hold, as you would pass the newest to page forward. Omit it for the tail. There is no total -- the field returns the page and nothing else. A page SHORTER than the limit is a truthful end-of-data under that cursor, and only here: this resolver applies every filter inside the query and never drops a row after the database take, so the take IS the page. Do not carry the same-looking inference to reads that overfetch and post-filter; there a short page can still mean rows were removed. Two boundaries worth knowing before you build on it: - A message with NO seq is older than every numbered one (cor:cht:050:02), so every cursor includes such messages. They are reachable, but they cannot themselves BE a cursor -- a legacy chat holding more counterless messages than one page cannot be paged through them. Only pre-#919 transcripts are affected. - seq is unique going forward but NOT on pre-#919 rows. Where two legacy messages share a seq, a cursor at that value excludes both, so paging can skip the sibling. See #992 for the underlying gap.
chatId String!
limit Int
beforeSeq Int
teamChatMessages TeamChatMessagesPage! Read a team App's chat (#939), seq-ordered ascending, as a uniform { items, total } page. sinceSeq is a watermark cursor: only messages with seq STRICTLY GREATER than it are returned (pass the last seq you have seen). With neither cursor nor offset, the NEWEST page is returned, still ascending. Pass sinceSeq: 0 to start a forward walk at the oldest message. beforeSeq (#1116) is its BACKWARD mirror for scroll-up paging: only messages with seq STRICTLY LESS than it are considered, and the NEWEST limit of those come back -- the page immediately before the cursor, still ascending. The two compose, so passing both reads a bounded slice. TeamChatMessagesPage.total counts that cursor range and the mentionsRef filter before paging, not the whole team-chat history. An explicit offset retains oldest-first positional paging for existing clients; prefer cursors for new reads. offset is IGNORED when beforeSeq is given: a cursor exists precisely because a position is unstable while workers keep posting, and honouring both would put that race back. mentionsRef filters to messages whose stored envelope mentions the referenced worker (a Worker id or name of THIS App, retired included) or user (handle/id) — matching runs against the mention tokens extracted at write time, never by re-parsing bodies. The ref must name this App's own staff or members (a Worker of the App, an AppMember, or the caller); an unresolvable or foreign mentionsRef yields the empty page identically (no existence oracle). limit defaults to 50 (cap 200); limit: 0 returns only total. Authorization: an AppMember of the App (any role), an org member with CONTRIBUTOR+ on the App's org, the owner of a user-owned App, or the App's own key (a pure App-key principal may READ its team chat). Error codes (extensions.code): FORBIDDEN (an authenticated caller who is not a participant), UNAUTHENTICATED (no principal at all; checked before the App is resolved, so it answers identically whether or not the App exists). appRef and mentionsRef accept the entity's ID or URN.
appRef ID!
sinceSeq Int
beforeSeq Int
mentionsRef ID
limit Int
offset Int
channelMessages TeamChatMessagesPage! Spec 049 Phase 8 — read ANY Channel by ref (its id, or its address — `Channel.chatRootUrn`, #1171), with teamChatMessages' cursors, mentions filter and paging; teamChatMessages is the appRef → App.defaultChannel convenience over this. Authorization is the host App's team-chat read gate; a Channel you may not read — or one that does not exist — is CHANNEL_NOT_FOUND, identically. TeamChatMessagesPage.total has the same cursor- and mention-filtered meaning as for teamChatMessages, before paging.
channelRef ID!
sinceSeq Int
beforeSeq Int
mentionsRef ID
limit Int
offset Int
teamWorkItems TeamWorkItemsPage! Read a team App's worklog (#947) — the provenance query: which sessions (and transcripts) produced this PR? Newest first, as a uniform { items, total } page. ref accepts ANY accepted spelling (URL, short form, canonical) and is matched tool-awarely: with tool 'github' (or none) it is normalized, so 'https://github.com/o/r/pull/371' and 'o/r#371' return the same records. An ownerless github ref such as cli#793 matches every readable canonical ref whose repository ends in cli and number is 793; it never selects one owner. With another tool ref matches the stored verbatim spelling; with no tool it matches either. sessionRef / tool / kind are equality filters; kind must be one of pr, issue, commit, branch, repo when given. limit defaults to 50 (cap 200); limit: 0 returns only total. Authorization: an AppMember of the App (any role), an org member with CONTRIBUTOR+ on the App's org, the owner of a user-owned App, or the App's own key (a pure App-key principal may READ its own App's worklog). appRef accepts the entity's ID or URN; sessionRef is the session id.
appRef ID!
ref String
sessionRef ID
workerRef ID #1035 — everything ONE worker has done, filtered server-side so the page envelope stays correct. Without it a client must fan out over sessions(workerRef:) and merge, which cannot reproduce `total` or a stable offset on the one surface whose whole point is completeness. Accepts a worker id, its URN, or its NAME within this App. A RETIRED worker still resolves — retirement ends the casting, not the history. Records from a session that was never worker-bound carry no worker and do not match. A ref that names nothing readable in this App yields an EMPTY page rather than an error, so this is never an existence oracle for another App's roster.
tool String
kind String
limit Int
offset Int
worker Worker One Worker by ref (#974) — its id, its URN (#991), or (with appRef) its NAME (#1015), the ergonomic handle a human already knows. The name form is ADDITIVE: it is activated by appRef, since a name is unique only within an App. Without appRef the ref must be an id or URN, and anything else refuses WORKER_NOT_FOUND as before. An id or URN wins over appRef rather than being checked against it, so a typo'd URN never degrades into a roster search. Selecting `prompt` re-renders the worker's boot briefing live from the role agent's template — the read that lets an agent recover a briefing it lost to a context compaction, instead of opening a second session or forcing a takeover of itself. Authorization: an AppMember of the worker's App (any role), an org member with CONTRIBUTOR+ on its org, the owner of a user-owned App, or the App's own key. For the NAME form the gate runs BEFORE the lookup and a denial is reported as WORKER_NOT_FOUND, so the roster is not an existence oracle.
ref ID!
appRef ID
workers WorkersPage! A team App's STAFF (#974, cor:agt:020:01 — Workers are the staff; the AppAgent join is the install roster): the App's castings, oldest first, as a uniform { items, total } page. Retired castings are hidden unless includeRetired. Same authorization as worker. appRef accepts the App's ID or URN.
appRef ID!
includeRetired Boolean
limit Int
offset Int
heldWorkers WorkersPage! Workers CURRENTLY held by userRef across Apps the caller may inspect (#1344), oldest casting first. userRef accepts a user id, handle, URN, or email. Missing users, no holds, and holds confined to unreadable Apps all return the same empty page; total and pagination are computed only after the App authorization filter. Retired castings are hidden unless includeRetired. hasLiveSession on each item uses the existing derived idle-window predicate — it means the worker is being driven now, not merely that an unended Session row exists.
userRef String!
includeRetired Boolean
limit Int
offset Int
teamRoles TeamRolesPage! An App's role definitions (#960): every roles:<role> node in the Team Agent's system memory. Ordered by role slug. Same read authorization as workers. #1050: no register projection. This once existed chiefly to answer "which names are still free", which needed the App's FULL roster and so could not be computed client-side — with names no longer allocated from a pool there is no such question, and the read is a plain list of definitions. teamAgentRef disambiguates when more than one installed agent carries a roles: branch (TEAM_AGENT_AMBIGUOUS otherwise); an encrypted system memory without an active session key refuses SESSION_EXPIRED.
appRef ID!
teamAgentRef ID
limit Int
offset Int
castWorkerPreview CastWorkerPreview! Dry-run castWorker (#964): run the cast's exact resolution — same arguments, same typed refusals (WORKER_AGENT_NOT_FOUND / _AMBIGUOUS / _NOT_INSTALLED, WORKER_NAME_REQUIRED, WORKER_NAME_TAKEN, APP_UNINSTALLED), same MINT gate — up to but not including the writes, and return what would be created. A Query, not a dryRun flag: casting a name is the one irreversible act in the team feature (cor:agt:020:02 — permanence), and previewing it must not need a mutation permission model or a fake Worker row. THE PREVIEW RESERVES NOTHING: no lease exists by law (cor:agt:020:03), so a previewed name may be gone at cast time — by design; the cast's uniqueness constraint remains the only allocation primitive.
appRef ID!
agentRef ID
role String
name String
promptOverride String
agentAssets AssetListResult! v1 — agent-addressed listing. Deprecated; use memoryAssets.
agentUrn String!
mimeType String
skip Int
count Int
includeDeleted Boolean
memoryAssets AssetListResult! v2 (spec 006) — memory-addressed listing. Returns every asset attached to the memory; the gate is the memory's read access. The replacement for agentAssets in the v2 surface. Set mine: true to narrow to the caller's own uploads — a filter on top of the read gate, not a substitute for it. Accepts the entity's ID or URN.
memoryId ID!
mimeType String
skip Int
count Int
includeDeleted Boolean
mine Boolean
assets AssetsPage! Cross-memory asset list (#891) — every file the caller can reach, which is every asset held by a memory they can read. The uniform find-many surface for assets (spec cor:api:120); memoryAssets stays as the per-memory one. The three tabs of a top-level Assets section are filters over this, not separate queries: mine: true, no filter, and orgId respectively.
filter AssetFilter
orgId ID
limit Int
offset Int
assetDownloadUrl AssetDownloadUrl! Mint a short-TTL presigned GET URL for an asset. Default TTL 5 minutes; ceiling 1 hour. Gated on the holding memory's read access and on scan_status = CLEAN.
assetId ID!
ttlSeconds Int
serverInfo ServerInfo! Identity of the hadron-server this GraphQL endpoint is bound to: the server-identity version and the deployment's canonical base URL. The GraphQL counterpart to the hadron_server_info MCP tool — both read the same getDeploymentInfo() helper, so MCP and GraphQL callers get one consistent answer to "which server/API am I talking to?". 'version' is HADRON_SERVER_VERSION — the MCP/API-surface contract version, bumped when the tool/query surface changes in a caller-visible way. It is intentionally NOT the repo's package.json release version. Public — no authentication required. Does NOT expose the DATABASE_URL (it may carry credentials).
nextNode Node The next sibling node under a parent, ordered by seq (#817) — the GraphQL twin of hadron_get_next_node, so guided reading order is no longer MCP-only. parentRef / currentRef take a node PK or a fully-qualified URN (cor:api:140). Omit currentRef to get the FIRST child. Order: seq ascending; a NULL seq sorts LAST, after every explicitly ordered sibling; ties break on loc ascending. Identical to the MCP tool, so the two surfaces cannot present different reading orders. Returns null at the end of the sibling chain, and for a parent that does not exist or the caller cannot read — denied and missing are indistinguishable. A currentRef that is not among the parent's direct children is likewise null rather than an error, so it cannot probe a node's parentage. A MALFORMED ref still throws: a shape error discloses nothing about what exists. Stateless — this is a next-sibling read, not a session-backed cursor.
parentRef ID!
currentRef ID
raw Boolean
renderRunNode String! Render a run/action node WITHOUT executing it (#818) — the GraphQL twin of hadron_run_action, which despite its name executes nothing: it loads the node, resolves its dependency edges, and renders with args. runTask covers the EXECUTE half. This is the render half, for previewing a task before running it (a task run --dry-run), for reading an action's assembled instructions when the CALLER is the executor, and for debugging template/dependency resolution without side effects. A Query because it is side-effect-free, and unlike the MCP tool it records NO action-run UsageEvent — metering a preview would inflate the ledger with actions nobody took. Uses the SAME assembler runTask executes (Mustache compilation via compileTemplate; flow-ROUTING edges skipped, since they are the walker's rails rather than references), so a dry run predicts the real prompt. Two deliberate narrowings relative to execution, both fail-safe: a referenced node the caller cannot READ is omitted rather than inlined (an edge may cross memories), and an edge whose target was soft-deleted is skipped so a preview cannot resurrect removed instructions. NOTE this is NOT the legacy MCP hadron_run_action bundle, which does a literal placeholder replace and inlines every outgoing edge. Where the two differ, this one matches execution. nodeRef is a node PK or a fully-qualified URN (cor:api:140). Denied and missing both raise NODE_NOT_FOUND - no existence oracle.
nodeRef String!
args JSON
validateMemory MemoryValidationResult! Memory health audit (#819) — the GraphQL twin of hadron_validate, which was MCP-only, so the CLI, the portal and CI had no way to answer "is this memory healthy?". Every check is over server-owned state a client cannot compute: abstractOriginHash vs current content, embedding_failed_at (not projected on Node at all), and property-schema conformance. So unlike other gaps there is no slow client-side fallback. Returns TYPED findings so a caller can branch and CI can gate on totalFindings. findings is capped by limit (default 200, max 1000) while totalFindings reports the true count - so a gate is never fooled by truncation. Read gate as the rest of the memory reads.
memoryRef ID!
limit Int
specBacklinks [SpecReference!]! #1449 — what refers to the spec at loc in a DRAFT spec corpus: node URNs in the corpus text (inside URLs too) plus real edges from corpus nodes. Exact citation, not descendants. Refuses SPEC_CORPUS_NOT_DRAFT for a minted memory. memoryRef accepts the memory's ID or URN.
memoryRef ID!
loc String!
specUnresolvedReferences [SpecReference!]! #1449 — references in a DRAFT spec corpus that do not reach a written spec: URNs citing a missing loc or a placeholder, edges to a placeholder or to a deleted spec of the corpus, and pending edges (field pendingEdge) whose target is not a written node yet. Minting refuses while any remain.
memoryRef ID!
appRun AppRun One run (cor:agt:010:02). ref is an AppRun ID or an hrn:apprun:<root>:<app>:<run-id> URN (#696). Readable by a platform admin, a READER of the run's organization, the run's own App key, or a user with a live App relationship for a run on their behalf or a MANUAL run they started. A run the caller may not read returns null, exactly like a missing one (#1577: no existence oracle).
ref ID!
latestAppRun AppRun The latest run of an App resolved to a concrete run (#696) — 'current/latest/previous' are queries, never magic run-ids in URN space (cor:urn:010:02). previous: true returns the run before the latest.
appRef String!
previous Boolean
appRuns AppRunsPage! The run audit list. statuses (#836) is a disjunction — 'everything still in flight' is [PENDING, RUNNING] and 'what went wrong' is [FAILED, TIMED_OUT], neither of which the single-valued status arg could express. Per conventions:multi-ref-scope-args: OMITTING it means no status filter, while an explicitly EMPTY list is the EMPTY scope and returns nothing - it never silently falls back to 'all'. Values are deduped; an unknown member is rejected by enum validation rather than dropped. status is DEPRECATED in favour of statuses. Both are accepted for one release; when both are given, statuses wins. sortBy / sortDir (#833) order the whole result set server-side, so a client table is no longer limited to sorting the page it happens to hold.
appRef String
orgRef String
statuses [AppRunStatus!]
parentRunId ID
sortBy AppRunSortField
sortDir SortDirection
limit Int
offset Int
agentSchedules AgentSchedulesPage!
appRef String!
limit Int
offset Int
agentWebhooks AgentWebhooksPage!
appRef String!
limit Int
offset Int
actionTickets ActionTicketsPage! The ticket ledger: minted, consumed-by-which-run, expired (org-ADMIN).
orgRef String!
limit Int
offset Int
connectionGrant ConnectionGrant One connection grant the caller created, by ID; null if not theirs (no existence oracle).
ref ID!
connectionGrants ConnectionGrantsPage! The caller's own connection grants; connectionRef narrows to one connection.
connectionRef String
limit Int
offset Int
principalGrant PrincipalGrant One action grant by ID - visible to the grantee or an admin of its org; null otherwise (no existence oracle).
ref ID!
principalGrants PrincipalGrantsPage! Action grants. Default: the caller's own (self-audit is never gated). An org admin passing orgRef sees that org's grants, optionally narrowed by userRef.
orgRef String
userRef String
limit Int
offset Int
quotaCounters [QuotaCounter!]! Usage-vs-limit counters for an org (+ one app when appRef given). Org-ADMIN. Rows materialize on first view, seeded with platform defaults (#510).
orgRef String!
appRef String
effectiveActionPolicy EffectiveActionPolicy! Explain the standing policy chain (org / user / app) for one action - which layer denies it (#510, cor:acl:040:02). Org-ADMIN on the app's org.
appRef String!
action String!
userRef String
aiModelPrice AiModelPrice One provider rate row by id (spec 046). Authenticated read - rates are not a secret.
ref ID!
aiModelPrices AiModelPriceList! Provider rate rows, newest effective first, optional provider/model filter (spec 046). Authenticated read.
provider String
model String
tier Int
limit Int
offset Int
creditLedgerEntries CreditLedgerEntryList! One org's append-only credit-ledger page, newest first (spec 046). Org-ADMIN.
orgRef String!
limit Int
offset Int
creditBalance CreditBalance! One org's credit balance - SUM over the append-only ledger (spec 046). Org-ADMIN.
orgRef String!

Mutation

Field Argument Type Description
startImpersonation StartImpersonationResult! Start impersonating another member of orgRef (admin support/diagnostics). Caller must be a live ADMIN/OWNER of the org (platform ADMIN/OWNER is OWNER-equivalent), the target a live member, PEER-OR-BELOW (an org ADMIN may not impersonate an org OWNER), never self. Returns the audit session and a short-TTL token (returned exactly once) whose requests run READ-ONLY as the target, scoped to this one org: other orgs and all personal-class resources stay invisible, and every mutation except stopImpersonation is rejected. Every request re-validates the session row, so stopping it (or demoting the admin) kills the token immediately.
orgRef String!
userRef String!
reason String
stopImpersonation ImpersonationSession! Stop an impersonation session (writes endedAt; the audit row is never deleted). With an impersonation token, id may be omitted (ends the token's own session — the one mutation an impersonated context may call). With a normal token, id is required and the caller must be the session's admin, an org ADMIN/OWNER of its org, or a platform admin. Idempotent on an already-ended session.
id ID
setDashboardWidgets [DashboardWidget!]! #772 — replace the caller's entire dashboard widget selection. Grid position is the array order; each widget's span (1-12) and rowSpan (1-4) are required and clamped server-side. Covers both create and update (the layout is always saved as a whole). Returns the new selection. Requires an authenticated user.
widgets [DashboardWidgetInput!]!
createDashboardWidgetPreset DashboardWidgetPreset! #806 — save a named widget configuration for the caller. 'name' is trimmed and must be 1-60 characters, unique among the caller's presets for that widget type; a duplicate is BAD_USER_INPUT. 'config' must be a JSON object within the same 4KB budget as a placement's config. Requires an authenticated user.
type DashboardWidgetType!
name String!
config JSON!
updateDashboardWidgetPreset DashboardWidgetPreset! #806 — rename a preset and/or replace its settings. Partial: an omitted (or null) argument leaves that field alone; there is no way to clear 'config', since a preset with no settings has nothing to apply. 'type' is immutable — delete and re-create to move settings to another widget. A preset the caller does not own is indistinguishable from one that does not exist (NOT_FOUND).
presetRef ID!
name String
config JSON
deleteDashboardWidgetPreset Boolean! #806 — delete one of the caller's presets. Placements already seeded from it keep their config (apply copies; there is no live link). A preset the caller does not own is indistinguishable from one that does not exist (NOT_FOUND).
presetRef ID!
createNode Node! Create a single node. Rejects with NodeLocConflictError when a live node already exists at (memoryId, loc) — creating is create-only (spec 039 Phase 0 D1); a soft-deleted node at the target loc is resurrected. 'name' is required here and only here (D4).
input CreateNodeInput!
createSpecNode Node! #1201 — the CREATE door for the spec kind. A write that would produce a node with role: 'spec' or a dotted 'spec.*' sub-role must come through here; the generic createNode refuses it. PURE ROUTING: it validates nothing and knows nothing beyond which kind it is the door for. The authoring RULES stay in the tool that owns them ('hadron spec', 'hadron coding'), which is a closed-list exception on the audience ground. STRENGTH: DILIGENCE. It guarantees a spec node was authored by the spec tool (#1152, and the corpus discipline resting on it). It does NOT claim a determined caller could not author one another way: the label is free to omit and a citation resolves without it. Specs are deliberately not treated as a security concern; the destructive half is closed by #1182/#1184, which made createNode mint-or-refuse and race-safe. Exempt from its OWN kind only — it is fully subject to every other kind and to Channel address protection, so it cannot forge a chat message. Deliberately NOT an MCP tool: an agent's node surface is hadron_create_node, which refuses, and that absence is the mechanism rather than an oversight.
input CreateNodeInput!
reserveSpecCitation Node! #1450 — reserve a citation in a DRAFT spec corpus as a placeholder: a spec node at loc (role spec, nodeType info) marked isPlaceholder, so links can point at it and the placeholders left are the corpus's to-do list. Writing real content through updateSpecNode turns it into a real spec at the same citation. Refuses SPEC_CORPUS_NOT_DRAFT in a minted memory, where a citation is permanent once written, and NodeLocConflictError at a live loc. memoryRef accepts the memory's ID or URN.
memoryRef ID!
loc String!
name String
renumberSpec SpecRenumberResult! #1449 — renumber a spec in a DRAFT spec corpus: move it and its subtree from fromLoc to toLoc (no URN alias in a draft), then rewrite every node URN in the corpus that cites the moved specs, each as a normal edit. Bare text citations (labels, names) are reported in textCitations, not rewritten. dryRun returns the plan and writes nothing. The move is atomic; each rewrite is a separate guarded edit, and one that fails is reported FAILED without undoing the move.
memoryRef ID!
fromLoc String!
toLoc String!
dryRun Boolean
mintSpecCorpus SpecCorpusMintReport! #1448 — mint a DRAFT spec corpus: once, one-way, for the whole corpus. After minting, citations are permanent (moves record URN aliases again) and no placeholder can be reserved. Refuses SPEC_CORPUS_MINT_BLOCKED while any blocker remains: a placeholder, an unresolved reference, a structural lint error over the citation-shaped nodes (nodetype-info, tag-spec, serialization-leak, duplicate-loc), or a stale abstract once staleAbstractsBlock is true. dryRun returns the full report without minting. Owner or org ADMIN only. memoryRef accepts the ID or URN. A real run holds every write to the corpus off while it checks and flips, and refuses SPEC_CORPUS_BUSY (retryable) if a write is already in flight. #1468 — a minted spec is then permanent: every path that would remove one from the corpus refuses SPEC_MINTED_PERMANENT, naming the specs (deleteNode soft or hard, a move to another memory or an overwrite of its citation, mergeNodes with deleteSource, extracting with move, a merge or deletion of the whole corpus, git-sync pruning, and account erasure while the owner still holds it). Supersede a spec to replace it; a rename within the corpus still records its URN alias.
memoryRef ID!
dryRun Boolean
updateSpecNode Node! #1201 — the UPDATE door for the spec kind, and the counterpart of createSpecNode. Same posture: pure routing, exempt from its own kind only, not an MCP tool. It is needed because the gate reads the RESULTING state: editing a node that already carries this kind still produces one, so the generic updateNode refuses it even when the edit touches neither signal. That is the point — a spec node must not be rewritable through the generic surface. Identical to updateNode otherwise, selectors included ('id' XOR 'memoryId' + 'loc'); it never creates and never moves.
input UpdateNodeInput!
createReviewNode Node! #1201 — the CREATE door for the review kind. A write that would produce a node with role: 'review' or a dotted 'review.*' sub-role must come through here; the generic createNode refuses it. PURE ROUTING: it validates nothing and knows nothing beyond which kind it is the door for. The authoring RULES stay in the tool that owns them ('hadron spec', 'hadron coding'), which is a closed-list exception on the audience ground. STRENGTH: SECURITY. A review is not coding-specific — any work performed can be reviewed, and a review may be a SAFETY CHECK. So an attacker can modify a work assignment AND remove the reviews meant to catch that. Omitting the label is self-defeating in its own way: an unlabelled check is invisible to the review tooling, so it is not a check. Exempt from its OWN kind only — it is fully subject to every other kind and to Channel address protection, so it cannot forge a chat message. This mutation is not itself an MCP tool; its MCP counterpart is the deliberate hadron_create_review tool, separate from hadron_create_node, which refuses (#1301 names it in the refusal).
input CreateNodeInput!
updateReviewNode Node! #1201 — the UPDATE door for the review kind, and the counterpart of createReviewNode. Same posture: pure routing, exempt from its own kind only; its MCP counterpart is hadron_update_review. It is needed because the gate reads the RESULTING state: editing a node that already carries this kind still produces one, so the generic updateNode refuses it even when the edit touches neither signal. That is the point — a review node must not be rewritable through the generic surface. Identical to updateNode otherwise, selectors included ('id' XOR 'memoryId' + 'loc'); it never creates and never moves.
input UpdateNodeInput!
createTaskNode Node! #1201 — the CREATE door for the task kind. A write that would produce a node with isRunnable: true must come through here; the generic createNode refuses it. PURE ROUTING: it validates nothing and knows nothing beyond which kind it is the door for. The authoring RULES stay in the tool that owns them ('hadron spec', 'hadron coding'), which is a closed-list exception on the audience ground. STRENGTH: CAPABILITY. The only one of the three whose gate cannot be evaded by omission, which is why it carries the security case. It keys on 'isRunnable' rather than on 'role': a label is caller-set and freely dropped, while omitting the capability means the node does not run. The threat is a memory shared with someone who authors a runnable node that reads the owner's data when the OWNER runs it (#1202). Exempt from its OWN kind only — it is fully subject to every other kind and to Channel address protection, so it cannot forge a chat message. This mutation is not itself an MCP tool; its MCP counterpart is the deliberate hadron_create_task tool, separate from hadron_create_node, which refuses (#1301 names it in the refusal).
input CreateNodeInput!
updateTaskNode Node! #1201 — the UPDATE door for the task kind, and the counterpart of createTaskNode. Same posture: pure routing, exempt from its own kind only; its MCP counterpart is hadron_update_task. It is needed because the gate reads the RESULTING state: editing a node that already carries this kind still produces one, so the generic updateNode refuses it even when the edit touches neither signal. That is the point — a task node must not be rewritable through the generic surface. Identical to updateNode otherwise, selectors included ('id' XOR 'memoryId' + 'loc'); it never creates and never moves.
input UpdateNodeInput!
updateNode Node! Update an existing node. Identify it by 'id' (PK or fully-qualified node URN) XOR the ('memoryId', 'loc') combo — supplying both selectors, or neither, is an error (D2). Rejects with NODE_NOT_FOUND when the target does not exist; updateNode never creates and never moves (relocation is moveNode's job, D3). Every content field is optional — omitted fields are preserved ('name' included, D4; 'abstract' keeps its omit/null/string contract; omitted 'tags' are preserved, issue #235).
input UpdateNodeInput!
updateNodeData Node! Shallow-merge a JSON patch into a node's 'data' bag. The supplied JSON's top-level keys are written over the node's existing 'data' (the patch wins on key collision); keys the patch doesn't mention are preserved. If the node has no 'data' yet, the supplied JSON becomes its 'data' verbatim. 'data' must be a JSON object (not an array or scalar). 'nodeRef' is a node ID or fully-qualified URN. Honors the same single-write invariants as updateNode (per-memory write auth, encryption, revision history, git mirror). Returns the updated node.
nodeRef ID!
data JSON!
reason String
updateNodeProperties Node! #742 - Shallow-merge a JSON patch into a node's structured 'properties' (the schema-governed JSONB column, DISTINCT from 'data'). The patch's top-level keys are written over the node's existing 'properties' (patch wins on collision); unmentioned keys are preserved; a node with no 'properties' takes the patch verbatim. ONE EXCEPTION (#1227): an OBJECT under 'exports' - the per-host skill export declarations, keyed by host (D12) - is applied as a JSON Merge Patch (RFC 7396), not replaced. Hosts and their fields merge recursively, a null member DELETES that host or field, and scalars/arrays replace. So '{exports: {codexSkill: {enable: false}}}' flips one flag and keeps the host's name, description and every other host, and '{exports: {codexSkill: null}}' removes that host. A non-object 'exports' in the patch (null, a scalar, an array) keeps the top-level rule and replaces the stored value. The merge runs under a per-node lock, so two callers writing different hosts at once both land - do NOT read-modify-write 'exports' client-side, which can lose a concurrent writer's host. 'properties' must be a JSON object (not an array or scalar). 'nodeRef' is a node ID or fully-qualified URN. On a schema-governed memory the MERGED result is validated against the memory's schema (BAD_USER_INPUT on a violation). 'properties' is plaintext at rest (never encrypted), so the merge + conformance check need no key — but the write still honors the same single-write invariants as updateNode (per-memory write auth, an active session on an encrypted memory, revision history, git mirror). Returns the updated node.
nodeRef ID!
properties JSON!
reason String
createObject JSON! #745 — Object store: the legible sugar surface over structured storage. An object IS a node; these ops are thin projections onto createNode / updateNodeProperties / deleteNode, inheriting all their invariants (auth, encryption, schema conformance, revisions, atomic merge). Create an object in a collection. 'memoryRef' is a memory ID or fully-qualified URN (relative refs are rejected, spec 022). 'type' is the collection (node objectType); 'fields' are the flat, typed properties (validated against the memory schema when the collection is declared). The node's loc is auto-derived (<type>:<key ?? generated-id>) and hidden; pass 'key' for a human-meaningful id (a single segment, no ':'), 'name' to override the derived node name. 'id' and 'type' are reserved and cannot be field names. Returns the flat object.
memoryRef String!
type String!
fields JSON!
key String
name String
updateObject JSON! Merge fields into an existing object (server-side atomic shallow merge + schema conformance on the result). 'ref' is the object id (or node URN); 'fields' wins on key collision, unmentioned keys preserved. Returns the updated flat object. The merge is shallow for EVERY field, including one named 'exports': the per-host merge-patch updateNodeProperties applies to 'properties.exports' (#1227) is a node-level skill-declaration rule and does not apply to object fields.
ref ID!
fields JSON!
reason String
deleteObject Boolean! Delete an object (soft by default; 'hard: true' removes the row). 'ref' is the object id (or node URN). Returns true on success.
ref ID!
hard Boolean
searchReplaceInNodes SearchReplaceResult! Bulk search-and-replace text across selected nodes (literal or regex). Honors per-memory write auth, encryption, revision history, abstract staleness, and re-embedding. Supports dryRun preview.
input SearchReplaceInNodesInput!
searchReplaceInSpecNodes SearchReplaceResult! Governed bulk replacement inside one draft spec corpus, with exact preview binding.
input SpecSearchReplaceInput!
deleteNode Boolean! Delete a node identified by 'nodeRef' (a node ID or fully-qualified URN; #542). Soft-delete by default (sets deletedAt; the node disappears from reads, matching the hadron_delete_node MCP tool). Pass hard: true to remove the row entirely (cascades edges + NodeRevision via FK) — irreversible. #391. A re-delete of an already soft-deleted node is idempotent (the ref resolves through the tombstone). If the node has descendants (nodes under its loc prefix), a plain delete REFUSES with NODE_HAS_DESCENDANTS (carrying the count) rather than silently orphaning the subtree — parity with the hadron_delete_node MCP tool. Pass recursive: true to delete the node's whole loc-subtree — the node at its loc PLUS every descendant, matched on ':' path boundaries (so deleting 'auth' does not touch 'authoring'). The subtree lives in one memory, so the single write-access check covers it; 'hard' applies to the whole subtree. A hard delete cascades edges on both endpoints via FK. If any affected node is on an edge that bridges this memory to another, the hard delete REFUSES with CROSS_MEMORY_EDGES (the caller may not own the other memory) unless cascadeCrossMemoryEdges: true. A non-recursive soft re-delete of an already soft-deleted node is an idempotent no-op that preserves its audit stamp.
nodeRef ID!
hard Boolean
recursive Boolean
cascadeCrossMemoryEdges Boolean
restoreNodeRevision Node! Restore a node to a previous revision (#617). Default (truncate: false): snapshots the CURRENT node state into history first, then restores the node's fields to the revision — non-destructive, the restore itself becomes undoable. truncate: true: restores the node's fields to the revision AND deletes every NodeRevision NEWER than the selected one (createdAt strictly greater), WITHOUT creating a pre-restore snapshot. Boundary is EXCLUSIVE of the selected row: the selected revision is KEPT and becomes the new baseline (the most-recent snapshot afterward); its era and older survive. The live revision counter still advances monotonically for the restore itself. Restore + truncate run in one transaction (atomic).
revisionId ID!
truncate Boolean
deleteNodeRevision Boolean! Delete a single node-revision snapshot by id (#617). Auth: the node's memory write access (as restoreNodeRevision). Throws NOT_FOUND for an unknown id.
revisionId ID!
updateNodeRevision NodeRevision! Set or clear the user-facing label on a revision snapshot (#620, NodeRevision.revLabel, 500-char cap). Omit revLabel to preserve; pass null to clear. Auth: the node's memory write access (as deleteNodeRevision) PLUS the nodeRevision query's read gates — it returns the full snapshot, so a soft-deleted node or a snapshot captured in an unreadable memory answers the same NOT_FOUND as an unknown id.
revisionId ID!
revLabel String
clearNodeHistory Int! Delete ALL NodeRevision rows for a node, returning the count deleted (#617). 'nodeRef' is the node's PK or fully-qualified URN. Auth: the node's memory write access. An unknown ref throws NODE_NOT_FOUND. Reachable on a soft-deleted node (purging residual history is a cleanup op).
nodeRef ID!
moveNode Node! Relocate a node (#564). 'sourceRef' is the node's PK or fully-qualified URN. The destination is EITHER 'targetUrn' (the node's new full URN — new memory and/or loc) OR 'targetMemoryRef' (a memory ID/URN; the node keeps its current loc, only its memory changes) — supply exactly one. The node keeps its id, so all incoming/outgoing edge references stay valid. Fails loudly with NODE_ALREADY_EXISTS if a live node already occupies the destination. Requires write access to both the source and destination memories. On a cross-memory move, select Node.moveLinkWarnings to see raw relative content links that now miss or resolve to another node.
sourceRef ID!
targetUrn String
targetMemoryRef ID
cloneNode Node! Clone a node to a new location, returning the NEW node with a fresh id (#564). Same selector shape as moveNode ('sourceRef' + exactly one of 'targetUrn' / 'targetMemoryRef'). Copies the source's own fields; outgoing edges are copied only where they naturally resolve — a live node exists at the target's loc in the destination memory. Incoming edges are not copied. Fails loudly if the destination loc is already occupied. Requires read access to the source memory and write access to the destination.
sourceRef ID!
targetUrn String
targetMemoryRef ID
mergeNodes Node! Fold one node (source) into another (target), returning the updated target. Per-field strategy: text fields (CONTENT/ABSTRACT/DESCRIPTION) concatenate target-first; TAGS unions; DATA/PROPERTIES shallow-merge with the target winning on key collisions; EDGES re-points the source's relationships onto the target. 'include' selects which fields fold in (omitted = all). 'deleteSource: true' hard-deletes the source afterward. Honors per-memory write auth, encryption, revision history, abstract staleness, and re-embedding (same invariants as createNode/updateNode).
input MergeNodesInput!
mergeMemories Memory! Merge one memory (source) into another (target), returning the updated target. Source nodes whose loc already exists in the target are folded into their counterpart via the mergeNodes field strategy ('include' selects which fields); source nodes with no counterpart move over with their loc unchanged. Edges follow. The source memory ends up empty; 'deleteSourceMem: true' then deletes it. v1 rejects encrypted source or target memories and system/app classes.
input MergeMemoriesInput!
replaceSubtree ⚠️ Int! Retired. Calls return REPLACE_SUBTREE_RETIRED without changing nodes or edges. The field remains temporarily so existing clients get a clear refusal rather than an unknown-field error.

⚠️ DEPRECATED

Retired (#454): every authorized call refuses with REPLACE_SUBTREE_RETIRED and writes nothing. Use the canonical node write mutations.
ownerRepo String!
memoryId String!
nodes [NodeInput!]!
edges [EdgeInput!]!
createEdge Edge!
sourceRef ID!
targetRef ID!
name String
loc String
description String
isRunnable Boolean
condition JSON
priority Int
data JSON
updateEdge Edge!
edgeRef ID!
name String
loc String
description String
isRunnable Boolean
condition JSON
priority Int
data JSON
deleteEdge Boolean!
edgeRef ID!
runTask String! Run a task — a runnable node (isRunnable=true) — identified by a single nodeRef (a node PK or a fully-qualified node URN, hrn:node:<root>:<memory>:<loc>), resolved by the shared resolveNodeRef (#542). A bare loc without a memory is rejected — pass a full URN or an ID. For loose-name matching and interactive task selection use the MCP hadron_run_task tool, which resolves to a concrete nodeRef before calling this. By default RENDERS the task: returns the assembled instructions (node content with {{arg}} substitution and referenced nodes) for an LLM agent to execute. Pass appRef (App PK or URN) to EXECUTE instead (#529): the server mints a MANUAL app run for the resolved task under that App and returns the run id (poll appRun(ref:) for status + output). runAsSelf attributes the run to the caller. Without appRef, behavior is unchanged. Spec cor:api:060.
nodeRef String!
args JSON
appRef ID
runAsSelf Boolean
importNode ImportNodeResult! Import external content into a node (#457, sync v1). Source: exactly one of 'url' (server-fetched inline, SSRF-guarded, Readability-extracted, ~30s budget — the fetch carries NO user credentials, so authenticated pages must come as 'content') or 'content' (client-captured, e.g. the Web Clipper's authenticated DOM). Target: 'nodeUrn' XOR ('memoryId' + 'loc'); an existing node is updated IN PLACE (NodeRevision snapshot is the undo), a missing one is created. HTML converts to Markdown at the write seam (contentType defaults to text/html here, unlike createNode).
input ImportNodeInput!
createAgentSchedule AgentSchedule!
input CreateAgentScheduleInput!
updateAgentSchedule AgentSchedule!
id ID!
input UpdateAgentScheduleInput!
deleteAgentSchedule Boolean!
id ID!
triggerAppRun AppRun! Run an entry node now (MANUAL trigger) — same launch gate + kernel as a schedule.
input TriggerAppRunInput!
cancelAppRun AppRun! The kill switch manual handle: PENDING/RUNNING to CANCELLED (cor:agt:010:02).
id ID!
createAgentWebhook AgentWebhookCredentials! Create a webhook trigger — returns the URL path + platform token ONCE (D-2026-05-02).
input CreateAgentWebhookInput!
rotateAgentWebhook AgentWebhookCredentials! New secret + token; the old URL dies immediately.
id ID!
deleteAgentWebhook Boolean!
id ID!
createConnectionGrant ConnectionGrant! Grant an App install scoped access to a connection you OWN. scopes must be a subset of {mail.read, mail.send, calendar.freebusy, calendar.read, drive.read}; expiresAt (ISO-8601) is optional and must be in the future. appRef is a PK or App URN.
connectionRef String!
appRef String!
scopes [String!]!
expiresAt String
revokeConnectionGrant Boolean! Revoke a connection grant you created (soft delete). Owner-only.
ref ID!
createPrincipalGrant PrincipalGrant! Grant a member extra management actions (org-ADMIN, interactive-only; design:grant-model). The grantee must be a live member of the org.
orgRef String!
userRef String!
actions [String!]!
expiresAt String
revokePrincipalGrant Boolean! Revoke an action grant (org-ADMIN, interactive-only; soft delete).
ref ID!
mintActionTickets Int! Mint comm.outbound tickets into the org ledger (org-ADMIN; cor:acl:050:04).
input MintActionTicketsInput!
setQuotaLimit QuotaCounter! Set the stored (enforced) quota limit for a scope x action (#510) - the operator dial that previously required psql. Org-ADMIN.
input SetQuotaLimitInput!
setAiModelPrice AiModelPrice! Append an effective-dated provider rate row (spec 046). PLATFORM ADMIN - org admins must never set the prices they are billed at.
input SetAiModelPriceInput!
grantCredits CreditLedgerEntry! Mint credits for an org - a positive grant ledger row (spec 046). PLATFORM ADMIN ONLY (self-grant risk); processor top-ups land here later.
input GrantCreditsInput!
updateMyPolicy User! Author YOUR user-layer action-policy link (cor:acl:040:02, #510). Null clears (= no restriction).
policy JSON
beginAssetUpload BeginAssetUploadResult!
input BeginAssetUploadInput!
completeAssetUpload Asset!
input CompleteAssetUploadInput!
setAgentAssetUploadEnabled App!
input SetAgentAssetUploadEnabledInput!
softDeleteAsset Asset!
assetId ID!
restoreAsset Asset!
assetId ID!
beginAssetUploadV2 BeginAssetUploadResult! Asset upload — v2 (spec 006-asset-upload-redesign) Memory-addressed begin: the caller specifies the destination memory directly, no agent traversal. Replaces agent-addressed beginAssetUpload during the deprecation window.
input BeginAssetUploadV2Input!
createAssetReferenceNode Node!
input CreateAssetReferenceNodeInput!
startSession Session! Open a session — and, when `input.workerRef` is given, a WORKER SESSION: the binding that makes the work attributable to that named teammate. The binding is held server-side and is independent of the caller's CHAT SESSION (the human's Desktop window, Claude Code session or IDE chat). **Ending your chat session does not end a worker session** (#1034): a chat session that closes leaves this session open until endSession — #1114 removed inactivity as a reason to end one, so nothing else will. The worker stops reading as LIVE once its idle window passes without a drive, which lets another bind proceed without ending the stale session or losing its unwritten handoff. A human HOLD stays in force until explicit release, so only its holder can bind it again. A client that ties its lifetime to a conversation must call endSession itself; nothing about closing a window reaches this server.
input SessionInput!
updateSession Session! #931: update a live session's mutable provenance fields (a PR is usually opened after startSession already ran). Explicit null clears; omitted preserves; an empty update is a valid liveness touch (bumps updatedAt, which liveness counts — #1114). id-only - sessions have no URN. Gate: platform admin, the session's App, or the attributed user (not via impersonation). Updates on an ended session stay allowed (late PR-merge attribution).
id ID!
prNumber Int
branch String
endSession Session! End a session, and — for a worker-bound one — write its closing handoff (#1029). This is the CLIENT operation that ends a worker session. Since #1114 it is very nearly the ONLY one: the reaper ends a session only when a hard expiresAt promised at session start has passed, never for going quiet. A client's chat session ending **does not end a worker session**, and never reaches this server (#1034). It ends the SESSION, not any HOLD. Where a person bound the worker the name stays theirs until explicitly released (#1050, [`cor:agt:020:09`](hrn:node:hadronmemory.com:specs:cor:agt:020:09)), so only that holder can bind it again. A pure App-key **bind takes no hold** — which is a fact about the bind, not a promise about the worker's state when this returns: an eligible user may have forced a binding meanwhile and taken the name, and ending the App-key session leaves that hold and that session untouched. Gate (#931): platform admin, the session's App, or the attributed user (not via impersonation). `handoff` is agent-composed prose: what landed, what is open, what is blocked, what the next driver should not repeat. The server owns where it goes (the worker's working memory, under the `handoffs` parent, at a lexically-ordered loc), so no client re-derives the convention. `summary` is NOT a smaller `handoff` (#1100). It sets `Session.summary`, a label on the session row — readable back through `session` / `sessions`, but read by no worker-continuity path, so the next driver never sees it. The two sit side by side and only one of them reaches that driver, so a caller writing a closing note picks by name: it is `handoff`. The two mistakes are not symmetric, and that is the whole hazard. Putting continuity prose in `summary` succeeds, returns a normal receipt, and produces NO handoff — discovered only once the context that could have written one is gone, and not correctable in place, since the sequence is append-only and the stint has ended (re-ending is refused). The remedy is to bind again and end THAT session with the prose. Putting a label in `handoff` is merely untidy: the row lacks its label and the text reaches the next driver anyway. Written BEFORE the session ends, and a failed write REFUSES the end (HANDOFF_WRITE_FAILED) rather than ending anyway: a still-bound worker is recoverable — retry, or end without a handoff deliberately — while an ended session whose handoff evaporated is not, because the context that composed it is gone. Passing `handoff` on a session with no worker is refused rather than dropped; there is no sequence to write it to.
id ID!
summary String
handoff String
inputTokens Int
outputTokens Int
turnCount Int
errorCount Int
recordUsage Boolean! Record a usage event
event UsageEventInput!
regenerateAppKey AppCreated! App key management — revoke all active keys for an App and mint a fresh one. (See createAppKey / revokeAppKey / deleteApp below.) Accepts the entity's ID or URN.
appRef ID!
updateMyProfile User!
name String
handle String
email String
updateUserRoles User!
userId ID!
roles [Role!]!
mergeUsers User! Consolidate a duplicate account into a surviving user. Source-only identities and relationships move; duplicate role-bearing relationships preserve the strongest live entitlement. The source is soft-deleted. Platform admin, or an org admin/owner when both users are live members of an organization they administer. The resulting merge is global.
input MergeUsersInput!
deleteUser Boolean! Delete a user account (self-serve account deletion, Apple 5.1.1(v) / GDPR erasure posture). Requires a user credential (JWT or hdr_user key); App-key callers are rejected. Omitting userRef deletes the CALLER's own account (no role needed). A platform ADMIN/OWNER may name another user; userRef accepts the user's ID, handle, or hrn:user:<handle> URN. Any other caller naming a target receives the same FORBIDDEN error whether or not that user exists (no user-enumeration oracle). Org memberships auto-resolve: an org where the target is the only active OWNER but which still has OTHER active members BLOCKS deletion (error code SOLE_ORG_OWNER, extensions.organizations lists the blockers - transfer ownership first, nothing is deleted); an org where the target is the ONLY active member is soft-deleted with the account; plain memberships are revoked. Owned data is hard-erased: personal/private and user-owned memories, user-owned Apps and Agents (with their standard delete cascades), sessions, OAuth/handoff codes, provider connections, grants, shares, subscriptions, widgets and user secrets; API keys are revoked. The User row itself is kept for audit/billing FK integrity but scrubbed (identity fields nulled, handle replaced with a random deleted-... value) and soft-deleted; a deleted account can no longer authenticate.
userRef ID
createOrganization Organization!
name String!
urn String!
updateOrganization Organization! Update an Organization. Accepts the entity's ID or URN.
id ID!
name String
urn String
listedOnMarketplace Boolean
policy JSON
deleteOrganization Boolean! Soft-delete an Organization. Accepts the entity's ID or URN.
id ID!
linkGithubApp Organization! Link a GitHub App installation to an organization (org ADMIN or platform ADMIN). Accepts the entity's ID or URN.
orgId ID!
installationId Int!
updateOrgGithubApp Organization! Set GitHub App credentials for an organization (encrypted at rest). Accepts the entity's ID or URN.
orgId ID!
appId String!
privateKey String!
unlinkGithubApp Organization! Remove all GitHub App config from an organization. Accepts the entity's ID or URN.
orgId ID!
createUserInvitation UserInvitation! User invitations (org ADMIN or platform ADMIN). Accepts the entity's ID or URN.
orgId ID!
memberRole Role!
name String
email String
githubUsername String
maxActivations Int
expiresInDays Int
inviteUser UserInvitation! Platform invite — no org association, just onboards a new user
name String
email String
githubUsername String
maxActivations Int
expiresInDays Int
acceptInvitation Boolean!
slug String!
addOrgMember OrgMember! Accepts the entity's ID or URN (orgId).
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
role Role!
updateOrgMember OrgMember! Accepts the entity's ID or URN (orgId).
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
role Role!
removeOrgMember Boolean! Accepts the entity's ID or URN (orgId).
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
createMemory Memory! Create a memory in an organization. Accepts the entity's ID or URN for orgId. Defaults to knowledge-class with ORGANIZATION visibility. Pass memoryClass: group + visibility: GROUP for a group-class memory (023-app-shape US4; the caller is auto-added as the first owner), or memoryClass: personal | private for an owner-only memory the caller owns (spec 034 — free-standing, no app/agent; the caller must be a member of the org container). system- and app-class memories are NOT created here — they auto-provision via Agent.systemMemoryId / the App install path.
orgId ID Owning organization. OPTIONAL (spec 047 — user-owned tenancy): when OMITTED, the memory is owned by the authenticated caller in their own handle namespace (organizationId NULL, ownerUserId = caller), and its URN roots on the caller's handle (hrn:mem:<handle>:<slug>) rather than an org domain. The org-less path supports only the owner-only classes 'personal' and 'private'; pass orgId for 'knowledge' / 'group'.
name String!
shortDescription String
description String
data JSON Free-form structured metadata bag (client-defined JSON). Null = unset.
tags [String!]
license String
category0 String
category1 String
category2 String
iconUrl String
heroUrl String
homeUrl String
source String
readBranch String
writeBranch String
visibility MemoryVisibility
memoryClass MemoryClass 'knowledge' (default), 'group', or the owner-only 'personal' / 'private' (spec 034). 'system' and 'app' are rejected — they auto-provision via different code paths.
vectorIndexEnabled Boolean Override the vector-index defaults. A 'knowledge'-class memory defaults to vectorIndexEnabled=true with embeddingSource=contentChunks (so any node with content is retrievable immediately, no abstract authoring needed — #281); every other class keeps the column defaults (false / abstract). An explicit value here wins for any class.
embeddingSource EmbeddingSource
maxRevCount Int #621 — per-node revision-history cap (default 10, minimum 1).
draftCorpus Boolean #1447/#1448 — create this memory as a DRAFT spec corpus, whose spec citations stay changeable until the corpus is minted. This is the only way to get a draft corpus: an existing memory can never be switched into draft. Omitted or false creates an ordinary (minted) memory.
createMemoryInApp Memory! Add a NEW memory to an App, born App-scoped. Unlike createMemory (which only makes free-standing memories), this scopes the new memory to an App. memoryClass accepts only the App-associable classes: 'app', 'personal', or 'private' ('system'/'knowledge'/'group' forbid an app_id). appRef/agentRef accept an ID, bare URN, or prefixed URN; the Agent must be installed in the App. Authorization is split by class: 'app' is shared deployment data and requires org OWNER/ADMIN; 'personal'/'private' are the caller's OWN memory and require only App membership (the caller becomes the owner). Typed errors: UNSUPPORTED_MEMORY_CLASS, BAD_USER_INPUT (maxRevCount < 1), APP_UNINSTALLED, AGENT_NOT_INSTALLED, MEMORY_URN_CONFLICT, FORBIDDEN, UNAUTHENTICATED.
appRef ID!
agentRef ID!
memoryClass MemoryClass!
name String!
shortDescription String
description String
data JSON
tags [String!]
vectorIndexEnabled Boolean
embeddingSource EmbeddingSource
maxRevCount Int
attachMemoryToApp Memory! Attach an EXISTING free-standing memory to an App. Applies only to 'personal'/'private' memories owned by the caller (an 'app'-class memory can't be free-standing, so there is nothing to attach). Sets the App and Agent scope; the memory's URN, class, and owner are unchanged. memoryRef/appRef/agentRef accept an ID, bare URN, or prefixed URN; the Agent must be installed in the App. Requires App membership. Typed errors: UNSUPPORTED_MEMORY_CLASS, MEMORY_ALREADY_APP_SCOPED, APP_UNINSTALLED, AGENT_NOT_INSTALLED, FORBIDDEN, ORGANIZATION_MISMATCH.
memoryRef ID!
appRef ID!
agentRef ID!
updateMemory Memory! Update a Memory. Accepts the entity's ID or URN. Spec 033 FR-026: enabling `vectorIndexEnabled` on an `isEncrypted` memory requires `acknowledgeVectorInversionRisk: true` in the same call. Without the flag, an `EncryptedVectorIndexNotAcknowledgedError` is thrown carrying the full four-point disclosure on `error.disclosure` (single source of truth in `FR_026_DISCLOSURE` — see `src/lib/entityRef/errors.ts`). On a non-encrypted memory the flag is a no-op.
id ID!
name String
urn String
organizationId ID
shortDescription String
description String
data JSON
tags [String!]
license String
category0 String
category1 String
category2 String
iconUrl String
heroUrl String
homeUrl String
source String
readBranch String
writeBranch String
visibility MemoryVisibility
listedOnMarketplace Boolean
vectorIndexEnabled Boolean
embeddingSource EmbeddingSource
acknowledgeVectorInversionRisk Boolean
chunkTokens Int
chunkOverlap Int
forceFixedSize Boolean
maxRevCount Int
schema JSON
transferMemoryOwnership MemoryOwnerTransferPreview! Transfer a free-standing Memory to exactly one new owner. dryRun previews the new URN and dependent handling without writing; apply recomputes under lock and can pin the preview with expectedNewUrn. A class change across user/org ownership must be explicit. The caller needs authority on both sides; unreadable source memories are concealed as not found. Group members block transfer by default; resetGroupMembers explicitly revokes every old membership inside the transfer transaction. A group destination then adds the caller as its first owner-member.
memoryRef ID!
orgRef ID
userRef ID
targetClass MemoryClass
expectedNewUrn String
resetGroupMembers Boolean
dryRun Boolean
retryFailedEmbeddings Int! #882 — re-drive every FAILED embed in a memory (the operator recovery surface; previously hand-written SQL). Re-stamps each live node whose `embeddingFailedAt` is set — permanent-class failures whose pending marker was cleared, and backed-off retryable failures (made immediately eligible) — resetting the failure state exactly like a fresh write, then wakes the embedding worker. Returns the count re-stamped; 0 when the memory is not vector-indexed. `memoryRef` accepts the memory's ID or URN. Requires memory-owner or org-ADMIN (same gate as updateMemory).
memoryRef ID!
deleteMemory Boolean! Soft-delete a Memory. Accepts the entity's ID or URN.
id ID!
cloneMemory Memory! Clone a Memory into a new Memory at `targetUrn`. `ref` accepts the source's ID or URN. `targetUrn` is a fully-qualified "root:slug" memory URN naming the clone; its org segment MAY differ from the source's, cloning the memory into another organization. The clone's display name is derived from the target slug. Copies the Memory row plus all live Nodes, Edges, and PendingEdges; references to the source memory's URN inside node content/abstract (canonical and legacy spellings) are rewritten to the clone's URN. Vector-index config carries over and the clone's nodes are stamped for re-embedding. NOT copied: revision history, subscriptions, shares, group members (the caller is bootstrapped as a group clone's first owner), sessions, licenses, log entries, assets, and git-sync config (the clone starts DB-only). Authorization: the SOURCE side mirrors deleteMemory (personal/private → owner only; knowledge/group → source-org ADMIN). When `targetUrn` names a DIFFERENT org, the caller must additionally be a non-reader member of that target org. system/app-class sources and encrypted memories are rejected.
ref ID!
targetUrn String!
extractParentNodeToMemory Memory! Extract a parent node and its whole loc-subtree into a BRAND-NEW memory, making the parent the new memory's root. The subtree is loc-prefix defined: the node at the parent's loc plus every live descendant (loc starting with 'parentLoc:'). Locs are REBASED so the parent becomes the root — 'findings:auth' becomes the memory slug and 'findings:auth:oauth' becomes '<slug>:oauth'. Edges wholly inside the subtree carry over (their loc re-derived from the rebased endpoints); boundary-crossing edges and PendingEdges are dropped. 'parentRef' is the parent node's ID or fully-qualified URN. 'targetUrn' is a fully-qualified '<root>:<slug>' URN naming the new memory (it may land in a DIFFERENT org). 'move' = false (default) COPIES the subtree, leaving the source intact; 'move' = true relocates it, soft-deleting the source subtree (its nodes, the source memory's touching edges, and its pending edges). The new memory PRESERVES the source's class so an extract never widens who can read the content: a member-restricted 'group' stays group, a personal/private source stays owner-owned, knowledge stays knowledge. Governance/config (requiresLicense, chunk dials, revision cap, acceptsUploads) carries over from the source. Authorization: read access to the source memory (denial reads as NODE_NOT_FOUND). For knowledge/group sources the caller needs the SOURCE org's memory.clone grant (an export) AND the destination org's memory.create (plus a non-reader membership for a cross-org drop); a personal/private extract instead requires the owner to be a member of the destination org. 'move' additionally requires source write access, and cannot target the source root (that would empty the source — clone + deleteMemory instead). Encrypted and system/app-class sources are rejected. v1 limitation: node content is copied verbatim — because both the slug and node locs change, URN references among the moved nodes WILL break; and unresolved PendingEdges within the subtree are dropped.
parentRef ID!
targetUrn String!
move Boolean
setMemorySourceToken Memory! Set encrypted source token for non-GitHub-App repos. Accepts the entity's ID or URN.
id ID!
token String!
expiresAt String
createAgent Agent! Create an agent. Provide orgId to create an ORG-owned agent (requires org ADMIN); OMIT orgId to create a USER-OWNED agent owned by the caller — its URN is rooted on the caller's bare handle (hrn:agent:<handle>:<slug>, grammar v2 — no @ sigil) and its system memory is user-owned too. Exactly one owner (org XOR user). personaRole and systemPrompt are the persona dressing (cor:agt:020:01) — the reusable role plus the '{{name}}'-templated prompt. The NAMED identity is a Worker, cast with castWorker; names never live on agents. orgId accepts the org's ID or URN.
orgId ID
name String!
description String
systemPrompt String
systemMemoryId String
visibility AgentVisibility
type AgentType
surfaces [String!]
personaRole String
updateAgent Agent! Update an Agent. urn renames the slug (org-owned agents only). personaRole is normalized (explicit null or blank clears, omitted preserves). Worker castings referencing this agent are unaffected — the named identity lives on the Worker. Accepts the entity's ID or URN.
ref ID!
name String
urn String
description String
systemPrompt String
systemMemoryId String
visibility AgentVisibility
listedOnMarketplace Boolean
type AgentType
surfaces [String!]
personaRole String
registerDefaults RegisterDefaultsInput
deleteAgent Boolean! Soft-delete an Agent. Accepts the entity's ID or URN.
ref ID!
repairAgentSystemMemory Agent! #949 — re-own an Agent's class=system memory to the Agent's own owner (org XOR user) and re-derive its URN as '<agent-urn>-system'. The recovery path for a system memory that ended up owned by a different tenant. Access to a memory follows the MEMORY's owner, so such a row puts one tenant's Agent design (for a Team Agent: its 'roles:<role>' definitions and name registers) inside another tenant's audience. System memories are only ever created by Agent provisioning, so before this the only fix was to recreate the Agent. Authorization: PLATFORM ADMIN only — the row being moved belongs to a different tenant than the Agent, so neither side's org admin is the right authority. Idempotent: an already-consistent pair is a no-op. Error codes (extensions.code): FORBIDDEN, AGENT_HAS_NO_SYSTEM_MEMORY, SYSTEM_MEMORY_NOT_FOUND. Accepts the entity's ID or URN.
agentRef ID!
addMemoryToAgent Agent! Attach a Memory to an Agent. Accepts the entity's ID or URN.
agentRef ID!
memoryRef ID!
role String
updateAgentMemoryRole Agent! Update an Agent's role on an attached Memory. Accepts the entity's ID or URN.
agentRef ID!
memoryRef ID!
role String!
removeMemoryFromAgent Agent! Detach a Memory from an Agent. Accepts the entity's ID or URN.
agentRef ID!
memoryRef ID!
createMemorySubscription MemorySubscription! Create a cross-org Memory subscription. Accepts the entity's ID or URN.
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
role Role!
createAgentOrgGrant AgentOrgGrant! Subscribe an organization to a PUBLIC Agent without creating an App. Accepts the entity's ID or URN.
agentRef ID!
orgRef ID!
revokeAgentOrgGrant AgentOrgGrant! Revoke an organization's Agent subscription without deleting install state. Accepts the entity's ID or URN.
agentRef ID!
orgRef ID!
updateMemorySubscription MemorySubscription! Update a cross-org Memory subscription. Accepts the entity's ID or URN.
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
role Role!
deleteMemorySubscription Boolean! Delete a cross-org Memory subscription. Accepts the entity's ID or URN.
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
orgRef String #764 — the organization as an id / slug / hrn:org:<slug> URN. Supply this OR orgId.
syncMemory Memory! Trigger an on-demand sync for one memory (org ADMIN or CONTRIBUTOR). Accepts the entity's ID or URN.
id ID!
pushMemoryToGit Boolean! Push all nodes in a memory to its configured git repo. Accepts the entity's ID or URN.
id ID!
deleteExchangeConnection Boolean!
id ID!
testExchangeConnection Boolean!
id ID!
createSlackConnection SlackConnection! Register a Slack workspace install (spec 043). orgRef/appRef accept ID or URN (cor:api:140); the App must belong to the org. Both tokens are validated live by the tool and stored encrypted THERE — core keeps identity only, and no field ever returns a token. Requires org ADMIN/OWNER.
orgRef ID!
appRef ID!
botToken String!
appToken String!
deleteSlackConnection Boolean! Soft-delete a Slack connection (org ADMIN/OWNER); tears down the tool side best-effort.
ref ID!
createMcpServer McpServer! Register an external MCP server (org ADMIN/OWNER). orgRef accepts ID or URN (cor:api:140). headers is a JSON object of static request headers (e.g. Authorization) — encrypted at rest, write-only, never returned. Registration grants nothing by itself: runs still need the policy chain to allow tool.mcp__<slug>__<tool>.
orgRef ID!
slug String!
name String!
url String!
headers JSON
toolAllowlist [String!]
enabled Boolean
updateMcpServer McpServer! Update a registered MCP server (org ADMIN/OWNER). The slug is immutable — flow nodes reference it in data.tools names. headers REPLACES the stored object; clearHeaders: true removes it (pass one or the other, not both).
ref ID!
name String
url String
headers JSON
clearHeaders Boolean
toolAllowlist [String!]
enabled Boolean
deleteMcpServer Boolean! Hard-delete a registered MCP server (org ADMIN/OWNER); runs referencing its tools fail loud afterwards.
ref ID!
createSecret Secret! Create a named, owner-scoped secret (#677). Gate per owner scope: user -> that user (ownerRef optional, defaults to the caller); org -> org ADMIN; app -> app owner / org ADMIN; memory -> memory WRITE. value is the secret payload — encrypted at rest, write-only, never returned. kind selects validation: generic (opaque JSON) or webfetch-auth ({type: bearer|basic|header, ...} + metadata.urlPrefix origin binding). A run resolves the name via the CSS cascade (memory -> app -> user -> org) at entitled scopes only.
ownerType String!
ownerRef ID
name String!
kind String!
metadata JSON
value JSON!
updateSecret Secret! Overwrite a secret's value and/or metadata (v1 rotation — no versioning). Name, kind, and owner are immutable — the cascade and flow config reference the name; create a new name instead. The effective (metadata, value) pair is re-validated per kind.
ref ID!
metadata JSON
value JSON
deleteSecret Boolean! Hard-delete a secret; referencing runs fail their resolution loud (or fall through the cascade) afterwards.
ref ID!
createHomeAssistantInstance HomeAssistantInstance! Register a Home Assistant instance (org ADMIN/OWNER). orgRef accepts ID or URN (cor:api:140). accessToken is an HA long-lived access token — encrypted at rest, write-only, never returned. Registration grants nothing by itself: runs still need the policy chain to allow tool.ha__<slug>__<op>.
orgRef ID!
slug String!
name String!
url String!
accessToken String!
opAllowlist [String!]
enabled Boolean
updateHomeAssistantInstance HomeAssistantInstance! Update a registered Home Assistant instance (org ADMIN/OWNER). The slug is immutable — flow nodes reference it in data.tools names. accessToken REPLACES the stored token (never cleared: an instance without a token is unusable).
ref ID!
name String
url String
accessToken String
opAllowlist [String!]
enabled Boolean
deleteHomeAssistantInstance Boolean! Hard-delete a registered Home Assistant instance (org ADMIN/OWNER); runs referencing its ops fail loud afterwards.
ref ID!
createResendSender ResendSender! Register one BYO Resend sender for an org. The API key is encrypted and never returned.
orgRef ID!
from String!
apiKey String!
enabled Boolean
updateResendSender ResendSender! Update the fixed sender, replace its write-only API key, or enable/disable it.
ref ID!
from String
apiKey String
enabled Boolean
deleteResendSender Boolean! Hard-delete an org's BYO Resend sender.
ref ID!
exchangeReply Boolean!
connectionId ID!
messageId ID!
body String!
exchangeMoveMessage Boolean!
connectionId ID!
messageId ID!
folderId String!
emailSaveDraft EmailDraftInfo! Save a draft in the mailbox's own Drafts folder (spec 002 US5): either a reply-draft (replyToMessageId) or a fresh draft (to + subject). Owner-only. idempotencyKey makes retries safe (at-least-once callers).
connectionRef ID!
replyToMessageId ID
to [String!]
subject String
bodyHtml String!
idempotencyKey String
emailUpdateDraft Boolean! Update a provider-side draft's body. Owner-only.
connectionRef ID!
draftId ID!
bodyHtml String!
emailSendDraft Boolean! Send a provider-side draft. Owner-only; idempotencyKey makes retries safe.
connectionRef ID!
draftId ID!
idempotencyKey String
emailDeleteMessage Boolean! Delete a message (provider trash semantics). Owner-only.
connectionRef ID!
messageId ID!
emailMarkRead Boolean! Mark a message read or unread (default read). Owner-only.
connectionRef ID!
messageId ID!
isRead Boolean
emailFlagMessage Boolean! Flag, unflag, or complete a message (default FLAGGED). Owner-only.
connectionRef ID!
messageId ID!
flag EmailFlag
emailCategorizeMessage Boolean! Set the categories (color labels) on a message. Owner-only.
connectionRef ID!
messageId ID!
categories [String!]!
calendarCreateEvent CalendarEvent! Create an event. calendarId defaults to the primary calendar. Owner-only.
connectionRef ID!
calendarId String
title String!
description String
location String
start CalendarEventTimeInput!
end CalendarEventTimeInput!
attendees [String!] Attendee email addresses.
notifyAttendees Boolean
idempotencyKey String
calendarUpdateEvent CalendarEvent! Patch an event — absent fields stay unchanged; attendees replaces the set. Owner-only.
connectionRef ID!
eventId ID!
calendarId String
title String
description String
location String
start CalendarEventTimeInput
end CalendarEventTimeInput
attendees [String!]
notifyAttendees Boolean
idempotencyKey String
calendarDeleteEvent Boolean! Delete (cancel) an event. Owner-only.
connectionRef ID!
eventId ID!
calendarId String
notifyAttendees Boolean
idempotencyKey String
calendarRespondToEvent CalendarEvent! Set the connected account's own RSVP on an event it was invited to (ACCEPTED / DECLINED / TENTATIVE — NONE is not a settable response). Owner-only.
connectionRef ID!
eventId ID!
response CalendarEventResponse!
calendarId String
exportNodeToGoogleDoc DriveFile! Export a node's markdown as a NEW Google Doc in the connected drive (nodeRef is a PK or fully-qualified URN; title defaults to the node name; folderId defaults to the drive root). Always creates — the drive tool's scope cannot edit existing files, and no update surface exists. Owner-only: no grant scope maps to doc creation (fail-closed for App/headless callers).
nodeRef ID!
connectionRef ID!
folderId ID
title String
startChat ChatStartResult! Start an AGENT CHAT (creates chat nodes, loads conversation, returns compiled prompt). When called by a JWT user, the agent chat is created in that user's personal memory for the agent (provisioned lazily if needed). #1034: an **agent chat** — deliberately not "chat session", which is reserved for the human's own conversation and names no row in this schema, and not the **team chat**, which is the App's shared channel. Accepts the entity's ID or URN.
agentId ID!
userId String
conversationName String
scope ChatScope
isTestRun Boolean
sendChatMessage ChatPromptResult! Process a user message (saves message, builds context, returns next prompt)
chatId String!
userMessage String!
processChatResponse ChatResponseResult! Process the LLM's tool response (extract data, transition stage, save assistant message)
chatId String!
toolResponse JSON!
saveChatSummary Boolean! Persist a caller-produced chat summary. #1115 — READ THIS BEFORE CALLING. The summary is STORED and nothing reads it back as a summary: no query excludes the messages it covers, and the model-history builder has no summary branch, so the node is replayed as an ordinary user turn ALONGSIDE the messages it summarises. Calling this makes the transcript longer, never shorter. The compression this was built for does not exist yet, and the trigger that used to request it (ChatPromptResult.summarizationNeeded) now always returns null. Kept working so existing callers do not break; re-arming it needs the design in #1115, not a flag.
chatId String!
summary String!
updateChatTitle Boolean! Rename a saved chat (user must own it)
chatId String!
title String!
deleteChat Boolean! Soft-delete a saved chat and all its messages
chatId String!
updateAgentAIConfig Agent! Update an Agent's AI provider config. Key is encrypted at rest. 036-ai-service-config: upserts the Agent's registry config named 'default'. Prefer createAiServiceConfig/updateAiServiceConfig for new callers. Accepts the entity's ID or URN.
agentId ID!
provider String!
model String!
apiKey String
clearAgentAIConfig Agent! Clear an Agent's AI provider config. 036-ai-service-config: deletes the Agent's registry config named 'default'. Accepts the entity's ID or URN.
agentId ID!
createAiServiceConfig AiServiceConfig! 036-ai-service-config: create a named AI config on an owner entity. apiKey semantics: non-empty = encrypted at rest with a masked preview. Omitted is accepted only where a run may use the server's own credential: a HADRON_SERVER config for openai, anthropic or bedrock at their default host (no endpoint). Every ORGANIZATION, APP and AGENT config needs its own key: the platform credential is platform spend (#1390). Any other provider (glm, openai-compatible), and any config with an endpoint, needs one too, because the SDK would otherwise send the server's credential to a host that didn't issue it (#1389). All of these are refused with AiConfigValidationError. A host that checks no key takes any placeholder. Name must be 1-64 lower-case [a-z0-9_-], unique per owner. provider must be a known provider; params are validated per provider. Auth: as aiServiceConfigs. ownerId accepts ID or URN (HADRON_SERVER: ID only).
ownerType AiConfigOwnerType!
ownerId ID!
name String!
provider String!
model String!
apiKey String
params JSON
endpoint String #1373: base URL; https only, no query/fragment/credentials. Omit for the provider default.
apiStyle AiApiStyle #1373: OpenAI-compatible providers only; omit for the provider default.
enabled Boolean
updateAiServiceConfig AiServiceConfig! 036-ai-service-config: update a named AI config. All fields optional. apiKey semantics: omitted = keep the stored key; empty string = clear it; non-empty = replace (encrypted, preview recomputed). The RESULTING config is held to createAiServiceConfig's key rule, so clearing the key of a config that needs one is refused. Auth: admin rights on the owning entity (as aiServiceConfigs).
id ID!
name String
provider String
model String
apiKey String
params JSON
endpoint String #1373: omitted = unchanged; null or empty string = clear (provider default).
apiStyle AiApiStyle #1373: omitted = unchanged; null = clear (provider default).
enabled Boolean
testAiServiceConfig AiServiceConfigTestResult! #1378: test a SAVED config by id with one small real call, through the same client, endpoint and egress guard a run uses; the stored key never leaves the server. The override argument tests edited values without saving them. Auth: as updateAiServiceConfig; refused under impersonation. Every test that reaches the provider is metered (usage type ai-config-test); NO_API_KEY and KEY_UNREADABLE make no call and record nothing. The call is bounded by a 20s timeout. Rate-limited per caller and config (RATE_LIMITED).
id ID!
override AiServiceConfigTestOverride
deleteAiServiceConfig Boolean! 036-ai-service-config: delete a named AI config (hard delete; the resolution walk simply no longer finds it). Auth: admin rights on the owning entity (as aiServiceConfigs).
id ID!
createScope Scope! Spec 049 Phase 2 — create a named scope on exactly one owner. Auth by owner: organization CONTRIBUTOR+; App or Agent ADMIN of the owning org, or the owner of a user-owned one. Name 1–64 [a-z0-9_-], unique per owner among live scopes (SCOPE_NAME_TAKEN), never `global` / `app`.
input CreateScopeInput!
createNodeRoleRule NodeRoleRulePayload! #1325 — add a rule to a memory's config (creating the config on first use). Managers only; anyone else gets MEMORY_NOT_FOUND. A role that already has a rule is refused NODE_ROLE_RULE_EXISTS.
memoryRef ID!
input CreateNodeRoleRuleInput!
updateNodeRoleRule NodeRoleRulePayload! #1325 — change a rule by its id. With expectedRevision, a rule changed since you read it is refused CONFLICT (extensions.currentRevision) and nothing is written; omitted, the last writer wins. A rule you may not manage reads as NOT_FOUND. A LOCKED rule (#1334) is refused with RULE_LOCKED (extensions ruleId, role, sourceTemplateId) for EVERY caller: unlock it first (unlockNodeRoleRule) or re-apply its required template.
ref ID!
input UpdateNodeRoleRuleInput!
expectedRevision Int
deleteNodeRoleRule Boolean! #1325 — delete a rule by its id; expectedRevision as for updateNodeRoleRule. A LOCKED rule (#1334) is refused with RULE_LOCKED for every caller: unlock it first (unlockNodeRoleRule).
ref ID!
expectedRevision Int
unlockNodeRoleRule NodeRoleRulePayload! #1334 — the deliberate step before a rule a required template LOCKED can be edited or deleted (Holger, 2026-09-27: nobody changes a locked rule through an ordinary edit). Needs lock authority: an org ADMIN/OWNER of the memory's org; on a personal/private or org-less memory, its manager. Anyone else who manages the memory gets RULE_LOCKED. Visible: bumps revision and stamps updatedBy. Unlocking an unlocked rule changes nothing. expectedRevision as for updateNodeRoleRule.
ref ID!
expectedRevision Int
applyMemoryConfigTemplate MemoryConfigApplyResult! #1334 — copy a template's rules into ONE memory's config. A one-off copy: later template edits never reach this memory, and re-applying is explicit. Needs MANAGE on the memory (else MemoryNotFound) and on the template, which exists for you only if you manage its owner (else MEMORY_CONFIG_TEMPLATE_NOT_FOUND). The rules are validated as a whole, under a share lock, as createNodeRoleRule validates a new rule's references (NODE_NOT_FOUND for one you cannot read, NOT_A_TASK), and a template rule whose task was deleted refuses the whole apply (NODE_ROLE_RULE_REF_BROKEN). All-or-nothing per memory. When the memory already has a rule for a role, a non-required template skips it and a required template replaces it. A LOCKED rule is only replaced by a caller with lock authority (see unlockNodeRoleRule); otherwise SKIPPED_LOCKED. Rules written by a required template are locked. expectedTemplateRevision: CONFLICT (with currentRevision) if the template changed since you read it. dryRun: the same report with nothing written.
templateRef ID!
memoryRef ID!
expectedTemplateRevision Int
dryRun Boolean
createMemoryConfigTemplate MemoryConfigTemplatePayload! #1325 part (c) — create a template. ownerRef names the organization, user or App; omit it for HADRON_SERVER (platform admins only; refused HADRON_SERVER_NOT_CONFIGURED when this server has no row) and for USER (yourself — you own templates for no one else). A second live template of the same name under the same owner is MEMORY_CONFIG_TEMPLATE_EXISTS.
ownerType MemoryConfigTemplateOwnerType!
ownerRef ID
input CreateMemoryConfigTemplateInput!
updateMemoryConfigTemplate MemoryConfigTemplatePayload! #1325 part (c) — change a template by its id. With expectedRevision, a template changed since you read it is refused CONFLICT (extensions.currentRevision) and nothing is written. A template you may not manage reads as MEMORY_CONFIG_TEMPLATE_NOT_FOUND.
ref ID!
input UpdateMemoryConfigTemplateInput!
expectedRevision Int
deleteMemoryConfigTemplate Boolean! #1325 part (c) — soft-delete a template. Configs it was applied to keep their copies.
ref ID!
expectedRevision Int
updateScope Scope! Spec 049 Phase 2 — update a scope you may write (auth as createScope). memoryRefs replaces the ordered list.
ref ID!
input UpdateScopeInput!
deleteScope Boolean! Spec 049 Phase 2 — soft-delete a scope you may write; its name is free again at once.
ref ID!
createChannel Channel! Spec 049 Phase 4 — create a named Channel with its chat root. The host is any memory class you may write; its audience decides the Channel's (cor:acl:030:01). LOC_OVERLAPS_CHANNEL when the address overlaps an existing Channel's.
input CreateChannelInput!
updateChannel Channel! Spec 049 Phase 4 — rename / re-describe a Channel whose host memory you may write. ref: its id or its address (Channel.chatRootUrn, #1171).
ref ID!
input UpdateChannelInput!
deleteChannel Boolean! Spec 049 Phase 4 — soft-delete a Channel AND its chat root together (the protection holds while either exists, so the address stays reserved and a restore brings both back). Host memory write access required. An App's default Channel is unlinked from the App. ref: its id or its address (`Channel.chatRootUrn`, #1171).
ref ID!
createRegisterEntry RegisterEntry! Spec 049 Phase 5 — declare that an attendee takes part in a Channel. Owner: an App (org ADMIN / owner of a user-owned App) or an organization (CONTRIBUTOR+). attendeeRef: a Worker or an Agent in the owner's context; omitted = every attendee there. REGISTER_ENTRY_EXISTS when a live row already holds that (owner, Channel, attendee). Intent, never permission.
input CreateRegisterEntryInput!
updateRegisterEntry RegisterEntry! Spec 049 Phase 5 — change a row's role / mentionOnly / description (the owner's gate).
ref ID!
input UpdateRegisterEntryInput!
deleteRegisterEntry Boolean! Spec 049 Phase 5 — soft-delete a row. Each owner removes its own rows; an App may remove its install-created default.
ref ID!
advanceChannelReadState ChannelReadState! Spec 049 Phase 7 — advance an attendee's cursor on a Channel (the relay's write; §8.7 gate: the caller must be the attendee's DRIVER — a user whose live session is bound to that Worker, or the App-key principal of the attendee's App). Monotonic: a lower seq is a no-op that returns the cursor. channelRef: the Channel's id or its address (`Channel.chatRootUrn`, #1171).
attendeeRef ID!
appRef ID
channelRef ID!
seq Int!
markOwnTeamChatRead OwnTeamChatReadState! #1353: explicitly advance the cursor owned by one exact, caller-owned live Worker session on one readable registered Channel. Shares the MCP mark-read helper; unlike advanceChannelReadState this door requires that session. Monotonic; a seq beyond the captured Channel watermark is refused.
appRef ID!
sessionRef ID!
channelRef ID!
seq Int!
confirmTeamAttentionSwitchover TeamAttentionSwitchoverResult! Explicitly confirm a previously previewed #1353 switchover. The proof is actor/App/scope-bound; any live-Worker, register or cursor drift rejects the whole transaction. It advances only through the heads captured by the preview, so messages arriving afterward remain unread.
appRef ID!
proof String!
createRevision RevisionResult! Snapshot an Agent's system memory as a new revision. Accepts the entity's ID or URN.
agentId ID!
description String
publishRevision RevisionResult! Publish a revision and pin the Agent to it. Accepts the entity's ID or URN.
agentId ID!
revisionLoc String!
versionLabel String
restoreRevision RevisionResult! Restore an Agent's system memory from a revision (auto-saves first). Accepts the entity's ID or URN.
agentId ID!
revisionLoc String!
acquireEditorLock EditorLockResult! Acquire (or renew) the editor lock for an Agent's system memory. Accepts the entity's ID or URN.
agentId ID!
releaseEditorLock Boolean! Release the editor lock for an Agent's system memory. Accepts the entity's ID or URN.
agentId ID!
joinWaitingList Boolean!
email String!
requestedFeatures String
sendContactMessage Boolean!
name String!
email String!
message String!
createApp App! Create an App and install its initial Agent. With orgRef, the App is owned by that Organization and requires org ADMIN; without orgRef, it is owned by the authenticated User and remains owner-only. An org-owned install auto-provisions an AgentOrgGrant only for its own Agent. A cross-org install requires an existing active grant; subscribe to a PUBLIC Agent through createAgentOrgGrant before creating the App. It adds the caller as an AppMember with role 'owner' only when that role is present in the Agent's installationPolicy and the caller is a User. Required AgentImports cascade automatically for org-owned Apps; optional imports cascade only when their id appears in installOptional. Personal Apps reject dependency cascades in v1. Selected dependencies must also be licensed before any App is created. The server enforces this even when a caller bypasses the portal. Accepts the entity's ID or URN.
orgRef ID Provide to create an ORG-owned App (requires org ADMIN). OMIT to create a USER-OWNED App owned by the caller — rooted on the caller's bare handle (hrn:app:<handle>:<slug>, grammar v2 — no @ sigil), owner-only. Accepts the org's ID or URN.
agentRef ID! The initial Agent installed into this new App. Required as of 009-install-agent-flow: every new App starts with an installed Agent. For an org-owned App, the server auto-provisions a grant only for an Agent owned by that org; any cross-org Agent needs an existing active AgentOrgGrant. A personal App accepts only a PUBLIC Agent or one owned by the caller, and licenses a foreign PUBLIC Agent through the owner's AgentSubscription.
name String!
urn String
appType AppType
role AppMembershipRole
description String
systemPrompt String
agentTools [String!]
aiProvider String
aiModel String
aiApiKey String 036 legacy shim over the App's 'default' AI config; held to createAiServiceConfig's key rule (#1389).
expiresAt String
surfaces [String!]
trainingMode Boolean
installOptional [ID!] 008-agent-installation: optional dep Agent ids to cascade-install. Required imports of the parent Agent always cascade; optional imports install only when their id appears here. Pass [] (or omit) to skip all optional deps. v1 accepts ID or URN.
createUserPermission CreateUserPermission
identifyUserMethod IdentifyUserMethod
sessionTimeoutSeconds Int
anonymousTtlDays Int
policy JSON App-layer action-policy link (cor:acl:040:02, #510). Explicit null clears; omitted preserves.
updateApp App! Update an App. Accepts the entity's ID or URN.
ref ID!
name String
urn String
appType AppType
role AppMembershipRole
description String
systemPrompt String
agentTools [String!]
aiProvider String
aiModel String
aiApiKey String 036 legacy shim over the App's 'default' AI config; held to createAiServiceConfig's key rule (#1389).
expiresAt String
surfaces [String!]
trainingMode Boolean
createUserPermission CreateUserPermission
identifyUserMethod IdentifyUserMethod
sessionTimeoutSeconds Int
anonymousTtlDays Int
deleteApp Boolean! Soft-delete an App. Accepts the entity's ID or URN.
ref ID!
installAgentIntoApp InstallAgentIntoAppPayload! Install an Agent into an App (023-app-shape US1). Creates an AppAgent row joining the two. An App can have multiple Agents installed; one credential addresses all of them. This is the ONE operation that attaches an EXISTING Agent to an EXISTING App — distinct from createApp (which makes a NEW App from an Agent). It is therefore also the re-attach that cor:dmo:050:03 promises: detaching an Agent retains the memories accumulated under that App-and-Agent pairing as orphans, and installing the Agent again is what makes them reachable. Under cor:agt:020:01 the AppAgent join is a team's install ROSTER (which agents are available); the App's STAFF are its Workers, cast from installed agents with castWorker. Worker rows survive uninstall (cor:dmo:050:11) — uninstalling severs future casting, not history. Rejects with code DUPLICATE_APP_AGENT when the Agent is already installed in the App. Authorization: the owner of a user-owned App, or an org member with CONTRIBUTOR+ on an org-owned App's org (platform admins included). NOT plain AppMembers: installing an existing Agent is itself a read grant on that Agent's design — its system memory becomes readable from every App context, and the returned Agent carries its systemPrompt — so this gate stays at the level that can already read the org's Agents. Accepts the entity's ID or URN for both appRef and agentRef. Optional trainingMode flag updates the per-App training flag (applies to every installed Agent — training mode is per-App, not per-Agent, per spec 023 FR-001).
appRef ID!
agentRef ID!
trainingMode Boolean
castWorker Worker! #974 — cast a Worker (cor:dmo:050:11, cor:agt:020:01/:02): the named casting of an agent ALREADY INSTALLED in the App, superseding createTeamPersona's minting semantics (nothing is minted — the agent carries the persona dressing; the Worker is the local named identity). Casting is opt-in, never requires a Team Agent (an explicit name skips the register entirely), and multiple castings of one agent per App are legal (Iris and Henry, both backend-engineer). Agent resolution: agentRef when given (must be installed here — WORKER_AGENT_NOT_INSTALLED); otherwise role picks the single installed agent whose personaRole matches (zero / several candidates: WORKER_AGENT_NOT_FOUND / WORKER_AGENT_AMBIGUOUS, never a guess). The casting's role defaults to the agent's personaRole when role is omitted. The name (cor:agt:020:02) is REQUIRED since #1050 — one attempt, and WORKER_NAME_TAKEN is the answer. There is no register to fall back to: a name is permanent within the App, so it is chosen and never derived, and a nameless cast refuses WORKER_NAME_REQUIRED rather than minting a permanent identifier nobody picked. The workers_app_name_uniq constraint IS the arbiter. Names are unique per App, case-insensitively, FOREVER (two Apps may each have an Iris; retirement and uninstall never free a name). A worker-scoped working memory is provisioned in the App's container (Worker.memoryId; best-effort — a failed provision leaves it null for lazy provisioning). Authorization: an org member with CONTRIBUTOR+ on the App's org, an AppMember of the App whose role is not 'reader', or the owner of a user-owned App. Pure App-key principals are denied. Error codes (extensions.code): WORKER_NAME_TAKEN, WORKER_NAME_REQUIRED, WORKER_AGENT_NOT_INSTALLED, WORKER_AGENT_NOT_FOUND, WORKER_AGENT_AMBIGUOUS, APP_UNINSTALLED. #1050 also removed WORKER_ROLE_NOT_FOUND from this mutation: it fired only because a nameless cast had to find a roles:<role> node to read a register. An unrecognized role now resolves no agent (WORKER_AGENT_NOT_FOUND) or, with an explicit agentRef, is simply the casting's label. #1050: the TEAM_AGENT_* and SESSION_EXPIRED refusals are gone from this mutation. They were reachable only because a nameless cast had to locate a Team Agent and read its (possibly encrypted) system memory to find a register. Casting now touches no system memory at all, so teamAgentRef is gone too rather than left accepted-and-ignored. Accepts the entity's ID or URN for appRef and agentRef.
appRef ID!
agentRef ID
role String
name String #1050: a name is now mandatory in EFFECT but stays nullable in the schema, refused at the resolver with the typed WORKER_NAME_REQUIRED. A non-null String would report the same mistake as a bare validation error, losing the reason a name cannot be derived — and it would split this surface from the MCP tool, which calls the resolver directly and would keep the typed refusal. One rule, one message, both paths.
promptOverride String
retireWorker Worker! #974 — retire a Worker (cor:agt:020:02): end the casting while reserving its name FOREVER. A retired worker stops authoring team chat at once (the worker-App pin checks retirement at post time), takes no new work records, and refuses new session bindings; the row and its name reservation survive. Idempotent — retiring an already-retired worker returns it unchanged. Same authorization as castWorker. workerRef is the worker's id or its URN (#991).
workerRef ID!
releaseWorker ReleaseWorkerPayload! #1050 — release a HELD name, so somebody else can hold it. A name is held by a person until released; nothing else frees one, which is what makes availability a fact rather than a judgement about whether an idle driver is really gone. Two principals may call this, and they are different acts: the HOLDER, releasing their own name and owing nobody notice; or an App/org ADMIN, force-releasing somebody else's — the path that exists so a departed colleague's names are not held forever. An admin force-release POSTS TO THE TEAM CHAT, naming who released what and from whom, because the incident this model answers is exactly the one where notice was owed and there was no way to give it. Best-effort: an unreachable chat never blocks the release. Releasing does NOT retire the worker, free the name for a different casting, or touch its history — the name stays permanently allocated to this casting, and the handoff sequence and worklog travel with the worker to whoever holds it next. Idempotent: releasing an unheld worker returns it unchanged. #1073 — OPTIONAL PRECONDITION. The two release paths are different acts: the holder's own release notifies nobody, an admin force-release announces itself in the team chat. A client that wants to tell its user which one it is about to perform can only classify from a pre-read, and the hold can change in between — so the act performed differs from the act described, and a hold taken in that interval is force-released silently. State what you expect and the server refuses instead. Pass expectedHolderUserId to assert a specific holder, or expectUnheld: true to assert there is none; a mismatch refuses WORKER_HOLD_STALE carrying the holder actually found. Passing neither keeps the unconditional behaviour, so this is additive. Passing both is BAD_USER_INPUT. Two arguments rather than one nullable id on purpose: distinguishing "expect nobody" from "no expectation" through a nullable argument would rest on KEY PRESENCE, and any layer that forwards the argument unconditionally sends undefined — present, and read as an assertion nobody made. That is a live hazard in this codebase, so a safety precondition does not lean on it.
workerRef ID!
expectedHolderUserId ID Assert the name is held by exactly this user. Mismatch refuses WORKER_HOLD_STALE.
expectUnheld Boolean Assert the name is held by nobody. Mismatch refuses WORKER_HOLD_STALE.
updateWorker Worker! #1010 — amend a casting's individuality after the fact. `promptOverride` is the per-worker escape hatch (cor:agt:020:01), but it could only be set at CASTING time — fixing a casting's individuality at the one moment nobody yet knows what makes it individual. The role agent's `systemPrompt` cannot stand in (it is SHARED by every casting of that role), and re-casting is barred by `WORKER_IN_USE` for any worker that has done work — precisely the ones with an identity to record. Scope is one field on purpose: `name` is permanent by law (cor:agt:020:02) and `role`/`agent` define the casting itself. OMITTING promptOverride preserves it; explicit `null` or a blank string CLEARS it, matching the nullable column and castWorker's blank-is-absent normalization. Refuses WORKER_RETIRED for a retired worker: an override is briefing text delivered at bind time, and a retired worker takes no new bindings, so the edit could never reach anyone. Same authorization as castWorker. workerRef is the worker's id or URN (#991).
workerRef ID!
promptOverride String
deleteWorker Boolean! #974 — hard-delete a NEVER-USED miscast (cor:dmo:050:11's only removal escape): refused with WORKER_IN_USE unless no session was ever bound and the worker's working memory holds no content. Anything with history retires instead — its name is bound to that history forever. Also removes the empty working memory. Same authorization as castWorker.
workerRef ID!
createTeamRole TeamRole! #960 — mint a roles:<role> definition in the Team Agent's system memory. Owns the spec knowledge a hand-authoring caller had to carry: the loc is roles:<role> (single atom). Refuses an existing role (TEAM_ROLE_EXISTS — updateTeamRole is the edit path). Authorization is the Team Agent's definition-edit gate: whoever may write the agent's system memory through the generic node surface may write roles — the write delegates to that same seam (encryption, revision snapshot, git mirror included). #1024 deliberately does NOT take `repos` here — set it with updateTeamRole. A create that also wrote data could not be made atomic: the write resurrects a tombstone through an unguarded upsert branch, and splitting it into create-then-guarded-update introduced a partial failure between the halves (the role lands, the repo write refuses). Two explicit calls put that seam where the caller can see and retry it, instead of hiding a non-atomic pair inside one mutation. #1050: a role definition no longer carries a NAME REGISTER. There is no names / nameRange / nameConvention / allowOutOfRange, and no register invariants to run, because names are not allocated — a cast supplies its own and workers_app_name_uniq is the sole arbiter. Any data.names left on an existing node is historical and is neither read nor rewritten.
appRef ID!
teamAgentRef ID
role String!
description String
updateTeamRole TeamRole! #960 — edit a roles:<role> definition. Omitted fields preserve. Unknown role: WORKER_ROLE_NOT_FOUND. Same authorization as createTeamRole. #1024: `repos` sets the role's repo affinity. Omitted PRESERVES; an empty array CLEARS; explicit `null` is treated as OMITTED, not as a clear — worth stating because a client whose variables default to null would otherwise have to guess, and the two readings differ (preserve vs wipe). Pass `[]` to clear deliberately. It is merged into the node's `data` rather than replacing it, and the write is guarded on the row's revision — a concurrent generic node write (updateNode, hadron_update_node_data) refuses TEAM_ROLE_DATA_CONFLICT rather than silently reverting, since those surfaces do not take this one's lock. Retry after re-reading. A description-only edit carries no data and takes no guard. #1050: with the register gone this otherwise edits the description alone, so the #987 expectedNames compare-and-swap is gone too — it existed solely to stop two concurrent wholesale register writes silently dropping each other's newly added FREE names, a hazard that cannot arise when the write owns no list. That closes #1028 (the client-side register CAS arithmetic) outright rather than by implementing it.
appRef ID!
teamAgentRef ID
role String!
description String
repos [String!]
deleteTeamRole DeleteTeamRolePayload! #1002 — retire a roles:<role> definition. Soft-deletes the role node and any sub-nodes under it (roles:<role>:notes is content OF the role), so the subtree is recoverable. Unknown role: WORKER_ROLE_NOT_FOUND. Same authorization as createTeamRole. #1050: unconditional. The minted-name check, TEAM_ROLE_IN_USE, transferTo's one-step supersede, the post-delete recheck and the restore compensation all existed to protect the REGISTER as a ledger of which names had been allocated to this role; with no register there is no ledger to protect and no second write to compensate for. What that machinery protected is unchanged, and was never the register's doing: a Worker's name is permanent per App (cor:agt:020:02, enforced by workers_app_name_uniq irrespective of any role), so deleting a role can no more free a taken name than it could before. Superseding a role is now create-the-successor then delete-the-old; the names were never the thing being moved.
appRef ID!
teamAgentRef ID
role String!
createTeamChatMessage TeamChatMessage! Post a message into a team App's chat (#939) as a platform operation. The team chat is ONE well-known chat per team App, at loc chats:team in the Team Agent's shared app-class memory, bootstrapped on first post. The message is written through the atomic chat-message allocator (#919), so racing posts get distinct consecutive seqs. Author derivation (#974): with sessionRef, the message is authored by that session's bound Worker (the session must be writable by the caller, a session OF this App, active, and bound to a non-retired Worker OF this App — the worker-App pin, checked at post time so retirement revokes) and the envelope records the driving sessionId; without it, by the calling human. Mentions ('@worker-name' / '@handle'; a multi-word name is mentioned by its slug, e.g. '@mary-jane') are extracted server-side at write time into the envelope. replyToSeq wires a replies-to edge to the cited message. body is capped at 65536 characters. Authorization: an AppMember of the App (any role — every member of the host is a participant per cor:acl:030:01), an org member with CONTRIBUTOR+ on the App's org, or the owner of a user-owned App. Pure App-key principals cannot post. Spec 049 Phase 8 (item J): the worker-App pin became the host-memory write check. A session's Worker authors here when its App is this one, OR when the session's on-behalf-of user may write the Channel's host memory — a cross-App post records authorAppId. A pure App-key session is admitted only on the same-App branch. SESSION_NOT_IN_APP and SESSION_WORKER_NOT_IN_APP are retired; CHANNEL_HOST_NOT_WRITABLE is the successor code. Error codes (extensions.code): TEAM_CHAT_BODY_TOO_LARGE, TEAM_CHAT_REPLY_NOT_FOUND, SESSION_NOT_FOUND, SESSION_ENDED, SESSION_NOT_WORKER_BOUND, CHANNEL_HOST_NOT_WRITABLE, WORKER_RETIRED, SESSION_EXPIRED (encrypted team memory without an active session key), TEAM_AGENT_NOT_FOUND / TEAM_AGENT_AMBIGUOUS (first-post bootstrap could not locate the Team Agent), APP_UNINSTALLED, FORBIDDEN (an authenticated caller who is not a participant), UNAUTHENTICATED (no principal at all: no token, an expired or unverifiable one; checked before the App is resolved, so it answers identically whether or not the App exists). appRef accepts the entity's ID or URN; sessionRef is the session id.
appRef ID!
body String!
replyToSeq Int
sessionRef ID
createChannelMessage TeamChatMessage! Spec 049 Phase 8 — post into ANY Channel by ref (its id, or its address — `Channel.chatRootUrn`, #1171): the createTeamChatMessage contract (author derivation, mentions, replyToSeq, the body cap, item J) addressed by Channel. createTeamChatMessage is the appRef → App.defaultChannel convenience over this — one gate, two entry points. A Channel you may not read is CHANNEL_NOT_FOUND; one you may read but not post into is FORBIDDEN.
channelRef ID!
body String!
replyToSeq Int
sessionRef ID
recordTeamWork TeamWorkItem! Record an externally visible work milestone into the team App's worklog (#947) — the append-only, authoritative PR-session join (spec cor:agt:020:03). The session must be writable by the caller and a session OF this App (SessionInput.appRef, #944); an ENDED session is accepted — late attribution (a merge lands after the session ends) is the point. ref accepts URL and short spellings and is stored in ONE canonical form (github: owner/repo#N, owner/repo@sha, owner/repo:branch, owner/repo); a bare number is refused — the server never infers a repo. kind must be one of pr, issue, commit, branch, repo; action is a free lowercased verb (opened, merged, claimed, reviewed, closed, pushed, ...). kind: pr also denormalizes the latest-wins Session.prNumber display convenience. detail is an optional JSON bag of display extras (title, URL) — stored, never filtered on. Records are append-only: correct a wrong record by recording a newer one. Authorization: an AppMember of the App (any role), an org member with CONTRIBUTOR+ on the App's org, or the owner of a user-owned App. A pure App-key principal may NOT record (read-only on the worklog). A worker-bound session records under its Worker's name (#974, cor:agt:020:05) — the worker must belong to THIS App and not be retired (SESSION_WORKER_NOT_IN_APP / WORKER_RETIRED — a retired or foreign worker refuses rather than reshaping the attribution); an unbound session records under the attributed user's handle. Typed refusals: WORK_REF_INVALID, SESSION_NOT_FOUND, SESSION_NOT_IN_APP, SESSION_WORKER_NOT_IN_APP, WORKER_RETIRED, TEAM_AGENT_NOT_FOUND / TEAM_AGENT_AMBIGUOUS (first-write bootstrap could not locate the Team Agent), APP_UNINSTALLED, FORBIDDEN, BAD_USER_INPUT. appRef accepts the entity's ID or URN; sessionRef is the session id.
appRef ID!
sessionRef ID!
tool String!
kind String!
ref String!
action String!
detail JSON
model String Optional explicit model report (#1398). Takes precedence over the session's model; a model switch or a late record should pass the model that actually did the work. Empty string is refused — pass nothing to fall back to the session's model.
updateTeamCollections TeamCollectionsPayload! (Re)declare the server-owned team collections on the team App's shared memory, converging a drifted declaration to the canonical definition (hadron-cli#401). Idempotent: 'changed' is false when the declaration already matched. Why this exists as its own operation. recordTeamWork declares the worklog collection on first write, but deliberately leaves an ALREADY-declared one untouched — a deployment that tightened it governs that surface too, which is the point of schema governance. A declaration is not always a deployment decision though: 'hadron team init' shipped a client-side copy of this schema that has since drifted from the server's (its kind enum predates the 'repo' kind), so on a CLI-bootstrapped memory a recordTeamWork(kind: 'repo') is refused by a rule nobody chose. Overriding silently would break real governance; this operation makes convergence an explicit act instead. Only the server-owned collections are rewritten — every other collection on the memory is preserved. Authorization: owner or org ADMIN on the memory, the same bar updateMemory applies to a schema edit. Team participation is deliberately NOT enough: it lets you record work, not redefine what a record is. Typed refusals: APP_UNINSTALLED, UNAUTHENTICATED, TEAM_AGENT_NOT_FOUND / TEAM_AGENT_AMBIGUOUS (when the App's shared memory must still be bootstrapped). Accepts the entity's ID or URN.
appRef ID!
uninstallAgentFromApp UninstallAgentFromAppPayload! Uninstall an Agent from an App. Deletes the AppAgent row. The Agent's per-(App, Agent, *) memories are NOT cascade-deleted (spec 023 FR-005); they persist as orphans and become reachable again if the same Agent is later reinstalled. Idempotent — succeeds whether or not the AppAgent row exists. Accepts the entity's ID or URN.
appRef ID!
agentRef ID!
addAgentToApp ⚠️ App! DEPRECATED: use installAgentIntoApp. Carried for source-compat with pre-023 portal callers. Accepts the entity's ID or URN.

⚠️ DEPRECATED

Use installAgentIntoApp (spec 023-app-shape US1). Returns Payload type with the AppAgent row.
appRef ID!
agentRef ID!
trainingMode Boolean
removeAgentFromApp ⚠️ App! DEPRECATED: use uninstallAgentFromApp. Carried for source-compat with pre-023 portal callers. Accepts the entity's ID or URN.

⚠️ DEPRECATED

Use uninstallAgentFromApp (spec 023-app-shape US1).
appRef ID!
agentRef ID!
ensureAppMember AppMember! Idempotent UPSERT of an AppMember row. Per spec 008-agent-installation FR-004 / FR-016. The role MUST be a value present in the parent Agent's installationPolicy.memberRoles. Creating a new member is rejected if the App's current member count meets or exceeds the Agent's installationPolicy.maxMembers. Updating an existing member's role does NOT trigger the maxMembers check. Accepts the entity's ID or URN.
appRef String #764 — the App as an id / URN. Supply this OR appId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
role String!
removeAppMember Boolean! Delete an AppMember row. Idempotent (no-op when the row doesn't exist). Personal-class memory at (appId, userId) is retained as an orphan per FR-015 — re-attaches automatically if the user later rejoins the same App. Accepts the entity's ID or URN.
appRef String #764 — the App as an id / URN. Supply this OR appId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
joinApp JoinAppPayload! 023-app-shape US2 — user-level install. The currently-logged-in user joins the App as an AppMember. NO OrgMember check (spec 023 FR-009) so this works for B2C / consumer / therapy use cases where the end-user is not in the App's operator org. Role defaults to the first value in the Agent's installationPolicy.memberRoles (the conventional "guest" or "owner" slot). Idempotent — if the user is already an AppMember of the App, the existing row is returned. Error codes (GraphQLError extensions.code, Error.name style): - UNAUTHENTICATED — no logged-in user in context. - AppNotFoundError — the App does not exist or is soft-deleted. - AppUninstalledError — the App is in the spec-021 soft-uninstall lifecycle phase. - OrphanAppError — the App has no installed Agents (so there's no Agent.installationPolicy to consult). - NoMemberRolesError — the primary Agent's installation_policy.memberRoles is empty, so joinApp can't pick a default role. - MaxMembersExceededError — the Agent's maxMembers limit is hit. - InvalidRoleError — the picked default role isn't accepted by the Agent's policy (rare; would indicate a policy update race). Accepts the entity's ID or URN.
appId ID!
leaveApp LeaveAppPayload! 023-app-shape US2 — user-level uninstall. The currently-logged-in user leaves an App. Idempotent (no-op when not a member). The user's personal-class Memory at (appId, userId) is NOT cascade-deleted per spec 008 FR-015 — it re-attaches if the user later re-joins. Accepts the entity's ID or URN.
appId ID!
createMemoryShare CreateMemorySharePayload! 023-app-shape US3 — asymmetric cross-user grant on a personal-class Memory. The principal (memory.userId) grants a grantee read or write access. Used for the per-pairing pattern (Alice's paired-with-Mentor-A memory is distinct from her paired-with-Mentor-B memory; each gets its own MemoryShare). Upsert semantics: re-calling with a different role on an existing (memoryId, granteeId) pair updates the role rather than throwing. For v1 the caller MUST be the principal themselves (memory.userId === ctx.userId). The Agent-mediated path (App backend acting on the principal's behalf via MCP) is supported by the access-control predicate but not by this GraphQL surface — see the deferred policy discussion linked from joinApp.ts. Share-audience rule (#778): a plain ORG-OWNED personal memory (organizationId set, no agent scope) may be shared ONLY with a current member of that org; a FREE-STANDING (org-less) personal memory may be shared with any user. Agent-scoped personal memories (the spec-023 per-pairing App pattern) are exempt — their audience is governed by AppMember + AgentSubscription, not org membership. Error codes (extensions.code, Error.name style): - UNAUTHENTICATED — no logged-in user in context. - FORBIDDEN — caller is not the Memory's principal. The caller-authority guard runs first and deliberately does not differentiate between "memory doesn't exist", "memory is not personal-class", and "caller isn't the principal" — all three return FORBIDDEN so memory metadata isn't leaked to non-principals. - MemoryShareGranteeMissingError — granteeId doesn't resolve to an existing User. Only reachable when the caller passes the principal guard. - CrossOrgShareNotAllowedError — the grantee is not a member of an org-owned personal memory's org (the #778 audience rule above). - InvalidMemoryClassForShareError / MemoryNotFoundForShareError — defined on the controller for completeness; functionally unreachable via this GraphQL mutation in v1 because the caller-authority guard short-circuits to FORBIDDEN first.
memoryId ID!
granteeRef String #769 — the grantee as a reference resolved server-side: a user id, email, handle, or hrn:user:<handle> URN. Resolution sees the full user table, so a cross-org personal-memory share works by email/handle. Supply this OR granteeId.
role MemoryShareRole!
deleteMemoryShare DeleteMemorySharePayload! 023-app-shape US3 — delete a MemoryShare. Per FR-022, revocation takes effect on the next read (there's no "deactivated" state; just a row delete). Renamed from revokeMemoryShare in #785. Caller-authority rule (#785) — principal OR self, the same shape removeMemoryMember uses: - The memory's principal (memory.userId) may delete ANY grantee row on their memory. Idempotent: deleting a share that is already gone succeeds. - A GRANTEE may delete THEIR OWN row — the stop-sharing-with-me / leave path. Omit granteeRef entirely and the mutation targets the caller's own share. - Everyone else gets FORBIDDEN. On the self path the SHARE ROW itself is the authorization, so that path is idempotent only up to the row's lifetime: repeating a successful leave answers FORBIDDEN (you already left). That is what keeps every unauthorized outcome identical — a caller who is neither principal nor grantee cannot tell "no such memory" from "not shared with me" from "not yours", by error code or by the difference between an error and a successful no-op. The same rule covers the grantee reference: a non-principal caller gets FORBIDDEN whether the named user exists or not, so neither memories nor users can be enumerated here. (A principal, who by definition already knows the memory exists, still gets NOT_FOUND for a granteeRef that resolves to nobody.) A grantee may leave a memory whose owner has soft-deleted it (the row would otherwise be stranded); the principal path still treats a soft-deleted memory as FORBIDDEN.
memoryRef ID! The memory, by id or URN (#785).
granteeRef String #769 — the grantee as a server-resolved reference (id / email / handle / hrn:user URN). Supply this OR granteeId. Omit BOTH to target the caller's own share (self-removal).
updateMemoryShareRole UpdateMemoryShareRolePayload! 023-app-shape US3 — change the role on an existing MemoryShare. Throws MemoryShareNotFoundError if the (memoryId, granteeId) row doesn't exist — use createMemoryShare to upsert. Caller-authority rule matches createMemoryShare.
memoryId ID!
granteeRef String #769 — the grantee as a server-resolved reference (id / email / handle / hrn:user URN). Supply this OR granteeId.
role MemoryShareRole!
addMemoryMember AddMemoryMemberPayload! 023-app-shape US4 — add a team member to a group-class Memory. Idempotent on the (memoryId, userId) PK: re-calling with a different role upserts the role. The caller MUST be an owner of the Memory (role = owner). The bootstrap case is handled by createMemory itself, which adds the creator as the first owner of a newly-created group memory. Error codes (extensions.code, Error.name style): - UNAUTHENTICATED — no logged-in user in context. - FORBIDDEN — caller is not an owner of the memory. (Uniform for missing-memory / wrong-class / not-an-owner cases, by the same don't-leak-metadata rule as MemoryShare mutations.) - InvalidMemoryClassForMemberError — memory is not group-class (only reachable from non-GraphQL callers in v1 — the caller-authority guard short-circuits to FORBIDDEN first). - MemoryMemberUserMissingError — the userId doesn't resolve. - LastOwnerProtectedError (FR-038) — reachable via the idempotent upsert path when the call would demote an existing sole owner to reader/writer.
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
role MemoryMemberRole!
updateMemoryMemberRole UpdateMemoryMemberRolePayload! 023-app-shape US4 — change the role on an existing team member. Throws MemoryMemberNotFoundError when the row doesn't exist; use addMemoryMember to upsert. Throws LastOwnerProtectedError (FR-038) when demoting the sole remaining owner. Caller-authority rule matches addMemoryMember. Error codes (extensions.code, Error.name style): - UNAUTHENTICATED — no logged-in user in context. - FORBIDDEN — caller is not an owner of a live group memory. - MemoryNotFoundForMemberError — memory absent or soft-deleted (only reachable from non-GraphQL callers in v1 — the guard short-circuits to FORBIDDEN first). - InvalidMemoryClassForMemberError — memory is not group-class (same; guard short-circuits to FORBIDDEN first). - MemoryMemberNotFoundError — no row at (memoryId, userId). - LastOwnerProtectedError (FR-038) — would demote the sole owner.
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
role MemoryMemberRole!
removeMemoryMember RemoveMemoryMemberPayload! 023-app-shape US4 — remove a team member. Idempotent. Removing the LAST owner is rejected with LastOwnerProtectedError (FR-038) — group memories must always have ≥1 owner; the path to fully empty one is to delete the Memory. Removing the last non-owner does NOT delete the Memory (FR-031); the row persists with its remaining owner(s). Caller-authority: either an owner of the Memory, or the member being removed (self-removal).
memoryRef String #764 — the memory as an id or URN. Supply this OR memoryId.
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
publishAgentImport AgentImport! Publish a dependency edge from a parent Agent to an imported Agent (008-agent-installation FR-005). v1 supports 1-level imports only: the imported Agent must not itself be a parent of any other import. Authorization: ADMIN/OWNER of the parent Agent's owning org. The parent's owning org MUST hold an active AgentOrgGrant for the imported Agent — bundling requires the same kind of license that installation does. Accepts the entity's ID or URN for both Agent ids.
parentAgentRef ID!
importedAgentRef ID!
position Int!
required Boolean
removeAgentImport Boolean! Delete a dependency edge between two Agents. Idempotent (no-op when the row doesn't exist). Apps that already installed the imported Agent are unaffected; removing the import only stops *future* parent installs from cascading the dep. Accepts the entity's ID or URN.
parentAgentRef ID!
importedAgentRef ID!
createAppKey AppKeyCreated! Mint a new AppKey for an App. Accepts the entity's ID or URN.
appRef ID!
label String
revokeAppKey Boolean! AppKey is keyed by raw ID — no urn column. Stays ID-only.
id ID!
createUserApiKey UserApiKeyCreateResult! 025-oauth-for-mcp FR-004: mint a new user-scoped API key for the calling User. Returns the raw key exactly once (the server stores only the SHA-256 hash). Rejected with UNAUTHENTICATED for AppKey- resolved callers (no user in context). Per Clarifications, label is optional (portal defaults to a placeholder when omitted).
label String
revokeUserApiKey UserApiKey! 025-oauth-for-mcp FR-004: revoke a user-scoped API key owned by the calling User. Returns the updated UserApiKey so the portal can render the new revokedAt without a refetch (PR-137 review delta D3 — was Boolean). Idempotent for already-revoked keys; rejected with FORBIDDEN if the key belongs to another user; NOT_FOUND if id does not exist; UNAUTHENTICATED for AppKey- resolved callers.
id ID!
revokeAgentSubscription AgentSubscription! 005-agent-subscription FR-023 + FR-028 + FR-029: revoke a user's AgentSubscription. Authorized for ADMIN/OWNER of the Agent's owning org. Side effect: empty personal Memory of (user, agent) is hard- deleted; non-empty is retained with userMemoryOfAgentId preserved. Accepts the entity's ID or URN (agentId).
userRef String #764 — the user as an id / email / handle / hrn:user:<handle> URN. Supply this OR userId.
agentRef String #764 — the Agent as an id / URN. Supply this OR agentId.
createAppUser User! App Key callers only: vouch for a user, creating (or returning) the User this App knows by `externalId`. Managing that user's SESSIONS is the startAgentSession / endAgentSession pair below, not this call.
externalId String!
handle String
name String
email String
startAgentSession Session! Multi-user-agent session lifecycle (renamed to avoid collision with the developer/guided startSession and endSession mutations). dataKey is base64-encoded, 32 bytes, required when the app is configured with identifyUserMethod: SECRET and the target memory is (or will become) encrypted.
agentId ID!
externalUserId String
sessionTimeoutSeconds Int
dataKey String
endAgentSession Boolean!
sessionId ID!
linkMemoryToUser Memory! Link an anonymous memory to a real user (converts session memory ownership). Providing dataKey encrypts the memory in place atomically with the link. Accepts the entity's ID or URN.
memoryId ID!
externalUserId String!
dataKey String
encryptMemory Memory! Convert a plaintext PRIVATE memory to encrypted (spec 041, caller-held keys). Exactly one of dataKey (base64, 32 bytes) / passphrase (scrypt- derived; salt + params stored on the memory, the key itself never). Owner or org-App-key. All node content/abstract/data is re-written as ciphertext in one transaction. One-way without the key: the server cannot recover the content. Accepts the entity's ID or URN.
memoryId ID!
dataKey String
passphrase String

Objects

AccessGrant

issue #323: one reason a subject has (or would have) access to a resource.

Field Argument Type Description
source AccessSource!
role String! The role/level this particular grant confers.
via String Human label for the grant's origin (owning org URN, the inherited memory URN, the app URN, etc.); null when the grant has no distinct origin.

ActionTicket

One consumable ticket in the org ledger (cor:acl:050:04 tier 2).

Field Argument Type Description
id ID!
organizationId ID!
appId ID
action String!
mintedBy String!
note String
consumedByRunId ID
consumedAt DateTime
expiresAt DateTime
createdAt DateTime!

ActionTicketsPage

Field Argument Type Description
items [ActionTicket!]!
total Int!

AddMemoryMemberPayload

Field Argument Type Description
memoryMember MemoryMember!

Agent

The reusable definition a Builder creates and the marketplace can list. Agents are installed into runtime Apps through the AppAgent N:M join: an App may install many Agents, and an Agent may be installed in many Apps. The App, not the Agent, is the org- or user-owned caller identity and deployment.

Field Argument Type Description
id ID!
organizationId String
urn String!
name String!
description String
systemPrompt String Templated Agent prompt. {{name}}/{{role}} bind to the Agent in chats or the named Worker in briefings.
systemMemoryId String
visibility AgentVisibility!
listedOnMarketplace Boolean! cor:acl:080:04 — opt-in marketplace catalogue flag (advertisement, not access).
type AgentType!
surfaces [String!]!
personaRole String Persona role - free string with conventions ('backend-engineer').
aiProvider String
aiModel String
hasAiApiKey Boolean!
aiApiKeyPreview String
properties JSON
memoryItems [AgentMemoryItem!]!
memoryProvisioning MemoryProvisioning!
installationPolicy InstallationPolicy!
registerDefaults RegisterDefaults! Spec 049 Phase 5 — the defaults this Agent's install-created register row is made with.
imports [AgentImport!]!
importedBy [AgentImport!]!
apps [App!]! Apps that have this Agent installed (via the AppAgent join). After spec 023-app-shape, an App can install multiple Agents; this list contains every App where THIS Agent is one of the installed ones. #551: these are OTHER tenants' App objects (whose keys/members are not re-gated at the field level), so — unlike the Agent's own fields — this is NOT public on a PUBLIC agent. Scoped to Apps in orgs the CALLER belongs to (platform ADMIN/OWNER: all); a non-member gets an empty list.
appAgents [AppAgent!]! 023-app-shape: the AppAgent join rows where this Agent is installed. Use App.appAgents to get the join rows from the App side. #551: same caller-org scope as apps — never exposes a foreign org's App.
appCount Int! Adoption count. #551: the agent owning-org members' view (0 otherwise).
orgGrants [AgentOrgGrant!]! Org grants for this Agent (which orgs were granted it). #551: the agent's customer list — visible to members of the Agent's owning org only (empty otherwise), regardless of the agent's PUBLIC visibility.
createdAt DateTime!

AgentAIConfig

Decrypted AI config for an agent — the portal backend uses it to make LLM calls on behalf of the logged-in user.

Gated by requireOwnerOrOrgRole, which branches on the Agent's OWNERSHIP CLASS: an org-owned Agent admits an ADMIN of that org (and a platform admin), while a USER-owned one (ownerUserId set, organizationId null) admits only its owner — strictly, with no org path and no admin bypass. Published as "only returned to org admins" until #1232, which is the org-owned half of the rule.

036-ai-service-config: registry-backed (the Agent's config named 'default'); prefer resolveAIConfig for new callers.

Field Argument Type Description
provider String!
apiKey String!
model String!

AgentImport

008-agent-installation: Agent dependency edge ("Suite imports Calendar"). v1 enforces 1-level imports only.

Field Argument Type Description
parentAgentId ID!
importedAgentId ID!
parentAgent Agent The parent Agent when this caller may read its definition.
importedAgent Agent The dependency Agent when this caller may read its definition; a builder grant alone does not grant that read.
position Int!
required Boolean!
createdAt DateTime!

AgentMemoryItem

Field Argument Type Description
id ID!
memory Memory!
role String!
createdAt DateTime!

AgentOrgGrant

008-agent-installation: Org↔Agent license. Authorizes (a) creating an App in this Org that uses this Agent, and (b) bundling this Agent in an AgentImport edge of an Agent the Org owns. Sibling to AgentSubscription.

Field Argument Type Description
orgId ID!
agentId ID!
organization Organization The customer Organization when the caller may read it; a vendor grant alone does not authorize it.
agent Agent The licensed Agent when this caller may read its definition.
activatedAt DateTime
expiresAt DateTime
revokedAt DateTime
revokedBy ID
isActive Boolean!
createdAt DateTime!

AgentSchedule

Recurring trigger (cor:agt:010, D-2026-07-04-E).

Field Argument Type Description
id ID!
organizationId ID!
appId ID!
agentId ID
userId ID On-behalf-of user captured at creation; v1 only the creator themself.
scopeId ID Spec 049: the scope this trigger's runs carry (null ⇒ 'app').
name String!
cron String 5-field cron for recurring schedules; null for one-shots (#510).
runAt DateTime One-shot instant (#510); null for recurring schedules. XOR with cron.
timezone String!
entryNodeUrn String!
eventData JSON
policy JSON
aiConfigName String
enabled Boolean!
lastRunAt DateTime
nextRunAt DateTime
createdAt DateTime!
createdBy String

AgentSchedulesPage

Field Argument Type Description
items [AgentSchedule!]!
total Int!

AgentSubscription

Field Argument Type Description
userId ID!
agentId ID!
user User
agent Agent
activatedAt DateTime
expiresAt DateTime
revokedAt DateTime
revokedBy ID
isActive Boolean!
createdAt DateTime!

AgentWebhook

Webhook trigger (D-2026-05-02). The secret is NEVER exposed here — the URL is returned once at create/rotate.

Field Argument Type Description
id ID!
organizationId ID!
appId ID!
agentId ID
userId ID
scopeId ID Spec 049: the scope this trigger's runs carry (null ⇒ 'app').
name String!
argsSchema JSON
entryNodeUrn String!
eventData JSON
policy JSON
aiConfigName String
enabled Boolean!
lastCalledAt DateTime
createdAt DateTime!
createdBy String

AgentWebhookCredentials

Create/rotate result — path + platform token are shown ONCE.

Field Argument Type Description
webhook AgentWebhook!
path String! Server-relative URL path: /hooks/<secret>/<name>. Append ?hpt=<token>.
token String! The time-limited platform JWT (hpt) — the second factor. Store it now.

AgentWebhooksPage

Field Argument Type Description
items [AgentWebhook!]!
total Int!

AgentsPage

Field Argument Type Description
items [Agent!]!
total Int!

AiConfigTestKnob

1417: what one provider knob in the effective params did on the call.

Field Argument Type Description
param String! The params key, e.g. temperature.
state String! SENT (carried to the provider, no adapter warning) | ADJUSTED (sent, but the adapter changed the value, e.g. clamped to its range) | DROPPED (the adapter removed it, e.g. temperature on a reasoning model) | NOT_SENT (the platform does not forward it; see detail)
detail String Why it was not sent, or the adapter's own warning text.

AiModelPrice

046-usage-metering: effective-dated provider rate row. Prices are micro-cents (1e-8 USD) per MILLION tokens, as base-10 integer strings.

Field Argument Type Description
id ID!
provider String!
model String!
tier Int! Pricing tier 1-3; tier 1 is the default everything resolves today.
inputMicroCentsPerMTok String!
outputMicroCentsPerMTok String!
markupBasisPoints Int Per-tier debit markup in basis points (2500 = 25%). Null falls back to the CREDIT_MARKUP_PERCENT env default.
effectiveAt DateTime!
createdAt DateTime!
createdBy String

AiModelPriceList

Field Argument Type Description
items [AiModelPrice!]!
total Int!

AiProviderEndpoint

1373: one known endpoint for a provider, from the server's single suggestion list.

Field Argument Type Description
url String!
label String!
isDefault Boolean! The endpoint a config with no endpoint uses.
note String What the URL does not say: billing, terms.

AiServiceConfig

036-ai-service-config: a named AI service configuration (masked management view — never carries key material beyond the preview). Owned by exactly one of HadronServer / Organization / App / Agent. Resolution walks App -> Agent -> Org (of the App) -> HadronServer and returns the first ENABLED config with the requested name. Well-known fallback name: 'default' (conventional extras: 'fast', 'frontier'). Name is unique per owner.

Field Argument Type Description
id ID!
name String!
ownerType AiConfigOwnerType!
ownerId ID! ID of the owning HadronServer / Organization / App / Agent.
provider String! Provider identifier; known: 'anthropic' | 'openai' | 'glm' | 'bedrock' | 'openai-compatible' (any OpenAI-wire host; endpoint required).
model String! Model identifier, passed verbatim to the provider.
hasApiKey Boolean!
apiKeyPreview String Ellipsis + last 4 characters of the stored key; null when no key.
params JSON Provider-specific knobs (maxTokens, thinking, effort, baseUrl, ...).
endpoint String #1373: the base URL this config's requests are sent to, as stored. Null = the provider's default (see effectiveEndpoint and aiProviderEndpoints).
effectiveEndpoint String #1373: where a request for this config actually goes: endpoint, else the provider default. Null only for bedrock, whose endpoint is derived from its region.
apiStyle AiApiStyle #1373: the wire protocol as stored; null = the provider default. OpenAI-compatible providers only.
effectiveApiStyle AiApiStyle #1373: the wire protocol a request actually uses (apiStyle, else the provider default); null for single-protocol providers.
enabled Boolean! Disabled configs are skipped by resolution (the walk continues outward).
createdAt DateTime!
updatedAt DateTime

AiServiceConfigTestError

Field Argument Type Description
code String! NO_API_KEY | KEY_UNREADABLE (the stored key cannot be decrypted here; re-enter it) | CONFIG_INVALID (the config cannot be built at all, e.g. a malformed Bedrock key; no call made) | QUOTA_EXCEEDED (a platform-funded test with the org's daily token allowance used up; no call made) | LLM_CALL_FAILED (the provider call failed or timed out; see transient)
message String! Provider error text, scrubbed of the key and truncated.
transient Boolean!

AiServiceConfigTestResult

1378: one test call, made through the same client and endpoint a run uses.

Field Argument Type Description
ok Boolean!
provider String!
model String!
endpoint String The endpoint the request actually went to: the explicit endpoint, else the host the SDK chose (OPENAI_BASE_URL / ANTHROPIC_BASE_URL when the server sets one, Bedrock's regional host), else the catalog default. Null when it can't be known (Bedrock with no region).
apiStyle AiApiStyle The wire protocol the request actually used.
used AiServiceConfigTestUsed!
latencyMs Int!
reply String The model's reply, truncated.
error AiServiceConfigTestError
knobs [AiConfigTestKnob!]! #1417: what each provider knob in the effective params did on the call. Empty unless the call completed (a failed call's adapter warnings are unknown), or when the params carry none. maxTokens is listed as the ceiling it set (ADJUSTED when the probe's cap is lower); toolCalling (a gate) is not a provider knob and is not listed.

AiServiceConfigTestUsed

1378: per field, whether the test used the saved value or the override.

Field Argument Type Description
provider AiConfigTestValueSource!
model AiConfigTestValueSource!
endpoint AiConfigTestValueSource!
apiStyle AiConfigTestValueSource!
params AiConfigTestValueSource!
key AiConfigTestKeySource!

AiServiceConfigsPage

Field Argument Type Description
items [AiServiceConfig!]!
total Int!

App

A runtime caller identity owned by exactly one Organization or User. It owns long-lived App Keys and installs Agents through the AppAgent N:M join: one App may install many Agents, and one Agent may be installed in many Apps. The singular agentId / agent fields below are soft-deprecated convenience reads of the first install, not a direct foreign key.

Field Argument Type Description
id ID!
name String!
urn String!
defaultChannel Channel Spec 049 Phase 4: the App's default Channel — the row its team chat became (null until provisioned).
policy JSON App-layer action-policy link (cor:acl:040:02, #510). Null = no restriction. Org-admin-authored via updateApp.
organizationId String
organization Organization
agentId ID SOFT-DEPRECATED convenience read: the FIRST installed Agent. An App installs MANY Agents through the `AppAgent` join (023-app-shape Phase 2b, which DROPPED the direct FK that 008-agent-installation had introduced) — see `appAgents` below for the real cardinality. Kept so single-Agent clients keep working; do not build new multi-Agent logic on it.
agent Agent
installedAgentUrn String SOFT-DEPRECATED convenience read: the FIRST installed Agent's canonical URN, matching agentId / agent. Use appAgents for the full install roster. An installed agent stays AUTHOR-ROOTED forever, so this is simply that Agent's own URN: hrn:agent:<author-org>:<agent-slug>. Two orgs installing same-slug agents from different authors therefore still produce distinct URNs (they differ at the author root), enabling audit-log entries to identify the selected Agent unambiguously without a PK disambiguator. The v1 spec-021 R2 install chain (hrn:agent:<installing-org>::<app-slug>::<author-org>:<agent-slug>) is RETIRED — see #697 Stage 2 / D10 and emitInstalledAgentUrnV2. Returns null when the App has no installed Agent or the selected Agent's stored URN cannot be emitted in grammar v2.
trainingMode Boolean!
surfaces [String!]!
members [AppMember!]!
memberCount Int!
sharedMemory Memory The App's SHARED app-class memory (#965) — the team space the shared/team chat and the worklog live in, provisioned at install since #951. This is the App → memory hop clients need to default a team-memory argument (hadron-cli#399) instead of scanning every readable memory for Memory.appId. Read-only resolution, never provisions: the memory holding the App's 'worklog' root wins (the ledger home — oldest, mirroring the worklog locator), else the oldest agent-keyed shared row (sharedMemoryOfAgentId set; Worker working memories are also app-class rows under this App and are never this answer). Null when nothing has provisioned a shared memory yet (an App predating #951 with no team activity), or when the caller cannot read the memory record (the ordinary record read gate — denied and missing are indistinguishable).
appType AppType!
role AppMembershipRole!
description String
systemPrompt String
agentTools [String!]!
aiProvider String
aiModel String
hasAiApiKey Boolean!
expiresAt DateTime
createUserPermission CreateUserPermission!
identifyUserMethod IdentifyUserMethod!
sessionTimeoutSeconds Int!
anonymousTtlDays Int!
appAgents [AppAgent!]! 023-app-shape US1: Agents installed in this App, via the AppAgent N:M join. Multiple Agents can be installed; one App credential routes to all of them via the URN supplied in the request.
agents [Agent!]! 023-app-shape: convenience that returns every installed Agent (equivalent to App.appAgents.map(aa => aa.agent)). Previously was a soft-deprecated single-element synthesis; now returns the FULL multi-Agent set per spec 023 US1.
keys [AppKey!]!
createdAt DateTime!
updatedAt DateTime

AppAIConfig

Decrypted AI config for an App (owner / org admin only).

Field Argument Type Description
provider String!
apiKey String!
model String!

AppAgent

023-app-shape US1: the App↔Agent N:M join. Reintroduced after spec 008 collapsed it; per FR-003 it carries NO role column (system memory is read-only to every App) and per FR-001 it carries NO trainingMode column (training mode is per-App, on App.trainingMode).

Field Argument Type Description
app App The App when this caller may read it; strict user ownership still applies.
agent Agent The installed Agent when this caller may read its definition; an installation license alone does not grant that read.
createdAt DateTime!
createdBy String
updatedAt DateTime
updatedBy String

AppCreated

Returned once at App creation / regenerateAppKey — the raw key is never stored

Field Argument Type Description
app App!
rawKey String!

AppKey

Field Argument Type Description
id ID!
appId String!
keyPreview String!
label String
createdAt DateTime!
lastUsedAt DateTime
revokedAt DateTime

AppKeyCreated

Returned once at creation — the raw key is never stored

Field Argument Type Description
key AppKey!
rawKey String!

AppMember

Field Argument Type Description
appId ID!
userId ID!
app App The App when the caller may read it; an external App member can still see their membership row.
user User!
role String!
isOrgExternal Boolean! 023-app-shape US2: true when this User is an AppMember of the App but NOT an OrgMember of the App's owning Organization. Derived at query time from the absence of an OrgMember row (per spec 023 FR-011 — no appOnly column is added to the AppMember table). Unlocks the B2C / therapy / consumer use cases where end-users use an App without joining the operator's org.
createdAt DateTime!
updatedAt DateTime

AppRun

One headless run — the audit record AND (v1) the activation: policy snapshot, live budgets (zeroing halts the run), lifecycle, trigger provenance. Spec cor:agt:010:02.

Field Argument Type Description
id ID!
urn String Computed grammar-v2 URN hrn:apprun:<root>:<app>:<run-id> (#696). Null for the rare multi-segment App URN the flat shape cannot express.
organizationId ID!
appId ID!
agentId ID
userId ID On-behalf-of user (cor:agt:010:01). Null = pure App service identity.
triggerKind AppRunTriggerKind!
triggerId ID
entryNodeUrn String!
eventData JSON
data JSON The run envelope (plan-multi-node D-MN-1): fields extracted by flow nodes as the walker advances. eventData stays the immutable trigger payload; on key collision the envelope wins.
hops JSON Per-hop trail (#538): one element per completed hop — [{node, edgeOut, startedAt, finishedAt, tokensSpent}]. edgeOut is the routing edge taken FROM the hop (null on the last). A hop that failed mid-execution has no element; curNodeUrn + failure identify it.
spawn JSON Fan-out state (plan-spawn, #548): set on runs that called hadron_spawn — {processItem, itemKey, callback?, itemCallback?, total, callbackFiredAt?, callbackError?}. callbackError records a callback mint that was denied (quota/policy) — the digest that never came.
policy JSON
budgetTokens Int Remaining LLM-token budget — live-decremented; zero halts the run.
budgetActions Int Remaining action budget — live-decremented per write/tool action.
tokensSpent Int Total LLM tokens consumed by this run (#832). Unclamped, so it stays truthful when a hop overshoots the remaining budget — unlike budgetTokensInitial minus budgetTokens, whose decrement is clamped. Counts every hop whose LLM call COMPLETED, including hops whose run later failed — strictly more than a sum over the hops trail, which has no element for a hop that did not complete. KNOWN GAP: a hop aborted by the TOOL LOOP (a tool denied by policy, or a tool handler that threw) is NOT counted. The provider has already billed that call, but the tool-loop helper discards the accumulated usage when a tool throws, so the number is not available at the failure site. Such a run under-reports. Do not treat this as a billing figure. NULL means UNKNOWN: a run minted before the counter existed, with no hop trail to back-fill from. 0 means it genuinely spent nothing.
actionsSpent Int Total budgeted actions consumed (#832) — only actions actually admitted by the budget guard; a denied action counts nothing. NULL means UNKNOWN, not zero: runs minted before the counter existed have no recorded action spend and, unlike tokens, no hop-trail signal to reconstruct one from. Reporting 0 would contradict their already-decremented budgetActions.
scope SearchScopeInfo Spec 049 (D-2026-09-13-004): the scope this run carries, as YOU may read it — the snapshot taken at mint (the trigger's scope, or a spawned parent's) re-intersected with your access; memoryUrns lists what you may read of it and droppedCount the rest. Null ⇒ the run carries no named scope and its view is the App's attached memories ('app').
register [RegisterViewRow!]! Spec 049 Phase 6 (D-2026-09-13-007): the register this run's attendee (its Agent in its App) carried at mint, as YOU may act on it — rows from the snapshot, watermarks live, gates evaluated now. Empty when the run has no Agent (no attendee) or no rows.
budgetTokensInitial Int The token budget this run was MINTED with — budgetTokens is what REMAINS. NULL for runs minted before this was recorded, which is deliberate: 0 would render as 'no budget' rather than 'unknown'.
budgetActionsInitial Int The action budget this run was minted with (#832). NULL for pre-existing runs.
status AppRunStatus!
curNodeUrn String
parentRunId ID
failure JSON
timeoutMs Int
attempts Int!
startedAt DateTime
finishedAt DateTime
createdAt DateTime!
createdBy String
app App The App this run activated. Null when the caller may not read that App, or when the App row was hard-deleted. appId remains the audit record.
agent Agent The agent that ran, when the run resolved one and the caller may read it.
user User On-behalf-of user (cor:agt:010:01). Null = pure App service identity. Personal fields on the returned User carry their own #384 gate.
entryNode Node The resolved entry node. NULLABLE by design — entryNodeUrn is the immutable audit record and the node may have been deleted, moved, or be unreadable to this caller since the run. A null here is a normal, expected state.

AppRunsPage

Field Argument Type Description
items [AppRun!]!
total Int!

AppsPage

Field Argument Type Description
items [App!]!
total Int!

Asset

Field Argument Type Description
id ID!
memoryId ID!
filename String!
mimeType String!
sizeBytes Int!
scanStatus AssetScanStatus!
scanSignature String Engine signature name recorded when the scan verdict was BLOCKED (the row is kept as an audit tombstone; its bytes are deleted). Null otherwise.
description String
uploadedAt DateTime!
uploadedBy ID Uploader (User id). Null when the asset was created server-side by an app run (hadron_store_file).
deletedAt DateTime
urn String! The asset's URN, grammar v2 (cor:urn:010:01): hrn:asset:<root>:<mem...>:assets:<asset.id>, emitted by emitAssetUrnV2 from the holding memory's STORED urn. <mem...> is one or more atoms, not always one. A per-user memory urn minted before the #697 Stage-3 flip is still compound (<root>:<agent>:app-user:<id> and friends), and each extra memory atom lengthens the asset URN with it — so the 'assets' marker is not at a fixed offset. A parser recovering the holding memory must take everything between the type word and that marker, never a fixed two-atom prefix. Prefer locating the LAST 'assets' atom and reading the id after it (assetIdFromRef does exactly this). Two degraded shapes a parser must tolerate, both of which really occur: * hrn:asset:unknown:assets:<asset.id> — the holding memory row could not be loaded. Carries no memory identity at all. * <memory.urn>:assets:<asset.id> — the pre-#697 shape, with NO hrn:asset: prefix, served when v2 emission throws (empty, over-length or charset-invalid atom in the stored memory urn). It interpolates the stored memory urn verbatim, which is normally bare (<root>:<slug>), so this shape usually looks like acme.com:docs:assets:<id>. A third oddity is reachable through the NORMAL branch, not a fallback: a legacy memories.urn row that predates the chk_memory_urn_not_prefixed guardrail can still hold a rendered hrn:mem:-prefixed value, and that composes into hrn:asset:hrn:mem:<root>:<slug>:assets:<id>. A prefix strip that assumes one leading type word will mis-read it.
publicUrl String Stable, UNAUTHENTICATED hotlink to the bytes (GET /assets/<id>). Anyone with this URL can fetch the file — there is no read gate on it. A deliberate, temporary posture; see src/api/assetPublic.ts. Do not present it in a UI as though it were access-controlled. Null when the server has no BASE_URL configured (no canonical origin to build an absolute URL from), when the asset is not CLEAN, or when its memory is encrypted — encrypted memories are never hotlinkable, since an anonymous request holds no session key.

AssetDownloadUrl

Field Argument Type Description
url String!
expiresAt DateTime!
filename String!
mimeType String!
sizeBytes Int!

AssetListResult

Field Argument Type Description
assets [Asset!]!
total Int!
hasMore Boolean!

AssetPutHeader

Field Argument Type Description
name String!
value String!

AssetsPage

Uniform find-many envelope for assets (spec cor:api:120).

Field Argument Type Description
items [Asset!]!
total Int!

AuthContext

The resolved principal for the credential presented on THIS request (issue #562). Exposes the AuthContext the server already resolves per request (cor:aut:020:02) read-only, so any surface (CLI, portal) can validate a token and name the exact credential without per-client hacks.

Security posture (held deliberately): - Current-request only. There is intentionally no query that takes an arbitrary token as an argument — that would be a token oracle. The caller must possess the credential, exactly as today. - No reason-leak. A credential that does not resolve yields a null authContext (Query field), identically for revoked / never-existed / malformed. Once a credential HAS resolved (you authenticated as it), surfacing active-vs-revoked via apiKey.revokedAt is fine.

Field Argument Type Description
principalType PrincipalType! Which kind of credential authenticated this request.
user User The authenticated User — populated for USER principals (JWT or hdr_user_ key), null for App keys.
appId ID App.id — populated for APP principals (hdr_app_ key).
agentId ID Reserved for a future agent-credential principal; always null today.
apiKey UserApiKey The specific UserApiKey presented — populated only when the request authenticated via an hdr_user_ key (its id, label, keyPreview, createdAt, lastUsedAt, revokedAt). Null for JWT sessions and App keys.
impersonation ImpersonationInfo Set when the credential is an impersonation token: this request runs READ-ONLY as the target user, scoped to one org. Null for every other credential. Portal and CLI read this to render the acting-as state.

BeginAssetUploadResult

Field Argument Type Description
uploadId ID!
putUrl String!
putHeaders [AssetPutHeader!]!
expiresAt DateTime!
storageKey String!
maxSizeBytes Int!
allowedMimeType String!

CalendarAttendee

Field Argument Type Description
name String
address String!
optional Boolean!
response CalendarEventResponse!

CalendarEvent

Field Argument Type Description
id ID!
calendarId String!
title String
description String
location String
start CalendarEventTime!
end CalendarEventTime!
isAllDay Boolean!
status String! confirmed, tentative, or cancelled (provider-neutral vocabulary).
organizer CalendarPerson
isOrganizer Boolean! True when the connected account organized the event.
attendees [CalendarAttendee!]!
myResponse CalendarEventResponse The connected account's own response; null when it is not an attendee.
webLink String
seriesId ID Series id when this is an instance of a recurring event.
createdAt DateTime
updatedAt DateTime

CalendarEventTime

An event boundary: exactly one of dateTime (timed, with timeZone) or date (YYYY-MM-DD, all-day) is set.

Field Argument Type Description
dateTime DateTime Instant for a timed event. Exactly one of dateTime / date is set.
date String Calendar date for an all-day event, as YYYY-MM-DD. Deliberately String and NOT DateTime (#205): an all-day event has no instant, and widening it to a timestamp would force a timezone the provider never supplied.
timeZone String

CalendarInfo

One calendar the connected account can see.

Field Argument Type Description
id ID!
name String!
description String
timeZone String
isPrimary Boolean! True for the account's primary calendar (the default calendarId).
canEdit Boolean! True when the connected account may create/modify events here.

CalendarPerson

Field Argument Type Description
name String
address String

CastWorkerPreview

What castWorker WOULD do (#964) — an unsaved projection, deliberately not a Worker (no fake id to mistake for a real row). Scalars only: the ids/names here are attribution-level facts the mint-gate audience already sees, so no nested entity hands out its field resolvers.

Field Argument Type Description
name String! The name the cast would take — the caller's, checked free against the App's FULL roster (retired names stay taken). Required since #1050; a nameless preview refuses exactly where the cast would.
role String The casting's role: explicit, else the agent's personaRole dressing.
agentId ID! The role-agent the casting would bind.
agentName String!
prompt String The composed boot prompt, post {{name}}/{{role}} substitution with promptOverride appended — the same composition Worker.prompt performs, reviewable BEFORE the name is permanent. Null when the agent carries no template and no override was given.
hasNamePlaceholder Boolean Whether the agent's systemPrompt binds {{name}} — a nameless template silently produces workers whose prompt never names them. Null when the agent has no template.

Channel

A CHANNEL (spec 049, D-2026-09-13-006): a durable, ordered message stream hosted in ONE memory at ONE reserved address, server-ordered and server-attributed — a platform entity, created one-to-one with its chat root. Creating it RESERVES and PROTECTS the address: generic node writes under it are refused (LOC_PROTECTED); its own operations (createTeamChatMessage, hadron_team_chat_post) are the only writers. The audience is the host memory's (cor:acl:030:01) — a Channel has no access layer of its own. lastSeq / lastMessageAt are the platform-maintained watermark.

Field Argument Type Description
id ID!
memoryId ID! Host memory.
memory Memory!
loc String! The reserved address of the chat root (e.g. chats:team).
name String!
kind ChannelKind!
description String
chatRootNodeId ID! The chat root node this Channel IS the entity for (one-to-one).
chatRootUrn String #1171 — the Channel's ADDRESS: its chat root's node URN (hrn:node:<org>:<memory>:<loc>). A channelRef takes TWO FORMS — an id, or a chat-root node ref — and the node ref is accepted in every spelling the node resolver takes (`urn:node:`, bare fully-qualified, canonical `::`); this field emits the one to copy. COPY it rather than composing one: a Channel a ref fails to name is indistinguishable from one you may not read. NULL when the server has no flat-v2 address it can safely advertise — commonly a pre-v2 compound host memory URN, which flattens into a node URN that cannot be split back. It does NOT mean the Channel is unaddressable in principle (a canonical `::` spelling can still resolve one); it means the server will not hand you a string it would itself reject. Use `id` there; it always works.
lastSeq Int! Highest seq the allocator has issued — a watermark, not a count.
lastMessageAt DateTime
createdAt DateTime!
updatedAt DateTime

ChannelReadState

Spec 049 Phase 7 — where an attendee is up to on a Channel (D-2026-09-13-009). Outside the register; keyed on the ATTENDEE (a Worker, or an Agent in an App); advances are monotonic. Mute is session state, never here.

Field Argument Type Description
attendeeUrn String
channelId ID!
lastSeenSeq Int!
updatedAt DateTime!

ChannelsPage

Field Argument Type Description
items [Channel!]!
total Int!

Chat

A saved chat in one of the chat-scope tiers.

Per spec 017, scope is set at chat creation and immutable thereafter. private chats live in the author's personal-class memory; shared chats live in the App's app-class memory and are readable by every AppMember of that App.

Field Argument Type Description
id ID!
loc String!
title String!
conversationName String
stageName String
messageCount Int!
createdAt DateTime!
lastMessageAt DateTime
scope ChatScope! 017-chat-visibility-and-personal-memory.
createdBy ID The chat's author (chat-root node's createdBy). Null for legacy chats predating createdBy persistence. Surfaces who started a shared chat so the portal can distinguish own vs other-member shared chats in the list.

ChatMessage

One chat message in history, as returned by chatHistory.

Field Argument Type Description
role String!
content String!
seq Int
createdAt DateTime!

ChatPromptResult

Field Argument Type Description
systemMessage String!
tools JSON
messageHistory JSON!
summarizationNeeded SummarizationRequest #1115 — ALWAYS NULL since 2026-09-04. It used to fire every 20 messages and ask the caller to produce a summary, but the summary it asked for is replayed to the model as an ordinary user turn beside the messages it summarises, so the loop made the transcript longer. Disarmed rather than removed: the field is nullable, was absent 19 turns in 20, and every caller already handles that. Re-arming needs the design in #1115.

ChatResponseResult

Field Argument Type Description
displayMessage String!
stageTransitioned Boolean!
newStageName String
conversationHandoff String
chatEnded Boolean!
onTrack Boolean!
offTrackReason String

ChatStartResult

Field Argument Type Description
chatId String!
systemMessage String!
tools JSON
conversationName String
stageName String

ConnectionGrant

A user-owned connection's scoped delegation to an App install (spec-042 Track B, #593). The connection OWNER grants a specific App scoped access to their mailbox/calendar; the enforcement gate is requireEmailConnection. The grantee App is exposed as SCALARS only (never a nested App object), so a cross-org grant can't reach the App keys/members. Token material is never exposed.

Field Argument Type Description
id ID!
connectionId ID!
granteeAppId ID! The App install this grant delegates to.
granteeAppUrn String Canonical URN of the grantee App (hrn:app:<root>:<slug>), or null if unresolvable.
granteeAppName String
scopes [String!]! Covered scopes: mail.read, mail.send, calendar.freebusy, calendar.read, drive.read.
expiresAt DateTime ISO-8601 expiry, or null for perpetual (until revoked).
createdAt DateTime!

ConnectionGrantsPage

Field Argument Type Description
items [ConnectionGrant!]!
total Int!

CreateMemorySharePayload

Field Argument Type Description
memoryShare MemoryShare!

CreditBalance

Field Argument Type Description
organizationId ID!
balanceMicroCents String!

CreditLedgerEntry

046-usage-metering: append-only org credit ledger row. Signed micro-cents as a base-10 integer string (grants +, debits -).

Field Argument Type Description
id ID!
organizationId ID!
kind String!
amountMicroCents String!
expiresAt DateTime
sourceType String
sourceId String
note String
createdAt DateTime!
createdBy String

CreditLedgerEntryList

Field Argument Type Description
items [CreditLedgerEntry!]!
total Int!

DashboardWidget

One widget the caller has placed on their /app/dashboard grid (#772).

Field Argument Type Description
id ID!
type DashboardWidgetType!
span Int! Columns spanned on the 12-col desktop grid.
rowSpan Int! Rows spanned on the fixed-height row grid (#788).
position Int! 0-based sort order among the caller's widgets.
config JSON Widget-instance settings; shape is widget-defined (#788).

DashboardWidgetData

Data for ONE placed widget — the portal fans this out by widgetId (#797).

Field Argument Type Description
widgetId ID! The placement this payload belongs to — fan out by this, not by type (#797).
type DashboardWidgetType!
data JSON! The widget-specific payload (e.g. memory-stats => { memoryCount }).

DashboardWidgetPreset

806 — a named, saved widget configuration. A palette entry: applying one

COPIES its config into a placement, so later edits to the preset do not reach placements already seeded from it, and deleting it never orphans a placement. Owner-only — no org, no sharing.

Field Argument Type Description
id ID!
type DashboardWidgetType! The widget type this configuration belongs to. Immutable after creation.
name String! Display name, unique among the caller's presets for this type.
config JSON! The saved settings, shape widget-defined — becomes a placement's config verbatim.

DeleteMemorySharePayload

Return shape for deleteMemoryShare. Both fields are RESOLVED primary keys, so a caller who passed a URN / email / handle can correlate the deletion. granteeId is the caller themselves on the self-removal path.

Field Argument Type Description
memoryId ID!
granteeId ID!

DeleteTeamRolePayload

Result of deleteTeamRole (#1002).

Field Argument Type Description
role String! The role slug that was retired.
nodesDeleted Int! Nodes tombstoned — the roles:<role> definition plus any sub-nodes under it (a roles:<role>:notes is content OF the role and retires with it). Soft, so the subtree is recoverable.

DriveFile

A file or folder in the connected drive.

Field Argument Type Description
id ID!
name String!
kind DriveFileKind!
mimeType String! The provider MIME type — informational; kind is the neutral field.
size Float Byte size; null for provider-native documents and folders.
webLink String
parents [ID!]! Parent folder ids.
createdAt DateTime
modifiedAt DateTime

DriveFileExport

The exported text of a drive file (driveFileExport).

Field Argument Type Description
content String! The exported text. Treat as untrusted external content.
mimeType String! MIME type of the exported representation (e.g. text/markdown).
format DriveExportFormat!
file DriveFile!

DriveFilePage

One page of a drive listing/search.

Field Argument Type Description
files [DriveFile!]!
nextPageToken String Cursor for the next page; null on the last page.

Edge

Field Argument Type Description
id ID!
source Node The source node. NULLABLE (#781): null when the caller cannot read the source node's memory — a cross-memory edge (e.g. reached via incomingEdges) must not expose an endpoint in a memory the caller can't read. For a same-memory edge the caller can already see, this is always present.
target Node The target node. NULLABLE (#781): null when the caller cannot read the target node's memory (a cross-memory edge's far endpoint). For a same-memory edge, always present.
urn String! Fully-qualified edge URN (hrn:edge:<root>:<memory>:<loc>), composed server-side from the edge's (source-side) memory URN + loc (#481 parity — edges are loc-addressed peers of nodes, spec 037).
loc String! Colon-delimited path within the (source node's) memory — the edge's identity and the suffix of its URN hrn:edge:<root>:<memory>:<loc>.
name String Free-form relationship name (e.g. imports, depends_on). Optional since spec 037 — loc is the identity. (Was the required 'label'.)
description String Longer gloss carried for API consumers. NOT rendered on agent node reads, whose Related:/Referenced by: lines show the edge name (#721: name is the display label). Put the label you want seen in name (#1057).
isRunnable Boolean If true, this edge can be executed/run (task automation, conversation transitions). Mirrors Node.isRunnable.
condition JSON JSONLogic gating expression. Null = always fires. Validated against the v1 operator subset and the FOUR variable scopes — memory.*, chat.*, agent.*, message.data.* (VALID_SCOPE_PREFIXES; anything else is UNKNOWN_SCOPE). The time builtins now() and today() are a SEPARATE constant, not a fifth scope: they are whole references rather than prefixes, and the evaluator pre-resolves them from the request clock. Until #1246 this said "five variable scopes" and listed the builtins as one of them — a count and a category error in one sentence. See hadron-docs/docs/reference/edge-conditions.md.
priority Int! Resolution-order hook (lower = earlier; default 0). When two edges share a priority, fall back to insertion order.
data JSON

EdgesPage

Field Argument Type Description
items [Edge!]!
total Int!

EditorLockResult

Field Argument Type Description
locked Boolean!
lockedBy String
expiresAt DateTime

EffectiveAccess

issue #323: the effective access a single user has to a single resource, with the grants that confer it. An empty grants list (with all capabilities false and role null) is a first-class 'no access' answer, NOT an error.

Field Argument Type Description
user User!
resourceUrn String! Canonical (grammar-v2, hrn:-prefixed) URN of the resolved resource, emitted the same way the entity's own urn field emits it — so it can be fed straight back into another query or command. Two exceptions have no URN by nature: an AiServiceConfig yields its id, and a user yields hrn:user:<handle>.
resourceKind String! memory | node | app | agent | aiServiceConfig | organization | user.
canRead Boolean!
canWrite Boolean!
canManage Boolean! Manage grants / settings (admin/owner-equivalent).
canDelete Boolean!
role String Highest single clean role label (e.g. owner, writer, ADMIN); null when access is purely capability-based or absent.
grants [AccessGrant!]! The reasons access is granted. Empty means no access. Ordered strongest-first.

EffectiveActionPolicy

The standing-chain policy debug surface (#510): per-layer verdicts for one action (cor:acl:040:02). Trigger/run links are per-trigger and not part of the standing chain.

Field Argument Type Description
allowed Boolean!
layers [PolicyLayerVerdict!]!

EmailDraftInfo

Result of emailSaveDraft: the provider-side draft (it lives in the mailbox's Drafts folder, not in any platform table — spec 002 US5).

Field Argument Type Description
draftId ID!
conversationId ID

ExchangeConnection

A connected PROVIDER ACCOUNT (identity record) — despite the name, this one type carries mailboxes, calendars and Drive accounts alike; the provider field below says which, and each routes to its own capability tool. Provider state lives in that tool, never here.

Operations are identity-scoped: the connection's OWNER may perform them, and an App/headless caller may perform those covered by a live ConnectionGrant whose scopes include the operation's (requireEmailConnection enforces both branches, fail-closed for an unmapped operation). list and delete stay org-ADMIN management operations.

Field Argument Type Description
id ID!
organizationId String!
userId String!
mailboxEmail String!
displayName String
provider String! Provider backing this connection. Dispatched per-connection through the tool registry: `ms-exchange`, `gmail`, `google-cal`, `google-drive`. (This said "'ms-exchange' today" until #1232 — true when written, and three providers out of date by the time it was published.)
syncEnabled Boolean!
syncStatus SyncStatus!
lastSyncAt DateTime
lastError String
createdAt DateTime!
updatedAt DateTime!

ExchangeFolder

Field Argument Type Description
id ID!
displayName String!
totalItemCount Int
unreadItemCount Int

ExchangeMessage

Field Argument Type Description
id ID!
subject String
from String
receivedDateTime DateTime
isRead Boolean
bodyPreview String
body String
hasAttachments Boolean

FindNodesResult

Envelope for findNodes (cor:api:090) — the unified node list + search field (it replaced the removed nodes + nodeSearch queries). hits carry per-hit score + vector metadata; passages carry chunk-granularity results; reason/degraded surface no-index / reduced-fidelity outcomes.

Field Argument Type Description
hits [NodeHit!]!
passages [Passage!]!
total Int
reason String
degraded String Reduced-fidelity flag(s); hits are usable. May carry MULTIPLE comma-separated codes (e.g. 'no_vector_index,literal_fallback') — parse the set, never compare the whole string. Codes: no_vector_index, embedding_unavailable, literal_fallback, and_relaxed_to_or (a bare multi-term keyword query matched nothing under AND, so it was retried as OR and these hits match ANY term), scope_narrowed (spec 049: the scope this search ran under lists memories you cannot read; they were dropped and `scope.droppedCount` says how many).
scope SearchScopeInfo Spec 049 (D-2026-09-13-002): what this search was produced under. Null when no scope applied (the pre-049 accessible set, optionally org-narrowed).

GlobalSearchHit

One unified, flat globalSearch result — common fields across every entity type so a single ranked list renders uniformly. urn is null for an aiServiceConfig (no URN) and for a user with no handle or a not-URN-legal handle (a user's URN is hrn:user:<handle>); memoryId / organizationId are convenience handles for building canonical navigation paths.

Field Argument Type Description
entityType SearchEntityType!
id ID!
urn String
name String
description String
matchedField SearchField!
score Float!
memoryId ID
organizationId ID

GlobalSearchResult

Field Argument Type Description
scope SearchScopeInfo Spec 049: the scope the Memory + Node slices ran under (see FindNodesResult.scope). Null when no scope applied.
hits [GlobalSearchHit!]!
total Int! Total ranked matches for building a pager. BOUNDED CONTRACT: this is the count within the per-entity candidate cap (each entity type contributes at most a fixed number of candidates before scoring), not an unbounded COUNT. On very large result sets it under-counts; page slicing (offset/limit) operates over this bounded, fully-ranked list.
degraded [String!]! Notes when the search ran at reduced fidelity (e.g. content search skipped on encrypted memories).

GraphEdge

Slim edge projection for the graph view (issue #466) — endpoint IDs only.

Field Argument Type Description
id ID!
loc String!
name String
sourceId ID!
targetId ID!

GraphEdgesPage

Paged envelope for memoryEdges — total is the memory's full edge count.

Field Argument Type Description
edges [GraphEdge!]!
total Int!

GraphNode

Slim node projection for the graph view (issue #466) — only what a node-link graph draws.

Field Argument Type Description
id ID!
loc String!
name String! Non-null — mirrors Node.name (the DB column is NOT NULL).
nodeType String! Non-null — mirrors Node.nodeType (NOT NULL, defaults to 'info').
tags [String!]!
seq Int Sibling sort order in the tree view (nullable; #513). Small scalar — kept in the slim projection.
isRunnable Boolean Whether the node can be run as a task — drives the tree action indicator (nullable; #513).
description String Short one-line description for graph tooltips / detail (nullable; #513). NOT the heavy body — content/abstract/data stay out of the slim projection.

GraphNodesPage

Paged envelope for memoryNodes — the standard limit/offset/total idiom; total is the memory's full node count.

Field Argument Type Description
nodes [GraphNode!]!
total Int!

HomeAssistantInstance

One registered Home Assistant instance (hadrontool-home-assistant registry, hadron-server#640). Org-owned. The long-lived access token is encrypted at rest and WRITE-ONLY: no field here ever returns it. The row grants nothing by itself; every run-time call walks the policy chain as 'tool.ha__<slug>__<op>' plus the run's action budget.

Field Argument Type Description
id ID!
organizationId String!
slug String! Run-tool name segment (ha__<slug>__<op>); lowercase [a-z0-9-], unique per org, immutable.
name String!
url String! Home Assistant base URL (e.g. https://ha.example.com or a Nabu Casa remote URL).
opAllowlist [String!]! Exposed-op allowlist over the closed op catalog; empty = every op. Lets an admin register a read-only instance.
enabled Boolean! Disabled instances fail run-tool resolution loud, same as an unregistered slug.
createdAt DateTime!
createdBy String
updatedAt DateTime!
updatedBy String

HomeAssistantInstancesPage

Uniform paginated list shape (cor:api:120).

Field Argument Type Description
items [HomeAssistantInstance!]!
total Int!

HomeAssistantOp

One op of the CLOSED Home Assistant op catalog, as admitted by a registry row's allowlist — what this query returns IS what a run can declare in data.tools. The catalog is static (no upstream round-trip).

Field Argument Type Description
op String! The op segment, e.g. call_service.
description String!
runToolName String! The run-tool / policy name: ha__<slug>__<op>.
inputSchema JSON The op's argument JSON Schema.

ImpersonationInfo

Admin impersonation (read-only support sessions): the acting-as facts of THIS request's credential, surfaced on AuthContext so whoami-style consumers can name the real actor, the scope org, and the expiry.

Field Argument Type Description
sessionId ID!
actorUser User The admin behind the curtain (visibility per the normal User field rules — the actor is a co-member of the scope org).
organizationId ID!
expiresAt DateTime!
readOnly Boolean! Always true in v1 — impersonated sessions cannot mutate.

ImpersonationSession

One admin-impersonation session: the durable audit record (who impersonated whom, in which org, when) AND the revocation source of truth (stopImpersonation writes endedAt; rows are never deleted).

Field Argument Type Description
id ID!
adminUser User!
targetUser User!
organization Organization!
reason String
createdAt DateTime!
expiresAt DateTime!
endedAt DateTime Explicit-stop timestamp; null while live or when the session merely lapsed by TTL.
endedBy ID User.id of whoever stopped it early (the admin actor for a self-stop from the impersonated context). Null while live / TTL-lapse.
isActive Boolean! Derived: endedAt is null AND expiresAt is in the future.

ImpersonationSessionList

Field Argument Type Description
items [ImpersonationSession!]!
total Int!

ImportNodeResult

Field Argument Type Description
status ImportNodeStatus!
node Node
jobId ID Reserved for the async path — always null in sync v1.

InstallAgentIntoAppPayload

Return shape for the installAgentIntoApp mutation. Wraps the new AppAgent row so callers can read its audit fields without re-querying.

Field Argument Type Description
appAgent AppAgent!

InstallationPolicy

Field Argument Type Description
maxMembers String! Integer-as-string OR the sentinel 'unlimited'. GraphQL does not have an Int|String union; clients parse: parseInt(maxMembers) succeeds for integer values; 'unlimited' is the sentinel.
memberRoles [String!]!

JoinAppPayload

Return shape for the joinApp mutation (023-app-shape US2). The AppMember row is included so callers can read its derived isOrgExternal flag without a re-query.

Field Argument Type Description
appMember AppMember!

LeaveAppPayload

Return shape for the leaveApp mutation (023-app-shape US2). The user's personal-class Memory at (appId, userId) is NOT cascade-deleted (spec 008 FR-015 orphan retention); it re-attaches if the user later re-joins the same App.

Field Argument Type Description
appId ID!
userId ID!

McpServer

One registered EXTERNAL MCP server (hadrontool-mcp conduit registry). Org-owned. Static auth headers are encrypted at rest and WRITE-ONLY: no field here ever returns them — hasHeaders is all a reader gets. The row grants nothing by itself; every run-time call walks the policy chain as 'tool.mcp__<slug>__<tool>' plus the run's action budget.

Field Argument Type Description
id ID!
organizationId String!
slug String! Run-tool name segment (mcp__<slug>__<tool>); lowercase [a-z0-9-], unique per org, immutable.
name String!
url String! Streamable-HTTP MCP endpoint URL.
toolAllowlist [String!]! Exposed-tool allowlist; empty = every tool the server advertises.
hasHeaders Boolean! Whether encrypted static headers are stored (the values are never returned).
enabled Boolean! Disabled servers fail run-tool resolution loud, same as an unregistered slug.
createdAt DateTime!
createdBy String
updatedAt DateTime!
updatedBy String

McpServerTool

One tool an external MCP server advertises, as admitted by the registry row (allowlist + name grammar + length cap applied) — what this query returns IS what a run can declare in data.tools.

Field Argument Type Description
name String! The tool's name on the external server.
description String
runToolName String! The run-tool / policy name: mcp__<slug>__<name>.
inputSchema JSON The tool's argument JSON Schema, passed through verbatim.

McpServersPage

Uniform paginated list shape (cor:api:120).

Field Argument Type Description
items [McpServer!]!
total Int!

MemoriesPage

Field Argument Type Description
items [Memory!]!
total Int!

Memory

A Hadron memory (git repo + branch settings).

Field Argument Type Description
id ID!
organizationId String
organization Organization The owning Organization when the caller may read that Organization; a Memory read alone does not grant it.
urn String! The memory's URN, grammar v2 (cor:urn:010:01): hrn:mem:<root>:<slug...>, emitted by safeCanonicalUrn/emitEntityUrnV2 from the stored urn — so a v1 double-colon chain, a legacy single-colon row and an already-v2 row all read back identically here. <slug...> is one atom for a migrated memory but still several for a compound pre-Stage-3 per-user one (<root>:<agent>:app-user:<id>). The STORED column is the bare form (<root>:<slug>, no scheme prefix — the chk_memory_urn_not_prefixed guardrail rejects writing a rendered one); this field is the rendered view of it. When emission throws, the stored value is served raw and logged, so a bare, unprefixed value is a possible read.
name String!
shortDescription String
description String
data JSON Free-form structured metadata bag (client-defined JSON, no server-side semantics). Null when unset. Settable via createMemory / updateMemory.
schema JSON #725 — opt-in per-memory property schema (declared collections + typed fields). NULL = unstructured. Authored via updateMemory; well-formedness validated server-side.
tags [String!]!
license String
category0 String
category1 String
category2 String
iconUrl String
heroUrl String
homeUrl String
source String
readBranch String
writeBranch String
visibility MemoryVisibility 035-visibility-enum-cleanup: nullable — set only for knowledge/group.
listedOnMarketplace Boolean! cor:acl:080:04 — opt-in marketplace catalogue flag (advertisement, not access).
class MemoryClass! Which memory typology this row belongs to. See MemoryClass for the members — deliberately not restated here, because the count is what went stale: this said "four-way" from 005-agent-subscription until #1232, long after `group` (023) and `private` made it six.
shareable Boolean! #766 — derived shareability signal: true iff this memory can be shared with an individual user via MemoryShare, i.e. class = 'personal' (spec 023 FR-018). Lets the CLI and portal surface or gate the share action before a createMemoryShare attempt rather than discovering InvalidMemoryClassForShareError at execution time.
acceptsUploads Boolean! 006-asset-upload-redesign: per-memory upload-acceptance gate. Default true for app/knowledge/personal-class memories; false for system-class. Org admins or memory owners can flip via the setMemoryAcceptsUploads mutation (added in v2 story phases).
userId ID The strict user owner, for BOTH `personal` and `private` classes — the DB invariant is `class IN ('personal','private') ⇔ user_id IS NOT NULL`, so a private memory has one too. Resolves to null for every other class, and also when the caller is not the memory owner / not ADMIN/OWNER of the memory's org.
isEncrypted Boolean!
vectorIndexEnabled Boolean!
embeddingSource EmbeddingSource!
chunkTokens Int Spec 033 US2 — chunk-window size in tokens (~4 chars/token). Null = platform default (512). Applies when embeddingSource includes content chunks.
chunkOverlap Int Spec 033 US2 — overlap between consecutive chunks in tokens. Null = platform default (64). Must be < chunkTokens.
forceFixedSize Boolean! Spec 033 US2 — force the fixed-size chunking strategy, bypassing the structure-aware default. Useful when the content's heading structure is unreliable (e.g. transcripts, machine-generated reports).
maxRevCount Int! #621 — cap on how many NodeRevision rows are kept per node in this memory. On each new revision the oldest overflow is pruned. Default 10; minimum 1.
corpusState CorpusState! #1447/#1448 — whether this memory is a DRAFT spec corpus or MINTED. In a draft corpus spec citations are not yet permanent: specs can be moved, renumbered and deleted, and a move records no URN alias. Chosen only at creation (createMemory draftCorpus); minting is one-way. Every memory created without asking for draft is MINTED.
corpusMintedAt DateTime #1448 — when this corpus was minted (draft → minted). Null if it was never a draft.
vectorIndexEncryptedAckAt DateTime Spec 033 FR-026 — timestamp at which the memory owner accepted the encrypted-memory vector-inversion disclosure. Set when the caller passes acknowledgeVectorInversionRisk: true on updateMemory for an isEncrypted: true memory enabling vectorIndexEnabled for the first time. The portal surfaces this readback so the user can confirm when they accepted the tradeoff (the disclosure text is the FR_026_DISCLOSURE constant in src/lib/entityRef/errors.ts). Survives a revoke + re-enable cycle (never cleared). Null for unencrypted memories and for encrypted memories where the index was never enabled.
userMemoryOfAgentId String
anonymousExpiresAt DateTime
appId ID The App this memory is scoped to. REQUIRED for `class='app'`, FORBIDDEN for `class='system'` (an agent's own definition is never App-scoped), and OPTIONAL for `knowledge` / `group` / `personal` / `private` — set when provisioned inside an App, NULL when the memory is free-standing. Enforced by `chk_memory_class_app_id`. (034 relaxed the personal case and #653 moved knowledge + group from forbidden to optional; this said "required for app/personal, NULL otherwise" until #1232.)
app App The linked App when the caller may read that App; a Memory read alone does not grant it.
agent Agent Populated only for class = system — the agent this memory is the system memory of. Null for regular knowledge memories.
syncStatus SyncStatus!
syncError String
pendingEdgeCount Int!
lastSyncedAt DateTime
createdAt DateTime!
updatedAt DateTime!
subscriptions [MemorySubscription!]!
shares [MemoryShare!]! 023-app-shape US3: cross-user grants on this memory. Non-empty only when class = personal (FR-018). Includes grantee + role for each row. Visible only to the principal (memory.userId) and to ADMIN/OWNER of the memory's owning org.
myShare MemoryShare The caller's OWN share of this memory (they are the grantee), or null. Grantee-readable — unlike shares (principal / org-admin only), this returns at most the single MemoryShare row keyed (memoryId, callerId), so it never leaks co-grantees. null for a non-grantee or an App-key caller. Lets the portal render Shared-by-@grantor + the role on each shared-with-me row.
members [MemoryMember!]! 023-app-shape US4: team membership rows on this memory. Non-empty only when class = group (FR-027). Visible to any current member (any role) and to ADMIN/OWNER of the memory's owning org.
config MemoryConfig #1325 — this memory's config: its node-role rules. Null for anyone who does not MANAGE the memory (the strict owner of a personal/private memory; else its user owner or an org ADMIN/OWNER), however the Memory was reached. A memory with no rules yet reads as the empty config (id null, no rules).

MemoryConfig

1325 — a memory's config: exactly one per memory, holding its node-role

rules (one per role or declared sub-role). Read and changed only by the memory's managers. Materialized by the first rule write; before that it reads as empty, with a null id.

Field Argument Type Description
id ID Null until the first rule is written.
memoryId ID!
rules [NodeRoleRule!]! Role-ascending.
createdAt DateTime
createdBy ID
updatedAt DateTime
updatedBy ID

MemoryConfigApplyEntry

Field Argument Type Description
role String!
outcome MemoryConfigApplyOutcome!
rule NodeRoleRule The rule in force for this role afterwards. On a dry run, the existing rule, or null for a would-be APPLIED.

MemoryConfigApplyResult

Field Argument Type Description
memoryId ID!
template NodeRoleRuleSourceTemplate!
templateRevision Int! The template revision that was applied; pass it back as expectedTemplateRevision to apply exactly what a dry run showed.
required Boolean!
dryRun Boolean!
entries [MemoryConfigApplyEntry!]! One per template rule, role-ascending.
warnings [MemoryConfigWarning!]! Empty until #1327 adds its visibility warnings.

MemoryConfigTemplate

1325 part (c) — a reusable rulebook with exactly one owner, COPIED into a

memory's config when applied (never live-linked). Read and managed only by its owner's managers: a platform admin (server), an org ADMIN/OWNER (organization), the user themselves (user), the App's owner or org ADMIN/OWNER (App). For anyone else it does not exist.

Field Argument Type Description
id ID!
name String! Unique per owner among live templates; 1-64 lower-case letters, digits, '-' or '_'.
description String
required Boolean! Applied as locked rules (#1334).
ownerType MemoryConfigTemplateOwnerType!
ownerId ID ID of the owning organization, user or App; null for the server.
rules [MemoryConfigTemplateRule!]! Role-ascending.
revision Int! Optimistic-concurrency token for the template AND its rules.
createdAt DateTime!
createdBy ID
updatedAt DateTime
updatedBy ID

MemoryConfigTemplatePayload

Field Argument Type Description
template MemoryConfigTemplate!
warnings [MemoryConfigWarning!]! Empty until #1327 adds its visibility warnings.

MemoryConfigTemplateRule

1325 part (c) — one rule of a template: a node-role rule without the config-only fields.

Field Argument Type Description
role String!
authorTask String
authorTaskId ID
authorTaskState NodeRoleRuleRefState!
validationTask String
validationTaskId ID
validationTaskState NodeRoleRuleRefState!
descriptionNode String Documentation, not procedure: a deleted description node reads as NONE, never BROKEN.
descriptionNodeId ID
descriptionNodeState NodeRoleRuleRefState!
strictSubRoles Boolean!
writers NodeRoleWriters!
validateBy ContentValidator
enabled Boolean!

MemoryConfigTemplatesPage

Field Argument Type Description
items [MemoryConfigTemplate!]!
total Int!

MemoryConfigWarning

1325 — a non-fatal finding about a saved rule. Never changes the mutation's success.

Field Argument Type Description
code String!
role String!
field String
taskUrn String
taskState NodeRoleRuleRefState

MemoryMember

023-app-shape US4: symmetric team-membership row for group-class memory. The "Company Brain" model — multiple users collaboratively read/write a shared memory, governance by role, no single owner on the Memory itself.

Field Argument Type Description
memory Memory!
user User!
role MemoryMemberRole!
createdAt DateTime!
createdBy String
updatedAt DateTime
updatedBy String

MemoryOwnerTransferPreview

Field Argument Type Description
memoryId ID!
oldUrn String!
newUrn String!
fromOwner ID!
toOwner ID!
currentClass MemoryClass!
targetClass MemoryClass!
dependents [MemoryTransferDependent!]!
blockers [MemoryTransferBlocker!]!
canApply Boolean!
applied Boolean!

MemoryProvisioning

Field Argument Type Description
appMemory AppMemoryKind!

MemoryShare

023-app-shape US3: asymmetric cross-user grant on a personal-class Memory. The principal (memory.userId) grants a grantee read/write access. Used for per-pairing isolation patterns (e.g., Alice's personal Memory paired-with-Mentor-A is distinct from her paired-with-Mentor-B Memory, each with its own MemoryShare).

Field Argument Type Description
memory Memory!
grantee User! The User who was granted access.
grantor User! The principal of the Memory (Memory.userId). Per spec 023 FR-019 this is always the principal, even when an App backend made the API call on the principal's behalf — that actor is recorded in createdBy instead.
role MemoryShareRole!
createdAt DateTime!
createdBy String The API actor who created the row. Distinct from grantor when an App backend creates the share on the principal's behalf.
updatedAt DateTime
updatedBy String

MemorySubscription

An organization's subscription to a memory it does not own.

Field Argument Type Description
id ID!
memory Memory!
organization Organization The subscriber Organization when the caller may read it; memory access alone does not authorize it.
role Role!
activated Boolean!
createdAt DateTime!

MemoryTransferBlocker

Field Argument Type Description
code String!
message String!

MemoryTransferDependent

Field Argument Type Description
kind String!
count Int!
handling MemoryTransferHandling!

MemoryValidationFinding

One finding (#819). nodeUrn is null when the URN could not be composed - a memory URN the flat v2 shape cannot express, or an unexpected/legacy loc - in which case nodeId and nodeLoc still identify the node.

Field Argument Type Description
kind MemoryValidationCheck!
nodeId ID!
nodeLoc String!
nodeUrn String
detail String!

MemoryValidationResult

Field Argument Type Description
memoryId ID!
nodesChecked Int! Live (non-deleted) nodes scanned - soft-deleted tombstones are excluded, since their findings would be unactionable (#884). The whole memory in one pass - the checks are cross-referential, so a partial scan would produce false positives rather than fewer findings.
findings [MemoryValidationFinding!]! Findings, capped by the limit argument.
totalFindings Int! The TRUE finding count, before any truncation - gate CI on this, not on findings.length.
truncated Boolean! True when findings was capped and totalFindings is larger.
skippedChecks [MemoryValidationSkippedCheck!]!
ok Boolean! True only when EVERY check ran and none found anything. A skipped check makes this false even with zero findings - you cannot claim health for a check you did not run.

MemoryValidationSkippedCheck

A check that could NOT run (#819). Reported explicitly rather than silently omitted: a clean bill of health that quietly skipped a check is worse than no report at all.

Field Argument Type Description
check MemoryValidationCheck!
reason String!

Node

Field Argument Type Description
id ID!
moveLinkWarnings [NodeMoveLinkWarning!] #1392 — warnings from a cross-memory move. Null on other Node reads; [] when a move changes no local content links. Raw content is preserved.
revision Int! #1323 — current live revision. Creation is revision 1; each committed authoring change advances it. NodeRevision.revNo N is the retained snapshot of this node when revision N was current, before the edit that advanced it.
authorship NodeAuthorship #1326 — attribution of this content state. Null means legacy or unknown. Task identity is redacted unless its memory is independently readable.
contentValidation NodeContentValidationStatus! #1326 — advisory validation for this exact revision. A report for an older revision is STALE.
memoryId String!
nodeType String! The PLATFORM-kind axis (info / task / prompt / record / …). Load-bearing for retrieval and rendering. NOT 'objectType', which says which domain COLLECTION a node belongs to, and NOT 'role', which says what the node is FOR. Three kind-ish fields is the accepted price of keeping retrieval stable while roles stay open, so each one says what it is not — mixing them up is the predictable failure.
objectType String #725 — the COLLECTION discriminator: which domain object this node is an instance of, e.g. "competitor" / "insight". Validated against the memory's property schema when it has one. NULL for an ordinary node. NOT 'nodeType' (the platform-kind axis, which drives retrieval) and NOT 'role' (what the node is for; role can be filtered explicitly but does not change default ranking or rendering).
loc String!
urn String! Fully-qualified node URN (hrn:node:<root>:<memory>:<loc>), composed server-side from the node's memory URN + loc (#481). Carried by every Node-returning surface (findNodes, node, appNodes, nodeBatch, mutation returns).
portalUrl String #881 — the portal URL that opens this node, built server-side so no client ever has to construct one (constructing means guessing which URN spelling the /app/u/<urn> route resolves, and the legacy '::' forms in circulation make that guess wrong). Form: <portal-origin>/app/u/<urn>, the portal's stable URN-alias route. NULL when the deployment has no portal origin configured (FRONTEND_URL) — a link to the wrong host is worse than none, so clients render the field only when present.
isPlaceholder Boolean! #1450 — true for a RESERVED spec citation that has no spec written yet (a placeholder in a draft corpus, created by reserveSpecCitation). Set only by the server, never inferred from an empty body; writing real content through updateSpecNode clears it. Minting refuses while any remain.
isLink Boolean!
name String!
alias String
description String
abstract String Paragraph-length summary of this node. Opt-in on hadron_get_node via the contentScope parameter. hadron_find_nodes preview surfacing ships in spec 031 US2 — not yet live. Never surfaced in hadron_list_nodes. Cap is 2000 characters; longer values are rejected with NodeAbstractTooLongError. Empty + whitespace-only values normalize to null. Spec 031.
abstractOriginHash String Spec 032 — fingerprint of the content value at the time abstract was authored. SHA-256 of plaintext content, truncated to 8 hex chars. Compared at read time against computeContentHash(node.content) to detect staleness: the abstract may not reflect current content when the two differ, OR when this is NULL on a node that has both an abstract and content (#1128 — an abstract written before the body existed was never fingerprinted, so it has never been checked against it; that reads as unverified, not as verified). NULL is only a clean state when the node has no abstract, or no content for the abstract to describe. Note restoreNodeRevision restores this field verbatim, so restoring a snapshot taken while it was NULL reinstates the unverified state — correctly, since that abstract has never been checked against the restored content. System-managed; never settable via NodeInput.
embeddingPendingAt DateTime Spec 033 FR-006/FR-007 — set when this node needs (re-)embedding; cleared on success. The single work signal the embedding worker drains. A FUTURE-dated value is a backed-off retry not yet due (#882) — the worker only selects stamps at or before now. Operational state (never versioned on NodeRevision). The portal renders this as a subtle "embedding…" badge so a user who just edited isn't confused that their change "didn't take" in search yet. System-managed.
embeddingFailedAt DateTime Spec 033 FR-009 — set when an embed attempt failed (record, not a work signal). Retryable failures (timeouts, 5xx) reschedule with exponential backoff and never give up (#882); permanent failures (deterministic 4xx, dimension mismatch, encrypted-no-plaintext) clear embeddingPendingAt — recovery is the retryFailedEmbeddings mutation. The portal renders this as a red badge with the embeddingError message inline so users with an empty index can distinguish "nothing matched" from "every embed failed". System-managed.
embeddingError String Spec 033 — last embed error message (diagnosability; also surfaced by hadron_validate). Common values: encrypted-no-plaintext (#206), embedding-endpoint-unreachable, dimension-mismatch. Null when no failure has been recorded since the most recent success or revoke. System-managed.
embeddingAttempts Int! Spec 033 — attempt counter feeding the exponential-backoff schedule (#882 — no terminal give-up; a retryable failure never removes the node from the queue). Resets to 0 on success or revoke. Surfaced for ops diagnostics (a node at a high attempt count is retrying at the backoff ceiling and likely needs operator attention). System-managed.
content String
tokens Int
tags [String!]!
properties JSON
data JSON
seq Int
ownerRepo String
isRunnable Boolean Whether this node can be run as a task (nullable; #513) — it drives the tree action indicator, and since #1201 it is also the CAPABILITY gate for the task kind: a write that would PRODUCE a runnable node must come through createTaskNode / updateTaskNode. Gated here rather than on a label because omitting a label is free, while omitting this means the node does not run.
role String #1201 — what this node is FOR, as an OPEN string. Set it to anything; the platform reads a small CLOSED subset and ignores every other value. NOT 'nodeType' (the platform-kind axis, load-bearing for retrieval) and NOT 'objectType' (the collection discriminator, schema-validated). This field does not change default retrieval or ranking; NodeFilter.role explicitly selects its exact dotted family (#1322). A GOVERNED family routes the write to that kind's own authoring door, and the generic node surface is refused: 'review' / 'review.*' and 'spec' / 'spec.*' today, alongside the task kind, which is gated on 'isRunnable' rather than on any label because a label can be omitted and a capability cannot. Other values are inert unless a caller explicitly filters for them.
llmModel String
aiAgent String
createdAt DateTime!
updatedAt DateTime!
memory Memory
outgoingEdges [Edge!]!
incomingEdges [Edge!]!
asset Asset The asset this node references, for a reference node created by createAssetReferenceNode or hadron_store_file (its data.asset points at one). Null for every ordinary node. Also null when the pointer is DANGLING — the asset was deleted, or is soft-deleted. There is no schema-level Asset-to-Node foreign key (cor:dmo:060:10 reserves it), so data.asset.id is a soft reference and this resolver is what keeps it honest: never assume a node carrying data.asset still has a live asset behind it.

NodeAuthorship

Field Argument Type Description
kind NodeAuthorshipKind!
taskRef String
taskRevision Int
human NodeRevisionEditor
authoredAt DateTime!

NodeBatchResult

Spec cor:api:040 — result envelope for the batch node read (nodeBatch). 'nodes' is the authorized, existing subset (input order for a ref set, loc order for a prefix). 'unavailable' and 'omitted' are both lists of REFS, not node objects. 'unavailable' lists the requested refs that were denied OR not found — indistinguishable, so the result never discloses whether an unreadable node exists. 'truncated' is true when the response-size cap was reached, and 'omitted' then carries the refs of the nodes dropped to stay under it. (Over the node-count cap the query errors instead — never a silent short read.) Both lists echo the caller's OWN ref strings for the 'refs' form — pass a URN, get that URN back, not a primary key you never sent — and node ids for the prefix form, which has no caller refs.

Field Argument Type Description
nodes [Node!]!
unavailable [ID!]!
truncated Boolean!
omitted [ID!]!

NodeContentValidationReport

Field Argument Type Description
nodeRevision Int!
validationTaskRef String
validationTaskRevision Int
outcome NodeContentValidationOutcome!
reportedAt DateTime!

NodeContentValidationStatus

Field Argument Type Description
state NodeContentValidationState!
latestReport NodeContentValidationReport

NodeExportResult

Field Argument Type Description
format NodeExportFormat!
mimeType String! MIME type, e.g. text/markdown or application/json.
filename String! Suggested download filename, e.g. <loc-leaf>.md.
encoding NodeExportEncoding!
data String! The rendered file contents (TEXT).
bytes Int! Byte length of the rendered payload (UTF-8).

NodeHit

A scored node hit (cor:api:090:04). score is null for an unscored filtered list (no query).

Field Argument Type Description
node Node!
score Float
vector NodeVectorMeta

NodeMoveLinkWarning

Field Argument Type Description
nodeId ID!
nodeLoc String!
raw String!
target String!
status NodeMoveLinkStatus! RETARGETED names a different node; AMBIGUOUS has multiple live alias claimants.

NodeRevision

Field Argument Type Description
id ID!
authorship NodeAuthorship #1326 — attribution of the content captured before the next edit.
contentValidation NodeContentValidationStatus! #1326 — advisory validation at this historical revision.
urn String Computed end-anchored URN hrn:noderev:<root>:<mem>:<loc...>:<rev> (#696). Null for legacy rows without a revNo, and for multi-segment per-user memories the flat shape cannot express.
revNo Int Per-node monotonic revision number (#696). Null on legacy rows predating the counter (backfilled by the migration).
nodeId String!
loc String!
name String!
alias String
description String
abstract String Paragraph summary at capture (spec 031).
content String
tags [String!]!
nodeType String #620: full curated-subset snapshot fields. Null on LEGACY rows captured before the columns existed (nodeType doubles as the legacy sentinel — it is non-null on every post-#620 snapshot).
isLink Boolean
properties JSON
data JSON
seq Int
isRunnable Boolean
role String #1201 — Node.role at capture time. In the curated subset because CLEARING a governed role is the escalation #1202 exists for: a history that cannot show the role cannot show the un-governing.
llmModel String
aiAgent String
editedBy String The editing User id, when a user is known (#620). Use editedByUser for resolution.
editedByInfo String What is known about the editor when no User id is available (#620): 'github:<login>' / 'email:<addr>' / 'user:<id>' identity strings, or 'app:<App.id>' for App-key principals. (Legacy free-form #88 reasons / commit messages that squatted in editedBy were migrated to revLabel.)
editedByUser NodeRevisionEditor The editor resolved to a user (handle + URN) — from editedBy as a User id, the createdBy fallback (#619), or an identity-string form of editedByInfo ('github:<login>' resolves via the user's linked GitHub username). Null when nothing resolves — the portal falls back to the raw strings. Public identifiers only (#617).
revLabel String User-settable label for this revision (#620, 500-char cap) — set via updateNodeRevision; also captures the 'reason' supplied with the edit that took this snapshot (legacy reasons were migrated here).
changes [String!]! Which node fields the edit that took this snapshot changed, e.g. ["abstract","tags"] (#620). The snapshot is the PRE-edit state, so this describes the delta between this snapshot and the state that replaced it. Empty for legacy rows.
approvedBy String Reserved (#620): User id that approved this revision. No write surface yet.
approvedByUser NodeRevisionEditor approvedBy resolved to public identifiers (handle + URN), like editedByUser.
createdAt DateTime!

NodeRevisionEditor

A revision's editor resolved to PUBLIC IDENTIFIERS ONLY (#617). Deliberately excludes name / email / any PII — resolving editedBy must never widen the disclosure surface. handle + urn are the same public identifiers the users search already exposes, so no per-caller visibility gate applies.

Field Argument Type Description
handle String
urn String

NodeRoleRule

1325 — one node-role rule. Task and description references are stored by

node id (they survive moves) and returned as a URN and an id, each with its state. Both are given only when the state is OK; see NodeRoleRuleRefState.

Field Argument Type Description
id ID! The rule's id — its ref for updateNodeRoleRule / deleteNodeRoleRule (a rule has no URN).
role String! The dotted role or sub-role this rule governs, unique within the config.
authorTask String
authorTaskId ID
authorTaskState NodeRoleRuleRefState!
validationTask String
validationTaskId ID
validationTaskState NodeRoleRuleRefState!
descriptionNode String Documentation, not procedure: a deleted description node reads as NONE, never BROKEN.
descriptionNodeId ID
descriptionNodeState NodeRoleRuleRefState!
strictSubRoles Boolean! On a parent role: refuse its undeclared sub-roles instead of falling back to it.
writers NodeRoleWriters!
validateBy ContentValidator
enabled Boolean!
locked Boolean! Set by applying a required template (#1334). A locked rule refuses every ordinary edit and delete (RULE_LOCKED); unlockNodeRoleRule is the deliberate step.
sourceTemplateId ID The template this rule was copied from, if any (provenance only; no live link).
sourceTemplate NodeRoleRuleSourceTemplate
revision Int! Optimistic-concurrency token: pass it as expectedRevision to update or delete.
createdAt DateTime!
createdBy ID
updatedAt DateTime
updatedBy ID

NodeRoleRulePayload

Field Argument Type Description
rule NodeRoleRule!
warnings [MemoryConfigWarning!]! Empty until #1327 adds its visibility warnings.

NodeRoleRuleSourceTemplate

Field Argument Type Description
id ID!
name String!
deleted Boolean! The template has been deleted; the rule keeps its copy.

NodeVectorMeta

Per-hit vector-retrieval lineage (cor:api:090:04), present when a hit came from the vector index.

Field Argument Type Description
similarity Float
model String
chunkIndex Int
offsets [Int!]
abstractStale Boolean
source String

ObjectList

745 — a page of objects from the object store. Each object is a flat JSON

record { id, type, ...fields } (the projection of a node). 'total' is the full match count (null when unbounded).

Field Argument Type Description
objects [JSON!]!
total Int

OrgMember

Per-organization membership record for a user.

Field Argument Type Description
id ID!
user User!
role Role Target member's role in this org. Null when the viewer isn't an ADMIN/OWNER of the org (#384 field-level visibility); always visible for one's own membership.
canInvite Boolean!
createdAt DateTime!

Organization

A company or team that owns one or more Hadron memories.

Field Argument Type Description
id ID!
name String!
urn String!
policy JSON Org-layer action-policy link (cor:acl:040:02, #510). Null = no restriction. Org-admin-authored via updateOrganization.
listedOnMarketplace Boolean! cor:acl:080:04 — opt-in marketplace catalogue flag (advertisement, not access).
githubInstallationId Int
githubAppId String
hasGithubAppPrivateKey Boolean!
members [OrgMember!]!
memories [Memory!]!
agents [Agent!]!
apps [App!]!
subscriptions [MemorySubscription!]!
exchangeConnections [ExchangeConnection!]!
agentOrgGrants [AgentOrgGrant!]! 008-agent-installation: this org's grants for using/bundling Agents. Resolvable to ADMIN/OWNER of the org only.
createdAt DateTime!
updatedAt DateTime!

OrganizationsPage

Field Argument Type Description
items [Organization!]!
total Int!

OwnTeamChatReadState

1353 — the exact Worker/Channel cursor advanced by markOwnTeamChatRead.

Field Argument Type Description
workerId ID!
channelId ID!
lastSeenSeq Int!

Passage

Spec 033 US2 — one matching chunk from a content-chunk vector index. Carries the locator metadata a RAG consumer needs for context-stuffing: span text, character offset within the parent node, chunk index, and the parent node's URN.

Field Argument Type Description
parentNodeId ID!
parentNodeUrn String!
chunkIndex Int!
charStart Int!
charEnd Int!
text String!
score Float!

PolicyLayerVerdict

One layer's verdict in an explained policy evaluation (#510).

Field Argument Type Description
layer String! org, user, or app.
present Boolean! Whether this layer has a link at all (absent = no restriction).
allowed Boolean!
allow [String!] The layer's allow-list, for display; null when absent.

PrincipalGrant

An individual action grant (design:grant-model): extra management actions for one member of one org, on top of their role bundle. Grantee and org are projected as scalars, never nested objects.

Field Argument Type Description
id ID!
principalType String! Grantee kind - 'USER' in v1.
principalId ID!
principalHandle String
organizationId ID!
organizationUrn String
actions [String!]! Allowed actions - exact ('memory.clone'), prefix ('memory.*'), or '*'.
expiresAt DateTime ISO-8601 expiry, or null for perpetual (until revoked).
createdAt DateTime!

PrincipalGrantsPage

Field Argument Type Description
items [PrincipalGrant!]!
total Int!

PublicInvitation

The invite-page view available to anyone holding the invitation slug.

Field Argument Type Description
memberRole Role!
maxActivations Int
activationCount Int!
expiresAt DateTime
sender PublicInvitationSender
organization PublicInvitationOrganization

PublicInvitationOrganization

Only the organization display name needed on the invite page.

Field Argument Type Description
name String!

PublicInvitationSender

Only the sender display fields needed on the invite page.

Field Argument Type Description
name String
githubUsername String

PublicOrganization

cor:acl:080:02 — a sanitized, non-member-safe view of a DISCOVERABLE org. Deliberately a SEPARATE type from Organization: it exposes only public identity + the org's public footprint, never members / private memories / apps / credentials (which the full Organization's nested resolvers would leak).

Field Argument Type Description
id ID!
name String!
urn String!
listedOnMarketplace Boolean!
publicMemories [PublicResourceRef!]! The org's PUBLIC, non-app-scoped memories (sanitized refs).
publicAgents [PublicResourceRef!]! The org's PUBLIC agents (sanitized refs).

PublicResourceRef

A minimal, safe reference to a public resource (marketplace / discovery).

Field Argument Type Description
id ID!
name String!
urn String!
description String

PublicResourceRefPage

A paginated page of sanitized public resource refs (marketplace catalogue).

Field Argument Type Description
items [PublicResourceRef!]!
total Int!

QuotaCounter

Usage-vs-limit for one scope x action window (#510). The stored limit is the enforced value; env defaults only seed first touch.

Field Argument Type Description
id ID!
scopeKind String! app or org.
scopeId String!
action String! run.execute, llm.tokens, sms.send, or email.send.
window String! Window kind (day). The row rolls in place at UTC midnight - used resets, limit persists.
used Int!
limit Int!
resetAt DateTime!

RegisterDefaults

Spec 049 Phase 5 — what an Agent's install-created register row is made with (plan §3.5).

Field Argument Type Description
mentionOnly Boolean!

RegisterEntriesPage

Field Argument Type Description
items [RegisterEntry!]!
total Int!

RegisterEntry

A REGISTER ENTRY (spec 049, D-2026-09-13-007): "this attendee takes part in this Channel". The attendee is a Worker when cast, otherwise an Agent in an App; both null = every attendee in the owner's context (org-wide for an organization row, App-wide for an App row). Not a trigger list — a row never starts a run. The register never grants: what the attendee may read or post is the Channel's host memory's decision. Not exportable.

Field Argument Type Description
id ID!
ownerType RegisterOwnerType!
ownerId ID! ID of the owning organization / App.
organizationId ID
appId ID
channelId ID!
channel Channel The Channel — null when you may not read its host memory (you stand on the owner side only).
attendeeWorkerId ID
attendeeAgentId ID
attendeeUrn String The attendee's URN (hrn:worker:… or the Agent's); null for 'every attendee'.
role RegisterRole!
mentionOnly Boolean! Only messages mentioning the attendee count as new for it.
installDefault Boolean! The row install materialised for the App's default Channel (D2).
description String The human's intent, free text.
createdAt DateTime!
updatedAt DateTime

RegisterViewChannel

The Channel half of a register view row; the host memory's URN only when the viewer may read it.

Field Argument Type Description
id ID!
name String!
kind ChannelKind!
loc String!
hostMemoryUrn String
hostMemoryId ID The host memory's id — like the URN, only when the viewer may read it.
lastSeq Int! The Channel's watermark — the highest seq issued, not a count.
lastMessageAt DateTime
deleted Boolean!

RegisterViewRow

One row of an attendee's RESOLVED register (spec 049 §4.3): the winning register entry for a Channel (App › organization, Worker › Agent › everyone, never unioned), the Channel's watermark, and the ATTENDEE's real standing on the host memory — canRead / canPost are the existing gates, the register grants nothing. A row the attendee cannot act on is returned with its reason, never dropped. Host memory identifiers remain gated for the VIEWER.

Field Argument Type Description
entry RegisterEntry!
namedBy RegisterNamedBy!
channel RegisterViewChannel!
lastSeenSeq Int Phase 7 — the attendee's cursor; null until read-state lands.
canRead Boolean!
canPost Boolean False for a Worker without an eligible driver (UNKNOWN_ACTOR); null only when no access subject was supplied.
disclosure RegisterDisclosure
attendeeUrn String

ReleaseWorkerPayload

1073 — what a releaseWorker call actually DID, rather than what the caller

predicted it would do.

releaseWorker used to return the post-release Worker, whose heldByUserId is null by construction — so a client could report only who held the name when it last looked. Per cor:agt:020:09 the two release paths are different ACTS: the holder's own release notifies nobody, an admin force-release must announce itself in the team chat. A receipt that cannot tell them apart describes an act that may not have happened.

Field Argument Type Description
worker Worker! The worker, re-read immediately after the guarded release and BEFORE the team-chat notice — so the interval in which another caller could bind is as narrow as it can be made. It is NOT zero, and this is a re-read rather than a snapshot of the write. So a non-null heldByUserId here is a LATER hold somebody else took, never a failed release. releasedFrom is what says the release happened; this field is current state, and the two answer different questions.
releasedFrom ReleasedFromUser The holder the WRITE ended — not the one a pre-read saw. Null when nothing was released, i.e. the name was already unheld (the idempotent path). This is the value the authorization decision was made against AND the value the guarded write matched on; #1084 made those the same user by construction, so this cannot name somebody other than whoever actually lost the name.
forced Boolean! True when the caller released SOMEBODY ELSE'S hold — the admin force-release path, the one act that takes a name without its holder acting, and the one that posts to the team chat. Computed server-side, from the authenticated principal and the hold the write ended in the same transaction. A client computing it would have to compare a value it was given against one it had to fetch, and that fetch fails on exactly the path where the answer matters. false always means the holder was YOU, or that nothing was released (releasedFrom null) — never "the server could not tell".
notified Boolean Whether the team-chat notice was posted. THREE states, deliberately: - null — no notice was owed. A self-release, or nothing released. - true — a notice was owed and posted. - false — a notice was OWED AND FAILED. The release still happened; an unreachable chat must not stop an admin recovering a departed colleague's names. A plain boolean would collapse the first and last, and a client cannot render "we may have silently failed to announce this" honestly — so the only truthful UI would be to say nothing, throwing the field away.

ReleasedFromUser

1073 — a sanitized identity for the person a release took a name FROM.

Deliberately a SEPARATE type from User, on the cor:acl:080:02 / PublicOrganization precedent: a field returning another entity's object hands the caller that entity's field resolvers, and User's default-resolved tail (policy, identityProvider, externalId, githubId, roles, maxReferrals) carries no gate of its own — it relies on the surface that returned it.

releaseWorker cannot lean on that. Its admin path exists for the departed colleague, and leaveApp DELETES the AppMember row while the hold survives — so the very case this path serves is the one where the caller has no other route to that User. Worker itself only ever exposed heldByUserId, a bare id, so a full User here would be a NEW disclosure rather than an inherited one.

Exposes what answers "who did I release", and nothing that grows back toward User. name keeps the same #384 gate it has on User rather than a second copy of the rule; the rest is public identity a URN already discloses.

Field Argument Type Description
id ID!
handle String
urn String Canonical hrn:user:<handle> URN (cor:urn:010:01). Null for a handle-less user.
name String Withheld (null) from a viewer who is not the user themselves, a platform admin, or a co-member of one of their orgs — the SAME #384 gate as User.name, reused rather than restated. An admin releasing a colleague who has left the org sees null here, and still gets id/handle/urn.

RemoveMemoryMemberPayload

Field Argument Type Description
memoryId ID!
userId ID!

ResendSender

One org-owned BYO Resend sender. The API key is encrypted and write-only.

Field Argument Type Description
id ID!
organizationId String!
from String! Fixed sender identity, e.g. Hadron Agent ; Resend enforces verification.
hasApiKey Boolean! Whether an encrypted API key is stored; the key is never returned.
enabled Boolean!
createdAt DateTime!
createdBy String
updatedAt DateTime!
updatedBy String

ResendSendersPage

Field Argument Type Description
items [ResendSender!]!
total Int!

ResolvedAiConfig

036-ai-service-config: privileged resolution result. Carries the DECRYPTED key — only returned by resolveAIConfig. A resolved platform credential is decrypted only for platform ADMIN/OWNER; org ADMIN may decrypt an authorized tenant-owned config. Successor to agentAIConfig / appAIConfig.

Field Argument Type Description
configId ID!
name String!
ownerType AiConfigOwnerType! Which level matched: APP | AGENT | ORGANIZATION | HADRON_SERVER.
provider String!
model String!
apiKey String! Decrypted key. For provider 'bedrock' this is the JSON-encoded accessKeyId/secretAccessKey/region triple the portal stores.
params JSON
effectiveEndpoint String #1373: where requests for this config go (see AiServiceConfig.effectiveEndpoint).
effectiveApiStyle AiApiStyle #1373: the wire protocol requests use (see AiServiceConfig.effectiveApiStyle).

RevisionResult

Field Argument Type Description
revisionLoc String!
label String!
published Boolean!
createdAt DateTime!

ScheduleBusyInterval

Field Argument Type Description
start DateTime!
end DateTime!

ScheduleFreeBusy

Free/busy intervals for one requested schedule (calendarSchedule).

Field Argument Type Description
id ID!
busy [ScheduleBusyInterval!]!
error String Set when the provider could not resolve this schedule; the query still succeeds for the others.

Scope

A named search SCOPE: an ORDERED list of memories with exactly one owner (organization | Agent | App). Selects memories only — no saved filter, no retrieval parameters — and NEVER grants: memories is the stored list ∩ what YOU may read (D-2026-09-13-004), and hiddenMemoryCount says how many you cannot see, as a count only. Names are unique per owner; global and app are reserved (the ladder's own views). Collisions across owners in an App's context resolve App › Agent › organization, never unioned.

Field Argument Type Description
id ID!
name String!
description String
ownerType ScopeOwnerType!
ownerId ID! ID of the owning organization / Agent / App.
ownerUrn String Bare stored URN of the owner, when it has one.
memories [ScopeMemoryEntry!]! The scope's memories YOU may read, in scope order.
memoryCount Int! How many memories the scope lists in total.
hiddenMemoryCount Int! How many of them you may NOT read (count only — never their names).
createdAt DateTime!
updatedAt DateTime

ScopeExplanation

What a scope resolves to FOR YOU: which owner tier won when resolved by name, the scopes it shadowed, the readable memories in order, how many were dropped for access, and — when loc was asked — which memory would win for that address (the input the task-customization operation consumes).

Field Argument Type Description
scope Scope!
resolvedVia ScopeOwnerType!
shadowed [Scope!]! Lower-precedence scopes of the same name in that App's context (that you may read).
memories [Memory!]!
droppedCount Int!
winner Memory

ScopeMemoryEntry

Field Argument Type Description
position Int! 0-based order within the scope.
memory Memory!

ScopesPage

Field Argument Type Description
items [Scope!]!
total Int!

SearchReplaceFieldResult

Field Argument Type Description
field NodeTextField!
matches Int!

SearchReplaceNodeResult

Field Argument Type Description
nodeId ID!
loc String!
memoryId ID!
replacements Int! Total replacements across all searched fields on this node.
fields [SearchReplaceFieldResult!]!

SearchReplaceResult

Field Argument Type Description
nodesSelected Int! Unique authorized nodes selected before bulk-write protections are applied.
nodesScanned Int! Selected nodes whose requested text fields were actually inspected.
nodesSkipped Int! Selected nodes protected from inspection and replacement.
skipCounts SearchReplaceSkipCounts! Mutually exclusive skip reasons. Governance takes precedence over Channel address protection, so a node carrying both signals counts once as governed.
nodesChanged Int! Nodes with at least one replacement.
totalReplacements Int!
dryRun Boolean! Echoes the request — true means nothing was written.
results [SearchReplaceNodeResult!]! Per-node breakdown; only nodes with at least one match are listed.
plan String Opaque exact preview fingerprint on the spec door; null on the generic door.
skips [SearchReplaceSkip!] Node-addressed protected selections on the spec door; null on the generic door.

SearchReplaceSkip

Field Argument Type Description
nodeId ID!
loc String!
memoryId ID!
reason SearchReplaceSkipReason!
governedKinds [String!]!

SearchReplaceSkipCounts

Mutually exclusive reasons selected nodes were protected from bulk replacement.

Field Argument Type Description
governed Int! Governed nodes of any kind. This is not a spec-only count.
protectedAddress Int! Nodes at or below a Channel address.

SearchScopeInfo

Spec 049 — the scope a search ran under (every result discloses it, D-2026-09-13-002). kind: scope | app | global | user-owned. source: call | session | active-app | active-org. memoryUrns lists only the memories YOU may read; droppedCount is how many the scope lists that you may not — a count, never their names.

Field Argument Type Description
kind String!
source String!
label String!
ownerUrn String
memoryUrns [String!]!
droppedCount Int!

Secret

One named, owner-scoped secret — the general secret store (#677). Polymorphic owner (user | org | app | memory). The value is encrypted at rest and WRITE-ONLY: no field here ever returns it — kind + metadata are the inspectable half (you can see THAT a secret exists and, for webfetch-auth, WHERE it applies, without decrypting). A bare name resolves at run time via the CSS cascade, most-specific-first: memory -> app -> user -> org.

Field Argument Type Description
id ID!
ownerType String! Owner scope: user | org | app | memory.
ownerId ID!
name String! Cascade-resolution key; unique per owner; lowercase [a-z0-9-], max 64.
kind String! Extensible kind; v1: generic | webfetch-auth.
metadata JSON The NON-secret, inspectable half (webfetch-auth: { type, urlPrefix }).
createdAt DateTime!
createdBy String
updatedAt DateTime!
updatedBy String

SecretsPage

Uniform paginated list shape (cor:api:120).

Field Argument Type Description
items [Secret!]!
total Int!

ServerInfo

Identity of the running hadron-server (see Query.serverInfo).

Field Argument Type Description
version String! HADRON_SERVER_VERSION — the MCP/API-surface contract version.
baseUrl String! The deployment's canonical base URL (BASE_URL, else the local default).

Session

A unit of work recorded against a user or App: the general row, and what the bare word "session" means throughout this schema (#1034). type says which kind — DEVELOPER, CHATBOT, AUTOMATION, EDGE — and workerId is optional.

A worker session is the subtype with workerId set: a Worker (named casting) bound to a driver, which is what makes work attributable to a teammate and what a merged PR traces back through. Binding is optional, so a list of these is not a staff view and may hold no worker sessions at all — read workerId per row rather than assuming either way.

A chat session is NOT one of these and has no row here: it is the conversation a human is in — the Claude Desktop window, the Claude Code session, the IDE chat. The two are independent and ending one does not end the other: a chat session that closes leaves its worker session open until endSession. Since #1114 nothing ends it for inactivity — silence is not evidence of abandonment — but it stops reading as LIVE once nobody has driven it inside its idle window, so the stale session stops blocking another bind without being ended or losing its unwritten handoff. That changes no HOLD: a human-held name stays held until explicit release, so only its holder can bind it again. Do not use "chat session" for a Chat (the agent conversation entity) — that is an agent chat, a third concept.

Field Argument Type Description
id ID!
type String! Which kind of session this is: DEVELOPER, CHATBOT, AUTOMATION or EDGE.
appId String
userId String
agentId String The role-agent driving the session (with workerId set, the agent behind the casting).
workerId String #974 / cor:agt:020:03: the Worker (named casting) this session works as.
worker Worker #980 / cor:agt:020:02: the Worker behind workerId, nested — so a session list can render the app-qualified compound ('eng-team/Iris') without a per-row worker(ref:) + app(ref:) round trip (Worker carries name and app). Resolves for RETIRED workers too: retirement ends the casting, not the history, so past sessions stay attributable. Worker.app and Worker.agent run their OWN read gates and mask to null on deny (#552), so this nesting never widens what the session read admits.
memoryId String
expiresAt DateTime
repo String
branch String
prNumber Int
customerId String
language String
plan String
llmModel String
transcriptPath String #928: where the driving tool's transcript lives on 'host'.
host String #928: machine identifier the session ran on.
tool String #928: the tool driving the session ('claude-code', 'codex', ...).
inputTokens Int
outputTokens Int
turnCount Int
errorCount Int
parentSessionId String
prevSessionId String
summary String
outcome Float
outcomeRef String
outcomeMeta JSON
startedAt DateTime!
endedAt DateTime
autoExpiredAt DateTime
isLive Boolean! hadron-cli#791 / cor:agt:020:11: whether THIS session is live: not ended, AND driven inside its type's idle window (activity is the latest of startedAt, the heartbeat updatedAt, and the latest attributed usage event, capped at endedAt). The same derived predicate as startSession's WORKER_TAKEN gate and Worker.hasLiveSession, so the three always agree. An open session that is not live has LAPSED: nobody drove it inside the window, so another driver may bind its worker; it is not ended, and can still be ended with a handoff. False for an ended or deleted session.
events [UsageEvent!]!

SkillExportPlanEntry

The server's filesystem plan for one skill. The client still performs and reports actual local I/O; this object records only what the server judged safe.

Field Argument Type Description
action SkillExportAction!
preservesExistingFile Boolean! True when the client must leave any installed file unchanged.
reasons [SkillExportReason!]!

SkillExportReason

One server-owned reason for an export action.

Field Argument Type Description
code String! Stable machine-readable reason, usually a drift class or lint rule.
message String! Actionable text clients can display without recreating server policy.

SkillFinding

One lint result, carrying the node and memory so a renderer never looks them back up.

Field Argument Type Description
urn String!
memory String!
rule String!
severity String! Either error or warning — the same vocabulary spec lint renders.
message String!

SkillOrphan

A file that paired with no declared node. Never removed without an explicit prune.

Field Argument Type Description
dirName String!
sourceUrn String
nodeId String

SkillPlan

Field Argument Type Description
entries [SkillPlanEntry!]!
orphans [SkillOrphan!]!
orphanAssessmentSkipped Boolean! True for scoped or selected exports: out-of-selection files are not classified as orphans.
scopeSelection SkillPlanScopeSelection Non-null only when EXPORT selected a persisted Scope.
selectionResults [SkillSelectionResult!]! Opaque unavailable refs and selected nodes with no declaration for this host.
scanned Int! How many nodes carrying ANY declaration key were in scope. A plan over an empty corpus and a plan the caller cannot see are otherwise the same empty list, and a client that cannot tell them apart reports nothing-to-do for a corpus it was denied.
judged Int! How many of those were judged for THIS host — the number of entries. A node declaring only another host is scanned and not judged, so the two differ by design; the gap is a fact about the corpus, not a discrepancy.
unrecognized [SkillPlanUnrecognized!]! Nodes in scope carrying a key under properties.exports that names no host (#1292, cor:agt:030:06): an unrecognised key is reported, never read as an alias, and never blocks anything. HOST-INDEPENDENT: the list is identical in every host's plan, because the key belongs to none of them, so a client reports each node once rather than once per host. A node here may also have an entry, when it declares a known host too; one declaring only unknown keys has none, and is part of the scanned-minus-judged gap.

SkillPlanEntry

Field Argument Type Description
urn String!
nodeId String!
revision Int! Authoritative current node revision used in rendered provenance.
name String! The stored skill name (D12 retired derivation).
class String One of: current, stale, locally-edited, renamed, never-exported, orphaned, unhashed, collision, unavailable, disabled. Null when the file could not be parsed — a parse failure is reported as a FAILURE, not as a class.
parseFailure Boolean! True when the file exists and does not parse. Reported identically by status and export.
movedFrom String Source directory selected for a MOVE or REMOVE action; also reported for the renamed class.
findings [SkillFinding!]!
renderedBody String Present only for EXPORT, only for a class whose action writes, and only when the node has no error findings — so a client cannot write a file the server already judged broken.
exportPlan SkillExportPlanEntry Present only for EXPORT. This is a planned action and its server-owned reasons, not evidence that the client completed a filesystem write.
outOfExportReason String Why an enabled source is out of export in STATUS or EXPORT, or null when this category does not apply. Separate from the planned filesystem action.

SkillPlanScopeSelection

The live, access-intersected Scope selection used by this EXPORT plan.

Field Argument Type Description
id ID!
name String!
readableMemoryCount Int!
droppedCount Int!
fingerprint String! Stable digest for comparing separate host plans from one export.

SkillPlanUnrecognized

A node carrying one or more exports keys that name no host (#1292).

Field Argument Type Description
urn String!
nodeId String!
name String! The node's display name, not a skill name: an unrecognised key has no host, so it declares no skill name anyone reads.
memory String!
keyCount Int! How many unrecognised keys the node carries — the true total, whether or not each is listed.
keys [String!]! The unrecognised keys LISTED, in UTF-8 byte order — e.g. codex for properties.exports.codex. At most 10, and none over 128 bytes: the report is bounded because properties is writer-controlled. keyCount is the total; anything not listed is counted in one extra warning, never dropped silently.
findings [SkillFinding!]! One warning per listed key, plus one for any not listed. Attributed to no host; never blocks.

SkillSelectionResult

A named task that cannot produce a host entry. An unavailable ref reveals no node metadata.

Field Argument Type Description
ref String!
nodeId String
action SkillExportAction!
reason SkillExportReason!

SlackConnection

One Slack workspace install of the Hadron Slack app (spec 043). Org-owned, workspace granularity: defaultAppId names the Hadron App that serves /hadron commands from this workspace. The bot + app tokens live ENCRYPTED IN THE TOOL (hadrontool-slack) — they are write-only inputs on createSlackConnection and no field here ever returns them.

Field Argument Type Description
id ID!
organizationId String!
defaultAppId String!
provider String! Slack provider backing this connection ('slack' today).
teamId String! Slack workspace (team) id, e.g. T0123456789.
teamName String!
botUserId String! The bot's Slack USER id (U…) — the sender on messages it posts.
botId String! The bot's BOT id (B…) — what bot message events carry. Distinct from botUserId; the bridge loop filter needs both.
status String! 'ACTIVE' or 'ERROR' (the tool's connection-status vocabulary).
lastError String
createdAt DateTime!
updatedAt DateTime!

SlackConnectionsPage

Uniform paginated list shape (cor:api:120).

Field Argument Type Description
items [SlackConnection!]!
total Int!

SpecCorpusMintReport

1448 — the mint report. blockers must all be gone for a mint to succeed.

staleAbstracts are listed separately; they block only when staleAbstractsBlock is true (once abstract re-affirmation is saved and deployed, server#1408). openQuestions never block.

Field Argument Type Description
memoryId ID!
dryRun Boolean!
minted Boolean! True when this call minted the corpus.
blockers [SpecMintFinding!]!
staleAbstracts [SpecMintFinding!]!
staleAbstractsBlock Boolean!
openQuestions [SpecOpenQuestionGroup!]!

SpecMintFinding

1448 — one item in a mint report. loc is where it was found; targetLoc the cited spec, for a reference.

Field Argument Type Description
kind SpecMintFindingKind!
loc String!
rule String
message String!
targetLoc String

SpecOpenQuestion

1448 — one open question in the corpus (report-only, never blocks minting).

Field Argument Type Description
loc String!
question String!

SpecOpenQuestionGroup

1448 — open questions grouped by who decides; decider null means unassigned.

Field Argument Type Description
decider String
questions [SpecOpenQuestion!]!

SpecReference

1449 — one reference to a spec in a draft corpus. For kind URN, field is

the text field it was found in and text the URN as written; for kind EDGE, field is 'edge' and text the edge's relationship name.

Field Argument Type Description
kind SpecReferenceKind!
sourceNodeId ID!
sourceLoc String!
field String! The scanned field; edge for a real edge; pendingEdge for an edge whose target is not a node yet.
targetLoc String! The cited loc; for an edge into another memory, the target's node URN; for a pendingEdge not yet resolved to a node, its target as recorded (a loc, file id or URN).
text String
reason SpecReferenceProblem Set only by specUnresolvedReferences.

SpecRenumberMove

1449 — one spec a renumber moves (the spec or a descendant).

Field Argument Type Description
fromLoc String!
toLoc String!

SpecRenumberResult

1449 — the result (or, with dryRun, the plan) of renumberSpec.

Field Argument Type Description
dryRun Boolean!
moved [SpecRenumberMove!]!
rewrites [SpecRenumberRewrite!]!
textCitations [SpecTextCitation!]!

SpecRenumberRewrite

1449 — one node whose URN references a renumber rewrites.

Field Argument Type Description
nodeId ID!
loc String!
fields [String!]! The text fields rewritten.
count Int! How many URN references were rewritten.
status SpecRewriteStatus!
error String

SpecTextCitation

1449 — a moved citation that appears as bare TEXT (a link label, a node

name, prose). Reported, never rewritten: it cannot be told apart from other characters in prose.

Field Argument Type Description
sourceNodeId ID!
sourceLoc String!
field String!
citation String!
excerpt String! A short window of text around the citation.

StartImpersonationResult

Returned exactly once by startImpersonation — the token is never stored or shown again (UserApiKey rawKey precedent).

Field Argument Type Description
session ImpersonationSession!
token String!

SummarizationRequest

Retained for ChatPromptResult.summarizationNeeded, which is currently always null (#1115). Kept so the schema does not break clients that still select the field.

Field Argument Type Description
messageCount Int!
prompt String!

TeamAttentionChannel

1353 — compact unread metadata for one Worker/Channel pair. No message body is returned.

Field Argument Type Description
channel ID!
name String!
unread Int!
unreadMentions Int!
firstUnreadSeq Int
lastSeq Int!

TeamAttentionPage

Field Argument Type Description
items [TeamAttentionPageItem!]!
scanComplete Boolean! False while any Channel is unfinished or queued items remain undelivered.
nextPage String Continuation for the next pull; null on the final page.
adoptableSince String FINAL PAGE ONLY: a since-watermark token (same shape v1 issued) to adopt after every listed nudge was accepted. Never present while scanComplete is false.

TeamAttentionPageItem

1384 paged (v2): one bounded slice of an attention scan. Counts are exact

for the captured (seen, head] range of the issuing scan only, and items are emitted solely from Channels that scan has exhausted.

Field Argument Type Description
worker ID!
workerName String!
workerUrn String
channel ID!
channelName String!
unread Int!
unreadMentions Int!
firstUnreadSeq Int
lastRelevantSeq Int
lastSeq Int! The frozen Channel head this item's counts are exact for.

TeamAttentionPreviewPage

Field Argument Type Description
items [TeamAttentionPageItem!]!
scanComplete Boolean!
nextPage String
proof String FINAL PAGE ONLY: the short-lived switchover proof (frozen heads). An incomplete preview never carries one, so a partial preview can never silently discard an unscanned backlog.
expiresAt DateTime

TeamAttentionResult

Field Argument Type Description
token String! Opaque signed snapshot. Adopt it only after every listed nudge is accepted.
workers [TeamAttentionWorker!]!

TeamAttentionSwitchoverChannel

Field Argument Type Description
channel ID!
name String!
fromSeq Int!
throughSeq Int!
unread Int!
unreadMentions Int!

TeamAttentionSwitchoverPreview

Field Argument Type Description
proof String! Short-lived proof binding the exact operator, App, Workers, registers, cursors and heads shown here.
expiresAt DateTime!
workers [TeamAttentionSwitchoverWorker!]!

TeamAttentionSwitchoverResult

Field Argument Type Description
applied Boolean!
workersAdvanced Int!
channelsAdvanced Int!

TeamAttentionSwitchoverWorker

Field Argument Type Description
worker ID!
name String!
urn String
channels [TeamAttentionSwitchoverChannel!]!

TeamAttentionWorker

1353 — one live Worker that has relevant unread attention after the supplied token.

Field Argument Type Description
worker ID!
name String!
urn String
live Boolean!
channels [TeamAttentionChannel!]!

TeamChatMessage

One message in a team App's chat (#939, Worker envelope since #974). Exactly one of authorUserId / authorWorkerId is set: a human post carries the user, a worker post carries the Worker (the named casting, cor:dmo:050:11) plus the driving sessionId. mentions holds the lowercased tokens extracted server-side at write time (the '@worker-name / @handle' format, stored without the '@').

Field Argument Type Description
nodeId ID! The message node's id.
urn String The message node's canonical URN, hrn:node:<memory>:<loc> (#1209), so a client can cite a message in a CLI call, an MCP tool or a reply. Composed server-side: a message loc carries a random component, so the chat root and the seq do not reconstruct it. Null when the address would not resolve back to this message: the host memory's stored URN or the loc cannot be emitted in the flat grammar-v2 shape, or the memory URN is a pre-v2 compound one (e.g. a legacy team space). A client shows nothing then, never a guessed address (the Worker.urn / Channel.urn contract).
seq Int! Ordering key, unique per chat, allocated atomically (ascending from 1).
body String!
at DateTime!
authorUserId ID
authorWorkerId ID
authorName String Display convenience: the worker name or the user handle.
authorAppId ID Spec 049 Phase 8 (item J): the Worker's App when the post crossed Apps — visible as one in the transcript.
sessionId ID The session driving the worker author, when posted via sessionRef.
replyToSeq Int The seq this message replies to, when it is a reply.
mentions [String!]!

TeamChatMessagesPage

Field Argument Type Description
items [TeamChatMessage!]!
total Int! Number of canonical messages matching this read's sinceSeq/beforeSeq cursor range and mentionsRef filter, before limit/offset. It is not the total number of messages in the chat or Channel when a cursor/filter is present.

TeamCollectionsPayload

Result of updateTeamCollections (hadron-cli#401).

Field Argument Type Description
memoryId ID! The team App's shared memory the declaration was written to.
collections [String!]! The server-owned collections declared on it.
changed Boolean! False when the declaration already matched the canonical definition (idempotent no-op).

TeamRole

A role definition (#960): the roles:<role> node in the Team Agent's system memory. #1050: it carries no name register — a role is a definition, not an allocation pool. The prompt template is NOT here — with the Worker model (#974) it lives on the role-agent (personaRole + systemPrompt); roleAgent points at it.

Field Argument Type Description
role String! The role slug, e.g. backend-engineer.
loc String! The node loc: roles:<role>.
nodeId ID!
description String
repos [String!]! #1024 — the repos a worker in this role normally works in, from `data.repos`. A SOFT signal for clients to warn on a role/repo mismatch at session start (hadron-cli#456): a worker bound as a server-engineer with `--repo …/hadron-cli` is usually a mistake and is sometimes deliberate. NEVER a refusal, and never inferred. Cross-repo work is legitimate — a coordinator does it by definition, and sibling repos share code — so a hard gate would be wrong the first time someone makes a genuine cross-cutting change. Affinity is declared, not guessed from the role slug, which happens to work for `cli-engineer` → `hadron-cli` and breaks for any team whose repo names do not match their role names. EMPTY AND UNSET ARE THE SAME: no affinity, never warn. A client cannot usefully act differently on the two, since "spans everything" and "not configured yet" both mean do-not-warn, and distinguishing them would make an unconfigured team look misconfigured.
roleAgent Agent The single installed agent whose personaRole matches this role — exactly the agent a role-mode castWorker would use (null when zero or ambiguous). Runs Query.agent's own read gate and masks to null on deny (the #552 posture), like Worker.agent.
hasNamePlaceholder Boolean Whether the role-agent's systemPrompt binds {{name}} — the check that finds templates which silently produce nameless workers (role-agents authored before any guard existed). Null when no single role-agent resolves. Computed even when roleAgent itself is masked: it discloses one boolean about the caller's own App's casting default.

TeamRolesPage

Field Argument Type Description
items [TeamRole!]!
total Int!

TeamWorkItem

One team-worklog record (#947) — an externally visible work milestone (a PR opened, a branch pushed, an issue closed), append-only. The worklog is the AUTHORITATIVE PR-session join (spec cor:agt:020:03); Session.prNumber is a latest-wins display convenience. ref carries the ONE canonical spelling per artifact (github: owner/repo#N, owner/repo@sha, owner/repo:branch, owner/repo; owner/repo lowercased).

Field Argument Type Description
nodeId ID! The record node's id.
sessionId ID! The session the work is attributed to.
workerName String! Display convenience, denormalized at write: the session's bound worker name, else the attributed user's handle (pre-#974 records surface their stored personaName here).
workerId ID The Worker (named casting) behind the record, when the session was worker-bound.
tool String! The external tool the artifact lives in ('github', ...). Lowercased.
kind String! Artifact kind: pr, issue, commit, branch, or repo.
ref String! The canonical artifact ref — the equality-lookup key.
action String! What happened: opened, merged, claimed, reviewed, closed, pushed, ... (free string, lowercased).
at DateTime! When the record was made (server-stamped).
detail JSON Optional display extras (title, URL) — never filtered on.
model String The LLM model that produced the work, snapshotted at record time (#1398): an explicit report wins over the recording session's model; null on records that predate the field or were made with none — never backfilled, so null always means unknown-at-record-time.

TeamWorkItemsPage

Field Argument Type Description
items [TeamWorkItem!]!
total Int!

UninstallAgentFromAppPayload

Return shape for the uninstallAgentFromApp mutation. The Agent's per-(App, Agent, *) memories are NOT cascade-deleted (spec 023 FR-005); they persist as orphans on the now-removed AppAgent edge.

Field Argument Type Description
appId ID!
agentId ID!

UpdateMemoryMemberRolePayload

Field Argument Type Description
memoryMember MemoryMember!

UpdateMemoryShareRolePayload

Field Argument Type Description
memoryShare MemoryShare!

UrnResolution

Result of resolving a Hadron URN to the ids a client needs to navigate to the resource's canonical page. Powers the portal's /app/u/<urn> redirect route (hadron-portal#262).

kind is the URN's type segment: memory | node | agent | org | app | user | apprun | worker | noderev. id is the resolved entity's primary id. memoryId is set only for nodes (their owning memory), organizationId for apps, app runs, and workers (their owning org) — both are the extra ids those resources' canonical routes require.

Field Argument Type Description
kind String!
id ID!
memoryId ID
organizationId ID
fragment String Set to 'data' when the resolved URN named a #data fragment of its parent node/apprun (#696); null otherwise.

UsageActivityBucket

One time bucket of read+write activity (#796).

Field Argument Type Description
date String! Bucket start as YYYY-MM-DD, in the requested timeZone. Deliberately String and NOT DateTime (#205): it is a calendar-day label in the caller's zone, not an instant — rendering it as a UTC timestamp would shift buckets across the date line.
reads Int!
writes Int!

UsageEvent

Field Argument Type Description
id ID!
type String!
nodeLoc String
memoryId String Memory the event is attributed to (#796). Stored at write time, so it outlives the node — null only for events written before the backfill whose node was already deleted, or paths with no memory in scope.
actionArgs JSON
appId String Caller identity — the App that authored this event.
sessionId String
model String
tokensIn Int
tokensOut Int
userId String
organizationId String
provider String
keyOwnerType String Owner level of the API key that served the call (HADRON_SERVER means platform-paid; anything else is BYOK).
costMicroCents String Provider cost snapshot in micro-cents (1e-8 USD), as a base-10 integer string. Null when the (provider, model) pair is unpriced.
createdAt DateTime!

UsageSummaryEntry

Field Argument Type Description
nodeLoc String!
type String!
count Int!

User

Field Argument Type Description
id ID!
handle String
policy JSON User-layer action-policy link (cor:acl:040:02, #510) — constrains runs made on the user's behalf. Null = no restriction, or hidden from viewers without personal-field access. Self-authored via updateMyPolicy.
urn String Canonical `hrn:user:` URN (spec cor:urn:010:01). Computed from the handle; null for a handle-less user (who has no URN).
githubId Int
githubUsername String
googleId String Google account subject id (#837). Opaque provider identifier, never an address. Withheld (null) from a viewer who is not the user themselves, a platform admin, or a co-member of one of their orgs — the same #384 gate that name/email use. Load-bearing for duplicate-account triage: mergeUsers adopts a source provider id only where the target column is null, and identityProvider records only which provider CREATED the row, so it cannot be used to infer this (a GITHUB row may carry a linked googleId).
appleId String Apple account subject id (#837). Same gate and rationale as googleId.
identityProvider String Account metadata, visible to self, platform admins, and co-members.
externalId String
externalAppId String
linkedAt DateTime
name String
email String
avatarUrl String
roles [Role!]! Platform roles; an empty list is returned to viewers without personal-field access.
maxReferrals Int
appMemberships [AppMember!]! 008-agent-installation: User-App memberships. Resolves to a non-empty list only when the queried User is the authenticated user (mirrors the User.agentSubscriptions resolver-side gate from 005).

UserApiKey

025-oauth-for-mcp: user-scoped bearer-token credential. Mirrors the AppKey shape but resolves to a User instead of an App. Surfaced to the portal revocation UI (Phase 3) so users can audit + revoke their own keys. userId is intentionally absent — for self-service v1 the caller is always the owner; admin-tooling auditability is out of scope (see contracts/graphql-mutations.md, PR-137 delta D1).

Field Argument Type Description
id ID!
keyPreview String!
label String
createdAt DateTime!
lastUsedAt DateTime
revokedAt DateTime
issuedVia String Issuance path: portal (createUserApiKey) or oauth:<client_id> (POST /oauth/token). Powers source labels in the revocation UI.
oauthScopes [String!]! Immutable OAuth scopes bound to this credential. Empty means an unrestricted portal/legacy key rather than an OAuth-scoped key.

UserApiKeyCreateResult

Returned once at creation — the raw key is never stored. Renamed from PR 137's UserApiKeyCreated for Result-suffix consistency; nested field is userApiKey, not key (PR-137 delta D2).

Field Argument Type Description
userApiKey UserApiKey!
rawKey String!

UserInvitation

Invitation to join the platform (and optionally an organization)

Field Argument Type Description
id ID!
slug String!
senderUserId String
sender User
organizationId String
organization Organization
memberRole Role!
name String
email String
githubUsername String
maxActivations Int
expiresAt DateTime
acceptedAt DateTime
createdAt DateTime!
activationCount Int!

UsersPage

Field Argument Type Description
items [User!]!
total Int!

Worker

A Worker (#974, cor:dmo:050:11) — the named casting of an installed Agent into an App: 'Iris', the backend-engineer agent cast into the eng-team App. The Agent carries the reusable persona dressing; the Worker is the local named identity that does attributable work. Names are unique per App, case-insensitively, forever (retirement and uninstall never free them — cor:agt:020:02); rows survive the agent's uninstall. A Worker is addressable by its id or by the computed urn below (#991).

Field Argument Type Description
id ID!
appId ID!
appUrn String The App's canonical URN (the same value App.urn returns, e.g. hrn:app:acme.com:team), as a scalar identity suitable for cross-App worker lists. Null when the caller cannot inspect this App's workers. This intentionally does not return the App object, whose nested keys and members have a narrower read audience.
agentId ID!
name String! The worker's name ('Iris') — the identity every surface renders.
slug String! The URN atom, derived from the name at cast time and permanent thereafter (#991). Lowercased, sanitized to the URN slug charset, and iterated ('iris', 'iris-2', …) until free within the App — so deriving it NEVER refuses a cast, and the name collision stays the only allocation failure a caller sees (cor:agt:020:02).
urn String hrn:worker:<root>:<app-slug>:<slug> (#991) — computed from the App's URN plus `slug`, not stored. Accepted anywhere a workerRef is taken, and resolvable via Query.resolveUrn, so a client can address a worker without holding its id. Null only when the App's URN predates the flat grammar-v2 shape this arity requires.
portalUrl String #1026 — the portal link that opens this worker, mirroring Node.portalUrl (#881), so a worker signing an artifact under #1008 hands over a URL instead of assembling one out of a URN spelling the /app/u route may not resolve. Built from the SAME URN the urn field returns, so the two cannot diverge. Null on both arms and neither is an error: no configured portal origin means no link (a link to the wrong host is worse than none), and an App URN the fixed arity cannot take means no URN to build one from.
role String The cast-list role this filling answers ('backend-engineer').
repos [String!]! #1024 — the repo affinity of this worker's ROLE, resolved so a client that already holds the worker needs no second round-trip to teamRoles. The CLI warns on a role/repo mismatch at session start (hadron-cli#456). WORKING-STATE field: behind the worker read gate and masked to [] on deny, like promptOverride and the holder — Session.worker reaches a Worker through a wider gate that admits any org member and survives a user leaving the App, so an ungated field would publish a team's repo list through any historical session. Masking to [] costs nothing, because empty already means "no affinity, never warn": a denied caller simply gets no warning, which is the safe direction for a signal that must never become a refusal. Same reason a worker with no role, a role with no definition, or an unreadable system memory all answer [] rather than erroring.
promptOverride String Per-worker individuality layered over the agent's template prompt.
memoryId ID The worker-scoped working memory in the App's container (null until provisioned).
heldByUserId ID #1050 — the human HOLDING this name, or null when nobody does. A name is held by a person, not by a live session: expiry, a reap, or a closed chat session never free it, and only an explicit release does. This is what separates the two meanings of "taken" — HELD is whose name it is, while a live session is only ever a question about your own worker. WORKING-STATE field, behind the worker read gate and masked to null on deny — like promptOverride and memoryId, and NOT like name/role/urn. Session.worker reaches a Worker row through a wider gate that admits any org member and survives a user leaving the App, so an ungated holder would let a former member read current staffing off a historical session.
heldAt DateTime When the current hold was taken. Null exactly when heldByUserId is — and likewise masked on deny. #1034: "taken" here is the HOLD's timestamp, unrelated to `WORKER_TAKEN` (a live worker session) and to `TeamRoleName.taken` (a name allocated in the register). Three senses of one word across two types.
hasLiveSession Boolean hadron-cli#487 — whether anyone is DRIVING this name right now. Not availability: availability is the HOLD above (`cor:agt:020:09`), and an ended or idle session never frees a name. This says only whether someone is mid-stint, which is the question a coordinator asks about a name already theirs. Conflating the two is the CLI bug this field exists for. #1114 — COMPUTED, never stored. It is "the session has not ended AND it was driven inside its type's idle window", not "a row is open". Nothing ends a session for inactivity any more, because silence is not evidence of abandonment — an agent used once a year is not abandoned in month two — so an open row proves only that nobody closed it. Deriving the answer at read time says exactly what the evidence supports; the previous design expressed the same judgement by ENDING the session, which wrote a guess into the record as a fact and destroyed the handoff that stint had not written yet. A worker that reads false is not gone: its session is untouched, and it reads true again the moment it is driven. Uses the SAME predicate startSession refuses WORKER_TAKEN on (`cor:agt:020:11`) — a reader must not be told a worker is busy that a binder can take, and since #1114 that is load-bearing rather than tidy: with nothing ending abandoned sessions, an `endedAt IS NULL` gate would refuse a bind forever and leave force as the only route. Nullable BECAUSE of the read gate, not because the answer is unknown: this is a working-state field like heldByUserId, masked to null on deny rather than answering false, which would state a fact the server never computed.
lastActiveAt DateTime hadron-cli#487 — when this worker was last DRIVEN, over every session it has ever had. Null for a worker nobody has ever bound, and that null is the point: a casting nobody picked up renders identically to one worked yesterday on every surface today, so a coordinator can dispatch into a channel no one reads and get no signal at all. The instant is the platform's one derivation of session activity — the greatest of startedAt, updatedAt, and the newest attributed usage event — so it agrees with the takeover prompt and with the idle window hasLiveSession is derived from (#1114: that window DESCRIBES a session, it no longer ends one). Masked to null on deny, like heldByUserId.
agent Agent The role-agent behind the casting.
app App The App whose staff this casting fills.
prompt String The worker's boot prompt, resolved: the agent's systemPrompt template with {{name}}/{{role}} bound, then promptOverride appended as its own paragraph. Null when the agent carries no template and the worker no override.
continuity WorkerContinuity #1029 — what a fresh binding of this worker inherits from the last one. Returned by startSession alongside the boot briefing (binding is briefing, so binding is handoff) and by the worker read, which is the recovery path after a context compaction — the reader who has most lost the handoff and least knows one exists. Working-state field: behind the worker read gate, masked to null on deny, like prompt/promptOverride/memoryId.
createdAt DateTime!
createdBy String
retiredAt DateTime Retirement instant — a retired worker stops authoring and takes no new sessions; the name stays reserved.
retiredBy String

WorkerContinuity

1029 — the continuity a worker carries between stints.

Handoffs are ordinary nodes in the worker's working memory, so a client walks the whole sequence through the node surface. This type exists for the ONE handoff that cannot be discovered: the newest, which a fresh session does not know to look for.

Field Argument Type Description
status WorkerContinuityStatus!
handoff Node The newest handoff node, or null when the worker has none. Present even when status is MISSING — a stale handoff is still the best available account of where this worker got to, and withholding it helps nobody.
previousSession WorkerContinuitySession The worker's most recent ENDED session, as a SANITIZED projection — the three facts continuity needs, and deliberately not the Session object. The worker read gate that admits this field is WIDER than the session read gate: an AppMember who is not an org member passes the first and fails the second. Returning a Session here would hand them another driver's transcriptPath, host, repo and summary through its field resolvers, which assume the outer query authorized them (#552 / #983).

WorkerContinuitySession

1029 — the previous stint, reduced to what a successor needs to know: which

session it was, when it ended, and whether the server reaped it. Not a Session, on purpose (see WorkerContinuity.previousSession).

Field Argument Type Description
id ID!
endedAt DateTime
autoExpiredAt DateTime Set when the #930 reaper ended it — nobody closed this stint deliberately.

WorkersPage

Field Argument Type Description
items [Worker!]!
total Int!

Inputs

AgentFilter

Filter for the uniform agents() list (#473). Clauses AND-combine.

Field Type Description
type AgentType
visibility AgentVisibility
ownedByMe Boolean #782 — true restricts the list to the caller's OWN user-owned (org-less) agents: organizationId IS NULL AND ownerUserId = the caller. For ALL callers INCLUDING platform ADMIN/OWNER (owner scope is never an admin-bypass surface), mirroring the owner-only personal/private memories slice. Org-less by definition, so orgId is not consulted. App-key callers (no user context) get an empty page. Powers the portal's "My agents".

AiServiceConfigFilter

Filter for the uniform aiServiceConfigs() list (#473) — the owning entity. Omitted entirely, the list is the platform ADMIN/OWNER's cross-owner view of every config; non-admin callers MUST filter by owner (org ADMIN of the owning org, mirroring the management-surface gate).

Field Type Description
ownerType AiConfigOwnerType!
ownerId ID! The owning entity — accepts ID or URN (HADRON_SERVER: ID only).

AiServiceConfigTestOverride

1378: values to test INSTEAD of the saved ones — e.g. an edited form before

it is saved. Never stored. Each is validated exactly as a write would be.

Field Type Description
provider String
model String
endpoint String Omitted = the saved endpoint; null or empty = the provider default.
apiStyle AiApiStyle Omitted = the saved style; null = the provider default.
apiKey String A key to test with instead of the stored one. Never stored, never echoed.
params JSON Edited params to test instead of the saved ones (#1413): validated exactly as a save validates them, including the params.baseUrl refusal, against the effective provider — before anything outbound. Used for the call, never stored. Omitted = the saved params; null = the edit clears them (the provider defaults). On the wire the probe exercises the effective provider/model/endpoint/key and (hard-capped at 16, per #1378's original ceiling pending a cost ruling) maxTokens, like its one-word prompt; the other knobs go through the same mapping every run uses (#1417): temperature, OpenAI reasoningEffort, Anthropic effort and Anthropic thinking (sent as adaptive on Claude 4.6 and newer models; not sent to older ones, which reject adaptive) are sent, and maxTokens is an output ceiling (min of the config's and the probe's cap); unrecognised keys are accepted but not forwarded. A write, or an edit under test, is refused a value outside a provider's known range (Anthropic and Bedrock temperature at most 1); a saved row is not re-judged. knobs on the result reports what each one did.

AppFilter

Filter for the uniform apps() list (#473). Clauses AND-combine.

Field Type Description
ownedByMe Boolean #782 — true restricts the list to the caller's OWN user-owned (org-less) apps: organizationId IS NULL AND ownerUserId = the caller. For ALL callers INCLUDING platform ADMIN/OWNER (owner scope is never an admin-bypass surface), mirroring the owner-only personal/private memories slice. Org-less by definition, so orgId is not consulted. App-key callers (no user context) get an empty page. Powers the portal's "My apps".

AssetFilter

Filter for the cross-memory assets() list. Every field narrows the caller's accessible set; none of them widens it.

Field Type Description
memoryRefs [ID!] Narrow to these memories (id or fully-qualified URN). Unreadable and unknown refs are silently dropped; a malformed URN is an error. An explicitly EMPTY list is the empty scope, never a fallback to the default scope.
mine Boolean Only the caller's own uploads. Never widens the gate.
mimeType String
scanStatus AssetScanStatus
includeDeleted Boolean

BeginAssetUploadInput

Field Type Description
agentUrn String! Accepts the entity's ID or URN.
filename String!
mimeType String!
sizeBytes Int!
description String
uploadIntent UploadIntent!
uploadRequestId String

BeginAssetUploadV2Input

v2 (spec 006-asset-upload-redesign): memory-addressed input. memoryId is required for the attached upload path. The staging path (memoryId omitted) is added in US4.

Field Type Description
memoryId ID! Memory reference. Accepts the entity's ID (CUID / 32-char hex) or its URN (per spec 007 ID-or-URN dispatch). URN inputs MUST be fully qualified (org:memory) per spec 022 — relative-form URNs are rejected as GraphQL errors with extensions.code "URN_NOT_QUALIFIED".
filename String!
mimeType String!
sizeBytes Int!
description String
uploadIntent UploadIntent!
uploadRequestId String

CalendarEventTimeInput

An event boundary for create/update: exactly one of dateTime (timed) or date (YYYY-MM-DD, all-day). A dateTime needs timeZone unless it carries its own UTC offset — a zone-less value is rejected, never guessed.

Field Type Description
dateTime String
date String
timeZone String

ChannelFilter

Filter for channels(): by App (its host memories' Channels) or by one host memory. Refs: ID or URN.

Field Type Description
appRef ID
memoryRef ID

CompleteAssetUploadInput

Field Type Description
uploadId ID!

CreateAgentScheduleInput

Field Type Description
appRef String! App the run executes as — ref: PK or fully-qualified URN.
name String!
cron String Recurring: 5-field cron expression, evaluated in timezone (default UTC). Provide exactly one of cron / runAt.
runAt String One-shot: run once at this ISO-8601 instant (#510). Provide exactly one of cron / runAt.
timezone String
entryNodeUrn String! Fully-qualified URN of the prompt node to run (D-2026-05-09-012/017).
agentRef String Agent — ref: PK or URN; must be installed in the App.
runAsSelf Boolean true: the run acts on behalf of YOU (required to reach your personal memories). v1 never delegates a third party (cor:agt:010:01).
eventData JSON Static payload merged into each run's template args.
policy JSON Trigger-layer allow-list { allow: [...] } — one link of the conjunctive chain (cor:acl:040).
aiConfigName String Named AI config for this trigger (spec-036 walk override).
enabled Boolean
scopeRef ID Spec 049 (D-2026-09-13-004): the scope this trigger's runs carry — a scope id, or a name resolved in the App's context (App › Agent › organization). Must be readable by you and resolvable in that App. Snapshotted onto each run at mint; omitted ⇒ the App's attached memories ('app').

CreateAgentWebhookInput

Field Type Description
appRef String! App — ref: PK or fully-qualified URN.
name String! Lowercase alphanumeric/dash, 1-64 chars — part of the URL.
entryNodeUrn String!
agentRef String Agent — ref: PK or URN; must be installed in the App.
runAsSelf Boolean
argsSchema JSON JSON Schema for POST args (stored in v1; enforcement is follow-on).
eventData JSON
policy JSON
aiConfigName String
enabled Boolean
scopeRef ID Spec 049 (D-2026-09-13-004): the scope this webhook's runs carry — a scope id, or a name resolved in the App's context. Snapshotted onto each run at mint; omitted ⇒ 'app'.

CreateAssetReferenceNodeInput

Attach an existing asset to the graph by creating a reference node that points at it.

The node is a nodeType: reference node whose data.asset carries the asset's id, urn, filename, mimeType and sizeBytes — the same shape hadron_store_file writes for run-created files, so a reader handles both origins identically. There is no schema-level Asset-to-Node link (cor:dmo:060:10 reserves it); the pointer is a soft reference, and Node.asset resolves to null once the asset is gone.

Requires READ access to the asset's holding memory and WRITE access to the memory the reference node lands in — which need not be the same memory.

Field Type Description
assetRef String! Asset id, or its URN — canonically hrn:asset:<root>:<mem...>:assets:<asset.id>. Parsing is Postel-liberal (assetIdFromRef): the id is whatever follows the LAST 'assets' atom, so every shape Asset.urn can emit is accepted, including the pre-#697 memory-prefixed <memory.urn>:assets:<asset.id> spelling and the other degraded fallback. A ref with no 'assets' atom is taken as a bare id.
nodeUrn String! Fully-qualified node URN for the reference node.
name String Node name. Defaults to the asset's filename.
description String Node description. Defaults to the asset's own description.

CreateChannelInput

Create a named Channel WITH its chat root, in one transaction. The host may be a memory of ANY class that you may write — the host memory's own audience decides who reads and posts (cor:acl:030:01), so a Channel in a private memory is private and one in an app memory is the App's. loc is the reserved address (e.g. chats:release) and must not overlap an existing Channel's; chats:team is every App's default and is created with the App.

There is no app-class precondition. This said there was until #1248 — the rule was retired by #1196 and the description outlived it, so every client mirroring this SDL published a refusal that cannot fire.

Creating at a DELETED Channel's address starts CLEAN (#1226). The address stays reserved by the tombstone, so a create here adopts it rather than inserting beside it — and the previous Channel's messages are tombstoned so the new room is empty. The allocator history remains, while the empty Channel's last-sequence and last-message watermarks reset. Its first new message therefore resumes above the old maximum, so an old read cursor cannot hide it. Expect a gap in the numbering, never inherited transcript or phantom attention.

Field Type Description
memoryRef ID!
loc String!
name String!
description String

CreateMemoryConfigTemplateInput

1325 part (c) — a new template. rules are validated as a whole before anything is written.

Field Type Description
name String!
description String
required Boolean
rules [CreateNodeRoleRuleInput!] Each role at most once (NODE_ROLE_RULE_EXISTS).

CreateNodeInput

Input for createNode. 'memoryId', 'loc', and 'name' are required; creating is create-only — a live node at (memoryId, loc) rejects with NodeLocConflictError (spec 039 Phase 0 D1/D4).

Field Type Description
id String Forced PK (importer/sync use case, e.g. a stable UUIDv7). Only ever a create-id here — never a selector (D2). Omit to mint.
memoryId String! Memory reference. Accepts the entity's ID (CUID / 32-char hex) or its URN (per spec 007 ID-or-URN dispatch). URN inputs MUST be fully qualified (org:memory) per spec 022 — relative-form URNs are rejected as GraphQL errors with extensions.code "URN_NOT_QUALIFIED".
loc String!
name String!
nodeType String
objectType String #725 — collection discriminator (e.g. "competitor" / "insight"). NULL for an ordinary node.
isRunnable Boolean Whether this node can be run as a task by hadron_run_task (cor:api:060).
role String #1201 — what this node is FOR (open string). NOT 'nodeType' (the platform-kind axis) and NOT 'objectType' (the collection discriminator). A GOVERNED value ('review', 'spec') requires that kind's own door; the generic createNode refuses it.
alias String
description String
abstract String Paragraph-length summary of this node — see Node.abstract for the surfacing contract. Optional. Empty + whitespace-only normalize to null. Cap is 2000 characters.
content String
contentType String MIME type of content (#476/#488, spec cor:cnv:010:01): 'text/markdown' (default) stores as-is; 'text/html' converts captured DOM to Markdown; 'application/pdf' extracts a PDF's text layer to Markdown (send 'content' as raw base64 — a PDF is binary; scanned/image-only PDFs error). The conversion runs before storage and fills properties.title from the extracted title when not supplied. Consumed at write time — never persisted. Any other value is rejected.
seq Int
tags [String!]
properties JSON
data JSON
ownerRepo String
llmModel String
aiAgent String
edges [NodeEdgeInput!] Outgoing edges to create with the node.
reason String Recorded on revision history only when a soft-deleted node is resurrected (a pure create snapshots nothing).

CreateNodeRoleRuleInput

1325 — a new rule, in a memory's config or in a template's rule list.

Task and description references are node IDs or URNs you can read; a node you cannot read is refused exactly like a missing one (NODE_NOT_FOUND), and a task reference must be a runnable task (NOT_A_TASK).

Field Type Description
role String! Lower-case letter/digit/dash segments joined by dots, 1-64 characters (INVALID_NODE_ROLE).
authorTaskRef ID
validationTaskRef ID
descriptionNodeRef ID
strictSubRoles Boolean
writers NodeRoleWriters
validateBy ContentValidator Needs a validation task.
enabled Boolean

CreateRegisterEntryInput

Field Type Description
ownerRef ID! An App or an organization (URN or id).
channelRef ID! A Channel: its id or its address (Channel.chatRootUrn, #1171).
attendeeRef ID A Worker or an Agent in the owner's context; omitted = every attendee there.
role RegisterRole Default BOTH.
mentionOnly Boolean
description String

CreateScopeInput

Exactly one of organizationRef / agentRef / appRef. memoryRefs is the ORDERED list (IDs or URNs); each must be readable by you — an unreadable memory reads as "no match", the same as a nonexistent one.

Field Type Description
name String!
description String
organizationRef ID
agentRef ID
appRef ID
memoryRefs [ID!]!

DashboardWidgetInput

One widget in a setDashboardWidgets selection; grid position is the array order.

Field Type Description
id ID When set and owned by the caller, update that placement in place (id survives the save); otherwise create a new one (#788).
type DashboardWidgetType!
span Int! Columns to span (1-12) on the 12-col grid; clamped server-side.
rowSpan Int! Rows to span (1-4); clamped server-side (#788).
config JSON Widget-instance settings; shape is widget-defined (#788).

EdgeFilter

Filter for the uniform edges() list (#473). Clauses AND-combine and are intersected with the caller's readable memories. memoryId / sourceNodeId / targetNodeId each accept an ID or a fully-qualified URN; an unresolvable ref yields an empty page (consistent with the no-disclosure posture).

Field Type Description
memoryId ID Confine to edges whose (denormalized, source-side) memory is this one.
sourceNodeId ID Edges leaving this node.
targetNodeId ID Edges arriving at this node.
isRunnable Boolean
includeDeleted Boolean Include soft-deleted edges (default false).

EdgeInput

Field Type Description
sourceLoc String!
targetLoc String!
name String Relationship name (was 'label'). Optional — loc is the identity.
loc String Explicit edge loc. Omit to derive <sourceLoc>:<name>:<targetLoc>.
description String
isRunnable Boolean

GrantCreditsInput

Field Type Description
orgRef String!
amountMicroCents String! Positive micro-cents, base-10 integer string.
expiresAt String Grant expiry policy field (no enforcement in Phase 0).
sourceType String Idempotency + provenance pair (both or neither), e.g. ('stripe-checkout', <session id>). (sourceType, sourceId) is unique across the ledger - re-granting the same pair returns the EXISTING entry instead of double-crediting (at-least-once webhook safety).
sourceId String
note String

ImpersonationSessionFilter

Field Type Description
activeOnly Boolean Only sessions that are live right now (endedAt null, unexpired).

ImportNodeInput

Input for importNode (#457). Target: 'nodeUrn' XOR ('memoryId' + 'loc') — the URN may name a not-yet-existing node (import creates it). Source: exactly one of 'url' | 'content'.

Field Type Description
memoryId String Target memory (ID or fully-qualified URN), with 'loc'. XOR with 'nodeUrn'.
loc String Target loc within 'memoryId'. XOR with 'nodeUrn'.
nodeUrn String Fully-qualified node URN naming the target (existing or new). XOR with memoryId+loc.
url String Source URL (http/https) — fetched server-side, SSRF-guarded, WITHOUT user credentials.
content String Inline source content (e.g. the Web Clipper's authenticated-DOM capture). XOR with 'url'.
contentType String MIME type of 'content': text/html (DEFAULT here — unlike createNode) converts to Markdown at the write seam; text/markdown stores as-is; application/pdf extracts a PDF's text layer to Markdown ('content' must be raw base64 — a PDF is binary; scanned/image-only PDFs error). Ignored on the url path (always HTML).
name String Display name. Default: the extracted page/article/document title; on a re-import of an existing node the current name is preserved; a fresh create without either falls back to the loc leaf.
nodeType String Default 'webpage' (or 'info' when contentType is application/pdf).
properties JSON Merged provenance metadata; the server sets properties.url on the url path when absent (properties.title is filled from the extracted title by the write seam).
taskRef ID Task node to run against the imported node once stored (#528) — PK or fully-qualified URN. Presence triggers a MANUAL app run; the result envelope is FETCH_PENDING + jobId (poll appRun(ref:)). The imported node's URN is passed to the task as eventData.importedNodeUrn.
taskArgs JSON Extra template args merged into the task run's eventData (only meaningful with taskRef).
appRef ID App to run the task under — PK or URN. Falls back to the caller's active App. Only meaningful with taskRef.

MemoryConfigTemplateFilter

Filter for memoryConfigTemplates(). Narrows the templates you manage; it never widens them.

Field Type Description
ownerType MemoryConfigTemplateOwnerType
ownerRef ID ID or URN of the owner (with ownerType). An owner you do not manage matches nothing.

MemoryFilter

Filter for the uniform memories() list (#473). All clauses AND-combine and only ever narrow the caller's accessible scope — with two documented slice SELECTIONS, each switching which readable set the list draws from (never widening access beyond what the caller may already read): - visibility: PUBLIC switches from the caller's own union (org-owned + org-subscribed + own user-owned) to the public marketplace slice (every PUBLIC memory — the old publicMemories query). - sharedWithMe: true switches to the memories shared WITH the caller via MemoryShare (the caller is a grantee) — the portal's Memories-shared-with-me tab.

Field Type Description
memoryClasses [MemoryClass!] Restrict to exactly these classes. Omitted, the noisy agent system class is hidden by default; pass it explicitly to surface system memories.
visibility MemoryVisibility Restrict by visibility. PUBLIC selects the marketplace slice (see above).
sharedWithMe Boolean true selects the distinct set of memories shared WITH the caller via MemoryShare (the caller is a grantee) — the portal's Memories-shared-with-me tab. This is its own slice, NOT part of the caller's owned/org union: a grantee is never their own grantor, so it excludes owned memories. App-key callers get an empty page (sharing is a user-to-user concept). See Memory.myShare for the per-row grantor + role.
ownedByMe Boolean #1179 — true restricts the list to the caller's OWN ORG-LESS memories: organizationId IS NULL AND the caller owns it. For ALL callers INCLUDING platform ADMIN/OWNER (owner scope is never an admin-bypass surface), the same intent as AgentFilter.ownedByMe and AppFilter.ownedByMe. Org-less by definition, so orgId is not consulted. App-key callers (no user context) and impersonated sessions get an empty page. Powers the portal's "My memories". "Owns it" is PER CLASS, because Memory has two independent owner columns and the read gates already split on exactly this line (canReadMemoryRecord and the MCP canReadMemory both branch on personal/private FIRST and admit userId there, falling through to ownerUserId for every other class): - personal / private → userId, the STRICT owner; - every other class → ownerUserId, the spec-047 user TENANT owner. This is where the memory filter is NECESSARILY wider than its Agent and App siblings, which have one owner column each and can say ownerUserId alone. Keying memories on ownerUserId alone would hand a personal memory to its tenant owner when the two columns differ — legal rows, since nothing constrains them to agree — while memory(ref:) refuses that same caller. Keying on the class pair alone was the ORIGINAL defect: it was only ever a proxy for ownership, correct while the two coincided (#1176). So a knowledge-class memory under a user root (hrn:mem:holger:holgers-gear) is in this slice, and an ORG-owned personal memory is not (it has an organizationId) — which matches what the portal's class-plus-client-side-repair query already returned. Composes by AND like every other clause, so it only ever narrows: combined with visibility PUBLIC it is empty by construction (#758 excludes user-owned rows from the marketplace slice), and combined with sharedWithMe it narrows that slice rather than replacing it.

MergeMemoriesInput

Field Type Description
source String! Source memory — ID or fully-qualified URN. Emptied into 'target'.
target String! Target memory — ID or fully-qualified URN. The surviving memory.
include [NodeMergeField!] Field strategy for source nodes whose loc collides with an existing target node (folded via the mergeNodes rules). Omit (or null) = every mergeable field. Nodes with no target counterpart move over unchanged (loc preserved).
deleteSourceMem Boolean Delete the (now-empty) source memory after a successful merge.

MergeNodesInput

Field Type Description
source String! Source node — ID or fully-qualified URN. Folded into 'target'.
target String! Target node — ID or fully-qualified URN. The surviving node.
include [NodeMergeField!] Fields to merge. Omit (or null) to merge every mergeable field.
deleteSource Boolean Hard-delete the source node after a successful merge.

MergeUsersInput

Field Type Description
source String! Source user — ID, bare handle, or fully-qualified user URN. Folded into 'target' and soft-deleted.
target String! Target user — ID, bare handle, or fully-qualified user URN. The surviving user; target wins non-combinable conflicts while roles preserve the strongest live entitlement.

MintActionTicketsInput

Field Type Description
orgRef String! Organization — ref: PK or URN.
appRef String Scope tickets to one App (ref: PK or URN); omit for org-wide.
action String! v1: comm.outbound only.
count Int!
note String Ledger legibility — why these tickets exist.
expiresAt String

NodeEdgeInput

Outgoing edge on a node, used in NodeInput.edges

Field Type Description
targetId String! Reference to the target node. Accepts a node ID, a full URN (hrn:node:<memory-urn>:<loc>), a memory-prefixed loc (<memory-urn>:<loc>), or a short loc resolved within the source node's memory.
name String Relationship name (was 'label'). Optional — loc is the identity.
loc String Explicit edge loc. Omit to derive <sourceLoc>:<name>:<targetLoc>.
description String
isRunnable Boolean

NodeFilter

Structured, unscored, AND-combined filter context for findNodes (cor:api:090:02).

Field Type Description
memoryIds [ID!] Memory scope — a mix of memory IDs and fully-qualified URNs; intersected with the caller's access.
memoryClasses [MemoryClass!] Restrict by memory class. SYSTEM is excluded by default; passing this (or naming a system memory in memoryIds) overrides that.
nodeType String
objectType String #725 — collection facet: only nodes whose objectType equals this (e.g. "competitor").
tags [String!]
isRunnable Boolean
role String #1322 — role-family filter. 'spec' matches 'spec' and 'spec.*' on a dot boundary, never 'special' / 'specification'.
locPrefix String
createdBy String
updatedAfter String
updatedBefore String
includeDeleted Boolean Include soft-deleted nodes (default false).
where NodeWhereInput Structured predicate over the node's properties/data JSONB (#719).

NodeInput

Legacy bulk-import node shape retained for replaceSubtree compatibility. replaceSubtree now refuses without writing; single-node writes use CreateNodeInput / UpdateNodeInput (spec 039 Phase 0 D1).

Field Type Description
id String Forced PK (importer-supplied UUIDv7 from node.yaml, stable across moves). Omit to mint.
memoryId String! Memory reference. Accepts the entity's ID (CUID / 32-char hex) or its URN (per spec 007 ID-or-URN dispatch). URN inputs MUST be fully qualified (org:memory) per spec 022 — relative-form URNs are rejected as GraphQL errors with extensions.code "URN_NOT_QUALIFIED".
nodeType String
objectType String #725 — collection discriminator (e.g. "competitor" / "insight"). NULL for an ordinary node.
isRunnable Boolean Whether this node can be run as a task by hadron_run_task (cor:api:060).
loc String!
name String!
alias String
description String
abstract String Paragraph-length summary of this node — see Node.abstract for the surfacing contract (hadron_get_node opt-in via contentScope; hadron_find_nodes preview ships in US2). Optional. Omit to preserve; null to clear; string to replace. Empty + whitespace-only normalize to null. Cap is 2000 characters.
content String
seq Int
tags [String!]
properties JSON
data JSON
ownerRepo String
llmModel String
aiAgent String
edges [NodeEdgeInput!] Replace all outgoing edges when provided (omit to leave edges unchanged)
reason String Why this change was made — recorded on the revision-history snapshot (NodeRevision.revLabel, #620; historically it squatted in editedBy), mirroring hadron_update_node's reason arg so CLI and MCP edits leave equally-traceable history. Only an update snapshots a prior revision, so reason has no effect on a pure create.

NodePropertySort

Order findNodes by the value at a properties/data JSON path (#719).

Reuses the NodeWhere leaf addressing: path into field (properties|data), typed by as (text|number|datetime|boolean). number/datetime route through the same DB guards, so a missing or unparseable value sorts LAST regardless of direction; loc ascending breaks ties for stable pagination. Overrides the sort enum when present. On vector/hybrid modes it re-orders the retrieved candidate window (the ranking runs against the vector index, not the JSONB), like the sort enum does.

Field Type Description
path [String!]! Path of object keys into the column, e.g. ['stage'].
field NodeWhereColumn JSONB column (default properties).
as NodeWhereCast Value typing before comparison (default text).
direction SortDirection Direction (default asc).

NodeWhereInput

A recursive structured predicate over a node's properties/data JSONB (#719).

A node is EITHER a branch (exactly one of and/or/not) OR a leaf (a path plus exactly one operator). Leaf values are JSON scalars, bound as parameters; path segments are identifier-validated. datetime/number comparisons route through DB guard functions so unparseable data drops the row (never a 500). Bounded: depth ≤ 4, ≤ 32 leaves, path ≤ 8 segments — a malformed/oversized tree is BAD_USER_INPUT. Composes with every mode (keyword/regex/vector/hybrid) and the no-query browse: for lexical modes + browse the predicate is applied in-query; for vector/hybrid it post-filters the ranked candidate set (rank order preserved).

Field Type Description
and [NodeWhereInput!]
or [NodeWhereInput!]
not NodeWhereInput
field NodeWhereColumn JSONB column (default properties).
path [String!] Path of object keys into the column, e.g. ['capturedAt'].
as NodeWhereCast Value typing for the comparison (default text).
eq JSON
ne JSON
in [JSON!]
lt JSON
lte JSON
gt JSON
gte JSON
between [JSON!]
exists Boolean
contains JSON jsonb containment (@>) of the JSON value at the path.

OrganizationFilter

Filter for the uniform organizations() list (#473).

Field Type Description
memberOnly Boolean true restricts the list to the caller's own memberships — even for platform ADMIN/OWNER, whose unscoped reach otherwise spans every live org; the old myOrganizations semantics.

PublicAgentFilter

Filter for the publicAgents() marketplace slice (#551). ONLY 'type' narrows. Deliberately a SEPARATE, narrower input from AgentFilter: the slice is PUBLIC by definition (so 'visibility' is meaningless) and a PUBLIC agent is never user-owned (user-owned agents are strictly PERSONAL, spec 047), so an 'ownedByMe' here would always be empty — rejecting it at the schema keeps a client from applying the filter uniformly and silently getting cross-org marketplace agents (#782 Codex review).

Field Type Description
type AgentType

RegisterDefaultsInput

Field Type Description
mentionOnly Boolean

RegisterEntryFilter

Filter for registerEntries(). Refs: ID or URN.

Field Type Description
ownerRef ID An App or an organization.
channelRef ID A Channel: its id or its address (Channel.chatRootUrn). A ref naming nothing matches nothing.
attendeeRef ID A Worker or an Agent.

ScopeFilter

Filter for the uniform scopes() list. Clauses AND-combine and only narrow the caller's readable set. ownerType + ownerRef name one owner; appRef selects the App's whole CONTEXT (its own scopes, its installed Agents', its organization's) — what a bare name would resolve against there.

Field Type Description
ownerType ScopeOwnerType
ownerRef ID ID or URN of the owner (with ownerType).
appRef ID ID or URN of an App — the resolution context.
name String

SearchReplaceInNodesInput

Bulk literal/regex search-and-replace across selected nodes.

Selection is a union — at least one of 'nodeIds' or 'memoryIds' is required: - nodeIds: explicit nodes (IDs or fully-qualified URNs). - memoryIds: every live node in those memories (IDs or URNs). - prefix: further restrict the memoryIds set to the node at 'prefix' plus its descendants, matched on colon loc-path boundaries (so 'auth' matches 'auth' and 'auth:tokens' but not 'authoring'). Requires 'memoryIds' — loc is only unique within a memory.

Matching is literal substring by default; set 'regex: true' to treat 'oldText' as a RegExp source (and 'newText' as a replacement pattern with dollar-sign backrefs). 'caseInsensitive' toggles case folding. Matching is always global.

Set 'dryRun: true' to get per-node/per-field match counts WITHOUT writing.

Field Type Description
oldText String! Text (literal) or RegExp source (regex mode) to find. Must be non-empty.
newText String! Replacement. May be empty (deletes the matched text). Regex mode honors dollar-sign backrefs.
nodeIds [ID!] Explicit node IDs or fully-qualified URNs.
memoryIds [ID!] Memory IDs or URNs — selects every live node in each.
prefix String Loc-prefix filter applied within 'memoryIds' (parent loc + descendants).
fields [NodeTextField!]! Which text fields to search. At least one required.
caseInsensitive Boolean
regex Boolean Treat oldText as a RegExp source and newText as a replacement pattern.
dryRun Boolean When true, report matches without writing anything.
reason String Why this change was made — recorded on each changed node's revision-history snapshot (NodeRevision.revLabel, #620; historically it squatted in editedBy), mirroring hadron_update_node's reason arg.

SelectedSkillFilePlanInput

Plan installed individual skill files for explicit task refs; plugin exports use skillPlan.

Field Type Description
nodes [String!]! Nonempty node IDs or fully qualified node URNs. Relative locs are refused.
host String
files [SkillFileFactsInput!]
force Boolean

SessionInput

Field Type Description
id String!
type String
repo String
branch String
prNumber Int
customerId String
language String
llmModel String
parentSessionId String
prevSessionId String
transcriptPath String #928 coding-session provenance.
host String
tool String
agentRef String #928 / cor:api:140: the role-Agent driving this session, as a PK or URN. Resolves to Session.agentId. The caller must be able to READ the agent (PUBLIC, or owner / org member) - a session is never attributed to an agent the caller cannot see. Usually omitted with workerRef, which derives it from the casting.
workerRef String #974 / cor:agt:020:03: the Worker (named casting) this session works as. Three forms resolve, told apart by SHAPE (never by trying each in turn, so a typo'd URN can't silently fall through to a roster search): - the worker's NAME ("Iris") — #990. Unique per App, case-insensitively, matched with the DATABASE's lower(), the expression workers_app_name_uniq indexes. Requires App context (appRef, an App-key credential, or an MCP active-App selection): a name means nothing without one, so a nameless-App call refuses SESSION_WORKER_NAME_NEEDS_APP rather than guessing across the caller's Apps. Gated on the STAFF READ GATE before the lookup runs — resolving a name discloses roster membership, which the id/URN forms never do. Which refusal you get depends on WHERE the denial happens, and clients should not assume one code covers both: a caller who supplied appRef has already been checked against the same participant predicate, so an outsider is refused FORBIDDEN there, before any name is read. Only a denial at the in-branch staff gate — reachable when the App came from an App-key credential rather than appRef — is masked as WORKER_NOT_FOUND, indistinguishable from an App with no such worker. - the worker's URN (hrn:worker:<root>:<app-slug>:<slug>, #991); the URN's leaf is the derived slug, never the display name (cor:agt:020:02). - the worker's id. Resolves to Session.workerId, and stamps Session.agentId with the worker's role-agent. The worker must belong to the session's App: with appRef (or an App-key credential) it must match the worker's App; without one, the worker's App becomes the session's, behind the same membership gate as appRef. A retired worker refuses (WORKER_RETIRED). #940: a worker with an ACTIVE session refuses WORKER_TAKEN — extensions carry workerId, sessionId, lastDriver, lastSeenAt, everything the takeover prompt needs — unless force is true (informed takeover, cor:agt:020:03: show who last drove it, proceed only on explicit override, never silently).
force Boolean #940: take over a worker whose binding would otherwise refuse WORKER_TAKEN. Only meaningful with workerRef. Clients must surface the WORKER_TAKEN payload (who last drove it, when) before retrying with force - that IS the informed-takeover contract; force exists so the override is explicit, never the default.
plan String The session's stated intent (Session.plan) - what this session is about. MCP's hadron_start_session description lands here.
appRef String #943 / cor:api:140: the App this session is a unit of work for, as a PK or URN. Resolves to Session.appId - the pivot that was previously stamped ONLY from an App-key credential, which made a user-started session structurally unable to satisfy the team-chat authorship gate (cor:agt:020:04 condition b, SESSION_NOT_IN_APP). The caller must be a member of that App: an AppMember at any role, an org member with CONTRIBUTOR+ on its owning org, or the owner of a user-owned App. Unknown or deleted App: BAD_USER_INPUT; non-member: FORBIDDEN. An App-key credential wins - if the caller authenticated as an App and appRef names a DIFFERENT App, the call is refused (SESSION_APP_MISMATCH). Uninstalled Apps differ by ref form, because App URNs are only unique among ACTIVE rows (spec 021 FR-027: one live App plus N uninstalled history rows may share a URN). By URN an uninstalled App is simply not found - BAD_USER_INPUT - since there is no single row the URN names; by PK it is found and refused APP_UNINSTALLED.

SetAgentAssetUploadEnabledInput

Field Type Description
agentUrn String! Agent reference. Accepts the entity's ID (CUID / 32-char hex) or its URN (per spec 007 ID-or-URN dispatch). URN inputs MUST be fully qualified (org:agent) per spec 022 — relative-form URNs are rejected as GraphQL errors with extensions.code "URN_NOT_QUALIFIED".
enabled Boolean!

SetAiModelPriceInput

Field Type Description
provider String!
model String!
tier Int Pricing tier 1-3; defaults to 1.
inputMicroCentsPerMTok String!
outputMicroCentsPerMTok String!
markupBasisPoints Int Per-tier debit markup in basis points (2500 = 25%); omit to fall back to the CREDIT_MARKUP_PERCENT env default.
effectiveAt String Defaults to now. Appends a new effective-dated row - never mutates.

SetQuotaLimitInput

Field Type Description
orgRef String! Org - ref: PK or URN.
appRef String Optional app scope - ref: PK or URN; must belong to the org. Omit to set the org-level limit.
action String! run.execute, llm.tokens, sms.send, or email.send.
limit Int! Non-negative integer; 0 pauses the scope (every launch denies QUOTA_EXCEEDED).

SkillFileFactsInput

What the client saw of ONE file on disk. The server never reads the caller's disk.

Field Type Description
dirName String! The directory the skill occupies — compared against the node's stored name.
nodeId String The id value from the provenance header; omit on a header generation that predates it.
sourceUrn String The source URN from the provenance header.
headerHash String The hash value from the header; omit for a legacy header that carries none.
fileHash String The hash the client computed over the file itself.
revision Int The positive rev=N value from provenance. Null means an older artifact omitted it.
parseFailed Boolean True when the file exists and could not be parsed.
hasExtraFrontmatter Boolean True when the file carries frontmatter keys the renderer never writes.

SkillPlanInput

Field Type Description
intent SkillPlanIntent!
memories [String!] Memory refs for LINT/STATUS only. EXPORT refuses this field, even an empty list.
scopeRef ID Persisted Scope ID for a narrowed EXPORT. Omit for unscoped export; explicit null refuses. Membership is resolved afresh for each call.
host String Target host, named by its D12 declaration key verbatim: claudeSkill (the default) or codexSkill. One host per call; the plan reads that host's declaration and applies its limits, and the retired top-level skill/claudeSkill keys alias to claudeSkill only. An unknown host is refused with BAD_USER_INPUT naming the supported hosts, never judged as another host and never answered with an empty plan.
files [SkillFileFactsInput!] What the client found on disk. Empty for lint, which touches no disk.
force Boolean With EXPORT only, explicitly override local-edit or missing-provenance-ID protection for this invocation.

SpecSearchReplaceInput

Replace text in explicit nodes of one DRAFT spec corpus. A dry run returns plan; apply must present that exact plan and refuses if any selected row, protection, match, or corpus state changed. Minted corpora are never edited through this door. Generic replacement keeps its governed-node protections.

Field Type Description
memoryRef ID!
nodeIds [ID!]!
oldText String!
newText String!
fields [SpecTextField!]!
caseInsensitive Boolean
regex Boolean
wordBoundary Boolean Literal mode only; matches the CLI's default ASCII word-boundary behavior.
dryRun Boolean
expectedPlan String Required for apply; copied from the exact dry-run response.
maxNodesChanged Int Refuse the entire apply when more nodes would change.
reason String

TriggerAppRunInput

Field Type Description
appRef String! App — ref: PK or fully-qualified URN.
entryNodeUrn String!
eventData JSON
runAsSelf Boolean
aiConfigName String

UpdateAgentScheduleInput

Field Type Description
name String
cron String Providing cron switches the schedule to recurring (clears runAt).
runAt String Providing runAt switches the schedule to one-shot (clears cron). At most one of cron / runAt per call.
timezone String
entryNodeUrn String
agentRef String Agent — ref: PK or URN; must be installed in the App.
runAsSelf Boolean
eventData JSON
policy JSON
aiConfigName String
enabled Boolean
scopeRef ID Spec 049: set the trigger's scope (id or name in the App's context); an empty string clears it back to 'app'.

UpdateChannelInput

Field Type Description
name String
description String

UpdateMemoryConfigTemplateInput

1325 part (c) — change a template. An omitted field is unchanged;

description null clears it. rules, when given, REPLACES the template's rules in one transaction.

Field Type Description
name String
description String
required Boolean
rules [CreateNodeRoleRuleInput!]

UpdateNodeInput

Input for updateNode. Identify the target by 'id' (PK or fully-qualified node URN) XOR ('memoryId' + 'loc') — both selectors, or neither, is an error (spec 039 Phase 0 D2). updateNode never creates and never moves (D3). Every content field is optional: omitted = preserved (D4).

Field Type Description
id String The node to change: PK (CUID / 32-char hex) or fully-qualified node URN (hrn:node:<root>:<memory>:<loc>). XOR with memoryId+loc.
memoryId String Selector, with 'loc': the target's memory (ID or fully-qualified URN). XOR with 'id'.
loc String Selector, with 'memoryId': the target's current loc. Not writable here — moveNode relocates.
expectedRevision Int Optional optimistic-concurrency precondition (#937). When supplied, the update is applied only while the node's current live revision still equals this positive integer. A mismatch refuses atomically with NODE_WRITE_CONFLICT; omitting it or passing null preserves last-write-wins compatibility. Read the baseline from Node.revision.
name String Omit to preserve (D4).
nodeType String
objectType String #725 — collection discriminator (e.g. "competitor"). Omit to preserve; null to clear.
isRunnable Boolean Whether this node can be run as a task by hadron_run_task (cor:api:060). Omit to preserve.
role String #1201 — what this node is FOR (open string). NOT 'nodeType' and NOT 'objectType' — see Node.role. Omit to preserve; null to clear. A GOVERNED value requires that kind's own door, and so does editing a node that ALREADY carries one: the gate reads the resulting state, not the change.
alias String
description String
abstract String Paragraph-length summary of this node — see Node.abstract for the surfacing contract. Omit to preserve; null to clear; string to replace. Empty + whitespace-only normalize to null. Cap is 2000 characters.
content String
contentType String MIME type of content (#476/#488, spec cor:cnv:010:01): 'text/markdown' (default) stores as-is; 'text/html' converts captured DOM to Markdown; 'application/pdf' extracts a PDF's text layer to Markdown (send 'content' as raw base64 — a PDF is binary; scanned/image-only PDFs error). The conversion runs before storage and fills properties.title from the extracted title when not supplied. Consumed at write time — never persisted. Any other value is rejected.
seq Int
tags [String!] Omit to preserve existing tags (issue #235); supply to replace the set.
properties JSON
data JSON
ownerRepo String
llmModel String
aiAgent String
edges [NodeEdgeInput!] Replace all outgoing edges when provided (omit to leave edges unchanged)
reason String Why this change was made — recorded on the revision-history snapshot (NodeRevision.revLabel, #620; historically it squatted in editedBy), mirroring hadron_update_node's reason arg so CLI and MCP edits leave equally-traceable history.

UpdateNodeRoleRuleInput

1325 — change a rule. An omitted field is unchanged. A reference given as

null or an empty string CLEARS it; validateBy null clears it. The role itself is the rule's identity and cannot be changed: delete and re-create instead.

Field Type Description
authorTaskRef ID
validationTaskRef ID
descriptionNodeRef ID
strictSubRoles Boolean
writers NodeRoleWriters
validateBy ContentValidator
enabled Boolean

UpdateRegisterEntryInput

Field Type Description
role RegisterRole
mentionOnly Boolean
description String

UpdateScopeInput

All optional. memoryRefs, when passed, REPLACES the list (it is the new order).

Field Type Description
name String
description String
memoryRefs [ID!]

UsageEventInput

Field Type Description
type String!
nodeLoc String
actionArgs JSON
sessionId String
model String
tokensIn Int
tokensOut Int

UserFilter

Filter for the uniform users() list (#473). For a non-platform-admin caller 'query' is REQUIRED (a blank/omitted query returns an empty page) and matching is enumeration-safe per cor:acl:070:02: public identifiers (handle, githubUsername) match by substring, email by exact equality, and name only where the viewer may see it (self/admin/co-member). Platform ADMIN/OWNER may omit 'query' for the full user list.

Field Type Description
query String

Enums

AccessSource

issue #323: where a single grant in an EffectiveAccess result comes from.

Value Description
PLATFORM_ROLE Platform ADMIN/OWNER.
ORG_ROLE ADMIN/OWNER (or, for org-readable knowledge, any Role) of the owning org.
MEMORY_PRINCIPAL personal/private memory owner (Memory.userId).
MEMORY_MEMBER group-class team membership row (MemoryMember).
MEMORY_SHARE personal-class cross-user grant (MemoryShare).
PRINCIPAL_GRANT individual action grant (PrincipalGrant, cor:acl:040:06) - clone/delete capabilities on top of the role bundle.
NODE_INHERITED node inherits its memory's access.
APP_MEMBER AppMember row.
AGENT_SUBSCRIPTION active AgentSubscription, or an org-level AgentOrgGrant the user inherits.
AICONFIG_OWNER admin of the AI config's owning entity.
PUBLIC_VISIBILITY resource is publicly / universally visible.

AgentType

Value Description
ASSISTANT
CHATBOT

AgentVisibility

035-visibility-enum-cleanup: agents have their own visibility enum, decoupled from MemoryVisibility. PERSONAL is the creator-only draft state.

Value Description
PUBLIC
ORGANIZATION
PERSONAL

AiApiStyle

1373: the wire protocol of an OpenAI-compatible provider.

Value Description
CHAT_COMPLETIONS POST {endpoint}/chat/completions — what most OpenAI-compatible hosts serve (the glm default).
RESPONSES POST {endpoint}/responses — OpenAI's Responses API (the openai default).

AiConfigOwnerType

036-ai-service-config: which entity owns an AiServiceConfig. Exactly one owner per config (DB-enforced).

Value Description
HADRON_SERVER
ORGANIZATION
APP
AGENT

AiConfigTestKeySource

1378: which key a test used; NONE when there was none to use.

Value Description
SAVED
OVERRIDE
PLATFORM A HADRON_SERVER config with no stored key: the server's own credential for that provider served the call, as it serves a run (#1385).
NONE

AiConfigTestValueSource

1378: where a tested value came from.

Value Description
SAVED
OVERRIDE

AppMembershipRole

Value Description
OWNER
ADMIN
CONTRIBUTOR
READER

AppMemoryKind

Value Description
shared
user
none

AppRunSortField

Orderable columns on the appRuns list (#833). Reuses the shared SortDirection enum. Default createdAt desc — existing callers depend on newest-first.

startedAt / finishedAt are NULLABLE (a PENDING run has neither); nulls sort LAST in both directions, so unstarted runs never crowd out the rows an operator asked to see. Every sort carries an id tiebreak so offset paging is deterministic.

Duration (finishedAt - startedAt) is deliberately absent: it has no stored column and Prisma cannot order by a computed expression, so it needs a generated column of its own - tracked separately rather than half-shipped.

Value Description
createdAt
startedAt
finishedAt
status

AppRunStatus

Value Description
PENDING
RUNNING
COMPLETED
FAILED
TIMED_OUT
CANCELLED

AppRunTriggerKind

Value Description
SCHEDULE
WEBHOOK
MANUAL
INTEGRATION
SPAWN

AppType

Value Description
WORKSTATION
CHATBOT
AGENT
AUTOMATION
CLOUD
IOT

AssetScanStatus

Value Description
PENDING
CLEAN
BLOCKED

CalendarEventResponse

Provider-neutral attendee response. NONE = invited, not yet answered.

Value Description
ACCEPTED
DECLINED
TENTATIVE
NONE

ChannelKind

What kind of feed a Channel is. CHAT today; BLOCKERS / TASKS when built.

Value Description
CHAT

ChatScope

017-chat-visibility-and-personal-memory: a chat is created in one of two tiers, chosen by the user at chat creation and immutable thereafter. shared chats live in the App's app-class memory. private chats live in the user's personal-class memory, OR in a knowledge-class one when the caller's org has no matching App install — validateChatScopeInvariants accepts both, so a client must not assume the memory class from the scope.

Value Description
private
shared

ContentValidator

Value Description
AGENT
PLATFORM

CorpusState

1447 — a spec corpus's lifecycle state. DRAFT becomes MINTED once, one-way.

Value Description
DRAFT
MINTED

CreateUserPermission

Value Description
DENY
EXPLICIT
IMPLICIT

DashboardWidgetType

772 — the kinds of widget placeable on a user's /app/dashboard grid.

Value Description
MEMORY_STATS { memoryCount } — how many memories the caller OWNS: the memories(filter: { ownedByMe: true }) slice, not everything they can read (#1231). Stated here because the population is the contract: a client that has to guess it writes its own count, and then the two disagree.
AGENT_STATS { agentCount } — how many agents the caller OWNS: the agents(filter: { ownedByMe: true }) slice (#1231). Owner-scoped for every caller, platform admins included.
USAGE_ACTIVITY { windowDays, buckets } — the caller's usage activity over a rolling window.

DriveExportFormat

Text representation a drive export can request.

Value Description
MARKDOWN
TEXT
HTML

DriveFileKind

Provider-neutral file kind (native document types vs plain files).

Value Description
FOLDER
DOCUMENT
SPREADSHEET
PRESENTATION
FILE

EmailFlag

Provider-neutral flag state (spec 002) — Graph's tri-state mapped to a neutral vocabulary so a future Gmail tool can map its own subset.

Value Description
FLAGGED
NONE
COMPLETE

EmbeddingSource

033-rag-vector-retrieval: what a memory's vector index is built from.

Value Description
abstract
contentChunks
both

FindNodesMode

Retrieval modes for findNodes (cor:api:090:01). keyword is stemmed FTS; vector/hybrid need the memory's vector index; regex is a POSIX pattern match.

Value Description
keyword
vector
hybrid
regex

IdentifyUserMethod

Value Description
USER_ID
SECRET

ImportNodeStatus

importNode outcome. Sync v1 always returns STORED with the stored node; FETCH_PENDING + jobId are RESERVED for the future async url path (same API shape, no breaking change when it lands).

Value Description
STORED
FETCH_PENDING

MemoryClass

Value Description
system
app
knowledge
personal
group 023-app-shape US4: team-shared memory with symmetric membership. Governed by a list of MemoryMember rows (each with reader/writer/ owner role) — no single owner field on Memory. Closes the Company Brain gap that wasn't covered by the four legacy classes.
private Single-owner, owner-only memory — no MemoryShare path (not shareable), no ADMIN/OWNER bypass. Spec 034 (hadron-server #242) made it user-creatable via createMemory: free-standing (no app/agent) or app-scoped. May opt into encrypt-at-rest (the private CLASS marks it; visibility is NULL), but the encryption implementation itself is a deferred follow-up — do NOT rely on at-rest encryption for secret material yet.

MemoryConfigApplyOutcome

1334 — what applying a template did (or, on a dry run, would do) for one role.

Value Description
APPLIED The memory had no rule for the role; the template's was copied in (locked if the template is required).
SKIPPED_CONFLICT The memory already had a rule for the role, and a non-required template never overrides one.
REPLACED The memory's rule for the role was overwritten by a required template's, and is now locked.
SKIPPED_LOCKED A required template met a LOCKED rule that you have no authority to move; the rule stands.

MemoryConfigTemplateOwnerType

1325 part (c) — which entity owns a MemoryConfigTemplate. Exactly one per template.

Value Description
HADRON_SERVER
ORGANIZATION
USER
APP

MemoryMemberRole

023-app-shape US4: role on a MemoryMember row. Symmetric team membership for group-class memory. - reader: read access. - writer: read + write (the member can add/edit/delete nodes within the memory). - owner: read + write + management — add/remove other members, change roles, delete the Memory itself. Subject to the last-owner protection rule (FR-038): the platform refuses to remove or demote the sole remaining owner; the path to fully empty a group memory is to delete it.

Value Description
reader
writer
owner

MemoryShareRole

023-app-shape US3: role on a MemoryShare row. Asymmetric grant for personal-class memory. - reader: read access only. - writer: read + write (the grantee can add/edit/delete nodes within the memory).

Value Description
reader
writer

MemoryTransferHandling

Value Description
CARRY
REFUSE
REMOVE
CREATE

MemoryValidationCheck

One health check the validator runs (#819).

Value Description
BROKEN_REF An edge whose target node is missing OR soft-deleted. A hard-deleted target cannot occur - Edge.target is FK-enforced with onDelete: Cascade - so in practice this means a dangling edge to a tombstone.
SPARSE A node with no description, content, or abstract.
STALE_ABSTRACT The abstract no longer reflects current content (spec 032 FR-011).
EMBED_FAILED embedding_failed_at is set - a retryable failure awaiting its backoff, or the permanent class (deterministic 4xx, dimension mismatch, #206 encrypted-no-plaintext) recoverable via retryFailedEmbeddings (spec 033 / #882). Such a node is absent from vector search, which from outside looks identical to no hits matching.
SCHEMA objectType/properties violate the memory declared schema (#725).

MemoryVisibility

035-visibility-enum-cleanup: meaningful only for knowledge (PUBLIC/ORGANIZATION) and group (GROUP); null otherwise. PERSONAL/PRIVATE were dropped — privacy is the personal/private memory CLASS now.

Value Description
PUBLIC
ORGANIZATION
GROUP 023-app-shape US4: team-shared visibility. Bound bidirectionally to MemoryClass.group by the chk_memory_group_visibility CHECK constraint — a memory has class=group iff visibility=GROUP.

NodeAuthorshipKind

Value Description
TASK
HUMAN

NodeContentValidationOutcome

Value Description
PASS
FAIL
ERROR

NodeContentValidationState

Value Description
UNVALIDATED
STALE
PASS
FAIL
ERROR

NodeExportEncoding

How the rendered payload is encoded. TEXT for MD/JSON; BASE64 for PDF.

Value Description
TEXT
BASE64

NodeExportFormat

Single-node export (#386). MD/JSON are the canonical, round-trippable files; PDF is a presentation render (content only) produced by the internal hadrontool-pdf service and delivered BASE64. HTML remains reserved.

Value Description
MD
JSON
PDF

NodeExportSection

Selectable sections for a presentation (full: false) export.

Value Description
METADATA
CONTENT
ABSTRACT
DATA
PROPERTIES
EDGES

NodeField

Node fields a lexical (keyword/regex) query matches and weights. Weight-mask, NOT a hard filter: naming a subset zeroes the excluded fields in scoring but does not strictly exclude them from matching (cor:api:090:03). Ignored by mode:vector (the index source is a per-memory config, not a query param).

Value Description
name
alias
loc
tags
description
abstract
content
properties
data

NodeMergeField

A node field that mergeNodes can fold from the source into the target.

Value Description
CONTENT Concatenate source content after target content (blank-line separated).
ABSTRACT Concatenate source abstract after target abstract (2000-char cap enforced).
DESCRIPTION Concatenate source description after target description.
TAGS Union the tag sets (target order first, then new source tags).
DATA Shallow-merge the encrypted 'data' JSON; target wins on key collisions.
PROPERTIES Shallow-merge the 'properties' JSON; target wins on key collisions.
EDGES Re-point the source's incoming and outgoing edges onto the target. Each re-homed edge's loc is recomputed from its new endpoints (deriveEdgeLoc); a re-homed edge whose recomputed (loc, memoryId) collides with one the target already holds is dropped rather than duplicated — so an equivalent edge with an endpoint-derived loc is de-duplicated — as are self-loops.

NodeMoveLinkStatus

Value Description
MISSING
RETARGETED
AMBIGUOUS

NodeRoleRuleRefState

1325 — the state of one rule reference, next to its URN and id. NONE: not

configured. OK: the node is live and you can read it; its id is given, and its URN too unless the node's memory has a legacy URN that cannot address it (#697), when the URN is null and the id is the ref to use. BROKEN: configured, but the node was deleted, so operations relying on the rule are refused until a manager repoints it (fail-closed). UNREADABLE: the node exists but you may not read it, so its URN and id are withheld.

Value Description
NONE
OK
BROKEN
UNREADABLE

NodeRoleWriters

Value Description
ALL
OWNER
ADMIN

NodeSort

Result ordering for findNodes. Default relevance; the rest suppress scoring (browse order).

Value Description
relevance
loc
seq
updatedAt

NodeTextField

Text-bearing Node fields that searchReplaceInNodes may rewrite. JSON fields (data, properties) and the structural 'loc' are intentionally excluded in v1.

Value Description
content
name
alias
description
abstract
tags

NodeWhereCast

How a NodeWhere leaf's extracted value is typed before comparison (#719). Default text.

Value Description
text
number
datetime
boolean

NodeWhereColumn

JSONB column a NodeWhere leaf reads (#719). Defaults to properties.

Value Description
properties
data

PrincipalType

The kind of principal a credential resolved to (issue #562). USER covers both JWT sessions and hdr_user_ personal API keys; APP is an hdr_app_ key. AGENT is reserved for a future agent-credential principal — no auth path produces it today.

Value Description
USER
APP
AGENT

RegisterDisclosure

S5 — why an attendee cannot act on a register row. Rendered identically by every consumer.

Value Description
HOST_MEMORY_NOT_READABLE
HOST_MEMORY_NOT_WRITABLE
CHANNEL_DELETED
DRIVER_NOT_PARTICIPANT
UNKNOWN_ACTOR
IMPERSONATION

RegisterNamedBy

Which attendee column the winning register row named.

Value Description
WORKER
AGENT
EVERYONE

RegisterOwnerType

A register row is owned by exactly one of the two; Agent-owned rows do not exist (plan §14).

Value Description
ORGANIZATION
APP

RegisterRole

What a register row declares: INTENT, never permission (D-2026-09-13-008).

Value Description
WATCH
POST
BOTH

Role

Value Description
OWNER
ADMIN
CONTRIBUTOR
READER

ScopeOwnerType

Which entity owns a Scope. Exactly one per scope (DB-enforced, the AiServiceConfig pattern).

Value Description
ORGANIZATION
AGENT
APP

SearchEntityType

Entity types the cross-entity global search (globalSearch) can return. Mirrors the SearchEntityType union in src/lib/global-search.

Value Description
organization
memory
node
agent
app
aiServiceConfig
user

SearchField

Fields globalSearch can match against. Omit fields to let the server smart-sniff the query shape (PK → URN → name); explicit fields override the sniff. pk is exact-match; the rest are case-insensitive substring. content (node bodies) is opt-in and skips encrypted memories.

Value Description
urn
name
description
pk
content

SearchGranularity

Result granularity for findNodes (cor:api:090). Spec 033.

  • node: one entry per matching node (default).
  • chunk: passage-level entries with offsets into the parent node's content (vector mode only; chunk hits collapse to node-level when granularity is node).
Value Description
node
chunk

SearchReplaceSkipReason

Value Description
OTHER_GOVERNED_KIND
PROTECTED_ADDRESS

SkillExportAction

A client-side filesystem action planned by the server. It does not claim that I/O occurred.

Value Description
WRITE
MOVE
REMOVE
SKIP
REFUSE
FAIL

SkillPlanIntent

What the caller intends to do with the plan. It decides the ACTION, never the class.

Value Description
LINT Corpus judgment only: findings, no files consulted, no bodies returned.
STATUS Classes and findings for files the client found. Never returns a body.
EXPORT Classes, findings, and the rendered body for anything the client should write.

SortDirection

Sort direction for a findNodes property-path sort (#719). Default asc.

Value Description
asc
desc

SpecMintFindingKind

1448 — why the mint report lists an item.

Value Description
PLACEHOLDER A reserved citation with no spec written yet.
UNRESOLVED_REFERENCE A reference to a spec that does not exist, or to a placeholder (rule: MISSING | PLACEHOLDER).
LINT A structural lint error (rule: nodetype-info | tag-spec | serialization-leak | duplicate-loc).
STALE_ABSTRACT An abstract written for an earlier body.

SpecReferenceKind

1449 — how a spec corpus reference was found.

Value Description
URN A node URN in the corpus text (name, description, abstract or content), including inside a URL.
EDGE A real edge from a corpus node.

SpecReferenceProblem

1449 — why a reference does not reach a written spec.

Value Description
MISSING No live node at the cited loc.
PLACEHOLDER The cited node is a placeholder: a reserved citation with no spec written yet.

SpecRewriteStatus

1449 — state of one node's reference rewrite in a renumber.

Value Description
PLANNED Dry run: this rewrite would be made.
REWRITTEN
UNCHANGED A concurrent edit removed the references (or the node) before the rewrite, so nothing was written.
FAILED The rewrite was refused or kept conflicting; error says why. The move itself stands.

SpecTextField

Only text fields a spec bulk edit may change.

Value Description
content
abstract

SubscriptionType

Value Description
READ_ONLY
READ_WRITE

SyncStatus

Value Description
PENDING
SYNCING
OK
ERROR

UploadIntent

Value Description
USER
AGENT_REQUEST

UsageActivityBucketSize

Bucket width for usageActivity (#796).

Value Description
DAY
WEEK
MONTH

WorkerContinuityStatus

1029. MISSING is deliberately not "no handoff": it means the PREVIOUS stint

did not write one, while handoff may still carry an older record. Silently presenting a stale handoff as current is the failure this distinction exists to prevent.

Value Description
FIRST_STINT No previous ended session — nothing to inherit, and that is not a gap.
CURRENT The previous session wrote the handoff returned here.
MISSING The previous session ended, or was auto-expired, without writing one.

Scalars

Boolean

The Boolean scalar type represents true or false.

DateTime

A timestamp string, e.g. '2026-07-30T12:34:56.789Z'.

Guaranteed parseable by Date.parse / new Date(). Values Hadron mints are ISO 8601 UTC; a value passed through from an external provider (calendar, mail, drive) keeps that provider's representation, which may carry a non-UTC offset — so compare instants, don't compare these as strings.

Issue #205: date fields used to be declared String and fed a Prisma Date, which graphql-js coerced through Date.valueOf() into epoch milliseconds ('1716943200000') — a value new Date() parses as Invalid Date. This scalar makes that shape unrepresentable, so no client needs to sniff the format.

Output-only today: date-valued arguments remain String.

Float

The Float scalar type represents signed double-precision fractional values as specified by IEEE 754.

ID

The ID scalar type represents a unique identifier, often used to refetch an object or as key for a cache. The ID type appears in a JSON response as a String; however, it is not intended to be human-readable. When expected as an input type, any string (such as "4") or integer (such as 4) input value will be accepted as an ID.

Int

The Int scalar type represents non-fractional signed whole numeric values. Int can represent values between -(2^31) and 2^31 - 1.

JSON

String

The String scalar type represents textual data, represented as UTF-8 character sequences. The String type is most often used by GraphQL to represent free-form human-readable text.