@rizom/brain 0.2.0-alpha.685 → 0.2.0-alpha.686

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/brain.js +27 -27
  2. package/dist/chunks/{app-jyfk7s9m.js → app-4c53djz8.js} +2 -2
  3. package/dist/chunks/{index-v7fe5rpz.js → index-6na1nq7g.js} +2 -2
  4. package/dist/chunks/{index-nj68nsdp.js → index-955w889r.js} +3 -3
  5. package/dist/chunks/{index-nj68nsdp.js.map → index-955w889r.js.map} +3 -3
  6. package/dist/chunks/{index-cn7cb3ve.js → index-ez8a274a.js} +3 -3
  7. package/dist/chunks/{index-cn7cb3ve.js.map → index-ez8a274a.js.map} +1 -1
  8. package/dist/chunks/{index-kyhqwbat.js → index-f33mthze.js} +2 -2
  9. package/dist/chunks/{index-27ea29ae.js → index-qte031xt.js} +2 -2
  10. package/dist/chunks/{index-7dx1zqyb.js → index-yhwj6qak.js} +2 -2
  11. package/dist/chunks/{linkedin-template-q4aankvw.js → linkedin-template-4we655mx.js} +2 -2
  12. package/dist/chunks/{package-gy1ank6b.js → package-pzecgmq7.js} +3 -3
  13. package/dist/chunks/{package-gy1ank6b.js.map → package-pzecgmq7.js.map} +1 -1
  14. package/dist/entities.js +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/interfaces.js +1 -1
  17. package/dist/model.js +25 -25
  18. package/dist/model.js.map +1 -1
  19. package/dist/services.js +1 -1
  20. package/dist/testing.js +1 -1
  21. package/dist/ui/studio-chunks/main-7zrz7m38.js.map +1 -1
  22. package/package.json +1 -1
  23. /package/dist/chunks/{app-jyfk7s9m.js.map → app-4c53djz8.js.map} +0 -0
  24. /package/dist/chunks/{index-v7fe5rpz.js.map → index-6na1nq7g.js.map} +0 -0
  25. /package/dist/chunks/{index-kyhqwbat.js.map → index-f33mthze.js.map} +0 -0
  26. /package/dist/chunks/{index-27ea29ae.js.map → index-qte031xt.js.map} +0 -0
  27. /package/dist/chunks/{index-7dx1zqyb.js.map → index-yhwj6qak.js.map} +0 -0
  28. /package/dist/chunks/{linkedin-template-q4aankvw.js.map → linkedin-template-4we655mx.js.map} +0 -0
@@ -353,7 +353,7 @@
353
353
  "import { defineChannel, type Channel } from \"@brains/contracts\";\nimport { z } from \"@brains/utils/zod\";\n\n/**\n * Internal shell coordination channel names.\n *\n * Keep names aligned with their lifecycle timing: registration coordination is\n * not the same as ready-state preparation.\n */\nexport const SYSTEM_CHANNELS = {\n /** Emitted after every plugin has completed registration. */\n pluginsRegistered: \"system:plugins:registered\",\n /** Emitted by directory-sync after startup import has completed. */\n initialSyncCompleted: \"sync:initial:completed\",\n /**\n * Emitted once startup content is in place — after a pending initial sync\n * completed or failed, or at boot when none was pending — and the shell's\n * ready-state defaults exist. Seed defaults here, never earlier.\n */\n startupContentSettled: \"system:startup-content:settled\",\n /** Emitted after shell boot completes and guarded shell APIs are available. */\n shellReady: \"system:shell:ready\",\n /**\n * Emitted after the brain character or anchor profile changed and the\n * shell's identity caches hold the new values. React to identity here,\n * not to the raw entity events, which race the cache refresh.\n */\n identityChanged: \"system:identity:changed\",\n} as const;\n\nexport type SystemChannelName =\n (typeof SYSTEM_CHANNELS)[keyof typeof SYSTEM_CHANNELS];\n\ntype PluginsRegisteredAnswerSchema = z.ZodObject<{\n initialSyncPending: z.ZodBoolean;\n}>;\n\n/**\n * A pluginsRegistered subscriber's answer when it has queued startup content\n * still to import; it then sends initialSyncCompleted once that settles.\n */\nexport const pluginsRegisteredAnswerSchema: PluginsRegisteredAnswerSchema =\n z.object({ initialSyncPending: z.boolean() });\n\nexport type PluginsRegisteredAnswer = z.output<\n typeof pluginsRegisteredAnswerSchema\n>;\n\ntype InitialSyncOutcomeSchema = z.ZodDiscriminatedUnion<\n [\n z.ZodObject<{ success: z.ZodLiteral<true> }>,\n z.ZodObject<{ success: z.ZodLiteral<false>; error: z.ZodString }>,\n ]\n>;\n\n/** How the startup import ended: imported, or failed with its reason. */\nexport const initialSyncOutcomeSchema: InitialSyncOutcomeSchema =\n z.discriminatedUnion(\"success\", [\n z.object({ success: z.literal(true) }),\n z.object({ success: z.literal(false), error: z.string() }),\n ]);\n\nexport type InitialSyncOutcome = z.output<typeof initialSyncOutcomeSchema>;\n\n/** Broadcast by directory-sync once its startup import has settled. */\nexport const initialSyncCompletedChannel: Channel<\n InitialSyncOutcome,\n unknown,\n typeof SYSTEM_CHANNELS.initialSyncCompleted\n> = defineChannel(\n SYSTEM_CHANNELS.initialSyncCompleted,\n initialSyncOutcomeSchema,\n);\n\ntype SystemTimestampSchema = z.ZodObject<{ timestamp: z.ZodString }>;\n\n/** When a lifecycle point was reached, and nothing else. */\nconst systemTimestampSchema: SystemTimestampSchema = z.object({\n timestamp: z.string(),\n});\n\nexport type SystemTimestamp = z.output<typeof systemTimestampSchema>;\n\n/** Broadcast by the shell once startup content is in place. */\nexport const startupContentSettledChannel: Channel<\n SystemTimestamp,\n unknown,\n typeof SYSTEM_CHANNELS.startupContentSettled\n> = defineChannel(SYSTEM_CHANNELS.startupContentSettled, systemTimestampSchema);\n\n/** Broadcast by the shell once boot completes. */\nexport const shellReadyChannel: Channel<\n SystemTimestamp,\n unknown,\n typeof SYSTEM_CHANNELS.shellReady\n> = defineChannel(SYSTEM_CHANNELS.shellReady, systemTimestampSchema);\n\ntype IdentityChangedSchema = z.ZodObject<{ entityType: z.ZodString }>;\n\n/** Which identity entity changed: the brain character or the anchor profile. */\nexport const identityChangedSchema: IdentityChangedSchema = z.object({\n entityType: z.string().min(1),\n});\n\nexport type IdentityChanged = z.output<typeof identityChangedSchema>;\n\n/** Broadcast by the shell once its identity caches hold the new values. */\nexport const identityChangedChannel: Channel<\n IdentityChanged,\n unknown,\n typeof SYSTEM_CHANNELS.identityChanged\n> = defineChannel(SYSTEM_CHANNELS.identityChanged, identityChangedSchema);\n",
354
354
  "import { mkdir, readdir, readFile, rm, stat, writeFile } from \"fs/promises\";\nimport { basename, dirname, join } from \"path\";\nimport { z } from \"@brains/utils/zod\";\nimport { getErrorMessage } from \"@brains/utils/error\";\nimport type { Logger } from \"@brains/utils/logger\";\nimport { isNotFoundError } from \"@brains/utils/predicates\";\n\n/** An upload id as the registry mints it: `upload-` and a UUID. */\nexport const runtimeUploadIdSource: string =\n \"upload-[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}\";\nexport const runtimeUploadIdPattern: RegExp = new RegExp(\n `^${runtimeUploadIdSource}$`,\n \"i\",\n);\nexport const defaultRuntimeUploadRetentionMs: number = 24 * 60 * 60 * 1000;\nexport const defaultRuntimeUploadMaxCount: number = 200;\n\nexport type RuntimeUploadRecord = z.output<typeof runtimeUploadRecordSchema>;\nexport type RuntimeUploadRef = RuntimeUploadRecord[\"ref\"];\n\nexport interface RuntimeUploadResponseBody extends RuntimeUploadRecord {\n url: string;\n downloadUrl: string;\n}\n\nexport interface RuntimeUploadScopeOptions {\n /** Filesystem namespace below the runtime data directory, e.g. \"upload\". */\n namespace: string;\n /** Ref discriminator exposed to clients, e.g. \"upload\". */\n refKind: string;\n /** HTTP route used to resolve stored uploads, e.g. \"/api/chat/uploads\". */\n routePath: string;\n retentionMs?: number | undefined;\n maxCount?: number | undefined;\n createId?: (() => string) | undefined;\n now?: (() => Date) | undefined;\n}\n\n/**\n * The runtime's shared upload store: the interfaces that accept a file write\n * it, and an `upload` reference anywhere in the brain points into it — a\n * create request's, a conversation's prior upload the agent restores, an\n * entity promoted from an upload. Writers and readers name it here, so they\n * cannot drift apart.\n */\nexport const sharedUploadScope: Readonly<RuntimeUploadScopeOptions> =\n Object.freeze({\n namespace: \"upload\",\n refKind: \"upload\",\n routePath: \"/api/chat/uploads\",\n });\n\nexport interface RuntimeUploadRegistryOptions {\n dataDir: string;\n /** Reports pruning faults, which must not fail a save but must be seen. */\n logger?: Logger | undefined;\n}\n\nexport interface SaveRuntimeUploadInput {\n filename: string;\n mediaType: string;\n content: Uint8Array;\n metadata?: Record<string, unknown> | undefined;\n}\n\nexport interface ResolvedRuntimeUpload {\n record: RuntimeUploadRecord;\n content: Uint8Array;\n}\n\nexport type RuntimeUploadStoreErrorCode =\n \"invalid_ref\" | \"not_found\" | \"invalid_metadata\";\n\nexport class RuntimeUploadStoreError extends Error {\n public readonly code: RuntimeUploadStoreErrorCode;\n constructor(code: RuntimeUploadStoreErrorCode, message: string) {\n super(message);\n this.code = code;\n this.name = \"RuntimeUploadStoreError\";\n }\n}\n\nconst runtimeUploadRecordSchema: z.ZodObject<{\n id: z.ZodString;\n ref: z.ZodObject<{ kind: z.ZodString; id: z.ZodString }>;\n filename: z.ZodString;\n mediaType: z.ZodString;\n sizeBytes: z.ZodNumber;\n createdAt: z.ZodString;\n metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;\n}> = z.object({\n id: z.string().regex(runtimeUploadIdPattern),\n ref: z.object({\n kind: z.string().min(1),\n id: z.string().regex(runtimeUploadIdPattern),\n }),\n filename: z.string().min(1),\n mediaType: z.string().min(1),\n sizeBytes: z.number().nonnegative(),\n createdAt: z.string().datetime(),\n metadata: z.record(z.string(), z.unknown()).optional(),\n});\n\n/**\n * The store surface consumers get, rather than the class that implements it.\n *\n * Declaring the class here made every consumer depend on its private fields\n * too, so nothing could stand in for a scoped store without asserting it was\n * one. The class satisfies this by construction.\n */\nexport type ScopedRuntimeUploadStore = Pick<\n RuntimeUploadStore,\n | \"save\"\n | \"read\"\n | \"readRecord\"\n | \"toResponseBody\"\n | \"prune\"\n | \"getUploadDir\"\n | \"remove\"\n>;\n\nexport interface IRuntimeUploadsNamespace {\n /** Create a scoped runtime upload store for an interface/plugin namespace. */\n scoped(options: RuntimeUploadScopeOptions): ScopedRuntimeUploadStore;\n /**\n * Delete a retired namespace's uploads directory, and nothing beside it.\n * Answers whether there was one to delete. The shared store is never\n * retired, and a name that is not a namespace is refused.\n */\n retire(namespace: string): Promise<boolean>;\n}\n\nconst uploadNamespacePattern = /^[a-z][a-z0-9-]*$/;\n\nexport function createRuntimeUploadsNamespace(\n registry: RuntimeUploadRegistry,\n): IRuntimeUploadsNamespace {\n return {\n scoped: (options: RuntimeUploadScopeOptions): RuntimeUploadStore =>\n registry.scoped(options),\n retire: (namespace: string): Promise<boolean> => registry.retire(namespace),\n };\n}\n\nexport function normalizeRuntimeUploadDataDir(contentDataDir: string): string {\n return basename(contentDataDir) === \"brain-data\"\n ? join(dirname(contentDataDir), \"data\")\n : contentDataDir;\n}\n\nexport class RuntimeUploadRegistry {\n private readonly dataDir: string;\n private readonly logger: Logger | undefined;\n\n constructor(options: RuntimeUploadRegistryOptions) {\n this.dataDir = normalizeRuntimeUploadDataDir(options.dataDir);\n this.logger = options.logger;\n }\n\n static createFresh(\n options: RuntimeUploadRegistryOptions,\n ): RuntimeUploadRegistry {\n return new RuntimeUploadRegistry(options);\n }\n\n scoped(options: RuntimeUploadScopeOptions): RuntimeUploadStore {\n return new RuntimeUploadStore({\n ...options,\n dataDir: this.dataDir,\n ...(this.logger !== undefined && { logger: this.logger }),\n });\n }\n\n async retire(namespace: string): Promise<boolean> {\n if (!uploadNamespacePattern.test(namespace)) {\n throw new Error(`\"${namespace}\" is not an upload namespace`);\n }\n if (namespace === sharedUploadScope.namespace) {\n throw new Error(\n `\"${namespace}\" is the runtime's shared upload store; the runtime's shared upload store is never retired`,\n );\n }\n const uploads = join(this.dataDir, namespace, \"uploads\");\n try {\n await stat(uploads);\n } catch (error) {\n if (isNotFoundError(error)) return false;\n throw error;\n }\n await rm(uploads, { recursive: true, force: true });\n return true;\n }\n}\n\nexport interface RuntimeUploadStoreOptions extends RuntimeUploadScopeOptions {\n dataDir: string;\n logger?: Logger | undefined;\n}\n\nexport class RuntimeUploadStore {\n private readonly options: RuntimeUploadStoreOptions;\n private readonly retentionMs: number;\n private readonly maxCount: number;\n private readonly createUploadId: () => string;\n private readonly getNow: () => Date;\n private readonly logger: Logger | undefined;\n\n constructor(options: RuntimeUploadStoreOptions) {\n this.options = options;\n this.logger = options.logger;\n this.retentionMs = options.retentionMs ?? defaultRuntimeUploadRetentionMs;\n this.maxCount = options.maxCount ?? defaultRuntimeUploadMaxCount;\n this.createUploadId =\n options.createId ?? ((): string => `upload-${crypto.randomUUID()}`);\n this.getNow = options.now ?? ((): Date => new Date());\n }\n\n async save(input: SaveRuntimeUploadInput): Promise<RuntimeUploadRecord> {\n const uploadId = this.createUploadId();\n this.assertValidUploadId(uploadId);\n\n const record: RuntimeUploadRecord = {\n id: uploadId,\n ref: { kind: this.options.refKind, id: uploadId },\n filename: input.filename,\n mediaType: input.mediaType,\n sizeBytes: input.content.byteLength,\n createdAt: this.getNow().toISOString(),\n ...(input.metadata !== undefined && { metadata: input.metadata }),\n };\n\n const uploadDir = this.getUploadDir(uploadId);\n await mkdir(uploadDir, { recursive: true });\n await writeFile(join(uploadDir, \"content\"), input.content);\n await writeFile(\n join(uploadDir, \"metadata.json\"),\n `${JSON.stringify(record, null, 2)}\\n`,\n \"utf8\",\n );\n // The upload is already on disk, so a pruning fault must not fail the\n // save. It must not vanish either: unpruned uploads grow without bound.\n await this.prune().catch((error: unknown) => {\n this.logger?.warn(\"Failed to prune runtime uploads\", {\n error: getErrorMessage(error),\n });\n });\n\n return record;\n }\n\n async remove(uploadId: string): Promise<void> {\n this.assertValidUploadId(uploadId);\n await rm(this.getUploadDir(uploadId), { recursive: true, force: true });\n }\n\n async read(uploadId: string): Promise<ResolvedRuntimeUpload> {\n const record = await this.readRecord(uploadId);\n try {\n const content = await readFile(\n join(this.getUploadDir(uploadId), \"content\"),\n );\n return { record, content };\n } catch (error) {\n // The metadata resolved, so the upload exists. A content file we cannot\n // read is a damaged upload, not an absent one.\n if (isNotFoundError(error)) {\n throw new RuntimeUploadStoreError(\"not_found\", \"Upload not found\");\n }\n throw new RuntimeUploadStoreError(\n \"invalid_metadata\",\n \"Upload content could not be read\",\n );\n }\n }\n\n async readRecord(uploadId: string): Promise<RuntimeUploadRecord> {\n this.assertValidUploadId(uploadId);\n\n try {\n const raw = await readFile(\n join(this.getUploadDir(uploadId), \"metadata.json\"),\n \"utf8\",\n );\n const parsed = runtimeUploadRecordSchema.safeParse(JSON.parse(raw));\n if (\n !parsed.success ||\n parsed.data.id !== uploadId ||\n parsed.data.ref.id !== uploadId ||\n parsed.data.ref.kind !== this.options.refKind\n ) {\n throw new RuntimeUploadStoreError(\n \"invalid_metadata\",\n \"Invalid upload metadata\",\n );\n }\n return parsed.data;\n } catch (error) {\n if (error instanceof RuntimeUploadStoreError) throw error;\n // No metadata file means no upload. A file that is present but will not\n // read or parse means a damaged one, and calling that \"not found\" sends\n // the caller looking for a problem they do not have.\n if (isNotFoundError(error)) {\n throw new RuntimeUploadStoreError(\"not_found\", \"Upload not found\");\n }\n throw new RuntimeUploadStoreError(\n \"invalid_metadata\",\n \"Upload metadata could not be read\",\n );\n }\n }\n\n toResponseBody(record: RuntimeUploadRecord): RuntimeUploadResponseBody {\n return {\n ...record,\n url: this.getUploadUrl(record.id),\n downloadUrl: this.getUploadUrl(record.id, true),\n };\n }\n\n /**\n * Drop uploads past the retention window or the count cap.\n *\n * Returns quietly only when the uploads directory does not exist yet — it is\n * created on first save. Every other failure raises: an unreadable directory\n * or a deletion that did not happen means uploads accumulate unbounded, and\n * a silent catch here made that indistinguishable from a successful prune.\n */\n async prune(): Promise<void> {\n const root = this.getUploadsRoot();\n\n let entries: string[];\n try {\n entries = await readdir(root);\n } catch (error) {\n if (isNotFoundError(error)) return;\n throw error;\n }\n\n const stats = await Promise.all(\n entries.map(async (entry) => {\n try {\n const info = await stat(join(root, entry));\n return info.isDirectory() ? { entry, mtimeMs: info.mtimeMs } : null;\n } catch (error) {\n // An upload directory can be removed between listing and stat.\n if (isNotFoundError(error)) return null;\n throw error;\n }\n }),\n );\n const dirs = stats\n .filter(\n (value): value is { entry: string; mtimeMs: number } => value !== null,\n )\n .sort((a, b) => b.mtimeMs - a.mtimeMs);\n const cutoff = this.getNow().getTime() - this.retentionMs;\n const stale = dirs.filter(\n (dir, index) => index >= this.maxCount || dir.mtimeMs < cutoff,\n );\n await Promise.all(\n stale.map((dir) =>\n rm(join(root, dir.entry), { recursive: true, force: true }),\n ),\n );\n }\n\n getUploadDir(uploadId: string): string {\n this.assertValidUploadId(uploadId);\n return join(this.getUploadsRoot(), uploadId);\n }\n\n private getUploadsRoot(): string {\n return join(this.options.dataDir, this.options.namespace, \"uploads\");\n }\n\n private getUploadUrl(uploadId: string, download = false): string {\n this.assertValidUploadId(uploadId);\n const encodedId = encodeURIComponent(uploadId);\n return `${this.options.routePath}?id=${encodedId}${download ? \"&download=1\" : \"\"}`;\n }\n\n private assertValidUploadId(uploadId: string): void {\n if (!runtimeUploadIdPattern.test(uploadId)) {\n throw new RuntimeUploadStoreError(\"invalid_ref\", \"Invalid upload ref\");\n }\n }\n}\n",
355
355
  "function assertPath(path){if(typeof path!==\"string\")throw TypeError(\"Path must be a string. Received \"+JSON.stringify(path))}function normalizeStringPosix(path,allowAboveRoot){var res=\"\",lastSegmentLength=0,lastSlash=-1,dots=0,code;for(var i=0;i<=path.length;++i){if(i<path.length)code=path.charCodeAt(i);else if(code===47)break;else code=47;if(code===47){if(lastSlash===i-1||dots===1);else if(lastSlash!==i-1&&dots===2){if(res.length<2||lastSegmentLength!==2||res.charCodeAt(res.length-1)!==46||res.charCodeAt(res.length-2)!==46){if(res.length>2){var lastSlashIndex=res.lastIndexOf(\"/\");if(lastSlashIndex!==res.length-1){if(lastSlashIndex===-1)res=\"\",lastSegmentLength=0;else res=res.slice(0,lastSlashIndex),lastSegmentLength=res.length-1-res.lastIndexOf(\"/\");lastSlash=i,dots=0;continue}}else if(res.length===2||res.length===1){res=\"\",lastSegmentLength=0,lastSlash=i,dots=0;continue}}if(allowAboveRoot){if(res.length>0)res+=\"/..\";else res=\"..\";lastSegmentLength=2}}else{if(res.length>0)res+=\"/\"+path.slice(lastSlash+1,i);else res=path.slice(lastSlash+1,i);lastSegmentLength=i-lastSlash-1}lastSlash=i,dots=0}else if(code===46&&dots!==-1)++dots;else dots=-1}return res}function _format(sep,pathObject){var dir=pathObject.dir||pathObject.root,base=pathObject.base||(pathObject.name||\"\")+(pathObject.ext||\"\");if(!dir)return base;if(dir===pathObject.root)return dir+base;return dir+sep+base}function resolve(){var resolvedPath=\"\",resolvedAbsolute=!1,cwd;for(var i=arguments.length-1;i>=-1&&!resolvedAbsolute;i--){var path;if(i>=0)path=arguments[i];else{if(cwd===void 0)cwd=process.cwd();path=cwd}if(assertPath(path),path.length===0)continue;resolvedPath=path+\"/\"+resolvedPath,resolvedAbsolute=path.charCodeAt(0)===47}if(resolvedPath=normalizeStringPosix(resolvedPath,!resolvedAbsolute),resolvedAbsolute)if(resolvedPath.length>0)return\"/\"+resolvedPath;else return\"/\";else if(resolvedPath.length>0)return resolvedPath;else return\".\"}function normalize(path){if(assertPath(path),path.length===0)return\".\";var isAbsolute=path.charCodeAt(0)===47,trailingSeparator=path.charCodeAt(path.length-1)===47;if(path=normalizeStringPosix(path,!isAbsolute),path.length===0&&!isAbsolute)path=\".\";if(path.length>0&&trailingSeparator)path+=\"/\";if(isAbsolute)return\"/\"+path;return path}function isAbsolute(path){return assertPath(path),path.length>0&&path.charCodeAt(0)===47}function join(){if(arguments.length===0)return\".\";var joined;for(var i=0;i<arguments.length;++i){var arg=arguments[i];if(assertPath(arg),arg.length>0)if(joined===void 0)joined=arg;else joined+=\"/\"+arg}if(joined===void 0)return\".\";return normalize(joined)}function relative(from,to){if(assertPath(from),assertPath(to),from===to)return\"\";if(from=resolve(from),to=resolve(to),from===to)return\"\";var fromStart=1;for(;fromStart<from.length;++fromStart)if(from.charCodeAt(fromStart)!==47)break;var fromEnd=from.length,fromLen=fromEnd-fromStart,toStart=1;for(;toStart<to.length;++toStart)if(to.charCodeAt(toStart)!==47)break;var toEnd=to.length,toLen=toEnd-toStart,length=fromLen<toLen?fromLen:toLen,lastCommonSep=-1,i=0;for(;i<=length;++i){if(i===length){if(toLen>length){if(to.charCodeAt(toStart+i)===47)return to.slice(toStart+i+1);else if(i===0)return to.slice(toStart+i)}else if(fromLen>length){if(from.charCodeAt(fromStart+i)===47)lastCommonSep=i;else if(i===0)lastCommonSep=0}break}var fromCode=from.charCodeAt(fromStart+i),toCode=to.charCodeAt(toStart+i);if(fromCode!==toCode)break;else if(fromCode===47)lastCommonSep=i}var out=\"\";for(i=fromStart+lastCommonSep+1;i<=fromEnd;++i)if(i===fromEnd||from.charCodeAt(i)===47)if(out.length===0)out+=\"..\";else out+=\"/..\";if(out.length>0)return out+to.slice(toStart+lastCommonSep);else{if(toStart+=lastCommonSep,to.charCodeAt(toStart)===47)++toStart;return to.slice(toStart)}}function _makeLong(path){return path}function dirname(path){if(assertPath(path),path.length===0)return\".\";var code=path.charCodeAt(0),hasRoot=code===47,end=-1,matchedSlash=!0;for(var i=path.length-1;i>=1;--i)if(code=path.charCodeAt(i),code===47){if(!matchedSlash){end=i;break}}else matchedSlash=!1;if(end===-1)return hasRoot?\"/\":\".\";if(hasRoot&&end===1)return\"//\";return path.slice(0,end)}function basename(path,ext){if(ext!==void 0&&typeof ext!==\"string\")throw TypeError('\"ext\" argument must be a string');assertPath(path);var start=0,end=-1,matchedSlash=!0,i;if(ext!==void 0&&ext.length>0&&ext.length<=path.length){if(ext.length===path.length&&ext===path)return\"\";var extIdx=ext.length-1,firstNonSlashEnd=-1;for(i=path.length-1;i>=0;--i){var code=path.charCodeAt(i);if(code===47){if(!matchedSlash){start=i+1;break}}else{if(firstNonSlashEnd===-1)matchedSlash=!1,firstNonSlashEnd=i+1;if(extIdx>=0)if(code===ext.charCodeAt(extIdx)){if(--extIdx===-1)end=i}else extIdx=-1,end=firstNonSlashEnd}}if(start===end)end=firstNonSlashEnd;else if(end===-1)end=path.length;return path.slice(start,end)}else{for(i=path.length-1;i>=0;--i)if(path.charCodeAt(i)===47){if(!matchedSlash){start=i+1;break}}else if(end===-1)matchedSlash=!1,end=i+1;if(end===-1)return\"\";return path.slice(start,end)}}function extname(path){assertPath(path);var startDot=-1,startPart=0,end=-1,matchedSlash=!0,preDotState=0;for(var i=path.length-1;i>=0;--i){var code=path.charCodeAt(i);if(code===47){if(!matchedSlash){startPart=i+1;break}continue}if(end===-1)matchedSlash=!1,end=i+1;if(code===46){if(startDot===-1)startDot=i;else if(preDotState!==1)preDotState=1}else if(startDot!==-1)preDotState=-1}if(startDot===-1||end===-1||preDotState===0||preDotState===1&&startDot===end-1&&startDot===startPart+1)return\"\";return path.slice(startDot,end)}function format(pathObject){if(pathObject===null||typeof pathObject!==\"object\")throw TypeError('The \"pathObject\" argument must be of type Object. Received type '+typeof pathObject);return _format(\"/\",pathObject)}function parse(path){assertPath(path);var ret={root:\"\",dir:\"\",base:\"\",ext:\"\",name:\"\"};if(path.length===0)return ret;var code=path.charCodeAt(0),isAbsolute2=code===47,start;if(isAbsolute2)ret.root=\"/\",start=1;else start=0;var startDot=-1,startPart=0,end=-1,matchedSlash=!0,i=path.length-1,preDotState=0;for(;i>=start;--i){if(code=path.charCodeAt(i),code===47){if(!matchedSlash){startPart=i+1;break}continue}if(end===-1)matchedSlash=!1,end=i+1;if(code===46){if(startDot===-1)startDot=i;else if(preDotState!==1)preDotState=1}else if(startDot!==-1)preDotState=-1}if(startDot===-1||end===-1||preDotState===0||preDotState===1&&startDot===end-1&&startDot===startPart+1){if(end!==-1)if(startPart===0&&isAbsolute2)ret.base=ret.name=path.slice(1,end);else ret.base=ret.name=path.slice(startPart,end)}else{if(startPart===0&&isAbsolute2)ret.name=path.slice(1,startDot),ret.base=path.slice(1,end);else ret.name=path.slice(startPart,startDot),ret.base=path.slice(startPart,end);ret.ext=path.slice(startDot,end)}if(startPart>0)ret.dir=path.slice(0,startPart-1);else if(isAbsolute2)ret.dir=\"/\";return ret}var sep=\"/\",delimiter=\":\",posix=((p)=>(p.posix=p,p))({resolve,normalize,isAbsolute,join,relative,_makeLong,dirname,basename,extname,format,parse,sep,delimiter,win32:null,posix:null});var path_default=posix;export{sep,resolve,relative,posix,parse,normalize,join,isAbsolute,format,extname,dirname,delimiter,path_default as default,basename,_makeLong};",
