# `PhoenixKitAgent.Prompt`
[🔗](https://github.com/BeamLabEU/phoenix_kit_agent/blob/v0.1.0/lib/phoenix_kit_agent/prompt.ex#L1)

The system prompt of a turn (spec §7.1), put together in the order of
priority, each part separated by a blank line:

  1. the module's prompt (`agent_system_prompt`, `PhoenixKitAgent.Config`),
     then the module's own rule for writes, always there: a tool that
     changes data only proposes the change, which waits for the user's
     confirmation in the chat, and the model never says it is done before a
     confirmation message says so, nor asks again for a change still
     waiting (`PhoenixKitAgent.Confirmations`); then, when the AI guard
     has the `agent.chat` task flag off-purpose requests
     (`PhoenixKitAI.Guard.self_flag?/1`), the guard's instruction
     (`PhoenixKitAI.Guard.self_flag_instruction/1`) and how to flag: the
     marker `⟦off_purpose: <reason>⟧` at the very end of the answer, which
     the turn cuts off (`PhoenixKitAgent.Turns.Runner`). It stands above
     everything the admin and the user write, so their instructions cannot
     undo it, and above the date, so the prompt's cached prefix holds;
  2. the host's addendum — `config :phoenix_kit_agent, prompt_addendum:
     {Mod, :fun}`: what an order is, the statuses, the language of the
     answer. `Mod.fun(ctx)` is called with `%{user_uuid:, locale:}` when it
     is exported with one argument, `Mod.fun()` otherwise; the result is a
     string or `nil`. An exception, an exit, a result that is not a string
     or a function that does not exist skips the addendum with a warning
     that names the reason only — never the content;
  3. the admin's prompt for this user (`UserSettings.system_prompt`);
  4. the user's personal instructions (`personal_instructions`), under a line
     that says they rank below the parts above and never override them;
  5. the context of the session: the user's name (never the e-mail), the
     role the session acts as, the locale, the date in the site time zone
     (not the time of day: the system message opens every request, and a
     prefix that changes by the minute defeats the providers' prompt
     caching), the page's path when the host passed it to
     `PhoenixKitAgent.Turns.start/6`, and what the host says of that page
     (below).

The page and the locale come from a browser, so they are not trusted: a
value with a control or line-separator character, or one that is not shaped
like a path / a locale, is left out whole; a long one is cut. The page is
reduced to its path (no query, no fragment: those may hold what the user
typed into a search).

## The host's page context

`config :phoenix_kit_agent, page_context: {Mod, :fun}` lets the host say
what the page is — the number of the order open there, say (spec §7.1 point
5). With a page path, `Mod.fun(path, scope)` is called: `path` is the
page's path as above, `scope` the scope of the user's **browser session**
(`PhoenixKit.Users.Auth.SessionRef.scope_for_session/2`: the role the
session acts as), not one built from the user alone. The host decides what
to tell, and tells only what that scope may see. The result is a string —
put on one line (control and line-separator characters become spaces),
trimmed and cut to 200 characters — or `nil`. An exception, an exit, a
result that is not a string (or not valid UTF-8) or a function that does not
exist skips it with a warning that names the reason only — never the path
or the content. No hook, or no page: the context has the path only.

The hook is called as the turn starts, in the process that starts it (the
chat panel's), while the turn holds its locks, and with no timeout of its
own: keep it fast — one indexed lookup, not a report. Until it returns, the
turn has not started and the chat and the user stay busy.

# `ctx`

```elixir
@type ctx() :: %{
  :user_uuid =&gt; String.t(),
  :scope =&gt; PhoenixKit.Users.Auth.Scope.t(),
  optional(:settings) =&gt; PhoenixKitAgent.UserConfig.effective(),
  optional(:locale) =&gt; String.t() | nil,
  optional(:page) =&gt; page() | nil,
  optional(:now) =&gt; DateTime.t()
}
```

# `page`

```elixir
@type page() :: String.t()
```

The page the user is on, as the browser says it is: a path.

# `build`

```elixir
@spec build(ctx()) :: String.t()
```

The system prompt for `ctx` (see the moduledoc); never empty.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
