Class: LittleGhost::PromptResolver

Inherits:
Object
  • Object
show all
Defined in:
lib/little_ghost/prompt_resolver.rb

Overview

PromptResolver turns conventional ERB files into an agent's system prompt. It supports ordered application roots and partials without allowing a template name to escape those roots.

resolver = LittleGhost::PromptResolver.new(paths: ["app/prompts"])
prompt = resolver.render("support/system", locals: {product: "Acme"})
prompt.include?("Acme") # => true

In support/system.erb:

<%= partial "shared/rules", locals: {product: product} %>

Earlier invocation roots override configured roots. Template names must be relative, and both lexical traversal and symbolic-link escapes are rejected. Partials use an underscore-prefixed filename and receive only their explicitly supplied locals.

Every configured root is trusted Ruby code because ERB executes inside the current process. Keep roots application-controlled and non-user-writable.

Defined Under Namespace

Classes: RenderContext

Constant Summary collapse

DEFAULT_MAX_DEPTH =

:nodoc:

20

Instance Method Summary collapse

Constructor Details

#initialize(paths: [], max_depth: DEFAULT_MAX_DEPTH) ⇒ PromptResolver

Configures ordered application roots and a partial recursion bound, which defaults to 20 nested templates.

Raises:

  • (ArgumentError)


79
80
81
82
83
84
85
86
# File 'lib/little_ghost/prompt_resolver.rb', line 79

def initialize(paths: [], max_depth: DEFAULT_MAX_DEPTH)
  @paths = normalize_roots(paths)
  @max_depth = Integer(max_depth)
  raise ArgumentError, "max_depth must be positive" unless @max_depth.positive?

  @cache = {}
  @cache_mutex = Mutex.new
end

Instance Method Details

#render(name, locals: {}, invocation_paths: []) ⇒ Object

Renders name with validated local variables.

invocation_paths accepts only TrustedPath values because those roots take precedence over application configuration. The wrapper records the caller's trust decision; it does not make an untrusted directory safe.



93
94
95
96
# File 'lib/little_ghost/prompt_resolver.rb', line 93

def render(name, locals: {}, invocation_paths: [])
  roots = normalize_invocation_roots(invocation_paths) + @paths
  render_template(normalize_name(name), locals, roots, [])
end