356
- "import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n authenticatedUserId,\n agentContextContract,\n generateExecuteContract,\n generateReportFailureContract,\n publishAssetRegisterContract,\n publishRegisterContract,\n siteBuildStagingContract,\n type SiteBuildStagingPayload,\n} from \"@brains/contracts\";\nimport {\n EntityWriteConflictError,\n createResultAttachmentSchema,\n internalFullScope,\n permissionToVisibilityScope,\n ProjectionJsonObjectSchema,\n applyVisibilityToMarkdown,\n generateFrontmatter,\n generateMarkdownWithFrontmatter,\n parseMarkdownWithFrontmatter,\n type BaseEntity,\n type ContentVisibility,\n type CreateCheckResult,\n type CreateExecutionContext,\n type CreateInput,\n type CreateInterceptionResult,\n type CreateInterceptor,\n type CreateResult,\n type DataSource,\n type EntityAdapter,\n type EntitySchema,\n type EntityTypeConfig,\n type ProjectionJsonObject,\n type ProjectionWriteIntent,\n} from \"@brains/entity-service\";\nimport type {\n AtprotoBlobRef,\n AtprotoProjection,\n AtprotoProjectionBuildInput,\n} from \"@brains/atproto-contracts\";\nimport { feedItemSchema, renderRssFeed } from \"@brains/site-composition\";\nimport {\n entityActionPolicyRuleSchema,\n type EntityActionRequiredLevel,\n type PermissionService,\n type Template,\n} from \"@brains/templates\";\nimport type {\n IShell,\n PluginCapabilities,\n PluginRegistrationContext,\n} from \"../interfaces\";\nimport { getErrorMessage } from \"@brains/utils/error\";\nimport { readMarkdownFrontmatter } from \"@brains/utils/markdown-frontmatter\";\nimport { isPlainRecord } from \"@brains/utils/predicates\";\nimport { definedFields } from \"@brains/utils/strip-undefined\";\nimport { z } from \"@brains/utils/zod\";\nimport { EntityPlugin, emptyEntityPluginConfigSchema } from \"./entity-plugin\";\nimport type { EntityPluginContext } from \"./context\";\nimport { entityTypeConfigFor } from \"./entity-definition-config\";\nimport { createEntityReader } from \"./entity-reader\";\nimport {\n contractJobOf,\n entityAnswersOf,\n bodyTemplateOf,\n ordinaryCreateOf,\n restatedMetadataOf,\n urlRouteOf,\n writesInDocumentOrderOf,\n frontmatterSchemaOf,\n builtProvider,\n queuedCreateOf,\n storedCreateOf,\n withEntityRuntime,\n type AnyEntityBinding,\n type EntityRuntimeBinding,\n type QueuedCreate,\n type EntityCreateCaller,\n type EvalGenerationOptions,\n type EvalGenerationResult,\n type QueuedAttribution,\n type QueuedPlaceholder,\n type StoredCreate,\n} from \"./entity-runtime\";\nimport { CallbackProgressReporter } from \"@brains/utils/progress\";\nimport { createUploadSaveHandler } from \"./upload-save\";\nimport { sharedUploadScope } from \"../service/upload-registry\";\nimport {\n admitPackageJob,\n createPackageEntityReader,\n packageReadScope,\n packageSelfCall,\n toolCaller,\n} from \"../service/job-authority\";\nimport {\n DeclarativeGenerationHandler,\n createEvalFixtures,\n createGenerationAI,\n contractJobHandler,\n entityJobHandler,\n type EntityJobWrites,\n type EntityRunAuthority,\n generationSource,\n packageEntityWrites,\n} from \"./declarative-jobs\";\nimport type { JobHandler, JobOptions } from \"@brains/job-queue\";\nimport { createPendingEntity } from \"./pending-ingestion\";\nimport {\n currentMetadata,\n entitySchema,\n parseDefinitionEntity,\n} from \"./entity-schema\";\nimport {\n defineProjectionRule,\n type ProjectionExecutionContext,\n type ProjectionInputContext,\n type ProjectionRule,\n} from \"./projection-rule\";\nimport type { InstalledPluginPackageMetadata } from \"../package-definition\";\nimport { createDeclarativeDashboardWidgetRegistration } from \"../operator/dashboard-widget-runtime\";\nimport type {\n AnyDashboardWidgetDefinition,\n BoundDashboardWidget,\n} from \"../operator/operator-definition-contract\";\nimport type {\n InboxActor,\n InboxItem,\n InboxItemDetail,\n InboxSource,\n} from \"../inbox-registry\";\nimport { SYSTEM_CHANNELS } from \"../system-channels\";\nimport { createDeclaredDataSource } from \"../public/entity-data-source\";\nimport type {\n AnyDataSourceDeclaration,\n AnyEntityDefinition,\n AnyEntityJobDeclaration,\n EntityCreateAllocation,\n EntityCreateContext,\n EntityCreateResolution,\n EntityCreateRoute,\n EntityCreateRouting,\n EntityAtprotoProjection,\n EntityDashboardBindingContext,\n EntityDashboardWidget,\n EntityInboxActionContext,\n EntityInboxChange,\n EntityInboxDeclaration,\n EntityInboxEdits,\n EntityReader,\n EntityRecordReader,\n DataSourceListOptions,\n EntityCreateUploadReader,\n EntityFeedDeclaration,\n MediaAttachmentContext,\n MediaEntityListRequest,\n MediaEntityReads,\n MediaEntityRequest,\n EntityOf,\n EntitySeedDefinition,\n EntitySeedTrigger,\n ProjectionDefinition,\n} from \"./entity-definition-contract\";\n\nexport { parseDefinitionEntity } from \"./entity-schema\";\n\n/**\n * Named seed triggers map to internal channels here, so the public surface\n * never names a channel.\n */\nconst SEED_TRIGGER_CHANNELS: Record<EntitySeedTrigger, string> = {\n \"startup-content-settled\": SYSTEM_CHANNELS.startupContentSettled,\n};\n\nconst rawFrontmatterSchema = z.record(z.string(), z.unknown());\n\n/**\n * Which route a create request takes, by how the caller expressed it: an\n * upload or attachment reference first, then content, then a prompt, then\n * a URL alone. A conversation message arrives as content.\n */\nfunction selectCreateRoute(\n routing: EntityCreateRouting,\n input: CreateInput,\n urlRoute?: EntityCreateRoute,\n): EntityCreateRoute | undefined {\n switch (input.from?.kind) {\n case \"entity-attachment\":\n return routing.fromAttachment;\n case \"upload\":\n return routing.fromUpload;\n default:\n if (input.content) return routing.fromContent;\n if (input.prompt) return routing.fromPrompt;\n // A first-party type may decide a create from a URL alone.\n return input.url ? urlRoute : undefined;\n }\n}\n\n/** A detached copy no route can change under the runtime. */\nfunction deepFrozen<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.values(value).forEach(deepFrozen);\n Object.freeze(value);\n }\n return value;\n}\n\n/** Whether a queued create's job is attributed to the caller. */\nfunction isAttributed(queued: QueuedCreate): boolean {\n const { attributed } = queued.job;\n return attributed !== undefined && attributed !== false;\n}\n\n/** Who asked for a create, as a first-party route sees them. */\nfunction createCaller(\n executionContext: CreateExecutionContext,\n): EntityCreateCaller {\n const userId = authenticatedUserId(executionContext);\n return Object.freeze({\n interfaceType: executionContext.interfaceType,\n actor: executionContext.actor,\n ...definedFields({\n userId,\n channelId: executionContext.channelId,\n channelName: executionContext.channelName,\n }),\n });\n}\n\n/** Who a create is attributed to in the store. */\nfunction eventContextOf(executionContext: CreateExecutionContext): {\n readonly actor: CreateExecutionContext[\"actor\"];\n readonly interfaceType: string;\n} {\n return {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n };\n}\n\n/**\n * A queued job's attribution to the caller who asked for it, as the entity\n * plugin classes queued their own jobs.\n */\nfunction attributionJobOptions(\n source: string,\n executionContext: CreateExecutionContext,\n attribution: QueuedAttribution = {},\n): JobOptions {\n return {\n source,\n metadata: {\n operationType: attribution.operationType ?? \"content_operations\",\n ...(attribution.channel === true &&\n executionContext.channelId !== undefined && {\n channelId: executionContext.channelId,\n }),\n interfaceType: executionContext.interfaceType,\n requestedByActor: executionContext.actor,\n ...(executionContext.actor.kind === \"user\"\n ? { requestedByUserId: executionContext.actor.userId }\n : {}),\n requestedByInterface: executionContext.interfaceType,\n },\n };\n}\n\nfunction createFailure(error: string): CreateResult {\n return { success: false, error };\n}\n\n/**\n * Public reads for a media provider. Each request is rebuilt from the fields\n * the contract names, so nothing a provider passes can widen visibility.\n */\nfunction mediaEntityReads(\n entityService: EntityPluginContext[\"entityService\"],\n): MediaEntityReads {\n function getEntity(request: MediaEntityRequest): Promise<BaseEntity | null>;\n function getEntity<T extends BaseEntity>(\n request: MediaEntityRequest,\n schema: EntitySchema<T>,\n ): Promise<T | null>;\n function getEntity<T extends BaseEntity>(\n request: MediaEntityRequest,\n schema?: EntitySchema<T>,\n ): Promise<BaseEntity | T | null> {\n const read = { entityType: request.entityType, id: request.id };\n return schema\n ? entityService.getEntity(read, schema)\n : entityService.getEntity(read);\n }\n function listEntities(request: MediaEntityListRequest): Promise<BaseEntity[]>;\n function listEntities<T extends BaseEntity>(\n request: MediaEntityListRequest,\n schema: EntitySchema<T>,\n ): Promise<T[]>;\n function listEntities<T extends BaseEntity>(\n request: MediaEntityListRequest,\n schema?: EntitySchema<T>,\n ): Promise<BaseEntity[] | T[]> {\n const read = {\n entityType: request.entityType,\n options: definedFields({ limit: request.options?.limit }),\n };\n return schema\n ? entityService.listEntities(read, schema)\n : entityService.listEntities(read);\n }\n return Object.freeze({ getEntity, listEntities });\n}\n\n/** The media members an attachment provider is built with, frozen and bound. */\nfunction mediaAttachmentContext(\n context: EntityPluginContext,\n): MediaAttachmentContext {\n const { identity, views } = context;\n return Object.freeze({\n domain: context.domain,\n themeCSS: context.themeCSS,\n identity: Object.freeze({ getProfile: () => identity.getProfile() }),\n entityService: mediaEntityReads(context.entityService),\n views: Object.freeze({ get: (name: string) => views.get(name) }),\n });\n}\n\n/** A feed path stays inside the build output: relative, no parent steps. */\nconst feedPathSchema = z\n .string()\n .min(1)\n .refine(\n (path) =>\n !path.startsWith(\"/\") &&\n !path.includes(\"\\\\\") &&\n path.split(\"/\").every((segment) => segment !== \"\" && segment !== \"..\"),\n { message: \"A feed path is relative to the build output\" },\n );\n\nconst projectionEnvelopeSchema = z.object({\n items: z.array(\n z.object({\n operation: z.enum([\"upsert\", \"delete\"]),\n id: z.string(),\n source: ProjectionJsonObjectSchema.optional(),\n }),\n ),\n});\n\nfunction encodeParts(\n definition: AnyEntityDefinition,\n input: {\n readonly content: string;\n readonly metadata: Record<string, unknown>;\n },\n): { readonly content: string; readonly frontmatter: Record<string, unknown> } {\n return definition.markdown?.encode\n ? definition.markdown.encode({\n content: input.content,\n metadata: definition.metadata.parse(\n currentMetadata(definition, input.metadata),\n ),\n })\n : { content: input.content, frontmatter: input.metadata };\n}\n\nfunction encodeEntityMarkdown(\n definition: AnyEntityDefinition,\n input: {\n readonly content: string;\n readonly metadata: Record<string, unknown>;\n },\n): string {\n const encoded = encodeParts(definition, input);\n return generateMarkdownWithFrontmatter(encoded.content, encoded.frontmatter);\n}\n\n/** A document's body; content with no frontmatter is a body already. */\nfunction documentBody(content: string): string {\n const document = readMarkdownFrontmatter(content);\n return isPlainRecord(document.data) && Object.keys(document.data).length > 0\n ? document.content\n : content;\n}\n\ntype DeclaredAdapter = EntityAdapter<\n EntityOf<AnyEntityDefinition>,\n Record<string, unknown>\n>;\n\n/** The fields a type writes: its metadata's and its header's, by name. */\nfunction writtenFields(\n metadata: z.ZodObject,\n header: z.ZodObject | undefined,\n): z.ZodObject {\n const names = new Set([\n ...Object.keys(metadata.shape),\n ...Object.keys(header?.shape ?? {}),\n ]);\n return z.object(\n Object.fromEntries([...names].map((name) => [name, z.unknown()])),\n );\n}\n\n/** How a text type's record is its markdown: its metadata is its frontmatter. */\nfunction markdownStorage(\n definition: AnyEntityDefinition,\n): Pick<\n DeclaredAdapter,\n | \"frontmatterSchema\"\n | \"writtenFrontmatterSchema\"\n | \"toMarkdown\"\n | \"fromMarkdown\"\n | \"parseFrontMatter\"\n | \"generateFrontMatter\"\n | \"metadataFromFrontmatter\"\n> {\n return {\n // The header its class declared, when it states one; else its metadata.\n frontmatterSchema: frontmatterSchemaOf(definition) ?? definition.metadata,\n // What it writes is still every field its metadata names, and the\n // header's: the serializer keeps from the source only what neither names.\n ...(frontmatterSchemaOf(definition) && {\n writtenFrontmatterSchema: writtenFields(\n definition.metadata,\n frontmatterSchemaOf(definition),\n ),\n }),\n // A whole document handed in, as an import or an editor writes one, had\n // its frontmatter decoded into the metadata already: the type writes the\n // body under its own frontmatter, in its own order, as an entity plugin\n // class's adapter did, and the serializer keeps after it the frontmatter\n // the type does not write.\n toMarkdown(entity): string {\n return encodeEntityMarkdown(definition, {\n ...entity,\n content: documentBody(entity.content),\n });\n },\n fromMarkdown(markdown): Partial<EntityOf<AnyEntityDefinition>> {\n const parsed = parseMarkdownWithFrontmatter(\n markdown,\n rawFrontmatterSchema,\n );\n const decoded = definition.markdown\n ? definition.markdown.decode({\n content: parsed.content,\n frontmatter: parsed.metadata,\n })\n : { content: parsed.content, metadata: parsed.metadata };\n return {\n // A singleton's document is its one record, whose id is the type,\n // as a class adapter read it; a writer that names none, such as\n // Studio creating a missing one, creates it there.\n ...(definition.singleton === true && { id: definition.type }),\n content: decoded.content,\n metadata: definition.metadata.parse(\n currentMetadata(definition, decoded.metadata),\n ),\n };\n },\n parseFrontMatter: (markdown, schemaToParse) =>\n parseMarkdownWithFrontmatter(markdown, schemaToParse).metadata,\n generateFrontMatter(entity): string {\n return generateFrontmatter(encodeParts(definition, entity).frontmatter);\n },\n // Metadata is the frontmatter, decoded by fromMarkdown, so a frontmatter\n // write on an update carries into metadata.\n metadataFromFrontmatter: true,\n };\n}\n\n/**\n * How a binary type's record is its content: stored unchanged, with no\n * frontmatter, its metadata kept beside it. Decoding says what the bytes\n * say, which an import merges with the metadata handed beside the file; the\n * schema checks the record when it is stored.\n */\nfunction binaryStorage(\n definition: AnyEntityDefinition,\n): Pick<\n DeclaredAdapter,\n \"toMarkdown\" | \"fromMarkdown\" | \"parseFrontMatter\" | \"generateFrontMatter\"\n> {\n return {\n toMarkdown: (entity): string => entity.content,\n fromMarkdown(content): Partial<EntityOf<AnyEntityDefinition>> {\n const decoded = definition.markdown?.decode({ content, frontmatter: {} });\n const entityType = definition.type;\n if (!decoded) return { entityType, content };\n // Bytes that say nothing leave the metadata to what is handed beside\n // them, as an adapter that recovers none returns none.\n const said = { ...decoded.metadata };\n return Object.keys(said).length > 0\n ? { entityType, content: decoded.content, metadata: said }\n : { entityType, content: decoded.content };\n },\n parseFrontMatter: (_content, schemaToParse) => schemaToParse.parse({}),\n generateFrontMatter: (): string => \"\",\n };\n}\n\nfunction entityAdapter(definition: AnyEntityDefinition): DeclaredAdapter {\n const schema = entitySchema(definition);\n const stub = definition.stub;\n return {\n entityType: definition.type,\n purpose: definition.purpose,\n schema,\n ...(definition.config?.binaryStorage === undefined\n ? markdownStorage(definition)\n : binaryStorage(definition)),\n // A first-party type that restates its metadata stores it as the\n // document it wrote states it.\n extractMetadata: (entity) =>\n restatedMetadataOf(definition)?.(entity.metadata) ?? entity.metadata,\n getBodyTemplate: () => bodyTemplateOf(definition),\n ...(writesInDocumentOrderOf(definition) && { writesInDocumentOrder: true }),\n // Left off when undeclared: system_generate reads its absence as \"this\n // type does not support queued generation\" and says so.\n ...(stub && {\n buildStub: (input: {\n id: string;\n title: string;\n }): { content: string; metadata: Record<string, unknown> } => {\n const built = stub(input);\n return { content: built.content, metadata: { ...built.metadata } };\n },\n }),\n // Each flag is left off when undeclared: a type that never declared one\n // reads the same as a type written before the flag existed.\n ...(definition.singleton === true && { isSingleton: true }),\n ...(definition.hasBody !== undefined && { hasBody: definition.hasBody }),\n ...(definition.coverImage === true && { supportsCoverImage: true }),\n ...(definition.create?.fields && {\n createFieldsSchema: definition.create.fields,\n }),\n ...(definition.displayTitle && {\n displayTitle: (entity): string | undefined =>\n displayTitle(definition, entity),\n }),\n };\n}\n\n/**\n * The declared label for a stored record. Metadata the schema refuses gets\n * no label rather than an error, so one bad record cannot break a listing;\n * the caller falls back to the record's own title or id.\n */\nfunction displayTitle(\n definition: AnyEntityDefinition,\n entity: { readonly content: string; readonly metadata: unknown },\n): string | undefined {\n const metadata = definition.metadata.safeParse(\n currentMetadata(definition, entity.metadata),\n );\n return metadata.success\n ? definition.displayTitle?.({\n content: entity.content,\n metadata: metadata.data,\n })\n : undefined;\n}\n\nexport async function deriveProjectionUpserts(\n projection: ProjectionDefinition,\n rawSource: ProjectionJsonObject,\n signal: AbortSignal,\n): Promise<ProjectionWriteIntent[]> {\n const source = entitySchema(projection.source).parse(rawSource);\n const intents: ProjectionWriteIntent[] = [];\n await projection.project({\n source,\n signal,\n target: {\n async upsert(entity): Promise<void> {\n const metadata = ProjectionJsonObjectSchema.parse(\n projection.target.metadata.parse(entity.metadata),\n );\n // A derived entry is as private as what it was derived from, unless\n // the projection says otherwise.\n const visibility = entity.visibility ?? source.visibility;\n intents.push({\n operation: \"upsert\",\n entity: {\n id: entity.id,\n entityType: projection.target.type,\n content: applyVisibilityToMarkdown(\n encodeEntityMarkdown(projection.target, {\n content: entity.content,\n metadata,\n }),\n visibility,\n ),\n metadata,\n visibility,\n },\n });\n },\n },\n });\n return intents;\n}\n\ntype BoundEntityWidget = EntityDashboardWidget &\n BoundDashboardWidget<\n AnyDashboardWidgetDefinition,\n Readonly<Record<string, never>>,\n Readonly<Record<string, never>>,\n undefined\n >;\n\nfunction isBoundEntityWidget(\n widget: EntityDashboardWidget,\n): widget is BoundEntityWidget {\n return (\n \"load\" in widget &&\n typeof widget.load === \"function\" &&\n \"view\" in widget.definition &&\n typeof widget.definition.view === \"function\"\n );\n}\n\n/** Entity types have no config, state or settings to bind widgets to. */\nconst entityDashboardBinding: EntityDashboardBindingContext = Object.freeze({\n config: Object.freeze({}),\n state: Object.freeze({}),\n accountSettings: undefined,\n});\n\nfunction boundProjectionInput(\n context: ProjectionInputContext,\n): ProjectionInputContext {\n const entities = context.entities;\n return Object.freeze({\n entities: Object.freeze({\n getEntity: entities.getEntity.bind(entities),\n listEntities: entities.listEntities.bind(entities),\n getEntityTypes: entities.getEntityTypes.bind(entities),\n hasEntityType: entities.hasEntityType.bind(entities),\n getEntityTypeConfig: entities.getEntityTypeConfig.bind(entities),\n isProjectionOwnedEntity: entities.isProjectionOwnedEntity.bind(entities),\n }),\n resolvePrompt: context.resolvePrompt.bind(context),\n appInfo: context.appInfo.bind(context),\n identityInput: context.identityInput.bind(context),\n });\n}\n\nfunction boundProjectionExecution(\n context: ProjectionExecutionContext,\n): ProjectionExecutionContext {\n const ai = context.ai;\n return Object.freeze({\n ai: Object.freeze({\n generate: ai.generate.bind(ai),\n generateObject: ai.generateObject.bind(ai),\n }),\n logger: context.logger,\n });\n}\n\nfunction projectionRule(\n projection: ProjectionDefinition,\n version: string,\n scope: (localId: string) => string,\n): ProjectionRule {\n return defineProjectionRule({\n id: scope(projection.id),\n version,\n sources: [{ kind: \"entity\", types: [projection.source.type] }],\n targetType: projection.target.type,\n inputSchema: ProjectionJsonObjectSchema,\n async selectInput(trigger, context) {\n const selected = trigger.inputs.filter(\n ({ sourceType }) => sourceType === projection.source.type,\n );\n const items = await Promise.all(\n selected.map(async (input): Promise<unknown> => {\n if (input.operation === \"delete\") {\n return { operation: \"delete\", id: input.sourceId };\n }\n // The source is a type of the projection's own package, read as\n // the package reads its own types; what it derives keeps the\n // source's visibility.\n const source = await context.entities.getEntity({\n entityType: input.sourceType,\n id: input.sourceId,\n visibilityScope: internalFullScope(\n \"projection rule loads source entity\",\n ),\n });\n if (!source) {\n throw new Error(\n `Projection \"${projection.id}\" could not load ${input.sourceType}:${input.sourceId}`,\n );\n }\n return { operation: \"upsert\", id: input.sourceId, source };\n }),\n );\n // One recursive validation of the whole envelope; per-item sources are\n // covered by this pass, so they are not parsed individually above.\n return ProjectionJsonObjectSchema.parse({ items });\n },\n async derive(input, _context, signal) {\n const envelope = projectionEnvelopeSchema.parse(input);\n const intents: ProjectionWriteIntent[] = [];\n for (const item of envelope.items) {\n signal.throwIfAborted();\n if (item.operation === \"delete\") {\n intents.push({\n operation: \"delete\",\n entityType: projection.target.type,\n id: item.id,\n });\n continue;\n }\n if (!item.source) {\n throw new Error(\n `Projection \"${projection.id}\" upsert item ${item.id} is missing its source`,\n );\n }\n intents.push(\n ...(await deriveProjectionUpserts(projection, item.source, signal)),\n );\n }\n return intents;\n },\n });\n}\n\nclass DeclarativeEntityPlugin extends EntityPlugin<\n EntityOf<AnyEntityDefinition>,\n Record<string, never>,\n Record<string, never>\n> {\n private readonly entityTypeConfig: EntityTypeConfig;\n private readonly projections: readonly ProjectionDefinition[];\n private readonly scope: (localId: string) => string;\n private readonly validatePersist: AnyEntityDefinition[\"validatePersist\"];\n private readonly seed:\n EntitySeedDefinition<AnyEntityDefinition[\"metadata\"]> | undefined;\n private readonly templates: AnyEntityDefinition[\"templates\"];\n private readonly dataSources: readonly AnyDataSourceDeclaration[];\n private readonly packageDataSourceIds: ReadonlySet<string>;\n private readonly feed:\n EntityFeedDeclaration<EntityOf<AnyEntityDefinition>> | undefined;\n private readonly definition: AnyEntityDefinition;\n private readonly create: EntityCreateRouting | undefined;\n private readonly attachments: AnyEntityDefinition[\"attachments\"];\n private readonly jobs: Readonly<Record<string, AnyEntityJobDeclaration>>;\n private readonly generation: AnyEntityDefinition[\"generation\"];\n private readonly projectionRules: readonly ProjectionRule[];\n /** The types the declaring package owns: its own, when it acts as itself. */\n private readonly packageTypes: ReadonlySet<string>;\n private permissions: PermissionService | undefined;\n private releases: readonly (() => void)[] = [];\n private readonly dashboardWidgetIds: string[] = [];\n private readonly widgetAbort: AbortController = new AbortController();\n public readonly entityType: string;\n public readonly schema: z.ZodType<EntityOf<AnyEntityDefinition>, unknown>;\n public readonly adapter: EntityAdapter<\n EntityOf<AnyEntityDefinition>,\n Record<string, unknown>\n >;\n\n constructor(\n definition: AnyEntityDefinition,\n projections: readonly ProjectionDefinition[],\n metadata: InstalledPluginPackageMetadata,\n scope: (localId: string) => string,\n dataSources: readonly AnyDataSourceDeclaration[],\n packageDataSourceIds: ReadonlySet<string>,\n packageTypes: ReadonlySet<string>,\n ) {\n super(\n scope(definition.type),\n metadata,\n {},\n emptyEntityPluginConfigSchema,\n // Keyed by the bare entity type: the policy is about what may be done\n // to entities of this type, not about which package declared them.\n definition.actions && {\n [definition.type]: entityActionPolicyRuleSchema.parse(\n definition.actions,\n ),\n },\n );\n this.entityTypeConfig = entityTypeConfigFor(\n definition.classification,\n definition.config,\n );\n this.projections = projections;\n this.scope = scope;\n this.validatePersist = definition.validatePersist;\n this.seed = definition.seed;\n this.templates = definition.templates;\n this.dataSources = dataSources;\n this.packageDataSourceIds = packageDataSourceIds;\n this.packageTypes = packageTypes;\n if (definition.feed) feedPathSchema.parse(definition.feed.path);\n this.feed = definition.feed;\n this.definition = definition;\n this.create = definition.create;\n this.attachments = definition.attachments;\n this.jobs = definition.jobs ?? {};\n // A create route names the type's own generation \"generation\"; a job\n // of that name would shadow it. A type without a generation may answer\n // that job name with a contract job, as an entity plugin class that ran\n // another handler under its generation job did.\n const namedGeneration = this.jobs[\"generation\"];\n if (\n namedGeneration !== undefined &&\n (definition.generation !== undefined ||\n contractJobOf(namedGeneration) === undefined)\n ) {\n throw new Error(\n `${definition.type} declares a job named \"generation\", the name a create route gives its own generation`,\n );\n }\n this.generation = definition.generation;\n this.entityType = definition.type;\n this.schema = entitySchema(definition);\n this.adapter = entityAdapter(definition);\n const declared = definition.projectionRules;\n this.projectionRules = (\n typeof declared === \"function\"\n ? declared({ template: (localName) => this.templateName(localName) })\n : (declared ?? [])\n ).map((rule) => this.declaredProjectionRule(rule));\n }\n\n /**\n * A declared rule as the runtime runs it: it writes this type only, and it\n * is handed bound reads and generation, never the services behind them.\n */\n private declaredProjectionRule(rule: ProjectionRule): ProjectionRule {\n if (rule.targetType !== this.entityType) {\n throw new Error(\n `Projection rule \"${rule.id}\" on ${this.entityType} writes ${rule.targetType}`,\n );\n }\n return Object.freeze({\n ...rule,\n selectInput: (\n trigger: Parameters<ProjectionRule[\"selectInput\"]>[0],\n context: ProjectionInputContext,\n signal: AbortSignal,\n ): ReturnType<ProjectionRule[\"selectInput\"]> =>\n rule.selectInput(trigger, boundProjectionInput(context), signal),\n derive: (\n input: Parameters<ProjectionRule[\"derive\"]>[0],\n context: ProjectionExecutionContext,\n signal: AbortSignal,\n ): ReturnType<ProjectionRule[\"derive\"]> =>\n rule.derive(input, boundProjectionExecution(context), signal),\n });\n }\n\n protected override getEntityTypeConfig(): EntityTypeConfig {\n return this.entityTypeConfig;\n }\n\n /** The registered name of a template this type declares. */\n private templateName(localName: string): string {\n if (!Object.hasOwn(this.templates ?? {}, localName)) {\n throw new Error(\n `${this.entityType} declares no template named \"${localName}\"`,\n );\n }\n return `${this.id}:${localName}`;\n }\n\n /** Whether a type is one the declaring package owns. */\n private ownsType(entityType: string): boolean {\n return this.packageTypes.has(entityType);\n }\n\n /**\n * Reads for the package acting as itself, with no caller behind it: its\n * own types at every visibility, other types at its grant, which is public\n * unless the brain grants this plugin more.\n */\n private asPackage(context: EntityPluginContext): EntityReader {\n const self = packageSelfCall(context.jobAuthority);\n return createPackageEntityReader(\n context.entityService,\n () => self,\n (entityType) => this.ownsType(entityType),\n );\n }\n\n private generationHandler(\n context: EntityPluginContext,\n declaration: NonNullable<AnyEntityDefinition[\"generation\"]>,\n jobTypeName: string,\n reportsToPipeline = false,\n readsLegacyRawData = false,\n ): DeclarativeGenerationHandler {\n return new DeclarativeGenerationHandler(this.logger, context, declaration, {\n entityType: this.entityType,\n jobTypeName,\n template: (localName) => this.templateName(localName),\n ownsType: (entityType) => this.ownsType(entityType),\n retries: this.retries,\n reportsToPipeline,\n readsLegacyRawData,\n pluginId: this.id,\n runtime: (authority) =>\n this.runBinding(context, authority, \"a generation\"),\n });\n }\n\n protected override async getInstructions(): Promise<string | undefined> {\n return this.definition.instructions;\n }\n\n /**\n * Answer the content pipeline's schedule for this type: list the material\n * the declaration names, pick from it, and queue this type's generation.\n * Nothing to write from is reported back rather than thrown.\n */\n private async runScheduledGeneration(\n context: EntityPluginContext,\n scheduled: NonNullable<AnyEntityDefinition[\"scheduledGeneration\"]>,\n ): Promise<void> {\n const reportFailure = (error: string): Promise<void> =>\n context.messaging.command(generateReportFailureContract, {\n entityType: this.entityType,\n error,\n });\n try {\n // The package starts this run, so it reads as itself, at its own\n // grant: its own types at every visibility, others as the grant allows.\n const self = packageSelfCall(context.jobAuthority);\n const access = await self.access();\n const scope = (entityType: string): ContentVisibility =>\n packageReadScope(self, this.ownsType(entityType), access);\n const sources = await context.entityService.listEntities({\n entityType: scheduled.from.entityType,\n options: {\n limit: scheduled.from.limit,\n filter: {\n visibilityScope: scope(scheduled.from.entityType),\n ...(scheduled.from.status !== undefined && {\n metadata: { status: scheduled.from.status },\n }),\n },\n },\n });\n const data = await this.scheduledInput(\n context,\n scheduled,\n sources.map(({ id }) => id),\n scope(this.entityType),\n );\n if (!data) {\n await reportFailure(\n `No ${[scheduled.from.status, scheduled.from.entityType]\n .filter(Boolean)\n .join(\" \")} available to write a ${this.entityType} from`,\n );\n return;\n }\n // Queued as the package, so the generation runs as the package too,\n // with the entries it is written from beside its input.\n const queued = {\n ...(await admitPackageJob(context.jobAuthority, undefined, data)),\n ...definedFields({ source: generationSource(data) }),\n };\n // A refused enqueue is retried until the queue admits it; the\n // schedule does not come again until its next cycle.\n await this.retries.run(\n () =>\n context.jobs.enqueue({\n type: `${this.entityType}:generation`,\n data: queued,\n toolContext: {\n interfaceType: \"job\",\n actor: { kind: \"service\", serviceId: this.id },\n },\n }),\n `queue a scheduled ${this.entityType} generation`,\n `generate:${this.entityType}`,\n );\n } catch (error) {\n await reportFailure(getErrorMessage(error));\n }\n }\n\n /** The generation input for a schedule, or null when nothing qualifies. */\n private async scheduledInput(\n context: EntityPluginContext,\n scheduled: NonNullable<AnyEntityDefinition[\"scheduledGeneration\"]>,\n sourceIds: readonly string[],\n visibilityScope: ContentVisibility,\n ): Promise<Record<string, unknown> | null> {\n if (sourceIds.length === 0) return null;\n const sourceEntityType = scheduled.from.entityType;\n if (scheduled.mode === \"batch\") {\n return { sourceEntityType, sourceEntityIds: [...sourceIds] };\n }\n // One at a time: a source this type was already written from — named\n // in its sourceEntityType/sourceEntityId metadata — is skipped.\n const derived = await Promise.all(\n sourceIds.map((sourceEntityId) =>\n context.entityService.listEntities({\n entityType: this.entityType,\n options: {\n filter: {\n visibilityScope,\n metadata: { sourceEntityType, sourceEntityId },\n },\n limit: 1,\n },\n }),\n ),\n );\n const sourceEntityId = sourceIds.find(\n (_id, index) => derived[index]?.length === 0,\n );\n return sourceEntityId ? { sourceEntityType, sourceEntityId } : null;\n }\n\n protected override createGenerationHandler(\n context: EntityPluginContext,\n ): JobHandler | null {\n return this.generation\n ? this.generationHandler(\n context,\n this.generation,\n `${this.entityType}:generation`,\n this.definition.scheduledGeneration !== undefined,\n true,\n )\n : null;\n }\n\n protected override getTemplates(): Record<string, Template> | null {\n const templates = this.templates;\n if (!templates) return null;\n // A template naming one of its package's data sources follows it to the\n // scoped id it registers under; any other id is left as written.\n const local = this.packageDataSourceIds;\n return Object.fromEntries(\n Object.entries(templates).map(([name, template]) => [\n name,\n template.dataSourceId && local.has(template.dataSourceId)\n ? { ...template, dataSourceId: this.scope(template.dataSourceId) }\n : template,\n ]),\n );\n }\n\n protected override getDataSources(): DataSource[] {\n // Scoped to the package, so two packages may each declare \"entities\".\n return this.dataSources.map((declaration) =>\n createDeclaredDataSource(\n declaration,\n this.scope(declaration.id),\n this.logger,\n ),\n );\n }\n\n /** The brain's action policy, read when a job writes. */\n private requiredLevel(\n entityType: string,\n action: \"create\" | \"update\" | \"delete\",\n ): EntityActionRequiredLevel | undefined {\n if (!this.permissions) {\n throw new Error(`Entity type \"${this.entityType}\" is not registered`);\n }\n return this.permissions.getEntityActionRequiredLevel(entityType, action);\n }\n\n override async register(\n shell: IShell,\n registrationContext?: PluginRegistrationContext,\n ): Promise<PluginCapabilities> {\n this.permissions = shell.getPermissionService();\n return super.register(shell, registrationContext);\n }\n\n protected override async onRegister(\n context: EntityPluginContext,\n ): Promise<void> {\n const validatePersist = this.validatePersist;\n if (validatePersist) {\n context.entities.registerPersistValidator(\n this.entityType,\n async (entity): Promise<void> => {\n await validatePersist(\n Object.freeze({\n content: entity.content,\n visibility: entity.visibility,\n }),\n );\n },\n );\n }\n\n // Local names: the jobs namespace scopes each to this type's plugin.\n Object.entries(this.jobs).forEach(([name, declaration]) => {\n const writes: EntityJobWrites = {\n declaring: this.definition,\n requiredLevel: (entityType, action) =>\n this.requiredLevel(entityType, action),\n };\n const ownsType = (entityType: string): boolean =>\n this.ownsType(entityType);\n const contract = contractJobOf(declaration);\n if (contract) {\n // Other packages queue it by its contract's type, so the name it\n // is registered under must scope to exactly that.\n if (contract.type !== `${this.id}:${name}`) {\n throw new Error(\n `Job \"${name}\" of ${this.entityType} answers \"${contract.type}\" but is declared as \"${this.id}:${name}\"`,\n );\n }\n if (\"generate\" in declaration) {\n throw new Error(\n `Contract job \"${contract.type}\" handles its own work`,\n );\n }\n context.jobs.registerHandler(\n name,\n contractJobHandler(\n declaration,\n context,\n ownsType,\n writes,\n this.id,\n (localName) => this.templateName(localName),\n ),\n );\n return;\n }\n context.jobs.registerHandler(\n name,\n \"generate\" in declaration\n ? this.generationHandler(context, declaration, name)\n : entityJobHandler(\n declaration,\n context,\n ownsType,\n writes,\n (authority) => this.runBinding(context, authority, \"a job\"),\n ),\n );\n });\n\n // system_generate queues this type's generation through here, so the\n // job carries the authority of the caller who asked for it.\n if (this.generation) {\n const generationType = `${this.entityType}:generation`;\n context.entities.registerGenerationEnqueuer(\n this.entityType,\n async (data, toolContext): Promise<string> => {\n const { entityId, ...input } = data;\n const queued = await admitPackageJob(\n context.jobAuthority,\n toolCaller(toolContext),\n input,\n );\n return context.jobs.enqueue({\n type: generationType,\n data: {\n ...queued,\n ...(typeof entityId === \"string\" && { entityId }),\n ...definedFields({ source: generationSource(data) }),\n },\n toolContext,\n });\n },\n );\n }\n\n const create = this.create;\n const urlRoute = urlRouteOf(this.definition);\n if (create || urlRoute) {\n this.registerCreateRouting(context, create ?? {}, urlRoute);\n }\n\n // Evals run with no caller, so as the package, with fixtures scoped to\n // this type.\n Object.entries(this.definition.evals ?? {}).forEach(([id, handler]) => {\n context.eval.registerHandler(id, (input) =>\n handler(\n input,\n withEntityRuntime(\n {\n ai: createGenerationAI(context, (localName) =>\n this.templateName(localName),\n ),\n entities: this.asPackage(context),\n fixtures: createEvalFixtures(context, this.entityType),\n },\n // An eval runs with no caller, so its searches read as the\n // package, and so does a generation it runs.\n {\n ...this.runtimeBinding(\n context,\n this.asPackage(context),\n \"an eval\",\n ),\n runGeneration: (input, options) =>\n this.runGenerationForEval(context, input, options),\n },\n ),\n ),\n );\n });\n\n const agentContext = this.definition.agentContext;\n const scheduled = this.definition.scheduledGeneration;\n\n const seed = this.seed;\n const feed = this.feed;\n const atproto = this.definition.atproto;\n const publish = this.definition.publish;\n // Built once, with the plugin's logger, when it is built with one.\n const publishProvider =\n publish && builtProvider(publish.provider, this.logger);\n const publishAssets = this.definition.publishAssets ?? [];\n // Registered through the plugin's scoped analytics, which removes them\n // when the plugin goes.\n for (const declared of this.definition.analyticsReports ?? []) {\n context.analytics.register({\n type: declared.type,\n description: declared.description,\n params: declared.params,\n ...definedFields({ minVisibility: declared.minVisibility }),\n handler: (params, { entityService, visibilityScope }) =>\n declared.report(\n params,\n Object.freeze({\n entities: createEntityReader(entityService, visibilityScope),\n visibility: visibilityScope,\n }),\n ),\n });\n }\n const widgets = (\n this.definition.dashboardWidgets?.(entityDashboardBinding) ?? []\n ).map((widget) => this.boundWidget(widget));\n const widgetIds = widgets.map(({ definition }) => definition.id);\n const repeated = widgetIds.find(\n (id, index) => widgetIds.indexOf(id) !== index,\n );\n if (repeated) {\n throw new Error(\n `${this.entityType} binds dashboard widget \"${repeated}\" twice`,\n );\n }\n // The shell removes a plugin's Inbox sources with the plugin.\n const inbox = this.definition.inbox;\n if (inbox) context.inbox.registerSource(this.inboxSource(context, inbox));\n const media = this.attachments?.length\n ? mediaAttachmentContext(context)\n : undefined;\n this.releases = [\n // First-party answers, under the channels their contracts already\n // have, as the package acting as itself.\n ...entityAnswersOf(this.definition).map((declared) =>\n context.messaging.answer(\n declared.contract,\n async (request, { sender }) =>\n declared.handle({\n request,\n sender,\n entities: await packageEntityWrites(\n context,\n this.id,\n (entityType) => this.ownsType(entityType),\n {\n declaring: this.definition,\n requiredLevel: (entityType, action) =>\n this.requiredLevel(entityType, action),\n },\n ),\n }),\n declared.everyProcess === true ? { everyProcess: true } : {},\n ),\n ),\n ...(agentContext\n ? [\n // Reads are scoped to the asker, so a provider cannot surface\n // restricted records into a public conversation.\n context.messaging.answer(agentContextContract, async (request) => ({\n items: [\n ...(await agentContext(\n Object.freeze({\n request: deepFrozen(structuredClone(request)),\n entities: createEntityReader(\n context.entityService,\n permissionToVisibilityScope(request.userPermissionLevel),\n ),\n }),\n )),\n ],\n })),\n ]\n : []),\n ...(scheduled\n ? [\n context.messaging.answer(\n generateExecuteContract,\n async ({ entityType }) => {\n if (entityType === this.entityType) {\n await this.runScheduledGeneration(context, scheduled);\n }\n },\n ),\n ]\n : []),\n // The runtime keeps the unregister handles, so an author never does.\n ...(this.attachments ?? []).flatMap((attachment) =>\n media\n ? [\n context.attachments.register(\n this.entityType,\n attachment.type,\n attachment.provider(media),\n ),\n ]\n : [],\n ),\n ...(seed\n ? [\n context.messaging.subscribe(\n SEED_TRIGGER_CHANNELS[seed.on],\n async (): Promise<{ success: true }> => {\n await this.plantSeed(context, seed);\n return { success: true };\n },\n ),\n ]\n : []),\n ...(feed\n ? [\n // Builds run in the worker, so every process answers.\n context.messaging.answer(\n siteBuildStagingContract,\n (payload) => this.stageFeed(context, feed, payload),\n { everyProcess: true },\n ),\n ]\n : []),\n // The service scopes each check id to this plugin and hands back the\n // release.\n ...(this.definition.checks ?? []).map((check) =>\n context.recurringChecks.register({\n id: check.id,\n cadence: check.cadence,\n ...definedFields({\n deliverAlerts: check.deliverAlerts,\n includeInInbox: check.includeInInbox,\n }),\n run: ({ signal }) =>\n check.run(\n Object.freeze({\n // A scheduled run has no caller: it reads as the package.\n entities: this.asPackage(context),\n siteUrl: context.siteUrl,\n signal,\n }),\n ),\n }),\n ),\n ...(atproto\n ? [\n context.atprotoProjections.register(\n this.atprotoProjection(context, atproto),\n ),\n ]\n : []),\n ...(widgets.length > 0\n ? [\n // The Dashboard answers registrations once every plugin is in.\n context.messaging.subscribe(\n SYSTEM_CHANNELS.pluginsRegistered,\n async (): Promise<{ success: true }> => {\n await this.registerDashboardWidgets(context, widgets);\n return { success: true };\n },\n ),\n ]\n : []),\n ...(publish || publishAssets.length > 0\n ? [\n // Announced once every plugin is registered, so the publish\n // pipeline is listening whichever order the plugins load in.\n context.messaging.subscribe(\n SYSTEM_CHANNELS.pluginsRegistered,\n async (): Promise<{ success: true }> => {\n if (publish) {\n await context.messaging.command(publishRegisterContract, {\n entityType: this.entityType,\n provider: publishProvider ?? publish.provider,\n config: {\n executionMode: \"provider\",\n ...definedFields({\n publishResultIdField: publish.resultIdField,\n publishTimestampField: publish.timestampField,\n }),\n },\n });\n }\n for (const asset of publishAssets) {\n await context.messaging.command(\n publishAssetRegisterContract,\n {\n entityType: this.entityType,\n attachmentType: asset.attachmentType,\n mediaEntityType: \"image\",\n // The image job records the id in the frontmatter the\n // stored record carries.\n targetEntityField: {\n location: \"frontmatter\",\n field: asset.field,\n },\n ...definedFields({ requiredWhen: asset.requiredWhen }),\n autoGenerate: true,\n },\n );\n }\n return { success: true };\n },\n ),\n ]\n : []),\n ];\n }\n\n private async registerDashboardWidgets(\n context: EntityPluginContext,\n widgets: readonly BoundEntityWidget[],\n ): Promise<void> {\n if (context.executionOnly || !context.dashboard.isAvailable()) return;\n for (const binding of widgets) {\n const registered = await context.dashboard.registerWidget(\n createDeclarativeDashboardWidgetRegistration({\n publicServiceId: this.id,\n packageName: this.packageName,\n config: entityDashboardBinding.config,\n state: entityDashboardBinding.state,\n binding,\n context,\n runtimeSignal: this.widgetAbort.signal,\n }),\n );\n if (registered) this.dashboardWidgetIds.push(binding.definition.id);\n }\n }\n\n /**\n * The binding a declared widget is: a definition's `bind` made it, and it\n * carries the loader the Dashboard calls.\n */\n private boundWidget(widget: EntityDashboardWidget): BoundEntityWidget {\n if (isBoundEntityWidget(widget)) return widget;\n throw new Error(\n `${this.entityType} dashboard widget \"${widget.definition.id}\" is not bound; bind it with defineDashboardWidget(...).bind(context, load)`,\n );\n }\n\n /** Reads of the declaring type alone, up to one visibility. */\n private records(\n context: EntityPluginContext,\n visibilityScope: ContentVisibility,\n ): EntityRecordReader<EntityOf<AnyEntityDefinition>> {\n const definition = this.definition;\n const reader = createEntityReader(context.entityService, visibilityScope);\n return Object.freeze({\n get: (id: string): Promise<EntityOf<AnyEntityDefinition> | null> =>\n reader.get(definition, id),\n list: (\n options?: DataSourceListOptions,\n ): Promise<readonly EntityOf<AnyEntityDefinition>[]> =>\n reader.list(definition, options),\n });\n }\n\n /**\n * The declared Inbox in the shape the Inbox reads. Listing reads as the\n * package; opening and acting read as the actor, and acting writes only\n * through edits the actor is allowed.\n */\n private inboxSource(\n context: EntityPluginContext,\n declared: EntityInboxDeclaration,\n ): InboxSource {\n // The declaring type is the package's own, so it lists at every visibility.\n const full = internalFullScope(\"the declared Inbox lists as its package\");\n const asActor = (\n actor: InboxActor,\n ): Omit<EntityInboxActionContext<AnyEntityDefinition>, \"edits\"> => {\n const scope = permissionToVisibilityScope(actor.permissionLevel);\n return {\n actor,\n records: this.records(context, scope),\n entities: createEntityReader(context.entityService, scope),\n };\n };\n const resolveDetail = declared.resolveDetail?.bind(declared);\n return {\n sourceId: declared.sourceId,\n displayName: declared.displayName,\n ...(declared.facets ? { facets: [...declared.facets] } : {}),\n // The listing is gathered for every operator at once, with no viewer\n // behind it, so it reads as the package: its own types at every\n // visibility, other types at its grant.\n list: async (): Promise<InboxItem[]> => [\n ...(await declared.list(\n Object.freeze({\n records: this.records(context, full),\n entities: this.asPackage(context),\n }),\n )),\n ],\n ...(resolveDetail && {\n resolveDetail: (\n itemId: string,\n actor: InboxActor,\n signal: AbortSignal,\n ): Promise<InboxItemDetail> =>\n resolveDetail(itemId, Object.freeze({ ...asActor(actor), signal })),\n }),\n act: (itemId: string, actionId: string, actor: InboxActor) =>\n declared.act(\n itemId,\n actionId,\n Object.freeze({\n ...asActor(actor),\n edits: this.inboxEdits(context, actor),\n }),\n ),\n };\n }\n\n private inboxEdits(\n context: EntityPluginContext,\n actor: InboxActor,\n ): EntityInboxEdits<AnyEntityDefinition> {\n const entityType = this.entityType;\n const allow = (action: \"update\" | \"publish\" | \"delete\"): void =>\n context.permissions.assertEntityActionAllowed(entityType, action, {\n userPermissionLevel: actor.permissionLevel,\n });\n const own = (record: EntityOf<AnyEntityDefinition>): void => {\n if (record.entityType !== entityType) {\n throw new Error(\n `An Inbox action on ${entityType} cannot write ${record.entityType}`,\n );\n }\n };\n const changed = (): Error =>\n new Error(`The ${entityType} changed meanwhile; try again`);\n const write = async (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n action: \"update\" | \"publish\",\n ): Promise<void> => {\n allow(action);\n own(record);\n const result = await context.entityService.updateEntity({\n entity: {\n ...record,\n content: change.content ?? record.content,\n metadata: this.definition.metadata.parse(\n change.metadata ?? record.metadata,\n ),\n },\n options: { expectedContentHash: record.contentHash },\n });\n if (!result.skipped) return;\n // Skipped is either an identical write or a stale one; only the\n // second means the operator acted on something they did not see.\n const current = await context.entityService.getEntity({\n entityType,\n id: record.id,\n visibilityScope: internalFullScope(\"an Inbox write checks its read\"),\n });\n if (current?.contentHash !== record.contentHash) throw changed();\n };\n return Object.freeze({\n update: (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n ): Promise<void> => write(record, change, \"update\"),\n publish: (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n ): Promise<void> => write(record, change, \"publish\"),\n delete: async (record: EntityOf<AnyEntityDefinition>): Promise<void> => {\n allow(\"delete\");\n own(record);\n const deleted = await context.entityService.deleteEntity({\n entityType,\n id: record.id,\n options: { expectedContentHash: record.contentHash },\n });\n if (!deleted) throw changed();\n },\n });\n }\n\n /**\n * The declared projection in the shape the AT Protocol service reads. It\n * reads through its own reader rather than the context the service passes,\n * and it is given the validated record.\n */\n private atprotoProjection(\n context: EntityPluginContext,\n declared: EntityAtprotoProjection<EntityOf<AnyEntityDefinition>>,\n ): AtprotoProjection {\n const definition = this.definition;\n // The record is published to a public PDS, so it is built from public\n // entries only, as the class-based blog projection builds its own.\n const entities = createEntityReader(context.entityService, \"public\");\n const isPublishable = declared.isPublishable?.bind(declared);\n return {\n entityType: this.entityType,\n collection: declared.lexicon.id,\n lexicon: declared.lexicon,\n ...definedFields({ validate: declared.validate }),\n ...(isPublishable && {\n // An invalid record stays publishable so its build fails and is\n // reported, instead of the published record being withdrawn.\n isPublishable: (entity: BaseEntity): boolean => {\n const parsed = entitySchema(definition).safeParse(entity);\n return parsed.success ? isPublishable(parsed.data) : true;\n },\n }),\n // Async, so a record that does not validate rejects rather than throws.\n buildRecord: async ({\n entity,\n config,\n client,\n pageUrl,\n topics,\n dryRun,\n }: AtprotoProjectionBuildInput): Promise<Record<string, unknown>> =>\n declared.buildRecord(\n Object.freeze({\n entity: parseDefinitionEntity(definition, entity),\n config: Object.freeze(\n definedFields({\n brainDid: config.brainDid,\n anchorDid: config.anchorDid,\n }),\n ),\n ...definedFields({\n pageUrl,\n topics: topics && Object.freeze([...topics]),\n }),\n dryRun: dryRun === true,\n entities,\n uploadBlob: async ({\n data,\n mimeType,\n }: {\n readonly data: Uint8Array;\n readonly mimeType: string;\n }): Promise<AtprotoBlobRef> => {\n if (dryRun) {\n return {\n $type: \"blob\",\n ref: { $link: \"dry-run\" },\n mimeType,\n size: data.byteLength,\n };\n }\n if (!client?.uploadBlob) {\n throw new Error(\n \"AT Protocol PDS client does not support blob uploads\",\n );\n }\n return (await client.uploadBlob({ data, mimeType })).blob;\n },\n }),\n ),\n };\n }\n\n private registerCreateRouting(\n context: EntityPluginContext,\n routing: EntityCreateRouting,\n urlRoute: EntityCreateRoute | undefined,\n ): void {\n const interceptCreate: CreateInterceptor = (input, executionContext) =>\n this.routeCreate(context, routing, urlRoute, input, executionContext);\n context.entities.registerCreateInterceptor(\n this.entityType,\n interceptCreate,\n );\n // Checked before the user confirms, through the route the confirmed\n // create will take; a rejecting route says so before anyone is asked.\n context.entities.registerCreateCheck(\n this.entityType,\n async (input, executionContext): Promise<CreateCheckResult> => {\n const route = selectCreateRoute(routing, input, urlRoute);\n if (!route) return { kind: \"ready\" };\n if (\"reject\" in route) return { kind: \"error\", error: route.reject };\n return route.check\n ? route.check(this.createContext(context, input, executionContext))\n : { kind: \"ready\" };\n },\n );\n // The upload endpoint routes by media type into the same route, rather\n // than a second handler re-implementing the create.\n const mediaTypes = routing.fromUpload?.mediaTypes ?? [];\n if (mediaTypes.length > 0) {\n context.entities.registerUploadSaveHandler({\n entityType: this.entityType,\n mediaTypes: [...mediaTypes],\n handler: createUploadSaveHandler(\n this.entityType,\n interceptCreate,\n `Upload was not handled by ${this.entityType}`,\n ),\n });\n }\n }\n\n private async routeCreate(\n context: EntityPluginContext,\n routing: EntityCreateRouting,\n urlRoute: EntityCreateRoute | undefined,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateInterceptionResult> {\n const route = selectCreateRoute(routing, input, urlRoute);\n if (!route) return { kind: \"continue\", input };\n if (\"reject\" in route) {\n return { kind: \"handled\", result: createFailure(route.reject) };\n }\n const resolution = await route.resolve(\n this.createContext(context, input, executionContext),\n );\n // A first-party route handing on to the ordinary create, which system\n // create performs at the caller's authority.\n const ordinary = ordinaryCreateOf(resolution);\n if (ordinary) {\n const { targetEntityId } = ordinary.options;\n const targeted: CreateInput = {\n ...input,\n ...(targetEntityId !== undefined && { targetEntityId }),\n };\n if (!ordinary.document) return { kind: \"continue\", input: targeted };\n const { fields: _folded, ...request } = targeted;\n const { title, content, fields } = ordinary.document;\n return {\n kind: \"continue\",\n input: {\n ...request,\n ...(title !== undefined && { title }),\n content,\n ...(fields !== undefined && { fields: { ...fields } }),\n },\n };\n }\n // First-party routes that queue a contract job, or store a complete\n // record, as an entity plugin's interceptor did.\n const queued = queuedCreateOf(resolution);\n if (queued) {\n return {\n kind: \"handled\",\n result: await this.applyQueuedCreate(\n context,\n queued,\n input,\n executionContext,\n ),\n };\n }\n const stored = storedCreateOf(resolution);\n if (stored) {\n return {\n kind: \"handled\",\n result: await this.applyStoredCreate(\n context,\n stored,\n input,\n executionContext,\n ),\n };\n }\n return {\n kind: \"handled\",\n result: await this.applyCreateResolution(\n context,\n resolution,\n input,\n executionContext,\n ),\n };\n }\n\n /** What a route decides with: frozen, read-only, at the caller's level. */\n private createContext(\n context: EntityPluginContext,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): EntityCreateContext {\n const uploads = context.uploads.scoped(sharedUploadScope);\n const reader: EntityCreateUploadReader = Object.freeze({\n read: (uploadId: string) => uploads.read(uploadId),\n readRecord: (uploadId: string) => uploads.readRecord(uploadId),\n });\n const entities = createEntityReader(\n context.entityService,\n this.callerScope(executionContext),\n );\n return withEntityRuntime(\n {\n input: deepFrozen(structuredClone(input)),\n entities,\n uploads: reader,\n },\n {\n ...this.runtimeBinding(context, entities, \"a create route\", {\n // Targets and sources are read public, as the entity plugin\n // classes' create interceptors read them.\n service: context.entityService,\n scope: () => \"public\",\n }),\n caller: createCaller(executionContext),\n },\n );\n }\n\n /**\n * The internal runtime a first-party definition reaches through\n * entityRuntime(context): searches at the call's visibility, reads of the\n * package's own types as the package, and free-form structured AI.\n */\n private runtimeBinding(\n context: EntityPluginContext,\n reader: EntityReader,\n where: string,\n anyEntities?: AnyEntityBinding,\n ): EntityRuntimeBinding {\n return {\n where,\n reader,\n ...(anyEntities && { anyEntities }),\n packageReader: this.asPackage(context),\n ownsType: (entityType) => this.ownsType(entityType),\n searchWithDistances: (request) =>\n context.entityService.searchWithDistances(request),\n generateObject: (prompt, schema) =>\n context.ai.generateObject(prompt, schema),\n generate: createGenerationAI(context, (localName) =>\n this.templateName(localName),\n ).generate,\n logger: context.logger,\n identity: Object.freeze({\n getProfile: () => context.identity.getProfile(),\n }),\n };\n }\n\n /**\n * Run this type's generation in process for an eval, queued as the\n * package, as an entity plugin class's eval ran its generation handler.\n */\n private async runGenerationForEval(\n context: EntityPluginContext,\n input: unknown,\n options: EvalGenerationOptions = {},\n ): Promise<EvalGenerationResult> {\n const generation = this.generation;\n if (!generation) {\n throw new Error(`Entity \"${this.entityType}\" declares no generation`);\n }\n const handler = this.generationHandler(\n context,\n generation,\n `${this.entityType}:generation`,\n this.definition.scheduledGeneration !== undefined,\n );\n const data = handler.validateAndParse(\n await admitPackageJob(context.jobAuthority, undefined, input),\n );\n if (data === null) {\n return { success: false, error: \"Invalid generation input\" };\n }\n const onProgress = options.onProgress;\n const reporter = onProgress\n ? CallbackProgressReporter.from(async ({ progress, message }) => {\n await onProgress({ progress, ...definedFields({ message }) });\n })\n : undefined;\n const result = await handler.process(\n data,\n `eval-${Date.now()}`,\n reporter ?? CallbackProgressReporter.noop(),\n );\n return { ...result };\n }\n\n /**\n * The internal runtime a declared job or generation reaches: reads at the\n * run's authority, untyped ones at the scope the run reads each type at.\n */\n private runBinding(\n context: EntityPluginContext,\n authority: EntityRunAuthority,\n where: string,\n ): EntityRuntimeBinding {\n return this.runtimeBinding(context, authority.entities, where, {\n service: context.entityService,\n scope: (entityType) => authority.scope(entityType),\n });\n }\n\n private callerScope(\n executionContext: CreateExecutionContext,\n ): ContentVisibility {\n return permissionToVisibilityScope(executionContext.userPermissionLevel);\n }\n\n /**\n * Perform what a route decided. The runtime writes, so \"created\" and\n * \"updated\" describe what it did rather than what the route claims.\n */\n private async applyCreateResolution(\n context: EntityPluginContext,\n resolution: EntityCreateResolution | EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n if (\"delegate\" in resolution) {\n return this.applyDelegation(context, resolution, input, executionContext);\n }\n if (\"refuse\" in resolution) return createFailure(resolution.refuse);\n const visibilityScope = this.callerScope(executionContext);\n const eventContext = {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n };\n\n if (\"existing\" in resolution) {\n const found = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.existing.id,\n visibilityScope,\n });\n if (!found) {\n return createFailure(\n `Cannot reuse ${this.entityType} \"${resolution.existing.id}\", which does not exist`,\n );\n }\n return this.createSuccess(\"existing\", found.id, resolution.attachment);\n }\n\n const target =\n \"update\" in resolution ? resolution.update : resolution.create;\n const metadata = this.definition.metadata.safeParse(target.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n\n if (\"update\" in resolution) {\n const existing = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.update.id,\n visibilityScope,\n });\n if (!existing) {\n return createFailure(\n `Cannot update ${this.entityType} \"${resolution.update.id}\", which does not exist`,\n );\n }\n const written = await context.entityService.updateEntity({\n entity: {\n ...existing,\n content: resolution.update.content,\n metadata: metadata.data,\n },\n options: { eventContext },\n });\n return this.createSuccess(\"updated\", written.entityId, undefined);\n }\n\n // Create-if-absent: a route that did not see an entity at the caller's\n // level must not write over one it could not see.\n const taken = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.create.id,\n visibilityScope: internalFullScope(\n \"create route checks whether its id is taken; no content returned\",\n ),\n });\n const alreadyExists = `A ${this.entityType} \"${resolution.create.id}\" already exists`;\n if (taken) return createFailure(alreadyExists);\n try {\n const written = await context.entityService.createEntity({\n entity: {\n id: resolution.create.id,\n entityType: this.entityType,\n content: resolution.create.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && {\n visibility: input.visibility,\n }),\n },\n options: {\n conditionalWrite: { expectedRevision: null },\n eventContext,\n },\n });\n return this.createSuccess(\n \"created\",\n written.entityId,\n resolution.attachment,\n );\n } catch (error) {\n if (error instanceof EntityWriteConflictError) {\n return createFailure(alreadyExists);\n }\n throw error;\n }\n }\n\n private invalid(error: z.ZodError): string {\n return `Invalid ${this.entityType} metadata: ${error.issues\n .map(({ path, message }) => `${path.join(\".\") || \"metadata\"}: ${message}`)\n .join(\"; \")}`;\n }\n\n /**\n * Allocate now and finish later: write the placeholder (or find the\n * entity), then queue the declared job with its id. Everything the\n * delegation needs is checked before anything is written.\n */\n private async applyDelegation(\n context: EntityPluginContext,\n allocation: EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const { job: name, input: jobInput = {} } = allocation.delegate;\n // \"generation\" names the type's own generation, queued as\n // `{type}:generation`, as an entity plugin's interceptor queued it.\n const ownGeneration = name === \"generation\" ? this.generation : undefined;\n const job =\n ownGeneration ??\n (Object.hasOwn(this.jobs, name) ? this.jobs[name] : undefined);\n if (!job) {\n return createFailure(\n `${this.entityType} declares no job named \"${name}\"`,\n );\n }\n const allocates = \"create\" in allocation || \"existing\" in allocation;\n if (allocates && !(\"generate\" in job)) {\n return createFailure(\n `${this.entityType} job \"${name}\" does not generate, so it cannot fill an allocated entity`,\n );\n }\n const parsed = job.input.safeParse(jobInput);\n if (!parsed.success) {\n return createFailure(\n `Invalid input for ${this.entityType} job \"${name}\": ${parsed.error.issues\n .map(({ message }) => message)\n .join(\"; \")}`,\n );\n }\n // The caller starts the job, and it runs with their authority, so they\n // are admitted before anything is written; one the brain no longer\n // knows is refused here.\n const queued = await admitPackageJob(\n context.jobAuthority,\n toolCaller(executionContext),\n jobInput,\n );\n\n const allocated = await this.allocate(\n context,\n allocation,\n input,\n executionContext,\n );\n if (\"error\" in allocated) return createFailure(allocated.error);\n const entityId = allocated.entityId;\n try {\n const data = {\n ...queued,\n ...(entityId !== undefined && { entityId }),\n ...(\"generate\" in job &&\n definedFields({ source: generationSource(input) })),\n };\n const jobId = ownGeneration\n ? // Content work for the caller who asked, routed back to their\n // conversation, as the classes queued their generations.\n await context.jobs.enqueue({\n type: `${this.entityType}:generation`,\n data,\n toolContext: executionContext,\n options: {\n source: this.id,\n metadata: { operationType: \"content_operations\" },\n },\n })\n : await context.jobs.enqueue({\n type: name,\n // The input as written, and beside it the entity a generation\n // fills and the entries the request named it from.\n data,\n toolContext: {\n interfaceType: executionContext.interfaceType,\n actor: executionContext.actor,\n ...definedFields({ channelId: executionContext.channelId }),\n },\n });\n const attachment =\n \"attachment\" in allocation ? allocation.attachment : undefined;\n return {\n success: true,\n data: {\n status: \"generating\",\n jobId,\n ...(entityId !== undefined && {\n entityId,\n ...(attachment && {\n attachment: createResultAttachmentSchema.parse(\n attachment({ entityId }),\n ),\n }),\n }),\n },\n };\n } catch (error) {\n // No job will fill a placeholder this create wrote; left behind it\n // would stay generating forever.\n if (allocated.written) {\n await context.entityService\n .deleteEntity({ entityType: this.entityType, id: allocated.written })\n .catch((cleanupError: unknown) => {\n this.logger.warn(\"Failed to remove an unqueued placeholder\", {\n entityId: allocated.written,\n error: cleanupError,\n });\n });\n }\n return createFailure(getErrorMessage(error));\n }\n }\n\n private async allocate(\n context: EntityPluginContext,\n allocation: EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<\n | { readonly entityId: string | undefined; readonly written?: string }\n | { readonly error: string }\n > {\n if (\"existing\" in allocation) {\n const found = await context.entityService.getEntity({\n entityType: this.entityType,\n id: allocation.existing.id,\n visibilityScope: this.callerScope(executionContext),\n });\n return found\n ? { entityId: found.id }\n : {\n error: `Cannot reuse ${this.entityType} \"${allocation.existing.id}\", which does not exist`,\n };\n }\n if (!(\"create\" in allocation)) return { entityId: undefined };\n const metadata = this.definition.metadata.safeParse(\n allocation.create.metadata,\n );\n if (!metadata.success) return { error: this.invalid(metadata.error) };\n // Deduplicated, as system_generate's stub is: a second import lands\n // beside the first rather than on top of it.\n const written = await context.entityService.createEntity({\n entity: {\n id: allocation.create.id,\n entityType: this.entityType,\n content: allocation.create.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && { visibility: input.visibility }),\n },\n options: {\n deduplicateId: true,\n eventContext: {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n },\n },\n });\n return { entityId: written.entityId, written: written.entityId };\n }\n\n /**\n * Write a first-party route's pending placeholder and queue the contract\n * job that fills it, as an entity plugin's interceptor did: a record that\n * already has the id is reused rather than written over, and one at a\n * different visibility than asked is refused.\n */\n private async applyQueuedCreate(\n context: EntityPluginContext,\n queued: QueuedCreate,\n _input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const attributed = isAttributed(queued);\n const placeholder = queued.placeholder;\n if (placeholder?.deduplicate === true) {\n return this.applyDeduplicatedQueuedCreate(\n context,\n queued,\n placeholder,\n executionContext,\n );\n }\n if (placeholder) {\n const metadata = this.definition.metadata.safeParse(placeholder.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const pending = await createPendingEntity({\n entityService: context.entityService,\n entity: {\n id: queued.entityId,\n entityType: this.entityType,\n content: placeholder.content,\n metadata: metadata.data,\n ...(placeholder.visibility !== undefined && {\n visibility: placeholder.visibility,\n }),\n created: now,\n updated: now,\n },\n ...(attributed && {\n options: { eventContext: eventContextOf(executionContext) },\n }),\n });\n if (\n placeholder.visibility !== undefined &&\n pending.existingEntity !== undefined &&\n pending.existingEntity.visibility !== placeholder.visibility\n ) {\n // Answered, as an entity plugin's interceptor answered it.\n return createFailure(\n `Existing ${this.entityType} visibility \"${pending.existingEntity.visibility}\" does not match requested visibility \"${placeholder.visibility}\".`,\n );\n }\n }\n return this.queueFill(context, queued, executionContext, queued.entityId);\n }\n\n /**\n * A queued create whose placeholder lands beside a record that has its id,\n * as an entity plugin's interceptor wrote one with `deduplicateId`: the\n * job is queued for, and the answer names, the id written, and the job's\n * input may be built from the placeholder as stored.\n */\n private async applyDeduplicatedQueuedCreate(\n context: EntityPluginContext,\n queued: QueuedCreate,\n placeholder: NonNullable<QueuedCreate[\"placeholder\"]>,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const metadata = this.definition.metadata.safeParse(placeholder.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const created = await context.entityService.createEntity({\n entity: {\n id: queued.entityId,\n entityType: this.entityType,\n content: placeholder.content,\n metadata: metadata.data,\n ...(placeholder.visibility !== undefined && {\n visibility: placeholder.visibility,\n }),\n created: now,\n updated: now,\n },\n options: {\n deduplicateId: true,\n ...(isAttributed(queued) && {\n eventContext: eventContextOf(executionContext),\n }),\n },\n });\n return this.queueFill(context, queued, executionContext, created.entityId);\n }\n\n /** The record a queued create's job fills, as it is stored now. */\n private async writtenPlaceholder(\n context: EntityPluginContext,\n entityId: string,\n ): Promise<QueuedPlaceholder> {\n const written = await context.entityService.getEntity({\n entityType: this.entityType,\n id: entityId,\n visibilityScope: internalFullScope(\n \"read back the placeholder a queued create wrote; only its hash is handed on\",\n ),\n });\n if (!written) {\n throw new Error(\n `The ${this.entityType} placeholder \"${entityId}\" could not be read back`,\n );\n }\n return { entityId: written.id, contentHash: written.contentHash };\n }\n\n /** Queue a queued create's job and answer generating. */\n private async queueFill(\n context: EntityPluginContext,\n queued: QueuedCreate,\n executionContext: CreateExecutionContext,\n entityId: string,\n ): Promise<CreateResult> {\n const { data } = queued.job;\n // Input built from the placeholder reads it back as it was stored.\n const jobId = await context.jobs.enqueue(\n queued.job.contract,\n typeof data === \"function\"\n ? data(await this.writtenPlaceholder(context, entityId))\n : data,\n ...(isAttributed(queued)\n ? [\n {\n options: attributionJobOptions(\n this.id,\n executionContext,\n typeof queued.job.attributed === \"object\"\n ? queued.job.attributed\n : undefined,\n ),\n },\n ]\n : []),\n );\n return {\n success: true,\n data: {\n entityId,\n status: \"generating\",\n jobId,\n ...(queued.attachment !== undefined && {\n attachment: createResultAttachmentSchema.parse(queued.attachment),\n }),\n },\n };\n }\n\n /**\n * Store a first-party route's complete record as the caller, under the\n * next free id when this one is taken, as an entity plugin's interceptor\n * did.\n */\n private async applyStoredCreate(\n context: EntityPluginContext,\n stored: StoredCreate,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const metadata = this.definition.metadata.safeParse(stored.record.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const written = await context.entityService.createEntity({\n entity: {\n id: stored.record.id,\n entityType: this.entityType,\n content: stored.record.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && { visibility: input.visibility }),\n created: now,\n updated: now,\n },\n options: {\n deduplicateId: true,\n eventContext: eventContextOf(executionContext),\n },\n });\n return {\n success: true,\n data: {\n entityId: written.entityId,\n status: \"created\",\n attachment: createResultAttachmentSchema.parse(\n stored.attachment({ entityId: written.entityId }),\n ),\n },\n };\n }\n\n private createSuccess(\n status: \"created\" | \"existing\" | \"updated\",\n entityId: string,\n attachment:\n ((written: { readonly entityId: string }) => unknown) | undefined,\n ): CreateResult {\n return {\n success: true,\n data: {\n status,\n entityId,\n ...(attachment && {\n attachment: createResultAttachmentSchema.parse(\n attachment({ entityId }),\n ),\n }),\n },\n };\n }\n\n protected override async onShutdown(): Promise<void> {\n this.releases.forEach((release) => release());\n this.releases = [];\n this.widgetAbort.abort();\n const context = this.getContext();\n const widgetIds = this.dashboardWidgetIds.splice(0);\n await Promise.all(\n widgetIds.map(async (widgetId) => {\n try {\n await context.dashboard.unregisterWidget(widgetId);\n } catch (error) {\n // Shutdown goes on: a Dashboard that is already gone holds nothing\n // to unregister, and one widget must not keep the others up.\n this.logger.error(\n \"Failed to unregister a declared dashboard widget\",\n {\n widgetId,\n error: getErrorMessage(error),\n },\n );\n }\n }),\n );\n }\n\n /**\n * Write this type's feed into the staged build. A failure is reported\n * rather than thrown, so the build fails and keeps the previous generation\n * instead of publishing one without the feed.\n */\n private async stageFeed(\n context: EntityPluginContext,\n feed: EntityFeedDeclaration<EntityOf<AnyEntityDefinition>>,\n payload: SiteBuildStagingPayload,\n ): Promise<void> {\n try {\n await this.writeFeed(context, feed, payload);\n } catch (error) {\n this.logger.error(`Failed to write the ${this.entityType} feed`, error);\n payload.reportFailure(\n `${this.entityType} feed generation failed: ${getErrorMessage(error)}`,\n );\n }\n }\n\n private async writeFeed(\n context: EntityPluginContext,\n feed: EntityFeedDeclaration<EntityOf<AnyEntityDefinition>>,\n payload: SiteBuildStagingPayload,\n ): Promise<void> {\n // A preview carries everything so an author sees unpublished work in\n // place; a production build carries what the type counts as published.\n const stored = await context.entityService.listEntities({\n entityType: this.entityType,\n options: {\n limit: 1000,\n publishedOnly: payload.environment === \"production\",\n },\n });\n const build = Object.freeze({ environment: payload.environment });\n const items = stored.flatMap((entity) => {\n const item = feed.toItem(\n parseDefinitionEntity(this.definition, entity),\n build,\n );\n return item ? [feedItemSchema.parse(item)] : [];\n });\n if (items.length === 0) return;\n\n // The channel links the site as configured and the feed hangs off it;\n // item links join the site and the route with one slash. This is the\n // feed the blog plugin class wrote, byte for byte.\n const siteUrl = payload.siteConfig.url ?? \"https://example.com\";\n const siteRoot = siteUrl.replace(/\\/+$/u, \"\");\n const xml = renderRssFeed(\n items,\n {\n title: payload.siteConfig.title ?? feed.title ?? \"Feed\",\n description:\n payload.siteConfig.description ??\n feed.description ??\n \"Latest updates\",\n link: siteUrl,\n selfLink: `${siteUrl}/${feed.path}`,\n },\n (item) =>\n `${siteRoot}/${payload\n .generateEntityUrl(this.entityType, item.slug)\n .replace(/^\\/+/u, \"\")}`,\n );\n const target = join(payload.outputDir, feed.path);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, xml, \"utf8\");\n }\n\n /** Create-if-absent: a seed never overwrites authored content. */\n private async plantSeed(\n context: EntityPluginContext,\n seed: EntitySeedDefinition<AnyEntityDefinition[\"metadata\"]>,\n ): Promise<void> {\n const existing = await context.entityService.getEntity({\n entityType: this.entityType,\n id: seed.id,\n visibilityScope: internalFullScope(\n \"seed checks whether its default exists; no content returned\",\n ),\n });\n if (existing) return;\n try {\n await context.entityService.createEntity({\n entity: {\n id: seed.id,\n entityType: this.entityType,\n content: seed.content(),\n metadata: seed.metadata ?? {},\n },\n options: { conditionalWrite: { expectedRevision: null } },\n });\n } catch (error) {\n // Someone created it between the read and the write; theirs stays.\n if (!(error instanceof EntityWriteConflictError)) throw error;\n }\n }\n\n protected override getProjectionRules(): ProjectionRule[] {\n return [\n ...this.projections.map((projection) =>\n projectionRule(projection, this.version, this.scope),\n ),\n ...this.projectionRules,\n ];\n }\n}\n\n/**\n * The plugin that registers a package data source: an entity data source\n * with the type it reads, a fetching one with the package's first type.\n */\nfunction installsDataSource(\n source: AnyDataSourceDeclaration,\n definition: AnyEntityDefinition,\n entities: readonly AnyEntityDefinition[],\n): boolean {\n return source.kind === \"rizom-entity-data-source\"\n ? source.entity === definition\n : entities[0] === definition;\n}\n\nexport function createEntityPackagePlugins(\n entities: readonly AnyEntityDefinition[],\n projections: readonly ProjectionDefinition[],\n metadata: InstalledPluginPackageMetadata,\n scope: (localId: string) => string,\n dataSources: readonly AnyDataSourceDeclaration[] = [],\n // Each entity with the slots its package's `configure` bound, keyed by\n // the declared definition, which projections and data sources name.\n bound: ReadonlyMap<AnyEntityDefinition, AnyEntityDefinition> = new Map(),\n): DeclarativeEntityPlugin[] {\n const packageDataSourceIds = new Set(dataSources.map(({ id }) => id));\n const packageTypes = new Set(entities.map(({ type }) => type));\n return entities.map(\n (definition) =>\n new DeclarativeEntityPlugin(\n bound.get(definition) ?? definition,\n projections.filter(({ source }) => source === definition),\n metadata,\n scope,\n dataSources.filter((source) =>\n installsDataSource(source, definition, entities),\n ),\n packageDataSourceIds,\n packageTypes,\n ),\n );\n}\n",
356
+ "import { mkdir, writeFile } from \"node:fs/promises\";\nimport { dirname, join } from \"node:path\";\nimport {\n authenticatedUserId,\n agentContextContract,\n generateExecuteContract,\n generateReportFailureContract,\n publishAssetRegisterContract,\n publishRegisterContract,\n siteBuildStagingContract,\n type SiteBuildStagingPayload,\n} from \"@brains/contracts\";\nimport {\n EntityWriteConflictError,\n createResultAttachmentSchema,\n internalFullScope,\n permissionToVisibilityScope,\n ProjectionJsonObjectSchema,\n applyVisibilityToMarkdown,\n generateFrontmatter,\n generateMarkdownWithFrontmatter,\n parseMarkdownWithFrontmatter,\n type BaseEntity,\n type ContentVisibility,\n type CreateCheckResult,\n type CreateExecutionContext,\n type CreateInput,\n type CreateInterceptionResult,\n type CreateInterceptor,\n type CreateResult,\n type DataSource,\n type EntityAdapter,\n type EntitySchema,\n type EntityTypeConfig,\n type ProjectionJsonObject,\n type ProjectionWriteIntent,\n} from \"@brains/entity-service\";\nimport type {\n AtprotoBlobRef,\n AtprotoProjection,\n AtprotoProjectionBuildInput,\n} from \"@brains/atproto-contracts\";\nimport { feedItemSchema, renderRssFeed } from \"@brains/site-composition\";\nimport {\n entityActionPolicyRuleSchema,\n type EntityActionRequiredLevel,\n type PermissionService,\n type Template,\n} from \"@brains/templates\";\nimport type {\n IShell,\n PluginCapabilities,\n PluginRegistrationContext,\n} from \"../interfaces\";\nimport { getErrorMessage } from \"@brains/utils/error\";\nimport { readMarkdownFrontmatter } from \"@brains/utils/markdown-frontmatter\";\nimport { isPlainRecord } from \"@brains/utils/predicates\";\nimport { definedFields } from \"@brains/utils/strip-undefined\";\nimport { z } from \"@brains/utils/zod\";\nimport { EntityPlugin, emptyEntityPluginConfigSchema } from \"./entity-plugin\";\nimport type { EntityPluginContext } from \"./context\";\nimport { entityTypeConfigFor } from \"./entity-definition-config\";\nimport { createEntityReader } from \"./entity-reader\";\nimport {\n contractJobOf,\n entityAnswersOf,\n bodyTemplateOf,\n ordinaryCreateOf,\n restatedMetadataOf,\n urlRouteOf,\n writesInDocumentOrderOf,\n frontmatterSchemaOf,\n builtProvider,\n queuedCreateOf,\n storedCreateOf,\n withEntityRuntime,\n type AnyEntityBinding,\n type EntityRuntimeBinding,\n type QueuedCreate,\n type EntityCreateCaller,\n type EvalGenerationOptions,\n type EvalGenerationResult,\n type QueuedAttribution,\n type QueuedPlaceholder,\n type StoredCreate,\n} from \"./entity-runtime\";\nimport { CallbackProgressReporter } from \"@brains/utils/progress\";\nimport { createUploadSaveHandler } from \"./upload-save\";\nimport { sharedUploadScope } from \"../service/upload-registry\";\nimport {\n admitPackageJob,\n createPackageEntityReader,\n packageReadScope,\n packageSelfCall,\n toolCaller,\n} from \"../service/job-authority\";\nimport {\n DeclarativeGenerationHandler,\n createEvalFixtures,\n createGenerationAI,\n contractJobHandler,\n entityJobHandler,\n type EntityJobWrites,\n type EntityRunAuthority,\n generationSource,\n packageEntityWrites,\n} from \"./declarative-jobs\";\nimport type { JobHandler, JobOptions } from \"@brains/job-queue\";\nimport { createPendingEntity } from \"./pending-ingestion\";\nimport {\n currentMetadata,\n entitySchema,\n parseDefinitionEntity,\n} from \"./entity-schema\";\nimport {\n defineProjectionRule,\n type ProjectionExecutionContext,\n type ProjectionInputContext,\n type ProjectionRule,\n} from \"./projection-rule\";\nimport type { InstalledPluginPackageMetadata } from \"../package-definition\";\nimport { createDeclarativeDashboardWidgetRegistration } from \"../operator/dashboard-widget-runtime\";\nimport type {\n AnyDashboardWidgetDefinition,\n BoundDashboardWidget,\n} from \"../operator/operator-definition-contract\";\nimport type {\n InboxActor,\n InboxItem,\n InboxItemDetail,\n InboxSource,\n} from \"../inbox-registry\";\nimport { SYSTEM_CHANNELS } from \"../system-channels\";\nimport { createDeclaredDataSource } from \"../public/entity-data-source\";\nimport type {\n AnyDataSourceDeclaration,\n AnyEntityDefinition,\n AnyEntityJobDeclaration,\n EntityCreateAllocation,\n EntityCreateContext,\n EntityCreateResolution,\n EntityCreateRoute,\n EntityCreateRouting,\n EntityAtprotoProjection,\n EntityDashboardBindingContext,\n EntityDashboardWidget,\n EntityInboxActionContext,\n EntityInboxChange,\n EntityInboxDeclaration,\n EntityInboxEdits,\n EntityReader,\n EntityRecordReader,\n DataSourceListOptions,\n EntityCreateUploadReader,\n EntityFeedDeclaration,\n MediaAttachmentContext,\n MediaEntityListRequest,\n MediaEntityReads,\n MediaEntityRequest,\n EntityOf,\n EntitySeedDefinition,\n EntitySeedTrigger,\n ProjectionDefinition,\n} from \"./entity-definition-contract\";\n\nexport { parseDefinitionEntity } from \"./entity-schema\";\n\n/**\n * Named seed triggers map to internal channels here, so the public surface\n * never names a channel.\n */\nconst SEED_TRIGGER_CHANNELS: Record<EntitySeedTrigger, string> = {\n \"startup-content-settled\": SYSTEM_CHANNELS.startupContentSettled,\n};\n\nconst rawFrontmatterSchema = z.record(z.string(), z.unknown());\n\n/**\n * Which route a create request takes, by how the caller expressed it: an\n * upload or attachment reference first, then content, then a prompt, then\n * a URL alone. A conversation message arrives as content.\n */\nfunction selectCreateRoute(\n routing: EntityCreateRouting,\n input: CreateInput,\n urlRoute?: EntityCreateRoute,\n): EntityCreateRoute | undefined {\n switch (input.from?.kind) {\n case \"entity-attachment\":\n return routing.fromAttachment;\n case \"upload\":\n return routing.fromUpload;\n default:\n if (input.content) return routing.fromContent;\n if (input.prompt) return routing.fromPrompt;\n // A first-party type may decide a create from a URL alone.\n return input.url ? urlRoute : undefined;\n }\n}\n\n/** A detached copy no route can change under the runtime. */\nfunction deepFrozen<T>(value: T): T {\n if (typeof value === \"object\" && value !== null && !Object.isFrozen(value)) {\n Object.values(value).forEach(deepFrozen);\n Object.freeze(value);\n }\n return value;\n}\n\n/** Whether a queued create's job is attributed to the caller. */\nfunction isAttributed(queued: QueuedCreate): boolean {\n const { attributed } = queued.job;\n return attributed !== undefined && attributed !== false;\n}\n\n/** Who asked for a create, as a first-party route sees them. */\nfunction createCaller(\n executionContext: CreateExecutionContext,\n): EntityCreateCaller {\n const userId = authenticatedUserId(executionContext);\n return Object.freeze({\n interfaceType: executionContext.interfaceType,\n actor: executionContext.actor,\n ...definedFields({\n userId,\n channelId: executionContext.channelId,\n channelName: executionContext.channelName,\n }),\n });\n}\n\n/** Who a create is attributed to in the store. */\nfunction eventContextOf(executionContext: CreateExecutionContext): {\n readonly actor: CreateExecutionContext[\"actor\"];\n readonly interfaceType: string;\n} {\n return {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n };\n}\n\n/**\n * A queued job's attribution to the caller who asked for it, as the entity\n * plugin classes queued their own jobs.\n */\nfunction attributionJobOptions(\n source: string,\n executionContext: CreateExecutionContext,\n attribution: QueuedAttribution = {},\n): JobOptions {\n return {\n source,\n metadata: {\n operationType: attribution.operationType ?? \"content_operations\",\n ...(attribution.channel === true &&\n executionContext.channelId !== undefined && {\n channelId: executionContext.channelId,\n }),\n interfaceType: executionContext.interfaceType,\n requestedByActor: executionContext.actor,\n ...(executionContext.actor.kind === \"user\"\n ? { requestedByUserId: executionContext.actor.userId }\n : {}),\n requestedByInterface: executionContext.interfaceType,\n },\n };\n}\n\nfunction createFailure(error: string): CreateResult {\n return { success: false, error };\n}\n\n/**\n * Public reads for a media provider. Each request is rebuilt from the fields\n * the contract names, so nothing a provider passes can widen visibility.\n */\nfunction mediaEntityReads(\n entityService: EntityPluginContext[\"entityService\"],\n): MediaEntityReads {\n function getEntity(request: MediaEntityRequest): Promise<BaseEntity | null>;\n function getEntity<T extends BaseEntity>(\n request: MediaEntityRequest,\n schema: EntitySchema<T>,\n ): Promise<T | null>;\n function getEntity<T extends BaseEntity>(\n request: MediaEntityRequest,\n schema?: EntitySchema<T>,\n ): Promise<BaseEntity | T | null> {\n const read = { entityType: request.entityType, id: request.id };\n return schema\n ? entityService.getEntity(read, schema)\n : entityService.getEntity(read);\n }\n function listEntities(request: MediaEntityListRequest): Promise<BaseEntity[]>;\n function listEntities<T extends BaseEntity>(\n request: MediaEntityListRequest,\n schema: EntitySchema<T>,\n ): Promise<T[]>;\n function listEntities<T extends BaseEntity>(\n request: MediaEntityListRequest,\n schema?: EntitySchema<T>,\n ): Promise<BaseEntity[] | T[]> {\n const read = {\n entityType: request.entityType,\n options: definedFields({ limit: request.options?.limit }),\n };\n return schema\n ? entityService.listEntities(read, schema)\n : entityService.listEntities(read);\n }\n return Object.freeze({ getEntity, listEntities });\n}\n\n/** The media members an attachment provider is built with, frozen and bound. */\nfunction mediaAttachmentContext(\n context: EntityPluginContext,\n): MediaAttachmentContext {\n const { identity, views } = context;\n return Object.freeze({\n domain: context.domain,\n themeCSS: context.themeCSS,\n identity: Object.freeze({ getProfile: () => identity.getProfile() }),\n entityService: mediaEntityReads(context.entityService),\n views: Object.freeze({ get: (name: string) => views.get(name) }),\n });\n}\n\n/** A feed path stays inside the build output: relative, no parent steps. */\nconst feedPathSchema = z\n .string()\n .min(1)\n .refine(\n (path) =>\n !path.startsWith(\"/\") &&\n !path.includes(\"\\\\\") &&\n path.split(\"/\").every((segment) => segment !== \"\" && segment !== \"..\"),\n { message: \"A feed path is relative to the build output\" },\n );\n\nconst projectionEnvelopeSchema = z.object({\n items: z.array(\n z.object({\n operation: z.enum([\"upsert\", \"delete\"]),\n id: z.string(),\n source: ProjectionJsonObjectSchema.optional(),\n }),\n ),\n});\n\nfunction encodeParts(\n definition: AnyEntityDefinition,\n input: {\n readonly content: string;\n readonly metadata: Record<string, unknown>;\n },\n): { readonly content: string; readonly frontmatter: Record<string, unknown> } {\n return definition.markdown?.encode\n ? definition.markdown.encode({\n content: input.content,\n metadata: definition.metadata.parse(\n currentMetadata(definition, input.metadata),\n ),\n })\n : { content: input.content, frontmatter: input.metadata };\n}\n\nfunction encodeEntityMarkdown(\n definition: AnyEntityDefinition,\n input: {\n readonly content: string;\n readonly metadata: Record<string, unknown>;\n },\n): string {\n const encoded = encodeParts(definition, input);\n return generateMarkdownWithFrontmatter(encoded.content, encoded.frontmatter);\n}\n\n/** A document's body; content with no frontmatter is a body already. */\nfunction documentBody(content: string): string {\n const document = readMarkdownFrontmatter(content);\n return isPlainRecord(document.data) && Object.keys(document.data).length > 0\n ? document.content\n : content;\n}\n\ntype DeclaredAdapter = EntityAdapter<\n EntityOf<AnyEntityDefinition>,\n Record<string, unknown>\n>;\n\n/** The fields a type writes: its metadata's and its header's, by name. */\nfunction writtenFields(\n metadata: z.ZodObject,\n header: z.ZodObject | undefined,\n): z.ZodObject {\n const names = new Set([\n ...Object.keys(metadata.shape),\n ...Object.keys(header?.shape ?? {}),\n ]);\n return z.object(\n Object.fromEntries([...names].map((name) => [name, z.unknown()])),\n );\n}\n\n/** How a text type's record is its markdown: its metadata is its frontmatter. */\nfunction markdownStorage(\n definition: AnyEntityDefinition,\n): Pick<\n DeclaredAdapter,\n | \"frontmatterSchema\"\n | \"writtenFrontmatterSchema\"\n | \"toMarkdown\"\n | \"fromMarkdown\"\n | \"parseFrontMatter\"\n | \"generateFrontMatter\"\n | \"metadataFromFrontmatter\"\n> {\n return {\n // The header its class declared, when it states one; else its metadata.\n frontmatterSchema: frontmatterSchemaOf(definition) ?? definition.metadata,\n // What it writes is still every field its metadata names, and the\n // header's: the serializer keeps from the source only what neither names.\n ...(frontmatterSchemaOf(definition) && {\n writtenFrontmatterSchema: writtenFields(\n definition.metadata,\n frontmatterSchemaOf(definition),\n ),\n }),\n // A whole document handed in, as an import or an editor writes one, had\n // its frontmatter decoded into the metadata already: the type writes the\n // body under its own frontmatter, in its own order, as an entity plugin\n // class's adapter did, and the serializer keeps after it the frontmatter\n // the type does not write.\n toMarkdown(entity): string {\n return encodeEntityMarkdown(definition, {\n ...entity,\n content: documentBody(entity.content),\n });\n },\n fromMarkdown(markdown): Partial<EntityOf<AnyEntityDefinition>> {\n const parsed = parseMarkdownWithFrontmatter(\n markdown,\n rawFrontmatterSchema,\n );\n const decoded = definition.markdown\n ? definition.markdown.decode({\n content: parsed.content,\n frontmatter: parsed.metadata,\n })\n : { content: parsed.content, metadata: parsed.metadata };\n return {\n // A singleton's document is its one record, whose id is the type,\n // as a class adapter read it; a writer that names none, such as\n // Studio creating a missing one, creates it there.\n ...(definition.singleton === true && { id: definition.type }),\n content: decoded.content,\n metadata: definition.metadata.parse(\n currentMetadata(definition, decoded.metadata),\n ),\n };\n },\n parseFrontMatter: (markdown, schemaToParse) =>\n parseMarkdownWithFrontmatter(markdown, schemaToParse).metadata,\n generateFrontMatter(entity): string {\n return generateFrontmatter(encodeParts(definition, entity).frontmatter);\n },\n // Metadata is the frontmatter, decoded by fromMarkdown, so a frontmatter\n // write on an update carries into metadata.\n metadataFromFrontmatter: true,\n };\n}\n\n/**\n * How a binary type's record is its content: stored unchanged, with no\n * frontmatter, its metadata kept beside it. Decoding says what the bytes\n * say, which an import merges with the metadata handed beside the file; the\n * schema checks the record when it is stored.\n */\nfunction binaryStorage(\n definition: AnyEntityDefinition,\n): Pick<\n DeclaredAdapter,\n \"toMarkdown\" | \"fromMarkdown\" | \"parseFrontMatter\" | \"generateFrontMatter\"\n> {\n return {\n toMarkdown: (entity): string => entity.content,\n fromMarkdown(content): Partial<EntityOf<AnyEntityDefinition>> {\n const decoded = definition.markdown?.decode({ content, frontmatter: {} });\n const entityType = definition.type;\n if (!decoded) return { entityType, content };\n // Bytes that say nothing leave the metadata to what is handed beside\n // them, as an adapter that recovers none returns none.\n const said = { ...decoded.metadata };\n return Object.keys(said).length > 0\n ? { entityType, content: decoded.content, metadata: said }\n : { entityType, content: decoded.content };\n },\n parseFrontMatter: (_content, schemaToParse) => schemaToParse.parse({}),\n generateFrontMatter: (): string => \"\",\n };\n}\n\nfunction entityAdapter(definition: AnyEntityDefinition): DeclaredAdapter {\n const schema = entitySchema(definition);\n const stub = definition.stub;\n return {\n entityType: definition.type,\n purpose: definition.purpose,\n schema,\n ...(definition.config?.binaryStorage === undefined\n ? markdownStorage(definition)\n : binaryStorage(definition)),\n // A first-party type that restates its metadata stores it as the\n // document it wrote states it.\n extractMetadata: (entity) =>\n restatedMetadataOf(definition)?.(entity.metadata) ?? entity.metadata,\n getBodyTemplate: () => bodyTemplateOf(definition),\n ...(writesInDocumentOrderOf(definition) && { writesInDocumentOrder: true }),\n // Left off when undeclared: system_generate reads its absence as \"this\n // type does not support queued generation\" and says so.\n ...(stub && {\n buildStub: (input: {\n id: string;\n title: string;\n }): { content: string; metadata: Record<string, unknown> } => {\n const built = stub(input);\n return { content: built.content, metadata: { ...built.metadata } };\n },\n }),\n // Each flag is left off when undeclared: a type that never declared one\n // reads the same as a type written before the flag existed.\n ...(definition.singleton === true && { isSingleton: true }),\n ...(definition.hasBody !== undefined && { hasBody: definition.hasBody }),\n ...(definition.coverImage === true && { supportsCoverImage: true }),\n ...(definition.create?.fields && {\n createFieldsSchema: definition.create.fields,\n }),\n ...(definition.displayTitle && {\n displayTitle: (entity): string | undefined =>\n displayTitle(definition, entity),\n }),\n };\n}\n\n/**\n * The declared label for a stored record. Metadata the schema refuses gets\n * no label rather than an error, so one bad record cannot break a listing;\n * the caller falls back to the record's own title or id.\n */\nfunction displayTitle(\n definition: AnyEntityDefinition,\n entity: { readonly content: string; readonly metadata: unknown },\n): string | undefined {\n const metadata = definition.metadata.safeParse(\n currentMetadata(definition, entity.metadata),\n );\n return metadata.success\n ? definition.displayTitle?.({\n content: entity.content,\n metadata: metadata.data,\n })\n : undefined;\n}\n\nexport async function deriveProjectionUpserts(\n projection: ProjectionDefinition,\n rawSource: ProjectionJsonObject,\n signal: AbortSignal,\n): Promise<ProjectionWriteIntent[]> {\n const source = entitySchema(projection.source).parse(rawSource);\n const intents: ProjectionWriteIntent[] = [];\n await projection.project({\n source,\n signal,\n target: {\n async upsert(entity): Promise<void> {\n const metadata = ProjectionJsonObjectSchema.parse(\n projection.target.metadata.parse(entity.metadata),\n );\n // A derived entry is as private as what it was derived from, unless\n // the projection says otherwise.\n const visibility = entity.visibility ?? source.visibility;\n intents.push({\n operation: \"upsert\",\n entity: {\n id: entity.id,\n entityType: projection.target.type,\n content: applyVisibilityToMarkdown(\n encodeEntityMarkdown(projection.target, {\n content: entity.content,\n metadata,\n }),\n visibility,\n ),\n metadata,\n visibility,\n },\n });\n },\n },\n });\n return intents;\n}\n\ntype BoundEntityWidget = EntityDashboardWidget &\n BoundDashboardWidget<\n AnyDashboardWidgetDefinition,\n Readonly<Record<string, never>>,\n Readonly<Record<string, never>>,\n undefined\n >;\n\nfunction isBoundEntityWidget(\n widget: EntityDashboardWidget,\n): widget is BoundEntityWidget {\n return (\n \"load\" in widget &&\n typeof widget.load === \"function\" &&\n \"view\" in widget.definition &&\n typeof widget.definition.view === \"function\"\n );\n}\n\n/** Entity types have no config, state or settings to bind widgets to. */\nconst entityDashboardBinding: EntityDashboardBindingContext = Object.freeze({\n config: Object.freeze({}),\n state: Object.freeze({}),\n accountSettings: undefined,\n});\n\nfunction boundProjectionInput(\n context: ProjectionInputContext,\n): ProjectionInputContext {\n const entities = context.entities;\n return Object.freeze({\n entities: Object.freeze({\n getEntity: entities.getEntity.bind(entities),\n listEntities: entities.listEntities.bind(entities),\n getEntityTypes: entities.getEntityTypes.bind(entities),\n hasEntityType: entities.hasEntityType.bind(entities),\n getEntityTypeConfig: entities.getEntityTypeConfig.bind(entities),\n isProjectionOwnedEntity: entities.isProjectionOwnedEntity.bind(entities),\n }),\n resolvePrompt: context.resolvePrompt.bind(context),\n appInfo: context.appInfo.bind(context),\n identityInput: context.identityInput.bind(context),\n });\n}\n\nfunction boundProjectionExecution(\n context: ProjectionExecutionContext,\n): ProjectionExecutionContext {\n const ai = context.ai;\n return Object.freeze({\n ai: Object.freeze({\n generate: ai.generate.bind(ai),\n generateObject: ai.generateObject.bind(ai),\n }),\n logger: context.logger,\n });\n}\n\nfunction projectionRule(\n projection: ProjectionDefinition,\n version: string,\n scope: (localId: string) => string,\n): ProjectionRule {\n return defineProjectionRule({\n id: scope(projection.id),\n version,\n sources: [{ kind: \"entity\", types: [projection.source.type] }],\n targetType: projection.target.type,\n inputSchema: ProjectionJsonObjectSchema,\n async selectInput(trigger, context) {\n const selected = trigger.inputs.filter(\n ({ sourceType }) => sourceType === projection.source.type,\n );\n const items = await Promise.all(\n selected.map(async (input): Promise<unknown> => {\n if (input.operation === \"delete\") {\n return { operation: \"delete\", id: input.sourceId };\n }\n // The source is a type of the projection's own package, read as\n // the package reads its own types; what it derives keeps the\n // source's visibility.\n const source = await context.entities.getEntity({\n entityType: input.sourceType,\n id: input.sourceId,\n visibilityScope: internalFullScope(\n \"projection rule loads source entity\",\n ),\n });\n if (!source) {\n throw new Error(\n `Projection \"${projection.id}\" could not load ${input.sourceType}:${input.sourceId}`,\n );\n }\n return { operation: \"upsert\", id: input.sourceId, source };\n }),\n );\n // One recursive validation of the whole envelope; per-item sources are\n // covered by this pass, so they are not parsed individually above.\n return ProjectionJsonObjectSchema.parse({ items });\n },\n async derive(input, _context, signal) {\n const envelope = projectionEnvelopeSchema.parse(input);\n const intents: ProjectionWriteIntent[] = [];\n for (const item of envelope.items) {\n signal.throwIfAborted();\n if (item.operation === \"delete\") {\n intents.push({\n operation: \"delete\",\n entityType: projection.target.type,\n id: item.id,\n });\n continue;\n }\n if (!item.source) {\n throw new Error(\n `Projection \"${projection.id}\" upsert item ${item.id} is missing its source`,\n );\n }\n intents.push(\n ...(await deriveProjectionUpserts(projection, item.source, signal)),\n );\n }\n return intents;\n },\n });\n}\n\nclass DeclarativeEntityPlugin extends EntityPlugin<\n EntityOf<AnyEntityDefinition>,\n Record<string, never>,\n Record<string, never>\n> {\n private readonly entityTypeConfig: EntityTypeConfig;\n private readonly projections: readonly ProjectionDefinition[];\n private readonly scope: (localId: string) => string;\n private readonly validatePersist: AnyEntityDefinition[\"validatePersist\"];\n private readonly seed:\n EntitySeedDefinition<AnyEntityDefinition[\"metadata\"]> | undefined;\n private readonly templates: AnyEntityDefinition[\"templates\"];\n private readonly dataSources: readonly AnyDataSourceDeclaration[];\n private readonly packageDataSourceIds: ReadonlySet<string>;\n private readonly feed:\n EntityFeedDeclaration<EntityOf<AnyEntityDefinition>> | undefined;\n private readonly definition: AnyEntityDefinition;\n private readonly create: EntityCreateRouting | undefined;\n private readonly attachments: AnyEntityDefinition[\"attachments\"];\n private readonly jobs: Readonly<Record<string, AnyEntityJobDeclaration>>;\n private readonly generation: AnyEntityDefinition[\"generation\"];\n private readonly projectionRules: readonly ProjectionRule[];\n /** The types the declaring package owns: its own, when it acts as itself. */\n private readonly packageTypes: ReadonlySet<string>;\n private permissions: PermissionService | undefined;\n private releases: readonly (() => void)[] = [];\n private readonly dashboardWidgetIds: string[] = [];\n private readonly widgetAbort: AbortController = new AbortController();\n public readonly entityType: string;\n public readonly schema: z.ZodType<EntityOf<AnyEntityDefinition>, unknown>;\n public readonly adapter: EntityAdapter<\n EntityOf<AnyEntityDefinition>,\n Record<string, unknown>\n >;\n\n constructor(\n definition: AnyEntityDefinition,\n projections: readonly ProjectionDefinition[],\n metadata: InstalledPluginPackageMetadata,\n scope: (localId: string) => string,\n dataSources: readonly AnyDataSourceDeclaration[],\n packageDataSourceIds: ReadonlySet<string>,\n packageTypes: ReadonlySet<string>,\n ) {\n super(\n scope(definition.type),\n metadata,\n {},\n emptyEntityPluginConfigSchema,\n // Keyed by the bare entity type: the policy is about what may be done\n // to entities of this type, not about which package declared them.\n definition.actions && {\n [definition.type]: entityActionPolicyRuleSchema.parse(\n definition.actions,\n ),\n },\n );\n this.entityTypeConfig = entityTypeConfigFor(\n definition.classification,\n definition.config,\n );\n this.projections = projections;\n this.scope = scope;\n this.validatePersist = definition.validatePersist;\n this.seed = definition.seed;\n this.templates = definition.templates;\n this.dataSources = dataSources;\n this.packageDataSourceIds = packageDataSourceIds;\n this.packageTypes = packageTypes;\n if (definition.feed) feedPathSchema.parse(definition.feed.path);\n this.feed = definition.feed;\n this.definition = definition;\n this.create = definition.create;\n this.attachments = definition.attachments;\n this.jobs = definition.jobs ?? {};\n // A create route names the type's own generation \"generation\"; a job\n // of that name would shadow it. A type without a generation may answer\n // that job name with a contract job, as an entity plugin class that ran\n // another handler under its generation job did.\n const namedGeneration = this.jobs[\"generation\"];\n if (\n namedGeneration !== undefined &&\n (definition.generation !== undefined ||\n contractJobOf(namedGeneration) === undefined)\n ) {\n throw new Error(\n `${definition.type} declares a job named \"generation\", the name a create route gives its own generation`,\n );\n }\n this.generation = definition.generation;\n this.entityType = definition.type;\n this.schema = entitySchema(definition);\n this.adapter = entityAdapter(definition);\n const declared = definition.projectionRules;\n this.projectionRules = (\n typeof declared === \"function\"\n ? declared({ template: (localName) => this.templateName(localName) })\n : (declared ?? [])\n ).map((rule) => this.declaredProjectionRule(rule));\n }\n\n /**\n * A declared rule as the runtime runs it: it writes this type only, and it\n * is handed bound reads and generation, never the services behind them.\n */\n private declaredProjectionRule(rule: ProjectionRule): ProjectionRule {\n if (rule.targetType !== this.entityType) {\n throw new Error(\n `Projection rule \"${rule.id}\" on ${this.entityType} writes ${rule.targetType}`,\n );\n }\n return Object.freeze({\n ...rule,\n selectInput: (\n trigger: Parameters<ProjectionRule[\"selectInput\"]>[0],\n context: ProjectionInputContext,\n signal: AbortSignal,\n ): ReturnType<ProjectionRule[\"selectInput\"]> =>\n rule.selectInput(trigger, boundProjectionInput(context), signal),\n derive: (\n input: Parameters<ProjectionRule[\"derive\"]>[0],\n context: ProjectionExecutionContext,\n signal: AbortSignal,\n ): ReturnType<ProjectionRule[\"derive\"]> =>\n rule.derive(input, boundProjectionExecution(context), signal),\n });\n }\n\n protected override getEntityTypeConfig(): EntityTypeConfig {\n return this.entityTypeConfig;\n }\n\n /** The registered name of a template this type declares. */\n private templateName(localName: string): string {\n if (!Object.hasOwn(this.templates ?? {}, localName)) {\n throw new Error(\n `${this.entityType} declares no template named \"${localName}\"`,\n );\n }\n return `${this.id}:${localName}`;\n }\n\n /** Whether a type is one the declaring package owns. */\n private ownsType(entityType: string): boolean {\n return this.packageTypes.has(entityType);\n }\n\n /**\n * Reads for the package acting as itself, with no caller behind it: its\n * own types at every visibility, other types at its grant, which is public\n * unless the brain grants this plugin more.\n */\n private asPackage(context: EntityPluginContext): EntityReader {\n const self = packageSelfCall(context.jobAuthority);\n return createPackageEntityReader(\n context.entityService,\n () => self,\n (entityType) => this.ownsType(entityType),\n );\n }\n\n private generationHandler(\n context: EntityPluginContext,\n declaration: NonNullable<AnyEntityDefinition[\"generation\"]>,\n jobTypeName: string,\n reportsToPipeline = false,\n readsLegacyRawData = false,\n ): DeclarativeGenerationHandler {\n return new DeclarativeGenerationHandler(this.logger, context, declaration, {\n entityType: this.entityType,\n jobTypeName,\n template: (localName) => this.templateName(localName),\n ownsType: (entityType) => this.ownsType(entityType),\n retries: this.retries,\n reportsToPipeline,\n readsLegacyRawData,\n pluginId: this.id,\n runtime: (authority) =>\n this.runBinding(context, authority, \"a generation\"),\n });\n }\n\n protected override async getInstructions(): Promise<string | undefined> {\n return this.definition.instructions;\n }\n\n /**\n * Answer the content pipeline's schedule for this type: list the material\n * the declaration names, pick from it, and queue this type's generation.\n * Nothing to write from is reported back rather than thrown.\n */\n private async runScheduledGeneration(\n context: EntityPluginContext,\n scheduled: NonNullable<AnyEntityDefinition[\"scheduledGeneration\"]>,\n ): Promise<void> {\n const reportFailure = (error: string): Promise<void> =>\n context.messaging.command(generateReportFailureContract, {\n entityType: this.entityType,\n error,\n });\n try {\n // The package starts this run, so it reads as itself, at its own\n // grant: its own types at every visibility, others as the grant allows.\n const self = packageSelfCall(context.jobAuthority);\n const access = await self.access();\n const scope = (entityType: string): ContentVisibility =>\n packageReadScope(self, this.ownsType(entityType), access);\n const sources = await context.entityService.listEntities({\n entityType: scheduled.from.entityType,\n options: {\n limit: scheduled.from.limit,\n filter: {\n visibilityScope: scope(scheduled.from.entityType),\n ...(scheduled.from.status !== undefined && {\n metadata: { status: scheduled.from.status },\n }),\n },\n },\n });\n const data = await this.scheduledInput(\n context,\n scheduled,\n sources.map(({ id }) => id),\n scope(this.entityType),\n );\n if (!data) {\n await reportFailure(\n `No ${[scheduled.from.status, scheduled.from.entityType]\n .filter(Boolean)\n .join(\" \")} available to write a ${this.entityType} from`,\n );\n return;\n }\n // Queued as the package, so the generation runs as the package too,\n // with the entries it is written from beside its input.\n const queued = {\n ...(await admitPackageJob(context.jobAuthority, undefined, data)),\n ...definedFields({ source: generationSource(data) }),\n };\n // A refused enqueue is retried until the queue admits it; the\n // schedule does not come again until its next cycle.\n await this.retries.run(\n () =>\n context.jobs.enqueue({\n type: `${this.entityType}:generation`,\n data: queued,\n toolContext: {\n interfaceType: \"job\",\n actor: { kind: \"service\", serviceId: this.id },\n },\n }),\n `queue a scheduled ${this.entityType} generation`,\n `generate:${this.entityType}`,\n );\n } catch (error) {\n await reportFailure(getErrorMessage(error));\n }\n }\n\n /** The generation input for a schedule, or null when nothing qualifies. */\n private async scheduledInput(\n context: EntityPluginContext,\n scheduled: NonNullable<AnyEntityDefinition[\"scheduledGeneration\"]>,\n sourceIds: readonly string[],\n visibilityScope: ContentVisibility,\n ): Promise<Record<string, unknown> | null> {\n if (sourceIds.length === 0) return null;\n const sourceEntityType = scheduled.from.entityType;\n if (scheduled.mode === \"batch\") {\n return { sourceEntityType, sourceEntityIds: [...sourceIds] };\n }\n // One at a time: a source this type was already written from — named\n // in its sourceEntityType/sourceEntityId metadata — is skipped.\n const derived = await Promise.all(\n sourceIds.map((sourceEntityId) =>\n context.entityService.listEntities({\n entityType: this.entityType,\n options: {\n filter: {\n visibilityScope,\n metadata: { sourceEntityType, sourceEntityId },\n },\n limit: 1,\n },\n }),\n ),\n );\n const sourceEntityId = sourceIds.find(\n (_id, index) => derived[index]?.length === 0,\n );\n return sourceEntityId ? { sourceEntityType, sourceEntityId } : null;\n }\n\n protected override createGenerationHandler(\n context: EntityPluginContext,\n ): JobHandler | null {\n return this.generation\n ? this.generationHandler(\n context,\n this.generation,\n `${this.entityType}:generation`,\n this.definition.scheduledGeneration !== undefined,\n true,\n )\n : null;\n }\n\n protected override getTemplates(): Record<string, Template> | null {\n const templates = this.templates;\n if (!templates) return null;\n // A template naming one of its package's data sources follows it to the\n // scoped id it registers under; any other id is left as written.\n const local = this.packageDataSourceIds;\n return Object.fromEntries(\n Object.entries(templates).map(([name, template]) => [\n name,\n template.dataSourceId && local.has(template.dataSourceId)\n ? { ...template, dataSourceId: this.scope(template.dataSourceId) }\n : template,\n ]),\n );\n }\n\n protected override getDataSources(): DataSource[] {\n // Scoped to the package, so two packages may each declare \"entities\".\n return this.dataSources.map((declaration) =>\n createDeclaredDataSource(\n declaration,\n this.scope(declaration.id),\n this.logger,\n ),\n );\n }\n\n /** The brain's action policy, read when a job writes. */\n private requiredLevel(\n entityType: string,\n action: \"create\" | \"update\" | \"delete\",\n ): EntityActionRequiredLevel | undefined {\n if (!this.permissions) {\n throw new Error(`Entity type \"${this.entityType}\" is not registered`);\n }\n return this.permissions.getEntityActionRequiredLevel(entityType, action);\n }\n\n override async register(\n shell: IShell,\n registrationContext?: PluginRegistrationContext,\n ): Promise<PluginCapabilities> {\n this.permissions = shell.getPermissionService();\n return super.register(shell, registrationContext);\n }\n\n protected override async onRegister(\n context: EntityPluginContext,\n ): Promise<void> {\n const validatePersist = this.validatePersist;\n if (validatePersist) {\n context.entities.registerPersistValidator(\n this.entityType,\n async (entity): Promise<void> => {\n await validatePersist(\n Object.freeze({\n content: entity.content,\n visibility: entity.visibility,\n }),\n );\n },\n );\n }\n\n // Local names: the jobs namespace scopes each to this type's plugin.\n Object.entries(this.jobs).forEach(([name, declaration]) => {\n const writes: EntityJobWrites = {\n declaring: this.definition,\n requiredLevel: (entityType, action) =>\n this.requiredLevel(entityType, action),\n };\n const ownsType = (entityType: string): boolean =>\n this.ownsType(entityType);\n const contract = contractJobOf(declaration);\n if (contract) {\n // Other packages queue it by its contract's type, so the name it\n // is registered under must scope to exactly that.\n if (contract.type !== `${this.id}:${name}`) {\n throw new Error(\n `Job \"${name}\" of ${this.entityType} answers \"${contract.type}\" but is declared as \"${this.id}:${name}\"`,\n );\n }\n if (\"generate\" in declaration) {\n throw new Error(\n `Contract job \"${contract.type}\" handles its own work`,\n );\n }\n context.jobs.registerHandler(\n name,\n contractJobHandler(\n declaration,\n context,\n ownsType,\n writes,\n this.id,\n (localName) => this.templateName(localName),\n ),\n );\n return;\n }\n context.jobs.registerHandler(\n name,\n \"generate\" in declaration\n ? this.generationHandler(context, declaration, name)\n : entityJobHandler(\n declaration,\n context,\n ownsType,\n writes,\n (authority) => this.runBinding(context, authority, \"a job\"),\n ),\n );\n });\n\n // system_generate queues this type's generation through here, so the\n // job carries the authority of the caller who asked for it.\n if (this.generation) {\n const generationType = `${this.entityType}:generation`;\n context.entities.registerGenerationEnqueuer(\n this.entityType,\n async (data, toolContext): Promise<string> => {\n const { entityId, ...input } = data;\n const queued = await admitPackageJob(\n context.jobAuthority,\n toolCaller(toolContext),\n input,\n );\n return context.jobs.enqueue({\n type: generationType,\n data: {\n ...queued,\n ...(typeof entityId === \"string\" && { entityId }),\n ...definedFields({ source: generationSource(data) }),\n },\n toolContext,\n });\n },\n );\n }\n\n const create = this.create;\n const urlRoute = urlRouteOf(this.definition);\n if (create || urlRoute) {\n this.registerCreateRouting(context, create ?? {}, urlRoute);\n }\n\n // Evals run with no caller, so as the package, with fixtures scoped to\n // this type.\n Object.entries(this.definition.evals ?? {}).forEach(([id, handler]) => {\n context.eval.registerHandler(id, (input) =>\n handler(\n input,\n withEntityRuntime(\n {\n ai: createGenerationAI(context, (localName) =>\n this.templateName(localName),\n ),\n entities: this.asPackage(context),\n fixtures: createEvalFixtures(context, this.entityType),\n },\n // An eval runs with no caller, so its searches read as the\n // package, and so does a generation it runs.\n {\n ...this.runtimeBinding(\n context,\n this.asPackage(context),\n \"an eval\",\n ),\n runGeneration: (input, options) =>\n this.runGenerationForEval(context, input, options),\n },\n ),\n ),\n );\n });\n\n const agentContext = this.definition.agentContext;\n const scheduled = this.definition.scheduledGeneration;\n\n const seed = this.seed;\n const feed = this.feed;\n const atproto = this.definition.atproto;\n const publish = this.definition.publish;\n // Built once, with the plugin's logger, when it is built with one.\n const publishProvider =\n publish && builtProvider(publish.provider, this.logger);\n const publishAssets = this.definition.publishAssets ?? [];\n // Registered through the plugin's scoped analytics, which removes them\n // when the plugin goes.\n for (const declared of this.definition.analyticsReports ?? []) {\n context.analytics.register({\n type: declared.type,\n description: declared.description,\n params: declared.params,\n ...definedFields({ minVisibility: declared.minVisibility }),\n handler: (params, { entityService, visibilityScope }) =>\n declared.report(\n params,\n Object.freeze({\n entities: createEntityReader(entityService, visibilityScope),\n visibility: visibilityScope,\n }),\n ),\n });\n }\n const widgets = (\n this.definition.dashboardWidgets?.(entityDashboardBinding) ?? []\n ).map((widget) => this.boundWidget(widget));\n const widgetIds = widgets.map(({ definition }) => definition.id);\n const repeated = widgetIds.find(\n (id, index) => widgetIds.indexOf(id) !== index,\n );\n if (repeated) {\n throw new Error(\n `${this.entityType} binds dashboard widget \"${repeated}\" twice`,\n );\n }\n // The shell removes a plugin's Inbox sources with the plugin.\n const inbox = this.definition.inbox;\n if (inbox) context.inbox.registerSource(this.inboxSource(context, inbox));\n const media = this.attachments?.length\n ? mediaAttachmentContext(context)\n : undefined;\n this.releases = [\n // First-party answers, under the channels their contracts already\n // have, as the package acting as itself.\n ...entityAnswersOf(this.definition).map((declared) =>\n context.messaging.answer(\n declared.contract,\n async (request, { sender }) =>\n declared.handle({\n request,\n sender,\n entities: await packageEntityWrites(\n context,\n this.id,\n (entityType) => this.ownsType(entityType),\n {\n declaring: this.definition,\n requiredLevel: (entityType, action) =>\n this.requiredLevel(entityType, action),\n },\n ),\n }),\n declared.everyProcess === true ? { everyProcess: true } : {},\n ),\n ),\n ...(agentContext\n ? [\n // Reads are scoped to the asker, so a provider cannot surface\n // restricted records into a public conversation.\n context.messaging.answer(agentContextContract, async (request) => ({\n items: [\n ...(await agentContext(\n Object.freeze({\n request: deepFrozen(structuredClone(request)),\n entities: createEntityReader(\n context.entityService,\n permissionToVisibilityScope(request.userPermissionLevel),\n ),\n }),\n )),\n ],\n })),\n ]\n : []),\n ...(scheduled\n ? [\n context.messaging.answer(\n generateExecuteContract,\n async ({ entityType }) => {\n if (entityType === this.entityType) {\n await this.runScheduledGeneration(context, scheduled);\n }\n },\n ),\n ]\n : []),\n // The runtime keeps the unregister handles, so an author never does.\n ...(this.attachments ?? []).flatMap((attachment) =>\n media\n ? [\n context.attachments.register(\n this.entityType,\n attachment.type,\n attachment.provider(media),\n ),\n ]\n : [],\n ),\n ...(seed\n ? [\n context.messaging.subscribe(\n SEED_TRIGGER_CHANNELS[seed.on],\n async (): Promise<{ success: true }> => {\n await this.plantSeed(context, seed);\n return { success: true };\n },\n ),\n ]\n : []),\n ...(feed\n ? [\n // Builds run in the worker, so every process answers.\n context.messaging.answer(\n siteBuildStagingContract,\n (payload) => this.stageFeed(context, feed, payload),\n { everyProcess: true },\n ),\n ]\n : []),\n // The service scopes each check id to this plugin and hands back the\n // release.\n ...(this.definition.checks ?? []).map((check) =>\n context.recurringChecks.register({\n id: check.id,\n cadence: check.cadence,\n ...definedFields({\n deliverAlerts: check.deliverAlerts,\n includeInInbox: check.includeInInbox,\n }),\n run: ({ signal }) =>\n check.run(\n Object.freeze({\n // A scheduled run has no caller: it reads as the package.\n entities: this.asPackage(context),\n siteUrl: context.siteUrl,\n signal,\n }),\n ),\n }),\n ),\n ...(atproto\n ? [\n context.atprotoProjections.register(\n this.atprotoProjection(context, atproto),\n ),\n ]\n : []),\n ...(widgets.length > 0\n ? [\n // The Dashboard answers registrations once every plugin is in.\n context.messaging.subscribe(\n SYSTEM_CHANNELS.pluginsRegistered,\n async (): Promise<{ success: true }> => {\n await this.registerDashboardWidgets(context, widgets);\n return { success: true };\n },\n ),\n ]\n : []),\n ...(publish || publishAssets.length > 0\n ? [\n // Announced once every plugin is registered, so the publish\n // pipeline is listening whichever order the plugins load in.\n context.messaging.subscribe(\n SYSTEM_CHANNELS.pluginsRegistered,\n async (): Promise<{ success: true }> => {\n if (publish) {\n await context.messaging.command(publishRegisterContract, {\n entityType: this.entityType,\n provider: publishProvider ?? publish.provider,\n config: {\n executionMode: \"provider\",\n ...definedFields({\n publishResultIdField: publish.resultIdField,\n publishTimestampField: publish.timestampField,\n }),\n },\n });\n }\n for (const asset of publishAssets) {\n await context.messaging.command(\n publishAssetRegisterContract,\n {\n entityType: this.entityType,\n attachmentType: asset.attachmentType,\n mediaEntityType: \"image\",\n // The image job records the id in the frontmatter the\n // stored record carries.\n targetEntityField: {\n location: \"frontmatter\",\n field: asset.field,\n },\n ...definedFields({ requiredWhen: asset.requiredWhen }),\n autoGenerate: true,\n },\n );\n }\n return { success: true };\n },\n ),\n ]\n : []),\n ];\n }\n\n private async registerDashboardWidgets(\n context: EntityPluginContext,\n widgets: readonly BoundEntityWidget[],\n ): Promise<void> {\n if (context.executionOnly || !context.dashboard.isAvailable()) return;\n for (const binding of widgets) {\n const registered = await context.dashboard.registerWidget(\n createDeclarativeDashboardWidgetRegistration({\n publicServiceId: this.id,\n packageName: this.packageName,\n config: entityDashboardBinding.config,\n state: entityDashboardBinding.state,\n binding,\n context,\n runtimeSignal: this.widgetAbort.signal,\n }),\n );\n if (registered) this.dashboardWidgetIds.push(binding.definition.id);\n }\n }\n\n /**\n * The binding a declared widget is: a definition's `bind` made it, and it\n * carries the loader the Dashboard calls.\n */\n private boundWidget(widget: EntityDashboardWidget): BoundEntityWidget {\n if (isBoundEntityWidget(widget)) return widget;\n throw new Error(\n `${this.entityType} dashboard widget \"${widget.definition.id}\" is not bound; bind it with defineDashboardWidget(...).bind(context, load)`,\n );\n }\n\n /** Reads of the declaring type alone, up to one visibility. */\n private records(\n context: EntityPluginContext,\n visibilityScope: ContentVisibility,\n ): EntityRecordReader<EntityOf<AnyEntityDefinition>> {\n const definition = this.definition;\n const reader = createEntityReader(context.entityService, visibilityScope);\n return Object.freeze({\n get: (id: string): Promise<EntityOf<AnyEntityDefinition> | null> =>\n reader.get(definition, id),\n list: (\n options?: DataSourceListOptions,\n ): Promise<readonly EntityOf<AnyEntityDefinition>[]> =>\n reader.list(definition, options),\n });\n }\n\n /**\n * The declared Inbox in the shape the Inbox reads. Listing reads as the\n * package; opening and acting are refused below admin, read as the actor,\n * and acting writes only through edits the actor is allowed.\n */\n private inboxSource(\n context: EntityPluginContext,\n declared: EntityInboxDeclaration,\n ): InboxSource {\n // The declaring type is the package's own, so it lists at every visibility.\n const full = internalFullScope(\"the declared Inbox lists as its package\");\n const asActor = (\n actor: InboxActor,\n ): Omit<EntityInboxActionContext<AnyEntityDefinition>, \"edits\"> => {\n const scope = permissionToVisibilityScope(actor.permissionLevel);\n return {\n actor,\n records: this.records(context, scope),\n entities: createEntityReader(context.entityService, scope),\n };\n };\n // An Inbox item is the operator's to open and act on, whichever surface\n // reaches it, so the source itself refuses anyone below admin.\n const requireAdmin = (actor: InboxActor): void => {\n if (actor.permissionLevel !== \"admin\") {\n throw new Error(`${declared.displayName} requires admin permission`);\n }\n };\n const resolveDetail = declared.resolveDetail?.bind(declared);\n return {\n sourceId: declared.sourceId,\n displayName: declared.displayName,\n ...(declared.facets ? { facets: [...declared.facets] } : {}),\n // The listing is gathered for every operator at once, with no viewer\n // behind it, so it reads as the package: its own types at every\n // visibility, other types at its grant.\n list: async (): Promise<InboxItem[]> => [\n ...(await declared.list(\n Object.freeze({\n records: this.records(context, full),\n entities: this.asPackage(context),\n }),\n )),\n ],\n ...(resolveDetail && {\n resolveDetail: (\n itemId: string,\n actor: InboxActor,\n signal: AbortSignal,\n ): Promise<InboxItemDetail> => {\n requireAdmin(actor);\n return resolveDetail(\n itemId,\n Object.freeze({ ...asActor(actor), signal }),\n );\n },\n }),\n act: async (\n itemId: string,\n actionId: string,\n actor: InboxActor,\n ): Promise<void> => {\n requireAdmin(actor);\n return declared.act(\n itemId,\n actionId,\n Object.freeze({\n ...asActor(actor),\n edits: this.inboxEdits(context, actor),\n }),\n );\n },\n };\n }\n\n private inboxEdits(\n context: EntityPluginContext,\n actor: InboxActor,\n ): EntityInboxEdits<AnyEntityDefinition> {\n const entityType = this.entityType;\n const allow = (action: \"update\" | \"publish\" | \"delete\"): void =>\n context.permissions.assertEntityActionAllowed(entityType, action, {\n userPermissionLevel: actor.permissionLevel,\n });\n const own = (record: EntityOf<AnyEntityDefinition>): void => {\n if (record.entityType !== entityType) {\n throw new Error(\n `An Inbox action on ${entityType} cannot write ${record.entityType}`,\n );\n }\n };\n const changed = (): Error =>\n new Error(`The ${entityType} changed meanwhile; try again`);\n const write = async (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n action: \"update\" | \"publish\",\n ): Promise<void> => {\n allow(action);\n own(record);\n const result = await context.entityService.updateEntity({\n entity: {\n ...record,\n content: change.content ?? record.content,\n metadata: this.definition.metadata.parse(\n change.metadata ?? record.metadata,\n ),\n },\n options: { expectedContentHash: record.contentHash },\n });\n if (!result.skipped) return;\n // Skipped is either an identical write or a stale one; only the\n // second means the operator acted on something they did not see.\n const current = await context.entityService.getEntity({\n entityType,\n id: record.id,\n visibilityScope: internalFullScope(\"an Inbox write checks its read\"),\n });\n if (current?.contentHash !== record.contentHash) throw changed();\n };\n return Object.freeze({\n update: (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n ): Promise<void> => write(record, change, \"update\"),\n publish: (\n record: EntityOf<AnyEntityDefinition>,\n change: EntityInboxChange<AnyEntityDefinition>,\n ): Promise<void> => write(record, change, \"publish\"),\n delete: async (record: EntityOf<AnyEntityDefinition>): Promise<void> => {\n allow(\"delete\");\n own(record);\n const deleted = await context.entityService.deleteEntity({\n entityType,\n id: record.id,\n options: { expectedContentHash: record.contentHash },\n });\n if (!deleted) throw changed();\n },\n });\n }\n\n /**\n * The declared projection in the shape the AT Protocol service reads. It\n * reads through its own reader rather than the context the service passes,\n * and it is given the validated record.\n */\n private atprotoProjection(\n context: EntityPluginContext,\n declared: EntityAtprotoProjection<EntityOf<AnyEntityDefinition>>,\n ): AtprotoProjection {\n const definition = this.definition;\n // The record is published to a public PDS, so it is built from public\n // entries only, as the class-based blog projection builds its own.\n const entities = createEntityReader(context.entityService, \"public\");\n const isPublishable = declared.isPublishable?.bind(declared);\n return {\n entityType: this.entityType,\n collection: declared.lexicon.id,\n lexicon: declared.lexicon,\n ...definedFields({ validate: declared.validate }),\n ...(isPublishable && {\n // An invalid record stays publishable so its build fails and is\n // reported, instead of the published record being withdrawn.\n isPublishable: (entity: BaseEntity): boolean => {\n const parsed = entitySchema(definition).safeParse(entity);\n return parsed.success ? isPublishable(parsed.data) : true;\n },\n }),\n // Async, so a record that does not validate rejects rather than throws.\n buildRecord: async ({\n entity,\n config,\n client,\n pageUrl,\n topics,\n dryRun,\n }: AtprotoProjectionBuildInput): Promise<Record<string, unknown>> =>\n declared.buildRecord(\n Object.freeze({\n entity: parseDefinitionEntity(definition, entity),\n config: Object.freeze(\n definedFields({\n brainDid: config.brainDid,\n anchorDid: config.anchorDid,\n }),\n ),\n ...definedFields({\n pageUrl,\n topics: topics && Object.freeze([...topics]),\n }),\n dryRun: dryRun === true,\n entities,\n uploadBlob: async ({\n data,\n mimeType,\n }: {\n readonly data: Uint8Array;\n readonly mimeType: string;\n }): Promise<AtprotoBlobRef> => {\n if (dryRun) {\n return {\n $type: \"blob\",\n ref: { $link: \"dry-run\" },\n mimeType,\n size: data.byteLength,\n };\n }\n if (!client?.uploadBlob) {\n throw new Error(\n \"AT Protocol PDS client does not support blob uploads\",\n );\n }\n return (await client.uploadBlob({ data, mimeType })).blob;\n },\n }),\n ),\n };\n }\n\n private registerCreateRouting(\n context: EntityPluginContext,\n routing: EntityCreateRouting,\n urlRoute: EntityCreateRoute | undefined,\n ): void {\n const interceptCreate: CreateInterceptor = (input, executionContext) =>\n this.routeCreate(context, routing, urlRoute, input, executionContext);\n context.entities.registerCreateInterceptor(\n this.entityType,\n interceptCreate,\n );\n // Checked before the user confirms, through the route the confirmed\n // create will take; a rejecting route says so before anyone is asked.\n context.entities.registerCreateCheck(\n this.entityType,\n async (input, executionContext): Promise<CreateCheckResult> => {\n const route = selectCreateRoute(routing, input, urlRoute);\n if (!route) return { kind: \"ready\" };\n if (\"reject\" in route) return { kind: \"error\", error: route.reject };\n return route.check\n ? route.check(this.createContext(context, input, executionContext))\n : { kind: \"ready\" };\n },\n );\n // The upload endpoint routes by media type into the same route, rather\n // than a second handler re-implementing the create.\n const mediaTypes = routing.fromUpload?.mediaTypes ?? [];\n if (mediaTypes.length > 0) {\n context.entities.registerUploadSaveHandler({\n entityType: this.entityType,\n mediaTypes: [...mediaTypes],\n handler: createUploadSaveHandler(\n this.entityType,\n interceptCreate,\n `Upload was not handled by ${this.entityType}`,\n ),\n });\n }\n }\n\n private async routeCreate(\n context: EntityPluginContext,\n routing: EntityCreateRouting,\n urlRoute: EntityCreateRoute | undefined,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateInterceptionResult> {\n const route = selectCreateRoute(routing, input, urlRoute);\n if (!route) return { kind: \"continue\", input };\n if (\"reject\" in route) {\n return { kind: \"handled\", result: createFailure(route.reject) };\n }\n const resolution = await route.resolve(\n this.createContext(context, input, executionContext),\n );\n // A first-party route handing on to the ordinary create, which system\n // create performs at the caller's authority.\n const ordinary = ordinaryCreateOf(resolution);\n if (ordinary) {\n const { targetEntityId } = ordinary.options;\n const targeted: CreateInput = {\n ...input,\n ...(targetEntityId !== undefined && { targetEntityId }),\n };\n if (!ordinary.document) return { kind: \"continue\", input: targeted };\n const { fields: _folded, ...request } = targeted;\n const { title, content, fields } = ordinary.document;\n return {\n kind: \"continue\",\n input: {\n ...request,\n ...(title !== undefined && { title }),\n content,\n ...(fields !== undefined && { fields: { ...fields } }),\n },\n };\n }\n // First-party routes that queue a contract job, or store a complete\n // record, as an entity plugin's interceptor did.\n const queued = queuedCreateOf(resolution);\n if (queued) {\n return {\n kind: \"handled\",\n result: await this.applyQueuedCreate(\n context,\n queued,\n input,\n executionContext,\n ),\n };\n }\n const stored = storedCreateOf(resolution);\n if (stored) {\n return {\n kind: \"handled\",\n result: await this.applyStoredCreate(\n context,\n stored,\n input,\n executionContext,\n ),\n };\n }\n return {\n kind: \"handled\",\n result: await this.applyCreateResolution(\n context,\n resolution,\n input,\n executionContext,\n ),\n };\n }\n\n /** What a route decides with: frozen, read-only, at the caller's level. */\n private createContext(\n context: EntityPluginContext,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): EntityCreateContext {\n const uploads = context.uploads.scoped(sharedUploadScope);\n const reader: EntityCreateUploadReader = Object.freeze({\n read: (uploadId: string) => uploads.read(uploadId),\n readRecord: (uploadId: string) => uploads.readRecord(uploadId),\n });\n const entities = createEntityReader(\n context.entityService,\n this.callerScope(executionContext),\n );\n return withEntityRuntime(\n {\n input: deepFrozen(structuredClone(input)),\n entities,\n uploads: reader,\n },\n {\n ...this.runtimeBinding(context, entities, \"a create route\", {\n // Targets and sources are read public, as the entity plugin\n // classes' create interceptors read them.\n service: context.entityService,\n scope: () => \"public\",\n }),\n caller: createCaller(executionContext),\n },\n );\n }\n\n /**\n * The internal runtime a first-party definition reaches through\n * entityRuntime(context): searches at the call's visibility, reads of the\n * package's own types as the package, and free-form structured AI.\n */\n private runtimeBinding(\n context: EntityPluginContext,\n reader: EntityReader,\n where: string,\n anyEntities?: AnyEntityBinding,\n ): EntityRuntimeBinding {\n return {\n where,\n reader,\n ...(anyEntities && { anyEntities }),\n packageReader: this.asPackage(context),\n ownsType: (entityType) => this.ownsType(entityType),\n searchWithDistances: (request) =>\n context.entityService.searchWithDistances(request),\n generateObject: (prompt, schema) =>\n context.ai.generateObject(prompt, schema),\n generate: createGenerationAI(context, (localName) =>\n this.templateName(localName),\n ).generate,\n logger: context.logger,\n identity: Object.freeze({\n getProfile: () => context.identity.getProfile(),\n }),\n };\n }\n\n /**\n * Run this type's generation in process for an eval, queued as the\n * package, as an entity plugin class's eval ran its generation handler.\n */\n private async runGenerationForEval(\n context: EntityPluginContext,\n input: unknown,\n options: EvalGenerationOptions = {},\n ): Promise<EvalGenerationResult> {\n const generation = this.generation;\n if (!generation) {\n throw new Error(`Entity \"${this.entityType}\" declares no generation`);\n }\n const handler = this.generationHandler(\n context,\n generation,\n `${this.entityType}:generation`,\n this.definition.scheduledGeneration !== undefined,\n );\n const data = handler.validateAndParse(\n await admitPackageJob(context.jobAuthority, undefined, input),\n );\n if (data === null) {\n return { success: false, error: \"Invalid generation input\" };\n }\n const onProgress = options.onProgress;\n const reporter = onProgress\n ? CallbackProgressReporter.from(async ({ progress, message }) => {\n await onProgress({ progress, ...definedFields({ message }) });\n })\n : undefined;\n const result = await handler.process(\n data,\n `eval-${Date.now()}`,\n reporter ?? CallbackProgressReporter.noop(),\n );\n return { ...result };\n }\n\n /**\n * The internal runtime a declared job or generation reaches: reads at the\n * run's authority, untyped ones at the scope the run reads each type at.\n */\n private runBinding(\n context: EntityPluginContext,\n authority: EntityRunAuthority,\n where: string,\n ): EntityRuntimeBinding {\n return this.runtimeBinding(context, authority.entities, where, {\n service: context.entityService,\n scope: (entityType) => authority.scope(entityType),\n });\n }\n\n private callerScope(\n executionContext: CreateExecutionContext,\n ): ContentVisibility {\n return permissionToVisibilityScope(executionContext.userPermissionLevel);\n }\n\n /**\n * Perform what a route decided. The runtime writes, so \"created\" and\n * \"updated\" describe what it did rather than what the route claims.\n */\n private async applyCreateResolution(\n context: EntityPluginContext,\n resolution: EntityCreateResolution | EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n if (\"delegate\" in resolution) {\n return this.applyDelegation(context, resolution, input, executionContext);\n }\n if (\"refuse\" in resolution) return createFailure(resolution.refuse);\n const visibilityScope = this.callerScope(executionContext);\n const eventContext = {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n };\n\n if (\"existing\" in resolution) {\n const found = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.existing.id,\n visibilityScope,\n });\n if (!found) {\n return createFailure(\n `Cannot reuse ${this.entityType} \"${resolution.existing.id}\", which does not exist`,\n );\n }\n return this.createSuccess(\"existing\", found.id, resolution.attachment);\n }\n\n const target =\n \"update\" in resolution ? resolution.update : resolution.create;\n const metadata = this.definition.metadata.safeParse(target.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n\n if (\"update\" in resolution) {\n const existing = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.update.id,\n visibilityScope,\n });\n if (!existing) {\n return createFailure(\n `Cannot update ${this.entityType} \"${resolution.update.id}\", which does not exist`,\n );\n }\n const written = await context.entityService.updateEntity({\n entity: {\n ...existing,\n content: resolution.update.content,\n metadata: metadata.data,\n },\n options: { eventContext },\n });\n return this.createSuccess(\"updated\", written.entityId, undefined);\n }\n\n // Create-if-absent: a route that did not see an entity at the caller's\n // level must not write over one it could not see.\n const taken = await context.entityService.getEntity({\n entityType: this.entityType,\n id: resolution.create.id,\n visibilityScope: internalFullScope(\n \"create route checks whether its id is taken; no content returned\",\n ),\n });\n const alreadyExists = `A ${this.entityType} \"${resolution.create.id}\" already exists`;\n if (taken) return createFailure(alreadyExists);\n try {\n const written = await context.entityService.createEntity({\n entity: {\n id: resolution.create.id,\n entityType: this.entityType,\n content: resolution.create.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && {\n visibility: input.visibility,\n }),\n },\n options: {\n conditionalWrite: { expectedRevision: null },\n eventContext,\n },\n });\n return this.createSuccess(\n \"created\",\n written.entityId,\n resolution.attachment,\n );\n } catch (error) {\n if (error instanceof EntityWriteConflictError) {\n return createFailure(alreadyExists);\n }\n throw error;\n }\n }\n\n private invalid(error: z.ZodError): string {\n return `Invalid ${this.entityType} metadata: ${error.issues\n .map(({ path, message }) => `${path.join(\".\") || \"metadata\"}: ${message}`)\n .join(\"; \")}`;\n }\n\n /**\n * Allocate now and finish later: write the placeholder (or find the\n * entity), then queue the declared job with its id. Everything the\n * delegation needs is checked before anything is written.\n */\n private async applyDelegation(\n context: EntityPluginContext,\n allocation: EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const { job: name, input: jobInput = {} } = allocation.delegate;\n // \"generation\" names the type's own generation, queued as\n // `{type}:generation`, as an entity plugin's interceptor queued it.\n const ownGeneration = name === \"generation\" ? this.generation : undefined;\n const job =\n ownGeneration ??\n (Object.hasOwn(this.jobs, name) ? this.jobs[name] : undefined);\n if (!job) {\n return createFailure(\n `${this.entityType} declares no job named \"${name}\"`,\n );\n }\n const allocates = \"create\" in allocation || \"existing\" in allocation;\n if (allocates && !(\"generate\" in job)) {\n return createFailure(\n `${this.entityType} job \"${name}\" does not generate, so it cannot fill an allocated entity`,\n );\n }\n const parsed = job.input.safeParse(jobInput);\n if (!parsed.success) {\n return createFailure(\n `Invalid input for ${this.entityType} job \"${name}\": ${parsed.error.issues\n .map(({ message }) => message)\n .join(\"; \")}`,\n );\n }\n // The caller starts the job, and it runs with their authority, so they\n // are admitted before anything is written; one the brain no longer\n // knows is refused here.\n const queued = await admitPackageJob(\n context.jobAuthority,\n toolCaller(executionContext),\n jobInput,\n );\n\n const allocated = await this.allocate(\n context,\n allocation,\n input,\n executionContext,\n );\n if (\"error\" in allocated) return createFailure(allocated.error);\n const entityId = allocated.entityId;\n try {\n const data = {\n ...queued,\n ...(entityId !== undefined && { entityId }),\n ...(\"generate\" in job &&\n definedFields({ source: generationSource(input) })),\n };\n const jobId = ownGeneration\n ? // Content work for the caller who asked, routed back to their\n // conversation, as the classes queued their generations.\n await context.jobs.enqueue({\n type: `${this.entityType}:generation`,\n data,\n toolContext: executionContext,\n options: {\n source: this.id,\n metadata: { operationType: \"content_operations\" },\n },\n })\n : await context.jobs.enqueue({\n type: name,\n // The input as written, and beside it the entity a generation\n // fills and the entries the request named it from.\n data,\n toolContext: {\n interfaceType: executionContext.interfaceType,\n actor: executionContext.actor,\n ...definedFields({ channelId: executionContext.channelId }),\n },\n });\n const attachment =\n \"attachment\" in allocation ? allocation.attachment : undefined;\n return {\n success: true,\n data: {\n status: \"generating\",\n jobId,\n ...(entityId !== undefined && {\n entityId,\n ...(attachment && {\n attachment: createResultAttachmentSchema.parse(\n attachment({ entityId }),\n ),\n }),\n }),\n },\n };\n } catch (error) {\n // No job will fill a placeholder this create wrote; left behind it\n // would stay generating forever.\n if (allocated.written) {\n await context.entityService\n .deleteEntity({ entityType: this.entityType, id: allocated.written })\n .catch((cleanupError: unknown) => {\n this.logger.warn(\"Failed to remove an unqueued placeholder\", {\n entityId: allocated.written,\n error: cleanupError,\n });\n });\n }\n return createFailure(getErrorMessage(error));\n }\n }\n\n private async allocate(\n context: EntityPluginContext,\n allocation: EntityCreateAllocation,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<\n | { readonly entityId: string | undefined; readonly written?: string }\n | { readonly error: string }\n > {\n if (\"existing\" in allocation) {\n const found = await context.entityService.getEntity({\n entityType: this.entityType,\n id: allocation.existing.id,\n visibilityScope: this.callerScope(executionContext),\n });\n return found\n ? { entityId: found.id }\n : {\n error: `Cannot reuse ${this.entityType} \"${allocation.existing.id}\", which does not exist`,\n };\n }\n if (!(\"create\" in allocation)) return { entityId: undefined };\n const metadata = this.definition.metadata.safeParse(\n allocation.create.metadata,\n );\n if (!metadata.success) return { error: this.invalid(metadata.error) };\n // Deduplicated, as system_generate's stub is: a second import lands\n // beside the first rather than on top of it.\n const written = await context.entityService.createEntity({\n entity: {\n id: allocation.create.id,\n entityType: this.entityType,\n content: allocation.create.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && { visibility: input.visibility }),\n },\n options: {\n deduplicateId: true,\n eventContext: {\n actor: executionContext.actor,\n interfaceType: executionContext.interfaceType,\n },\n },\n });\n return { entityId: written.entityId, written: written.entityId };\n }\n\n /**\n * Write a first-party route's pending placeholder and queue the contract\n * job that fills it, as an entity plugin's interceptor did: a record that\n * already has the id is reused rather than written over, and one at a\n * different visibility than asked is refused.\n */\n private async applyQueuedCreate(\n context: EntityPluginContext,\n queued: QueuedCreate,\n _input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const attributed = isAttributed(queued);\n const placeholder = queued.placeholder;\n if (placeholder?.deduplicate === true) {\n return this.applyDeduplicatedQueuedCreate(\n context,\n queued,\n placeholder,\n executionContext,\n );\n }\n if (placeholder) {\n const metadata = this.definition.metadata.safeParse(placeholder.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const pending = await createPendingEntity({\n entityService: context.entityService,\n entity: {\n id: queued.entityId,\n entityType: this.entityType,\n content: placeholder.content,\n metadata: metadata.data,\n ...(placeholder.visibility !== undefined && {\n visibility: placeholder.visibility,\n }),\n created: now,\n updated: now,\n },\n ...(attributed && {\n options: { eventContext: eventContextOf(executionContext) },\n }),\n });\n if (\n placeholder.visibility !== undefined &&\n pending.existingEntity !== undefined &&\n pending.existingEntity.visibility !== placeholder.visibility\n ) {\n // Answered, as an entity plugin's interceptor answered it.\n return createFailure(\n `Existing ${this.entityType} visibility \"${pending.existingEntity.visibility}\" does not match requested visibility \"${placeholder.visibility}\".`,\n );\n }\n }\n return this.queueFill(context, queued, executionContext, queued.entityId);\n }\n\n /**\n * A queued create whose placeholder lands beside a record that has its id,\n * as an entity plugin's interceptor wrote one with `deduplicateId`: the\n * job is queued for, and the answer names, the id written, and the job's\n * input may be built from the placeholder as stored.\n */\n private async applyDeduplicatedQueuedCreate(\n context: EntityPluginContext,\n queued: QueuedCreate,\n placeholder: NonNullable<QueuedCreate[\"placeholder\"]>,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const metadata = this.definition.metadata.safeParse(placeholder.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const created = await context.entityService.createEntity({\n entity: {\n id: queued.entityId,\n entityType: this.entityType,\n content: placeholder.content,\n metadata: metadata.data,\n ...(placeholder.visibility !== undefined && {\n visibility: placeholder.visibility,\n }),\n created: now,\n updated: now,\n },\n options: {\n deduplicateId: true,\n ...(isAttributed(queued) && {\n eventContext: eventContextOf(executionContext),\n }),\n },\n });\n return this.queueFill(context, queued, executionContext, created.entityId);\n }\n\n /** The record a queued create's job fills, as it is stored now. */\n private async writtenPlaceholder(\n context: EntityPluginContext,\n entityId: string,\n ): Promise<QueuedPlaceholder> {\n const written = await context.entityService.getEntity({\n entityType: this.entityType,\n id: entityId,\n visibilityScope: internalFullScope(\n \"read back the placeholder a queued create wrote; only its hash is handed on\",\n ),\n });\n if (!written) {\n throw new Error(\n `The ${this.entityType} placeholder \"${entityId}\" could not be read back`,\n );\n }\n return { entityId: written.id, contentHash: written.contentHash };\n }\n\n /** Queue a queued create's job and answer generating. */\n private async queueFill(\n context: EntityPluginContext,\n queued: QueuedCreate,\n executionContext: CreateExecutionContext,\n entityId: string,\n ): Promise<CreateResult> {\n const { data } = queued.job;\n // Input built from the placeholder reads it back as it was stored.\n const jobId = await context.jobs.enqueue(\n queued.job.contract,\n typeof data === \"function\"\n ? data(await this.writtenPlaceholder(context, entityId))\n : data,\n ...(isAttributed(queued)\n ? [\n {\n options: attributionJobOptions(\n this.id,\n executionContext,\n typeof queued.job.attributed === \"object\"\n ? queued.job.attributed\n : undefined,\n ),\n },\n ]\n : []),\n );\n return {\n success: true,\n data: {\n entityId,\n status: \"generating\",\n jobId,\n ...(queued.attachment !== undefined && {\n attachment: createResultAttachmentSchema.parse(queued.attachment),\n }),\n },\n };\n }\n\n /**\n * Store a first-party route's complete record as the caller, under the\n * next free id when this one is taken, as an entity plugin's interceptor\n * did.\n */\n private async applyStoredCreate(\n context: EntityPluginContext,\n stored: StoredCreate,\n input: CreateInput,\n executionContext: CreateExecutionContext,\n ): Promise<CreateResult> {\n const metadata = this.definition.metadata.safeParse(stored.record.metadata);\n if (!metadata.success) return createFailure(this.invalid(metadata.error));\n const now = new Date().toISOString();\n const written = await context.entityService.createEntity({\n entity: {\n id: stored.record.id,\n entityType: this.entityType,\n content: stored.record.content,\n metadata: metadata.data,\n ...(input.visibility !== undefined && { visibility: input.visibility }),\n created: now,\n updated: now,\n },\n options: {\n deduplicateId: true,\n eventContext: eventContextOf(executionContext),\n },\n });\n return {\n success: true,\n data: {\n entityId: written.entityId,\n status: \"created\",\n attachment: createResultAttachmentSchema.parse(\n stored.attachment({ entityId: written.entityId }),\n ),\n },\n };\n }\n\n private createSuccess(\n status: \"created\" | \"existing\" | \"updated\",\n entityId: string,\n attachment:\n ((written: { readonly entityId: string }) => unknown) | undefined,\n ): CreateResult {\n return {\n success: true,\n data: {\n status,\n entityId,\n ...(attachment && {\n attachment: createResultAttachmentSchema.parse(\n attachment({ entityId }),\n ),\n }),\n },\n };\n }\n\n protected override async onShutdown(): Promise<void> {\n this.releases.forEach((release) => release());\n this.releases = [];\n this.widgetAbort.abort();\n const context = this.getContext();\n const widgetIds = this.dashboardWidgetIds.splice(0);\n await Promise.all(\n widgetIds.map(async (widgetId) => {\n try {\n await context.dashboard.unregisterWidget(widgetId);\n } catch (error) {\n // Shutdown goes on: a Dashboard that is already gone holds nothing\n // to unregister, and one widget must not keep the others up.\n this.logger.error(\n \"Failed to unregister a declared dashboard widget\",\n {\n widgetId,\n error: getErrorMessage(error),\n },\n );\n }\n }),\n );\n }\n\n /**\n * Write this type's feed into the staged build. A failure is reported\n * rather than thrown, so the build fails and keeps the previous generation\n * instead of publishing one without the feed.\n */\n private async stageFeed(\n context: EntityPluginContext,\n feed: EntityFeedDeclaration<EntityOf<AnyEntityDefinition>>,\n payload: SiteBuildStagingPayload,\n ): Promise<void> {\n try {\n await this.writeFeed(context, feed, payload);\n } catch (error) {\n this.logger.error(`Failed to write the ${this.entityType} feed`, error);\n payload.reportFailure(\n `${this.entityType} feed generation failed: ${getErrorMessage(error)}`,\n );\n }\n }\n\n private async writeFeed(\n context: EntityPluginContext,\n feed: EntityFeedDeclaration<EntityOf<AnyEntityDefinition>>,\n payload: SiteBuildStagingPayload,\n ): Promise<void> {\n // A preview carries everything so an author sees unpublished work in\n // place; a production build carries what the type counts as published.\n const stored = await context.entityService.listEntities({\n entityType: this.entityType,\n options: {\n limit: 1000,\n publishedOnly: payload.environment === \"production\",\n },\n });\n const build = Object.freeze({ environment: payload.environment });\n const items = stored.flatMap((entity) => {\n const item = feed.toItem(\n parseDefinitionEntity(this.definition, entity),\n build,\n );\n return item ? [feedItemSchema.parse(item)] : [];\n });\n if (items.length === 0) return;\n\n // The channel links the site as configured and the feed hangs off it;\n // item links join the site and the route with one slash. This is the\n // feed the blog plugin class wrote, byte for byte.\n const siteUrl = payload.siteConfig.url ?? \"https://example.com\";\n const siteRoot = siteUrl.replace(/\\/+$/u, \"\");\n const xml = renderRssFeed(\n items,\n {\n title: payload.siteConfig.title ?? feed.title ?? \"Feed\",\n description:\n payload.siteConfig.description ??\n feed.description ??\n \"Latest updates\",\n link: siteUrl,\n selfLink: `${siteUrl}/${feed.path}`,\n },\n (item) =>\n `${siteRoot}/${payload\n .generateEntityUrl(this.entityType, item.slug)\n .replace(/^\\/+/u, \"\")}`,\n );\n const target = join(payload.outputDir, feed.path);\n await mkdir(dirname(target), { recursive: true });\n await writeFile(target, xml, \"utf8\");\n }\n\n /** Create-if-absent: a seed never overwrites authored content. */\n private async plantSeed(\n context: EntityPluginContext,\n seed: EntitySeedDefinition<AnyEntityDefinition[\"metadata\"]>,\n ): Promise<void> {\n const existing = await context.entityService.getEntity({\n entityType: this.entityType,\n id: seed.id,\n visibilityScope: internalFullScope(\n \"seed checks whether its default exists; no content returned\",\n ),\n });\n if (existing) return;\n try {\n await context.entityService.createEntity({\n entity: {\n id: seed.id,\n entityType: this.entityType,\n content: seed.content(),\n metadata: seed.metadata ?? {},\n },\n options: { conditionalWrite: { expectedRevision: null } },\n });\n } catch (error) {\n // Someone created it between the read and the write; theirs stays.\n if (!(error instanceof EntityWriteConflictError)) throw error;\n }\n }\n\n protected override getProjectionRules(): ProjectionRule[] {\n return [\n ...this.projections.map((projection) =>\n projectionRule(projection, this.version, this.scope),\n ),\n ...this.projectionRules,\n ];\n }\n}\n\n/**\n * The plugin that registers a package data source: an entity data source\n * with the type it reads, a fetching one with the package's first type.\n */\nfunction installsDataSource(\n source: AnyDataSourceDeclaration,\n definition: AnyEntityDefinition,\n entities: readonly AnyEntityDefinition[],\n): boolean {\n return source.kind === \"rizom-entity-data-source\"\n ? source.entity === definition\n : entities[0] === definition;\n}\n\nexport function createEntityPackagePlugins(\n entities: readonly AnyEntityDefinition[],\n projections: readonly ProjectionDefinition[],\n metadata: InstalledPluginPackageMetadata,\n scope: (localId: string) => string,\n dataSources: readonly AnyDataSourceDeclaration[] = [],\n // Each entity with the slots its package's `configure` bound, keyed by\n // the declared definition, which projections and data sources name.\n bound: ReadonlyMap<AnyEntityDefinition, AnyEntityDefinition> = new Map(),\n): DeclarativeEntityPlugin[] {\n const packageDataSourceIds = new Set(dataSources.map(({ id }) => id));\n const packageTypes = new Set(entities.map(({ type }) => type));\n return entities.map(\n (definition) =>\n new DeclarativeEntityPlugin(\n bound.get(definition) ?? definition,\n projections.filter(({ source }) => source === definition),\n metadata,\n scope,\n dataSources.filter((source) =>\n installsDataSource(source, definition, entities),\n ),\n packageDataSourceIds,\n packageTypes,\n ),\n );\n}\n",
357
357
  "import type {\n EntityTypeClassification,\n EntityTypeConfig,\n} from \"@brains/entity-service\";\nimport { entityActionPolicyRuleSchema } from \"@brains/templates\";\nimport { definedFields } from \"@brains/utils/strip-undefined\";\nimport { z } from \"@brains/utils/zod\";\n\n/** Classification is a top-level declaration, so config never carries it. */\ntype DeclarableEntityTypeConfigKey = Exclude<\n keyof EntityTypeConfig,\n \"classification\"\n>;\n\ntype SortFieldSchema = z.ZodObject<\n {\n field: z.ZodString;\n direction: z.ZodEnum<{ asc: \"asc\"; desc: \"desc\" }>;\n nullsFirst: z.ZodOptional<z.ZodBoolean>;\n nullsLast: z.ZodOptional<z.ZodBoolean>;\n },\n z.core.$strict\n>;\n\ntype FieldNamesSchema = z.ZodOptional<z.ZodArray<z.ZodString>>;\n\nconst sortFieldSchema: SortFieldSchema = z.strictObject({\n field: z.string().min(1).max(100),\n direction: z.enum([\"asc\", \"desc\"]),\n nullsFirst: z.boolean().optional(),\n nullsLast: z.boolean().optional(),\n});\n\nconst fieldNamesSchema: FieldNamesSchema = z\n .array(z.string().min(1))\n .optional();\n\n/**\n * The entity-type settings a declaration may set: main's `EntityTypeConfig`\n * minus `classification`. Every key is optional, and an omitted key keeps\n * the runtime default.\n */\nexport const entityDefinitionConfigSchema: z.ZodObject<\n {\n weight: z.ZodOptional<z.ZodNumber>;\n embeddable: z.ZodOptional<z.ZodBoolean>;\n fullTextSearchable: z.ZodOptional<z.ZodBoolean>;\n includeInBroadSearch: z.ZodOptional<z.ZodBoolean>;\n binaryStorage: z.ZodOptional<\n z.ZodEnum<{ \"data-url\": \"data-url\"; asset: \"asset\" }>\n >;\n defaultSort: z.ZodOptional<z.ZodArray<SortFieldSchema>>;\n markdownImport: z.ZodOptional<z.ZodBoolean>;\n retiredFields: FieldNamesSchema;\n translatedFields: FieldNamesSchema;\n actionPolicy: z.ZodOptional<typeof entityActionPolicyRuleSchema>;\n projectionSource: z.ZodOptional<z.ZodBoolean>;\n projectionSourceRole: z.ZodOptional<\n z.ZodEnum<{\n canonical: \"canonical\";\n primary: \"primary\";\n secondary: \"secondary\";\n supporting: \"supporting\";\n ambient: \"ambient\";\n excluded: \"excluded\";\n }>\n >;\n containedIn: z.ZodOptional<z.ZodString>;\n publish: z.ZodOptional<\n z.ZodObject<{ publishStatuses: z.ZodArray<z.ZodString> }, z.core.$strict>\n >;\n },\n z.core.$strict\n> = z.strictObject({\n weight: z.number().optional(),\n embeddable: z.boolean().optional(),\n fullTextSearchable: z.boolean().optional(),\n includeInBroadSearch: z.boolean().optional(),\n binaryStorage: z.enum([\"data-url\", \"asset\"]).optional(),\n defaultSort: z.array(sortFieldSchema).max(10).optional(),\n markdownImport: z.boolean().optional(),\n retiredFields: fieldNamesSchema,\n translatedFields: fieldNamesSchema,\n actionPolicy: entityActionPolicyRuleSchema.optional(),\n projectionSource: z.boolean().optional(),\n projectionSourceRole: z\n .enum([\n \"canonical\",\n \"primary\",\n \"secondary\",\n \"supporting\",\n \"ambient\",\n \"excluded\",\n ])\n .optional(),\n containedIn: z.string().min(1).optional(),\n publish: z\n .strictObject({ publishStatuses: z.array(z.string().min(1)) })\n .optional(),\n} satisfies Record<DeclarableEntityTypeConfigKey, z.ZodType>);\n\nexport type EntityDefinitionConfig = z.input<\n typeof entityDefinitionConfigSchema\n>;\n\n/**\n * The registration config for a declared type: its classification plus the\n * declared settings, validated and copied so later edits to the author's\n * object cannot reach the registry.\n */\nexport function entityTypeConfigFor(\n classification: EntityTypeClassification,\n config: EntityDefinitionConfig | undefined,\n): EntityTypeConfig {\n const { defaultSort, actionPolicy, ...settings } =\n entityDefinitionConfigSchema.parse(config ?? {});\n return {\n classification,\n ...definedFields({\n ...settings,\n defaultSort: defaultSort?.map(({ field, direction, ...nulls }) => ({\n field,\n direction,\n ...definedFields(nulls),\n })),\n actionPolicy: actionPolicy && definedFields(actionPolicy),\n }),\n };\n}\n\ntype ScheduledGenerationSchema = z.ZodObject<\n {\n from: z.ZodObject<\n {\n entityType: z.ZodString;\n status: z.ZodOptional<z.ZodString>;\n limit: z.ZodNumber;\n },\n z.core.$strict\n >;\n mode: z.ZodEnum<{ each: \"each\"; batch: \"batch\" }>;\n },\n z.core.$strict\n>;\n\n/**\n * Where a scheduled generation takes its material from when the content\n * pipeline asks for one and nothing says what to write about.\n */\nexport const entityScheduledGenerationSchema: ScheduledGenerationSchema =\n z.strictObject({\n from: z.strictObject({\n /** A type, not a package: the material is a fact about the content. */\n entityType: z.string().min(1),\n /** The metadata status a source must have. */\n status: z.string().min(1).optional(),\n limit: z.number().int().min(1).max(100),\n }),\n /** `each` writes from one source not yet written from; `batch` from all. */\n mode: z.enum([\"each\", \"batch\"]),\n });\n\nexport type EntityScheduledGenerationDeclaration = z.input<\n typeof entityScheduledGenerationSchema\n>;\n",
