@agimon-ai/doompi-config 0.0.1-alpha.64 → 0.0.1-alpha.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. package/dist/adapters/harnessState.cjs +1 -1
  2. package/dist/adapters/harnessState.cjs.map +1 -1
  3. package/dist/adapters/harnessState.d.cts.map +1 -1
  4. package/dist/adapters/harnessState.d.mts.map +1 -1
  5. package/dist/adapters/harnessState.mjs +1 -1
  6. package/dist/adapters/harnessState.mjs.map +1 -1
  7. package/dist/adapters/personaIcon.cjs +2 -0
  8. package/dist/adapters/personaIcon.cjs.map +1 -0
  9. package/dist/adapters/personaIcon.d.cts +9 -0
  10. package/dist/adapters/personaIcon.d.cts.map +1 -0
  11. package/dist/adapters/personaIcon.d.mts +9 -0
  12. package/dist/adapters/personaIcon.d.mts.map +1 -0
  13. package/dist/adapters/personaIcon.mjs +2 -0
  14. package/dist/adapters/personaIcon.mjs.map +1 -0
  15. package/dist/adapters/profiles.cjs +2 -2
  16. package/dist/adapters/profiles.cjs.map +1 -1
  17. package/dist/adapters/profiles.d.cts +24 -0
  18. package/dist/adapters/profiles.d.cts.map +1 -1
  19. package/dist/adapters/profiles.d.mts +24 -0
  20. package/dist/adapters/profiles.d.mts.map +1 -1
  21. package/dist/adapters/profiles.mjs +2 -2
  22. package/dist/adapters/profiles.mjs.map +1 -1
  23. package/dist/adapters/selectionSwitch.cjs +1 -1
  24. package/dist/adapters/selectionSwitch.cjs.map +1 -1
  25. package/dist/adapters/selectionSwitch.d.cts +1 -1
  26. package/dist/adapters/selectionSwitch.d.mts +1 -1
  27. package/dist/adapters/selectionSwitch.mjs +1 -1
  28. package/dist/adapters/selectionSwitch.mjs.map +1 -1
  29. package/dist/index.d.cts +1 -1
  30. package/dist/index.d.mts +1 -1
  31. package/dist/profiles.cjs +1 -1
  32. package/dist/profiles.d.cts +4 -2
  33. package/dist/profiles.d.mts +4 -2
  34. package/dist/profiles.mjs +1 -1
  35. package/dist/services/configPolicy.cjs +1 -1
  36. package/dist/services/configPolicy.cjs.map +1 -1
  37. package/dist/services/configPolicy.d.cts +15 -1
  38. package/dist/services/configPolicy.d.cts.map +1 -1
  39. package/dist/services/configPolicy.d.mts +15 -1
  40. package/dist/services/configPolicy.d.mts.map +1 -1
  41. package/dist/services/configPolicy.mjs +1 -1
  42. package/dist/services/configPolicy.mjs.map +1 -1
  43. package/dist/services/personaFrontMatter.cjs +5 -0
  44. package/dist/services/personaFrontMatter.cjs.map +1 -0
  45. package/dist/services/personaFrontMatter.d.cts +44 -0
  46. package/dist/services/personaFrontMatter.d.cts.map +1 -0
  47. package/dist/services/personaFrontMatter.d.mts +44 -0
  48. package/dist/services/personaFrontMatter.d.mts.map +1 -0
  49. package/dist/services/personaFrontMatter.mjs +5 -0
  50. package/dist/services/personaFrontMatter.mjs.map +1 -0
  51. package/dist/types/config.d.cts +10 -0
  52. package/dist/types/config.d.cts.map +1 -1
  53. package/dist/types/config.d.mts +10 -0
  54. package/dist/types/config.d.mts.map +1 -1
  55. package/package.json +3 -3
