{"version":3,"file":"index.cjs","names":["LAUNCHER_COMPOSITION_ENV","fs","getHarnessState","createLayerResolvers","resolveExtensionComposition","PERSONA_ENTRY","loadMajorModesConfig"],"sources":["../../../../src/builders/cli/launcherComposition/index.ts"],"sourcesContent":["import fs from 'node:fs';\n\nimport { loadMajorModesConfig } from '@agimon-ai/doompi-config/majorModes';\nimport { writeFileAtomic } from '@agimon-ai/doompi-core/runtime-json';\n\nimport { getHarnessState } from '../../../composition/harnessState';\nimport { LAUNCHER_COMPOSITION_ENV, LAUNCHER_COMPOSITION_VERSION } from '../constants';\nimport { createLayerResolvers, PERSONA_ENTRY, resolveExtensionComposition } from '../extensionAssembler';\nimport { type LauncherCompositionState } from '../types';\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n  return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nfunction bundleRecord(value: unknown): Record<string, string> {\n  if (!isRecord(value)) return {};\n  return Object.fromEntries(\n    Object.entries(value).filter((entry): entry is [string, string] => typeof entry[1] === 'string'),\n  );\n}\n\n/** Records what a launcher session's stable entry needs, for the entry to read back. */\nexport function writeLauncherComposition(target: string, state: LauncherCompositionState): void {\n  writeFileAtomic(target, `${JSON.stringify(state, null, 2)}\\n`);\n}\n\n/**\n * The composition record this session was started with, when it has one.\n *\n * A session launched the historical way has no record and no environment\n * pointer, so every reader treats undefined as \"not a composing launcher\"\n * rather than as a failure.\n */\nexport function readLauncherComposition(\n  environment: NodeJS.ProcessEnv = process.env,\n): LauncherCompositionState | undefined {\n  const target = environment[LAUNCHER_COMPOSITION_ENV];\n  if (!target) return undefined;\n  let parsed: unknown;\n  try {\n    parsed = JSON.parse(fs.readFileSync(target, 'utf8'));\n  } catch {\n    // An unreadable record leaves the session on its startup composition,\n    // which is strictly better than refusing to load any extension at all.\n    return undefined;\n  }\n  if (!isRecord(parsed) || parsed.version !== LAUNCHER_COMPOSITION_VERSION || typeof parsed.root !== 'string') {\n    return undefined;\n  }\n  return {\n    version: LAUNCHER_COMPOSITION_VERSION,\n    root: parsed.root,\n    preset: typeof parsed.preset === 'string' ? parsed.preset : 'default',\n    mute: parsed.mute === true,\n    autoStop: parsed.autoStop === true,\n    agents: parsed.agents !== false,\n    bundles: bundleRecord(parsed.bundles),\n  };\n}\n\nexport interface LauncherLoadPlan {\n  /** Entries in Pi factory activation order: one aggregate, or the sources. */\n  entries: string[];\n  fingerprint: string;\n  childExtensions: string[];\n}\n\n/**\n * Resolves this session's composition from the selection that is live now.\n *\n * The major mode and layers come from harness state rather than the record, so\n * a `/mode` switch that persisted its selection before reloading lands on the\n * new composition. Where the synced path can rely on sync having precompiled\n * every combination, a launcher session may be asking for one that was never\n * built; the individual entries are the answer then, and the aggregate is an\n * optimisation applied when it happens to exist.\n */\nexport function resolveLauncherLoadPlan(\n  state: LauncherCompositionState,\n  harness = getHarnessState(),\n  exists: (file: string) => boolean = fs.existsSync,\n): LauncherLoadPlan {\n  const repoRoot = harness.root ?? state.root;\n  const resolvers = createLayerResolvers(repoRoot);\n  const composition = resolveExtensionComposition({\n    agents: state.agents,\n    autoStop: state.autoStop,\n    mute: state.mute,\n    preset: state.preset,\n    personaEntry: resolvers.packageEntry(PERSONA_ENTRY),\n    majorMode: harness.majorMode,\n    layers: [...harness.layers],\n    majorModesConfig: loadMajorModesConfig(repoRoot),\n    resolvers,\n  });\n  const bundle = state.bundles[composition.fingerprint];\n  return {\n    entries: bundle && exists(bundle) ? [bundle] : [...composition.parentActivation],\n    fingerprint: composition.fingerprint,\n    childExtensions: [...composition.childActivation],\n  };\n}\n"],"mappings":";;;;;;;;;AAUA,SAAS,SAAS,OAAkD;CAClE,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;AAEA,SAAS,aAAa,OAAwC;CAC5D,IAAI,CAAC,SAAS,KAAK,GAAG,OAAO,CAAC;CAC9B,OAAO,OAAO,YACZ,OAAO,QAAQ,KAAK,CAAC,CAAC,QAAQ,UAAqC,OAAO,MAAM,OAAO,QAAQ,CACjG;AACF;;AAGA,SAAgB,yBAAyB,QAAgB,OAAuC;CAC9F,CAAA,GAAA,oCAAA,gBAAA,CAAgB,QAAQ,GAAG,KAAK,UAAU,OAAO,MAAM,CAAC,EAAE,GAAG;AAC/D;;;;;;;;AASA,SAAgB,wBACd,cAAiC,QAAQ,KACH;CACtC,MAAM,SAAS,YAAYA,kBAAAA;CAC3B,IAAI,CAAC,QAAQ,OAAO,KAAA;CACpB,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAMC,QAAAA,QAAG,aAAa,QAAQ,MAAM,CAAC;CACrD,QAAQ;EAGN;CACF;CACA,IAAI,CAAC,SAAS,MAAM,KAAK,OAAO,YAAA,KAA4C,OAAO,OAAO,SAAS,UACjG;CAEF,OAAO;EACL,SAAA;EACA,MAAM,OAAO;EACb,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,SAAS;EAC5D,MAAM,OAAO,SAAS;EACtB,UAAU,OAAO,aAAa;EAC9B,QAAQ,OAAO,WAAW;EAC1B,SAAS,aAAa,OAAO,OAAO;CACtC;AACF;;;;;;;;;;;AAmBA,SAAgB,wBACd,OACA,UAAUC,cAAAA,gBAAgB,GAC1B,SAAoCD,QAAAA,QAAG,YACrB;CAClB,MAAM,WAAW,QAAQ,QAAQ,MAAM;CACvC,MAAM,YAAYE,8CAAAA,qBAAqB,QAAQ;CAC/C,MAAM,cAAcC,8CAAAA,4BAA4B;EAC9C,QAAQ,MAAM;EACd,UAAU,MAAM;EAChB,MAAM,MAAM;EACZ,QAAQ,MAAM;EACd,cAAc,UAAU,aAAaC,8CAAAA,aAAa;EAClD,WAAW,QAAQ;EACnB,QAAQ,CAAC,GAAG,QAAQ,MAAM;EAC1B,mBAAA,GAAkBC,oCAAAA,qBAAAA,CAAqB,QAAQ;EAC/C;CACF,CAAC;CACD,MAAM,SAAS,MAAM,QAAQ,YAAY;CACzC,OAAO;EACL,SAAS,UAAU,OAAO,MAAM,IAAI,CAAC,MAAM,IAAI,CAAC,GAAG,YAAY,gBAAgB;EAC/E,aAAa,YAAY;EACzB,iBAAiB,CAAC,GAAG,YAAY,eAAe;CAClD;AACF"}