358
358
  "import {\n SdkError,\n type ActorRef,\n type BusContract,\n type ImageAspectRatio,\n type JobContract,\n type PublishMediaData,\n type PublishProvider,\n} from \"@brains/contracts\";\nimport {\n findEntityByIdentifier,\n type BaseEntity,\n type ContentVisibility,\n type CreateResultAttachment,\n type IEntityService,\n type SearchWithDistancesRequest,\n} from \"@brains/entity-service\";\nimport { isPlainRecord } from \"@brains/utils/predicates\";\nimport type { Logger } from \"@brains/utils/logger\";\nimport { z } from \"@brains/utils/zod\";\nimport type { AnchorProfile } from \"../contracts/identity\";\nimport type { AIGenerationSchema } from \"./ai-types\";\nimport type { UniqueTitleContext } from \"../service/create-entity-with-unique-title\";\nimport type {\n AnyEntityDefinition,\n DataSourceListOptions,\n EntityCreateRoute,\n EntityCreateUploadReader,\n EntityGenerationAI,\n EntityGenerationDeclaration,\n EntityGenerationLink,\n EntityGenerationResult,\n EntityJobDeclaration,\n EntityJobHandleContext,\n EntityOf,\n EntityReader,\n EntityVisibility,\n OwnedEntityWriter,\n} from \"./entity-definition-contract\";\nimport type { SchemaValue } from \"../internal/schema-value\";\nimport type { RetryScope } from \"@brains/utils/retry-backoff\";\nimport type { EntityPluginContext } from \"./context\";\n\n/**\n * Internal runtime capabilities bound to one entity callback's call, for\n * first-party packages only. A first-party definition reaches them with\n * `entityRuntime(context)` on the context it was handed; the public context\n * types never name them, and a context the runtime did not build has none.\n *\n * Each keeps the call's authority: `nearest` reads at the call's\n * visibility, `packageEntities` reads only the declaring package's own\n * types, at every visibility, and nothing here writes.\n *\n * Tracked in docs/legacy-code-inventory.json (first-party-entity-runtime)\n * until each capability is public or no first-party package needs it.\n */\nexport interface EntityRuntime {\n /**\n * Records of a type nearest a query by embedding distance, closest\n * first, at the call's visibility. Distances are raw cosine distances.\n */\n nearest<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n query: string,\n options?: { maxDistance?: number; limit?: number },\n ): Promise<Array<{ entity: EntityOf<TDefinition>; distance: number }>>;\n /** One structured AI answer to a free-form prompt. */\n generateObject<TSchema extends z.ZodType>(\n prompt: string,\n schema: TSchema,\n ): Promise<z.output<TSchema>>;\n /**\n * Generate through a template the declaring type declares, named\n * locally, in the brain's voice when asked, as the entity plugin classes\n * generated through their own templates from create routes, checks and\n * job handlers. A template the type does not declare is refused.\n */\n readonly generate: EntityGenerationAI[\"generate\"];\n /**\n * A generation's result, marked so the runtime creates it under the next\n * free id when its own is taken, as the entity plugin classes' generations\n * created with `deduplicateId`. A generation filling an allocated entity\n * keeps that entity's id. Elsewhere the mark has no effect.\n */\n deduplicated<TResult extends EntityGenerationResult>(\n result: TResult,\n ): TResult;\n /** Reads of the declaring package's own types, at every visibility. */\n readonly packageEntities: EntityReader;\n /**\n * A create route's answer that hands the request to the ordinary create,\n * as an entity plugin's interceptor continued it: unchanged, or with this\n * title and content in place of the request's and its stated fields\n * folded in, and with the target it resolved. The runtime performs it at\n * the caller's authority, choosing the id from the title and making it\n * unique, as the direct create does. Outside the brain's runtime it is a\n * refusal.\n */\n ordinaryCreate(\n document?: OrdinaryCreateDocument,\n options?: OrdinaryCreateOptions,\n ): { readonly refuse: string };\n /**\n * A create route's answer that writes a pending placeholder and queues a\n * contract job to fill it, as an entity plugin's interceptor did: an\n * existing record with the placeholder's id is reused, not written over,\n * and one at a different visibility than the request asks is refused. The\n * placeholder is written as the caller, and the route is answered\n * `generating` with the job's id. Outside the brain's runtime it is a\n * refusal.\n */\n queued(create: QueuedCreate): { readonly refuse: string };\n /**\n * A create route's answer that writes a complete record as the caller,\n * under the next free id when this one is taken, and answers `created`.\n * Outside the brain's runtime it is a refusal.\n */\n stored(create: StoredCreate): { readonly refuse: string };\n /**\n * Untyped reads of any entity type, at the call's own scope: a data\n * source's build scope (public, and only published records in a\n * production build), a create route's public scope, which the entity\n * plugin classes read targets and sources at, or a contract job's run.\n * They never widen the scope and never write.\n */\n readonly anyEntities: AnyEntityReads;\n /** The brain's image model. Contract jobs only. */\n readonly images: EntityRuntimeImages;\n /** Attachments other types provide, such as a rendered PDF. Contract jobs only. */\n readonly attachments: EntityRuntimeAttachments;\n /** The runtime's shared upload store. Contract jobs only. */\n readonly uploads: EntityCreateUploadReader;\n /** The package's logger, as its entity plugin class logged. */\n readonly logger: Logger;\n /**\n * Who asked for a create: the interface, the signed-in user and the\n * channel, when there is one, as the entity plugin classes' interceptors\n * read their execution context. Create routes and their checks only.\n */\n readonly caller: EntityCreateCaller;\n /** The anchor's profile, as the shell's identity holds it now, read-only. */\n readonly identity: EntityRuntimeIdentity;\n /**\n * Run the declaring type's own generation on this input, in process and\n * as the package, writing its record to the store, and answer the job's\n * result, as the entity plugin classes' evals ran their generation\n * handlers. Evals only.\n */\n runGeneration(\n input: unknown,\n options?: EvalGenerationOptions,\n ): Promise<EvalGenerationResult>;\n /**\n * Point a target at a record of the declaring type: set a field, or add\n * to a list, dropping entries the record supersedes. The generation-link\n * rules hold, and the target must be visible at the run's read scope, but\n * the target's action policy does not: the write is the runtime's own\n * follow-up on a target the enqueuer named, as the entity plugin classes'\n * job handlers made it. Contract jobs only.\n */\n linkInto(entityId: string, link: EntityGenerationLink): Promise<void>;\n}\n\n/** A step of an eval's generation run, as its job reported it. */\nexport interface EvalGenerationProgress {\n readonly progress: number;\n readonly message?: string | undefined;\n}\n\nexport interface EvalGenerationOptions {\n /** Told each progress step the generation reports. */\n readonly onProgress?:\n ((step: EvalGenerationProgress) => void | Promise<void>) | undefined;\n}\n\n/** A generation job's result: whether it wrote, what, and its extras. */\nexport type EvalGenerationResult = Readonly<Record<string, unknown>> & {\n readonly success: boolean;\n readonly entityId?: string | undefined;\n readonly error?: string | undefined;\n};\n\n/** Read-only, untyped reads across entity types. */\nexport interface AnyEntityReads {\n getEntityTypes(): string[];\n listEntities(request: {\n readonly entityType: string;\n readonly options?:\n | {\n readonly filter?:\n | {\n readonly metadata?:\n Readonly<Record<string, unknown>> | undefined;\n }\n | undefined;\n readonly limit?: number | undefined;\n }\n | undefined;\n }): Promise<BaseEntity[]>;\n getEntity(request: {\n readonly entityType: string;\n readonly id: string;\n }): Promise<BaseEntity | null>;\n /** A record by its id, else its slug, else its title. */\n find(entityType: string, identifier: string): Promise<BaseEntity | null>;\n}\n\nexport interface EntityRuntimeIdentity {\n getProfile(): AnchorProfile;\n}\n\nexport interface EntityRuntimeImages {\n canGenerate(): boolean;\n generate(\n prompt: string,\n options?: { readonly aspectRatio?: ImageAspectRatio | undefined },\n ): Promise<{ readonly dataUrl: string }>;\n}\n\nexport interface EntityRuntimeAttachments {\n resolve(request: {\n readonly sourceEntityType: string;\n readonly sourceEntityId: string;\n readonly attachmentType: string;\n }): Promise<PublishMediaData | undefined>;\n}\n\n/** The reads an untyped view reads through. */\nexport type AnyEntityService = Pick<\n IEntityService,\n \"getEntityTypes\" | \"listEntities\" | \"getEntity\" | \"getEntityRaw\"\n>;\n\n/** What an ordinary create takes in place of the request's own. */\nexport interface OrdinaryCreateDocument {\n readonly title?: string | undefined;\n readonly content: string;\n readonly fields?: Readonly<Record<string, unknown>> | undefined;\n}\n\nexport interface OrdinaryCreateOptions {\n /** The request's target, resolved from the identifier it named. */\n readonly targetEntityId?: string | undefined;\n}\n\n/** The placeholder a queued create wrote, which its job fills. */\nexport interface QueuedPlaceholder {\n readonly entityId: string;\n readonly contentHash: string;\n}\n\n/** How a queued create's job is attributed to the caller who asked. */\nexport interface QueuedAttribution {\n /** Unset, content work. */\n readonly operationType?: \"content_operations\" | \"data_processing\" | undefined;\n /** The caller's channel, so the job's progress reaches them there. */\n readonly channel?: boolean | undefined;\n}\n\nexport interface QueuedCreate {\n /** The record the job fills, and the answer names. */\n readonly entityId: string;\n /** Unset, the job fills a record that already exists. */\n readonly placeholder?:\n | {\n readonly content: string;\n readonly metadata: Readonly<Record<string, unknown>>;\n /** Unset, the store's default; set, a record that exists at another is refused. */\n readonly visibility?: EntityVisibility | undefined;\n /**\n * Write it under the next free id when `entityId` is taken, rather\n * than reuse the record that has it; the job fills, and the answer\n * names, the id written.\n */\n readonly deduplicate?: boolean | undefined;\n }\n | undefined;\n readonly job: {\n readonly contract: JobContract<z.ZodType>;\n /**\n * The job's input, or the input built from the placeholder as written:\n * its id and the content hash of what was stored.\n */\n readonly data:\n | Readonly<Record<string, unknown>>\n | ((written: QueuedPlaceholder) => unknown);\n /**\n * Queue the job with the caller as its requester, for attribution: as\n * content work, or as the attribution says, routed to the caller's\n * channel when it asks for one.\n */\n readonly attributed?: boolean | QueuedAttribution | undefined;\n };\n /** Unset, the answer names no attachment. */\n readonly attachment?: CreateResultAttachment | undefined;\n}\n\nexport interface StoredCreate {\n readonly record: {\n readonly id: string;\n readonly content: string;\n readonly metadata: Readonly<Record<string, unknown>>;\n };\n attachment(written: { readonly entityId: string }): CreateResultAttachment;\n}\n\n/** Where an untyped view reads, and the scope it reads each type at. */\nexport interface AnyEntityBinding {\n readonly service: AnyEntityService;\n /** Unset, the service is already scoped, as a build's is. */\n readonly scope?: ((entityType: string) => ContentVisibility) | undefined;\n}\n\n/** What a contract job's own capabilities are bound to. */\nexport interface ContractJobRuntimeBinding {\n readonly images: EntityRuntimeImages;\n readonly attachments: EntityRuntimeAttachments;\n readonly uploads: EntityCreateUploadReader;\n readonly logger: Logger;\n linkInto(entityId: string, link: EntityGenerationLink): Promise<void>;\n}\n\n/** What binding a call's runtime needs. */\n/** Who asked for a create, as a first-party create route sees them. */\nexport interface EntityCreateCaller {\n readonly interfaceType: string;\n readonly actor: ActorRef;\n /** The signed-in user, when the actor is one. */\n readonly userId?: string | undefined;\n readonly channelId?: string | undefined;\n readonly channelName?: string | undefined;\n}\n\nexport interface EntityRuntimeBinding {\n /** Where the call is, for the refusals of what it does not offer. */\n readonly where: string;\n /** Reads at the call's visibility. */\n readonly reader: EntityReader;\n /** Reads as the package acting as itself. */\n readonly packageReader: EntityReader;\n readonly ownsType: (entityType: string) => boolean;\n readonly searchWithDistances: (\n request: SearchWithDistancesRequest,\n ) => Promise<\n Array<{ entityId: string; entityType: string; distance: number }>\n >;\n readonly generateObject: <T>(\n prompt: string,\n schema: AIGenerationSchema<T>,\n ) => Promise<{ object: T }>;\n readonly anyEntities?: AnyEntityBinding | undefined;\n /** Template generation for the declaring type; unset, refused. */\n readonly generate?: EntityGenerationAI[\"generate\"] | undefined;\n /** The package's logger; unset, a contract job's, else refused. */\n readonly logger?: Logger | undefined;\n readonly identity?: EntityRuntimeIdentity | undefined;\n readonly job?: ContractJobRuntimeBinding | undefined;\n /** Who asked, for a create route and its check; unset, refused. */\n readonly caller?: EntityCreateCaller | undefined;\n /** The declaring type's generation, run in process; unset, refused. */\n readonly runGeneration?: EntityRuntime[\"runGeneration\"] | undefined;\n}\n\nconst entityRuntimeKey = Symbol(\"rizom.entity-runtime\");\nconst ordinaryCreateKey = Symbol(\"rizom.ordinary-create\");\nconst queuedCreateKey = Symbol(\"rizom.queued-create\");\nconst storedCreateKey = Symbol(\"rizom.stored-create\");\nconst contractJobKey = Symbol(\"rizom.contract-job\");\nconst deduplicatedKey = Symbol(\"rizom.deduplicated-generation\");\n\n/** Whether a generation's result asks to land under the next free id. */\nexport function isDeduplicated(result: object): boolean {\n return Reflect.get(result, deduplicatedKey) === true;\n}\n\n/** A resolution only the runtime's accessor marks; elsewhere, a refusal. */\nfunction marked(\n key: symbol,\n refuse: string,\n value: object,\n): {\n readonly refuse: string;\n} {\n const resolution = { refuse };\n Object.defineProperty(resolution, key, {\n value: Object.freeze(value),\n enumerable: false,\n });\n return Object.freeze(resolution);\n}\n\n/**\n * The ordinary create a route answered, when it did: the document it hands\n * on, or `null` for the request as made, and the target it resolved.\n */\nexport function ordinaryCreateOf(resolution: object):\n | {\n document: OrdinaryCreateDocument | null;\n options: OrdinaryCreateOptions;\n }\n | undefined {\n const found: unknown = Reflect.get(resolution, ordinaryCreateKey);\n if (typeof found !== \"object\" || found === null) return undefined;\n const document: unknown = Reflect.get(found, \"document\");\n const targetEntityId: unknown = Reflect.get(found, \"targetEntityId\");\n return {\n document: isOrdinaryCreateDocument(document) ? document : null,\n options: typeof targetEntityId === \"string\" ? { targetEntityId } : {},\n };\n}\n\n/** The queued create a route answered, when it did. */\nexport function queuedCreateOf(resolution: object): QueuedCreate | undefined {\n const found: unknown = Reflect.get(resolution, queuedCreateKey);\n return isQueuedCreate(found) ? found : undefined;\n}\n\n/** The stored create a route answered, when it did. */\nexport function storedCreateOf(resolution: object): StoredCreate | undefined {\n const found: unknown = Reflect.get(resolution, storedCreateKey);\n return isStoredCreate(found) ? found : undefined;\n}\n\nfunction isQueuedCreate(value: unknown): value is QueuedCreate {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"entityId\") === \"string\" &&\n typeof Reflect.get(value, \"job\") === \"object\"\n );\n}\n\nfunction isStoredCreate(value: unknown): value is StoredCreate {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"record\") === \"object\" &&\n typeof Reflect.get(value, \"attachment\") === \"function\"\n );\n}\n\nfunction isOrdinaryCreateDocument(\n value: unknown,\n): value is OrdinaryCreateDocument {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"content\") === \"string\"\n );\n}\n\n/**\n * A first-party job another package queues through its contract: the\n * queued data is the contract's input, as written, with no requester\n * beside it, and the job runs as the package, as the entity plugin\n * classes' job handlers did. Declared in a type's `jobs` under the local\n * name its contract's type scopes to; the runtime refuses any other.\n */\nexport function contractJob<TInputSchema extends z.ZodType>(\n contract: JobContract<TInputSchema>,\n declaration: {\n handle(\n context: EntityJobHandleContext<z.output<TInputSchema>>,\n ): Promise<unknown>;\n },\n): EntityJobDeclaration<TInputSchema> {\n const job = { input: contract.input, handle: declaration.handle };\n Object.defineProperty(job, contractJobKey, {\n value: Object.freeze({ type: contract.type }),\n enumerable: false,\n });\n return Object.freeze(job);\n}\n\n/** The contract a declared job answers, when it is a contract job. */\nexport function contractJobOf(\n declaration: object,\n): { readonly type: string } | undefined {\n const found: unknown = Reflect.get(declaration, contractJobKey);\n if (typeof found !== \"object\" || found === null) return undefined;\n const type: unknown = Reflect.get(found, \"type\");\n return typeof type === \"string\" ? { type } : undefined;\n}\n\nfunction ownTypesOnly(\n reader: EntityReader,\n ownsType: (entityType: string) => boolean,\n): EntityReader {\n const own = (definition: AnyEntityDefinition): void => {\n if (!ownsType(definition.type)) {\n throw new SdkError(\"permission_denied\", {\n message: `Package reads reach only the package's own types, not \"${definition.type}\"`,\n });\n }\n };\n return Object.freeze({\n get: async <TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n id: string,\n ): Promise<EntityOf<TDefinition> | null> => {\n own(definition);\n return reader.get(definition, id);\n },\n list: async <TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n options?: DataSourceListOptions,\n ): Promise<readonly EntityOf<TDefinition>[]> => {\n own(definition);\n return reader.list(definition, options);\n },\n count: async (definition: AnyEntityDefinition): Promise<number> => {\n own(definition);\n return reader.count(definition);\n },\n });\n}\n\n/** A capability a call does not offer, refused with where it is. */\nfunction unavailable(name: string, where: string): () => never {\n return (): never => {\n throw new Error(`${name} is not available in ${where}`);\n };\n}\n\nfunction unavailableGenerate(where: string): EntityGenerationAI[\"generate\"] {\n return async (): Promise<never> => {\n throw new Error(`generate is not available in ${where}`);\n };\n}\n\nfunction unavailableAnyEntities(where: string): AnyEntityReads {\n const refuse = unavailable(\"anyEntities\", where);\n return Object.freeze({\n getEntityTypes: refuse,\n listEntities: refuse,\n getEntity: refuse,\n find: refuse,\n });\n}\n\nfunction unavailableJob(where: string): ContractJobRuntimeBinding {\n return {\n images: Object.freeze({\n canGenerate: unavailable(\"images\", where),\n generate: unavailable(\"images\", where),\n }),\n attachments: Object.freeze({\n resolve: unavailable(\"attachments\", where),\n }),\n uploads: Object.freeze({\n read: unavailable(\"uploads\", where),\n readRecord: unavailable(\"uploads\", where),\n }),\n linkInto: unavailable(\"linkInto\", where),\n logger: unavailableLogger(where),\n };\n}\n\n/** A logger that refuses, where the call has none to offer. */\nfunction unavailableLogger(where: string): Logger {\n const refuse = unavailable(\"logger\", where);\n return Object.freeze({\n silly: refuse,\n verbose: refuse,\n debug: refuse,\n info: refuse,\n warn: refuse,\n error: refuse,\n child: refuse,\n setUseStderr: refuse,\n });\n}\n\n/**\n * Untyped reads through the service the call is bound to. Requests are\n * rebuilt from the fields the view names, so no scope or option a caller\n * passes can widen what the call may read.\n */\nfunction scopedAnyEntities({\n service,\n scope,\n}: AnyEntityBinding): AnyEntityReads {\n const visibility = (\n entityType: string,\n ): { visibilityScope?: ContentVisibility } =>\n scope ? { visibilityScope: scope(entityType) } : {};\n return Object.freeze({\n getEntityTypes: (): string[] => service.getEntityTypes(),\n listEntities: (\n request: Parameters<AnyEntityReads[\"listEntities\"]>[0],\n ): Promise<BaseEntity[]> => {\n const metadata = request.options?.filter?.metadata;\n const limit = request.options?.limit;\n const scoped = visibility(request.entityType);\n return service.listEntities({\n entityType: request.entityType,\n options: {\n ...((metadata !== undefined || scoped.visibilityScope) && {\n filter: {\n ...(metadata !== undefined && { metadata: { ...metadata } }),\n ...scoped,\n },\n }),\n ...(limit !== undefined && { limit }),\n },\n });\n },\n getEntity: (\n request: Parameters<AnyEntityReads[\"getEntity\"]>[0],\n ): Promise<BaseEntity | null> =>\n service.getEntity({\n entityType: request.entityType,\n id: request.id,\n ...visibility(request.entityType),\n }),\n find: (\n entityType: string,\n identifier: string,\n ): Promise<BaseEntity | null> =>\n findEntityByIdentifier(\n service,\n entityType,\n identifier,\n undefined,\n scope ? scope(entityType) : undefined,\n ),\n });\n}\n\nfunction bindEntityRuntime(binding: EntityRuntimeBinding): EntityRuntime {\n const job = binding.job ?? unavailableJob(binding.where);\n return Object.freeze({\n async nearest<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n query: string,\n options: { maxDistance?: number; limit?: number } = {},\n ): Promise<Array<{ entity: EntityOf<TDefinition>; distance: number }>> {\n const { maxDistance, limit } = options;\n const hits = (\n await binding.searchWithDistances({\n query,\n types: [definition.type],\n ...(maxDistance !== undefined && { maxDistance }),\n })\n )\n .filter(\n (hit) =>\n hit.entityType === definition.type &&\n (maxDistance === undefined || hit.distance <= maxDistance),\n )\n .sort((left, right) => left.distance - right.distance);\n // Read at the call's visibility: a hit the caller may not see is not\n // a hit.\n const read = await Promise.all(\n hits.map(async (hit) => ({\n entity: await binding.reader.get(definition, hit.entityId),\n distance: hit.distance,\n })),\n );\n const visible = read.flatMap(({ entity, distance }) =>\n entity ? [{ entity, distance }] : [],\n );\n return limit === undefined ? visible : visible.slice(0, limit);\n },\n async generateObject<TSchema extends z.ZodType>(\n prompt: string,\n schema: TSchema,\n ): Promise<z.output<TSchema>> {\n const { object } = await binding.generateObject(prompt, schema);\n return schema.parse(object);\n },\n generate: binding.generate ?? unavailableGenerate(binding.where),\n deduplicated: <TResult extends EntityGenerationResult>(\n result: TResult,\n ): TResult => {\n const marked = { ...result };\n Object.defineProperty(marked, deduplicatedKey, {\n value: true,\n enumerable: false,\n });\n return marked;\n },\n packageEntities: ownTypesOnly(binding.packageReader, binding.ownsType),\n ordinaryCreate: (\n document?: OrdinaryCreateDocument,\n options?: OrdinaryCreateOptions,\n ): { readonly refuse: string } =>\n marked(\n ordinaryCreateKey,\n \"This create hands on to the ordinary create, which only the brain's runtime performs\",\n {\n document: document ? Object.freeze({ ...document }) : null,\n ...(options?.targetEntityId !== undefined && {\n targetEntityId: options.targetEntityId,\n }),\n },\n ),\n queued: (create: QueuedCreate): { readonly refuse: string } =>\n marked(\n queuedCreateKey,\n \"This create queues a job, which only the brain's runtime performs\",\n { ...create },\n ),\n stored: (create: StoredCreate): { readonly refuse: string } =>\n marked(\n storedCreateKey,\n \"This create stores a record, which only the brain's runtime performs\",\n { ...create },\n ),\n anyEntities: binding.anyEntities\n ? scopedAnyEntities(binding.anyEntities)\n : unavailableAnyEntities(binding.where),\n images: job.images,\n attachments: job.attachments,\n uploads: job.uploads,\n logger: binding.logger ?? job.logger,\n get caller(): EntityCreateCaller {\n if (!binding.caller) {\n throw new Error(`caller is not available in ${binding.where}`);\n }\n return binding.caller;\n },\n identity:\n binding.identity ??\n Object.freeze({ getProfile: unavailable(\"identity\", binding.where) }),\n runGeneration:\n binding.runGeneration ??\n (async (): Promise<never> => {\n throw new Error(`runGeneration is not available in ${binding.where}`);\n }),\n linkInto: (entityId: string, link: EntityGenerationLink): Promise<void> =>\n job.linkInto(entityId, link),\n });\n}\n\n/**\n * A data source's read context with its internal runtime: untyped reads at\n * the build's own scope. A data source neither searches, generates, writes\n * nor creates, so those refuse.\n */\nexport function withDataSourceRuntime<TContext extends object>(\n context: TContext,\n service: AnyEntityService,\n): Readonly<TContext> {\n const where = \"a data source\";\n const refusingReader: EntityReader = Object.freeze({\n get: unavailable(\"packageEntities\", where),\n list: unavailable(\"packageEntities\", where),\n count: unavailable(\"packageEntities\", where),\n });\n const runtime = bindEntityRuntime({\n where,\n reader: refusingReader,\n packageReader: refusingReader,\n ownsType: () => true,\n searchWithDistances: unavailable(\"nearest\", where),\n generateObject: unavailable(\"generateObject\", where),\n anyEntities: { service },\n });\n Object.defineProperty(context, entityRuntimeKey, {\n value: runtime,\n enumerable: false,\n writable: false,\n configurable: false,\n });\n return Object.freeze(context);\n}\n\n/**\n * A callback context the runtime hands a definition, with the call's\n * internal runtime attached under a key no public type names. The context\n * is frozen afterwards, as every callback context is.\n */\nexport function withEntityRuntime<TContext extends object>(\n context: TContext,\n binding: EntityRuntimeBinding,\n): Readonly<TContext> {\n Object.defineProperty(context, entityRuntimeKey, {\n value: bindEntityRuntime(binding),\n enumerable: false,\n writable: false,\n configurable: false,\n });\n return Object.freeze(context);\n}\n\nfunction isEntityRuntime(value: unknown): value is EntityRuntime {\n return typeof value === \"object\" && value !== null;\n}\n\n/** What a first-party entity type's answer is handed with its request. */\nexport interface EntityAnswerContext<TRequest> {\n readonly request: TRequest;\n /** The package that asked, as the bus stamps it. */\n readonly sender: string;\n /** The declaring type's reads and writes, as the package acting as itself. */\n readonly entities: EntityReader & OwnedEntityWriter<AnyEntityDefinition>;\n}\n\n/** A first-party contract a type answers, declared with `entityAnswer`. */\nexport interface EntityAnswerDeclaration {\n readonly contract: BusContract<z.ZodType, z.ZodType>;\n handle(context: EntityAnswerContext<unknown>): Promise<unknown>;\n readonly everyProcess?: boolean | undefined;\n}\n\n/**\n * Answer a first-party contract under the channel it already has, such as\n * `note:capture`, as an entity plugin class answered it: as the package,\n * in the main process, or in every process with `everyProcess`. The\n * request reaches the handler parsed, and the answer reaches the asker\n * only when the contract accepts it; a failure crosses in its public form.\n */\nexport function entityAnswer<\n const TRequest extends z.ZodType,\n const TResponse extends z.ZodType,\n>(\n contract: BusContract<TRequest, TResponse>,\n handle: (\n context: EntityAnswerContext<z.output<TRequest>>,\n ) => Promise<SchemaValue<TResponse>>,\n options: { readonly everyProcess?: boolean | undefined } = {},\n): EntityAnswerDeclaration {\n const erased: BusContract<z.ZodType, z.ZodType> = contract;\n return Object.freeze({\n contract: erased,\n handle: (context: EntityAnswerContext<unknown>): Promise<unknown> =>\n handle({ ...context, request: contract.schema.parse(context.request) }),\n ...(options.everyProcess === true && { everyProcess: true }),\n });\n}\n\nconst entityAnswersKey = Symbol(\"rizom.entity-answers\");\n\n/**\n * A first-party definition that also answers first-party contracts: the\n * definition, carrying its answers under a key no public type names, which\n * the runtime registers with the type and releases when it shuts down.\n */\nexport function answering<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n answers: readonly EntityAnswerDeclaration[],\n): TDefinition {\n return Object.freeze({\n ...definition,\n [entityAnswersKey]: Object.freeze([...answers]),\n });\n}\n\n/** The first-party answers a definition carries, when it carries any. */\nexport function entityAnswersOf(\n definition: object,\n): readonly EntityAnswerDeclaration[] {\n const found: unknown = Reflect.get(definition, entityAnswersKey);\n return Array.isArray(found) ? found : [];\n}\n\nconst bodyTemplateKey = Symbol(\"rizom.body-template\");\n\n/**\n * A first-party definition whose body has a shape of its own: the\n * definition, carrying the scaffold its body starts from under a key no\n * public type names, which the runtime gives as its adapter's body template\n * (the vault's template for a new record).\n */\nexport function withBodyTemplate<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n template: string,\n): TDefinition {\n return Object.freeze({ ...definition, [bodyTemplateKey]: template });\n}\n\n/** The body template a definition carries, or none. */\nexport function bodyTemplateOf(definition: object): string {\n const found: unknown = Reflect.get(definition, bodyTemplateKey);\n return typeof found === \"string\" ? found : \"\";\n}\n\nconst restatedMetadataKey = Symbol(\"rizom.restated-metadata\");\n\n/** The metadata as the document a type wrote states it. */\nexport type RestateMetadata = (\n metadata: Readonly<Record<string, unknown>>,\n) => Record<string, unknown>;\n\n/**\n * A first-party definition whose encode writes what its metadata did not\n * state, such as a slug resolved from the title: the definition, carrying\n * how its metadata reads once that document is written, under a key no\n * public type names. The runtime stores the restated metadata beside the\n * document, so the next write reads what the file now says.\n */\nexport function restatingMetadata<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n restate: RestateMetadata,\n): TDefinition {\n return Object.freeze({ ...definition, [restatedMetadataKey]: restate });\n}\n\n/** How a definition restates its stored metadata, when it does. */\nexport function restatedMetadataOf(\n definition: object,\n): RestateMetadata | undefined {\n const found: unknown = Reflect.get(definition, restatedMetadataKey);\n return typeof found === \"function\"\n ? (metadata): Record<string, unknown> => {\n const restated: unknown = Reflect.apply(found, undefined, [metadata]);\n return isPlainRecord(restated) ? restated : { ...metadata };\n }\n : undefined;\n}\n\nconst urlRouteKey = Symbol(\"rizom.url-route\");\n\n/**\n * A first-party definition created from a URL alone, as system_create hands\n * on a `source: { kind: \"url\" }` request and as the link plugin class's\n * interceptor captured it: the route that decides such a create, carried\n * under a key no public type names. A URL beside content or a prompt goes\n * the way content or a prompt goes.\n */\nexport function withUrlRoute<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n route: EntityCreateRoute<TDefinition[\"metadata\"]>,\n): TDefinition {\n return Object.freeze({ ...definition, [urlRouteKey]: route });\n}\n\n/** The route a definition carries for a create from a URL alone. */\nexport function urlRouteOf(definition: object): EntityCreateRoute | undefined {\n const found: unknown = Reflect.get(definition, urlRouteKey);\n return isCreateRoute(found) ? found : undefined;\n}\n\nfunction isCreateRoute(value: unknown): value is EntityCreateRoute {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof Reflect.get(value, \"resolve\") === \"function\"\n );\n}\n\nconst documentOrderKey = Symbol(\"rizom.document-order\");\n\n/**\n * A first-party definition whose files people write by hand: each write\n * keeps the frontmatter order of the document it replaces, as an entity\n * plugin class's adapter wrote over existing frontmatter, so directory sync\n * leaves a file it only read as it was.\n */\nexport function writesInDocumentOrder<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n): TDefinition {\n return Object.freeze({ ...definition, [documentOrderKey]: true });\n}\n\nconst frontmatterSchemaKey = Symbol(\"rizom.frontmatter-schema\");\n\n/**\n * A first-party definition whose file header is not its metadata states the\n * header as its frontmatter schema, as its entity plugin class's adapter\n * declared it: what Studio's editor, the Obsidian vault's templates and\n * frontmatter extensions read as the type's frontmatter. A type that\n * states none keeps its metadata as its frontmatter. A type whose metadata\n * holds what its class kept out of the file (a generation error, a stated\n * slug) states the file's own, with the defaults its class gave.\n */\nexport function withFrontmatterSchema<TDefinition extends AnyEntityDefinition>(\n definition: TDefinition,\n schema: z.ZodObject,\n): TDefinition {\n return Object.freeze({ ...definition, [frontmatterSchemaKey]: schema });\n}\n\n/** The frontmatter schema a definition states, if it states one. */\nexport function frontmatterSchemaOf(\n definition: object,\n): z.ZodObject | undefined {\n const schema: unknown = Reflect.get(definition, frontmatterSchemaKey);\n return schema instanceof z.ZodObject ? schema : undefined;\n}\n\n/** Whether a definition writes in the order of the document it replaces. */\nexport function writesInDocumentOrderOf(definition: object): boolean {\n return Reflect.get(definition, documentOrderKey) === true;\n}\n\n/** How a first-party generation came out: the record it wrote, or why it failed. */\nexport type GenerationOutcome =\n | {\n readonly written: {\n readonly entityId: string;\n /** The title the record was written with, when it has one. */\n readonly title?: string | undefined;\n };\n }\n | { readonly failed: string };\n\n/**\n * What a first-party generation's step after it runs is given: its parsed\n * input (none when the input itself was refused), how it came out, and the\n * package's messaging, jobs and retry scope, as an entity plugin class's\n * generation handler had them.\n */\nexport interface GenerationOutcomeContext<TInput> {\n readonly input: TInput | undefined;\n readonly outcome: GenerationOutcome;\n readonly messaging: EntityPluginContext[\"messaging\"];\n readonly jobs: EntityPluginContext[\"jobs\"];\n readonly retries: RetryScope;\n}\n\nexport type GenerationStep<TInput> = (\n context: GenerationOutcomeContext<TInput>,\n) => Promise<void>;\n\nconst providerBuilders = new WeakMap<\n PublishProvider,\n (logger: Logger) => PublishProvider\n>();\n\n/**\n * A first-party publish provider built with the plugin's logger, as an\n * entity plugin class built its client with a child of its own: the runtime\n * builds it when it hands the provider to the publish pipeline. Until then\n * it carries its name and refuses to publish.\n */\nexport function loggedProvider(\n name: string,\n build: (logger: Logger) => PublishProvider,\n): PublishProvider {\n const provider: PublishProvider = Object.freeze({\n name,\n publish: async (): Promise<never> => {\n throw new Error(\n `Publish provider \"${name}\" is built by the brain's runtime`,\n );\n },\n });\n providerBuilders.set(provider, build);\n return provider;\n}\n\n/** The provider to hand the publish pipeline: built, when it is built with a logger. */\nexport function builtProvider(\n provider: PublishProvider,\n logger: Logger,\n): PublishProvider {\n return providerBuilders.get(provider)?.(logger) ?? provider;\n}\n\nconst afterGenerationKey = Symbol(\"rizom.after-generation\");\n\n/**\n * A first-party generation with a step after it runs: once its record is\n * written, told the id written (the next free one when its own was taken)\n * and the title it was written with;\n * when it fails, told why. The step rides on the declaration under a key no\n * public type names.\n */\nexport function afterGeneration<\n TInputSchema extends z.ZodType,\n TMetadataSchema extends AnyEntityDefinition[\"metadata\"],\n>(\n generation: EntityGenerationDeclaration<TInputSchema, TMetadataSchema>,\n step: GenerationStep<z.output<TInputSchema>>,\n): EntityGenerationDeclaration<TInputSchema, TMetadataSchema> {\n return Object.freeze({ ...generation, [afterGenerationKey]: step });\n}\n\n/** The step a generation carries after it runs, when it carries one. */\nexport function afterGenerationOf(\n declaration: object,\n): GenerationStep<unknown> | undefined {\n const found: unknown = Reflect.get(declaration, afterGenerationKey);\n return typeof found === \"function\"\n ? (context): Promise<void> =>\n Promise.resolve(Reflect.apply(found, undefined, [context]))\n : undefined;\n}\n\n/**\n * What `ensureUniqueTitle` reads, through a call's runtime: a record of\n * the title's id at the call's scope, one structured answer, and the\n * package's logger, as the entity plugin classes' generations read them.\n */\nexport function uniqueTitleContext(runtime: EntityRuntime): UniqueTitleContext {\n return {\n entityService: {\n getEntity: ({ entityType, id }) =>\n runtime.anyEntities.getEntity({ entityType, id }),\n },\n ai: {\n generateObject: async <T>(\n prompt: string,\n schema: AIGenerationSchema<T>,\n ): Promise<{ object: T }> => ({\n object: await runtime.generateObject(prompt, schema),\n }),\n },\n logger: runtime.logger,\n };\n}\n\n/**\n * The internal runtime bound to a callback's call. First-party packages\n * only: a context the brain's runtime did not build, such as a test double,\n * has none, and is refused.\n */\nexport function entityRuntime(context: object): EntityRuntime {\n const runtime: unknown = Reflect.get(context, entityRuntimeKey);\n if (!isEntityRuntime(runtime)) {\n throw new Error(\n \"This context carries no entity runtime: entityRuntime() reaches internal capabilities of a callback context the brain's runtime built, for first-party packages only\",\n );\n }\n return runtime;\n}\n",