@@ -1,2 +1,2 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);const n=new Set([`profileEnvironment`,`pluginHooks`,`mcpProjection`,`packageAttribution`]),r={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,compositionFingerprint:`DOOMPI_COMPOSITION_FINGERPRINT`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function i(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e?.split(t.default.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function o(e){return e!==`0`}function s(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function u(e=process.env,t){let n=r,u=e[n.hookGroups];return{root:e[n.root]||void 0,majorMode:e[n.majorMode]||`copilot`,temporaryDirectory:e[n.temporaryDirectory]||void 0,domains:i(e[n.domains]),layers:i(e[n.layers]),compositionFingerprint:e[n.compositionFingerprint]||void 0,profile:e[n.profile]||void 0,profileEnvironment:s(e[n.profileEnvironment],n.profileEnvironment,t),hookGroups:u===void 0?void 0:i(u),skillDirectories:a(e[n.skillDirectories]),agentDirectories:a(e[n.agentDirectories]),additionalDirectories:a(e[n.additionalDirectories]),childExtensions:c(e[n.childExtensions],n.childExtensions,t),pluginDirectories:a(e[n.pluginDirectories]),pluginHooks:l(e[n.pluginHooks],n.pluginHooks,t),mcpConfigPath:e[n.mcpConfigPath]||void 0,personaFile:e[n.personaFile]||void 0,hooks:o(e[n.hooks]),agents:o(e[n.agents]),mcp:o(e[n.mcp]),allowProtectedWrites:o(e[n.allowProtectedWrites])}}function d(e,n=process.env){let i=(e,t)=>{t===void 0?delete n[e]:n[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=r;return`root`in e&&i(o.root,e.root),`majorMode`in e&&i(o.majorMode,e.majorMode),`temporaryDirectory`in e&&i(o.temporaryDirectory,e.temporaryDirectory),`domains`in e&&i(o.domains,e.domains?.join(`,`)),`layers`in e&&i(o.layers,e.layers?.join(`,`)),`compositionFingerprint`in e&&i(o.compositionFingerprint,e.compositionFingerprint),`profile`in e&&i(o.profile,e.profile),`hookGroups`in e&&i(o.hookGroups,e.hookGroups?.join(`,`)),`skillDirectories`in e&&i(o.skillDirectories,e.skillDirectories?.join(t.default.delimiter)),`agentDirectories`in e&&i(o.agentDirectories,e.agentDirectories?.join(t.default.delimiter)),`additionalDirectories`in e&&i(o.additionalDirectories,e.additionalDirectories?.join(t.default.delimiter)),`childExtensions`in e&&i(o.childExtensions,JSON.stringify(e.childExtensions??[])),`pluginDirectories`in e&&i(o.pluginDirectories,e.pluginDirectories?.join(t.default.delimiter)),`mcpConfigPath`in e&&i(o.mcpConfigPath,e.mcpConfigPath),`personaFile`in e&&i(o.personaFile,e.personaFile),e.hooks!==void 0&&a(o.hooks,e.hooks),e.agents!==void 0&&a(o.agents,e.agents),e.mcp!==void 0&&a(o.mcp,e.mcp),e.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,e.allowProtectedWrites),n}exports.FILE_ONLY_STATE_FIELDS=n,exports.HARNESS_STATE_KEYS=r,exports.projectHarnessEnvironment=d,exports.readHarnessState=u;
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:path");t=e.__toESM(t,1);const n=new Set([`profileEnvironment`,`pluginHooks`,`mcpProjection`,`packageAttribution`,`profileIdentity`,`profileVoice`]),r={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,compositionFingerprint:`DOOMPI_COMPOSITION_FINGERPRINT`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function i(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e?.split(t.default.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function o(e){return e!==`0`}function s(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function u(e=process.env,t){let n=r,u=e[n.hookGroups];return{root:e[n.root]||void 0,majorMode:e[n.majorMode]||`copilot`,temporaryDirectory:e[n.temporaryDirectory]||void 0,domains:i(e[n.domains]),layers:i(e[n.layers]),compositionFingerprint:e[n.compositionFingerprint]||void 0,profile:e[n.profile]||void 0,profileEnvironment:s(e[n.profileEnvironment],n.profileEnvironment,t),hookGroups:u===void 0?void 0:i(u),skillDirectories:a(e[n.skillDirectories]),agentDirectories:a(e[n.agentDirectories]),additionalDirectories:a(e[n.additionalDirectories]),childExtensions:c(e[n.childExtensions],n.childExtensions,t),pluginDirectories:a(e[n.pluginDirectories]),pluginHooks:l(e[n.pluginHooks],n.pluginHooks,t),mcpConfigPath:e[n.mcpConfigPath]||void 0,personaFile:e[n.personaFile]||void 0,hooks:o(e[n.hooks]),agents:o(e[n.agents]),mcp:o(e[n.mcp]),allowProtectedWrites:o(e[n.allowProtectedWrites])}}function d(e,n=process.env){let i=(e,t)=>{t===void 0?delete n[e]:n[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=r;return`root`in e&&i(o.root,e.root),`majorMode`in e&&i(o.majorMode,e.majorMode),`temporaryDirectory`in e&&i(o.temporaryDirectory,e.temporaryDirectory),`domains`in e&&i(o.domains,e.domains?.join(`,`)),`layers`in e&&i(o.layers,e.layers?.join(`,`)),`compositionFingerprint`in e&&i(o.compositionFingerprint,e.compositionFingerprint),`profile`in e&&i(o.profile,e.profile),`hookGroups`in e&&i(o.hookGroups,e.hookGroups?.join(`,`)),`skillDirectories`in e&&i(o.skillDirectories,e.skillDirectories?.join(t.default.delimiter)),`agentDirectories`in e&&i(o.agentDirectories,e.agentDirectories?.join(t.default.delimiter)),`additionalDirectories`in e&&i(o.additionalDirectories,e.additionalDirectories?.join(t.default.delimiter)),`childExtensions`in e&&i(o.childExtensions,JSON.stringify(e.childExtensions??[])),`pluginDirectories`in e&&i(o.pluginDirectories,e.pluginDirectories?.join(t.default.delimiter)),`mcpConfigPath`in e&&i(o.mcpConfigPath,e.mcpConfigPath),`personaFile`in e&&i(o.personaFile,e.personaFile),e.hooks!==void 0&&a(o.hooks,e.hooks),e.agents!==void 0&&a(o.agents,e.agents),e.mcp!==void 0&&a(o.mcp,e.mcp),e.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,e.allowProtectedWrites),n}exports.FILE_ONLY_STATE_FIELDS=n,exports.HARNESS_STATE_KEYS=r,exports.projectHarnessEnvironment=d,exports.readHarnessState=u;
2
2
  //# sourceMappingURL=harnessState.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"harnessState.cjs","names":["path"],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * These are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment. The plugin hook list and profile defaults\n * are read from the store, while Config publishes the MCP projection through\n * the session-scoped Cordis registry.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set([\n 'profileEnvironment',\n 'pluginHooks',\n 'mcpProjection',\n // One entry per composed package. Nothing that spawns a process needs it,\n // and putting a map that size in front of every exec would cost far more\n // than the one surface that reads it is worth.\n 'packageAttribution',\n]);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n compositionFingerprint: 'DOOMPI_COMPOSITION_FINGERPRINT',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n compositionFingerprint: environment[keys.compositionFingerprint] || undefined,\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('compositionFingerprint' in patch) assign(keys.compositionFingerprint, patch.compositionFingerprint);\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"harnessState.cjs","names":["path"],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * These are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment. The plugin hook list and profile defaults\n * are read from the store, while Config publishes the MCP projection through\n * the session-scoped Cordis registry.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set([\n 'profileEnvironment',\n 'pluginHooks',\n 'mcpProjection',\n // One entry per composed package. Nothing that spawns a process needs it,\n // and putting a map that size in front of every exec would cost far more\n // than the one surface that reads it is worth.\n 'packageAttribution',\n // The persona's presentation. The icon is a base64 data URL, which has no\n // business in front of every exec, and nothing that spawns a process reads it.\n 'profileIdentity',\n 'profileVoice',\n]);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n compositionFingerprint: 'DOOMPI_COMPOSITION_FINGERPRINT',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n compositionFingerprint: environment[keys.compositionFingerprint] || undefined,\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('compositionFingerprint' in patch) assign(keys.compositionFingerprint, patch.compositionFingerprint);\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"harnessState.d.cts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;qBAwBa,wBAAwB,kBAAkB;YAU3C,6BAA6B,aAAa;qBAEzC;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;wBA4Ec,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;wBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
1
+ {"version":3,"file":"harnessState.d.cts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;qBAwBa,wBAAwB,kBAAkB;YAc3C,6BAA6B,aAAa;qBAEzC;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;wBA4Ec,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;wBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -1 +1 @@
1
- {"version":3,"file":"harnessState.d.mts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;qBAwBa,wBAAwB,kBAAkB;YAU3C,6BAA6B,aAAa;qBAEzC;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;wBA4Ec,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;wBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
1
+ {"version":3,"file":"harnessState.d.mts","names":[],"sources":["../../src/adapters/harnessState.ts"],"mappings":";;;;;;;;;;qBAwBa,wBAAwB,kBAAkB;YAc3C,6BAA6B,aAAa;qBAEzC;WACX;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;wBA4Ec,iBACd,cAAa,OAAO,YACpB,SAAS,4BACR;;;;;;;;;;;wBAsCa,0BACd,OAAO,QAAQ,eACf,cAAa,OAAO,aACnB,OAAO"}
@@ -1,2 +1,2 @@
1
- import e from"node:path";const t=new Set([`profileEnvironment`,`pluginHooks`,`mcpProjection`,`packageAttribution`]),n={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,compositionFingerprint:`DOOMPI_COMPOSITION_FINGERPRINT`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function r(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function i(t){return t?.split(e.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e!==`0`}function o(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function s(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e=process.env,t){let l=n,u=e[l.hookGroups];return{root:e[l.root]||void 0,majorMode:e[l.majorMode]||`copilot`,temporaryDirectory:e[l.temporaryDirectory]||void 0,domains:r(e[l.domains]),layers:r(e[l.layers]),compositionFingerprint:e[l.compositionFingerprint]||void 0,profile:e[l.profile]||void 0,profileEnvironment:o(e[l.profileEnvironment],l.profileEnvironment,t),hookGroups:u===void 0?void 0:r(u),skillDirectories:i(e[l.skillDirectories]),agentDirectories:i(e[l.agentDirectories]),additionalDirectories:i(e[l.additionalDirectories]),childExtensions:s(e[l.childExtensions],l.childExtensions,t),pluginDirectories:i(e[l.pluginDirectories]),pluginHooks:c(e[l.pluginHooks],l.pluginHooks,t),mcpConfigPath:e[l.mcpConfigPath]||void 0,personaFile:e[l.personaFile]||void 0,hooks:a(e[l.hooks]),agents:a(e[l.agents]),mcp:a(e[l.mcp]),allowProtectedWrites:a(e[l.allowProtectedWrites])}}function u(t,r=process.env){let i=(e,t)=>{t===void 0?delete r[e]:r[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=n;return`root`in t&&i(o.root,t.root),`majorMode`in t&&i(o.majorMode,t.majorMode),`temporaryDirectory`in t&&i(o.temporaryDirectory,t.temporaryDirectory),`domains`in t&&i(o.domains,t.domains?.join(`,`)),`layers`in t&&i(o.layers,t.layers?.join(`,`)),`compositionFingerprint`in t&&i(o.compositionFingerprint,t.compositionFingerprint),`profile`in t&&i(o.profile,t.profile),`hookGroups`in t&&i(o.hookGroups,t.hookGroups?.join(`,`)),`skillDirectories`in t&&i(o.skillDirectories,t.skillDirectories?.join(e.delimiter)),`agentDirectories`in t&&i(o.agentDirectories,t.agentDirectories?.join(e.delimiter)),`additionalDirectories`in t&&i(o.additionalDirectories,t.additionalDirectories?.join(e.delimiter)),`childExtensions`in t&&i(o.childExtensions,JSON.stringify(t.childExtensions??[])),`pluginDirectories`in t&&i(o.pluginDirectories,t.pluginDirectories?.join(e.delimiter)),`mcpConfigPath`in t&&i(o.mcpConfigPath,t.mcpConfigPath),`personaFile`in t&&i(o.personaFile,t.personaFile),t.hooks!==void 0&&a(o.hooks,t.hooks),t.agents!==void 0&&a(o.agents,t.agents),t.mcp!==void 0&&a(o.mcp,t.mcp),t.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,t.allowProtectedWrites),r}export{t as FILE_ONLY_STATE_FIELDS,n as HARNESS_STATE_KEYS,u as projectHarnessEnvironment,l as readHarnessState};
1
+ import e from"node:path";const t=new Set([`profileEnvironment`,`pluginHooks`,`mcpProjection`,`packageAttribution`,`profileIdentity`,`profileVoice`]),n={root:`DOOMPI_ROOT`,majorMode:`DOOMPI_MAJOR_MODE`,temporaryDirectory:`DOOMPI_TEMP_DIR`,domains:`DOOMPI_DOMAINS`,layers:`DOOMPI_LAYERS`,compositionFingerprint:`DOOMPI_COMPOSITION_FINGERPRINT`,profile:`DOOMPI_PROFILE`,profileEnvironment:`DOOMPI_PROFILE_ENV`,hookGroups:`DOOMPI_HOOK_GROUPS`,skillDirectories:`DOOMPI_SKILL_DIRS`,agentDirectories:`DOOMPI_AGENT_DIRS`,additionalDirectories:`DOOMPI_ADDITIONAL_DIRS`,childExtensions:`DOOMPI_CHILD_EXTENSIONS`,pluginDirectories:`DOOMPI_PLUGIN_DIRS`,pluginHooks:`DOOMPI_PLUGIN_HOOKS`,mcpConfigPath:`DOOMPI_MCP_CONFIG`,personaFile:`DOOMPI_PERSONA_FILE`,hooks:`DOOMPI_HOOKS_ENABLED`,agents:`DOOMPI_AGENTS_ENABLED`,mcp:`DOOMPI_MCP_ENABLED`,allowProtectedWrites:`DOOMPI_ALLOW_PROTECTED_WRITES`};function r(e){return e?.split(`,`).map(e=>e.trim()).filter(Boolean)??[]}function i(t){return t?.split(e.delimiter).map(e=>e.trim()).filter(Boolean)??[]}function a(e){return e!==`0`}function o(e,t,n){if(!e)return{};try{let t=JSON.parse(e);return typeof t!=`object`||!t||Array.isArray(t)?{}:Object.fromEntries(Object.entries(t).filter(e=>typeof e[1]==`string`))}catch(e){return n?.(t,e),{}}}function s(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`string`):[]}catch(e){return n?.(t,e),[]}}function c(e,t,n){if(!e)return[];try{let t=JSON.parse(e);return Array.isArray(t)?t.filter(e=>typeof e==`object`&&!!e&&typeof e.pluginRoot==`string`&&typeof e.configPath==`string`):[]}catch(e){return n?.(t,e),[]}}function l(e=process.env,t){let l=n,u=e[l.hookGroups];return{root:e[l.root]||void 0,majorMode:e[l.majorMode]||`copilot`,temporaryDirectory:e[l.temporaryDirectory]||void 0,domains:r(e[l.domains]),layers:r(e[l.layers]),compositionFingerprint:e[l.compositionFingerprint]||void 0,profile:e[l.profile]||void 0,profileEnvironment:o(e[l.profileEnvironment],l.profileEnvironment,t),hookGroups:u===void 0?void 0:r(u),skillDirectories:i(e[l.skillDirectories]),agentDirectories:i(e[l.agentDirectories]),additionalDirectories:i(e[l.additionalDirectories]),childExtensions:s(e[l.childExtensions],l.childExtensions,t),pluginDirectories:i(e[l.pluginDirectories]),pluginHooks:c(e[l.pluginHooks],l.pluginHooks,t),mcpConfigPath:e[l.mcpConfigPath]||void 0,personaFile:e[l.personaFile]||void 0,hooks:a(e[l.hooks]),agents:a(e[l.agents]),mcp:a(e[l.mcp]),allowProtectedWrites:a(e[l.allowProtectedWrites])}}function u(t,r=process.env){let i=(e,t)=>{t===void 0?delete r[e]:r[e]=t},a=(e,t)=>i(e,t?`1`:`0`),o=n;return`root`in t&&i(o.root,t.root),`majorMode`in t&&i(o.majorMode,t.majorMode),`temporaryDirectory`in t&&i(o.temporaryDirectory,t.temporaryDirectory),`domains`in t&&i(o.domains,t.domains?.join(`,`)),`layers`in t&&i(o.layers,t.layers?.join(`,`)),`compositionFingerprint`in t&&i(o.compositionFingerprint,t.compositionFingerprint),`profile`in t&&i(o.profile,t.profile),`hookGroups`in t&&i(o.hookGroups,t.hookGroups?.join(`,`)),`skillDirectories`in t&&i(o.skillDirectories,t.skillDirectories?.join(e.delimiter)),`agentDirectories`in t&&i(o.agentDirectories,t.agentDirectories?.join(e.delimiter)),`additionalDirectories`in t&&i(o.additionalDirectories,t.additionalDirectories?.join(e.delimiter)),`childExtensions`in t&&i(o.childExtensions,JSON.stringify(t.childExtensions??[])),`pluginDirectories`in t&&i(o.pluginDirectories,t.pluginDirectories?.join(e.delimiter)),`mcpConfigPath`in t&&i(o.mcpConfigPath,t.mcpConfigPath),`personaFile`in t&&i(o.personaFile,t.personaFile),t.hooks!==void 0&&a(o.hooks,t.hooks),t.agents!==void 0&&a(o.agents,t.agents),t.mcp!==void 0&&a(o.mcp,t.mcp),t.allowProtectedWrites!==void 0&&a(o.allowProtectedWrites,t.allowProtectedWrites),r}export{t as FILE_ONLY_STATE_FIELDS,n as HARNESS_STATE_KEYS,u as projectHarnessEnvironment,l as readHarnessState};
2
2
  //# sourceMappingURL=harnessState.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"harnessState.mjs","names":[],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * These are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment. The plugin hook list and profile defaults\n * are read from the store, while Config publishes the MCP projection through\n * the session-scoped Cordis registry.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set([\n 'profileEnvironment',\n 'pluginHooks',\n 'mcpProjection',\n // One entry per composed package. Nothing that spawns a process needs it,\n // and putting a map that size in front of every exec would cost far more\n // than the one surface that reads it is worth.\n 'packageAttribution',\n]);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n compositionFingerprint: 'DOOMPI_COMPOSITION_FINGERPRINT',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n compositionFingerprint: environment[keys.compositionFingerprint] || undefined,\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('compositionFingerprint' in patch) assign(keys.compositionFingerprint, patch.compositionFingerprint);\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"harnessState.mjs","names":[],"sources":["../../src/adapters/harnessState.ts"],"sourcesContent":["import path from 'node:path';\nimport type { HarnessState, PluginHookSource } from '../types/config.ts';\n\n/**\n * The codec between harness state and the environment.\n *\n * Decoding exists for processes that were started without a state file: a\n * nested run, a third-party spawn, or anything older than the store. Encoding\n * exists because bash hooks, the shell launchers, and packages that do not\n * depend on this one can only read an environment. Ownership, persistence and\n * mutation belong to harnessStore.ts; nothing here touches disk or holds state.\n */\n\nconst DEFAULT_MAJOR_MODE = 'copilot';\nconst DISABLED_FLAG = '0';\n\n/**\n * State that lives in the file only.\n *\n * These are JSON documents that every spawned process and every hook would\n * otherwise copy in its environment. The plugin hook list and profile defaults\n * are read from the store, while Config publishes the MCP projection through\n * the session-scoped Cordis registry.\n */\nexport const FILE_ONLY_STATE_FIELDS: ReadonlySet<keyof HarnessState> = new Set([\n 'profileEnvironment',\n 'pluginHooks',\n 'mcpProjection',\n // One entry per composed package. Nothing that spawns a process needs it,\n // and putting a map that size in front of every exec would cost far more\n // than the one surface that reads it is worth.\n 'packageAttribution',\n // The persona's presentation. The icon is a base64 data URL, which has no\n // business in front of every exec, and nothing that spawns a process reads it.\n 'profileIdentity',\n 'profileVoice',\n]);\n\nexport type HarnessStateParseReporter = (key: string, error: unknown) => void;\n\nexport const HARNESS_STATE_KEYS = {\n root: 'DOOMPI_ROOT',\n majorMode: 'DOOMPI_MAJOR_MODE',\n temporaryDirectory: 'DOOMPI_TEMP_DIR',\n domains: 'DOOMPI_DOMAINS',\n layers: 'DOOMPI_LAYERS',\n compositionFingerprint: 'DOOMPI_COMPOSITION_FINGERPRINT',\n profile: 'DOOMPI_PROFILE',\n profileEnvironment: 'DOOMPI_PROFILE_ENV',\n hookGroups: 'DOOMPI_HOOK_GROUPS',\n skillDirectories: 'DOOMPI_SKILL_DIRS',\n agentDirectories: 'DOOMPI_AGENT_DIRS',\n additionalDirectories: 'DOOMPI_ADDITIONAL_DIRS',\n childExtensions: 'DOOMPI_CHILD_EXTENSIONS',\n pluginDirectories: 'DOOMPI_PLUGIN_DIRS',\n pluginHooks: 'DOOMPI_PLUGIN_HOOKS',\n mcpConfigPath: 'DOOMPI_MCP_CONFIG',\n personaFile: 'DOOMPI_PERSONA_FILE',\n hooks: 'DOOMPI_HOOKS_ENABLED',\n agents: 'DOOMPI_AGENTS_ENABLED',\n mcp: 'DOOMPI_MCP_ENABLED',\n allowProtectedWrites: 'DOOMPI_ALLOW_PROTECTED_WRITES',\n} as const;\n\nfunction splitCsv(value: string | undefined): string[] {\n return (\n value\n ?.split(',')\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction splitPaths(value: string | undefined): string[] {\n return (\n value\n ?.split(path.delimiter)\n .map((item) => item.trim())\n .filter(Boolean) ?? []\n );\n}\n\nfunction parseFlag(value: string | undefined): boolean {\n return value !== DISABLED_FLAG;\n}\n\nfunction parseStringRecord(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): Record<string, string> {\n if (!value) return {};\n try {\n const parsed = JSON.parse(value) as unknown;\n if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) return {};\n return Object.fromEntries(\n Object.entries(parsed).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n );\n } catch (error) {\n report?.(key, error);\n return {};\n }\n}\n\nfunction parseStringList(value: string | undefined, key: string, report?: HarnessStateParseReporter): string[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n return Array.isArray(parsed) ? parsed.filter((item): item is string => typeof item === 'string') : [];\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nfunction parsePluginHooks(\n value: string | undefined,\n key: string,\n report?: HarnessStateParseReporter,\n): PluginHookSource[] {\n if (!value) return [];\n try {\n const parsed = JSON.parse(value) as unknown;\n if (!Array.isArray(parsed)) return [];\n return parsed.filter(\n (item): item is PluginHookSource =>\n typeof item === 'object' &&\n item !== null &&\n typeof (item as PluginHookSource).pluginRoot === 'string' &&\n typeof (item as PluginHookSource).configPath === 'string',\n );\n } catch (error) {\n report?.(key, error);\n return [];\n }\n}\n\nexport function readHarnessState(\n environment: NodeJS.ProcessEnv = process.env,\n report?: HarnessStateParseReporter,\n): HarnessState {\n const keys = HARNESS_STATE_KEYS;\n const hookGroups = environment[keys.hookGroups];\n return {\n root: environment[keys.root] || undefined,\n majorMode: environment[keys.majorMode] || DEFAULT_MAJOR_MODE,\n temporaryDirectory: environment[keys.temporaryDirectory] || undefined,\n domains: splitCsv(environment[keys.domains]),\n layers: splitCsv(environment[keys.layers]),\n compositionFingerprint: environment[keys.compositionFingerprint] || undefined,\n profile: environment[keys.profile] || undefined,\n profileEnvironment: parseStringRecord(environment[keys.profileEnvironment], keys.profileEnvironment, report),\n hookGroups: hookGroups === undefined ? undefined : splitCsv(hookGroups),\n skillDirectories: splitPaths(environment[keys.skillDirectories]),\n agentDirectories: splitPaths(environment[keys.agentDirectories]),\n additionalDirectories: splitPaths(environment[keys.additionalDirectories]),\n childExtensions: parseStringList(environment[keys.childExtensions], keys.childExtensions, report),\n pluginDirectories: splitPaths(environment[keys.pluginDirectories]),\n pluginHooks: parsePluginHooks(environment[keys.pluginHooks], keys.pluginHooks, report),\n mcpConfigPath: environment[keys.mcpConfigPath] || undefined,\n personaFile: environment[keys.personaFile] || undefined,\n hooks: parseFlag(environment[keys.hooks]),\n agents: parseFlag(environment[keys.agents]),\n mcp: parseFlag(environment[keys.mcp]),\n allowProtectedWrites: parseFlag(environment[keys.allowProtectedWrites]),\n };\n}\n\n/**\n * Publishes a state patch into an environment.\n *\n * Takes the environment rather than assuming `process.env`, because the\n * launcher builds a child environment and Doom Team builds a spawn snapshot;\n * neither should mutate its own process to describe another one.\n *\n * Fields in FILE_ONLY_FIELDS are skipped: the file carries them, and a reader\n * that needs them reads the store.\n */\nexport function projectHarnessEnvironment(\n patch: Partial<HarnessState>,\n environment: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const assign = (key: string, value: string | undefined): void => {\n if (value === undefined) delete environment[key];\n else environment[key] = value;\n };\n const assignFlag = (key: string, value: boolean): void => assign(key, value ? '1' : DISABLED_FLAG);\n const keys = HARNESS_STATE_KEYS;\n\n if ('root' in patch) assign(keys.root, patch.root);\n if ('majorMode' in patch) assign(keys.majorMode, patch.majorMode);\n if ('temporaryDirectory' in patch) assign(keys.temporaryDirectory, patch.temporaryDirectory);\n if ('domains' in patch) assign(keys.domains, patch.domains?.join(','));\n if ('layers' in patch) assign(keys.layers, patch.layers?.join(','));\n if ('compositionFingerprint' in patch) assign(keys.compositionFingerprint, patch.compositionFingerprint);\n if ('profile' in patch) assign(keys.profile, patch.profile);\n if ('hookGroups' in patch) assign(keys.hookGroups, patch.hookGroups?.join(','));\n if ('skillDirectories' in patch) assign(keys.skillDirectories, patch.skillDirectories?.join(path.delimiter));\n if ('agentDirectories' in patch) assign(keys.agentDirectories, patch.agentDirectories?.join(path.delimiter));\n if ('additionalDirectories' in patch) {\n assign(keys.additionalDirectories, patch.additionalDirectories?.join(path.delimiter));\n }\n if ('childExtensions' in patch) assign(keys.childExtensions, JSON.stringify(patch.childExtensions ?? []));\n if ('pluginDirectories' in patch) assign(keys.pluginDirectories, patch.pluginDirectories?.join(path.delimiter));\n if ('mcpConfigPath' in patch) assign(keys.mcpConfigPath, patch.mcpConfigPath);\n if ('personaFile' in patch) assign(keys.personaFile, patch.personaFile);\n if (patch.hooks !== undefined) assignFlag(keys.hooks, patch.hooks);\n if (patch.agents !== undefined) assignFlag(keys.agents, patch.agents);\n if (patch.mcp !== undefined) assignFlag(keys.mcp, patch.mcp);\n if (patch.allowProtectedWrites !== undefined) assignFlag(keys.allowProtectedWrites, patch.allowProtectedWrites);\n return environment;\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ const e=require("../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);let r=require("@agimon-ai/doompi-extension-contracts/profile-identity");const i=[{mime:`image/png`,test:e=>e.length>8&&e.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))},{mime:`image/jpeg`,test:e=>e.length>3&&e[0]===255&&e[1]===216&&e[2]===255},{mime:`image/gif`,test:e=>e.length>6&&e.subarray(0,6).toString(`latin1`).startsWith(`GIF8`)},{mime:`image/webp`,test:e=>e.length>12&&e.subarray(0,4).toString(`latin1`)===`RIFF`&&e.subarray(8,12).toString(`latin1`)===`WEBP`}];function a(e){return i.find(t=>t.test(e))?.mime}function o(e,i){if(!n.default.isAbsolute(i))try{let o=t.default.realpathSync(e),s=n.default.resolve(o,i);if(!t.default.existsSync(s))return;let c=t.default.realpathSync(s),l=n.default.relative(o,c);if(l===`..`||l.startsWith(`..${n.default.sep}`)||n.default.isAbsolute(l)||!t.default.statSync(c).isFile())return;let u=t.default.readFileSync(c),d=a(u);if(!d)return;let f=`data:${d};base64,${u.toString(`base64`)}`;return f.length>r.PROFILE_ICON_MAX_DATA_URL_LENGTH?void 0:f}catch{return}}exports.readPersonaIcon=o;
2
+ //# sourceMappingURL=personaIcon.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personaIcon.cjs","names":["path","fs","PROFILE_ICON_MAX_DATA_URL_LENGTH"],"sources":["../../src/adapters/personaIcon.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n PROFILE_ICON_MAX_DATA_URL_LENGTH,\n type ProfileIconMimeType,\n} from '@agimon-ai/doompi-extension-contracts/profile-identity';\n\n/**\n * Reads a persona icon into a bounded `data:` URL.\n *\n * The icon travels inside the journalled identity entry rather than through an\n * HTTP route. A route would leave a request-time filesystem resolution reachable\n * from the browser for the life of a session, defended on every request forever;\n * this resolves the path once, server side, at the moment the profile loads.\n *\n * The path comes from user-authored front-matter, so it is confined the same way\n * `buildPersonaPrompt` confines persona files: a symlink must not read outside\n * the persona directory.\n *\n * Every failure is silent and returns undefined. An avatar is decoration, and a\n * broken one must never stop a persona from loading.\n */\nconst PARENT_DIRECTORY = '..';\n\n/**\n * Magic-byte signatures, checked instead of trusting the filename.\n *\n * `image/svg+xml` is absent on purpose: an SVG rendered same-origin executes\n * script, and the author of this path is whoever wrote the persona. Do not add\n * it to match the cockpit's broader media catalog.\n */\nconst SIGNATURES: readonly { readonly mime: ProfileIconMimeType; readonly test: (bytes: Buffer) => boolean }[] = [\n {\n mime: 'image/png',\n test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])),\n },\n { mime: 'image/jpeg', test: (b) => b.length > 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff },\n { mime: 'image/gif', test: (b) => b.length > 6 && b.subarray(0, 6).toString('latin1').startsWith('GIF8') },\n {\n mime: 'image/webp',\n test: (b) =>\n b.length > 12 &&\n b.subarray(0, 4).toString('latin1') === 'RIFF' &&\n b.subarray(8, 12).toString('latin1') === 'WEBP',\n },\n];\n\nfunction sniff(bytes: Buffer): ProfileIconMimeType | undefined {\n return SIGNATURES.find((signature) => signature.test(bytes))?.mime;\n}\n\n/**\n * Resolves an icon declared in persona front-matter, or undefined when it is\n * missing, unreadable, not an allowed image, escapes the persona directory, or\n * exceeds the data-URL ceiling.\n */\nexport function readPersonaIcon(personaPath: string, iconRelative: string): string | undefined {\n if (path.isAbsolute(iconRelative)) return undefined;\n\n try {\n const realPersonaPath = fs.realpathSync(personaPath);\n const iconPath = path.resolve(realPersonaPath, iconRelative);\n if (!fs.existsSync(iconPath)) return undefined;\n\n const realIconPath = fs.realpathSync(iconPath);\n const relative = path.relative(realPersonaPath, realIconPath);\n if (\n relative === PARENT_DIRECTORY ||\n relative.startsWith(`${PARENT_DIRECTORY}${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n return undefined;\n }\n if (!fs.statSync(realIconPath).isFile()) return undefined;\n\n const bytes = fs.readFileSync(realIconPath);\n const mime = sniff(bytes);\n if (!mime) return undefined;\n\n const dataUrl = `data:${mime};base64,${bytes.toString('base64')}`;\n return dataUrl.length > PROFILE_ICON_MAX_DATA_URL_LENGTH ? undefined : dataUrl;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"mNAsBA,MASM,EAA2G,CAC/G,CACE,KAAM,YACN,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,OAAO,OAAO,KAAK,CAAC,IAAK,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,EAAE,CAAC,CAAC,CACrG,EACA,CAAE,KAAM,aAAc,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,KAAO,KAAQ,EAAE,KAAO,KAAQ,EAAE,KAAO,GAAK,EACnG,CAAE,KAAM,YAAa,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,WAAW,MAAM,CAAE,EACzG,CACE,KAAM,aACN,KAAO,GACL,EAAE,OAAS,IACX,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,IAAM,QACxC,EAAE,SAAS,EAAG,EAAE,CAAC,CAAC,SAAS,QAAQ,IAAM,MAC7C,CACF,EAEA,SAAS,EAAM,EAAgD,CAC7D,OAAO,EAAW,KAAM,GAAc,EAAU,KAAK,CAAK,CAAC,CAAC,EAAE,IAChE,CAOA,SAAgB,EAAgB,EAAqB,EAA0C,CACzFA,MAAAA,QAAK,WAAW,CAAY,EAEhC,GAAI,CACF,IAAM,EAAkBC,EAAAA,QAAG,aAAa,CAAW,EAC7C,EAAWD,EAAAA,QAAK,QAAQ,EAAiB,CAAY,EAC3D,GAAI,CAACC,EAAAA,QAAG,WAAW,CAAQ,EAAG,OAE9B,IAAM,EAAeA,EAAAA,QAAG,aAAa,CAAQ,EACvC,EAAWD,EAAAA,QAAK,SAAS,EAAiB,CAAY,EAQ5D,GANE,IAAa,MACb,EAAS,WAAW,KAAsBA,EAAAA,QAAK,KAAK,GACpDA,EAAAA,QAAK,WAAW,CAAQ,GAItB,CAACC,EAAAA,QAAG,SAAS,CAAY,CAAC,CAAC,OAAO,EAAG,OAEzC,IAAM,EAAQA,EAAAA,QAAG,aAAa,CAAY,EACpC,EAAO,EAAM,CAAK,EACxB,GAAI,CAAC,EAAM,OAEX,IAAM,EAAU,QAAQ,EAAK,UAAU,EAAM,SAAS,QAAQ,IAC9D,OAAO,EAAQ,OAASC,EAAAA,iCAAmC,IAAA,GAAY,CACzE,MAAQ,CACN,MACF,CACF"}
@@ -0,0 +1,9 @@
1
+ //#region src/adapters/personaIcon.d.ts
2
+ /**
3
+ * Resolves an icon declared in persona front-matter, or undefined when it is
4
+ * missing, unreadable, not an allowed image, escapes the persona directory, or
5
+ * exceeds the data-URL ceiling.
6
+ */
7
+ export declare function readPersonaIcon(personaPath: string, iconRelative: string): string | undefined;
8
+ //#endregion
9
+ //# sourceMappingURL=personaIcon.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personaIcon.d.cts","names":[],"sources":["../../src/adapters/personaIcon.ts"],"mappings":";;;;;;wBAwDgB,gBAAgB,qBAAqB"}
@@ -0,0 +1,9 @@
1
+ //#region src/adapters/personaIcon.d.ts
2
+ /**
3
+ * Resolves an icon declared in persona front-matter, or undefined when it is
4
+ * missing, unreadable, not an allowed image, escapes the persona directory, or
5
+ * exceeds the data-URL ceiling.
6
+ */
7
+ export declare function readPersonaIcon(personaPath: string, iconRelative: string): string | undefined;
8
+ //#endregion
9
+ //# sourceMappingURL=personaIcon.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personaIcon.d.mts","names":[],"sources":["../../src/adapters/personaIcon.ts"],"mappings":";;;;;;wBAwDgB,gBAAgB,qBAAqB"}
@@ -0,0 +1,2 @@
1
+ import e from"node:fs";import t from"node:path";import{PROFILE_ICON_MAX_DATA_URL_LENGTH as n}from"@agimon-ai/doompi-extension-contracts/profile-identity";const r=[{mime:`image/png`,test:e=>e.length>8&&e.subarray(0,8).equals(Buffer.from([137,80,78,71,13,10,26,10]))},{mime:`image/jpeg`,test:e=>e.length>3&&e[0]===255&&e[1]===216&&e[2]===255},{mime:`image/gif`,test:e=>e.length>6&&e.subarray(0,6).toString(`latin1`).startsWith(`GIF8`)},{mime:`image/webp`,test:e=>e.length>12&&e.subarray(0,4).toString(`latin1`)===`RIFF`&&e.subarray(8,12).toString(`latin1`)===`WEBP`}];function i(e){return r.find(t=>t.test(e))?.mime}function a(r,a){if(!t.isAbsolute(a))try{let o=e.realpathSync(r),s=t.resolve(o,a);if(!e.existsSync(s))return;let c=e.realpathSync(s),l=t.relative(o,c);if(l===`..`||l.startsWith(`..${t.sep}`)||t.isAbsolute(l)||!e.statSync(c).isFile())return;let u=e.readFileSync(c),d=i(u);if(!d)return;let f=`data:${d};base64,${u.toString(`base64`)}`;return f.length>n?void 0:f}catch{return}}export{a as readPersonaIcon};
2
+ //# sourceMappingURL=personaIcon.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"personaIcon.mjs","names":[],"sources":["../../src/adapters/personaIcon.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\nimport {\n PROFILE_ICON_MAX_DATA_URL_LENGTH,\n type ProfileIconMimeType,\n} from '@agimon-ai/doompi-extension-contracts/profile-identity';\n\n/**\n * Reads a persona icon into a bounded `data:` URL.\n *\n * The icon travels inside the journalled identity entry rather than through an\n * HTTP route. A route would leave a request-time filesystem resolution reachable\n * from the browser for the life of a session, defended on every request forever;\n * this resolves the path once, server side, at the moment the profile loads.\n *\n * The path comes from user-authored front-matter, so it is confined the same way\n * `buildPersonaPrompt` confines persona files: a symlink must not read outside\n * the persona directory.\n *\n * Every failure is silent and returns undefined. An avatar is decoration, and a\n * broken one must never stop a persona from loading.\n */\nconst PARENT_DIRECTORY = '..';\n\n/**\n * Magic-byte signatures, checked instead of trusting the filename.\n *\n * `image/svg+xml` is absent on purpose: an SVG rendered same-origin executes\n * script, and the author of this path is whoever wrote the persona. Do not add\n * it to match the cockpit's broader media catalog.\n */\nconst SIGNATURES: readonly { readonly mime: ProfileIconMimeType; readonly test: (bytes: Buffer) => boolean }[] = [\n {\n mime: 'image/png',\n test: (b) => b.length > 8 && b.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10])),\n },\n { mime: 'image/jpeg', test: (b) => b.length > 3 && b[0] === 0xff && b[1] === 0xd8 && b[2] === 0xff },\n { mime: 'image/gif', test: (b) => b.length > 6 && b.subarray(0, 6).toString('latin1').startsWith('GIF8') },\n {\n mime: 'image/webp',\n test: (b) =>\n b.length > 12 &&\n b.subarray(0, 4).toString('latin1') === 'RIFF' &&\n b.subarray(8, 12).toString('latin1') === 'WEBP',\n },\n];\n\nfunction sniff(bytes: Buffer): ProfileIconMimeType | undefined {\n return SIGNATURES.find((signature) => signature.test(bytes))?.mime;\n}\n\n/**\n * Resolves an icon declared in persona front-matter, or undefined when it is\n * missing, unreadable, not an allowed image, escapes the persona directory, or\n * exceeds the data-URL ceiling.\n */\nexport function readPersonaIcon(personaPath: string, iconRelative: string): string | undefined {\n if (path.isAbsolute(iconRelative)) return undefined;\n\n try {\n const realPersonaPath = fs.realpathSync(personaPath);\n const iconPath = path.resolve(realPersonaPath, iconRelative);\n if (!fs.existsSync(iconPath)) return undefined;\n\n const realIconPath = fs.realpathSync(iconPath);\n const relative = path.relative(realPersonaPath, realIconPath);\n if (\n relative === PARENT_DIRECTORY ||\n relative.startsWith(`${PARENT_DIRECTORY}${path.sep}`) ||\n path.isAbsolute(relative)\n ) {\n return undefined;\n }\n if (!fs.statSync(realIconPath).isFile()) return undefined;\n\n const bytes = fs.readFileSync(realIconPath);\n const mime = sniff(bytes);\n if (!mime) return undefined;\n\n const dataUrl = `data:${mime};base64,${bytes.toString('base64')}`;\n return dataUrl.length > PROFILE_ICON_MAX_DATA_URL_LENGTH ? undefined : dataUrl;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"0JAsBA,MASM,EAA2G,CAC/G,CACE,KAAM,YACN,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,OAAO,OAAO,KAAK,CAAC,IAAK,GAAI,GAAI,GAAI,GAAI,GAAI,GAAI,EAAE,CAAC,CAAC,CACrG,EACA,CAAE,KAAM,aAAc,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,KAAO,KAAQ,EAAE,KAAO,KAAQ,EAAE,KAAO,GAAK,EACnG,CAAE,KAAM,YAAa,KAAO,GAAM,EAAE,OAAS,GAAK,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,CAAC,CAAC,WAAW,MAAM,CAAE,EACzG,CACE,KAAM,aACN,KAAO,GACL,EAAE,OAAS,IACX,EAAE,SAAS,EAAG,CAAC,CAAC,CAAC,SAAS,QAAQ,IAAM,QACxC,EAAE,SAAS,EAAG,EAAE,CAAC,CAAC,SAAS,QAAQ,IAAM,MAC7C,CACF,EAEA,SAAS,EAAM,EAAgD,CAC7D,OAAO,EAAW,KAAM,GAAc,EAAU,KAAK,CAAK,CAAC,CAAC,EAAE,IAChE,CAOA,SAAgB,EAAgB,EAAqB,EAA0C,CACzF,MAAK,WAAW,CAAY,EAEhC,GAAI,CACF,IAAM,EAAkB,EAAG,aAAa,CAAW,EAC7C,EAAW,EAAK,QAAQ,EAAiB,CAAY,EAC3D,GAAI,CAAC,EAAG,WAAW,CAAQ,EAAG,OAE9B,IAAM,EAAe,EAAG,aAAa,CAAQ,EACvC,EAAW,EAAK,SAAS,EAAiB,CAAY,EAQ5D,GANE,IAAa,MACb,EAAS,WAAW,KAAsB,EAAK,KAAK,GACpD,EAAK,WAAW,CAAQ,GAItB,CAAC,EAAG,SAAS,CAAY,CAAC,CAAC,OAAO,EAAG,OAEzC,IAAM,EAAQ,EAAG,aAAa,CAAY,EACpC,EAAO,EAAM,CAAK,EACxB,GAAI,CAAC,EAAM,OAEX,IAAM,EAAU,QAAQ,EAAK,UAAU,EAAM,SAAS,QAAQ,IAC9D,OAAO,EAAQ,OAAS,EAAmC,IAAA,GAAY,CACzE,MAAQ,CACN,MACF,CACF"}
@@ -1,4 +1,4 @@
1
- const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./layeredConfig.cjs");let n=require("node:fs");n=e.__toESM(n,1);let r=require("node:path");r=e.__toESM(r,1);let i=require("node:os");i=e.__toESM(i,1);const a=[`profile.md`,`SOUL.md`,`AGENTS.md`],o=new Set([`persona`,`env`]),s=new Set([`roots`,`entries`]),c=`profiles`;function l(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function u(e,t){let n=r.default.relative(e,t);return n===``||!r.default.isAbsolute(n)&&n!==`..`&&!n.startsWith(`..${r.default.sep}`)}function d(e,t){if(t===void 0)return{};if(!l(t))throw Error(`Profile "${e}" env must be a string mapping`);let n={};for(let[r,i]of Object.entries(t)){if(typeof i!=`string`)throw Error(`Profile "${e}" env.${r} must be a string`);n[r]=i}return n}function f(e,t,n){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`profiles.roots in ${t} must be an array`);return e.map((e,i)=>{if(typeof e!=`string`||e.trim().length===0)throw Error(`profiles.roots[${i}] in ${t} must be a non-empty string`);return r.default.resolve(n,e.trim())})}function p(e){if(!l(e.document)||!l(e.document[c]))throw Error(`${e.filePath} must contain a profiles mapping`);let t=Object.keys(e.document).filter(e=>e!==c);if(t.length>0)throw Error(`${e.filePath} may only contain profiles; unsupported: ${t.join(`, `)}`);let n=e.document[c];if(!(Object.hasOwn(n,`roots`)||Object.hasOwn(n,`entries`)))return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:[],entries:n};let r=Object.keys(n).filter(e=>!s.has(e));if(r.length>0)throw Error(`profiles in ${e.filePath} may only contain roots and entries; unsupported: ${r.join(`, `)}`);if(n.entries!==void 0&&!l(n.entries))throw Error(`profiles.entries in ${e.filePath} must be a mapping`);return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:f(n.roots,e.filePath,e.baseDirectory),entries:n.entries??{}}}function m(e,t,r){let i=n.default.realpathSync(t);if(!r.some(e=>u(n.default.realpathSync(e),i)))throw Error(`Profile "${e}" persona must stay inside agents/ or a configured profile root`)}function h(e,t,i,a){if(!l(a))throw Error(`Profile "${i}" must be a mapping`);let s=Object.keys(a).filter(e=>!o.has(e));if(s.length>0)throw Error(`Profile "${i}" may only set persona and env; unsupported: ${s.join(`, `)}`);if(typeof a.persona!=`string`||a.persona.trim().length===0)throw Error(`Profile "${i}" must set a persona`);if(r.default.isAbsolute(a.persona))throw Error(`Profile "${i}" persona must be relative to the config that declares it`);let c=a.persona,f=r.default.resolve(e,c),p=[r.default.resolve(e,`agents`),...t].filter(e=>u(e,f));if(p.length===0)throw Error(`Profile "${i}" persona must stay inside agents/ or a configured profile root`);if(!n.default.existsSync(f)||!n.default.statSync(f).isDirectory())throw Error(`Profile "${i}" references a missing persona: ${c}`);if(m(i,f,p),!S(e,c))throw Error(`Profile "${i}" persona has no readable persona files: ${c}`);return{name:i,persona:c,personaRoot:e,env:d(i,a.env)}}function g(e){return a.some(t=>{try{return n.default.lstatSync(r.default.join(e,t)).isFile()}catch{return!1}})}function _(e,t,i){if(g(e))try{if(!u(n.default.realpathSync(t),n.default.realpathSync(e)))return;let a=r.default.relative(i,e)||`.`;return S(i,a)?{name:r.default.basename(e),persona:a,personaRoot:i,env:{}}:void 0}catch{return}}function v(e,t){if(!n.default.existsSync(e)||!n.default.statSync(e).isDirectory())throw Error(`Configured profile root is not a directory: ${e}`);let i=_(e,e,t);return i?[i]:n.default.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).sort((e,t)=>e.name.localeCompare(t.name)).flatMap(n=>{let i=_(r.default.join(e,n.name),e,t);return i?[i]:[]})}function y(e,n=i.default.homedir()){let r=t.readDoomConfigSources(`profiles.yaml`,e,n).map(p),a=new Map;for(let e of r)for(let t of e.roots)for(let n of v(t,e.baseDirectory))a.set(n.name,n);for(let e of r)for(let[t,n]of Object.entries(e.entries))a.set(t,h(e.baseDirectory,e.roots,t,n));return[...a.values()].sort((e,t)=>e.name.localeCompare(t.name))}function b(e,t,n){let r=y(e,n).find(e=>e.name===t);if(!r)throw Error(`Unknown profile: ${t}`);return r}function x(e,t){return y(e,t).map(e=>e.name)}function S(e,t){let i=[],o=n.default.realpathSync(r.default.join(e,t));for(let e of a){let a=r.default.join(o,e);if(!n.default.existsSync(a))continue;let s=n.default.realpathSync(a),c=r.default.relative(o,s);if(c===`..`||c.startsWith(`..${r.default.sep}`))throw Error(`Persona file must stay inside ${t}: ${e}`);let l=n.default.readFileSync(s,`utf8`).trim();l&&i.push(l)}if(i.length!==0)return[`[PERSONA] You are operating as the person described below (source: ${t}).`,...i].join(`
1
+ const e=require("../_virtual/_rolldown/runtime.cjs"),t=require("./layeredConfig.cjs"),n=require("../services/personaFrontMatter.cjs"),r=require("./personaIcon.cjs");let i=require("node:fs");i=e.__toESM(i,1);let a=require("node:path");a=e.__toESM(a,1);let o=require("node:os");o=e.__toESM(o,1);const s=[`profile.md`,`SOUL.md`,`AGENTS.md`],c=`profile.md`,l=new Set([`persona`,`env`]),u=new Set([`roots`,`entries`,`defaultProfile`]),d=`profiles.yaml`,f=`profiles`,p=`utf8`;function m(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function h(e,t){let n=a.default.relative(e,t);return n===``||!a.default.isAbsolute(n)&&n!==`..`&&!n.startsWith(`..${a.default.sep}`)}function g(e,t){if(t===void 0)return{};if(!m(t))throw Error(`Profile "${e}" env must be a string mapping`);let n={};for(let[r,i]of Object.entries(t)){if(typeof i!=`string`)throw Error(`Profile "${e}" env.${r} must be a string`);n[r]=i}return n}function _(e,t,n){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`profiles.roots in ${t} must be an array`);return e.map((e,r)=>{if(typeof e!=`string`||e.trim().length===0)throw Error(`profiles.roots[${r}] in ${t} must be a non-empty string`);return a.default.resolve(n,e.trim())})}function v(e){if(!m(e.document)||!m(e.document[f]))throw Error(`${e.filePath} must contain a profiles mapping`);let t=Object.keys(e.document).filter(e=>e!==f);if(t.length>0)throw Error(`${e.filePath} may only contain profiles; unsupported: ${t.join(`, `)}`);let n=e.document[f];if(!(Object.hasOwn(n,`roots`)||Object.hasOwn(n,`entries`)))return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:[],entries:n};let r=Object.keys(n).filter(e=>!u.has(e));if(r.length>0)throw Error(`profiles in ${e.filePath} may only contain roots, entries and defaultProfile; unsupported: ${r.join(`, `)}`);if(n.defaultProfile!==void 0&&(typeof n.defaultProfile!=`string`||n.defaultProfile.trim().length===0))throw Error(`profiles.defaultProfile in ${e.filePath} must be a non-empty string`);if(n.entries!==void 0&&!m(n.entries))throw Error(`profiles.entries in ${e.filePath} must be a mapping`);return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:_(n.roots,e.filePath,e.baseDirectory),entries:n.entries??{},...n.defaultProfile===void 0?{}:{defaultProfile:String(n.defaultProfile).trim()}}}function y(e,t,n){let r=i.default.realpathSync(t);if(!n.some(e=>h(i.default.realpathSync(e),r)))throw Error(`Profile "${e}" persona must stay inside agents/ or a configured profile root`)}function b(e,t,n,r){if(!m(r))throw Error(`Profile "${n}" must be a mapping`);let o=Object.keys(r).filter(e=>!l.has(e));if(o.length>0)throw Error(`Profile "${n}" may only set persona and env; unsupported: ${o.join(`, `)}`);if(typeof r.persona!=`string`||r.persona.trim().length===0)throw Error(`Profile "${n}" must set a persona`);if(a.default.isAbsolute(r.persona))throw Error(`Profile "${n}" persona must be relative to the config that declares it`);let s=r.persona,c=a.default.resolve(e,s),u=[a.default.resolve(e,`agents`),...t].filter(e=>h(e,c));if(u.length===0)throw Error(`Profile "${n}" persona must stay inside agents/ or a configured profile root`);if(!i.default.existsSync(c)||!i.default.statSync(c).isDirectory())throw Error(`Profile "${n}" references a missing persona: ${s}`);if(y(n,c,u),!A(e,s)){let t=k(e,s)!==void 0;throw Error(t?`Profile "${n}" persona declares identity front-matter but no persona text: ${s}`:`Profile "${n}" persona has no readable persona files: ${s}`)}return{name:n,persona:s,personaRoot:e,env:g(n,r.env),...O(e,s)}}function x(e){return s.some(t=>{try{return i.default.lstatSync(a.default.join(e,t)).isFile()}catch{return!1}})}function S(e,t,n){if(x(e))try{if(!h(i.default.realpathSync(t),i.default.realpathSync(e)))return;let r=a.default.relative(n,e)||`.`;return A(n,r)?{name:a.default.basename(e),persona:r,personaRoot:n,env:{},...O(n,r)}:void 0}catch{return}}function C(e,t){if(!i.default.existsSync(e)||!i.default.statSync(e).isDirectory())throw Error(`Configured profile root is not a directory: ${e}`);let n=S(e,e,t);return n?[n]:i.default.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).sort((e,t)=>e.name.localeCompare(t.name)).flatMap(n=>{let r=S(a.default.join(e,n.name),e,t);return r?[r]:[]})}function w(e,n=o.default.homedir()){let r=t.readDoomConfigSources(d,e,n).map(v),i=new Map;for(let e of r)for(let t of e.roots)for(let n of C(t,e.baseDirectory))i.set(n.name,n);for(let e of r)for(let[t,n]of Object.entries(e.entries))i.set(t,b(e.baseDirectory,e.roots,t,n));return[...i.values()].sort((e,t)=>e.name.localeCompare(t.name))}function T(e,n=o.default.homedir()){let r=t.readDoomConfigSources(d,e,n).map(v),i=w(e,n),a;for(let e of r)e.defaultProfile!==void 0&&(a=e.defaultProfile);if(a!==void 0&&!i.some(e=>e.name===a))throw Error(`profiles.defaultProfile names an unknown profile: ${a}`);return{profiles:i,...a===void 0?{}:{defaultProfile:a}}}function E(e,t,n){let r=w(e,n).find(e=>e.name===t);if(!r)throw Error(`Unknown profile: ${t}`);return r}function D(e,t){return w(e,t).map(e=>e.name)}function O(e,t){let n=k(e,t);if(!n)return{};let i=n.icon===void 0?void 0:r.readPersonaIcon(a.default.join(e,t),n.icon),o={...n.name===void 0?{}:{name:n.name},...i===void 0?{}:{icon:i}};return{...o.name===void 0&&o.icon===void 0?{}:{identity:o},...n.voice===void 0?{}:{voice:n.voice}}}function k(e,t){try{let r=i.default.realpathSync(a.default.join(e,t)),o=a.default.join(r,c);return i.default.existsSync(o)?n.parsePersonaFrontMatter(i.default.readFileSync(i.default.realpathSync(o),p)).frontMatter:void 0}catch{return}}function A(e,t){let r=[],o=i.default.realpathSync(a.default.join(e,t));for(let e of s){let s=a.default.join(o,e);if(!i.default.existsSync(s))continue;let l=i.default.realpathSync(s),u=a.default.relative(o,l);if(u===`..`||u.startsWith(`..${a.default.sep}`))throw Error(`Persona file must stay inside ${t}: ${e}`);let d=i.default.readFileSync(l,p),f=(e===c?n.parsePersonaFrontMatter(d).body:d).trim();f&&r.push(f)}if(r.length!==0)return[`[PERSONA] You are operating as the person described below (source: ${t}).`,...r].join(`
2
2
 
3
- `)}function C(e,t){let n={};for(let[r,i]of Object.entries(t))e[r]===void 0&&(e[r]=i,n[r]=i);return n}function w(e,t,n){for(let[n,r]of Object.entries(t))e[n]===r&&delete e[n];return C(e,n)}exports.PERSONA_FILES=a,exports.applyProfileEnvironment=C,exports.buildPersonaPrompt=S,exports.listProfileNames=x,exports.loadProfiles=y,exports.replaceProfileEnvironment=w,exports.resolveProfile=b;
3
+ `)}function j(e,t){let n={};for(let[r,i]of Object.entries(t))e[r]===void 0&&(e[r]=i,n[r]=i);return n}function M(e,t,n){for(let[n,r]of Object.entries(t))e[n]===r&&delete e[n];return j(e,n)}exports.PERSONA_FILES=s,exports.applyProfileEnvironment=j,exports.buildPersonaPrompt=A,exports.listProfileNames=D,exports.loadProfileCatalog=T,exports.loadProfiles=w,exports.replaceProfileEnvironment=M,exports.resolveProfile=E;
4
4
  //# sourceMappingURL=profiles.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.cjs","names":["path","fs","os","readDoomConfigSources"],"sources":["../../src/adapters/profiles.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { readDoomConfigSources } from './layeredConfig.ts';\n\n/** Files that make up one repository persona, concatenated in this order. */\nexport const PERSONA_FILES = ['profile.md', 'SOUL.md', 'AGENTS.md'];\n\nexport interface AgentProfile {\n name: string;\n /** Directory relative to personaRoot, which remains the persona source. */\n persona: string;\n /**\n * Root the persona path resolves against: the repository root for a\n * repository profile, the global `.doom` directory for a global one.\n */\n personaRoot: string;\n /** Environment defaults. Values already exported by the caller win. */\n env: Record<string, string>;\n}\n\ninterface ProfileConfigLayer {\n filePath: string;\n baseDirectory: string;\n roots: string[];\n entries: Record<string, unknown>;\n}\n\nconst PROFILE_KEYS = new Set(['persona', 'env']);\nconst PROFILE_CATALOG_KEYS = new Set(['roots', 'entries']);\nconst PROFILES_FILE = 'profiles.yaml';\nconst PROFILES_ROOT_KEY = 'profiles';\nconst AGENTS_DIRECTORY = 'agents';\nconst PARENT_DIRECTORY = '..';\nconst TEXT_ENCODING = 'utf8';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isPathInside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return (\n relative === '' ||\n (!path.isAbsolute(relative) &&\n relative !== PARENT_DIRECTORY &&\n !relative.startsWith(`${PARENT_DIRECTORY}${path.sep}`))\n );\n}\n\nfunction parseEnvironment(name: string, value: unknown): Record<string, string> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`Profile \"${name}\" env must be a string mapping`);\n\n const environment: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry !== 'string') throw new Error(`Profile \"${name}\" env.${key} must be a string`);\n environment[key] = entry;\n }\n return environment;\n}\n\nfunction parseProfileRoots(value: unknown, filePath: string, baseDirectory: string): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) throw new Error(`profiles.roots in ${filePath} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || entry.trim().length === 0) {\n throw new Error(`profiles.roots[${index}] in ${filePath} must be a non-empty string`);\n }\n return path.resolve(baseDirectory, entry.trim());\n });\n}\n\nfunction parseProfileLayer(source: { filePath: string; baseDirectory: string; document: unknown }): ProfileConfigLayer {\n if (!isRecord(source.document) || !isRecord(source.document[PROFILES_ROOT_KEY])) {\n throw new Error(`${source.filePath} must contain a profiles mapping`);\n }\n const unsupportedDocumentKeys = Object.keys(source.document).filter((key) => key !== PROFILES_ROOT_KEY);\n if (unsupportedDocumentKeys.length > 0) {\n throw new Error(`${source.filePath} may only contain profiles; unsupported: ${unsupportedDocumentKeys.join(', ')}`);\n }\n\n const profiles = source.document[PROFILES_ROOT_KEY];\n const usesCatalogShape = Object.hasOwn(profiles, 'roots') || Object.hasOwn(profiles, 'entries');\n if (!usesCatalogShape) {\n return { filePath: source.filePath, baseDirectory: source.baseDirectory, roots: [], entries: profiles };\n }\n\n const unsupportedCatalogKeys = Object.keys(profiles).filter((key) => !PROFILE_CATALOG_KEYS.has(key));\n if (unsupportedCatalogKeys.length > 0) {\n throw new Error(\n `profiles in ${source.filePath} may only contain roots and entries; unsupported: ${unsupportedCatalogKeys.join(', ')}`,\n );\n }\n if (profiles.entries !== undefined && !isRecord(profiles.entries)) {\n throw new Error(`profiles.entries in ${source.filePath} must be a mapping`);\n }\n return {\n filePath: source.filePath,\n baseDirectory: source.baseDirectory,\n roots: parseProfileRoots(profiles.roots, source.filePath, source.baseDirectory),\n entries: profiles.entries ?? {},\n };\n}\n\nfunction assertProfileInsideAllowedRoot(name: string, personaPath: string, lexicalRoots: readonly string[]): void {\n const realPersonaPath = fs.realpathSync(personaPath);\n if (!lexicalRoots.some((root) => isPathInside(fs.realpathSync(root), realPersonaPath))) {\n throw new Error(`Profile \"${name}\" persona must stay inside agents/ or a configured profile root`);\n }\n}\n\nfunction parseProfile(\n personaRoot: string,\n configuredRoots: readonly string[],\n name: string,\n value: unknown,\n): AgentProfile {\n if (!isRecord(value)) throw new Error(`Profile \"${name}\" must be a mapping`);\n\n const unsupported = Object.keys(value).filter((key) => !PROFILE_KEYS.has(key));\n if (unsupported.length > 0) {\n throw new Error(`Profile \"${name}\" may only set persona and env; unsupported: ${unsupported.join(', ')}`);\n }\n if (typeof value.persona !== 'string' || value.persona.trim().length === 0) {\n throw new Error(`Profile \"${name}\" must set a persona`);\n }\n if (path.isAbsolute(value.persona)) {\n throw new Error(`Profile \"${name}\" persona must be relative to the config that declares it`);\n }\n\n const persona = value.persona;\n const personaPath = path.resolve(personaRoot, persona);\n const lexicalRoots = [path.resolve(personaRoot, AGENTS_DIRECTORY), ...configuredRoots].filter((root) =>\n isPathInside(root, personaPath),\n );\n if (lexicalRoots.length === 0) {\n throw new Error(`Profile \"${name}\" persona must stay inside agents/ or a configured profile root`);\n }\n if (!fs.existsSync(personaPath) || !fs.statSync(personaPath).isDirectory()) {\n throw new Error(`Profile \"${name}\" references a missing persona: ${persona}`);\n }\n assertProfileInsideAllowedRoot(name, personaPath, lexicalRoots);\n if (!buildPersonaPrompt(personaRoot, persona)) {\n throw new Error(`Profile \"${name}\" persona has no readable persona files: ${persona}`);\n }\n\n return { name, persona, personaRoot, env: parseEnvironment(name, value.env) };\n}\n\nfunction containsPersonaFile(directory: string): boolean {\n return PERSONA_FILES.some((file) => {\n try {\n return fs.lstatSync(path.join(directory, file)).isFile();\n } catch {\n return false;\n }\n });\n}\n\nfunction discoverProfile(directory: string, configuredRoot: string, personaRoot: string): AgentProfile | undefined {\n if (!containsPersonaFile(directory)) return undefined;\n try {\n const realConfiguredRoot = fs.realpathSync(configuredRoot);\n const realDirectory = fs.realpathSync(directory);\n if (!isPathInside(realConfiguredRoot, realDirectory)) return undefined;\n const persona = path.relative(personaRoot, directory) || '.';\n if (!buildPersonaPrompt(personaRoot, persona)) return undefined;\n return { name: path.basename(directory), persona, personaRoot, env: {} };\n } catch {\n // Automatic discovery ignores unreadable or unsafe candidates; explicit entries remain fatal.\n return undefined;\n }\n}\n\nfunction discoverProfileRoot(root: string, personaRoot: string): AgentProfile[] {\n if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {\n throw new Error(`Configured profile root is not a directory: ${root}`);\n }\n\n const rootProfile = discoverProfile(root, root, personaRoot);\n if (rootProfile) return [rootProfile];\n\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .sort((left, right) => left.name.localeCompare(right.name))\n .flatMap((entry) => {\n const profile = discoverProfile(path.join(root, entry.name), root, personaRoot);\n return profile ? [profile] : [];\n });\n}\n\n/**\n * Loads profiles from the global and repository configs, repository last.\n *\n * Configured roots accumulate and inspect themselves or their direct-child\n * directories for persona files. Later discoveries replace earlier names, and\n * explicit entries replace discoveries. Legacy flat profile mappings remain\n * accepted as explicit entries.\n */\nexport function loadProfiles(repoRoot: string, homeDirectory: string = os.homedir()): AgentProfile[] {\n const layers = readDoomConfigSources<unknown>(PROFILES_FILE, repoRoot, homeDirectory).map(parseProfileLayer);\n const byName = new Map<string, AgentProfile>();\n\n for (const layer of layers) {\n for (const root of layer.roots) {\n for (const profile of discoverProfileRoot(root, layer.baseDirectory)) byName.set(profile.name, profile);\n }\n }\n\n for (const layer of layers) {\n for (const [name, value] of Object.entries(layer.entries)) {\n byName.set(name, parseProfile(layer.baseDirectory, layer.roots, name, value));\n }\n }\n\n return [...byName.values()].sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/** Resolves one selected profile and fails clearly when its name is unknown. */\nexport function resolveProfile(repoRoot: string, name: string, homeDirectory?: string): AgentProfile {\n const profile = loadProfiles(repoRoot, homeDirectory).find((candidate) => candidate.name === name);\n if (!profile) throw new Error(`Unknown profile: ${name}`);\n return profile;\n}\n\nexport function listProfileNames(repoRoot: string, homeDirectory?: string): string[] {\n return loadProfiles(repoRoot, homeDirectory).map((profile) => profile.name);\n}\n\n/** Builds the persona system-prompt block, or undefined when nothing is readable. */\nexport function buildPersonaPrompt(personaRoot: string, personaDirectory: string): string | undefined {\n const sections: string[] = [];\n const personaPath = fs.realpathSync(path.join(personaRoot, personaDirectory));\n for (const file of PERSONA_FILES) {\n const filePath = path.join(personaPath, file);\n if (!fs.existsSync(filePath)) continue;\n const realFilePath = fs.realpathSync(filePath);\n const relativeFilePath = path.relative(personaPath, realFilePath);\n if (relativeFilePath === PARENT_DIRECTORY || relativeFilePath.startsWith(`${PARENT_DIRECTORY}${path.sep}`)) {\n throw new Error(`Persona file must stay inside ${personaDirectory}: ${file}`);\n }\n const content = fs.readFileSync(realFilePath, TEXT_ENCODING).trim();\n if (content) sections.push(content);\n }\n if (sections.length === 0) return undefined;\n return [`[PERSONA] You are operating as the person described below (source: ${personaDirectory}).`, ...sections].join(\n '\\n\\n',\n );\n}\n\n/** Applies profile environment defaults and returns only the values it contributed. */\nexport function applyProfileEnvironment(\n environment: NodeJS.ProcessEnv,\n defaults: Record<string, string>,\n): Record<string, string> {\n const applied: Record<string, string> = {};\n for (const [key, value] of Object.entries(defaults)) {\n if (environment[key] !== undefined) continue;\n environment[key] = value;\n applied[key] = value;\n }\n return applied;\n}\n\n/** Replaces environment defaults previously contributed by another profile. */\nexport function replaceProfileEnvironment(\n environment: NodeJS.ProcessEnv,\n previous: Record<string, string>,\n next: Record<string, string>,\n): Record<string, string> {\n for (const [key, value] of Object.entries(previous)) {\n if (environment[key] === value) delete environment[key];\n }\n return applyProfileEnvironment(environment, next);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"profiles.cjs","names":["path","fs","os","readDoomConfigSources","readPersonaIcon","parsePersonaFrontMatter"],"sources":["../../src/adapters/profiles.ts"],"sourcesContent":["import fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport {\n type PersonaFrontMatter,\n parsePersonaFrontMatter,\n type PersonaVoiceOverride,\n} from '../services/personaFrontMatter.ts';\nimport { readDoomConfigSources } from './layeredConfig.ts';\nimport { readPersonaIcon } from './personaIcon.ts';\n\n/** Files that make up one repository persona, concatenated in this order. */\nexport const PERSONA_FILES = ['profile.md', 'SOUL.md', 'AGENTS.md'];\n\n/** The persona file that may carry an identity block; the others are prose only. */\nconst PERSONA_IDENTITY_FILE = 'profile.md';\n\n/** A persona's resolved presentation: display name, and an icon already read into a data URL. */\nexport interface PersonaIdentity {\n name?: string;\n icon?: string;\n}\n\nexport interface AgentProfile {\n name: string;\n /** Directory relative to personaRoot, which remains the persona source. */\n persona: string;\n /**\n * Root the persona path resolves against: the repository root for a\n * repository profile, the global `.doom` directory for a global one.\n */\n personaRoot: string;\n /** Environment defaults. Values already exported by the caller win. */\n env: Record<string, string>;\n /** Display name and avatar declared in the persona's own front-matter. */\n identity?: PersonaIdentity;\n /** Narrowly scoped TTS override declared alongside the identity. */\n voice?: PersonaVoiceOverride;\n}\n\ninterface ProfileConfigLayer {\n filePath: string;\n baseDirectory: string;\n roots: string[];\n entries: Record<string, unknown>;\n defaultProfile?: string;\n}\n\nconst PROFILE_KEYS = new Set(['persona', 'env']);\nconst PROFILE_CATALOG_KEYS = new Set(['roots', 'entries', 'defaultProfile']);\nconst PROFILES_FILE = 'profiles.yaml';\nconst PROFILES_ROOT_KEY = 'profiles';\nconst AGENTS_DIRECTORY = 'agents';\nconst PARENT_DIRECTORY = '..';\nconst TEXT_ENCODING = 'utf8';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction isPathInside(root: string, candidate: string): boolean {\n const relative = path.relative(root, candidate);\n return (\n relative === '' ||\n (!path.isAbsolute(relative) &&\n relative !== PARENT_DIRECTORY &&\n !relative.startsWith(`${PARENT_DIRECTORY}${path.sep}`))\n );\n}\n\nfunction parseEnvironment(name: string, value: unknown): Record<string, string> {\n if (value === undefined) return {};\n if (!isRecord(value)) throw new Error(`Profile \"${name}\" env must be a string mapping`);\n\n const environment: Record<string, string> = {};\n for (const [key, entry] of Object.entries(value)) {\n if (typeof entry !== 'string') throw new Error(`Profile \"${name}\" env.${key} must be a string`);\n environment[key] = entry;\n }\n return environment;\n}\n\nfunction parseProfileRoots(value: unknown, filePath: string, baseDirectory: string): string[] {\n if (value === undefined) return [];\n if (!Array.isArray(value)) throw new Error(`profiles.roots in ${filePath} must be an array`);\n return value.map((entry, index) => {\n if (typeof entry !== 'string' || entry.trim().length === 0) {\n throw new Error(`profiles.roots[${index}] in ${filePath} must be a non-empty string`);\n }\n return path.resolve(baseDirectory, entry.trim());\n });\n}\n\nfunction parseProfileLayer(source: { filePath: string; baseDirectory: string; document: unknown }): ProfileConfigLayer {\n if (!isRecord(source.document) || !isRecord(source.document[PROFILES_ROOT_KEY])) {\n throw new Error(`${source.filePath} must contain a profiles mapping`);\n }\n const unsupportedDocumentKeys = Object.keys(source.document).filter((key) => key !== PROFILES_ROOT_KEY);\n if (unsupportedDocumentKeys.length > 0) {\n throw new Error(`${source.filePath} may only contain profiles; unsupported: ${unsupportedDocumentKeys.join(', ')}`);\n }\n\n const profiles = source.document[PROFILES_ROOT_KEY];\n const usesCatalogShape = Object.hasOwn(profiles, 'roots') || Object.hasOwn(profiles, 'entries');\n if (!usesCatalogShape) {\n return { filePath: source.filePath, baseDirectory: source.baseDirectory, roots: [], entries: profiles };\n }\n\n const unsupportedCatalogKeys = Object.keys(profiles).filter((key) => !PROFILE_CATALOG_KEYS.has(key));\n if (unsupportedCatalogKeys.length > 0) {\n throw new Error(\n `profiles in ${source.filePath} may only contain roots, entries and defaultProfile; unsupported: ${unsupportedCatalogKeys.join(', ')}`,\n );\n }\n if (\n profiles.defaultProfile !== undefined &&\n (typeof profiles.defaultProfile !== 'string' || profiles.defaultProfile.trim().length === 0)\n ) {\n throw new Error(`profiles.defaultProfile in ${source.filePath} must be a non-empty string`);\n }\n if (profiles.entries !== undefined && !isRecord(profiles.entries)) {\n throw new Error(`profiles.entries in ${source.filePath} must be a mapping`);\n }\n return {\n filePath: source.filePath,\n baseDirectory: source.baseDirectory,\n roots: parseProfileRoots(profiles.roots, source.filePath, source.baseDirectory),\n entries: profiles.entries ?? {},\n ...(profiles.defaultProfile === undefined ? {} : { defaultProfile: String(profiles.defaultProfile).trim() }),\n };\n}\n\nfunction assertProfileInsideAllowedRoot(name: string, personaPath: string, lexicalRoots: readonly string[]): void {\n const realPersonaPath = fs.realpathSync(personaPath);\n if (!lexicalRoots.some((root) => isPathInside(fs.realpathSync(root), realPersonaPath))) {\n throw new Error(`Profile \"${name}\" persona must stay inside agents/ or a configured profile root`);\n }\n}\n\nfunction parseProfile(\n personaRoot: string,\n configuredRoots: readonly string[],\n name: string,\n value: unknown,\n): AgentProfile {\n if (!isRecord(value)) throw new Error(`Profile \"${name}\" must be a mapping`);\n\n const unsupported = Object.keys(value).filter((key) => !PROFILE_KEYS.has(key));\n if (unsupported.length > 0) {\n throw new Error(`Profile \"${name}\" may only set persona and env; unsupported: ${unsupported.join(', ')}`);\n }\n if (typeof value.persona !== 'string' || value.persona.trim().length === 0) {\n throw new Error(`Profile \"${name}\" must set a persona`);\n }\n if (path.isAbsolute(value.persona)) {\n throw new Error(`Profile \"${name}\" persona must be relative to the config that declares it`);\n }\n\n const persona = value.persona;\n const personaPath = path.resolve(personaRoot, persona);\n const lexicalRoots = [path.resolve(personaRoot, AGENTS_DIRECTORY), ...configuredRoots].filter((root) =>\n isPathInside(root, personaPath),\n );\n if (lexicalRoots.length === 0) {\n throw new Error(`Profile \"${name}\" persona must stay inside agents/ or a configured profile root`);\n }\n if (!fs.existsSync(personaPath) || !fs.statSync(personaPath).isDirectory()) {\n throw new Error(`Profile \"${name}\" references a missing persona: ${persona}`);\n }\n assertProfileInsideAllowedRoot(name, personaPath, lexicalRoots);\n if (!buildPersonaPrompt(personaRoot, persona)) {\n // An identity block is not persona text, so a profile.md holding nothing else\n // leaves the persona empty. Say that, rather than claiming the file is unreadable.\n const declaresIdentity = readPersonaFrontMatter(personaRoot, persona) !== undefined;\n throw new Error(\n declaresIdentity\n ? `Profile \"${name}\" persona declares identity front-matter but no persona text: ${persona}`\n : `Profile \"${name}\" persona has no readable persona files: ${persona}`,\n );\n }\n\n return {\n name,\n persona,\n personaRoot,\n env: parseEnvironment(name, value.env),\n ...readPersonaPresentation(personaRoot, persona),\n };\n}\n\nfunction containsPersonaFile(directory: string): boolean {\n return PERSONA_FILES.some((file) => {\n try {\n return fs.lstatSync(path.join(directory, file)).isFile();\n } catch {\n return false;\n }\n });\n}\n\nfunction discoverProfile(directory: string, configuredRoot: string, personaRoot: string): AgentProfile | undefined {\n if (!containsPersonaFile(directory)) return undefined;\n try {\n const realConfiguredRoot = fs.realpathSync(configuredRoot);\n const realDirectory = fs.realpathSync(directory);\n if (!isPathInside(realConfiguredRoot, realDirectory)) return undefined;\n const persona = path.relative(personaRoot, directory) || '.';\n if (!buildPersonaPrompt(personaRoot, persona)) return undefined;\n return {\n name: path.basename(directory),\n persona,\n personaRoot,\n env: {},\n ...readPersonaPresentation(personaRoot, persona),\n };\n } catch {\n // Automatic discovery ignores unreadable or unsafe candidates; explicit entries remain fatal.\n return undefined;\n }\n}\n\nfunction discoverProfileRoot(root: string, personaRoot: string): AgentProfile[] {\n if (!fs.existsSync(root) || !fs.statSync(root).isDirectory()) {\n throw new Error(`Configured profile root is not a directory: ${root}`);\n }\n\n const rootProfile = discoverProfile(root, root, personaRoot);\n if (rootProfile) return [rootProfile];\n\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory())\n .sort((left, right) => left.name.localeCompare(right.name))\n .flatMap((entry) => {\n const profile = discoverProfile(path.join(root, entry.name), root, personaRoot);\n return profile ? [profile] : [];\n });\n}\n\n/**\n * Loads profiles from the global and repository configs, repository last.\n *\n * Configured roots accumulate and inspect themselves or their direct-child\n * directories for persona files. Later discoveries replace earlier names, and\n * explicit entries replace discoveries. Legacy flat profile mappings remain\n * accepted as explicit entries.\n */\nexport function loadProfiles(repoRoot: string, homeDirectory: string = os.homedir()): AgentProfile[] {\n const layers = readDoomConfigSources<unknown>(PROFILES_FILE, repoRoot, homeDirectory).map(parseProfileLayer);\n const byName = new Map<string, AgentProfile>();\n\n for (const layer of layers) {\n for (const root of layer.roots) {\n for (const profile of discoverProfileRoot(root, layer.baseDirectory)) byName.set(profile.name, profile);\n }\n }\n\n for (const layer of layers) {\n for (const [name, value] of Object.entries(layer.entries)) {\n byName.set(name, parseProfile(layer.baseDirectory, layer.roots, name, value));\n }\n }\n\n return [...byName.values()].sort((left, right) => left.name.localeCompare(right.name));\n}\n\n/**\n * The profile catalog: every resolved profile, plus the name a launch starts on\n * when the caller names none.\n *\n * `defaultProfile` mirrors `defaultMajorMode` in modes.yaml and `defaultDomains`\n * in domains.yaml, so each selection axis declares its default in its own\n * catalog. A repository declaration replaces a personal one. An unknown name is\n * an error rather than a silent fallback: a typo that quietly starts the session\n * with no persona is worse than a refused launch.\n */\nexport function loadProfileCatalog(\n repoRoot: string,\n homeDirectory: string = os.homedir(),\n): { profiles: AgentProfile[]; defaultProfile?: string } {\n const layers = readDoomConfigSources<unknown>(PROFILES_FILE, repoRoot, homeDirectory).map(parseProfileLayer);\n const profiles = loadProfiles(repoRoot, homeDirectory);\n let defaultProfile: string | undefined;\n for (const layer of layers) {\n if (layer.defaultProfile !== undefined) defaultProfile = layer.defaultProfile;\n }\n if (defaultProfile !== undefined && !profiles.some((profile) => profile.name === defaultProfile)) {\n throw new Error(`profiles.defaultProfile names an unknown profile: ${defaultProfile}`);\n }\n return { profiles, ...(defaultProfile === undefined ? {} : { defaultProfile }) };\n}\n\n/** Resolves one selected profile and fails clearly when its name is unknown. */\nexport function resolveProfile(repoRoot: string, name: string, homeDirectory?: string): AgentProfile {\n const profile = loadProfiles(repoRoot, homeDirectory).find((candidate) => candidate.name === name);\n if (!profile) throw new Error(`Unknown profile: ${name}`);\n return profile;\n}\n\nexport function listProfileNames(repoRoot: string, homeDirectory?: string): string[] {\n return loadProfiles(repoRoot, homeDirectory).map((profile) => profile.name);\n}\n\n/**\n * Reads the identity block from a persona's `profile.md`.\n *\n * Separate from `buildPersonaPrompt` because the prompt is assembled on several\n * paths that do not want the icon bytes, and because a persona that declares\n * nothing must stay exactly as cheap as it is today.\n */\nfunction readPersonaPresentation(\n personaRoot: string,\n personaDirectory: string,\n): { identity?: PersonaIdentity; voice?: PersonaVoiceOverride } {\n const frontMatter = readPersonaFrontMatter(personaRoot, personaDirectory);\n if (!frontMatter) return {};\n\n const icon =\n frontMatter.icon === undefined\n ? undefined\n : readPersonaIcon(path.join(personaRoot, personaDirectory), frontMatter.icon);\n const identity: PersonaIdentity = {\n ...(frontMatter.name === undefined ? {} : { name: frontMatter.name }),\n ...(icon === undefined ? {} : { icon }),\n };\n return {\n ...(identity.name === undefined && identity.icon === undefined ? {} : { identity }),\n ...(frontMatter.voice === undefined ? {} : { voice: frontMatter.voice }),\n };\n}\n\nfunction readPersonaFrontMatter(personaRoot: string, personaDirectory: string): PersonaFrontMatter | undefined {\n try {\n const personaPath = fs.realpathSync(path.join(personaRoot, personaDirectory));\n const filePath = path.join(personaPath, PERSONA_IDENTITY_FILE);\n if (!fs.existsSync(filePath)) return undefined;\n return parsePersonaFrontMatter(fs.readFileSync(fs.realpathSync(filePath), TEXT_ENCODING)).frontMatter;\n } catch {\n return undefined;\n }\n}\n/** Builds the persona system-prompt block, or undefined when nothing is readable. */\nexport function buildPersonaPrompt(personaRoot: string, personaDirectory: string): string | undefined {\n const sections: string[] = [];\n const personaPath = fs.realpathSync(path.join(personaRoot, personaDirectory));\n for (const file of PERSONA_FILES) {\n const filePath = path.join(personaPath, file);\n if (!fs.existsSync(filePath)) continue;\n const realFilePath = fs.realpathSync(filePath);\n const relativeFilePath = path.relative(personaPath, realFilePath);\n if (relativeFilePath === PARENT_DIRECTORY || relativeFilePath.startsWith(`${PARENT_DIRECTORY}${path.sep}`)) {\n throw new Error(`Persona file must stay inside ${personaDirectory}: ${file}`);\n }\n const raw = fs.readFileSync(realFilePath, TEXT_ENCODING);\n // Only profile.md may declare identity, so only profile.md is stripped.\n const content = (file === PERSONA_IDENTITY_FILE ? parsePersonaFrontMatter(raw).body : raw).trim();\n if (content) sections.push(content);\n }\n if (sections.length === 0) return undefined;\n return [`[PERSONA] You are operating as the person described below (source: ${personaDirectory}).`, ...sections].join(\n '\\n\\n',\n );\n}\n\n/** Applies profile environment defaults and returns only the values it contributed. */\nexport function applyProfileEnvironment(\n environment: NodeJS.ProcessEnv,\n defaults: Record<string, string>,\n): Record<string, string> {\n const applied: Record<string, string> = {};\n for (const [key, value] of Object.entries(defaults)) {\n if (environment[key] !== undefined) continue;\n environment[key] = value;\n applied[key] = value;\n }\n return applied;\n}\n\n/** Replaces environment defaults previously contributed by another profile. */\nexport function replaceProfileEnvironment(\n environment: NodeJS.ProcessEnv,\n previous: Record<string, string>,\n next: Record<string, string>,\n): Record<string, string> {\n for (const [key, value] of Object.entries(previous)) {\n if (environment[key] === value) delete environment[key];\n }\n return applyProfileEnvironment(environment, next);\n}\n"],"mappings":"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"}
@@ -1,6 +1,12 @@
1
+ import { PersonaVoiceOverride } from "../services/personaFrontMatter.cjs";
1
2
  //#region src/adapters/profiles.d.ts
2
3
  /** Files that make up one repository persona, concatenated in this order. */
3
4
  export declare const PERSONA_FILES: string[];
5
+ /** A persona's resolved presentation: display name, and an icon already read into a data URL. */
6
+ export interface PersonaIdentity {
7
+ name?: string;
8
+ icon?: string;
9
+ }
4
10
  export interface AgentProfile {
5
11
  name: string;
6
12
  /** Directory relative to personaRoot, which remains the persona source. */
@@ -12,6 +18,10 @@ export interface AgentProfile {
12
18
  personaRoot: string;
13
19
  /** Environment defaults. Values already exported by the caller win. */
14
20
  env: Record<string, string>;
21
+ /** Display name and avatar declared in the persona's own front-matter. */
22
+ identity?: PersonaIdentity;
23
+ /** Narrowly scoped TTS override declared alongside the identity. */
24
+ voice?: PersonaVoiceOverride;
15
25
  }
16
26
  /**
17
27
  * Loads profiles from the global and repository configs, repository last.
@@ -22,6 +32,20 @@ export interface AgentProfile {
22
32
  * accepted as explicit entries.
23
33
  */
24
34
  export declare function loadProfiles(repoRoot: string, homeDirectory?: string): AgentProfile[];
35
+ /**
36
+ * The profile catalog: every resolved profile, plus the name a launch starts on
37
+ * when the caller names none.
38
+ *
39
+ * `defaultProfile` mirrors `defaultMajorMode` in modes.yaml and `defaultDomains`
40
+ * in domains.yaml, so each selection axis declares its default in its own
41
+ * catalog. A repository declaration replaces a personal one. An unknown name is
42
+ * an error rather than a silent fallback: a typo that quietly starts the session
43
+ * with no persona is worse than a refused launch.
44
+ */
45
+ export declare function loadProfileCatalog(repoRoot: string, homeDirectory?: string): {
46
+ profiles: AgentProfile[];
47
+ defaultProfile?: string;
48
+ };
25
49
  /** Resolves one selected profile and fails clearly when its name is unknown. */
26
50
  export declare function resolveProfile(repoRoot: string, name: string, homeDirectory?: string): AgentProfile;
27
51
  export declare function listProfileNames(repoRoot: string, homeDirectory?: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.d.cts","names":[],"sources":["../../src/adapters/profiles.ts"],"mappings":";;qBAMa;iBAEI;EACf;;EAEA;;;;;EAKA;;EAEA,KAAK;;;;;;;;;;wBAuLS,aAAa,kBAAkB,yBAAuC;;wBAoBtE,eAAe,kBAAkB,cAAc,yBAAyB;wBAMxE,iBAAiB,kBAAkB;;wBAKnC,mBAAmB,qBAAqB;;wBAqBxC,wBACd,aAAa,OAAO,YACpB,UAAU,yBACT;;wBAWa,0BACd,aAAa,OAAO,YACpB,UAAU,wBACV,MAAM,yBACL"}
1
+ {"version":3,"file":"profiles.d.cts","names":[],"sources":["../../src/adapters/profiles.ts"],"mappings":";;;qBAYa;;iBAMI;EACf;EACA;;iBAGe;EACf;;EAEA;;;;;EAKA;;EAEA,KAAK;;EAEL,WAAW;;EAEX,QAAQ;;;;;;;;;;wBAkNM,aAAa,kBAAkB,yBAAuC;;;;;;;;;;;wBA6BtE,mBACd,kBACA;EACG,UAAU;EAAgB;;;wBAcf,eAAe,kBAAkB,cAAc,yBAAyB;wBAMxE,iBAAiB,kBAAkB;;wBA2CnC,mBAAmB,qBAAqB;;wBAuBxC,wBACd,aAAa,OAAO,YACpB,UAAU,yBACT;;wBAWa,0BACd,aAAa,OAAO,YACpB,UAAU,wBACV,MAAM,yBACL"}
@@ -1,6 +1,12 @@
1
+ import { PersonaVoiceOverride } from "../services/personaFrontMatter.mjs";
1
2
  //#region src/adapters/profiles.d.ts
2
3
  /** Files that make up one repository persona, concatenated in this order. */
3
4
  export declare const PERSONA_FILES: string[];
5
+ /** A persona's resolved presentation: display name, and an icon already read into a data URL. */
6
+ export interface PersonaIdentity {
7
+ name?: string;
8
+ icon?: string;
9
+ }
4
10
  export interface AgentProfile {
5
11
  name: string;
6
12
  /** Directory relative to personaRoot, which remains the persona source. */
@@ -12,6 +18,10 @@ export interface AgentProfile {
12
18
  personaRoot: string;
13
19
  /** Environment defaults. Values already exported by the caller win. */
14
20
  env: Record<string, string>;
21
+ /** Display name and avatar declared in the persona's own front-matter. */
22
+ identity?: PersonaIdentity;
23
+ /** Narrowly scoped TTS override declared alongside the identity. */
24
+ voice?: PersonaVoiceOverride;
15
25
  }
16
26
  /**
17
27
  * Loads profiles from the global and repository configs, repository last.
@@ -22,6 +32,20 @@ export interface AgentProfile {
22
32
  * accepted as explicit entries.
23
33
  */
24
34
  export declare function loadProfiles(repoRoot: string, homeDirectory?: string): AgentProfile[];
35
+ /**
36
+ * The profile catalog: every resolved profile, plus the name a launch starts on
37
+ * when the caller names none.
38
+ *
39
+ * `defaultProfile` mirrors `defaultMajorMode` in modes.yaml and `defaultDomains`
40
+ * in domains.yaml, so each selection axis declares its default in its own
41
+ * catalog. A repository declaration replaces a personal one. An unknown name is
42
+ * an error rather than a silent fallback: a typo that quietly starts the session
43
+ * with no persona is worse than a refused launch.
44
+ */
45
+ export declare function loadProfileCatalog(repoRoot: string, homeDirectory?: string): {
46
+ profiles: AgentProfile[];
47
+ defaultProfile?: string;
48
+ };
25
49
  /** Resolves one selected profile and fails clearly when its name is unknown. */
26
50
  export declare function resolveProfile(repoRoot: string, name: string, homeDirectory?: string): AgentProfile;
27
51
  export declare function listProfileNames(repoRoot: string, homeDirectory?: string): string[];
@@ -1 +1 @@
1
- {"version":3,"file":"profiles.d.mts","names":[],"sources":["../../src/adapters/profiles.ts"],"mappings":";;qBAMa;iBAEI;EACf;;EAEA;;;;;EAKA;;EAEA,KAAK;;;;;;;;;;wBAuLS,aAAa,kBAAkB,yBAAuC;;wBAoBtE,eAAe,kBAAkB,cAAc,yBAAyB;wBAMxE,iBAAiB,kBAAkB;;wBAKnC,mBAAmB,qBAAqB;;wBAqBxC,wBACd,aAAa,OAAO,YACpB,UAAU,yBACT;;wBAWa,0BACd,aAAa,OAAO,YACpB,UAAU,wBACV,MAAM,yBACL"}
1
+ {"version":3,"file":"profiles.d.mts","names":[],"sources":["../../src/adapters/profiles.ts"],"mappings":";;;qBAYa;;iBAMI;EACf;EACA;;iBAGe;EACf;;EAEA;;;;;EAKA;;EAEA,KAAK;;EAEL,WAAW;;EAEX,QAAQ;;;;;;;;;;wBAkNM,aAAa,kBAAkB,yBAAuC;;;;;;;;;;;wBA6BtE,mBACd,kBACA;EACG,UAAU;EAAgB;;;wBAcf,eAAe,kBAAkB,cAAc,yBAAyB;wBAMxE,iBAAiB,kBAAkB;;wBA2CnC,mBAAmB,qBAAqB;;wBAuBxC,wBACd,aAAa,OAAO,YACpB,UAAU,yBACT;;wBAWa,0BACd,aAAa,OAAO,YACpB,UAAU,wBACV,MAAM,yBACL"}
@@ -1,4 +1,4 @@
1
- import{readDoomConfigSources as e}from"./layeredConfig.mjs";import t from"node:fs";import n from"node:path";import r from"node:os";const i=[`profile.md`,`SOUL.md`,`AGENTS.md`],a=new Set([`persona`,`env`]),o=new Set([`roots`,`entries`]),s=`profiles`;function c(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function l(e,t){let r=n.relative(e,t);return r===``||!n.isAbsolute(r)&&r!==`..`&&!r.startsWith(`..${n.sep}`)}function u(e,t){if(t===void 0)return{};if(!c(t))throw Error(`Profile "${e}" env must be a string mapping`);let n={};for(let[r,i]of Object.entries(t)){if(typeof i!=`string`)throw Error(`Profile "${e}" env.${r} must be a string`);n[r]=i}return n}function d(e,t,r){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`profiles.roots in ${t} must be an array`);return e.map((e,i)=>{if(typeof e!=`string`||e.trim().length===0)throw Error(`profiles.roots[${i}] in ${t} must be a non-empty string`);return n.resolve(r,e.trim())})}function f(e){if(!c(e.document)||!c(e.document[s]))throw Error(`${e.filePath} must contain a profiles mapping`);let t=Object.keys(e.document).filter(e=>e!==s);if(t.length>0)throw Error(`${e.filePath} may only contain profiles; unsupported: ${t.join(`, `)}`);let n=e.document[s];if(!(Object.hasOwn(n,`roots`)||Object.hasOwn(n,`entries`)))return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:[],entries:n};let r=Object.keys(n).filter(e=>!o.has(e));if(r.length>0)throw Error(`profiles in ${e.filePath} may only contain roots and entries; unsupported: ${r.join(`, `)}`);if(n.entries!==void 0&&!c(n.entries))throw Error(`profiles.entries in ${e.filePath} must be a mapping`);return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:d(n.roots,e.filePath,e.baseDirectory),entries:n.entries??{}}}function p(e,n,r){let i=t.realpathSync(n);if(!r.some(e=>l(t.realpathSync(e),i)))throw Error(`Profile "${e}" persona must stay inside agents/ or a configured profile root`)}function m(e,r,i,o){if(!c(o))throw Error(`Profile "${i}" must be a mapping`);let s=Object.keys(o).filter(e=>!a.has(e));if(s.length>0)throw Error(`Profile "${i}" may only set persona and env; unsupported: ${s.join(`, `)}`);if(typeof o.persona!=`string`||o.persona.trim().length===0)throw Error(`Profile "${i}" must set a persona`);if(n.isAbsolute(o.persona))throw Error(`Profile "${i}" persona must be relative to the config that declares it`);let d=o.persona,f=n.resolve(e,d),m=[n.resolve(e,`agents`),...r].filter(e=>l(e,f));if(m.length===0)throw Error(`Profile "${i}" persona must stay inside agents/ or a configured profile root`);if(!t.existsSync(f)||!t.statSync(f).isDirectory())throw Error(`Profile "${i}" references a missing persona: ${d}`);if(p(i,f,m),!x(e,d))throw Error(`Profile "${i}" persona has no readable persona files: ${d}`);return{name:i,persona:d,personaRoot:e,env:u(i,o.env)}}function h(e){return i.some(r=>{try{return t.lstatSync(n.join(e,r)).isFile()}catch{return!1}})}function g(e,r,i){if(h(e))try{if(!l(t.realpathSync(r),t.realpathSync(e)))return;let a=n.relative(i,e)||`.`;return x(i,a)?{name:n.basename(e),persona:a,personaRoot:i,env:{}}:void 0}catch{return}}function _(e,r){if(!t.existsSync(e)||!t.statSync(e).isDirectory())throw Error(`Configured profile root is not a directory: ${e}`);let i=g(e,e,r);return i?[i]:t.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).sort((e,t)=>e.name.localeCompare(t.name)).flatMap(t=>{let i=g(n.join(e,t.name),e,r);return i?[i]:[]})}function v(t,n=r.homedir()){let i=e(`profiles.yaml`,t,n).map(f),a=new Map;for(let e of i)for(let t of e.roots)for(let n of _(t,e.baseDirectory))a.set(n.name,n);for(let e of i)for(let[t,n]of Object.entries(e.entries))a.set(t,m(e.baseDirectory,e.roots,t,n));return[...a.values()].sort((e,t)=>e.name.localeCompare(t.name))}function y(e,t,n){let r=v(e,n).find(e=>e.name===t);if(!r)throw Error(`Unknown profile: ${t}`);return r}function b(e,t){return v(e,t).map(e=>e.name)}function x(e,r){let a=[],o=t.realpathSync(n.join(e,r));for(let e of i){let i=n.join(o,e);if(!t.existsSync(i))continue;let s=t.realpathSync(i),c=n.relative(o,s);if(c===`..`||c.startsWith(`..${n.sep}`))throw Error(`Persona file must stay inside ${r}: ${e}`);let l=t.readFileSync(s,`utf8`).trim();l&&a.push(l)}if(a.length!==0)return[`[PERSONA] You are operating as the person described below (source: ${r}).`,...a].join(`
1
+ import{readDoomConfigSources as e}from"./layeredConfig.mjs";import{parsePersonaFrontMatter as t}from"../services/personaFrontMatter.mjs";import{readPersonaIcon as n}from"./personaIcon.mjs";import r from"node:fs";import i from"node:path";import a from"node:os";const o=[`profile.md`,`SOUL.md`,`AGENTS.md`],s=`profile.md`,c=new Set([`persona`,`env`]),l=new Set([`roots`,`entries`,`defaultProfile`]),u=`profiles.yaml`,d=`profiles`,f=`utf8`;function p(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function m(e,t){let n=i.relative(e,t);return n===``||!i.isAbsolute(n)&&n!==`..`&&!n.startsWith(`..${i.sep}`)}function h(e,t){if(t===void 0)return{};if(!p(t))throw Error(`Profile "${e}" env must be a string mapping`);let n={};for(let[r,i]of Object.entries(t)){if(typeof i!=`string`)throw Error(`Profile "${e}" env.${r} must be a string`);n[r]=i}return n}function g(e,t,n){if(e===void 0)return[];if(!Array.isArray(e))throw Error(`profiles.roots in ${t} must be an array`);return e.map((e,r)=>{if(typeof e!=`string`||e.trim().length===0)throw Error(`profiles.roots[${r}] in ${t} must be a non-empty string`);return i.resolve(n,e.trim())})}function _(e){if(!p(e.document)||!p(e.document[d]))throw Error(`${e.filePath} must contain a profiles mapping`);let t=Object.keys(e.document).filter(e=>e!==d);if(t.length>0)throw Error(`${e.filePath} may only contain profiles; unsupported: ${t.join(`, `)}`);let n=e.document[d];if(!(Object.hasOwn(n,`roots`)||Object.hasOwn(n,`entries`)))return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:[],entries:n};let r=Object.keys(n).filter(e=>!l.has(e));if(r.length>0)throw Error(`profiles in ${e.filePath} may only contain roots, entries and defaultProfile; unsupported: ${r.join(`, `)}`);if(n.defaultProfile!==void 0&&(typeof n.defaultProfile!=`string`||n.defaultProfile.trim().length===0))throw Error(`profiles.defaultProfile in ${e.filePath} must be a non-empty string`);if(n.entries!==void 0&&!p(n.entries))throw Error(`profiles.entries in ${e.filePath} must be a mapping`);return{filePath:e.filePath,baseDirectory:e.baseDirectory,roots:g(n.roots,e.filePath,e.baseDirectory),entries:n.entries??{},...n.defaultProfile===void 0?{}:{defaultProfile:String(n.defaultProfile).trim()}}}function v(e,t,n){let i=r.realpathSync(t);if(!n.some(e=>m(r.realpathSync(e),i)))throw Error(`Profile "${e}" persona must stay inside agents/ or a configured profile root`)}function y(e,t,n,a){if(!p(a))throw Error(`Profile "${n}" must be a mapping`);let o=Object.keys(a).filter(e=>!c.has(e));if(o.length>0)throw Error(`Profile "${n}" may only set persona and env; unsupported: ${o.join(`, `)}`);if(typeof a.persona!=`string`||a.persona.trim().length===0)throw Error(`Profile "${n}" must set a persona`);if(i.isAbsolute(a.persona))throw Error(`Profile "${n}" persona must be relative to the config that declares it`);let s=a.persona,l=i.resolve(e,s),u=[i.resolve(e,`agents`),...t].filter(e=>m(e,l));if(u.length===0)throw Error(`Profile "${n}" persona must stay inside agents/ or a configured profile root`);if(!r.existsSync(l)||!r.statSync(l).isDirectory())throw Error(`Profile "${n}" references a missing persona: ${s}`);if(v(n,l,u),!k(e,s)){let t=O(e,s)!==void 0;throw Error(t?`Profile "${n}" persona declares identity front-matter but no persona text: ${s}`:`Profile "${n}" persona has no readable persona files: ${s}`)}return{name:n,persona:s,personaRoot:e,env:h(n,a.env),...D(e,s)}}function b(e){return o.some(t=>{try{return r.lstatSync(i.join(e,t)).isFile()}catch{return!1}})}function x(e,t,n){if(b(e))try{if(!m(r.realpathSync(t),r.realpathSync(e)))return;let a=i.relative(n,e)||`.`;return k(n,a)?{name:i.basename(e),persona:a,personaRoot:n,env:{},...D(n,a)}:void 0}catch{return}}function S(e,t){if(!r.existsSync(e)||!r.statSync(e).isDirectory())throw Error(`Configured profile root is not a directory: ${e}`);let n=x(e,e,t);return n?[n]:r.readdirSync(e,{withFileTypes:!0}).filter(e=>e.isDirectory()).sort((e,t)=>e.name.localeCompare(t.name)).flatMap(n=>{let r=x(i.join(e,n.name),e,t);return r?[r]:[]})}function C(t,n=a.homedir()){let r=e(u,t,n).map(_),i=new Map;for(let e of r)for(let t of e.roots)for(let n of S(t,e.baseDirectory))i.set(n.name,n);for(let e of r)for(let[t,n]of Object.entries(e.entries))i.set(t,y(e.baseDirectory,e.roots,t,n));return[...i.values()].sort((e,t)=>e.name.localeCompare(t.name))}function w(t,n=a.homedir()){let r=e(u,t,n).map(_),i=C(t,n),o;for(let e of r)e.defaultProfile!==void 0&&(o=e.defaultProfile);if(o!==void 0&&!i.some(e=>e.name===o))throw Error(`profiles.defaultProfile names an unknown profile: ${o}`);return{profiles:i,...o===void 0?{}:{defaultProfile:o}}}function T(e,t,n){let r=C(e,n).find(e=>e.name===t);if(!r)throw Error(`Unknown profile: ${t}`);return r}function E(e,t){return C(e,t).map(e=>e.name)}function D(e,t){let r=O(e,t);if(!r)return{};let a=r.icon===void 0?void 0:n(i.join(e,t),r.icon),o={...r.name===void 0?{}:{name:r.name},...a===void 0?{}:{icon:a}};return{...o.name===void 0&&o.icon===void 0?{}:{identity:o},...r.voice===void 0?{}:{voice:r.voice}}}function O(e,n){try{let a=r.realpathSync(i.join(e,n)),o=i.join(a,s);return r.existsSync(o)?t(r.readFileSync(r.realpathSync(o),f)).frontMatter:void 0}catch{return}}function k(e,n){let a=[],c=r.realpathSync(i.join(e,n));for(let e of o){let o=i.join(c,e);if(!r.existsSync(o))continue;let l=r.realpathSync(o),u=i.relative(c,l);if(u===`..`||u.startsWith(`..${i.sep}`))throw Error(`Persona file must stay inside ${n}: ${e}`);let d=r.readFileSync(l,f),p=(e===s?t(d).body:d).trim();p&&a.push(p)}if(a.length!==0)return[`[PERSONA] You are operating as the person described below (source: ${n}).`,...a].join(`
2
2
 
3
- `)}function S(e,t){let n={};for(let[r,i]of Object.entries(t))e[r]===void 0&&(e[r]=i,n[r]=i);return n}function C(e,t,n){for(let[n,r]of Object.entries(t))e[n]===r&&delete e[n];return S(e,n)}export{i as PERSONA_FILES,S as applyProfileEnvironment,x as buildPersonaPrompt,b as listProfileNames,v as loadProfiles,C as replaceProfileEnvironment,y as resolveProfile};
3
+ `)}function A(e,t){let n={};for(let[r,i]of Object.entries(t))e[r]===void 0&&(e[r]=i,n[r]=i);return n}function j(e,t,n){for(let[n,r]of Object.entries(t))e[n]===r&&delete e[n];return A(e,n)}export{o as PERSONA_FILES,A as applyProfileEnvironment,k as buildPersonaPrompt,E as listProfileNames,w as loadProfileCatalog,C as loadProfiles,j as replaceProfileEnvironment,T as resolveProfile};
4
4
  //# sourceMappingURL=profiles.mjs.map