{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/cli/launchPlan/index.ts"],"sourcesContent":["import { randomUUID } from 'node:crypto';\n\nimport { DOOMPI_EXTENSIONS_PROVIDED_ENV } from '@agimon-ai/doompi-core/child-process';\nimport { HARNESS_EVENT, type HarnessTelemetry } from '@agimon-ai/doompi-core/runtime-log-sink-telemetry';\n\nimport { updateHarnessState } from '../../../composition/harnessState';\nimport { applyProjectTrust, hasProjectTrustOption, loadDoomConfig } from '../../../composition/projectTrust';\nimport type { DoomConfig } from '../../../composition/projectTrust';\nimport { LAUNCHER_COMPOSITION_ENV, LAUNCHER_COMPOSITION_VERSION } from '../constants';\nimport { ownEntry } from '../entryResolution';\nimport type { HarnessContext } from '../harnessContext';\nimport { writeLauncherComposition } from '../launcherComposition';\n\nconst LAUNCHER_BOOTSTRAP_ENTRY = 'launcherBootstrap';\n\nconst EXTENSION_OPTION = '--extension';\nconst THEME_OPTION = '--theme';\nconst NO_THEMES_OPTION = '--no-themes';\nconst ENABLED_ENV = '1';\nconst ELICITATION_SESSION_ENV = 'ELICITATION_SESSION_ID';\n\n/** Gives each Doompi launch a stable fallback identity without replacing one supplied by its caller. */\nexport function ensureElicitationSessionId(environment: NodeJS.ProcessEnv): string {\n  const provided = environment[ELICITATION_SESSION_ENV]?.trim();\n  const sessionId = provided || randomUUID();\n  environment[ELICITATION_SESSION_ENV] = sessionId;\n  return sessionId;\n}\n\n/** Ignores configured themes and loads only themes explicitly passed to the launcher. */\nexport function overridePiThemes(piArgs: string[], themePath: string): string[] {\n  return [NO_THEMES_OPTION, THEME_OPTION, themePath, ...piArgs];\n}\n\nexport interface ResolveLaunchPlanOptions {\n  /**\n   * Where to record this session's composition, enabling reload-time recomposition.\n   *\n   * Pi is then given one stable entry instead of the fingerprint-named\n   * aggregate, so a `/mode` switch can reload in place rather than requiring a\n   * new process. Omit it to hand Pi the aggregate directly, which is the\n   * historical behaviour and freezes the composition for the process.\n   */\n  compositionRecordPath?: string;\n}\n\nexport interface PiLaunchPlan {\n  /** Complete argument vector for Pi, extension activation first. */\n  piArgs: string[];\n  /** Environment the Pi process must run with. */\n  environment: NodeJS.ProcessEnv;\n  /** The compiled aggregate, or undefined when compilation fell back to individual entries. */\n  bundle?: string;\n  /** Canonical composition identity for this launch. */\n  fingerprint: string;\n  /** Entries a detached child agent inherits. */\n  childExtensions: string[];\n  /** Extension entries in Pi factory activation order. */\n  extensions: string[];\n}\n\n/**\n * Resolves everything Pi needs to start, short of spawning it.\n *\n * The launcher and the session server both need this exact sequence, and a\n * second copy would drift the moment either gains a step. Spawning is left to\n * the caller because that is the only part they disagree on: the launcher\n * inherits a terminal, the server owns pipes.\n */\nexport async function resolveLaunchPlan(\n  context: HarnessContext,\n  telemetry: HarnessTelemetry,\n  planOptions: ResolveLaunchPlanOptions = {},\n): Promise<PiLaunchPlan> {\n  const { options, environment } = context;\n  ensureElicitationSessionId(environment);\n\n  const { buildRuntimeBundle, createRuntimeExtensionPlan } = await import('../runtimeBundle');\n  const extensionPlan = createRuntimeExtensionPlan(context);\n  const { extensions, childExtensions } = extensionPlan;\n  updateHarnessState(\n    {\n      childExtensions,\n      compositionFingerprint: extensionPlan.fingerprint,\n    },\n    environment,\n  );\n\n  // Pi normally imports each package entry separately. Flattening the exact\n  // Pi load order into one graph removes repeated module resolution and parse\n  // work. The graph manifest makes the common path a stat-only cache hit;\n  // compilation failure keeps the historical extension list intact.\n  let launchExtensions = extensions;\n  let bundle: string | undefined;\n  try {\n    const built = await buildRuntimeBundle(context, extensionPlan);\n    launchExtensions = [built.bundle];\n    bundle = built.bundle;\n    // A synced repository also has the doom wrapper in project settings. It\n    // must not compose the same factories after this aggregate has run.\n    environment[DOOMPI_EXTENSIONS_PROVIDED_ENV] = ENABLED_ENV;\n  } catch (error) {\n    const detail = error instanceof Error ? error.message : String(error);\n    process.stderr.write(`[doompi] extension bundle unavailable; using individual entries: ${detail}\\n`);\n  }\n\n  // Recording the composition swaps the fingerprint-named aggregate for one\n  // stable entry that resolves the selection every time it loads, which is\n  // what lets a mode switch reload instead of taking a new process. The\n  // aggregate is still built: it becomes the fast path this entry looks up.\n  if (planOptions.compositionRecordPath !== undefined) {\n    writeLauncherComposition(planOptions.compositionRecordPath, {\n      version: LAUNCHER_COMPOSITION_VERSION,\n      root: options.repoRoot,\n      preset: options.preset,\n      mute: options.mute,\n      autoStop: options.autoStop,\n      agents: options.agents,\n      ...(bundle === undefined ? { bundles: {} } : { bundles: { [extensionPlan.fingerprint]: bundle } }),\n    });\n    environment[LAUNCHER_COMPOSITION_ENV] = planOptions.compositionRecordPath;\n    environment[DOOMPI_EXTENSIONS_PROVIDED_ENV] = ENABLED_ENV;\n    launchExtensions = [ownEntry(LAUNCHER_BOOTSTRAP_ENTRY)];\n  }\n\n  // A malformed .doom/config.yaml aborts the launch, and the message only\n  // reaches the terminal that ran it, so record it before it propagates.\n  let doomConfig: DoomConfig;\n  try {\n    doomConfig = loadDoomConfig(options.repoRoot);\n  } catch (error) {\n    await telemetry.recordError(HARNESS_EVENT.configLoadFailed, error, { 'harness.phase': 'load_config' });\n    throw error;\n  }\n\n  // Whether a session ran with repository approval is the single most useful\n  // fact when reconstructing what an agent was allowed to do.\n  void telemetry.recordEvent(HARNESS_EVENT.projectTrustResolved, {\n    'harness.project_trust': doomConfig.projectTrust,\n    'harness.project_trust.overridden': hasProjectTrustOption(options.piArgs),\n  });\n\n  let piArgs = applyProjectTrust(options.piArgs, doomConfig);\n  piArgs = overridePiThemes(piArgs, context.defaultThemePath);\n  // launchExtensions is canonical factory activation order. Keep CLI argument\n  // adaptation here rather than leaking reversal rules into composition.\n  piArgs = [...launchExtensions.flatMap((extension) => [EXTENSION_OPTION, extension]), ...piArgs];\n\n  return {\n    piArgs,\n    environment,\n    bundle,\n    fingerprint: extensionPlan.fingerprint,\n    childExtensions,\n    extensions: launchExtensions,\n  };\n}\n"],"mappings":";;;;;;;;;AAaA,MAAM,2BAA2B;AAEjC,MAAM,mBAAmB;AACzB,MAAM,eAAe;AACrB,MAAM,mBAAmB;AACzB,MAAM,cAAc;AACpB,MAAM,0BAA0B;;AAGhC,SAAgB,2BAA2B,aAAwC;CAEjF,MAAM,YADW,YAAY,wBAAwB,EAAE,KAAK,KAC9B,WAAW;CACzC,YAAY,2BAA2B;CACvC,OAAO;AACT;;AAGA,SAAgB,iBAAiB,QAAkB,WAA6B;CAC9E,OAAO;EAAC;EAAkB;EAAc;EAAW,GAAG;CAAM;AAC9D;;;;;;;;;AAqCA,eAAsB,kBACpB,SACA,WACA,cAAwC,CAAC,GAClB;CACvB,MAAM,EAAE,SAAS,gBAAgB;CACjC,2BAA2B,WAAW;CAEtC,MAAM,EAAE,oBAAoB,+BAA+B,MAAM,OAAO;CACxE,MAAM,gBAAgB,2BAA2B,OAAO;CACxD,MAAM,EAAE,YAAY,oBAAoB;CACxC,mBACE;EACE;EACA,wBAAwB,cAAc;CACxC,GACA,WACF;CAMA,IAAI,mBAAmB;CACvB,IAAI;CACJ,IAAI;EACF,MAAM,QAAQ,MAAM,mBAAmB,SAAS,aAAa;EAC7D,mBAAmB,CAAC,MAAM,MAAM;EAChC,SAAS,MAAM;EAGf,YAAY,kCAAkC;CAChD,SAAS,OAAO;EACd,MAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;EACpE,QAAQ,OAAO,MAAM,oEAAoE,OAAO,GAAG;CACrG;CAMA,IAAI,YAAY,0BAA0B,KAAA,GAAW;EACnD,yBAAyB,YAAY,uBAAuB;GAC1D,SAAA;GACA,MAAM,QAAQ;GACd,QAAQ,QAAQ;GAChB,MAAM,QAAQ;GACd,UAAU,QAAQ;GAClB,QAAQ,QAAQ;GAChB,GAAI,WAAW,KAAA,IAAY,EAAE,SAAS,CAAC,EAAE,IAAI,EAAE,SAAS,GAAG,cAAc,cAAc,OAAO,EAAE;EAClG,CAAC;EACD,YAAY,4BAA4B,YAAY;EACpD,YAAY,kCAAkC;EAC9C,mBAAmB,CAAC,SAAS,wBAAwB,CAAC;CACxD;CAIA,IAAI;CACJ,IAAI;EACF,aAAa,eAAe,QAAQ,QAAQ;CAC9C,SAAS,OAAO;EACd,MAAM,UAAU,YAAY,cAAc,kBAAkB,OAAO,EAAE,iBAAiB,cAAc,CAAC;EACrG,MAAM;CACR;CAIA,UAAe,YAAY,cAAc,sBAAsB;EAC7D,yBAAyB,WAAW;EACpC,oCAAoC,sBAAsB,QAAQ,MAAM;CAC1E,CAAC;CAED,IAAI,SAAS,kBAAkB,QAAQ,QAAQ,UAAU;CACzD,SAAS,iBAAiB,QAAQ,QAAQ,gBAAgB;CAG1D,SAAS,CAAC,GAAG,iBAAiB,SAAS,cAAc,CAAC,kBAAkB,SAAS,CAAC,GAAG,GAAG,MAAM;CAE9F,OAAO;EACL;EACA;EACA;EACA,aAAa,cAAc;EAC3B;EACA,YAAY;CACd;AACF"}