359
359
  "import { AsyncLocalStorage } from \"node:async_hooks\";\nimport {\n SdkError,\n type ImageAspectRatio,\n fetchVoiceGuidance,\n generateReportFailureContract,\n generateReportSuccessContract,\n type GenerationResult,\n} from \"@brains/contracts\";\nimport {\n canWriteVisibility,\n canonicalContentVisibilitySchema,\n internalFullScope,\n isVisibleWithinScope,\n type ContentVisibility,\n} from \"@brains/entity-service\";\nimport type { JobHandler } from \"@brains/job-queue\";\nimport type { EntityActionRequiredLevel } from \"@brains/templates\";\nimport { getErrorMessage } from \"@brains/utils/error\";\nimport { parseMarkdown, updateFrontmatterField } from \"@brains/utils/markdown\";\nimport { parseWithSchema } from \"@brains/utils/parse-schema\";\nimport { isPlainRecord } from \"@brains/utils/predicates\";\nimport type { ProgressReporter } from \"@brains/utils/progress\";\nimport type { RetryScope } from \"@brains/utils/retry-backoff\";\nimport { definedFields } from \"@brains/utils/strip-undefined\";\nimport { slugify } from \"@brains/utils/string-utils\";\nimport type { Logger } from \"@brains/utils/logger\";\nimport { z } from \"@brains/utils/zod\";\nimport {\n BaseGenerationJobHandler,\n type GeneratedContent,\n} from \"../service/base-generation-job-handler\";\nimport {\n createPackageEntityReader,\n packageReadScope,\n packageSelfCall,\n queuedJobCall,\n serviceJobDataSchema,\n type PackageCall,\n type QueuedJobCall,\n type ServiceJobData,\n} from \"../service/job-authority\";\nimport type { EntityPluginContext } from \"./context\";\nimport type {\n AnyEntityDefinition,\n EntityEvalFixtures,\n EntityGenerationAI,\n EntityGenerationContext,\n EntityGenerationDeclaration,\n EntityGenerationLink,\n EntityJobContext,\n EntityJobDeclaration,\n EntityJobMessaging,\n EntityJobProgress,\n EntityReader,\n OwnedEntityWriter,\n} from \"./entity-definition-contract\";\nimport { waitForEmbeddingsToDrain } from \"./embedding-drain\";\nimport {\n afterGenerationOf,\n isDeduplicated,\n withEntityRuntime,\n type EntityRuntimeBinding,\n type GenerationOutcome,\n} from \"./entity-runtime\";\nimport { sharedUploadScope } from \"../service/upload-registry\";\nimport type { AttachmentResolveRequest } from \"../service/attachment-registry\";\nimport {\n assertOwnedWriteAllowed,\n createOwnedEntityWriter,\n} from \"./owned-entity-writer-runtime\";\n\n/**\n * Template generation through main's AI path: a template this type declares,\n * named locally, with the style guide governing voice when asked.\n */\nexport function createGenerationAI(\n context: EntityPluginContext,\n template: (localName: string) => string,\n signal?: AbortSignal,\n): EntityGenerationAI {\n return Object.freeze({\n async generate<TSchema extends z.ZodType>(\n request: Parameters<EntityGenerationAI[\"generate\"]>[0],\n schema: TSchema,\n ): Promise<z.output<TSchema>> {\n const templateName = template(request.template);\n const voice = request.voice\n ? await fetchVoiceGuidance(context.entityService)\n : \"\";\n const raw = await context.ai.generate(\n {\n prompt: request.prompt,\n templateName,\n ...(request.data && { data: { ...request.data } }),\n ...definedFields({\n representedIdentity: request.representedIdentity,\n }),\n ...(voice && { styleGuide: { voice } }),\n },\n z.unknown(),\n signal,\n );\n return parseWithSchema<TSchema>(schema, raw);\n },\n });\n}\n\n/**\n * Fixture control for one declaring type: seed what an eval measures, wait\n * for embeddings, and reset this type's records plus what was seeded.\n */\nexport function createEvalFixtures(\n context: EntityPluginContext,\n entityType: string,\n): EntityEvalFixtures {\n const seeded = new Map<string, { entityType: string; id: string }>();\n const key = (type: string, id: string): string => `${type}\\u0000${id}`;\n return Object.freeze({\n async seed(\n entity: Parameters<EntityEvalFixtures[\"seed\"]>[0],\n ): Promise<void> {\n await context.entityService.createEntity({\n entity: {\n id: entity.id,\n entityType: entity.entityType,\n content: entity.content,\n visibility: canonicalContentVisibilitySchema.parse(\n entity.visibility ?? \"public\",\n ),\n metadata: { ...entity.metadata },\n },\n });\n seeded.set(key(entity.entityType, entity.id), {\n entityType: entity.entityType,\n id: entity.id,\n });\n },\n async reset(\n options?: Parameters<EntityEvalFixtures[\"reset\"]>[0],\n ): Promise<void> {\n const visibilityScope = canonicalContentVisibilitySchema.parse(\n options?.visibilityScope ?? \"public\",\n );\n const stored = await context.entityService.listEntities({\n entityType,\n options: { filter: { visibilityScope } },\n });\n const targets = new Map([\n ...stored.map(\n (entity): [string, { entityType: string; id: string }] => [\n key(entity.entityType, entity.id),\n { entityType: entity.entityType, id: entity.id },\n ],\n ),\n ...seeded,\n ]);\n await Promise.all(\n [...targets.values()].map((target) =>\n context.entityService.deleteEntity(target),\n ),\n );\n seeded.clear();\n },\n settleEmbeddings: (): Promise<void> =>\n waitForEmbeddingsToDrain(context.jobs),\n });\n}\n\n/**\n * What a declared job reads and writes as: whoever started it, resolved again\n * when it runs, or the package itself when the package queued it. Acting as\n * itself, the package reads and writes the package's own types at every\n * visibility and past the action policy, as a service package does; any\n * other type, and every type for a requester, is held to the run's access.\n */\nexport interface EntityRunAuthority {\n /** Definition-typed reads, each type at `scope`. */\n readonly entities: EntityReader;\n /** Where a read of the type looks. */\n scope(entityType: string): ContentVisibility;\n /** Why the run may not take this action on the type, when it may not. */\n refusal(entityType: string, action: \"create\" | \"update\"): string | undefined;\n /** Why the run may not write the type at this visibility, when it may not. */\n visibilityRefusal(\n entityType: string,\n visibility: ContentVisibility,\n ): string | undefined;\n}\n\n/** A queued job's authority, for a call resolved when the job runs. */\nasync function runAuthority(\n context: EntityPluginContext,\n call: PackageCall,\n ownsType: (entityType: string) => boolean,\n): Promise<EntityRunAuthority> {\n const access = await call.access();\n const ownedByPackage = (entityType: string): boolean =>\n call.asPackage === true && ownsType(entityType);\n return Object.freeze({\n entities: createPackageEntityReader(\n context.entityService,\n () => call,\n ownsType,\n ),\n scope: (entityType: string): ContentVisibility =>\n packageReadScope(call, ownsType(entityType), access),\n refusal: (\n entityType: string,\n action: \"create\" | \"update\",\n ): string | undefined => {\n if (ownedByPackage(entityType)) return undefined;\n try {\n context.permissions.assertEntityActionAllowed(entityType, action, {\n userPermissionLevel: access.permissionLevel,\n });\n return undefined;\n } catch (error) {\n // The policy's refusal names the action and the level it needs.\n return getErrorMessage(error);\n }\n },\n visibilityRefusal: (\n entityType: string,\n visibility: ContentVisibility,\n ): string | undefined =>\n ownedByPackage(entityType) ||\n canWriteVisibility(access.permissionLevel, visibility)\n ? undefined\n : `A ${access.permissionLevel} requester cannot write a ${visibility} ${entityType}`,\n });\n}\n\nconst generationSourceSchema: z.ZodObject<{\n entityType: z.ZodString;\n ids: z.ZodArray<z.ZodString>;\n}> = z.object({\n entityType: z.string().min(1),\n ids: z.array(z.string().min(1)).min(1),\n});\n\n/** The entries a generation is written from, as the runtime records them. */\nexport type GenerationSource = z.output<typeof generationSourceSchema>;\n\n/**\n * The source a generation's queued data names, in the fields the content\n * pipeline, `system_generate` and a create request write it in:\n * `sourceEntityType` with `sourceEntityId` or `sourceEntityIds`.\n */\nexport function generationSource(fields: {\n readonly sourceEntityType?: unknown;\n readonly sourceEntityId?: unknown;\n readonly sourceEntityIds?: unknown;\n}): GenerationSource | undefined {\n const { sourceEntityType, sourceEntityId, sourceEntityIds } = fields;\n if (typeof sourceEntityType !== \"string\" || sourceEntityType === \"\") {\n return undefined;\n }\n const named = [\n ...(typeof sourceEntityId === \"string\" ? [sourceEntityId] : []),\n ...(Array.isArray(sourceEntityIds)\n ? sourceEntityIds.filter((id): id is string => typeof id === \"string\")\n : []),\n ].filter((id) => id !== \"\");\n const ids = [...new Set(named)];\n return ids.length > 0 ? { entityType: sourceEntityType, ids } : undefined;\n}\n\n/** The more private of two visibilities. */\nfunction morePrivate(\n held: ContentVisibility | undefined,\n next: ContentVisibility,\n): ContentVisibility {\n return held === undefined || isVisibleWithinScope(held, next) ? next : held;\n}\n\n/** The parts of a job's context every declared job shares, frozen. */\nfunction jobContext<TInput>(\n context: EntityPluginContext,\n input: TInput,\n progress: ProgressReporter,\n signal: AbortSignal,\n entities: EntityReader,\n): EntityJobContext<TInput> {\n const announcer: EntityJobMessaging = {\n // A published message is an event: every subscriber hears it.\n publish: (channel, payload) =>\n context.messaging.broadcast(channel, payload),\n };\n const messaging = Object.freeze(announcer);\n const reporter: EntityJobProgress = Object.freeze({\n report: (update: {\n readonly progress: number;\n readonly total?: number | undefined;\n readonly message?: string | undefined;\n }): Promise<void> =>\n progress.report({\n progress: update.progress,\n ...definedFields({ total: update.total, message: update.message }),\n }),\n });\n return Object.freeze({\n input,\n entities,\n messaging,\n progress: reporter,\n signal,\n });\n}\n\n/** What a declared job may write, and the policy its writes are held to. */\nexport interface EntityJobWrites {\n /** The type whose `jobs` slot declares the job: the one type it writes. */\n readonly declaring: AnyEntityDefinition;\n /** The level a type's action needs under the brain's policy. */\n requiredLevel(\n entityType: string,\n action: \"create\" | \"update\" | \"delete\",\n ): EntityActionRequiredLevel | undefined;\n}\n\n/**\n * The writes a declared job's `handle` makes: the declaring type's records,\n * as the run's authority, with the service writer's semantics. A requester\n * is also held to the visibility they may write.\n */\nfunction entityJobWriter(\n context: EntityPluginContext,\n call: QueuedJobCall,\n authority: EntityRunAuthority,\n ownsType: (entityType: string) => boolean,\n writes: EntityJobWrites,\n): OwnedEntityWriter<AnyEntityDefinition> {\n const { declaring } = writes;\n return createOwnedEntityWriter({\n entityService: context.entityService,\n owned: (definition) => {\n if (definition.type !== declaring.type) {\n throw new SdkError(\"permission_denied\", {\n message: `A ${declaring.type} job may only write ${declaring.type} records, and \"${definition.type}\" is not its type`,\n });\n }\n return declaring;\n },\n currentCall: () => call,\n assertWriteAllowed: (entityType, action, current) =>\n assertOwnedWriteAllowed(\n writes.requiredLevel(entityType, action),\n entityType,\n action,\n current,\n ),\n readScope: (current, entityType, access) =>\n packageReadScope(current, ownsType(entityType), access),\n assertCreateVisibility: async (entityType, visibility) => {\n const refusal = authority.visibilityRefusal(entityType, visibility);\n if (refusal) {\n throw new SdkError(\"permission_denied\", {\n message: refusal,\n publicMessage: refusal,\n });\n }\n },\n });\n}\n\n/**\n * Reads and writes of the declaring type as the package acting as itself,\n * with no caller behind it: its own types at every visibility and past the\n * action policy, other types at its own grant, its writes attributed to it.\n */\nexport async function packageEntityWrites(\n context: EntityPluginContext,\n pluginId: string,\n ownsType: (entityType: string) => boolean,\n writes: EntityJobWrites,\n): Promise<EntityReader & OwnedEntityWriter<AnyEntityDefinition>> {\n const call: QueuedJobCall = {\n ...packageSelfCall(context.jobAuthority),\n eventContext: { actor: { kind: \"service\", serviceId: pluginId } },\n };\n const authority = await runAuthority(context, call, ownsType);\n return Object.freeze({\n ...authority.entities,\n ...entityJobWriter(context, call, authority, ownsType, writes),\n });\n}\n\n/**\n * Bind a declared job that does its own work. It reads as whoever started\n * it, as a service package's job does, and writes the declaring type's\n * records as them.\n */\nexport function entityJobHandler(\n declaration: EntityJobDeclaration,\n context: EntityPluginContext,\n ownsType: (entityType: string) => boolean,\n writes: EntityJobWrites,\n runtime: (authority: EntityRunAuthority) => EntityRuntimeBinding,\n): JobHandler<string, ServiceJobData, unknown> {\n // Queued data names who started the job; data without it is invalid.\n const dataSchema = serviceJobDataSchema(declaration.input);\n return {\n // The queue checks a job's data when it is queued and when it runs, and\n // keeps what this answers: the input as written, so it is parsed once.\n validateAndParse(data): ServiceJobData | null {\n const parsed = dataSchema.safeParse(data);\n return parsed.success ? parsed.data : null;\n },\n async process(job, _jobId, progress, signal): Promise<unknown> {\n // Its requester's current access, never more than it was admitted at;\n // a revoked or rebound requester is refused before the job reads.\n const call = await queuedJobCall(context.jobAuthority, job);\n const authority = await runAuthority(context, call, ownsType);\n const base = jobContext(\n context,\n parseWithSchema(declaration.input, job.input),\n progress,\n signal,\n authority.entities,\n );\n return declaration.handle(\n withEntityRuntime(\n {\n ...base,\n entities: Object.freeze({\n ...authority.entities,\n ...entityJobWriter(context, call, authority, ownsType, writes),\n }),\n },\n runtime(authority),\n ),\n );\n },\n };\n}\n\n/**\n * Bind a first-party job another package queues through its contract. Its\n * queued data is the contract's input as written, with no requester beside\n * it, so it runs as the package, as the entity plugin classes' job handlers\n * did: it writes its own type at every visibility past the policy, reads\n * other types at the package's grant, and links what it made into a target\n * past the target's action policy.\n */\nexport function contractJobHandler(\n declaration: EntityJobDeclaration,\n context: EntityPluginContext,\n ownsType: (entityType: string) => boolean,\n writes: EntityJobWrites,\n pluginId: string,\n template: (localName: string) => string,\n): JobHandler<string, unknown, unknown> {\n return {\n validateAndParse(data): unknown {\n const parsed = declaration.input.safeParse(data);\n return parsed.success ? parsed.data : null;\n },\n async process(data, _jobId, progress, signal): Promise<unknown> {\n const call: QueuedJobCall = {\n ...packageSelfCall(context.jobAuthority),\n eventContext: { actor: { kind: \"service\", serviceId: pluginId } },\n };\n const authority = await runAuthority(context, call, ownsType);\n const uploads = context.uploads.scoped(sharedUploadScope);\n const base = jobContext(\n context,\n data,\n progress,\n signal,\n authority.entities,\n );\n return declaration.handle(\n withEntityRuntime(\n {\n ...base,\n entities: Object.freeze({\n ...authority.entities,\n ...entityJobWriter(context, call, authority, ownsType, writes),\n }),\n },\n {\n where: \"a contract job\",\n reader: authority.entities,\n packageReader: authority.entities,\n ownsType,\n searchWithDistances: (request) =>\n context.entityService.searchWithDistances(request),\n generateObject: (prompt, schema) =>\n context.ai.generateObject(prompt, schema),\n // Through the templates its type declares, as the entity plugin\n // classes' job handlers generated through their own.\n generate: createGenerationAI(context, template, signal).generate,\n anyEntities: {\n service: context.entityService,\n // Public, as the entity plugin classes' job handlers read\n // through the store's default scope; the typed reads and\n // writes reach the package's own types at every visibility.\n scope: () => \"public\",\n },\n job: {\n logger: context.logger,\n images: Object.freeze({\n canGenerate: (): boolean => context.ai.canGenerateImages(),\n generate: async (\n prompt: string,\n options?: {\n readonly aspectRatio?: ImageAspectRatio | undefined;\n },\n ): Promise<{ readonly dataUrl: string }> => {\n const { dataUrl } = await context.ai.generateImage(\n prompt,\n definedFields({ aspectRatio: options?.aspectRatio }),\n );\n return { dataUrl };\n },\n }),\n attachments: Object.freeze({\n resolve: (request: AttachmentResolveRequest) =>\n context.attachments.resolve({ ...request }),\n }),\n uploads: Object.freeze({\n read: (uploadId: string) => uploads.read(uploadId),\n readRecord: (uploadId: string) => uploads.readRecord(uploadId),\n }),\n linkInto: async (\n entityId: string,\n link: EntityGenerationLink,\n ): Promise<void> => {\n const problem = await linkTargetProblem(\n context,\n link,\n authority,\n );\n if (problem) throw new Error(problem);\n await writeGenerationLink(context, entityId, link, authority);\n },\n },\n },\n ),\n );\n },\n };\n}\n\n/**\n * Why a link cannot be made, checked before anything is saved: a write of the\n * target its run may not make, a target the run cannot see, or a cover on a\n * type that never declared one — the same refusal `system_update` makes.\n */\nexport async function generationLinkProblem(\n context: EntityPluginContext,\n link: EntityGenerationLink,\n authority: EntityRunAuthority,\n): Promise<string | undefined> {\n return (\n authority.refusal(link.entityType, \"update\") ??\n (await linkTargetProblem(context, link, authority))\n );\n}\n\n/**\n * Why a link's target cannot hold it: one the run cannot see, a cover on a\n * type that never declared one, or a field or list a declared type's\n * metadata cannot hold. The target's action policy is not asked.\n */\nexport async function linkTargetProblem(\n context: EntityPluginContext,\n link: EntityGenerationLink,\n authority: EntityRunAuthority,\n): Promise<string | undefined> {\n // A target the run cannot see is not there for it, so the link never\n // writes an entry its requester may not write.\n const target = await context.entityService.getEntity({\n entityType: link.entityType,\n id: link.entityId,\n visibilityScope: authority.scope(link.entityType),\n });\n if (!target) {\n return `Target entity not found: ${link.entityType}/${link.entityId}`;\n }\n if (\n \"field\" in link &&\n link.field === \"coverImageId\" &&\n context.entities.getAdapter(link.entityType)?.supportsCoverImage !== true\n ) {\n return `Entity type '${link.entityType}' doesn't support cover images`;\n }\n // A declared type's metadata is its frontmatter, so a link writes one of\n // its metadata fields; one the schema does not declare would be stored\n // where no read reaches it. Other types keep free-form frontmatter.\n if (\n context.entities.getAdapter(link.entityType)?.metadataFromFrontmatter !==\n true\n ) {\n return undefined;\n }\n const schema = context.entities.getEffectiveFrontmatterSchema(\n link.entityType,\n );\n if (!schema) return undefined;\n const linked = \"field\" in link ? link.field : link.list;\n // The metadata as the link would leave it, with a stand-in for the id.\n const probe = {\n ...target.metadata,\n [linked]:\n \"field\" in link\n ? \"linked-entity\"\n : appendToList(target.metadata[link.list], link, \"linked-entity\"),\n };\n let parsed: unknown;\n try {\n parsed = schema.parse(probe);\n } catch {\n return `Entity type '${link.entityType}' field '${linked}' cannot hold a link`;\n }\n return typeof parsed === \"object\" &&\n parsed !== null &&\n Object.hasOwn(parsed, linked)\n ? undefined\n : `Entity type '${link.entityType}' declares no metadata field '${linked}' to link into`;\n}\n\n/**\n * The target's list with this entity in it and superseded entries out, in\n * order, without listing an entity twice.\n */\nfunction appendToList(\n held: unknown,\n link: {\n readonly list: string;\n readonly replaces?: readonly string[] | undefined;\n },\n entityId: string,\n): { id: string }[] {\n const dropped = new Set(link.replaces ?? []);\n const kept = (Array.isArray(held) ? held : []).flatMap((item: unknown) =>\n typeof item === \"object\" &&\n item !== null &&\n \"id\" in item &&\n typeof item.id === \"string\" &&\n !dropped.has(item.id)\n ? [{ id: item.id }]\n : [],\n );\n return kept.some(({ id }) => id === entityId)\n ? kept\n : [...kept, { id: entityId }];\n}\n\n/** Point the target at what a generation produced. */\nexport async function writeGenerationLink(\n context: EntityPluginContext,\n entityId: string,\n link: EntityGenerationLink,\n authority: EntityRunAuthority,\n): Promise<void> {\n // As stored, so the target is written back with its own references rather\n // than their resolved data; read as the run reads, as the check was.\n const target = await context.entityService.getEntityRaw({\n entityType: link.entityType,\n id: link.entityId,\n visibilityScope: authority.scope(link.entityType),\n });\n if (!target) {\n throw new Error(\n `Cannot link ${entityId} into ${link.entityType}/${link.entityId}, which does not exist`,\n );\n }\n // A declared type's metadata is its frontmatter and its content is the\n // body, so the link is a metadata write. Other types keep the field in the\n // file, and their content is the file.\n const declared =\n context.entities.getAdapter(link.entityType)?.metadataFromFrontmatter ===\n true;\n const fields = declared\n ? target.metadata\n : parseMarkdown(target.content).frontmatter;\n const [field, value] =\n \"field\" in link\n ? [link.field, entityId]\n : [link.list, appendToList(fields[link.list], link, entityId)];\n await context.entityService.updateEntity({\n entity: {\n ...target,\n content: declared\n ? target.content\n : updateFrontmatterField(target.content, field, value),\n // Metadata that already carries the field must agree with the file.\n metadata:\n declared || Object.hasOwn(target.metadata, field)\n ? { ...target.metadata, [field]: value }\n : target.metadata,\n },\n });\n}\n\n/** The allocated entity a generation fills, when its job was given one. */\nfunction allocatedEntityId(data: unknown): string | undefined {\n if (typeof data !== \"object\" || data === null || !(\"entityId\" in data)) {\n return undefined;\n }\n const entityId = data.entityId;\n return typeof entityId === \"string\" && entityId.trim().length > 0\n ? entityId.trim()\n : undefined;\n}\n\ntype LinkedContent = GeneratedContent & {\n readonly linkInto: EntityGenerationLink;\n};\n\nfunction isLinked(generated: GeneratedContent): generated is LinkedContent {\n return \"linkInto\" in generated;\n}\n\nexport interface GenerationTarget {\n readonly entityType: string;\n readonly jobTypeName: string;\n /** The registered name of a template this type declares. */\n readonly template: (localName: string) => string;\n /** Whether a type is one the declaring package owns. */\n readonly ownsType: (entityType: string) => boolean;\n readonly retries?: RetryScope | undefined;\n /** Tell the content pipeline how each run went: the type is scheduled. */\n readonly reportsToPipeline?: boolean | undefined;\n /**\n * Whether this is the type's own `{type}:generation`, which an entity\n * plugin class queued in its handler's raw shape before the type was\n * declared; no other declared job ever was.\n */\n readonly readsLegacyRawData?: boolean | undefined;\n /** The plugin a job queued in the class handler's raw shape runs as. */\n readonly pluginId: string;\n /** The internal runtime a run reaches, at the run's authority. */\n readonly runtime: (authority: EntityRunAuthority) => EntityRuntimeBinding;\n}\n\n/**\n * A generation's queued data: a package job's, with the entity it fills and\n * the entries it is written from beside its input; the runtime alone writes\n * both. The input is checked by the base handler, which also reads a cover\n * request from it.\n */\nconst generationJobDataSchema = serviceJobDataSchema(z.unknown()).extend({\n entityId: z.string().optional(),\n source: generationSourceSchema.optional(),\n});\n\n/**\n * A generation queued before its type was declared, in the class handler's\n * raw shape: its input as written, with the entity it fills beside it and\n * no requester recorded. Tracked in docs/legacy-code-inventory.json\n * (legacy-raw-generation-data) for removal once no such job can be pending.\n */\nconst legacyGenerationDataSchema = z.object({\n legacyRawData: z.literal(true),\n input: z.unknown(),\n entityId: z.string().optional(),\n source: generationSourceSchema.optional(),\n});\n\ntype LegacyGenerationData = z.output<typeof legacyGenerationDataSchema>;\n\n/**\n * Queued data in the class handler's raw shape, read as a legacy job; data\n * that names a requester is a package job's, whose envelope it failed.\n */\nfunction legacyGenerationData(data: unknown): LegacyGenerationData | null {\n const stored = legacyGenerationDataSchema.safeParse(data);\n if (stored.success) return stored.data;\n if (!isPlainRecord(data) || \"authority\" in data) return null;\n const { entityId, ...input } = data;\n return {\n legacyRawData: true,\n input,\n ...(typeof entityId === \"string\" && { entityId }),\n ...definedFields({ source: generationSource(data) }),\n };\n}\n\n/** One running generation: its signal, its input, and whom it acts as. */\ninterface GenerationRun {\n readonly signal: AbortSignal;\n readonly input: unknown;\n readonly authority: EntityRunAuthority;\n readonly source: GenerationSource | undefined;\n}\n\n/**\n * A declared generation on main's generation lifecycle: the base handler\n * fills a pre-allocated stub (keeping edits made while it generated), marks\n * it failed on refusal, error or terminal failure, and creates the entity\n * when nothing was allocated. This class supplies the declared `generate`,\n * the link write, and whom both act as: the generation's requester, or the\n * package when the package queued it.\n */\nexport class DeclarativeGenerationHandler extends BaseGenerationJobHandler<unknown> {\n private readonly runs = new AsyncLocalStorage<GenerationRun>();\n private readonly declaration: EntityGenerationDeclaration;\n private readonly target: GenerationTarget;\n\n constructor(\n logger: Logger,\n context: EntityPluginContext,\n declaration: EntityGenerationDeclaration,\n target: GenerationTarget,\n ) {\n super(logger, context, {\n schema: declaration.input,\n jobTypeName: target.jobTypeName,\n entityType: target.entityType,\n retries: target.retries,\n });\n this.declaration = declaration;\n this.target = target;\n }\n\n /**\n * Queued data names who started the generation; data without it is\n * invalid. The input is checked as the base handler checks it, which also\n * reads a cover request from it, and kept as written, since the queue\n * stores what this answers: `generate` parses it once.\n */\n override validateAndParse(data: unknown): unknown {\n const job = generationJobDataSchema.safeParse(data);\n const queued = job.success\n ? job.data\n : this.target.readsLegacyRawData === true\n ? legacyGenerationData(data)\n : null;\n if (!queued) return null;\n const checked = super.validateAndParse(queued.input);\n if (checked === null) return null;\n return {\n ...queued,\n ...definedFields({\n coverImage: isPlainRecord(checked) ? checked[\"coverImage\"] : undefined,\n }),\n };\n }\n\n override async process(\n data: unknown,\n jobId: string,\n progressReporter: ProgressReporter,\n signal?: AbortSignal,\n ): Promise<GenerationResult> {\n const envelope = generationJobDataSchema.safeParse(data);\n const job = envelope.success\n ? envelope.data\n : legacyGenerationDataSchema.parse(data);\n // Its requester's current access, never more than it was admitted at; a\n // revoked or rebound requester is refused before anything runs. A job\n // queued in the class handler's raw shape recorded none, and runs as\n // the package, as the class handler ran it.\n const call: QueuedJobCall =\n \"legacyRawData\" in job\n ? {\n ...packageSelfCall(this.context.jobAuthority),\n eventContext: {\n actor: { kind: \"service\", serviceId: this.target.pluginId },\n },\n }\n : await queuedJobCall(this.context.jobAuthority, job);\n const run: GenerationRun = {\n signal: signal ?? new AbortController().signal,\n input: job.input,\n authority: await runAuthority(this.context, call, this.target.ownsType),\n source: job.source,\n };\n return this.runs.run(run, () =>\n super.process(data, jobId, progressReporter),\n );\n }\n\n protected async generate(\n data: unknown,\n progressReporter: ProgressReporter,\n ): Promise<GeneratedContent> {\n const { signal, input, authority, source } = this.currentRun();\n const entityId = allocatedEntityId(data);\n // Written as its requester: a create of this type is held to its policy,\n // and an allocated entity is filled only while they may still see it, so\n // the write never lands at a visibility they may not write. A new entity\n // is written without one, which is public.\n const refused = authority.refusal(this.entityType, \"create\");\n if (refused) this.failEarly(refused);\n const allocated =\n entityId === undefined\n ? undefined\n : await this.context.entityService.getEntity({\n entityType: this.entityType,\n id: entityId,\n visibilityScope: authority.scope(this.entityType),\n });\n if (entityId !== undefined && !allocated) {\n this.failEarly(\n `Pre-allocated entity stub not found: ${this.entityType}/${entityId}`,\n );\n }\n const generationContext = {\n ...jobContext(\n this.context,\n parseWithSchema(this.declaration.input, input),\n progressReporter,\n signal,\n authority.entities,\n ),\n entityId,\n ai: this.generationAI(signal),\n } satisfies EntityGenerationContext<unknown>;\n const result = await this.declaration.generate(\n withEntityRuntime(generationContext, this.target.runtime(authority)),\n );\n if (!result.success) this.failEarly(result.error);\n\n // No more visible than what it was written from, as a projected entry\n // is, unless the generation says otherwise; and never more visible than\n // its requester may write.\n const fromSource = await this.sourceVisibility(source);\n const visibility =\n result.visibility ??\n (fromSource && morePrivate(allocated?.visibility, fromSource));\n const unwritable =\n visibility && authority.visibilityRefusal(this.entityType, visibility);\n if (unwritable) this.failEarly(unwritable);\n\n const link = result.linkInto;\n if (link) {\n const problem = await generationLinkProblem(\n this.context,\n link,\n authority,\n );\n if (problem) this.failEarly(problem);\n }\n const metadata: Record<string, unknown> = { ...result.metadata };\n const title =\n typeof metadata[\"title\"] === \"string\" ? metadata[\"title\"] : undefined;\n return {\n id: entityId ?? result.id ?? slugify(title ?? this.entityType),\n content: result.content,\n metadata,\n ...definedFields({\n title,\n resultExtras: result.resultExtras && { ...result.resultExtras },\n }),\n ...(link && { linkInto: link }),\n ...(visibility && { visibility }),\n // A generation that asks lands beside a record holding its id; one\n // filling an allocated entity keeps that entity.\n ...(entityId === undefined &&\n isDeduplicated(result) && { createOptions: { deduplicateId: true } }),\n };\n }\n\n protected override async afterCreate(\n _data: unknown,\n entityId: string,\n _progressReporter: ProgressReporter,\n generated: GeneratedContent,\n ): Promise<void> {\n if (isLinked(generated)) {\n await writeGenerationLink(\n this.context,\n entityId,\n generated.linkInto,\n this.currentRun().authority,\n );\n }\n await this.runStep({\n written: { entityId, ...definedFields({ title: generated.title }) },\n });\n if (this.target.reportsToPipeline) {\n await this.context.messaging.command(generateReportSuccessContract, {\n entityType: this.entityType,\n entityId,\n });\n }\n }\n\n protected override async onGenerationFailure(\n _data: unknown,\n error: string,\n ): Promise<void> {\n await this.runStep({ failed: error });\n if (this.target.reportsToPipeline) {\n await this.context.messaging.command(generateReportFailureContract, {\n entityType: this.entityType,\n error,\n });\n }\n }\n\n /**\n * The step a first-party generation carries after it runs, given the\n * input as its schema reads it and the package's jobs and retries.\n */\n private async runStep(outcome: GenerationOutcome): Promise<void> {\n const step = afterGenerationOf(this.declaration);\n const retries = this.target.retries;\n if (!step || !retries) return;\n const parsed = this.declaration.input.safeParse(\n this.runs.getStore()?.input,\n );\n await step({\n input: parsed.success ? parsed.data : undefined,\n outcome,\n messaging: this.context.messaging,\n jobs: this.context.jobs,\n retries,\n });\n }\n\n private generationAI(signal: AbortSignal): EntityGenerationAI {\n return createGenerationAI(this.context, this.target.template, signal);\n }\n\n /**\n * The most private visibility among the entries a generation is written\n * from. Only their visibility is read, so a source its requester cannot\n * see still keeps the output private; one that is gone limits nothing.\n */\n private async sourceVisibility(\n source: GenerationSource | undefined,\n ): Promise<ContentVisibility | undefined> {\n if (!source) return undefined;\n const entries = await Promise.all(\n source.ids.map((id) =>\n this.context.entityService.getEntity({\n entityType: source.entityType,\n id,\n visibilityScope: internalFullScope(\n \"a generation is no more visible than its source; only the visibility is read\",\n ),\n }),\n ),\n );\n return entries.reduce<ContentVisibility | undefined>(\n (held, entry) => (entry ? morePrivate(held, entry.visibility) : held),\n undefined,\n );\n }\n\n /** The generation `process` is running; the base handler calls back inside it. */\n private currentRun(): GenerationRun {\n const run = this.runs.getStore();\n if (!run) throw new Error(`${this.jobTypeName} ran outside its job`);\n return run;\n }\n}\n",