@rizom/brain 0.2.0-alpha.379 → 0.2.0-alpha.380
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/brain.js +37 -37
- package/dist/chunks/{app-zxr5x34a.js → app-nvdgwkjv.js} +2 -2
- package/dist/chunks/{index-jfb71q5v.js → index-0asz7jsc.js} +2 -2
- package/dist/chunks/{index-jfb71q5v.js.map → index-0asz7jsc.js.map} +1 -1
- package/dist/chunks/{index-e7kyv559.js → index-5b1wstvc.js} +3 -3
- package/dist/chunks/{index-e7kyv559.js.map → index-5b1wstvc.js.map} +1 -1
- package/dist/chunks/{index-2dv6ee5j.js → index-bttt0hwq.js} +2 -2
- package/dist/index.js +1 -1
- package/dist/model.js +37 -37
- package/dist/model.js.map +1 -1
- package/dist/ui/studio-app.css +9 -3
- package/dist/ui/studio-app.js +3 -3
- package/dist/ui/studio-app.js.map +1 -1
- package/dist/ui/studio-asset-manifest.json +18 -18
- package/dist/ui/studio-chunks/{account-view-xe37mz6g.js → account-view-54k15mfk.js} +2 -2
- package/dist/ui/studio-chunks/{account-view-xe37mz6g.js.map → account-view-54k15mfk.js.map} +1 -1
- package/dist/ui/studio-chunks/{highlighted-body-OFNGDK62-rk8j2gp8.js → highlighted-body-OFNGDK62-z3qjyyn6.js} +2 -2
- package/dist/ui/studio-chunks/{highlighted-body-OFNGDK62-rk8j2gp8.js.map → highlighted-body-OFNGDK62-z3qjyyn6.js.map} +1 -1
- package/dist/ui/studio-chunks/{main-kgem2nc7.js → main-7hrg1q8k.js} +2 -2
- package/dist/ui/studio-chunks/{main-kgem2nc7.js.map → main-7hrg1q8k.js.map} +1 -1
- package/dist/ui/studio-chunks/main-gnx0qn72.js +7 -0
- package/dist/ui/studio-chunks/{main-wrwp0bss.js.map → main-gnx0qn72.js.map} +8 -8
- package/dist/ui/studio-chunks/{main-jyb12r9e.js → main-ppw6n9n7.js} +10 -10
- package/dist/ui/studio-chunks/{main-jyb12r9e.js.map → main-ppw6n9n7.js.map} +4 -3
- package/dist/ui/studio-chunks/{main-19ek6kcp.js → main-v7s3nh4a.js} +4 -4
- package/dist/ui/studio-chunks/{main-19ek6kcp.js.map → main-v7s3nh4a.js.map} +1 -1
- package/dist/ui/studio-chunks/{main-j8m9t6k5.js → main-xt7avfqt.js} +2 -2
- package/dist/ui/studio-chunks/{main-j8m9t6k5.js.map → main-xt7avfqt.js.map} +1 -1
- package/dist/ui/studio-chunks/{mermaid-GHXKKRXX-cvdypvaf.js → mermaid-GHXKKRXX-1rrd687e.js} +1 -1
- package/dist/ui/studio-chunks/{studio-chat-workspace-yk5ycrr7.js → studio-chat-workspace-myavww9e.js} +3 -3
- package/dist/ui/studio-chunks/{studio-chat-workspace-yk5ycrr7.js.map → studio-chat-workspace-myavww9e.js.map} +1 -1
- package/package.json +1 -1
- package/dist/ui/studio-chunks/main-wrwp0bss.js +0 -7
- /package/dist/chunks/{app-zxr5x34a.js.map → app-nvdgwkjv.js.map} +0 -0
- /package/dist/chunks/{index-2dv6ee5j.js.map → index-bttt0hwq.js.map} +0 -0
- /package/dist/ui/studio-chunks/{mermaid-GHXKKRXX-cvdypvaf.js.map → mermaid-GHXKKRXX-1rrd687e.js.map} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{ka as a,la as b,ma as c,na as d}from"./index-
|
|
2
|
+
import{ka as a,la as b,ma as c,na as d}from"./index-bttt0hwq.js";import"./index-xtafk5s4.js";import"./index-zncqaqm4.js";import"./index-7wp6jksm.js";import"./index-dbtf9myk.js";import"./index-n75bdj3d.js";import"./index-6zc7b41v.js";import"./index-e5pxnqxk.js";import"./index-74dsq2mw.js";import"./index-5v1n5093.js";export{d as App,a as STARTUP_CHECK_API_KEY,c as buildShellConfig,b as toAppConfig};
|
|
3
3
|
|
|
4
4
|
//# debugId=D3620E96E99885C364756E2164756E21
|
|
5
|
-
//# sourceMappingURL=app-
|
|
5
|
+
//# sourceMappingURL=app-nvdgwkjv.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{ja as h}from"./index-
|
|
2
|
+
import{ja as h}from"./index-bttt0hwq.js";import{Ya as D,_a as R,ee as a}from"./index-7wp6jksm.js";import{eg as i}from"./index-dbtf9myk.js";import{tj as c,uj as m,vj as v}from"./index-e5pxnqxk.js";import{Fj as e}from"./index-74dsq2mw.js";function j(t,n={}){if(!a(t))throw Error("use() expects a package definition returned by a @rizom/brain define helper");return Object.freeze({kind:"rizom-configured-plugin",definition:t,config:Object.freeze({...n})})}function w(t){if(t===null||typeof t!=="object")return!1;return Reflect.get(t,"kind")==="rizom-configured-plugin"&&a(Reflect.get(t,"definition"))&&typeof Reflect.get(t,"config")==="object"}var Ie=e.array(e.string()),Ae=e.record(e.string(),e.unknown()),De=e.unknown();var Te=e.enum(["eval"]),g=e.enum(["person","team","organization"]);function F(t){if(t.bundleContract?.trim().length===0)throw Error("Brain bundleContract must not be empty");return t}function p(t){if(Array.isArray(t))return t.map((n)=>p(n));if(i(t))return Object.fromEntries(Object.entries(t).map(([n,o])=>[n,p(o)]));return t}function f(t){return p(t)}var r=e.string().min(1),K=e.strictObject({member:r,value:e.record(e.string(),e.unknown()),overrides:r.optional()}),V=e.custom(i,{message:"Expected an opaque permission config object"}),G=e.strictObject({member:r,config:V,overrides:r.optional()}),q=e.strictObject({id:r,members:e.array(r),config:e.array(K).optional(),permissions:e.array(G).optional(),agentInstructions:e.array(e.string().min(1)).optional(),evalDisable:e.array(r).optional()});function O(t,n,o,N){let x=new Set;for(let[Z,d]of t.entries()){if(x.has(d))N.addIssue({code:e.ZodIssueCode.custom,message:`Duplicate ${n} "${d}"`,path:[o,Z]});x.add(d)}}var U=q.superRefine((t,n)=>{O(t.members,"member","members",n),O(t.evalDisable??[],"eval exclusion","evalDisable",n)});function E(t){return f(U.parse(t))}function H(t){if(t===void 0)return[];return Array.isArray(t)?t:[t]}var J=e.record(e.string(),e.unknown()),mt=e.record(e.string(),e.unknown()),W=e.enum(["eval"]),Y=e.enum(["debug","info","warn","error"]),Q=c,s=m,X=e.strictObject({create:s.optional(),update:s.optional(),delete:s.optional(),extract:s.optional(),publish:s.optional()}),ee=e.record(e.string(),X),I=J.superRefine((t,n)=>{if(typeof t.package!=="string")return;n.addIssue({code:"custom",message:'"package" uses the removed alpha external-plugin contract. Default-export a declarative package definition from a @rizom/brain define helper and compose it with use() in defineBrain().'})}),ht=e.strictObject({brain:e.string().optional(),bundleContract:e.string().trim().min(1).optional(),anchor:g.optional(),site:e.object({package:e.string().optional(),variant:e.string().optional(),theme:e.string().optional(),themeOverride:e.string().optional()}).optional(),name:e.string().optional(),kind:e.string().trim().min(1).optional(),logLevel:Y.optional(),logFile:e.string().optional(),port:e.number().optional(),domain:e.string().optional(),database:e.string().optional(),embedding:e.strictObject({enabled:e.boolean().optional()}).optional(),model:e.string().optional(),reasoningEffort:h.optional(),bundles:e.array(e.string().min(1)).optional(),mode:W.optional(),add:e.array(e.string()).optional(),remove:e.array(e.string()).optional(),admins:e.array(e.string()).optional(),anchors:e.array(e.string()).optional(),trusted:e.array(e.string()).optional(),spaces:e.array(e.string()).optional(),plugins:e.record(e.string(),I).optional(),permissions:e.object({admins:e.array(e.string()).optional(),anchors:e.array(e.string()).optional(),trusted:e.array(e.string()).optional(),rules:e.array(e.object({pattern:e.string(),level:Q})).optional(),entityActions:ee.optional()}).optional()});var St=e.record(e.string(),e.unknown());var ne=e.strictObject({pattern:e.string(),level:c}),It=e.strictObject({admins:e.array(e.string()).optional(),anchors:e.array(e.string()).optional(),trusted:e.array(e.string()).optional(),rules:e.array(ne).optional(),entityActions:v.optional()});var A=`/* ============================================
|
|
3
3
|
THEME BASE - Shared utilities for all themes
|
|
4
4
|
============================================
|
|
5
5
|
This file contains CSS that is IDENTICAL across all themes.
|
|
@@ -528,4 +528,4 @@ import{ja as h}from"./index-2dv6ee5j.js";import{Ya as D,_a as R,ee as a}from"./i
|
|
|
528
528
|
`+t}var z=e.custom((t)=>R.safeParse(t).success),se=e.custom((t)=>{if(!i(t))return!1;let{plugin:n,...o}=t;if(n!==void 0&&typeof n!=="function")return!1;return D.safeParse(o).success});var le=e.looseObject({id:e.string().min(1)}),de=e.looseObject({label:e.string().min(1)}),ge=e.custom((t)=>typeof t==="function"),pe=e.looseObject({layouts:e.record(e.string(),e.unknown()).optional(),plugin:ge.optional(),pluginConfig:e.record(e.string(),e.unknown()).optional(),routes:e.array(le).optional(),entityDisplay:e.record(e.string(),de).optional(),content:e.unknown().optional(),themeOverride:e.string().optional(),headScripts:e.array(e.string()).optional(),staticAssets:e.record(e.string(),e.string()).optional()}),Ht=e.custom((t)=>pe.safeParse(t).success);var In=e.looseObject({name:e.string().min(1),version:e.string().min(1),dependencies:e.record(e.string(),e.string()).optional(),peerDependencies:e.record(e.string(),e.string()).optional()});export{j as M,w as N,F as O,E as P,H as Q,re as R};
|
|
529
529
|
|
|
530
530
|
//# debugId=BCEF8217D0D1A80B64756E2164756E21
|
|
531
|
-
//# sourceMappingURL=index-
|
|
531
|
+
//# sourceMappingURL=index-0asz7jsc.js.map
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"sources": ["../../../shell/app/src/configured-plugin.ts", "../../../shell/app/src/cli.ts", "../../../shell/app/src/brain-definition.ts", "../../../shared/utils/src/clone.ts", "../../../shell/app/src/bundle-definition.ts", "../../../shared/utils/src/array.ts", "../../../shell/app/src/instance-overrides.ts", "../../../shell/app/src/resolver/merge.ts", "../../../shell/app/src/bundle-permissions.ts", "../../../shared/theme-base/src/index.ts", "../../../shell/app/src/site-package.ts", "../../../shell/app/src/resolver/site.ts", "../../../shell/app/src/installed-package-metadata.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import {\n isPluginPackageDefinition,\n type PluginPackageConfigInput,\n type PluginPackageDefinition,\n} from \"@brains/plugins\";\n\nexport interface ConfiguredPluginDefinition<\n TDefinition extends PluginPackageDefinition = PluginPackageDefinition,\n> {\n readonly kind: \"rizom-configured-plugin\";\n readonly definition: TDefinition;\n readonly config: Readonly<Partial<PluginPackageConfigInput<TDefinition>>>;\n}\n\nexport function use<TDefinition extends PluginPackageDefinition>(\n definition: TDefinition,\n config: Partial<PluginPackageConfigInput<TDefinition>> = {},\n): ConfiguredPluginDefinition<TDefinition> {\n if (!isPluginPackageDefinition(definition)) {\n throw new Error(\n \"use() expects a package definition returned by a @rizom/brain define helper\",\n );\n }\n\n return Object.freeze({\n kind: \"rizom-configured-plugin\" as const,\n definition,\n config: Object.freeze({ ...config }),\n });\n}\n\nexport function isConfiguredPluginDefinition(\n value: unknown,\n): value is ConfiguredPluginDefinition {\n if (value === null || typeof value !== \"object\") return false;\n return (\n Reflect.get(value, \"kind\") === \"rizom-configured-plugin\" &&\n isPluginPackageDefinition(Reflect.get(value, \"definition\")) &&\n typeof Reflect.get(value, \"config\") === \"object\"\n );\n}\n",
|
|
6
|
-
"import { z } from \"@brains/utils/zod\";\nimport type { AppConfig } from \"./types\";\nimport type { App as AppClass, AppRuntimeOptions } from \"./app\";\nimport {\n createCliOperatorContext,\n type CliOperatorContext,\n} from \"./cli-operator\";\nimport type { ToolResponse } from \"@brains/mcp-service\";\nimport { getErrorMessage } from \"@brains/utils/error\";\n\nexport interface AppFactory {\n create: typeof AppClass.create;\n run: typeof AppClass.run;\n}\n\n/**\n * Everything handleCLI touches outside its arguments.\n *\n * Production wires this to the process and console. A test hands in a fake\n * and reads outcomes off it — what was printed, what exit code — instead of\n * patching globals and restoring them afterwards. `exit` is typed `never` so\n * a fake must throw rather than return; a fake that returned would let code\n * run on past an exit into states the real process never reaches.\n */\nexport interface CliIo {\n argv: readonly string[];\n log: (line: string) => void;\n error: (line: string, ...detail: unknown[]) => void;\n exit: (code: number) => never;\n app: AppFactory;\n}\n\ntype InitializeOptions = Parameters<\n ReturnType<typeof AppClass.create>[\"initialize\"]\n>[0];\n\nasync function productionIo(config: AppConfig): Promise<CliIo> {\n // Imported here rather than at the top to avoid a circular dependency.\n const { App } = await import(\"./app\");\n\n process.on(\"uncaughtException\", (error) => {\n console.error(`❌ ${config.name} crashed:`, error);\n process.exit(1);\n });\n process.on(\"unhandledRejection\", (reason) => {\n console.error(`❌ ${config.name} unhandled rejection:`, reason);\n process.exit(1);\n });\n\n return {\n argv: process.argv.slice(2),\n log: console.log,\n error: console.error,\n exit: process.exit,\n app: App,\n };\n}\n\nfunction getArgValue(\n args: readonly string[],\n flag: string,\n): string | undefined {\n const flagIdx = args.indexOf(flag);\n return flagIdx !== -1 ? args[flagIdx + 1] : undefined;\n}\n\nfunction requireArgValue(io: CliIo, flag: string, message: string): string {\n const value = getArgValue(io.argv, flag);\n if (value === undefined) {\n io.error(message);\n io.exit(1);\n }\n return value;\n}\n\ninterface JsonFlagSchema<T> {\n parse(value: unknown): T;\n}\n\nconst cliArgsSchema = z.array(z.string());\nconst cliFlagsSchema = z.record(z.string(), z.unknown());\nconst jsonValueSchema = z.unknown();\n\nfunction parseJsonFlag<T>(\n args: readonly string[],\n flag: string,\n defaultValue: T,\n schema: JsonFlagSchema<T>,\n): T {\n const value = getArgValue(args, flag);\n return value ? schema.parse(JSON.parse(value)) : defaultValue;\n}\n\nfunction createHeadlessConfig(config: AppConfig): AppConfig {\n return {\n ...config,\n plugins: (config.plugins ?? []).filter((p) => p.type !== \"interface\"),\n };\n}\n\nasync function routeLogsToStderr(): Promise<void> {\n const { ConsoleLogger } = await import(\"@brains/utils/logger\");\n ConsoleLogger.getInstance().setUseStderr(true);\n}\n\nasync function initializeHeadlessApp(\n config: AppConfig,\n io: CliIo,\n options?: InitializeOptions,\n): Promise<ReturnType<typeof AppClass.create>> {\n await routeLogsToStderr();\n\n const app = io.app.create(createHeadlessConfig(config));\n await app.initialize(options);\n return app;\n}\n\nfunction printToolResult(io: CliIo, result: ToolResponse): void {\n if (\"needsConfirmation\" in result) {\n const detail = result.preview ? `\\n\\n${result.preview}` : \"\";\n io.log(`Confirmation needed: ${result.summary}${detail}`);\n io.exit(0);\n }\n\n if (!result.success) {\n io.error(`❌ ${result.error}`);\n io.exit(1);\n }\n\n if (result.message) {\n io.log(result.message);\n }\n if (result.data !== undefined) {\n io.log(\n typeof result.data === \"string\"\n ? result.data\n : JSON.stringify(result.data, null, 2),\n );\n }\n}\n\nasync function invokeCliTool(\n io: CliIo,\n handler: (\n input: unknown,\n context: CliOperatorContext,\n ) => Promise<ToolResponse>,\n input: unknown,\n failureLabel: string,\n): Promise<void> {\n // Only the handler call is guarded. Reporting is deliberately outside: a\n // failure while printing — serializing circular `data`, say — is not the\n // tool failing, and labelling it as one hides where it went wrong.\n let result: ToolResponse;\n try {\n result = await handler(input, createCliOperatorContext());\n } catch (error) {\n io.error(`❌ ${failureLabel} failed:`, getErrorMessage(error));\n io.exit(1);\n }\n printToolResult(io, result);\n}\n\n/**\n * Export deployment config as JSON for shell scripts\n * This is used by deploy scripts to extract config without starting the app\n */\nfunction exportDeployConfig(io: CliIo, config: AppConfig): void {\n const deployment = config.deployment;\n\n const deployConfig = {\n name: config.name,\n version: config.version,\n // Server\n provider: deployment.provider,\n serverSize: deployment.serverSize,\n location: deployment.location,\n domain: deployment.domain,\n // Docker\n docker: {\n enabled: deployment.docker.enabled,\n image: deployment.docker.image ?? config.name,\n },\n // Ports\n ports: {\n default: deployment.ports.default,\n preview: deployment.ports.preview,\n production: deployment.ports.production,\n },\n // CDN\n cdn: {\n enabled: deployment.cdn.enabled,\n provider: deployment.cdn.provider,\n },\n // DNS\n dns: {\n enabled: deployment.dns.enabled,\n provider: deployment.dns.provider,\n },\n // Paths (compute defaults based on app name)\n paths: {\n install: deployment.paths.install ?? `/opt/${config.name}`,\n data: deployment.paths.data ?? `/opt/${config.name}/data`,\n },\n };\n\n io.log(JSON.stringify(deployConfig, null, 2));\n io.exit(0);\n}\n\n/**\n * Handle CLI arguments and run appropriate commands from a generated brain entrypoint.\n */\nexport async function handleCLI(\n config: AppConfig,\n runtimeOptions?: AppRuntimeOptions,\n io?: CliIo,\n): Promise<void> {\n const cli = io ?? (await productionIo(config));\n const args = cli.argv;\n\n // Handle --export-deploy-config first (no app startup needed)\n if (args.includes(\"--export-deploy-config\")) {\n exportDeployConfig(cli, config);\n }\n\n // Handle CLI commands\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n showHelp(cli, config);\n } else if (args.includes(\"--version\") || args.includes(\"-v\")) {\n cli.log(`${config.name} v${config.version}`);\n cli.exit(0);\n } else if (args.includes(\"--startup-check\")) {\n // Startup check: register plugins and run ready hooks, but do not start\n // daemons or job workers. Used by external plugin smoke tests.\n const app = cli.app.create(config);\n await app.initialize({ mode: \"startup-check\" });\n await app.stop();\n } else if (args.includes(\"--list-cli-commands\")) {\n // List CLI-enabled tools for dynamic help\n await listCliCommands(cli, config);\n } else if (args[0] === \"diagnostics\") {\n // Diagnostics mode: boot brain, run diagnostics, exit\n await runDiagnostics(cli, config, args.slice(1));\n } else if (args.includes(\"--cli-command\")) {\n // Headless mode via CLI command name: boot, find tool by cli.name, invoke, exit\n await runCliCommand(cli, config);\n } else if (args.includes(\"--tool\")) {\n // Raw tool invocation: boot, invoke by full tool name, exit\n await runTool(cli, config);\n } else {\n // Default: run the app\n cli.log(`🚀 Starting ${config.name} v${config.version}...`);\n if (runtimeOptions) {\n await cli.app.run(config, undefined, runtimeOptions);\n } else {\n await cli.app.run(config);\n }\n }\n}\n\n/**\n * List all CLI-enabled tools. Used by brain --help to discover available commands.\n */\nasync function listCliCommands(io: CliIo, config: AppConfig): Promise<void> {\n const app = await initializeHeadlessApp(config, io, {\n mode: \"register-only\",\n });\n\n const cliTools = app.getShell().getMCPService().getCliTools();\n for (const { tool } of cliTools) {\n if (tool.cli) {\n io.log(`${tool.cli.name.padEnd(16)}${tool.description}`);\n }\n }\n\n io.exit(0);\n}\n\n/**\n * Headless mode via CLI command name: boot brain, find tool by cli.name, invoke, exit.\n *\n * Used by `brain list`, `brain sync`, etc. The brain CLI passes the command\n * name and args/flags as JSON. This function discovers the matching tool\n * via getCliTools() and invokes it.\n */\nasync function runCliCommand(io: CliIo, config: AppConfig): Promise<void> {\n const commandName = requireArgValue(\n io,\n \"--cli-command\",\n \"❌ --cli-command requires a command name\",\n );\n const cliArgs = parseJsonFlag(io.argv, \"--cli-args\", [], cliArgsSchema);\n const cliFlags = parseJsonFlag(io.argv, \"--cli-flags\", {}, cliFlagsSchema);\n\n const app = await initializeHeadlessApp(config, io);\n const cliTools = app.getShell().getMCPService().getCliTools();\n const match = cliTools.find((t) => t.tool.cli?.name === commandName);\n\n if (!match?.tool.cli) {\n const available = cliTools\n .map((t) => t.tool.cli?.name)\n .filter(Boolean)\n .join(\", \");\n io.error(`❌ Unknown command: ${commandName}`);\n io.error(`Available commands: ${available}`);\n io.exit(1);\n }\n\n const { mapArgsToInput } = await import(\"@brains/mcp-service\");\n const toolInput = mapArgsToInput(match.tool.inputSchema, cliArgs, cliFlags);\n await invokeCliTool(\n io,\n match.tool.handler,\n toolInput,\n `Command ${commandName}`,\n );\n\n io.exit(0);\n}\n\n/**\n * Headless mode: boot brain without daemons, invoke a tool, print result, exit.\n *\n * Used by `brain list`, `brain get`, `brain sync`, etc.\n * Skips all interface plugins (MCP, Discord, webserver) — only loads\n * entity plugins and service plugins.\n */\nasync function runTool(io: CliIo, config: AppConfig): Promise<void> {\n const toolName = requireArgValue(\n io,\n \"--tool\",\n \"❌ --tool requires a tool name\",\n );\n\n let toolInput: unknown = {};\n try {\n toolInput = parseJsonFlag(io.argv, \"--tool-input\", {}, jsonValueSchema);\n } catch {\n io.error(\"❌ --tool-input must be valid JSON\");\n io.exit(1);\n }\n\n const app = await initializeHeadlessApp(config, io);\n const tools = app.getShell().getMCPService().listTools();\n const match = tools.find((t) => t.tool.name === toolName);\n\n if (!match) {\n io.error(`❌ Tool not found: ${toolName}`);\n io.error(`Available tools: ${tools.map((t) => t.tool.name).join(\", \")}`);\n io.exit(1);\n }\n\n await invokeCliTool(io, match.tool.handler, toolInput, `Tool ${toolName}`);\n\n io.exit(0);\n}\n\n/**\n * Run diagnostics: boot brain (full, with daemons disabled), analyze, exit.\n */\nasync function runDiagnostics(\n io: CliIo,\n config: AppConfig,\n args: readonly string[],\n): Promise<void> {\n const { ConsoleLogger, LogLevel } = await import(\"@brains/utils/logger\");\n // Suppress plugin registration noise — only show warnings and errors\n ConsoleLogger.resetInstance();\n ConsoleLogger.getInstance({ level: LogLevel.WARN, useStderr: true });\n\n const subcommand = args[0] ?? \"\";\n\n if (subcommand === \"usage\") {\n await runUsageDiagnostics(io, config);\n return;\n }\n\n if (subcommand !== \"search\") {\n io.error(\"Usage: brain diagnostics <search|usage>\");\n io.exit(1);\n }\n\n // Boot in register-only mode — no daemons, no sync, no builds.\n // We only need access to the existing entity + embedding data.\n const app = io.app.create(createHeadlessConfig(config));\n await app.initialize({ mode: \"register-only\" });\n\n const shell = app.getShell();\n const entityService = shell.getEntityService();\n await entityService.initialize();\n\n const entityTypes = entityService.getEntityTypes();\n const allEntities: Array<{ id: string; entityType: string; title: string }> =\n [];\n\n const entityLists = await Promise.all(\n entityTypes.map((type) =>\n entityService.listEntities({\n entityType: type,\n options: { limit: 100 },\n }),\n ),\n );\n for (const entities of entityLists) {\n for (const entity of entities) {\n const meta = cliFlagsSchema.parse(entity.metadata);\n const title = String(meta[\"title\"] ?? meta[\"name\"] ?? entity.id);\n allEntities.push({ id: entity.id, entityType: entity.entityType, title });\n }\n }\n\n if (allEntities.length === 0) {\n await shell.shutdown();\n io.error(\"No entities found\");\n io.exit(1);\n }\n\n io.log(`\\nAnalyzing ${allEntities.length} entities...\\n`);\n\n const sampleSize = Math.min(20, allEntities.length);\n const samples = allEntities\n .sort(() => Math.random() - 0.5)\n .slice(0, sampleSize);\n\n const allDistances: number[] = [];\n const selfDistances: number[] = [];\n\n const searchResults = await Promise.all(\n samples.map((s) => entityService.searchWithDistances({ query: s.title })),\n );\n for (const [i, sample] of samples.entries()) {\n for (const r of searchResults[i] ?? []) {\n allDistances.push(r.distance);\n if (r.entityId === sample.id && r.entityType === sample.entityType) {\n selfDistances.push(r.distance);\n }\n }\n }\n\n allDistances.sort((a, b) => a - b);\n selfDistances.sort((a, b) => a - b);\n\n const pct = (arr: number[], p: number): number => {\n if (arr.length === 0) return 0;\n const idx = Math.ceil((p / 100) * arr.length) - 1;\n return arr[Math.max(0, idx)] ?? 0;\n };\n\n io.log(\"=== Search Distance Distribution ===\\n\");\n io.log(`Queries sampled: ${samples.length}`);\n io.log(`Total distance measurements: ${allDistances.length}`);\n io.log(`Self-match distances: ${selfDistances.length}\\n`);\n\n io.log(\"All distances:\");\n for (const p of [0, 25, 50, 75, 90, 95, 100]) {\n const label = p === 0 ? \"min\" : p === 100 ? \"max\" : `p${p}`;\n io.log(` ${label.padEnd(5)} ${pct(allDistances, p).toFixed(4)}`);\n }\n\n io.log(\"\\nSelf-match distances (query = entity title):\");\n io.log(` min: ${pct(selfDistances, 0).toFixed(4)}`);\n io.log(` p50: ${pct(selfDistances, 50).toFixed(4)}`);\n io.log(` max: ${pct(selfDistances, 100).toFixed(4)}\\n`);\n\n const p75 = pct(allDistances, 75);\n const p90 = pct(allDistances, 90);\n const suggested = Number(((p75 + p90) / 2).toFixed(4));\n\n io.log(`Current threshold: 0.82`);\n io.log(`Suggested threshold: ${suggested}`);\n io.log(\n ` (midpoint between p75=${p75.toFixed(4)} and p90=${p90.toFixed(4)})\\n`,\n );\n\n await shell.shutdown();\n io.exit(0);\n}\n\n/**\n * Run usage diagnostics: read the log file, aggregate ai:usage events.\n */\nasync function runUsageDiagnostics(\n io: CliIo,\n config: AppConfig,\n): Promise<void> {\n const logFile = config.logFile;\n if (!logFile) {\n io.error(\n \"No log file configured. Set logFile in brain.yaml to enable usage tracking.\",\n );\n io.exit(1);\n }\n\n const { existsSync, readFileSync } = await import(\"node:fs\");\n if (!existsSync(logFile)) {\n io.error(`Log file not found: ${logFile}`);\n io.exit(1);\n }\n\n const { aggregateUsage } = await import(\"./usage-aggregator\");\n const content = readFileSync(logFile, \"utf-8\");\n const report = aggregateUsage(content);\n\n if (report.events.length === 0) {\n io.log(\"No ai:usage events found in log file.\");\n io.exit(0);\n }\n\n const total = report.totalInputTokens + report.totalOutputTokens;\n\n io.log(\"=== AI Usage ===\\n\");\n io.log(`Period: ${report.firstTs} → ${report.lastTs}`);\n io.log(`Total events: ${report.events.length}`);\n io.log(`Total input tokens: ${report.totalInputTokens.toLocaleString()}`);\n io.log(`Total output tokens: ${report.totalOutputTokens.toLocaleString()}`);\n io.log(`Total tokens: ${total.toLocaleString()}\\n`);\n\n io.log(\"By model:\");\n for (const [key, agg] of report.byModel.entries()) {\n io.log(\n ` ${key.padEnd(40)} ${String(agg.calls).padStart(5)} calls, ` +\n `${agg.inputTokens.toLocaleString().padStart(12)} in, ` +\n `${agg.outputTokens.toLocaleString().padStart(12)} out`,\n );\n }\n\n io.exit(0);\n}\n\n/**\n * Show help information\n */\nfunction showHelp(io: CliIo, config: AppConfig): void {\n io.log(`\n${config.name} v${config.version}\n\nUsage:\n brains [options]\n\nOptions:\n --help, -h Show this help message\n --version, -v Show version information\n --cli Enable CLI interface (passed to app)\n --export-deploy-config Export deployment config as JSON (for deploy scripts)\n\nExamples:\n brains # Start the app\n brains --cli # Start with CLI interface\n brains --export-deploy-config # Output deployment JSON\n`);\n io.exit(0);\n}\n",
|
|
6
|
+
"import { z } from \"@brains/utils/zod\";\nimport type { AppConfig } from \"./types\";\nimport type { App as AppClass, AppRuntimeOptions } from \"./app\";\nimport {\n createCliOperatorContext,\n type CliOperatorContext,\n} from \"./cli-operator\";\nimport type { ToolResponse } from \"@brains/mcp-service\";\nimport { getErrorMessage } from \"@brains/utils/error\";\n\nexport interface AppFactory {\n create: typeof AppClass.create;\n run: typeof AppClass.run;\n}\n\n/**\n * Everything handleCLI touches outside its arguments.\n *\n * Production wires this to the process and console. A test hands in a fake\n * and reads outcomes off it — what was printed, what exit code — instead of\n * patching globals and restoring them afterwards. `exit` is typed `never` so\n * a fake must throw rather than return; a fake that returned would let code\n * run on past an exit into states the real process never reaches.\n */\nexport interface CliIo {\n argv: readonly string[];\n log: (line: string) => void;\n error: (line: string, ...detail: unknown[]) => void;\n exit: (code: number) => never;\n app: AppFactory;\n}\n\ntype InitializeOptions = Parameters<\n ReturnType<typeof AppClass.create>[\"initialize\"]\n>[0];\n\nasync function productionIo(config: AppConfig): Promise<CliIo> {\n // Imported here rather than at the top to avoid a circular dependency.\n const { App } = await import(\"./app\");\n\n process.on(\"uncaughtException\", (error) => {\n console.error(`❌ ${config.name} crashed:`, error);\n process.exit(1);\n });\n process.on(\"unhandledRejection\", (reason) => {\n console.error(`❌ ${config.name} unhandled rejection:`, reason);\n process.exit(1);\n });\n\n return {\n argv: process.argv.slice(2),\n log: console.log,\n error: console.error,\n exit: process.exit,\n app: App,\n };\n}\n\nfunction getArgValue(\n args: readonly string[],\n flag: string,\n): string | undefined {\n const flagIdx = args.indexOf(flag);\n return flagIdx !== -1 ? args[flagIdx + 1] : undefined;\n}\n\nfunction requireArgValue(io: CliIo, flag: string, message: string): string {\n const value = getArgValue(io.argv, flag);\n if (value === undefined) {\n io.error(message);\n io.exit(1);\n }\n return value;\n}\n\ninterface JsonFlagSchema<T> {\n parse(value: unknown): T;\n}\n\nconst cliArgsSchema = z.array(z.string());\nconst cliFlagsSchema = z.record(z.string(), z.unknown());\nconst jsonValueSchema = z.unknown();\n\nfunction parseJsonFlag<T>(\n args: readonly string[],\n flag: string,\n defaultValue: T,\n schema: JsonFlagSchema<T>,\n): T {\n const value = getArgValue(args, flag);\n return value ? schema.parse(JSON.parse(value)) : defaultValue;\n}\n\nfunction createHeadlessConfig(config: AppConfig): AppConfig {\n return {\n ...config,\n plugins: (config.plugins ?? []).filter((p) => p.type !== \"interface\"),\n };\n}\n\nasync function routeLogsToStderr(): Promise<void> {\n const { ConsoleLogger } = await import(\"@brains/utils/logger\");\n ConsoleLogger.getInstance().setUseStderr(true);\n}\n\nasync function initializeHeadlessApp(\n config: AppConfig,\n io: CliIo,\n options?: InitializeOptions,\n): Promise<ReturnType<typeof AppClass.create>> {\n await routeLogsToStderr();\n\n const app = io.app.create(createHeadlessConfig(config));\n await app.initialize(options);\n return app;\n}\n\nfunction printToolResult(io: CliIo, result: ToolResponse): void {\n if (\"needsConfirmation\" in result) {\n const detail = result.preview ? `\\n\\n${result.preview}` : \"\";\n io.log(`Confirmation needed: ${result.summary}${detail}`);\n io.exit(0);\n }\n\n if (!result.success) {\n io.error(`❌ ${result.error}`);\n io.exit(1);\n }\n\n if (result.message) {\n io.log(result.message);\n }\n if (result.data !== undefined) {\n io.log(\n typeof result.data === \"string\"\n ? result.data\n : JSON.stringify(result.data, null, 2),\n );\n }\n}\n\nasync function invokeCliTool(\n io: CliIo,\n handler: (\n input: unknown,\n context: CliOperatorContext,\n ) => Promise<ToolResponse>,\n input: unknown,\n failureLabel: string,\n): Promise<void> {\n // Only the handler call is guarded. Reporting is deliberately outside: a\n // failure while printing — serializing circular `data`, say — is not the\n // tool failing, and labelling it as one hides where it went wrong.\n let result: ToolResponse;\n try {\n result = await handler(input, createCliOperatorContext());\n } catch (error) {\n io.error(`❌ ${failureLabel} failed:`, getErrorMessage(error));\n io.exit(1);\n }\n printToolResult(io, result);\n}\n\n/**\n * Export deployment config as JSON for shell scripts\n * This is used by deploy scripts to extract config without starting the app\n */\nfunction exportDeployConfig(io: CliIo, config: AppConfig): void {\n const deployment = config.deployment;\n\n const deployConfig = {\n name: config.name,\n version: config.version,\n // Server\n provider: deployment.provider,\n serverSize: deployment.serverSize,\n location: deployment.location,\n domain: deployment.domain,\n // Docker\n docker: {\n enabled: deployment.docker.enabled,\n image: deployment.docker.image ?? config.name,\n },\n // Ports\n ports: {\n default: deployment.ports.default,\n preview: deployment.ports.preview,\n production: deployment.ports.production,\n },\n // CDN\n cdn: {\n enabled: deployment.cdn.enabled,\n provider: deployment.cdn.provider,\n },\n // DNS\n dns: {\n enabled: deployment.dns.enabled,\n provider: deployment.dns.provider,\n },\n // Paths (compute defaults based on app name)\n paths: {\n install: deployment.paths.install ?? `/opt/${config.name}`,\n data: deployment.paths.data ?? `/opt/${config.name}/data`,\n },\n };\n\n io.log(JSON.stringify(deployConfig, null, 2));\n io.exit(0);\n}\n\n/**\n * Handle CLI arguments and run appropriate commands from a generated brain entrypoint.\n */\nexport async function handleCLI(\n config: AppConfig,\n runtimeOptions?: AppRuntimeOptions,\n io?: CliIo,\n): Promise<void> {\n const cli = io ?? (await productionIo(config));\n const args = cli.argv;\n\n // Handle --export-deploy-config first (no app startup needed)\n if (args.includes(\"--export-deploy-config\")) {\n exportDeployConfig(cli, config);\n }\n\n // Handle CLI commands\n if (args.includes(\"--help\") || args.includes(\"-h\")) {\n showHelp(cli, config);\n } else if (args.includes(\"--version\") || args.includes(\"-v\")) {\n cli.log(`${config.name} v${config.version}`);\n cli.exit(0);\n } else if (args.includes(\"--startup-check\")) {\n // Startup check: register plugins and run ready hooks, but do not start\n // daemons or job workers. Used by external plugin smoke tests.\n const app = cli.app.create(config);\n await app.initialize({ mode: \"startup-check\" });\n await app.stop();\n } else if (args.includes(\"--list-cli-commands\")) {\n // List CLI-enabled tools for dynamic help\n await listCliCommands(cli, config);\n } else if (args[0] === \"diagnostics\") {\n // Diagnostics mode: boot brain, run diagnostics, exit\n await runDiagnostics(cli, config, args.slice(1));\n } else if (args.includes(\"--cli-command\")) {\n // Headless mode via CLI command name: boot, find tool by cli.name, invoke, exit\n await runCliCommand(cli, config);\n } else if (args.includes(\"--tool\")) {\n // Raw tool invocation: boot, invoke by full tool name, exit\n await runTool(cli, config);\n } else {\n // Default: run the app\n cli.log(`🚀 Starting ${config.name} v${config.version}...`);\n if (runtimeOptions) {\n await cli.app.run(config, undefined, runtimeOptions);\n } else {\n await cli.app.run(config);\n }\n }\n}\n\n/**\n * List all CLI-enabled tools. Used by brain --help to discover available commands.\n */\nasync function listCliCommands(io: CliIo, config: AppConfig): Promise<void> {\n const app = await initializeHeadlessApp(config, io, {\n mode: \"register-only\",\n });\n\n const cliTools = app.getShell().getMCPService().getCliTools();\n for (const { tool } of cliTools) {\n if (tool.cli) {\n io.log(`${tool.cli.name.padEnd(16)}${tool.description}`);\n }\n }\n\n io.exit(0);\n}\n\n/**\n * Headless mode via CLI command name: boot brain, find tool by cli.name, invoke, exit.\n *\n * Used by `brain list`, `brain sync`, etc. The brain CLI passes the command\n * name and args/flags as JSON. This function discovers the matching tool\n * via getCliTools() and invokes it.\n */\nasync function runCliCommand(io: CliIo, config: AppConfig): Promise<void> {\n const commandName = requireArgValue(\n io,\n \"--cli-command\",\n \"❌ --cli-command requires a command name\",\n );\n const cliArgs = parseJsonFlag(io.argv, \"--cli-args\", [], cliArgsSchema);\n const cliFlags = parseJsonFlag(io.argv, \"--cli-flags\", {}, cliFlagsSchema);\n\n const app = await initializeHeadlessApp(config, io, {\n mode: \"register-only\",\n });\n const cliTools = app.getShell().getMCPService().getCliTools();\n const match = cliTools.find((t) => t.tool.cli?.name === commandName);\n\n if (!match?.tool.cli) {\n const available = cliTools\n .map((t) => t.tool.cli?.name)\n .filter(Boolean)\n .join(\", \");\n io.error(`❌ Unknown command: ${commandName}`);\n io.error(`Available commands: ${available}`);\n io.exit(1);\n }\n\n const { mapArgsToInput } = await import(\"@brains/mcp-service\");\n const toolInput = mapArgsToInput(match.tool.inputSchema, cliArgs, cliFlags);\n await invokeCliTool(\n io,\n match.tool.handler,\n toolInput,\n `Command ${commandName}`,\n );\n\n io.exit(0);\n}\n\n/**\n * Headless mode: boot brain register-only, invoke a tool, print result, exit.\n *\n * Used by `brain list`, `brain get`, `brain sync`, etc.\n * Skips all interface plugins (MCP, Discord, webserver) — only loads\n * entity plugins and service plugins.\n *\n * Register-only, never a full boot: a one-shot process owns no runtime work.\n * A full boot starts a job worker on the queue's stable slot, which\n * supersedes the running app's worker session; that worker then stops\n * claiming jobs until the app restarts. The bundled runtime boots its tool\n * invocations register-only for the same reason.\n */\nasync function runTool(io: CliIo, config: AppConfig): Promise<void> {\n const toolName = requireArgValue(\n io,\n \"--tool\",\n \"❌ --tool requires a tool name\",\n );\n\n let toolInput: unknown = {};\n try {\n toolInput = parseJsonFlag(io.argv, \"--tool-input\", {}, jsonValueSchema);\n } catch {\n io.error(\"❌ --tool-input must be valid JSON\");\n io.exit(1);\n }\n\n const app = await initializeHeadlessApp(config, io, {\n mode: \"register-only\",\n });\n const tools = app.getShell().getMCPService().listTools();\n const match = tools.find((t) => t.tool.name === toolName);\n\n if (!match) {\n io.error(`❌ Tool not found: ${toolName}`);\n io.error(`Available tools: ${tools.map((t) => t.tool.name).join(\", \")}`);\n io.exit(1);\n }\n\n await invokeCliTool(io, match.tool.handler, toolInput, `Tool ${toolName}`);\n\n io.exit(0);\n}\n\n/**\n * Run diagnostics: boot brain (full, with daemons disabled), analyze, exit.\n */\nasync function runDiagnostics(\n io: CliIo,\n config: AppConfig,\n args: readonly string[],\n): Promise<void> {\n const { ConsoleLogger, LogLevel } = await import(\"@brains/utils/logger\");\n // Suppress plugin registration noise — only show warnings and errors\n ConsoleLogger.resetInstance();\n ConsoleLogger.getInstance({ level: LogLevel.WARN, useStderr: true });\n\n const subcommand = args[0] ?? \"\";\n\n if (subcommand === \"usage\") {\n await runUsageDiagnostics(io, config);\n return;\n }\n\n if (subcommand !== \"search\") {\n io.error(\"Usage: brain diagnostics <search|usage>\");\n io.exit(1);\n }\n\n // Boot in register-only mode — no daemons, no sync, no builds.\n // We only need access to the existing entity + embedding data.\n const app = io.app.create(createHeadlessConfig(config));\n await app.initialize({ mode: \"register-only\" });\n\n const shell = app.getShell();\n const entityService = shell.getEntityService();\n await entityService.initialize();\n\n const entityTypes = entityService.getEntityTypes();\n const allEntities: Array<{ id: string; entityType: string; title: string }> =\n [];\n\n const entityLists = await Promise.all(\n entityTypes.map((type) =>\n entityService.listEntities({\n entityType: type,\n options: { limit: 100 },\n }),\n ),\n );\n for (const entities of entityLists) {\n for (const entity of entities) {\n const meta = cliFlagsSchema.parse(entity.metadata);\n const title = String(meta[\"title\"] ?? meta[\"name\"] ?? entity.id);\n allEntities.push({ id: entity.id, entityType: entity.entityType, title });\n }\n }\n\n if (allEntities.length === 0) {\n await shell.shutdown();\n io.error(\"No entities found\");\n io.exit(1);\n }\n\n io.log(`\\nAnalyzing ${allEntities.length} entities...\\n`);\n\n const sampleSize = Math.min(20, allEntities.length);\n const samples = allEntities\n .sort(() => Math.random() - 0.5)\n .slice(0, sampleSize);\n\n const allDistances: number[] = [];\n const selfDistances: number[] = [];\n\n const searchResults = await Promise.all(\n samples.map((s) => entityService.searchWithDistances({ query: s.title })),\n );\n for (const [i, sample] of samples.entries()) {\n for (const r of searchResults[i] ?? []) {\n allDistances.push(r.distance);\n if (r.entityId === sample.id && r.entityType === sample.entityType) {\n selfDistances.push(r.distance);\n }\n }\n }\n\n allDistances.sort((a, b) => a - b);\n selfDistances.sort((a, b) => a - b);\n\n const pct = (arr: number[], p: number): number => {\n if (arr.length === 0) return 0;\n const idx = Math.ceil((p / 100) * arr.length) - 1;\n return arr[Math.max(0, idx)] ?? 0;\n };\n\n io.log(\"=== Search Distance Distribution ===\\n\");\n io.log(`Queries sampled: ${samples.length}`);\n io.log(`Total distance measurements: ${allDistances.length}`);\n io.log(`Self-match distances: ${selfDistances.length}\\n`);\n\n io.log(\"All distances:\");\n for (const p of [0, 25, 50, 75, 90, 95, 100]) {\n const label = p === 0 ? \"min\" : p === 100 ? \"max\" : `p${p}`;\n io.log(` ${label.padEnd(5)} ${pct(allDistances, p).toFixed(4)}`);\n }\n\n io.log(\"\\nSelf-match distances (query = entity title):\");\n io.log(` min: ${pct(selfDistances, 0).toFixed(4)}`);\n io.log(` p50: ${pct(selfDistances, 50).toFixed(4)}`);\n io.log(` max: ${pct(selfDistances, 100).toFixed(4)}\\n`);\n\n const p75 = pct(allDistances, 75);\n const p90 = pct(allDistances, 90);\n const suggested = Number(((p75 + p90) / 2).toFixed(4));\n\n io.log(`Current threshold: 0.82`);\n io.log(`Suggested threshold: ${suggested}`);\n io.log(\n ` (midpoint between p75=${p75.toFixed(4)} and p90=${p90.toFixed(4)})\\n`,\n );\n\n await shell.shutdown();\n io.exit(0);\n}\n\n/**\n * Run usage diagnostics: read the log file, aggregate ai:usage events.\n */\nasync function runUsageDiagnostics(\n io: CliIo,\n config: AppConfig,\n): Promise<void> {\n const logFile = config.logFile;\n if (!logFile) {\n io.error(\n \"No log file configured. Set logFile in brain.yaml to enable usage tracking.\",\n );\n io.exit(1);\n }\n\n const { existsSync, readFileSync } = await import(\"node:fs\");\n if (!existsSync(logFile)) {\n io.error(`Log file not found: ${logFile}`);\n io.exit(1);\n }\n\n const { aggregateUsage } = await import(\"./usage-aggregator\");\n const content = readFileSync(logFile, \"utf-8\");\n const report = aggregateUsage(content);\n\n if (report.events.length === 0) {\n io.log(\"No ai:usage events found in log file.\");\n io.exit(0);\n }\n\n const total = report.totalInputTokens + report.totalOutputTokens;\n\n io.log(\"=== AI Usage ===\\n\");\n io.log(`Period: ${report.firstTs} → ${report.lastTs}`);\n io.log(`Total events: ${report.events.length}`);\n io.log(`Total input tokens: ${report.totalInputTokens.toLocaleString()}`);\n io.log(`Total output tokens: ${report.totalOutputTokens.toLocaleString()}`);\n io.log(`Total tokens: ${total.toLocaleString()}\\n`);\n\n io.log(\"By model:\");\n for (const [key, agg] of report.byModel.entries()) {\n io.log(\n ` ${key.padEnd(40)} ${String(agg.calls).padStart(5)} calls, ` +\n `${agg.inputTokens.toLocaleString().padStart(12)} in, ` +\n `${agg.outputTokens.toLocaleString().padStart(12)} out`,\n );\n }\n\n io.exit(0);\n}\n\n/**\n * Show help information\n */\nfunction showHelp(io: CliIo, config: AppConfig): void {\n io.log(`\n${config.name} v${config.version}\n\nUsage:\n brains [options]\n\nOptions:\n --help, -h Show this help message\n --version, -v Show version information\n --cli Enable CLI interface (passed to app)\n --export-deploy-config Export deployment config as JSON (for deploy scripts)\n\nExamples:\n brains # Start the app\n brains --cli # Start with CLI interface\n brains --export-deploy-config # Output deployment JSON\n`);\n io.exit(0);\n}\n",
|
|
7
7
|
"import type { Plugin } from \"@brains/plugins\";\nimport type { PermissionConfig } from \"@brains/templates\";\nimport type { DeploymentConfigInput, ReasoningEffort } from \"./types\";\nimport type { SitePackage } from \"./site-package\";\nimport type { CapabilityBundleDefinition } from \"./bundle-definition\";\n\nimport { z } from \"@brains/utils/zod\";\n\nexport const modeSchema: z.ZodEnum<{ eval: \"eval\" }> = z.enum([\"eval\"]);\nexport type BrainMode = z.output<typeof modeSchema>;\n\nexport const brainAnchorConfigKindSchema: z.ZodEnum<{\n person: \"person\";\n team: \"team\";\n organization: \"organization\";\n}> = z.enum([\"person\", \"team\", \"organization\"]);\nexport type BrainAnchorConfigKind = z.output<\n typeof brainAnchorConfigKindSchema\n>;\n\n/**\n * Environment record — the deployment-specific variables\n * passed to interface env mappers and the resolver.\n */\nexport type BrainEnvironment = Record<string, string | undefined>;\n\n/** Plugin config objects — always key/value records. */\nexport type PluginConfig = Record<string, unknown>;\n\n/**\n * A capability is an [id, factory, config] tuple.\n * The id is used for disable checks and override matching in brain.yaml.\n * The factory is called with the config at resolve time to create a fresh plugin instance.\n *\n * Config can be:\n * - A static value (passed directly to the factory)\n * - A function `(env, context) => config` that receives the deployment environment\n * and active bundle context (use this when the plugin needs credentials,\n * bundle-specific values, or other env-specific settings)\n * - undefined (plugin uses its own defaults)\n */\nexport interface CapabilityContext {\n bundles: readonly string[];\n}\n\nexport type CapabilityConfig =\n | PluginConfig\n | ((env: BrainEnvironment, context: CapabilityContext) => PluginConfig)\n | undefined;\n\n/**\n * A plugin factory builds one plugin or — for composite features that bundle\n * an entity + service pair behind one shared config (e.g. newsletter +\n * buttondown) — an array of plugins. Composite sub-plugins are gated as a\n * unit by the capability id; add or remove the entry from a bundle to\n * enable/disable all of them.\n */\nexport type PluginFactory = (config: PluginConfig) => Plugin | Plugin[];\n\nexport type CapabilityEntry = [\n id: string,\n factory: PluginFactory,\n config: CapabilityConfig,\n];\n\n/**\n * An interface entry is an [id, constructor, envMapper] tuple.\n * The id is used for disable checks and override matching in brain.yaml.\n * The envMapper receives the deployment environment and returns the interface config,\n * or null to skip this interface (e.g. when credentials are missing).\n * The constructor is called with `new` to create a fresh interface instance.\n */\nexport type InterfaceConstructor = new (config: PluginConfig) => Plugin;\n\nexport type InterfaceEntry = [\n id: string,\n constructor: InterfaceConstructor,\n envMapper: (env: BrainEnvironment) => PluginConfig | null,\n];\n\n/**\n * Brain identity — who this brain is.\n * Pure data, no code references.\n */\nexport interface BrainIdentity {\n characterName: string;\n role: string;\n purpose: string;\n values: string[];\n}\n\n/**\n * The brain definition — a reusable model that describes what a brain IS.\n *\n * Key design principles:\n * - No `process.env` — environment is injected at resolve time\n * - Capabilities are [id, factory, config] tuples, not instantiated plugins\n * - Interfaces are [id, constructor, envMapper] tuples\n * - Identity, permissions, content model, deployment are pure data\n * - Can be instantiated multiple times with different environments\n */\nexport interface BrainDefinition {\n /** Brain name (used as app name) */\n name: string;\n /** Semantic version */\n version: string;\n\n /**\n * Exact instance bundle contract required by this definition.\n * Definitions that change the meaning of existing bundle IDs must change\n * this value so stale brain.yaml selections fail instead of drifting.\n */\n bundleContract?: string;\n\n /**\n * Default Anchor profile flavor. Instances may override this in brain.yaml.\n * Team and organization are both collective ownership mechanically.\n */\n anchor?: BrainAnchorConfigKind;\n\n /**\n * Default semantic profile kind selected from the composed catalog, e.g.\n * `professional`. It decides which field schema validates the anchor\n * profile, so a brain whose content or playbooks use kind-specific fields\n * must name one. Instances may override this with `kind` in brain.yaml.\n */\n kind?: string;\n\n /**\n * Default text model for this brain.\n * Can be overridden per-instance via `model` in brain.yaml.\n */\n model?: string;\n\n /** Default OpenAI reasoning effort for this brain. */\n reasoningEffort?: ReasoningEffort;\n\n /** Brain identity — character name, role, purpose, values */\n identity?: BrainIdentity;\n\n /**\n * Brain-specific agent behavior layered on top of shell-neutral tool rules.\n * Use this for model positioning such as Rover's publishing workflows or\n * Relay's team-memory/coordination focus.\n */\n agentInstructions?: string[];\n\n /**\n * Default site package — layout, routes, site plugin, and display metadata.\n * Can be overridden per-instance via `site.package` in brain.yaml.\n */\n site?: SitePackage;\n\n /**\n * Default composed theme CSS for the brain instance.\n * Can be overridden per-instance via `site.theme` in brain.yaml.\n */\n theme?: string;\n\n /**\n * Capabilities as [id, factory, config] tuples.\n * Each resolve() call invokes the factories to create fresh plugin instances.\n * Any plugin factory works — no central registry needed.\n */\n capabilities: CapabilityEntry[];\n\n /**\n * Interfaces as [id, constructor, envMapper] tuples.\n * The envMapper receives the deployment environment and returns interface config,\n * or null to skip (e.g. when credentials are missing).\n */\n interfaces: InterfaceEntry[];\n\n /**\n * Ordered capability bundles. Definition order controls deterministic\n * config, instruction, eval, and permission composition.\n */\n bundles?: CapabilityBundleDefinition[];\n\n /** Structural permission rules (no credentials) */\n permissions?: PermissionConfig;\n\n /** Deployment infrastructure config (domain, CDN, DNS) */\n deployment?: DeploymentConfigInput;\n\n /**\n * Plugin/interface IDs to disable when running in eval mode.\n * These are plugins with external side effects (chat, email, analytics, etc.)\n * that should not run during evaluation.\n */\n evalDisable?: string[];\n\n /**\n * Additional config passed directly to the app.\n * Escape hatch for anything not covered by the structured fields above.\n * Values here are merged last and can override resolved config.\n */\n extra?: Record<string, unknown>;\n}\n\n/**\n * Define a brain model.\n *\n * This is a simple identity function that provides type checking\n * for brain definitions. It returns the definition as-is.\n *\n * @example\n * ```typescript\n * import { defineBrain } from \"@brains/app\";\n * import { notePlugin } from \"@brains/note\";\n * import { MCPInterface } from \"@brains/mcp\";\n *\n * export default defineBrain({\n * name: \"my-brain\",\n * version: \"1.0.0\",\n * identity: {\n * characterName: \"Atlas\",\n * role: \"Knowledge manager\",\n * purpose: \"Organize and surface knowledge\",\n * values: [\"clarity\", \"accuracy\"],\n * },\n * capabilities: [\n * [\"note\", notePlugin, {}],\n * ],\n * interfaces: [\n * [\"mcp\", MCPInterface, (env) => ({ domain: env.DOMAIN })],\n * ],\n * });\n * ```\n */\nexport function defineBrain(definition: BrainDefinition): BrainDefinition {\n if (definition.bundleContract?.trim().length === 0) {\n throw new Error(\"Brain bundleContract must not be empty\");\n }\n return definition;\n}\n",
|
|
8
8
|
"import { isPlainRecord } from \"./predicates\";\n\nfunction cloneUnknown(value: unknown): unknown {\n if (Array.isArray(value)) {\n return value.map((item) => cloneUnknown(item));\n }\n\n if (isPlainRecord(value)) {\n return Object.fromEntries(\n Object.entries(value).map(([key, item]) => [key, cloneUnknown(item)]),\n );\n }\n\n // Dates, class instances, functions: copied by reference, as callers rely on.\n return value;\n}\n\n/**\n * Deep-copy plain data (records and arrays), passing anything else through by\n * reference.\n *\n * The single assertion below is irreducible: a homomorphic deep clone cannot be\n * expressed in TypeScript, because the compiler cannot prove that mapping over\n * `T & unknown[]`, or rebuilding a record via `Object.fromEntries`, reproduces\n * `T`. Isolating it here keeps that one unchecked step in a tested place\n * instead of repeating it at each call site.\n */\nexport function clonePlainData<T>(value: T): T {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- cloneUnknown walks and rebuilds the value, so the copy has the input shape by construction; the single proof point lives here so call sites need none\n return cloneUnknown(value) as T;\n}\n",
|
|
9
9
|
"import type { PermissionConfig } from \"@brains/templates\";\nimport { z } from \"@brains/utils/zod\";\nimport { clonePlainData } from \"@brains/utils/clone\";\nimport { isPlainRecord } from \"@brains/utils/predicates\";\n\nconst bundleIdSchema: z.ZodString = z.string().min(1);\n\ntype BundleConfigContributionSchema = z.ZodObject<\n {\n member: z.ZodString;\n value: z.ZodRecord<z.ZodString, z.ZodUnknown>;\n overrides: z.ZodOptional<z.ZodString>;\n },\n z.core.$strict\n>;\n\nexport const bundleConfigContributionSchema: BundleConfigContributionSchema =\n z.strictObject({\n member: bundleIdSchema,\n value: z.record(z.string(), z.unknown()),\n overrides: bundleIdSchema.optional(),\n });\n\nexport type BundleConfigContribution = z.output<\n typeof bundleConfigContributionSchema\n>;\n\nconst opaquePermissionConfigSchema: z.ZodCustom<\n PermissionConfig,\n PermissionConfig\n> = z.custom<PermissionConfig>(isPlainRecord, {\n message: \"Expected an opaque permission config object\",\n});\n\ntype BundlePermissionContributionSchema = z.ZodObject<\n {\n member: z.ZodString;\n config: typeof opaquePermissionConfigSchema;\n overrides: z.ZodOptional<z.ZodString>;\n },\n z.core.$strict\n>;\n\nexport const bundlePermissionContributionSchema: BundlePermissionContributionSchema =\n z.strictObject({\n member: bundleIdSchema,\n config: opaquePermissionConfigSchema,\n overrides: bundleIdSchema.optional(),\n });\n\nexport type BundlePermissionContribution = z.output<\n typeof bundlePermissionContributionSchema\n>;\n\ntype CapabilityBundleDefinitionSchema = z.ZodObject<\n {\n id: z.ZodString;\n members: z.ZodArray<z.ZodString>;\n config: z.ZodOptional<z.ZodArray<BundleConfigContributionSchema>>;\n permissions: z.ZodOptional<z.ZodArray<BundlePermissionContributionSchema>>;\n agentInstructions: z.ZodOptional<z.ZodArray<z.ZodString>>;\n evalDisable: z.ZodOptional<z.ZodArray<z.ZodString>>;\n },\n z.core.$strict\n>;\n\nconst rawCapabilityBundleDefinitionSchema: CapabilityBundleDefinitionSchema =\n z.strictObject({\n id: bundleIdSchema,\n members: z.array(bundleIdSchema),\n config: z.array(bundleConfigContributionSchema).optional(),\n permissions: z.array(bundlePermissionContributionSchema).optional(),\n agentInstructions: z.array(z.string().min(1)).optional(),\n evalDisable: z.array(bundleIdSchema).optional(),\n });\n\nfunction addDuplicateIssues(\n values: readonly string[],\n label: string,\n path: string,\n ctx: z.RefinementCtx,\n): void {\n const seen = new Set<string>();\n for (const [index, value] of values.entries()) {\n if (seen.has(value)) {\n ctx.addIssue({\n code: z.ZodIssueCode.custom,\n message: `Duplicate ${label} \"${value}\"`,\n path: [path, index],\n });\n }\n seen.add(value);\n }\n}\n\nexport const capabilityBundleDefinitionSchema: CapabilityBundleDefinitionSchema =\n rawCapabilityBundleDefinitionSchema.superRefine((definition, ctx) => {\n addDuplicateIssues(definition.members, \"member\", \"members\", ctx);\n addDuplicateIssues(\n definition.evalDisable ?? [],\n \"eval exclusion\",\n \"evalDisable\",\n ctx,\n );\n });\n\nexport type CapabilityBundleDefinition = z.output<\n typeof capabilityBundleDefinitionSchema\n>;\n\n/** Validate bundle data without constructing any plugins or runtime resources. */\nexport function defineBundle(\n definition: CapabilityBundleDefinition,\n): CapabilityBundleDefinition {\n return clonePlainData(capabilityBundleDefinitionSchema.parse(definition));\n}\n",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{a as z,b as f}from"./index-pyscvexd.js";import{d as c,e as l}from"./index-61cf8cb4.js";import{ra as D}from"./index-zncqaqm4.js";import{Lf as B,Ne as x,bg as M,if as I,jf as A,kf as P,lf as T,mf as R,nf as S,qf as L,xf as w}from"./index-7wp6jksm.js";import"./index-dbtf9myk.js";import"./index-n75bdj3d.js";import"./index-6zc7b41v.js";import{Xh as k}from"./index-e5pxnqxk.js";import{Fj as d}from"./index-74dsq2mw.js";import{Nj as h}from"./index-5v1n5093.js";var m=d.object({theme:d.object({primaryColor:d.string().describe("Primary color for the CLI theme").default("#0066cc"),accentColor:d.string().describe("Accent color for the CLI theme").default("#ff6600")}).describe("Theme configuration for the CLI interface").default({primaryColor:"#0066cc",accentColor:"#ff6600"})});var y={name:"@brains/chat-repl",license:"AGPL-3.0-only",version:"0.2.0-alpha.
|
|
2
|
+
import{a as z,b as f}from"./index-pyscvexd.js";import{d as c,e as l}from"./index-61cf8cb4.js";import{ra as D}from"./index-zncqaqm4.js";import{Lf as B,Ne as x,bg as M,if as I,jf as A,kf as P,lf as T,mf as R,nf as S,qf as L,xf as w}from"./index-7wp6jksm.js";import"./index-dbtf9myk.js";import"./index-n75bdj3d.js";import"./index-6zc7b41v.js";import{Xh as k}from"./index-e5pxnqxk.js";import{Fj as d}from"./index-74dsq2mw.js";import{Nj as h}from"./index-5v1n5093.js";var m=d.object({theme:d.object({primaryColor:d.string().describe("Primary color for the CLI theme").default("#0066cc"),accentColor:d.string().describe("Accent color for the CLI theme").default("#ff6600")}).describe("Theme configuration for the CLI interface").default({primaryColor:"#0066cc",accentColor:"#ff6600"})});var y={name:"@brains/chat-repl",license:"AGPL-3.0-only",version:"0.2.0-alpha.380",dependencies:{"@brains/job-queue":"workspace:*","@brains/plugins":"workspace:*","@brains/utils":"workspace:*",chalk:"^5.4.1",ink:"^7.0.6","ink-spinner":"^5.0.0","ink-text-input":"^6.0.0",marked:"^18.0.5",react:"^19.2.7"},devDependencies:{"@brains/eslint-config":"workspace:*","@brains/typescript-config":"workspace:*","@types/bun":"^1.4.0","@types/react":"^19.2.17",typescript:"^7.0.2"},files:["src"],main:"./src/index.ts",module:"./src/index.ts",private:!0,scripts:{dev:"tsc --watch",lint:"eslint . --max-warnings 0","lint:fix":"eslint . --max-warnings 0 --fix",test:"bun test",typecheck:"tsc --noEmit"},type:"module",types:"./src/index.ts"};var j={completed:"\u2713",declined:"\u25CB",failed:"\u2717"},Z={deniedCardIds:void 0,resolveUrl:(e)=>e,isHiddenUrl:()=>!1,eventActionUnavailableLabel:void 0};class E extends x{inkApp=null;responseCallback;systemMessageCallback;agentService;removeSignalHandlers;approvalTracker;constructor(e={}){super("cli",y,e,m)}getAgentService(){if(!this.agentService)throw Error("AgentService not initialized - plugin not registered");return this.agentService}async onRegister(e){await super.onRegister(e),this.agentService=e.agent,this.logger.debug("CLI interface registered with AgentService")}sendMessageToChannel({message:e}){let s=typeof e==="string"?e:e.fallbackText??"";if(!s)return;(this.systemMessageCallback??this.responseCallback)?.(s)}sendReply(e){this.responseCallback?.(e)}registerResponseCallback(e){this.responseCallback=e}registerSystemMessageCallback(e){this.systemMessageCallback=e}unregisterMessageCallbacks(){this.responseCallback=void 0,this.systemMessageCallback=void 0}async onProgressUpdate(e){this.logger.debug("CLI progress update",{eventId:e.id,status:e.status,progress:e.progress,message:e.message})}createDaemon(){return{start:async()=>{if(!this.context)throw new M(this.id,"Initialization failed: Plugin context not initialized");this.logger.debug("Starting CLI interface");try{let[e,s,n]=await Promise.all([import("./index-gvzp5wj2.js"),import("react"),import("./EnhancedApp-n1fdds03.js")]),{render:r}=e,t=s.default,o=n.default,g=t.createElement(o,{interface:this,registerProgressCallback:(i)=>this.registerProgressCallback(i),unregisterProgressCallback:()=>this.unregisterProgressCallback(),registerResponseCallback:(i)=>this.registerResponseCallback(i),registerSystemMessageCallback:(i)=>this.registerSystemMessageCallback(i),unregisterMessageCallbacks:()=>this.unregisterMessageCallbacks()});this.inkApp=r(g),this.registerSignalHandlers()}catch(e){throw this.logger.error("Failed to start CLI interface",{error:e}),e}},stop:async()=>{this.logger.debug("Stopping CLI interface"),await this.cleanup()},healthCheck:async()=>{let e=this.inkApp!==null;return{status:e?"healthy":"error",message:e?"CLI interface is running":"CLI interface not running",lastCheck:new Date,details:{hasInkApp:this.inkApp!==null,hasCallbacks:this.hasProgressCallback()}}}}}getApprovalTracker(){return this.approvalTracker??=new L({loadMessages:async(e)=>await this.context?.conversations.getMessages(e,{limit:50})??[],onRestoreError:(e,s)=>{this.logger.debug("Failed to restore pending CLI approvals",{error:e,conversationId:s})}}),this.approvalTracker}async getPendingApprovalIds(e){return[...await this.getApprovalTracker().getApprovalIds(e)]}async processInput(e){this.startProcessingInput();try{let n=await this.getPendingApprovalIds("cli");if(n.length>0){if(await this.handleConfirmationResponse(e,"cli",n))return}let r=await this.getAgentService().chat(e,"cli",{userPermissionLevel:"admin",isAnchor:this.getContext().permissions.isAnchor("cli","local"),interfaceType:"cli",channelId:"cli",channelName:"CLI Terminal"}),t=I(r.cards),o=t.length>0?t.map((i)=>i.id):r.pendingConfirmations?.map((i)=>i.id)??[];this.getApprovalTracker().replaceApprovals("cli",o);let g=this.renderAgentResponse(r);this.logger.debug("Agent response received",{textLength:r.text.length,toolResultsCount:r.toolResults?.length??0,toolResults:r.toolResults?.map((i)=>({toolName:i.toolName,hasData:i.data!==void 0}))}),this.sendReply(g)}catch(n){this.logger.error("Error processing input",{error:n,input:e});let r=k(n,"An unknown error occurred");this.sendReply(`**Error:** ${r}`)}finally{this.endProcessingInput()}}renderAgentResponse(e){let s=w(e,{deniedCardIds:void 0}),n=[],r=[],t="";for(let o of s.directives)switch(o.kind){case"text":t=o.text;break;case"approvals":n.push(...o.cards);break;default:if(o.card.kind==="tool-approval"&&o.card.state==="approval-requested"){n.push(o.card);break}r.push(B(o.card,Z))}return[this.formatAgentResponseText(t,n),...r].filter((o)=>o.trim().length>0).join(`
|
|
3
3
|
|
|
4
4
|
`)}formatAgentResponseText(e,s){if(s.length===0)return e;let n=P(e,s);if(s.length===1){let t=s[0];if(!t)return e;let o=t.preview?`
|
|
5
5
|
|
|
@@ -15,5 +15,5 @@ _Please reply with **yes 1** / **no 1** for the matching action._`}formatApprova
|
|
|
15
15
|
|
|
16
16
|
${r.error}`:`${t} ${r.summary}`}resolveApprovalIndexSugar(e,s){let n=/^(.*?)\s+#?(\d+)$/.exec(e.trim());if(!n?.[1]||!n[2])return e;let r=s[Number(n[2])-1];return r?`${n[1]} ${r}`:e}async handleConfirmationResponse(e,s,n){let r=new Set(n);if(!S(e,r))return!1;let t=R({message:this.resolveApprovalIndexSugar(e,n),approvalIds:r});if(t.kind==="not-confirmation")return!1;if(t.kind==="notice")return this.sendReply(`_${t.message}_`),!0;this.getApprovalTracker().removeApproval(s,t.approvalId);let o=await this.getAgentService().confirmPendingAction(s,t.confirmed,t.approvalId,{userPermissionLevel:"admin",isAnchor:this.getContext().permissions.isAnchor("cli","local"),interfaceType:"cli"});return this.getApprovalTracker().syncFromResponse(s,o,t.approvalId),this.sendReply(this.formatApprovalResultText(o.text,o.cards)),!0}registerSignalHandlers(){let e=()=>{this.logger.debug("Received termination signal, stopping CLI"),this.cleanup()};this.removeSignalHandlers=D(["SIGINT","SIGTERM"],e)}async cleanup(){if(this.unregisterProgressCallback(),this.unregisterMessageCallbacks(),this.removeSignalHandlers?.(),this.removeSignalHandlers=void 0,this.inkApp)this.inkApp.unmount(),this.inkApp=null}}import{jsx as a,jsxs as p}from"react/jsx-runtime";function q({batchStatus:e,showDetails:s=!0}){let{batchId:n,totalOperations:r,completedOperations:t,failedOperations:o,currentOperation:g,errors:i,status:u}=e,O=u==="processing",v=u==="completed",C=u==="failed",b=t-o;return p(c,{flexDirection:"column",marginY:1,children:[a(c,{marginBottom:1,children:p(l,{bold:!0,color:"cyan",children:["Batch Operation: ",n.slice(0,8),"..."]})}),a(c,{marginBottom:1,children:a(f,{current:t,total:r,message:"Overall Progress:",color:C?"red":v?"green":"yellow"})}),O&&g&&p(c,{marginBottom:1,children:[a(l,{color:"green",children:a(z,{type:"dots"})}),p(l,{color:"gray",children:[" ",g]})]}),s&&p(c,{flexDirection:"column",marginLeft:2,children:[b>0&&p(c,{children:[a(l,{color:"green",children:"\u2713 "}),p(l,{color:"gray",children:[b," successful"]})]}),o>0&&p(c,{children:[a(l,{color:"red",children:"\u2717 "}),p(l,{color:"gray",children:[o," failed"]})]}),i.length>0&&p(c,{flexDirection:"column",marginTop:1,children:[a(l,{color:"red",bold:!0,children:"Errors:"}),i.slice(0,3).map((H,_)=>p(c,{marginLeft:2,children:[a(l,{color:"red",children:"\u2022 "}),a(l,{color:"gray",children:H})]},_)),i.length>3&&a(c,{marginLeft:2,children:p(l,{color:"gray",children:["... and ",i.length-3," more"]})})]})]}),v&&a(c,{marginTop:1,children:a(l,{color:"green",bold:!0,children:"\u2713 Batch completed successfully"})}),C&&a(c,{marginTop:1,children:a(l,{color:"red",bold:!0,children:"\u2717 Batch failed"})})]})}export{q as BatchProgress,E as CLIInterface,f as ProgressBar,m as cliConfigSchema};
|
|
17
17
|
|
|
18
|
-
//# debugId=
|
|
19
|
-
//# sourceMappingURL=index-
|
|
18
|
+
//# debugId=CAD20534D3D08C2764756E2164756E21
|
|
19
|
+
//# sourceMappingURL=index-5b1wstvc.js.map
|
|
@@ -7,6 +7,6 @@
|
|
|
7
7
|
"/** @jsxImportSource react */\nimport React from \"react\";\nimport { Box, Text } from \"ink\";\nimport Spinner from \"ink-spinner\";\nimport type { BatchJobStatus } from \"@brains/job-queue\";\nimport { ProgressBar } from \"./ProgressBar\";\n\ninterface BatchProgressProps {\n batchStatus: BatchJobStatus;\n showDetails?: boolean;\n}\n\nexport function BatchProgress({\n batchStatus,\n showDetails = true,\n}: BatchProgressProps): React.ReactElement {\n const {\n batchId,\n totalOperations,\n completedOperations,\n failedOperations,\n currentOperation,\n errors,\n status,\n } = batchStatus;\n\n const inProgress = status === \"processing\";\n const isCompleted = status === \"completed\";\n const isFailed = status === \"failed\";\n\n // Calculate successful operations\n const successfulOperations = completedOperations - failedOperations;\n\n return (\n <Box flexDirection=\"column\" marginY={1}>\n {/* Header */}\n <Box marginBottom={1}>\n <Text bold color=\"cyan\">\n Batch Operation: {batchId.slice(0, 8)}...\n </Text>\n </Box>\n\n {/* Main progress bar */}\n <Box marginBottom={1}>\n <ProgressBar\n current={completedOperations}\n total={totalOperations}\n message=\"Overall Progress:\"\n color={isFailed ? \"red\" : isCompleted ? \"green\" : \"yellow\"}\n />\n </Box>\n\n {/* Current operation */}\n {inProgress && currentOperation && (\n <Box marginBottom={1}>\n <Text color=\"green\">\n <Spinner type=\"dots\" />\n </Text>\n <Text color=\"gray\"> {currentOperation}</Text>\n </Box>\n )}\n\n {/* Details section */}\n {showDetails && (\n <Box flexDirection=\"column\" marginLeft={2}>\n {/* Success count */}\n {successfulOperations > 0 && (\n <Box>\n <Text color=\"green\">✓ </Text>\n <Text color=\"gray\">{successfulOperations} successful</Text>\n </Box>\n )}\n\n {/* Failure count */}\n {failedOperations > 0 && (\n <Box>\n <Text color=\"red\">✗ </Text>\n <Text color=\"gray\">{failedOperations} failed</Text>\n </Box>\n )}\n\n {/* Errors */}\n {errors.length > 0 && (\n <Box flexDirection=\"column\" marginTop={1}>\n <Text color=\"red\" bold>\n Errors:\n </Text>\n {errors.slice(0, 3).map((error, index) => (\n <Box key={index} marginLeft={2}>\n <Text color=\"red\">• </Text>\n <Text color=\"gray\">{error}</Text>\n </Box>\n ))}\n {errors.length > 3 && (\n <Box marginLeft={2}>\n <Text color=\"gray\">... and {errors.length - 3} more</Text>\n </Box>\n )}\n </Box>\n )}\n </Box>\n )}\n\n {/* Final status */}\n {isCompleted && (\n <Box marginTop={1}>\n <Text color=\"green\" bold>\n ✓ Batch completed successfully\n </Text>\n </Box>\n )}\n\n {isFailed && (\n <Box marginTop={1}>\n <Text color=\"red\" bold>\n ✗ Batch failed\n </Text>\n </Box>\n )}\n </Box>\n );\n}\n"
|
|
8
8
|
],
|
|
9
9
|
"mappings": "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",
|
|
10
|
-
"debugId": "
|
|
10
|
+
"debugId": "CAD20534D3D08C2764756E2164756E21",
|
|
11
11
|
"names": []
|
|
12
12
|
}
|
|
@@ -179,9 +179,9 @@ ${a}`:"")}function hd(e,t,n,o){let i=n.actor??{kind:"agent",agentId:"brain-agent
|
|
|
179
179
|
`),i=[];for(let a=0;a<Math.max(n.length,o.length);a++)if((n[a]??"")!==(o[a]??"")){if(n[a])i.push(`- ${n[a]}`);if(o[a])i.push(`+ ${o[a]}`)}return i.join(`
|
|
180
180
|
`)}function VI(e){let{entityService:t,logger:n,entityRegistry:o}=e,i=en({label:"update",requestNoun:"the update"});return Nr("update","Update an entity's fields or content. Requires confirmation; call this tool without confirmed to request that confirmation instead of asking for plain-text approval. For direct requests that provide exact IDs to set an existing image as an entity cover, call this tool on the target entity with fields.coverImageId set to the image ID; do not stop after lookup.",pI,async(a,s)=>{let l=ar(s.userPermissionLevel),c=await Dr(t,a.entityType,a.id,n,void 0,l);if(!c.ok)return{success:!1,error:c.error};let{entity:u}=c,d=bI({...a.fields!==void 0?{fields:a.fields}:{},...a.content!==void 0?{content:a.content}:{}}),m=d.content?.trim().length===0&&d.fields===void 0,p=u.metadata.status,f=!1;if(a.confirmed&&u.entityType==="agent"&&(p==="discovered"||p==="approved")&&(!d.content&&!d.fields||m)){let k=EG.safeParse(i.takePending(a.confirmationToken));if(!k.success||k.data.id!==u.id)return{success:!1,error:"No pending update confirmation found for this agent. Please request the update again and confirm the new approval."};d={fields:{status:"approved"}},f=!0}if(d.content&&d.fields)return{success:!1,error:"Provide either 'content' or 'fields', not both"};if(!d.content&&!d.fields)return{success:!1,error:"Provide 'content' (full replacement) or 'fields' (partial update)"};let g=PG(u.entityType,d);if(g)return g;let h=OG(u,d,o);if(h)return h;let v=NG(u.entityType,d,o);if(v)return v;let I=DG(u.entityType,d,o);if(I)return I;let S=u.metadata.status,y=zG(u,d,o),_=WI(u.entityType,S,y,o)==="non-publish"?"update":"publish",C=Or(e,a.entityType,_,s);if(C)return C;if(a.confirmed){if(!f){let z=i.validateConfirmed(a.confirmationToken,a);if(z)return z}if(a.contentHash&&u.contentHash!==a.contentHash)return{success:!1,error:"Entity was modified since you reviewed the changes. Please try again."};let k=d.content!==void 0?{...u,content:d.content,visibility:$s(d.content)??u.visibility}:HI(u,d.fields??{});if(k.visibility!==u.visibility&&!qs(s.userPermissionLevel,k.visibility))return{success:!1,error:`Cannot set entity visibility to "${k.visibility}" \u2014 caller permission "${s.userPermissionLevel??"public"}" is not allowed to write at that level.`};try{let z=zo(s);await t.updateEntity({entity:k,...z?{options:{eventContext:z}}:{}})}catch(z){return{success:!1,error:Ne(z,"Failed to update entity")}}return{success:!0,data:{updated:u.id}}}let x=zs(u),b=qG(u,d);return{needsConfirmation:!0,toolName:"system_update",summary:`Update "${x}"?`,completionSummary:`Updated ${_I(u.entityType)}.`,preview:b,args:i.buildArgs((k)=>({...a,...d,id:u.id,confirmed:!0,confirmationToken:k,contentHash:u.contentHash}))}},{visibility:"trusted",sideEffects:"writes"})}function ZI(e){return[xI(e),II(e),CI(e),VI(e)]}function Bs(e){if(e.interfaceType!==mt){if(e.guestExecution!==void 0)throw Error("Guest execution scope mismatch");return{}}let t=yn.safeParse(e.guestExecution);if(!t.success||e.userPermissionLevel!=="public"||e.isAnchor||!(e.signal instanceof AbortSignal))throw Error("Guest execution denied");return e.signal.throwIfAborted(),{readBudget:t.data.limits.retrieval,signal:e.signal,queryEmbedding:e.guestQueryEmbedding??(async()=>{throw Error("Guest query embedding unavailable")})}}var $G=0.5;function KI(e){let{entityService:t,logger:n}=e;return[Sn("system","search","Search entities using semantic search. For broad search, make one system_search call with scope.kind all. Use scope.kind type only when the user asks for a specific entity type. Applies a default minScore of 0.5 to reduce weak matches; lower minScore only for exploratory or loose recall. Search results include each matched entity's content. When relevant results already contain the complete evidence needed, answer from those results instead of redundantly listing or getting the same entities. Search results are candidates; do not present weak or unrelated candidates as exact matches.",sI,async(o,i)=>{let a=Bs(i),s=ar(i.userPermissionLevel);return{success:!0,data:{results:(await t.search({query:o.query,options:{limit:o.limit??e.searchLimit,...o.scope.kind==="type"&&{types:[o.scope.entityType]},minScore:o.minScore??$G,...o.includeUngenerated!==void 0&&{includeUngenerated:o.includeUngenerated},visibilityScope:s,...a}})).map((l)=>({...l,entity:Vd(l.entity)}))}}},{visibility:"public",sideEffects:"none",cli:{name:"search"}}),Sn("system","get","Retrieve a specific entity by type and identifier (ID, slug, or title). If retrieval fails, report the entity as not found rather than describing related generation work as pending.",lI,async(o,i)=>{let a=Bs(i);if(!a.readBudget&&!t.getEntityTypes().includes(o.entityType))return{success:!1,error:`Unknown entity type: ${o.entityType}. Available: ${t.getEntityTypes().join(", ")}`};let s=ar(i.userPermissionLevel),l=await Dr(t,o.entityType,o.id,n,void 0,s,a);if(!l.ok)return{success:!1,error:l.error};return{success:!0,data:{entity:Vd(l.entity)}}},{visibility:"public",sideEffects:"none",cli:{name:"get"}}),Sn("system","list","List entities by a known entity type. Returns metadata only \u2014 use system_get for full content. Use system_search, not system_list, for broad or vague lookup requests. Use system_list to inspect metadata dates such as publishedAt when the user asks for the latest item of a known type, such as latest blog post.",cI,async(o,i)=>{let a=Bs(i);if(!a.readBudget&&!t.getEntityTypes().includes(o.entityType))return{success:!1,error:`Unknown entity type: ${o.entityType}. Available: ${t.getEntityTypes().join(", ")}`};let l={visibilityScope:ar(i.userPermissionLevel)};if(o.status&&o.status!=="any")l.metadata={status:o.status};let u=(await t.listEntities({entityType:o.entityType,options:{limit:o.limit??20,filter:l,...a}})).map(({content:d,contentHash:m,...p})=>p);if(o.entityType==="post")u.sort((d,m)=>{let p=d.metadata.publishedAt,f=m.metadata.publishedAt,g=typeof p==="string"?Date.parse(p):0;return(typeof f==="string"?Date.parse(f):0)-g});return{success:!0,data:{entities:u,count:u.length}}},{visibility:"public",sideEffects:"none",cli:{name:"list"}})]}function QI(e){let{entityService:t}=e;return[Sn("system","insights",`Get aggregate content insights and analytics only. For a general overview of actual content, use system_list instead. Available types: ${e.insights.getTypes().join(", ")}.`,fI,async(n,o)=>{let i=ar(o.userPermissionLevel);return{success:!0,data:await e.insights.get(n.type,t,i)}},{visibility:"public",sideEffects:"none"})]}function YI(e){let{jobs:t}=e;return[Nr("job_status","Inspect runtime job status for ready checks only when the prior operation created a job, or for status disputes. With a known batch ID, pass batchId. Without an ID, call this tool with no arguments to list active jobs and batches; do not ask the user for an ID first. Do not argue from the transcript alone.",gI,async(n)=>{if(n.batchId){let a=await t.getBatchStatus(n.batchId);if(!a)return{success:!1,error:`No batch found with ID: ${n.batchId}`};let s=a.totalOperations>0?Math.round(a.completedOperations/a.totalOperations*100):0;return{success:!0,data:{batchId:n.batchId,status:a.status,progress:{total:a.totalOperations,completed:a.completedOperations,failed:a.failedOperations,percentComplete:s},currentOperation:a.currentOperation,errors:a.errors}}}let o=await t.getActiveJobs(n.jobTypes),i=await t.getActiveBatches();return{success:!0,data:{summary:{activeJobs:o.length,activeBatches:i.length},jobs:o.map((a)=>({id:a.id,type:a.type,status:a.status,priority:a.priority,retryCount:a.retryCount,createdAt:new Date(a.createdAt).toISOString(),startedAt:a.startedAt?new Date(a.startedAt).toISOString():null})),batches:i.map((a)=>({batchId:a.batchId,status:a.status.status,totalOperations:a.status.totalOperations,completedOperations:a.status.completedOperations,failedOperations:a.status.failedOperations,currentOperation:a.status.currentOperation,pluginId:a.metadata.metadata.pluginId,errors:a.status.errors}))}}},{visibility:"public",sideEffects:"none"})]}function UG(e){let{entityCounts:t,daemons:n,endpoints:o,interactions:i,...a}=e;return a}function XI(e){return[Sn("system","status","Get system status including model, version, interfaces, and tools",r.object({}),async()=>({success:!0,data:UG(await e.getAppInfo())}),{visibility:"public",sideEffects:"none",cli:{name:"status"}})]}function eC(e){return[...KI(e),...YI(e),...XI(e),...ZI(e),...QI(e)]}function tC(e){return[{uri:"entity://types",name:"Entity Types",description:"List of registered entity types",mimeType:"text/plain",handler:async()=>({contents:[{uri:"entity://types",mimeType:"text/plain",text:e.entityService.getEntityTypes().join(`
|
|
181
181
|
`)}]})},{uri:"brain://identity",name:"Brain Identity",description:"Brain character \u2014 name, role, purpose, values",mimeType:"application/json",handler:async()=>({contents:[{uri:"brain://identity",mimeType:"application/json",text:JSON.stringify(e.getIdentity(),null,2)}]})},{uri:"brain://profile",name:"Anchor Profile",description:"Brain owner profile \u2014 name, bio, expertise",mimeType:"application/json",handler:async()=>({contents:[{uri:"brain://profile",mimeType:"application/json",text:JSON.stringify(e.getProfile(),null,2)}]})},{uri:"brain://status",name:"Brain Status",description:"System status \u2014 version, model, interfaces, tools",mimeType:"application/json",handler:async()=>({contents:[{uri:"brain://status",mimeType:"application/json",text:JSON.stringify(await e.getAppInfo(),null,2)}]})}]}function rC(e){let{entityService:t}=e;return[{name:"entity-list",uriTemplate:"entity://{type}",description:"List entities of a given type",mimeType:"application/json",list:async()=>t.getEntityTypes().map((o)=>({uri:`entity://${o}`,name:`${o} entities`})),complete:{type:async()=>t.getEntityTypes()},handler:async({type:n})=>{let o=t.getEntityTypes();if(!o.includes(n))throw Error(`Unknown entity type: ${n}. Available: ${o.join(", ")}`);let a=(await t.listEntities({entityType:n})).map((s)=>({id:s.id,entityType:s.entityType,...s.metadata,updated:s.updated}));return{contents:[{uri:`entity://${n}`,mimeType:"application/json",text:JSON.stringify(a,null,2)}]}}},{name:"entity-detail",uriTemplate:"entity://{type}/{id}",description:"Read a single entity by type and ID",mimeType:"text/markdown",complete:{type:async(n)=>{let o=t.getEntityTypes();return n?o.filter((i)=>i.startsWith(n)):o},id:async()=>[]},handler:async({type:n,id:o})=>{let i=await t.getEntity({entityType:n,id:o});if(!i)throw Error(`Entity not found: ${n}/${o}`);return{contents:[{uri:`entity://${n}/${o}`,mimeType:"text/markdown",text:i.content}]}}}]}function nC(e){return[{name:"create",description:"Create new content of any type",args:{type:{description:"Entity type (e.g. post, deck, note)",required:!0},topic:{description:"Topic or title for the content"}},handler:async({topic:n,type:o})=>({messages:[{role:"user",content:{type:"text",text:n?`Create a new ${o} about: ${n}`:`Create a new ${o}. Ask me what it should be about.`}}]})},{name:"generate",description:"AI-generate content with a prompt",args:{type:{description:`Entity type (${(()=>e.entityService.getEntityTypes().join(", "))()})`,required:!0},topic:{description:"What to generate",required:!0}},handler:async({type:n,topic:o})=>({messages:[{role:"user",content:{type:"text",text:`Generate a ${n} about: ${o}`}}]})},{name:"review",description:"Review and improve existing content",args:{type:{description:"Entity type",required:!0},id:{description:"Entity ID or slug",required:!0}},handler:async({type:n,id:o})=>({messages:[{role:"user",content:{type:"text",text:`Review my ${n} "${o}". Read it first, then give me specific feedback on structure, clarity, and impact. Suggest concrete improvements.`}}]})},{name:"publish",description:"Publish content \u2014 preview, confirm, and publish",args:{type:{description:"Entity type",required:!0},id:{description:"Entity ID or slug",required:!0}},handler:async({type:n,id:o})=>({messages:[{role:"user",content:{type:"text",text:`I want to publish my ${n} "${o}". Show me a preview first, then publish it.`}}]})},{name:"brainstorm",description:"Brainstorm ideas using brain context and expertise",args:{topic:{description:"Topic to brainstorm about",required:!0}},handler:async({topic:n})=>({messages:[{role:"user",content:{type:"text",text:`Let's brainstorm about: ${n}. Use my existing content and expertise as context. Give me fresh angles and concrete ideas.`}}]})}]}function oC(e){let t=e.entityService.getEntityTypes(),n=t.map((o)=>`- ${o}: ${e.entityRegistry.getAdapter(o).purpose}`);return["## Entity CRUD","","Use these system tools for ALL entity operations:","","Treat explicit verbs such as save, create, capture, remember, import, add/connect an agent, approve/archive an agent, update, delete, generate, publish, or sync as mutation requests. Entity-specific instructions may require proactively recording the user's request itself for demand or workflow tracking; follow those instructions even when the user did not use a separate save/create verb. Ordinary status questions like 'what is the status', 'is it ready', 'did that finish', or 'what happened' are not capture requests; answer them by using the relevant status/read tool for the prior operation. Never call system_create for a status follow-up.","",`- **system_create**: Create an entity from existing/concrete material. Requires confirmation before persisting; call system_create without \`confirmed\` to request that confirmation, and never pass \`confirmed: true\` on the initial user request. Use the typed \`visibility\` field instead of rewriting exact source text: omit it for public content, use \`shared\` for team/collaborator content, and use \`restricted\` for private/admin-only content. Public callers may write only public content, Trusted callers public or shared, and Admin callers all three levels. If the user only says 'not public' without clarifying team/shared versus private/admin-only, ask which scope they mean. Required \`source\` union: \`{ kind: "text", content }\` stores exact/direct content; \`{ kind: "url", url }\` creates URL/domain-backed records such as links (preserve bare domains as bare domains); \`{ kind: "upload", upload, transform: "extract-markdown" }\` imports extractable upload bytes as a markdown note; \`{ kind: "upload", upload, transform: "preserve" }\` preserves raw uploaded bytes as their durable file entity; \`{ kind: "prior-response", messageId? }\` saves a previous assistant answer/summary by reference without copying or paraphrasing it. If a direct save request includes the content in the same message, that content is sufficient, even if it is a short sentence or fragment after a colon or embedded in the request phrase (for example, "Save this memo about the launch timeline"); call system_create instead of asking the user to paste it again. If the immediate context is an assistant summary/answer about an upload and the user asks to save it/that/this/summary, create a note with \`source.kind: "prior-response"\`; do this even when the prior answer says there was limited readable content. Do not ask whether they meant the summary or the file after you just summarized/answered about the upload: save the visible prior response as a note. Do not treat a bare upload receipt like "I got filename. What would you like me to do?" as a summary/answer; if the user then says "save it", preserve the latest uploaded file itself with upload \`preserve\` (image uploads as entityType image, PDFs/documents as their file entity type). Only use \`source.kind: "upload"\` when the runtime supplies an actual upload reference/attachment; if the message contains a pasted uploaded-file transcript with readable content (for example, \`User uploaded a file "name.md": ...\`), save the visible transcript content as \`source.kind: "text"\`. Only use upload \`preserve\` otherwise when they explicitly want to save the uploaded file/document itself. Use system_generate, not system_create, for AI generation, generated images, cover images, and source-derived artifacts. Available entity types: ${t.join(", ")}.`,'- **system_generate**: Generate a new durable entity or deterministic artifact. Requires confirmation before queueing generation; call system_generate without `confirmed` to request that confirmation, and never pass `confirmed: true` on the initial user request. When the user asks you to create, write, draft, or generate new durable content and has not supplied final exact content to store, call system_generate instead of drafting the content in chat or asking for separate prose approval first. An underspecified generation request still supplies a usable prompt: preserve the user\'s wording and call system_generate rather than stopping to ask what details to highlight. If generation depends on an existing source entity (for example, a newsletter from a blog post), resolve the source with read/search/list tools first unless the user gives an exact entity id in quotes, then call system_generate with `operation.kind: "prompt-from-source"` in the same turn when a clear source is found. If you identify the latest source entity, do not stop after saying you found it or can generate from it; call system_generate immediately. If no existing durable source entity is resolved, use `operation.kind: "prompt"` in the same turn, including after an optional search returns no source; never invent source IDs, use upload IDs/filenames as entity refs, use profile/brain-character as a generic source, or create placeholder source refs. For broad topical prompts like \'about AI\' or \'about continuous learning\', use `operation.kind: "prompt"`. If the user asks for the latest published/blog source, use system_list for that entity type (for blog posts, entityType post) to inspect publishedAt dates; do not rely on semantic search result order. Choose the newest matching candidate by publishedAt (not updated/created order) and proceed with system_generate rather than asking for separate prose approval; do not say \'if that is the one you mean\' when a newest dated candidate is visible. Do not say \'I can generate it if you want\' or \'I need to queue it first\'; calling system_generate without `confirmed` is the confirmation request. Required `operation` union: `{ kind: "prompt", entityType, prompt }` generates a new non-image durable entity from a broad prompt with no source; `{ kind: "prompt-from-source", entityType, source: { entityType, entityId }, prompt }` generates from an existing resolved entity, such as a newsletter from a blog post. `{ kind: "standalone-image", prompt }` generates an unattached image; `{ kind: "cover-image", target: { entityType, entityId }, prompt }` generates an image and attaches it to an existing canonical entity as `coverImageId`; `{ kind: "attachment", source: { entityType, entityId }, attachmentType }` renders a deterministic artifact from an existing entity attachment provider. For save/render/regenerate/replace requests for source-derived artifacts, call `system_generate` with `operation.kind: "attachment"` immediately; do not say you can queue it later. Use `replace: true` when the user says regenerate, replace, refresh, or update an existing artifact. For OG/social preview renders, use `attachmentType: "og-image"`; the provider derives the output image and updates `ogImageId`. For deck carousel PDFs use `attachmentType: "carousel"`; for printable post/project PDFs use `attachmentType: "printable"`. If the user asks to attach a generated artifact to another entity, generate the artifact first in the same turn; do not ask whether they mean an existing artifact when the source entity is explicit or given as a quoted id such as deck `distributed-systems-primer`. Update the target only after the real artifact ID exists. To create an entity and a cover image, first create/generate only the entity, then after confirmation and after the real canonical entity ID is known call system_generate with operation.kind cover-image. When the user asks to create/regenerate a cover for an existing entity and search/get resolves a clear target, call system_generate with operation.kind cover-image in the same turn; do not stop after saying you found it. In a single initial request for \'a post with a cover image\', do not also call standalone-image; never guess a future target ID, and do not generate a standalone image as a substitute for a requested cover image.','- **system_update**: Modify an entity\'s fields or content. Use `fields` for title, status, coverImageId, ogImageId, and other frontmatter/metadata changes. To remove or clear a cover image, set `fields.coverImageId` to null, not an empty string. Use system_update for coverImageId when an existing image entity id is known; if the user says to set an existing image as a cover, update `coverImageId` even when `ogImageId` already points at that image. To generate a new cover image for an existing entity, use system_generate with operation.kind cover-image. Agent approval/archive is a status field update on `entityType: "agent"`. Cover images use `coverImageId`; OG/social previews use `ogImageId`; do not treat them as interchangeable. Requires confirmation before applying changes; call system_update without `confirmed` to request that confirmation, rather than asking for prose approval. For explicit publish requests, use the publishing tool if available instead of changing status with system_update; follow-up pronouns like \'publish it\' should target the entity just read or updated in the conversation.',"- **system_delete**: Admin-only entity removal. Requires confirmation before deleting. Never pass `confirmed: true` on the initial user request; call without `confirmed` so the confirmation flow can ask an administrator first. If system_delete is not available in the current tool surface, say deletion requires Admin access; do not imply a confirmation flow is available. Protected identity/profile records such as brain-character and anchor-profile cannot be deleted; update them instead.","- **system_get**: Retrieve a specific entity by type and ID/slug/title.","- **system_list**: List entities by type with optional filters. For simple list/show-all/summary requests that name or browse a known type or synonym (for example essays/articles/blog posts => post), use system_list with that entity type instead of semantic search. For 'summarize my blog posts' or similar whole-type summaries, first call system_list for entityType post, then summarize those returned entities. For 'latest' requests, use system_list metadata dates such as publishedAt to choose the newest candidate.","- **system_search**: Semantic search across all entities. Search results include matched entity content; when relevant results already provide the complete evidence needed, answer from them instead of redundantly calling system_list or system_get for those same entities. For each new user question about what they have written, mentioned, discussed, or saved, run a fresh search for that turn instead of answering from prior-turn search results or memory; this includes analysis phrasing such as 'How do I discuss X in my writing?' even if prior turns searched related topics.","","Do not ask for prose confirmation instead of calling the applicable mutating tool; for create, update, delete, generate, publish, sync, and similar state-changing requests, the initial tool call without `confirmed` is how you create the pending confirmation. Never claim a durable create/update/delete change was saved, created, done, or completed unless the relevant tool call succeeded or returned a pending confirmation. If a create/update/delete tool is unavailable, unauthorized, or intentionally not called, say you can't make that durable change from the current context; never say saved/created/done. For an explicit write request from an unauthorized/currently unwritable context, refuse directly; do not call system_status or system_search to check permission or look up related content. Entity type descriptions may mark projection-owned or system-maintained types read-only; refuse mutation requests for those types directly without calling read or write tools, searching for a target, or asking which record to mutate.","","Entity types \u2014 select `entityType` by what the type is for:",...n].join(`
|
|
182
|
-
`)}var qo="system",LG=r.object({toolName:r.string(),args:r.unknown(),...aC.partial().shape});function Gs(e,t,n,o,i){for(let a of e)try{t(a)}catch{i.debug(`System ${o} ${n(a)} already registered, skipping`)}}function sC(e,t,n,o){let i=eC(e);Gs(i,(d)=>t.registerTool(qo,d),(d)=>d.name,"tool",o);let a=n.subscribe(iC.toolExecute(qo),async(d)=>{let m=LG.safeParse(d.payload);if(!m.success)return{success:!1,error:"Invalid system tool execute payload"};let{toolName:p,args:f,interfaceType:g,actor:h,conversationId:v,channelId:I,userPermissionLevel:S}=m.data,y=i.find((C)=>C.name===p);if(!y)return{success:!1,error:`System tool not found: ${p}`};let w={interfaceType:g??"system",actor:h??{kind:"service",serviceId:"system"}};if(v)w.conversationId=v;if(I)w.channelId=I;if(S)w.userPermissionLevel=S;return{success:!0,data:await y.handler(f,w)}});o.debug(`Registered ${i.length} system tools`);let s=tC(e);Gs(s,(d)=>t.registerResource(qo,d),(d)=>d.uri,"resource",o),o.debug(`Registered ${s.length} system resources`);let l=rC(e);Gs(l,(d)=>t.registerResourceTemplate(qo,d),(d)=>d.uriTemplate,"resource template",o),o.debug(`Registered ${l.length} system resource templates`);let c=nC(e);Gs(c,(d)=>t.registerPrompt(qo,d),(d)=>d.name,"prompt",o),o.debug(`Registered ${c.length} system prompts`);let u=oC(e);try{t.registerInstructions(qo,u)}catch{o.debug("System instructions already registered, skipping")}return o.debug("Registered system instructions"),a}function dC(e){let{services:t,jobs:n,insights:o,query:i,getAppInfo:a}=e,s=t.jobQueueService,l=t.logger.child("system"),c=sC({entityService:t.entityService,entityRegistry:t.entityRegistry,jobs:{...n,enqueue:lC(s,"system",!1),enqueueBatch:cC(n,"system"),registerHandler:uC(s,"system")},conversationService:t.conversationService,runtimeUploads:t.runtimeUploadRegistry,attachments:Js(t.attachmentRegistry),logger:l,query:i,getIdentity:()=>t.identityService.getCharacter(),getProfile:()=>t.profileService.getProfile(),getAppInfo:a,searchLimit:10,insights:o,permissionService:t.permissionService},t.mcpService,t.messageBus,l);t.disposables.push(c)}class tp{scope;closePromise=null;closed=!1;constructor(){this.scope=de.runSync(Xe.make())}buildLayer(e){this.assertOpen();let t=de.runSyncExit(Xr.buildWithScope(e,this.scope));if(tt.isFailure(t))throw or.squash(t.cause);return t.value}addFinalizer(e){this.assertOpen(),de.runSync(Xe.addFinalizer(this.scope,de.promise(async()=>{await e()})))}addSyncFinalizer(e){this.assertOpen(),de.runSync(Xe.addFinalizer(this.scope,de.sync(()=>{e()})))}async fork(e){if(this.closed)throw Error("Cannot start background work after shell shutdown");await jr(de.forkIn(e,this.scope))}close(e=tt.void){if(this.closePromise)return this.closePromise;if(this.closed)return Promise.resolve();return this.closed=!0,this.closePromise=jr(Xe.close(this.scope,e)),this.closePromise}closeSync(e){if(this.closed)return;this.closed=!0,de.runSync(Xe.close(this.scope,e))}assertOpen(){if(this.closed)throw Error("Cannot register cleanup after shell shutdown")}}class ra{config;services;lifecycle;bootloader;initialized=!1;lifecycleState="constructed";bootMode;bootPromise=null;shutdownPromise=null;insightsRegistry;bootTime=Date.now();endpointRegistry=new Jd;interactionRegistry=new Wd;httpRouteRegistry=Ns.createFresh();jobs;static createFresh(e,t,n){return new ra(la(e),t,n)}constructor(e,t,n){this.config=e,this.lifecycle=new tp,fC(this,()=>this.httpRouteRegistry.getSnapshot());let o=t?.logger??vr.getInstance(),i=Ms.createFresh(o,this.config,n?.processRole);try{this.services=i.initializeServices(this.lifecycle,t),this.jobs=eI(this.services.batchJobManager,this.services.jobQueueService),this.insightsRegistry=KT(),this.bootloader=new Bd(this.config,this.services,this.lifecycle,i,n?.processRole,{registerCoreDataSources:()=>bp(this.services,this.config),finalizeHttpRoutes:()=>{this.httpRouteRegistry.finalize(nI(this.services.pluginManager))},registerSystemCapabilities:()=>dC({services:this.services,jobs:this.jobs,insights:this.insightsRegistry,query:(a,s)=>this.query(a,s),getAppInfo:()=>this.getAppInfo()}),createProjectionInputContext:()=>({entities:this.services.entityService,resolvePrompt:(a,s)=>ca(this.services.entityService,a,s),appInfo:()=>this.getAppInfo(),identityInput:()=>{let a=this.getIdentity(),s=this.getProfile();return mC.parse({brainName:a.name,role:a.role,purpose:a.purpose,values:a.values,profileName:s.name,...s.description!==void 0?{profileDescription:s.description}:{},...s.organization!==void 0?{profileOrganization:s.organization}:{}})}}),createProjectionExecutionContext:()=>({ai:gC(this),logger:this.services.logger.child("ProjectionRuntime")}),...t?.projectionRuntime&&{projectionRuntime:t.projectionRuntime}}),i.wireShell(this.services,this),iI(this.lifecycle,this.services)}catch(a){try{this.lifecycle.closeSync(tt.fail(a))}catch(s){o.error("Failed to roll back Shell service construction",s)}throw a}}getInsightsRegistry(){return this.insightsRegistry}async initialize(e){this.services.logger.debug("Shell.initialize() called");let t=e?.mode;if(this.lifecycleState==="running")return this.services.logger.warn("Shell already initialized"),Promise.resolve();if(this.lifecycleState==="booting"){if(this.bootMode!==t)return Promise.reject(Error(`Shell is already initializing in ${this.bootMode??"normal"} mode; cannot join ${t??"normal"} mode`));return this.bootPromise??Promise.resolve()}if(this.lifecycleState==="closing"||this.lifecycleState==="closed")return Promise.reject(Error("Cannot initialize a closing shell"));if(this.lifecycleState==="failed")return Promise.reject(Error("Cannot initialize a shell after boot failure"));return this.lifecycleState="booting",this.bootMode=t,this.bootPromise=this.boot(e),this.bootPromise}async boot(e){try{if(await this.bootloader.boot(e),this.lifecycleState==="booting"){if(this.lifecycleState="running",this.initialized=!0,e?.mode===void 0)await this.services.messageBus.send({type:Os.shellReady,payload:{timestamp:new Date().toISOString()},sender:"shell",broadcast:!0}),this.services.logger.debug("Emitted shell ready event")}}catch(t){if(this.initialized=!1,this.services.logger.error("Failed to initialize Shell",t),this.lifecycleState!=="closing")this.lifecycleState="failed";try{await this.lifecycle.close(tt.fail(t))}catch(n){this.services.logger.error("Failed to clean up Shell after initialization failure",n)}throw t}}registerTemplates(e,t){this.services.logger.debug("Registering templates",{pluginId:t,count:Object.keys(e).length});for(let[n,o]of Object.entries(e))this.registerTemplate(n,o,t)}shutdown(){return this.shutdownPromise??=this.shutdownShell(),this.shutdownPromise}async shutdownShell(){if(this.services.logger.debug("Shutting down Shell"),this.lifecycleState==="closed")return;let e=this.bootPromise;if(this.lifecycleState="closing",this.initialized=!1,e)try{await e}catch{}await this.lifecycle.close(),this.lifecycleState="closed",this.services.logger.debug("Shell shutdown complete")}async generateContent(e){return this.requireInitialized("Shell content generation"),_p(this.services,e)}async query(e,t){this.requireInitialized("Shell query");let n=await this.getAppInfo(),o={...t,appInfo:n,timestamp:new Date().toISOString()},{conversationHistory:i,...a}=o;return pC.parse(await this.generateContent({prompt:e,templateName:Zo.KNOWLEDGE_QUERY,...i&&{conversationHistory:i},data:a,interfacePermissionGrant:"public"}))}registerPlugin(e){this.requireInitialized("Plugin registration"),this.services.pluginManager.registerPlugin(e)}isInitialized(){return this.initialized}requireInitialized(e){if(!this.initialized)throw Error(`${e} attempted before initialization`)}getEntityService(){return this.services.entityService}getConversationService(){return this.services.conversationService}getEntityRegistry(){return this.services.entityRegistry}async generateObject(e,t,n){this.requireInitialized("Shell generateObject");let{object:o}=await this.services.aiService.generateObject("You are a helpful assistant. Respond with the requested structured data.",e,t,n);return{object:o}}async judge(e){return this.requireInitialized("Shell judge"),this.services.aiService.judge(e)}getAIService(){return this.services.aiService}generateImage(e,t){return this.services.aiService.generateImage(e,t)}canGenerateImages(){return this.services.aiService.canGenerateImages()}getPluginManager(){return this.services.pluginManager}getContentService(){return this.services.contentService}getRenderService(){return this.services.renderService}getAttachmentRegistry(){return Js(this.services.attachmentRegistry)}getRuntimeUploadRegistry(){return hC(this.services.runtimeUploadRegistry)}getRuntimeState(){return this.services.runtimeStateService}getRecurringChecks(e){return this.services.recurringCheckService.namespace(e)}isJobQueueWorkerRunning(){return this.services.jobQueueWorker.isWorkerRunning()}getMessageBus(){return this.services.messageBus}getLogger(){return this.services.logger}getJobQueueService(){return this.services.jobQueueService}getMCPService(){return this.services.mcpService}listToolsForPermissionLevel(e){return this.services.mcpService.listToolsForPermissionLevel(e).map(({pluginId:t,tool:n})=>({name:n.name,description:n.description,pluginId:t}))}getPermissionService(){return this.services.permissionService}getAgentService(){return this.services.agentService}registerTools(e,t){for(let n of t)try{this.services.mcpService.registerTool(e,n)}catch(o){this.services.logger.error(`Failed to register tool ${n.name} from ${e}:`,o)}}registerResources(e,t){for(let n of t)try{this.services.mcpService.registerResource(e,n)}catch(o){this.services.logger.error(`Failed to register resource ${n.name} from ${e}:`,o)}}registerResourceTemplate(e,t){this.services.mcpService.registerResourceTemplate(e,t)}registerPrompt(e,t){this.services.mcpService.registerPrompt(e,t)}registerInstructions(e,t){if(this.services.mcpService.registerInstructions(e,t),this.initialized)this.services.agentService.invalidateAgent()}async unregisterPluginCapabilities(e){this.services.mcpService.unregisterPlugin?.(e),this.services.jobQueueService.unregisterPluginHandlers(e),this.endpointRegistry.unregister(e),this.interactionRegistry.unregister(e);for(let n of this.services.templateRegistry.getNames())if(n.startsWith(`${e}:`))this.services.templateRegistry.unregister(n);let t=this.config.evalHandlerRegistry;if(t){for(let n of t.list())if(n.pluginId===e)t.unregister(e,n.handlerId)}this.services.agentService.invalidateAgent(),await this.services.recurringCheckService.unregisterPlugin(e)}getPluginPackageName(e){return this.services.pluginManager.getPluginPackageName(e)}hasPlugin(e){return this.services.pluginManager.hasPlugin(e)}getPluginHttpRouteManifest(){return this.httpRouteRegistry.getManifest()}getPluginApiRoutes(){return this.httpRouteRegistry.getApiRoutes()}getPluginWebRoutes(){return this.httpRouteRegistry.getWebRoutes()}registerDaemon(e,t,n){this.services.daemonRegistry.register(e,t,n)}registerEndpoint(e){this.endpointRegistry.register(e)}listEndpoints(){return this.endpointRegistry.list()}registerInteraction(e){this.interactionRegistry.register(e)}listInteractions(){return this.interactionRegistry.list()}registerTemplate(e,t,n){let o=n?`${n}:${e}`:`shell:${e}`;this.services.templateRegistry.register(o,t),this.services.logger.debug(`Registered template: ${o}`)}getTemplate(e){return this.services.templateRegistry.get(e)}getDataSourceRegistry(){return this.services.dataSourceRegistry}getIdentity(){return this.services.identityService.getCharacter()}getProfile(){return this.services.profileService.getProfile()}getProfileKindRegistry(){return this.services.profileKindRegistry}getChannelRegistry(){return this.services.channelRegistry}getInboxRegistry(){return this.services.inboxRegistry}getInboxFollowUpRegistry(){return this.services.inboxFollowUpRegistry}getOperationalHealthRegistry(){return this.services.operationalHealthRegistry}getAccountSettingsRegistry(){return this.services.accountSettingsRegistry}getDomain(){return this.config.siteBaseUrl}getLocalSiteUrl(){return this.config.localSiteUrl}shouldPreferLocalUrls(){return this.config.preferLocalUrls}getThemeCSS(){return this.config.themeCSS}getSpaces(){return this.config.spaces}getDataDir(){return this.config.dataDir}getGitBrokerSocket(){return this.config.gitBrokerSocket}getGitBrokerCheckout(){return this.config.gitBrokerCheckout}registerEvalHandler(e,t,n){this.config.evalHandlerRegistry?.register(e,t,n)}async getAppInfo(){return dp({config:this.config,services:this.services,bootTime:this.bootTime,endpoints:()=>this.listEndpoints(),interactions:()=>this.listInteractions()})}getRuntimeReadiness(){return gp(this.services)}}var yC={kind:"service",serviceId:"brain-cli"},rp=`${yC.kind}:${yC.serviceId}`;var FG=r.object({id:r.string(),version:r.string(),type:r.enum(["core","entity","service","interface"]),description:r.string().optional(),dependencies:r.array(r.string()).optional(),packageName:r.string()}),BG=r.object({name:r.string(),role:r.string(),purpose:r.string(),values:r.array(r.string())}),GG=r.enum(["debug","info","warn","error"]),JG=r.enum(["none","low","medium","high","xhigh","max"]),WG=r.object({provider:r.enum(["hetzner","docker"]).default("hetzner"),serverSize:r.string().default("cx33"),location:r.string().default("fsn1"),domain:r.string().optional(),docker:r.object({enabled:r.boolean().default(!0),image:r.string().optional()}).prefault({}),ports:r.object({default:r.number().default(3333),preview:r.number().default(4321),production:r.number().default(8080)}).prefault({}),cdn:r.object({enabled:r.boolean().default(!1),provider:r.enum(["bunny","none"]).default("none")}).prefault({}),dns:r.object({enabled:r.boolean().default(!1),provider:r.enum(["bunny","none"]).default("none")}).prefault({}),paths:r.object({install:r.string().optional(),data:r.string().optional()}).prefault({})}),vC=r.object({name:r.string().default("brain-app"),version:r.string().default("1.0.0"),database:r.string().optional(),aiApiKey:r.string().optional(),aiImageKey:r.string().optional(),aiModel:r.string().optional(),aiReasoningEffort:JG.optional(),logLevel:GG.optional(),logFile:r.string().optional(),profileKind:r.string().trim().min(1).optional(),plugins:r.array(FG).default([]),spaces:r.array(r.string()).default([]),identity:BG.optional(),agentInstructions:r.array(r.string()).optional(),deployment:WG.prefault({})});async function RC(e,t){await wn({label:"entity",config:e,schema:{assets:bC,entities:_C,projectionDirtyInputs:SC,projectionWaves:wC,projectionIncidents:xC,projectionWaveInputs:TC,projectionWaveRules:IC,projectionRuleMemos:CC},migrationsFolder:xn(import.meta.url,"entity-service"),authTokenEnv:"DATABASE_AUTH_TOKEN",logger:t,afterMigrate:kC})}async function PC(e,t){await wn({label:"job-queue",config:e,schema:{jobQueue:EC,jobWorkerSessions:AC},migrationsFolder:xn(import.meta.url,"job-queue"),authTokenEnv:"JOB_QUEUE_DATABASE_AUTH_TOKEN",logger:t})}async function NC(e,t){await wn({label:"conversation",config:e,schema:{conversations:MC,messages:jC,summaryTracking:OC},migrationsFolder:xn(import.meta.url,"conversation-service"),authTokenEnv:"CONVERSATION_DATABASE_AUTH_TOKEN",logger:t})}async function DC(e,t){await wn({label:"runtime-state",config:e,schema:{runtimeStateRecords:Le},migrationsFolder:xn(import.meta.url,"runtime-state"),authTokenEnv:"RUNTIME_STATE_DATABASE_AUTH_TOKEN",logger:t})}import{mkdir as np}from"fs/promises";import{isAbsolute as HG}from"path";function zC(e=process.env){return{dataDir:e.XDG_DATA_HOME??"./data",cacheDir:e.XDG_CACHE_HOME??"./cache",distDir:"./dist"}}function qC(e=process.env){let t=e.BRAIN_GIT_BROKER_CHECKOUT;if(!t)return;if(!HG(t))throw Error(`Git broker checkout path must be absolute: ${t}`);return t}function $C(e=process.env){let t=e.BRAIN_GIT_BROKER_SOCKET;if(!t)return;let n=107;if(Buffer.byteLength(t)>n)throw Error(`Git broker socket path is too long for a unix socket (${Buffer.byteLength(t)} > ${n} bytes): ${t}`);return t}function UC(e=process.env){return Cn(zC(e))}async function LC(e=process.env){let t=zC(e);try{await np(t.dataDir,{recursive:!0}),await np(t.cacheDir,{recursive:!0}),await np(t.distDir,{recursive:!0})}catch(n){let o=n instanceof Error&&n.message.includes("EACCES")?`Cannot create data directories \u2014 permission denied. Run from a writable directory or check permissions on ${t.dataDir}`:`Cannot create data directories: ${Ne(n)}`;throw Error(o,{cause:n})}return Cn(t)}class op{logger;migrations;constructor(e,t){this.logger=e,this.migrations=t??{getStandardConfigWithDirectories:()=>LC(),migrateEntities:RC,migrateJobQueue:PC,migrateConversations:NC,migrateRuntimeState:DC}}async runAllMigrations(e){this.logger.debug("Running database migrations...");let t=await this.migrations.getStandardConfigWithDirectories();if(e?.database)t.database.url=e.database;if(e?.jobQueueDatabase)t.jobQueueDatabase.url=e.jobQueueDatabase;if(e?.conversationDatabase)t.conversationDatabase.url=e.conversationDatabase;if(e?.runtimeStateDatabase)t.runtimeStateDatabase.url=e.runtimeStateDatabase;await this.migrateEntityDatabase(t),await this.migrateJobQueueDatabase(t),await this.migrateConversationDatabase(t),await this.migrateRuntimeStateDatabase(t),this.logger.debug("All database migrations completed successfully")}async migrateEntityDatabase(e){this.logger.debug("Running entity database migrations..."),await this.migrations.migrateEntities({url:e.database.url,...e.database.authToken&&{authToken:e.database.authToken}},this.logger)}async migrateJobQueueDatabase(e){this.logger.debug("Running job queue database migrations..."),await this.migrations.migrateJobQueue({url:e.jobQueueDatabase.url,...e.jobQueueDatabase.authToken&&{authToken:e.jobQueueDatabase.authToken}},this.logger)}async migrateConversationDatabase(e){this.logger.debug("Running conversation database migrations..."),await this.migrations.migrateConversations({url:e.conversationDatabase.url,...e.conversationDatabase.authToken&&{authToken:e.conversationDatabase.authToken}},this.logger)}async migrateRuntimeStateDatabase(e){this.logger.debug("Running runtime state database migrations..."),await this.migrations.migrateRuntimeState({url:e.runtimeStateDatabase.url,...e.runtimeStateDatabase.authToken&&{authToken:e.runtimeStateDatabase.authToken}},this.logger)}}var VG="NODE_ENV";function ZG(){return process.env[VG]}function FC(){return ZG()!=="production"}var KG="startup-check";function QG(e){let n={...vC.parse(e??{}),plugins:e?.plugins??[]};if(e?.permissions)n.permissions=e.permissions;if(e?.spaces)n.spaces=e.spaces;if(e?.cliConfig)n.cliConfig=e.cliConfig;if(e?.shellConfig)n.shellConfig=e.shellConfig;return n}function YG(e,t){let n={plugins:e.plugins??[],...e.shellConfig};return XG(n),e4(e,n),t4(e,n,t),r4(e,n),n4(e,n),o4(e,n),i4(e,n),a4(e,n),s4(e,n),n}function XG(e){let t=UC();e.database??=t.database,e.jobQueueDatabase??=t.jobQueueDatabase,e.conversationDatabase??=t.conversationDatabase,e.runtimeStateDatabase??=t.runtimeStateDatabase,e.embeddingDatabase??=t.embeddingDatabase,e.embedding??=t.embedding,e.gitBrokerSocket??=$C(),e.gitBrokerCheckout??=qC()}function e4(e,t){if(e.database)t.database={url:e.database};if(e.profileKind)t.profileKind=e.profileKind;t.features={}}function t4(e,t,n){let o=n?.mode==="startup-check";if(!e.aiApiKey&&!e.aiImageKey&&!e.aiModel&&!e.aiReasoningEffort&&!o)return;t.ai={...t.ai,...o&&!t.ai?.apiKey&&{apiKey:KG},...e.aiApiKey&&{apiKey:e.aiApiKey},...e.aiImageKey&&{imageApiKey:e.aiImageKey},...e.aiModel&&{model:e.aiModel},...e.aiReasoningEffort&&{reasoningEffort:e.aiReasoningEffort}}}function r4(e,t){if(!e.logLevel&&!e.logFile)return;t.logging={level:e.logLevel??"info",format:"text",context:e.name,...e.logFile&&{file:e.logFile}}}function n4(e,t){let n=e.permissions?.rules??[];t.permissions={...e.permissions,rules:n.some((o)=>o.pattern===rp)?n:[{pattern:rp,level:"admin"},...n]}}function o4(e,t){if(e.spaces)t.spaces=e.spaces}function i4(e,t){if(e.identity)t.identity=e.identity}function a4(e,t){if(e.agentInstructions)t.agentInstructions=e.agentInstructions}function s4(e,t){if(t.name=e.name,t.version=e.version,e.deployment.domain)t.siteBaseUrl=e.deployment.domain;t.localSiteUrl=`http://localhost:${e.deployment.ports.production}`,t.preferLocalUrls=FC()}class ap{shell=null;config;signalScope=null;signalShutdownFiber=null;stopPromise=null;hasCLI=!1;static create(e,t){return new ap(QG(e),t)}constructor(e,t){if(this.config=e,this.hasCLI=process.argv.slice(2).includes("--cli"),t)this.shell=t}async migrate(){let e=vr.getInstance();await new op(e).runAllMigrations({database:this.config.shellConfig?.database?.url??this.config.database,jobQueueDatabase:this.config.shellConfig?.jobQueueDatabase?.url,conversationDatabase:this.config.shellConfig?.conversationDatabase?.url,runtimeStateDatabase:this.config.shellConfig?.runtimeStateDatabase?.url})}createShell(e,t){this.shell=ra.createFresh(YG(this.config,e),void 0,{...t&&{processRole:t}})}async registerCLIInterface(){if(!this.hasCLI)return;let e=this.getShell().getPluginManager(),{CLIInterface:t}=await import("./index-e7kyv559.js"),n=new t(this.config.cliConfig);e.registerPlugin(n)}async initialize(e,t){if(!this.shell){if(!t?.migrationsCompleted)await this.migrate();this.createShell(e,t?.processRole)}await this.registerCLIInterface(),await this.getShell().initialize(e)}async start(){if(this.stopPromise)return;this.setupSignalHandlers()}stop(){return this.stopPromise??=this.stopApp(),this.stopPromise}async stopApp(){await this.closeSignalScope(),await this.shell?.shutdown()}async run(e){let n={debug:zt.DEBUG,info:zt.INFO,warn:zt.WARN,error:zt.ERROR}[this.config.logLevel??"info"]??zt.INFO,o=vr.createFresh({level:n,context:this.config.name,useStderr:this.hasCLI});if(this.hasCLI)vr.getInstance().setUseStderr(!0);try{await this.initialize(void 0,e),await this.start(),e?.onRuntimeReady?.(),o.info(`\u2705 ${this.config.name} v${this.config.version} ready`),process.stdin.resume()}catch(i){o.error(`\u274C Failed to start ${this.config.name}:`,i),process.exit(1)}}static async run(e,t,n){await ap.create(e,t).run(n)}setupSignalHandlers(){if(this.signalScope)return;let e=de.runSync(Xe.make()),t=()=>{this.requestGracefulShutdown("SIGINT")},n=()=>{this.requestGracefulShutdown("SIGTERM")},o=ip(["SIGINT"],t),i=ip(["SIGTERM"],n);de.runSync(Xe.addFinalizer(e,de.sync(()=>{i(),o()}))),this.signalScope=e}requestGracefulShutdown(e){if(this.signalShutdownFiber)return;let t=vr.getInstance();t.info(`
|
|
182
|
+
`)}var qo="system",LG=r.object({toolName:r.string(),args:r.unknown(),...aC.partial().shape});function Gs(e,t,n,o,i){for(let a of e)try{t(a)}catch{i.debug(`System ${o} ${n(a)} already registered, skipping`)}}function sC(e,t,n,o){let i=eC(e);Gs(i,(d)=>t.registerTool(qo,d),(d)=>d.name,"tool",o);let a=n.subscribe(iC.toolExecute(qo),async(d)=>{let m=LG.safeParse(d.payload);if(!m.success)return{success:!1,error:"Invalid system tool execute payload"};let{toolName:p,args:f,interfaceType:g,actor:h,conversationId:v,channelId:I,userPermissionLevel:S}=m.data,y=i.find((C)=>C.name===p);if(!y)return{success:!1,error:`System tool not found: ${p}`};let w={interfaceType:g??"system",actor:h??{kind:"service",serviceId:"system"}};if(v)w.conversationId=v;if(I)w.channelId=I;if(S)w.userPermissionLevel=S;return{success:!0,data:await y.handler(f,w)}});o.debug(`Registered ${i.length} system tools`);let s=tC(e);Gs(s,(d)=>t.registerResource(qo,d),(d)=>d.uri,"resource",o),o.debug(`Registered ${s.length} system resources`);let l=rC(e);Gs(l,(d)=>t.registerResourceTemplate(qo,d),(d)=>d.uriTemplate,"resource template",o),o.debug(`Registered ${l.length} system resource templates`);let c=nC(e);Gs(c,(d)=>t.registerPrompt(qo,d),(d)=>d.name,"prompt",o),o.debug(`Registered ${c.length} system prompts`);let u=oC(e);try{t.registerInstructions(qo,u)}catch{o.debug("System instructions already registered, skipping")}return o.debug("Registered system instructions"),a}function dC(e){let{services:t,jobs:n,insights:o,query:i,getAppInfo:a}=e,s=t.jobQueueService,l=t.logger.child("system"),c=sC({entityService:t.entityService,entityRegistry:t.entityRegistry,jobs:{...n,enqueue:lC(s,"system",!1),enqueueBatch:cC(n,"system"),registerHandler:uC(s,"system")},conversationService:t.conversationService,runtimeUploads:t.runtimeUploadRegistry,attachments:Js(t.attachmentRegistry),logger:l,query:i,getIdentity:()=>t.identityService.getCharacter(),getProfile:()=>t.profileService.getProfile(),getAppInfo:a,searchLimit:10,insights:o,permissionService:t.permissionService},t.mcpService,t.messageBus,l);t.disposables.push(c)}class tp{scope;closePromise=null;closed=!1;constructor(){this.scope=de.runSync(Xe.make())}buildLayer(e){this.assertOpen();let t=de.runSyncExit(Xr.buildWithScope(e,this.scope));if(tt.isFailure(t))throw or.squash(t.cause);return t.value}addFinalizer(e){this.assertOpen(),de.runSync(Xe.addFinalizer(this.scope,de.promise(async()=>{await e()})))}addSyncFinalizer(e){this.assertOpen(),de.runSync(Xe.addFinalizer(this.scope,de.sync(()=>{e()})))}async fork(e){if(this.closed)throw Error("Cannot start background work after shell shutdown");await jr(de.forkIn(e,this.scope))}close(e=tt.void){if(this.closePromise)return this.closePromise;if(this.closed)return Promise.resolve();return this.closed=!0,this.closePromise=jr(Xe.close(this.scope,e)),this.closePromise}closeSync(e){if(this.closed)return;this.closed=!0,de.runSync(Xe.close(this.scope,e))}assertOpen(){if(this.closed)throw Error("Cannot register cleanup after shell shutdown")}}class ra{config;services;lifecycle;bootloader;initialized=!1;lifecycleState="constructed";bootMode;bootPromise=null;shutdownPromise=null;insightsRegistry;bootTime=Date.now();endpointRegistry=new Jd;interactionRegistry=new Wd;httpRouteRegistry=Ns.createFresh();jobs;static createFresh(e,t,n){return new ra(la(e),t,n)}constructor(e,t,n){this.config=e,this.lifecycle=new tp,fC(this,()=>this.httpRouteRegistry.getSnapshot());let o=t?.logger??vr.getInstance(),i=Ms.createFresh(o,this.config,n?.processRole);try{this.services=i.initializeServices(this.lifecycle,t),this.jobs=eI(this.services.batchJobManager,this.services.jobQueueService),this.insightsRegistry=KT(),this.bootloader=new Bd(this.config,this.services,this.lifecycle,i,n?.processRole,{registerCoreDataSources:()=>bp(this.services,this.config),finalizeHttpRoutes:()=>{this.httpRouteRegistry.finalize(nI(this.services.pluginManager))},registerSystemCapabilities:()=>dC({services:this.services,jobs:this.jobs,insights:this.insightsRegistry,query:(a,s)=>this.query(a,s),getAppInfo:()=>this.getAppInfo()}),createProjectionInputContext:()=>({entities:this.services.entityService,resolvePrompt:(a,s)=>ca(this.services.entityService,a,s),appInfo:()=>this.getAppInfo(),identityInput:()=>{let a=this.getIdentity(),s=this.getProfile();return mC.parse({brainName:a.name,role:a.role,purpose:a.purpose,values:a.values,profileName:s.name,...s.description!==void 0?{profileDescription:s.description}:{},...s.organization!==void 0?{profileOrganization:s.organization}:{}})}}),createProjectionExecutionContext:()=>({ai:gC(this),logger:this.services.logger.child("ProjectionRuntime")}),...t?.projectionRuntime&&{projectionRuntime:t.projectionRuntime}}),i.wireShell(this.services,this),iI(this.lifecycle,this.services)}catch(a){try{this.lifecycle.closeSync(tt.fail(a))}catch(s){o.error("Failed to roll back Shell service construction",s)}throw a}}getInsightsRegistry(){return this.insightsRegistry}async initialize(e){this.services.logger.debug("Shell.initialize() called");let t=e?.mode;if(this.lifecycleState==="running")return this.services.logger.warn("Shell already initialized"),Promise.resolve();if(this.lifecycleState==="booting"){if(this.bootMode!==t)return Promise.reject(Error(`Shell is already initializing in ${this.bootMode??"normal"} mode; cannot join ${t??"normal"} mode`));return this.bootPromise??Promise.resolve()}if(this.lifecycleState==="closing"||this.lifecycleState==="closed")return Promise.reject(Error("Cannot initialize a closing shell"));if(this.lifecycleState==="failed")return Promise.reject(Error("Cannot initialize a shell after boot failure"));return this.lifecycleState="booting",this.bootMode=t,this.bootPromise=this.boot(e),this.bootPromise}async boot(e){try{if(await this.bootloader.boot(e),this.lifecycleState==="booting"){if(this.lifecycleState="running",this.initialized=!0,e?.mode===void 0)await this.services.messageBus.send({type:Os.shellReady,payload:{timestamp:new Date().toISOString()},sender:"shell",broadcast:!0}),this.services.logger.debug("Emitted shell ready event")}}catch(t){if(this.initialized=!1,this.services.logger.error("Failed to initialize Shell",t),this.lifecycleState!=="closing")this.lifecycleState="failed";try{await this.lifecycle.close(tt.fail(t))}catch(n){this.services.logger.error("Failed to clean up Shell after initialization failure",n)}throw t}}registerTemplates(e,t){this.services.logger.debug("Registering templates",{pluginId:t,count:Object.keys(e).length});for(let[n,o]of Object.entries(e))this.registerTemplate(n,o,t)}shutdown(){return this.shutdownPromise??=this.shutdownShell(),this.shutdownPromise}async shutdownShell(){if(this.services.logger.debug("Shutting down Shell"),this.lifecycleState==="closed")return;let e=this.bootPromise;if(this.lifecycleState="closing",this.initialized=!1,e)try{await e}catch{}await this.lifecycle.close(),this.lifecycleState="closed",this.services.logger.debug("Shell shutdown complete")}async generateContent(e){return this.requireInitialized("Shell content generation"),_p(this.services,e)}async query(e,t){this.requireInitialized("Shell query");let n=await this.getAppInfo(),o={...t,appInfo:n,timestamp:new Date().toISOString()},{conversationHistory:i,...a}=o;return pC.parse(await this.generateContent({prompt:e,templateName:Zo.KNOWLEDGE_QUERY,...i&&{conversationHistory:i},data:a,interfacePermissionGrant:"public"}))}registerPlugin(e){this.requireInitialized("Plugin registration"),this.services.pluginManager.registerPlugin(e)}isInitialized(){return this.initialized}requireInitialized(e){if(!this.initialized)throw Error(`${e} attempted before initialization`)}getEntityService(){return this.services.entityService}getConversationService(){return this.services.conversationService}getEntityRegistry(){return this.services.entityRegistry}async generateObject(e,t,n){this.requireInitialized("Shell generateObject");let{object:o}=await this.services.aiService.generateObject("You are a helpful assistant. Respond with the requested structured data.",e,t,n);return{object:o}}async judge(e){return this.requireInitialized("Shell judge"),this.services.aiService.judge(e)}getAIService(){return this.services.aiService}generateImage(e,t){return this.services.aiService.generateImage(e,t)}canGenerateImages(){return this.services.aiService.canGenerateImages()}getPluginManager(){return this.services.pluginManager}getContentService(){return this.services.contentService}getRenderService(){return this.services.renderService}getAttachmentRegistry(){return Js(this.services.attachmentRegistry)}getRuntimeUploadRegistry(){return hC(this.services.runtimeUploadRegistry)}getRuntimeState(){return this.services.runtimeStateService}getRecurringChecks(e){return this.services.recurringCheckService.namespace(e)}isJobQueueWorkerRunning(){return this.services.jobQueueWorker.isWorkerRunning()}getMessageBus(){return this.services.messageBus}getLogger(){return this.services.logger}getJobQueueService(){return this.services.jobQueueService}getMCPService(){return this.services.mcpService}listToolsForPermissionLevel(e){return this.services.mcpService.listToolsForPermissionLevel(e).map(({pluginId:t,tool:n})=>({name:n.name,description:n.description,pluginId:t}))}getPermissionService(){return this.services.permissionService}getAgentService(){return this.services.agentService}registerTools(e,t){for(let n of t)try{this.services.mcpService.registerTool(e,n)}catch(o){this.services.logger.error(`Failed to register tool ${n.name} from ${e}:`,o)}}registerResources(e,t){for(let n of t)try{this.services.mcpService.registerResource(e,n)}catch(o){this.services.logger.error(`Failed to register resource ${n.name} from ${e}:`,o)}}registerResourceTemplate(e,t){this.services.mcpService.registerResourceTemplate(e,t)}registerPrompt(e,t){this.services.mcpService.registerPrompt(e,t)}registerInstructions(e,t){if(this.services.mcpService.registerInstructions(e,t),this.initialized)this.services.agentService.invalidateAgent()}async unregisterPluginCapabilities(e){this.services.mcpService.unregisterPlugin?.(e),this.services.jobQueueService.unregisterPluginHandlers(e),this.endpointRegistry.unregister(e),this.interactionRegistry.unregister(e);for(let n of this.services.templateRegistry.getNames())if(n.startsWith(`${e}:`))this.services.templateRegistry.unregister(n);let t=this.config.evalHandlerRegistry;if(t){for(let n of t.list())if(n.pluginId===e)t.unregister(e,n.handlerId)}this.services.agentService.invalidateAgent(),await this.services.recurringCheckService.unregisterPlugin(e)}getPluginPackageName(e){return this.services.pluginManager.getPluginPackageName(e)}hasPlugin(e){return this.services.pluginManager.hasPlugin(e)}getPluginHttpRouteManifest(){return this.httpRouteRegistry.getManifest()}getPluginApiRoutes(){return this.httpRouteRegistry.getApiRoutes()}getPluginWebRoutes(){return this.httpRouteRegistry.getWebRoutes()}registerDaemon(e,t,n){this.services.daemonRegistry.register(e,t,n)}registerEndpoint(e){this.endpointRegistry.register(e)}listEndpoints(){return this.endpointRegistry.list()}registerInteraction(e){this.interactionRegistry.register(e)}listInteractions(){return this.interactionRegistry.list()}registerTemplate(e,t,n){let o=n?`${n}:${e}`:`shell:${e}`;this.services.templateRegistry.register(o,t),this.services.logger.debug(`Registered template: ${o}`)}getTemplate(e){return this.services.templateRegistry.get(e)}getDataSourceRegistry(){return this.services.dataSourceRegistry}getIdentity(){return this.services.identityService.getCharacter()}getProfile(){return this.services.profileService.getProfile()}getProfileKindRegistry(){return this.services.profileKindRegistry}getChannelRegistry(){return this.services.channelRegistry}getInboxRegistry(){return this.services.inboxRegistry}getInboxFollowUpRegistry(){return this.services.inboxFollowUpRegistry}getOperationalHealthRegistry(){return this.services.operationalHealthRegistry}getAccountSettingsRegistry(){return this.services.accountSettingsRegistry}getDomain(){return this.config.siteBaseUrl}getLocalSiteUrl(){return this.config.localSiteUrl}shouldPreferLocalUrls(){return this.config.preferLocalUrls}getThemeCSS(){return this.config.themeCSS}getSpaces(){return this.config.spaces}getDataDir(){return this.config.dataDir}getGitBrokerSocket(){return this.config.gitBrokerSocket}getGitBrokerCheckout(){return this.config.gitBrokerCheckout}registerEvalHandler(e,t,n){this.config.evalHandlerRegistry?.register(e,t,n)}async getAppInfo(){return dp({config:this.config,services:this.services,bootTime:this.bootTime,endpoints:()=>this.listEndpoints(),interactions:()=>this.listInteractions()})}getRuntimeReadiness(){return gp(this.services)}}var yC={kind:"service",serviceId:"brain-cli"},rp=`${yC.kind}:${yC.serviceId}`;var FG=r.object({id:r.string(),version:r.string(),type:r.enum(["core","entity","service","interface"]),description:r.string().optional(),dependencies:r.array(r.string()).optional(),packageName:r.string()}),BG=r.object({name:r.string(),role:r.string(),purpose:r.string(),values:r.array(r.string())}),GG=r.enum(["debug","info","warn","error"]),JG=r.enum(["none","low","medium","high","xhigh","max"]),WG=r.object({provider:r.enum(["hetzner","docker"]).default("hetzner"),serverSize:r.string().default("cx33"),location:r.string().default("fsn1"),domain:r.string().optional(),docker:r.object({enabled:r.boolean().default(!0),image:r.string().optional()}).prefault({}),ports:r.object({default:r.number().default(3333),preview:r.number().default(4321),production:r.number().default(8080)}).prefault({}),cdn:r.object({enabled:r.boolean().default(!1),provider:r.enum(["bunny","none"]).default("none")}).prefault({}),dns:r.object({enabled:r.boolean().default(!1),provider:r.enum(["bunny","none"]).default("none")}).prefault({}),paths:r.object({install:r.string().optional(),data:r.string().optional()}).prefault({})}),vC=r.object({name:r.string().default("brain-app"),version:r.string().default("1.0.0"),database:r.string().optional(),aiApiKey:r.string().optional(),aiImageKey:r.string().optional(),aiModel:r.string().optional(),aiReasoningEffort:JG.optional(),logLevel:GG.optional(),logFile:r.string().optional(),profileKind:r.string().trim().min(1).optional(),plugins:r.array(FG).default([]),spaces:r.array(r.string()).default([]),identity:BG.optional(),agentInstructions:r.array(r.string()).optional(),deployment:WG.prefault({})});async function RC(e,t){await wn({label:"entity",config:e,schema:{assets:bC,entities:_C,projectionDirtyInputs:SC,projectionWaves:wC,projectionIncidents:xC,projectionWaveInputs:TC,projectionWaveRules:IC,projectionRuleMemos:CC},migrationsFolder:xn(import.meta.url,"entity-service"),authTokenEnv:"DATABASE_AUTH_TOKEN",logger:t,afterMigrate:kC})}async function PC(e,t){await wn({label:"job-queue",config:e,schema:{jobQueue:EC,jobWorkerSessions:AC},migrationsFolder:xn(import.meta.url,"job-queue"),authTokenEnv:"JOB_QUEUE_DATABASE_AUTH_TOKEN",logger:t})}async function NC(e,t){await wn({label:"conversation",config:e,schema:{conversations:MC,messages:jC,summaryTracking:OC},migrationsFolder:xn(import.meta.url,"conversation-service"),authTokenEnv:"CONVERSATION_DATABASE_AUTH_TOKEN",logger:t})}async function DC(e,t){await wn({label:"runtime-state",config:e,schema:{runtimeStateRecords:Le},migrationsFolder:xn(import.meta.url,"runtime-state"),authTokenEnv:"RUNTIME_STATE_DATABASE_AUTH_TOKEN",logger:t})}import{mkdir as np}from"fs/promises";import{isAbsolute as HG}from"path";function zC(e=process.env){return{dataDir:e.XDG_DATA_HOME??"./data",cacheDir:e.XDG_CACHE_HOME??"./cache",distDir:"./dist"}}function qC(e=process.env){let t=e.BRAIN_GIT_BROKER_CHECKOUT;if(!t)return;if(!HG(t))throw Error(`Git broker checkout path must be absolute: ${t}`);return t}function $C(e=process.env){let t=e.BRAIN_GIT_BROKER_SOCKET;if(!t)return;let n=107;if(Buffer.byteLength(t)>n)throw Error(`Git broker socket path is too long for a unix socket (${Buffer.byteLength(t)} > ${n} bytes): ${t}`);return t}function UC(e=process.env){return Cn(zC(e))}async function LC(e=process.env){let t=zC(e);try{await np(t.dataDir,{recursive:!0}),await np(t.cacheDir,{recursive:!0}),await np(t.distDir,{recursive:!0})}catch(n){let o=n instanceof Error&&n.message.includes("EACCES")?`Cannot create data directories \u2014 permission denied. Run from a writable directory or check permissions on ${t.dataDir}`:`Cannot create data directories: ${Ne(n)}`;throw Error(o,{cause:n})}return Cn(t)}class op{logger;migrations;constructor(e,t){this.logger=e,this.migrations=t??{getStandardConfigWithDirectories:()=>LC(),migrateEntities:RC,migrateJobQueue:PC,migrateConversations:NC,migrateRuntimeState:DC}}async runAllMigrations(e){this.logger.debug("Running database migrations...");let t=await this.migrations.getStandardConfigWithDirectories();if(e?.database)t.database.url=e.database;if(e?.jobQueueDatabase)t.jobQueueDatabase.url=e.jobQueueDatabase;if(e?.conversationDatabase)t.conversationDatabase.url=e.conversationDatabase;if(e?.runtimeStateDatabase)t.runtimeStateDatabase.url=e.runtimeStateDatabase;await this.migrateEntityDatabase(t),await this.migrateJobQueueDatabase(t),await this.migrateConversationDatabase(t),await this.migrateRuntimeStateDatabase(t),this.logger.debug("All database migrations completed successfully")}async migrateEntityDatabase(e){this.logger.debug("Running entity database migrations..."),await this.migrations.migrateEntities({url:e.database.url,...e.database.authToken&&{authToken:e.database.authToken}},this.logger)}async migrateJobQueueDatabase(e){this.logger.debug("Running job queue database migrations..."),await this.migrations.migrateJobQueue({url:e.jobQueueDatabase.url,...e.jobQueueDatabase.authToken&&{authToken:e.jobQueueDatabase.authToken}},this.logger)}async migrateConversationDatabase(e){this.logger.debug("Running conversation database migrations..."),await this.migrations.migrateConversations({url:e.conversationDatabase.url,...e.conversationDatabase.authToken&&{authToken:e.conversationDatabase.authToken}},this.logger)}async migrateRuntimeStateDatabase(e){this.logger.debug("Running runtime state database migrations..."),await this.migrations.migrateRuntimeState({url:e.runtimeStateDatabase.url,...e.runtimeStateDatabase.authToken&&{authToken:e.runtimeStateDatabase.authToken}},this.logger)}}var VG="NODE_ENV";function ZG(){return process.env[VG]}function FC(){return ZG()!=="production"}var KG="startup-check";function QG(e){let n={...vC.parse(e??{}),plugins:e?.plugins??[]};if(e?.permissions)n.permissions=e.permissions;if(e?.spaces)n.spaces=e.spaces;if(e?.cliConfig)n.cliConfig=e.cliConfig;if(e?.shellConfig)n.shellConfig=e.shellConfig;return n}function YG(e,t){let n={plugins:e.plugins??[],...e.shellConfig};return XG(n),e4(e,n),t4(e,n,t),r4(e,n),n4(e,n),o4(e,n),i4(e,n),a4(e,n),s4(e,n),n}function XG(e){let t=UC();e.database??=t.database,e.jobQueueDatabase??=t.jobQueueDatabase,e.conversationDatabase??=t.conversationDatabase,e.runtimeStateDatabase??=t.runtimeStateDatabase,e.embeddingDatabase??=t.embeddingDatabase,e.embedding??=t.embedding,e.gitBrokerSocket??=$C(),e.gitBrokerCheckout??=qC()}function e4(e,t){if(e.database)t.database={url:e.database};if(e.profileKind)t.profileKind=e.profileKind;t.features={}}function t4(e,t,n){let o=n?.mode==="startup-check";if(!e.aiApiKey&&!e.aiImageKey&&!e.aiModel&&!e.aiReasoningEffort&&!o)return;t.ai={...t.ai,...o&&!t.ai?.apiKey&&{apiKey:KG},...e.aiApiKey&&{apiKey:e.aiApiKey},...e.aiImageKey&&{imageApiKey:e.aiImageKey},...e.aiModel&&{model:e.aiModel},...e.aiReasoningEffort&&{reasoningEffort:e.aiReasoningEffort}}}function r4(e,t){if(!e.logLevel&&!e.logFile)return;t.logging={level:e.logLevel??"info",format:"text",context:e.name,...e.logFile&&{file:e.logFile}}}function n4(e,t){let n=e.permissions?.rules??[];t.permissions={...e.permissions,rules:n.some((o)=>o.pattern===rp)?n:[{pattern:rp,level:"admin"},...n]}}function o4(e,t){if(e.spaces)t.spaces=e.spaces}function i4(e,t){if(e.identity)t.identity=e.identity}function a4(e,t){if(e.agentInstructions)t.agentInstructions=e.agentInstructions}function s4(e,t){if(t.name=e.name,t.version=e.version,e.deployment.domain)t.siteBaseUrl=e.deployment.domain;t.localSiteUrl=`http://localhost:${e.deployment.ports.production}`,t.preferLocalUrls=FC()}class ap{shell=null;config;signalScope=null;signalShutdownFiber=null;stopPromise=null;hasCLI=!1;static create(e,t){return new ap(QG(e),t)}constructor(e,t){if(this.config=e,this.hasCLI=process.argv.slice(2).includes("--cli"),t)this.shell=t}async migrate(){let e=vr.getInstance();await new op(e).runAllMigrations({database:this.config.shellConfig?.database?.url??this.config.database,jobQueueDatabase:this.config.shellConfig?.jobQueueDatabase?.url,conversationDatabase:this.config.shellConfig?.conversationDatabase?.url,runtimeStateDatabase:this.config.shellConfig?.runtimeStateDatabase?.url})}createShell(e,t){this.shell=ra.createFresh(YG(this.config,e),void 0,{...t&&{processRole:t}})}async registerCLIInterface(){if(!this.hasCLI)return;let e=this.getShell().getPluginManager(),{CLIInterface:t}=await import("./index-5b1wstvc.js"),n=new t(this.config.cliConfig);e.registerPlugin(n)}async initialize(e,t){if(!this.shell){if(!t?.migrationsCompleted)await this.migrate();this.createShell(e,t?.processRole)}await this.registerCLIInterface(),await this.getShell().initialize(e)}async start(){if(this.stopPromise)return;this.setupSignalHandlers()}stop(){return this.stopPromise??=this.stopApp(),this.stopPromise}async stopApp(){await this.closeSignalScope(),await this.shell?.shutdown()}async run(e){let n={debug:zt.DEBUG,info:zt.INFO,warn:zt.WARN,error:zt.ERROR}[this.config.logLevel??"info"]??zt.INFO,o=vr.createFresh({level:n,context:this.config.name,useStderr:this.hasCLI});if(this.hasCLI)vr.getInstance().setUseStderr(!0);try{await this.initialize(void 0,e),await this.start(),e?.onRuntimeReady?.(),o.info(`\u2705 ${this.config.name} v${this.config.version} ready`),process.stdin.resume()}catch(i){o.error(`\u274C Failed to start ${this.config.name}:`,i),process.exit(1)}}static async run(e,t,n){await ap.create(e,t).run(n)}setupSignalHandlers(){if(this.signalScope)return;let e=de.runSync(Xe.make()),t=()=>{this.requestGracefulShutdown("SIGINT")},n=()=>{this.requestGracefulShutdown("SIGTERM")},o=ip(["SIGINT"],t),i=ip(["SIGTERM"],n);de.runSync(Xe.addFinalizer(e,de.sync(()=>{i(),o()}))),this.signalScope=e}requestGracefulShutdown(e){if(this.signalShutdownFiber)return;let t=vr.getInstance();t.info(`
|
|
183
183
|
Received ${e}, shutting down gracefully...`);let n=de.tryPromise({try:()=>this.stop(),catch:(o)=>o}).pipe(de.match({onFailure:(o)=>{t.error("Error during shutdown:",o),process.exit(1)},onSuccess:()=>{process.exit(0)}}));this.signalShutdownFiber=de.runFork(n)}async closeSignalScope(){let e=this.signalScope;if(this.signalScope=null,e)await de.runPromise(Xe.close(e,tt.void))}getShell(){if(!this.shell)throw Error("Shell not initialized. Call initialize() first.");return this.shell}}
|
|
184
184
|
export{$j as S,V5 as T,ce as U,ta as V,AI as W,jI as X,Ls as Y,bG as Z,Kd as _,SG as $,Fs as aa,wG as ba,lee as ca,LI as da,IG as ea,Xd as fa,ep as ga,CG as ha,RG as ia,JG as ja,KG as ka,QG as la,YG as ma,ap as na};
|
|
185
185
|
|
|
186
186
|
//# debugId=3E61A044D4887BDF64756E2164756E21
|
|
187
|
-
//# sourceMappingURL=index-
|
|
187
|
+
//# sourceMappingURL=index-bttt0hwq.js.map
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
import{M as d,N as t}from"./chunks/index-
|
|
2
|
+
import{M as d,N as t}from"./chunks/index-0asz7jsc.js";import"./chunks/index-bttt0hwq.js";import"./chunks/index-xtafk5s4.js";import"./chunks/index-zncqaqm4.js";import{de as a}from"./chunks/index-7wp6jksm.js";import"./chunks/index-dbtf9myk.js";import"./chunks/index-n75bdj3d.js";import"./chunks/index-6zc7b41v.js";import"./chunks/index-e5pxnqxk.js";import"./chunks/index-74dsq2mw.js";import"./chunks/index-5v1n5093.js";function s(e,i){let o=new Set,n=new Set;for(let r of e){if(!t(r))throw Error(`${i} contains a value that was not returned by use()`);if(o.has(r.definition))throw Error(`${i} contains duplicate plugin definition "${r.definition.id}"; include each configured definition only once`);if(n.has(r.definition.id))throw Error(`${i} contains duplicate local plugin id "${r.definition.id}"; give each package definition a unique local id before composing it`);o.add(r.definition),n.add(r.definition.id)}}function l(e){a(e.id,"Bundle id"),s(e.members,`Bundle "${e.id}"`);for(let i of e.config??[])if(!t(i.member))throw Error(`Bundle "${e.id}" config contains a value that was not returned by use()`);for(let i of e.permissions??[])if(!t(i.member))throw Error(`Bundle "${e.id}" permissions contains a value that was not returned by use()`);for(let i of e.evalDisable??[])if(!t(i))throw Error(`Bundle "${e.id}" evalDisable contains a value that was not returned by use()`);return Object.freeze({...e,members:Object.freeze([...e.members]),...e.config?{config:Object.freeze([...e.config])}:{},...e.permissions?{permissions:Object.freeze([...e.permissions])}:{},...e.agentInstructions?{agentInstructions:Object.freeze([...e.agentInstructions])}:{},...e.evalDisable?{evalDisable:Object.freeze([...e.evalDisable])}:{}})}function u(e){if(!e.name.trim())throw Error("Brain name must not be empty");if(e.bundleContract?.trim().length===0)throw Error("Brain bundleContract must not be empty");s(e.plugins,`Brain "${e.name}"`);let i=new Set(e.plugins),o=new Set;for(let n of e.bundles??[]){if(o.has(n.id))throw Error(`Brain "${e.name}" has duplicate bundle "${n.id}"`);o.add(n.id);for(let r of n.members)if(!i.has(r))throw Error(`Brain "${e.name}" bundle "${n.id}" references a plugin outside its catalog`);for(let r of n.config??[])if(!i.has(r.member))throw Error(`Brain "${e.name}" bundle "${n.id}" config references a plugin outside its catalog`);for(let r of n.permissions??[])if(!i.has(r.member))throw Error(`Brain "${e.name}" bundle "${n.id}" permissions reference a plugin outside its catalog`);for(let r of n.evalDisable??[])if(!i.has(r))throw Error(`Brain "${e.name}" bundle "${n.id}" evalDisable references a plugin outside its catalog`)}for(let n of e.evalDisable??[])if(!i.has(n))throw Error(`Brain "${e.name}" evalDisable references a plugin outside its catalog`);return Object.freeze({...e,plugins:Object.freeze([...e.plugins]),...e.bundles?{bundles:Object.freeze([...e.bundles])}:{},...e.agentInstructions?{agentInstructions:Object.freeze([...e.agentInstructions])}:{},...e.evalDisable?{evalDisable:Object.freeze([...e.evalDisable])}:{}})}export{u as defineBrain,l as defineBundle,d as use};
|
|
3
3
|
|
|
4
4
|
//# debugId=214BB75CD903F84864756E2164756E21
|
|
5
5
|
//# sourceMappingURL=index.js.map
|