Extension-facing runtime/query surfaces and operational notes.
Extensions are Clojure namespaces loaded at runtime. Each extension receives an API map with:
register-tool!)query)dialogs, widgets, status, notifications, renderers)Extensions can also call shared library namespaces directly when they need
common deterministic behavior that should not be reimplemented per extension.
Current example: psi.ai.model-selection for role/policy-based model choice.
For workflow-backed extensions, prefer projecting reusable display/read-model
data from :public-data-fn rather than formatting separately in every widget
or command consumer.
Preferred display-map keys:
:top-line:detail-line:question-lines:action-lineStore that map under an extension-specific public key such as :run/display,
:chain/display, or :agent/display, then let consumers merge/render that
public surface via shared helpers such as extensions.workflow-display.
Preferred helper usage:
extensions.workflow-display/merged-display + display-linesextensions.workflow-display/text-lines over the rendered workflow linesInspect provider selection/fallback + failure telemetry via EQL:
[:psi.memory.store/active-provider-id
:psi.memory.store/selection
:psi.memory.store/health
:psi.memory.store/active-provider-telemetry
:psi.memory.store/last-failure
:psi.memory.store/providers]
Telemetry fields (per provider map):
:telemetry :write-count:telemetry :read-count:telemetry :failure-count:telemetry :last-errorOperational notes:
:psi.memory.store/selection (:used-fallback, :reason).snapshots, deltas): newest N kept, oldest trimmed.When an extension needs to inspect or mutate a specific session rather than the ambient runtime session, prefer explicit session-targeted helpers:
(:query-session api) session-id eql-query(:mutate-session api) session-id op-sym paramsThis is the recommended pattern for delayed/background extension work. It keeps session routing explicit and avoids relying on implicit adapter focus.
For slash-command handlers invoked through the shared command dispatcher, implicit
(:query api) / (:mutate api) calls are now rebound to the active invoking
session during handler execution. That implicit routing is intended for immediate
command handling only; cross-session and deferred/background work should still use
query-session / mutate-session explicitly.
Example:
(let [model-ctx ((:query-session api) source-session-id
[:psi.agent-session/model-provider
:psi.agent-session/model-id])]
((:mutate-session api) child-session-id 'psi.extension/run-agent-loop-in-session
{:prompt "..."
:model {:provider (keyword (:psi.agent-session/model-provider model-ctx))
:id (:psi.agent-session/model-id model-ctx)}}))
Extensions can create targeted helper/background child sessions with:
psi.extension/create-child-sessionpsi.extension/run-agent-loop-in-sessioncreate-child-session accepts the existing child runtime controls such as:
:session-name:system-prompt:tool-defs:thinking-level:preloaded-messages:cache-breakpointsIt also now accepts prompt-shaping controls for reduced helper runs:
:prompt-component-selection
:agents-md? — include discovered AGENTS.md / context-file content when true:extension-prompt-contributions — allowlist of extension prompt-contribution owners; [] means none:tool-names — caller-declared tool selection metadata for the helper run:skill-names — caller-declared skill selection metadata for the helper run:components — standard prompt-component set, currently including :preamble, :context-files, :skills, :runtime-metadata:system-prompt
Behavior note:
Current reference example:
auto-session-name creates a helper child with no AGENTS/context prompt content, no extension prompt contributions, no tool defs, no skill prelude content, and one minimal naming-specific system promptExtensions that need to choose a model for helper/background work should prefer
psi.ai.model-selection over per-extension fallback chains.
Current shared entrypoint:
psi.ai.model-selection/resolve-selectionTypical usage:
(let [model-ctx ((:query-session api) source-session-id
[:psi.agent-session/model-provider
:psi.agent-session/model-id])
request {:mode :resolve
:required [{:criterion :supports-text
:match :true}]
:strong-preferences [{:criterion :input-cost
:prefer :lower}]
:weak-preferences [{:criterion :same-provider-as-session
:prefer :context-match}]
:context {:session-model {:provider (some-> (:psi.agent-session/model-provider model-ctx)
keyword)
:id (:psi.agent-session/model-id model-ctx)}}}
selection (psi.ai.model-selection/resolve-selection
{:request request})]
(when (= :ok (:outcome selection))
(:candidate selection)))
Current request shape:
{:request {:mode :resolve | :explicit | :inherit-session
:role keyword?
:required [...]
:strong-preferences [...]
:weak-preferences [...]
:context {...}
:model {:provider :openai :id "gpt-5"}}}
Current result shape:
{:outcome :ok | :no-winner
:candidate candidate?
:ambiguous? boolean?
:reason keyword?
:effective-request {...}
:filtering {...}
:ranking {...}
:trace {:short {...}
:full {...}}}
Guidance:
:no-winner as a first-class outcome:trace when an extension needs explainability/debug outputCan you improve this documentation? These fine people already did:
Hugo Duncan & Test AuthorEdit on GitHub
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |