{"version":3,"file":"index.cjs","names":["resolveRootSessionId","readChildProcessContext","SUBAGENT_PARENT_SESSION_ENV","AsyncLocalStorage"],"sources":["../../../../src/services/workflowFence/index.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks';\n\nimport {\n  readChildProcessContext,\n  resolveRootSessionId,\n  SUBAGENT_PARENT_SESSION_ENV,\n} from '@agimon-ai/doompi-core/child-process';\nimport type { PiEventHandlers } from '@agimon-ai/doompi-core/pi-extension';\nimport type { DoomToolRestriction } from '@agimon-ai/doompi-core/tool-surface';\nimport type { ExtensionAPI, ExtensionContext, ToolDefinition } from '@earendil-works/pi-coding-agent';\n\nconst DISPATCHER_AGENT_NAME = 'agiflow-dispatcher';\nconst SESSION_START_EVENT = 'session_start';\nconst INACTIVE_RUNTIME_MESSAGE = 'The workflow runtime is no longer active.';\nconst CLEANUP_SCOPE = Symbol('doom-workflow-cleanup');\n\nconst DISPATCHER_TOOL_NAMES = new Set(['list_workflows', 'launch_workflow']);\nconst ROOT_SCOPED_TOOL_NAMES = new Set(['launch_workflow']);\nconst WORKFLOW_RUN_TOOL_NAME = 'workflow_run';\n\ntype ToolSchema = Parameters<ExtensionAPI['registerTool']>[0]['parameters'];\ntype CommandOptions = Parameters<ExtensionAPI['registerCommand']>[1];\ntype ShortcutOptions = Parameters<ExtensionAPI['registerShortcut']>[1];\ntype RuntimeScope = number | typeof CLEANUP_SCOPE;\ntype LifecycleHandler = (event: unknown, context: ExtensionContext) => unknown;\n\nexport interface WorkflowFence {\n  tool<TSchema extends ToolSchema, TDetails>(\n    tool: ToolDefinition<TSchema, TDetails>,\n  ): ToolDefinition<TSchema, TDetails>;\n  command(name: string, options: CommandOptions): readonly [string, CommandOptions];\n  shortcut(\n    shortcut: Parameters<ExtensionAPI['registerShortcut']>[0],\n    options: ShortcutOptions,\n  ): readonly [Parameters<ExtensionAPI['registerShortcut']>[0], ShortcutOptions];\n  events(handlers: PiEventHandlers): PiEventHandlers;\n  readonly pi: ExtensionAPI;\n  readonly beginDisposal: () => void;\n  readonly finishDisposal: () => void;\n  readonly isCurrentInvocation: () => boolean;\n  readonly runCleanup: <T>(operation: () => T) => T;\n}\n\nfunction boundValue(target: object, property: PropertyKey): unknown {\n  const value = Reflect.get(target, property, target);\n  return typeof value === 'function' ? value.bind(target) : value;\n}\n\nfunction withParentSession(ctx: ExtensionContext, parentSessionId: string): ExtensionContext {\n  const sessionManager = new Proxy(ctx.sessionManager, {\n    get(target, property) {\n      if (property === 'getSessionId') return () => parentSessionId;\n      return boundValue(target, property);\n    },\n  });\n  return new Proxy(ctx, {\n    get(target, property) {\n      if (property === 'sessionManager') return sessionManager;\n      return boundValue(target, property);\n    },\n  });\n}\n\n/**\n * A dispatcher child launches workflows for its root session and never runs one\n * itself, so `workflow_run` stays off its surface even if something registers it.\n */\nexport function dispatcherToolRestriction(): DoomToolRestriction {\n  return (incoming) => incoming.filter((name) => name !== WORKFLOW_RUN_TOOL_NAME);\n}\n\n/** Restrict a dispatcher child to discovery and root-owned launch. */\nexport function dispatcherTools<TSchema extends ToolSchema, TDetails>(\n  tools: readonly ToolDefinition<TSchema, TDetails>[],\n  parentSessionId: string | undefined,\n  environment: Readonly<Record<string, string | undefined>>,\n): ToolDefinition<TSchema, TDetails>[] {\n  const rootSessionId = parentSessionId ? resolveRootSessionId(parentSessionId, environment) : undefined;\n  return tools\n    .filter((tool) => DISPATCHER_TOOL_NAMES.has(tool.name))\n    .map((tool) => {\n      if (!rootSessionId || !ROOT_SCOPED_TOOL_NAMES.has(tool.name)) return tool;\n      return {\n        ...tool,\n        execute: (toolCallId, params, signal, onUpdate, ctx) =>\n          tool.execute(toolCallId, params, signal, onUpdate, withParentSession(ctx, rootSessionId)),\n      };\n    });\n}\n\nexport function resolveDispatcherParentSession(\n  environment: Readonly<Record<string, string | undefined>>,\n): string | undefined {\n  const childContext = readChildProcessContext(environment);\n  return childContext?.parentSessionId ?? (environment[SUBAGENT_PARENT_SESSION_ENV]?.trim() || undefined);\n}\n\nexport function isWorkflowDispatcherProcess(environment: Readonly<Record<string, string | undefined>>): boolean {\n  const childContext = readChildProcessContext(environment);\n  if (childContext?.mode === DISPATCHER_AGENT_NAME) return true;\n  return (\n    environment.PI_SUBAGENT_CHILD_AGENT === DISPATCHER_AGENT_NAME &&\n    Boolean(environment[SUBAGENT_PARENT_SESSION_ENV]?.trim())\n  );\n}\n\n/** Fence every callback retained by Pi after this package root is replaced. */\nexport function createWorkflowFence(host: ExtensionAPI, signal?: AbortSignal): WorkflowFence {\n  const scope = new AsyncLocalStorage<RuntimeScope>();\n  let active = true;\n  let disposing = false;\n  let generation = 0;\n  let lastSessionStartEvent: unknown;\n  let lastSessionStartContext: ExtensionContext | undefined;\n\n  const isCurrentInvocation = (): boolean => {\n    const currentScope = scope.getStore();\n    if (!active || (signal?.aborted && currentScope !== CLEANUP_SCOPE)) return false;\n    return currentScope === undefined || currentScope === CLEANUP_SCOPE || currentScope === generation;\n  };\n  const ownsContext = (contextGeneration: number): boolean => {\n    const currentScope = scope.getStore();\n    if (!active || (signal?.aborted && currentScope !== CLEANUP_SCOPE)) return false;\n    return (\n      currentScope === CLEANUP_SCOPE ||\n      (contextGeneration === generation && (currentScope === undefined || currentScope === contextGeneration))\n    );\n  };\n\n  const guardContext = <TContext extends ExtensionContext>(context: TContext, contextGeneration: number): TContext => {\n    const guardedUi = new Proxy(context.ui, {\n      get(target, property) {\n        const value = Reflect.get(target, property, target);\n        if (typeof value !== 'function') return value;\n        return (...args: unknown[]): unknown => {\n          if (ownsContext(contextGeneration)) return Reflect.apply(value, target, args);\n          if (property === 'confirm') return Promise.resolve(false);\n          if (property === 'custom' || property === 'editor' || property === 'input' || property === 'select') {\n            return Promise.resolve(undefined);\n          }\n          return undefined;\n        };\n      },\n    });\n    return new Proxy(context, {\n      get(target, property) {\n        if (property === 'ui') return guardedUi;\n        return boundValue(target, property);\n      },\n    }) as TContext;\n  };\n\n  const tool = <TSchema extends ToolSchema, TDetails>(\n    definition: ToolDefinition<TSchema, TDetails>,\n  ): ToolDefinition<TSchema, TDetails> => {\n    return {\n      ...definition,\n      execute: async (toolCallId, params, signal, onUpdate, context) => {\n        const executionGeneration = generation;\n        if (!active || disposing || signal?.aborted) throw new Error(INACTIVE_RUNTIME_MESSAGE);\n        const guardedContext = guardContext(context, executionGeneration);\n        const guardedUpdate = onUpdate\n          ? (update: Parameters<NonNullable<typeof onUpdate>>[0]): void => {\n              if (active && executionGeneration === generation && !signal?.aborted) onUpdate(update);\n            }\n          : undefined;\n        const result = await scope.run(executionGeneration, () =>\n          definition.execute(toolCallId, params, signal, guardedUpdate, guardedContext),\n        );\n        if (!active || disposing || executionGeneration !== generation) throw new Error(INACTIVE_RUNTIME_MESSAGE);\n        return result;\n      },\n    };\n  };\n\n  const command: WorkflowFence['command'] = (name, options) => {\n    return [\n      name,\n      {\n        ...options,\n        handler: async (args, context) => {\n          const executionGeneration = generation;\n          if (!active || disposing) return;\n          await scope.run(executionGeneration, () => options.handler(args, guardContext(context, executionGeneration)));\n        },\n      },\n    ];\n  };\n\n  const shortcut: WorkflowFence['shortcut'] = (key, options) => {\n    return [\n      key,\n      {\n        ...options,\n        handler: (context) => {\n          const executionGeneration = generation;\n          if (!active || disposing) return;\n          return scope.run(executionGeneration, () => options.handler(guardContext(context, executionGeneration)));\n        },\n      },\n    ];\n  };\n\n  const events = (handlers: PiEventHandlers): PiEventHandlers =>\n    Object.fromEntries(\n      Object.entries(handlers).map(([eventName, handler]) => [\n        eventName,\n        (event: unknown, context: ExtensionContext) => {\n          if (!active || disposing) return undefined;\n          if (\n            eventName === SESSION_START_EVENT &&\n            (event !== lastSessionStartEvent || context !== lastSessionStartContext)\n          ) {\n            generation += 1;\n            lastSessionStartEvent = event;\n            lastSessionStartContext = context;\n          }\n          const eventGeneration = generation;\n          return scope.run(eventGeneration, () =>\n            (handler as LifecycleHandler)(event, guardContext(context, eventGeneration)),\n          );\n        },\n      ]),\n    ) as PiEventHandlers;\n\n  const guardedPi = new Proxy(host, {\n    get(target, property) {\n      if (property === 'sendMessage') {\n        return (...args: Parameters<ExtensionAPI['sendMessage']>): void => {\n          if (isCurrentInvocation()) target.sendMessage(...args);\n        };\n      }\n      if (property === 'sendUserMessage') {\n        return (...args: Parameters<ExtensionAPI['sendUserMessage']>): void => {\n          if (isCurrentInvocation()) target.sendUserMessage(...args);\n        };\n      }\n      if (property === 'exec') {\n        return (...args: Parameters<ExtensionAPI['exec']>): ReturnType<ExtensionAPI['exec']> =>\n          isCurrentInvocation()\n            ? target.exec(...args)\n            : Promise.resolve({ code: 1, killed: false, stderr: INACTIVE_RUNTIME_MESSAGE, stdout: '' });\n      }\n      return boundValue(target, property);\n    },\n  });\n\n  return {\n    pi: guardedPi,\n    tool,\n    command,\n    shortcut,\n    events,\n    beginDisposal() {\n      if (disposing || !active) return;\n      disposing = true;\n      generation += 1;\n    },\n    finishDisposal() {\n      active = false;\n      disposing = false;\n      generation += 1;\n    },\n    isCurrentInvocation,\n    runCleanup: (operation) => scope.run(CLEANUP_SCOPE, operation),\n  };\n}\n"],"mappings":"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"}