{"version":3,"file":"pi.cjs","names":["AUTO_STOP_EVENT","decideOnRecheck","AUTO_STOP_ACTION","decideOnSettled","defineRoot","createBackgroundWorkGate","createDoomTelemetry","DEFAULT_AUTO_STOP_DELAYS","defineCliHook","defineCliHook","defineCliHook","definePiExtension","rootSession","composeRootHooks","hookAgentSettled","hookAgentStart","hookInput"],"sources":["../../src/services/autostopDiagnostics/index.ts","../../src/services/idleShutdown/index.ts","../../src/extensions/workspaces/sessions/(backend)/root.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/agent-start.cli.ts","../../src/extensions/workspaces/sessions/(backend)/hook/input.cli.ts","../../generated/pi.ts"],"sourcesContent":["import {\n  AUTO_STOP_DETAIL_MAX_LENGTH,\n  AUTO_STOP_EVENT,\n  AUTO_STOP_REPORT_INTERVAL_MS,\n  AUTO_STOP_TELEMETRY_FLUSH_MS,\n} from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics, AutostopDiagnosticsOptions, AutostopEventAttributes } from './type';\n\nfunction detail(values: readonly string[]): string {\n  const joined = values.join(', ');\n  return joined.length > AUTO_STOP_DETAIL_MAX_LENGTH ? `${joined.slice(0, AUTO_STOP_DETAIL_MAX_LENGTH - 1)}…` : joined;\n}\n\n/**\n * Report what an idle session is waiting on before it stops.\n *\n * The first wait is reported at once and a long one again each interval, so a\n * session held open for an hour names the work holding it without flooding\n * the sink every cooldown.\n */\nexport function createAutostopDiagnostics(options: AutostopDiagnosticsOptions): AutostopDiagnostics {\n  const reportIntervalMs = options.reportIntervalMs ?? AUTO_STOP_REPORT_INTERVAL_MS;\n  const flushTimeoutMs = options.flushTimeoutMs ?? AUTO_STOP_TELEMETRY_FLUSH_MS;\n  let waitStartedAt: number | undefined;\n  let lastReportedAt: number | undefined;\n  let deferrals = 0;\n\n  const record = (event: string, attributes: AutostopEventAttributes): void => {\n    // Diagnostics must never change whether the session stops.\n    options.telemetry.recordEvent(event, attributes).catch(() => undefined);\n  };\n  const waitAttributes = (now: number): AutostopEventAttributes => ({\n    deferral_count: deferrals,\n    waited_ms: waitStartedAt === undefined ? 0 : now - waitStartedAt,\n  });\n  const reset = (): void => {\n    waitStartedAt = undefined;\n    lastReportedAt = undefined;\n    deferrals = 0;\n  };\n\n  return {\n    waiting(work) {\n      const now = options.now();\n      waitStartedAt ??= now;\n      deferrals += 1;\n      if (lastReportedAt !== undefined && now - lastReportedAt < reportIntervalMs) return;\n      lastReportedAt = now;\n      record(AUTO_STOP_EVENT.waitingOnBackgroundWork, {\n        ...waitAttributes(now),\n        item_count: work.items.length,\n        error_count: work.errors.length,\n        items: detail(work.items),\n        errors: detail(work.errors),\n      });\n    },\n    stoodDown() {\n      record(AUTO_STOP_EVENT.stoodDown, { ...waitAttributes(options.now()), reason: 'pending_messages' });\n      reset();\n    },\n    shutdownRequested() {\n      record(AUTO_STOP_EVENT.shutdownRequested, waitAttributes(options.now()));\n      reset();\n    },\n    reset,\n    async dispose() {\n      let timer: ReturnType<typeof setTimeout> | undefined;\n      await Promise.race([\n        options.telemetry.shutdown().catch(() => undefined),\n        new Promise((settle) => {\n          timer = setTimeout(settle, flushTimeoutMs);\n          timer.unref?.();\n        }),\n      ]);\n      if (timer) clearTimeout(timer);\n    },\n  };\n}\n","import type { ExtensionContext } from '@earendil-works/pi-coding-agent';\n\nimport { AUTO_STOP_ACTION } from '../../constants/idlePolicy';\nimport type { AutostopDiagnostics } from '../autostopDiagnostics/type';\nimport type { BackgroundWorkSummary } from '../backgroundWorkGate';\nimport { decideOnRecheck, decideOnSettled } from '../idlePolicy';\nimport type { AutoStopDelays, SessionActivity } from '../idlePolicy/type';\nimport type { IdleShutdown } from './type';\n\nconst NO_WORK: BackgroundWorkSummary = Object.freeze({ active: false, items: [], errors: [] });\n\nfunction readActivity(context: ExtensionContext): SessionActivity {\n  return { hasPendingMessages: context.hasPendingMessages(), isIdle: context.isIdle() };\n}\nexport function createIdleShutdown(\n  delays: AutoStopDelays,\n  inspectBackgroundWork: (context: ExtensionContext) => BackgroundWorkSummary = () => NO_WORK,\n  diagnostics?: Pick<AutostopDiagnostics, 'waiting' | 'stoodDown' | 'shutdownRequested' | 'reset'>,\n): IdleShutdown {\n  let shutdownTimer: NodeJS.Timeout | undefined;\n\n  const cancelScheduledShutdown = (): void => {\n    diagnostics?.reset();\n    if (!shutdownTimer) return;\n    clearTimeout(shutdownTimer);\n    shutdownTimer = undefined;\n  };\n\n  const scheduleShutdown = (context: ExtensionContext, delayMs: number): void => {\n    shutdownTimer = setTimeout(() => {\n      shutdownTimer = undefined;\n      const work = inspectBackgroundWork(context);\n      if (work.active) {\n        diagnostics?.waiting(work);\n        scheduleShutdown(context, delays.cooldownMs);\n        return;\n      }\n      const decision = decideOnRecheck(readActivity(context), delays);\n      if (decision.action === AUTO_STOP_ACTION.shutdown) {\n        diagnostics?.shutdownRequested();\n        context.shutdown();\n        return;\n      }\n      if (decision.action === AUTO_STOP_ACTION.recheck) scheduleShutdown(context, decision.delayMs);\n      else diagnostics?.stoodDown();\n    }, delayMs);\n  };\n\n  return {\n    cancel: cancelScheduledShutdown,\n    settled(context) {\n      cancelScheduledShutdown();\n      const decision = decideOnSettled(readActivity(context), delays);\n      if (decision.action === AUTO_STOP_ACTION.recheck) scheduleShutdown(context, decision.delayMs);\n      else diagnostics?.stoodDown();\n    },\n  };\n}\n","import { defineRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\nimport { createDoomTelemetry } from '@agimon-ai/doompi-telemetry';\n\nimport { DEFAULT_AUTO_STOP_DELAYS } from '../../../../constants/idlePolicy';\nimport { createAutostopDiagnostics } from '../../../../services/autostopDiagnostics';\nimport { createBackgroundWorkGate } from '../../../../services/backgroundWorkGate';\nimport type { AutoStopDelays } from '../../../../services/idlePolicy/type';\nimport { createIdleShutdown } from '../../../../services/idleShutdown';\nexport default defineRoot(({ options }: PiPluginContext<AutoStopDelays>) => {\n  const backgroundWork = createBackgroundWorkGate();\n  const diagnostics = createAutostopDiagnostics({\n    telemetry: createDoomTelemetry({\n      serviceName: 'doom-autostop',\n      packageName: '@agimon-ai/doompi-autostop',\n      enableLogs: true,\n      enableTraces: false,\n    }),\n    now: Date.now,\n  });\n  const watch = createIdleShutdown(\n    options ?? DEFAULT_AUTO_STOP_DELAYS,\n    (context) => backgroundWork.inspect(context.sessionManager.getSessionId()),\n    diagnostics,\n  );\n  return {\n    value: watch,\n    services: [backgroundWork.plugin],\n    onDispose: async () => {\n      watch.cancel();\n      await diagnostics.dispose();\n    },\n  };\n});\n","import { defineCliHook, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\ntype AutoStopPiScope = Awaited<ReturnType<typeof import('../root.cli').default>>['value'];\nexport default defineCliHook(\n  (context: WithRoot<PiPluginContext<unknown>, AutoStopPiScope>) =>\n    (_event: unknown, execution: Parameters<AutoStopPiScope['settled']>[0]) =>\n      context.root.settled(execution),\n);\n","import { defineCliHook, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\ntype AutoStopPiScope = Awaited<ReturnType<typeof import('../root.cli').default>>['value'];\n\nexport default defineCliHook((context: WithRoot<PiPluginContext<unknown>, AutoStopPiScope>) => context.root.cancel);\n","import { defineCliHook, type WithRoot } from '@agimon-ai/doompi-core/extensionFile';\nimport type { PiPluginContext } from '@agimon-ai/doompi-core/piExtension';\n\ntype AutoStopPiScope = Awaited<ReturnType<typeof import('../root.cli').default>>['value'];\n\nexport default defineCliHook((context: WithRoot<PiPluginContext<unknown>, AutoStopPiScope>) => context.root.cancel);\n","// Generated by @agimon-ai/doompi-build. Do not edit by hand.\nimport { definePiExtension, type PiPluginContext, type PiPluginContributions } from '@agimon-ai/doompi-core/piExtension';\nimport { composeRootHooks } from '@agimon-ai/doompi-core/extensionFile';\n\nimport rootSession from '../src/extensions/workspaces/sessions/(backend)/root.cli';\nimport hookAgentSettled from '../src/extensions/workspaces/sessions/(backend)/hook/agent-settled.cli';\nimport hookAgentStart from '../src/extensions/workspaces/sessions/(backend)/hook/agent-start.cli';\nimport hookInput from '../src/extensions/workspaces/sessions/(backend)/hook/input.cli';\n\ntype OptionsOf<T> = T extends (context: PiPluginContext<infer Options>) => unknown ? Options : never;\ntype InferredOptions = Exclude<OptionsOf<typeof rootSession> | OptionsOf<typeof hookAgentSettled> | OptionsOf<typeof hookAgentStart> | OptionsOf<typeof hookInput>, undefined>;\ntype Intersect<Union> =\n  (Union extends unknown ? (value: Union) => void : never) extends (value: infer Value) => void ? Value : never;\ntype ExtensionOptions = [InferredOptions] extends [never] ? undefined : Intersect<InferredOptions>;\n\ntype Factory<T, C> = (context: C) => T;\nconst at = <T, C>(value: T | Factory<T, C>, context: C): T =>\n  typeof value === 'function' ? (value as Factory<T, C>)(context) : value;\n\nexport const extension = definePiExtension<ExtensionOptions>('@agimon-ai/doompi-autostop', async (context) => {\n  const scopeSession = await rootSession(context);\n  const contextSession = { ...context, root: scopeSession.value };\n  return {\n    ...composeRootHooks(scopeSession),\n    events: {\n      agent_settled: at(hookAgentSettled, contextSession) as NonNullable<PiPluginContributions['events']>['agent_settled'],\n      agent_start: at(hookAgentStart, contextSession) as NonNullable<PiPluginContributions['events']>['agent_start'],\n      input: at(hookInput, contextSession) as NonNullable<PiPluginContributions['events']>['input'],\n    },\n    services: [...(scopeSession.services ?? [])],\n  };\n});\n\nexport default extension;\n"],"mappings":"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"}