{"version":3,"file":"harnessState-BP7bwXNf.cjs","names":["path"],"sources":["../src/services/harnessState/index.ts"],"sourcesContent":["import path from 'node:path';\n\nimport type { HarnessState, PluginHookSource } from '../../types/config';\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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"}