jeevesagent.core
================

.. py:module:: jeevesagent.core

.. autoapi-nested-parse::

   Layer-free primitives: types, protocols, errors, IDs.



Submodules
----------

.. toctree::
   :maxdepth: 1

   /api/jeevesagent/core/context/index
   /api/jeevesagent/core/errors/index
   /api/jeevesagent/core/ids/index
   /api/jeevesagent/core/protocols/index
   /api/jeevesagent/core/types/index


Exceptions
----------

.. autoapisummary::

   jeevesagent.core.AuthenticationError
   jeevesagent.core.BudgetExceeded
   jeevesagent.core.CancelledByUser
   jeevesagent.core.ConfigError
   jeevesagent.core.ContentFilterError
   jeevesagent.core.FreshnessError
   jeevesagent.core.InvalidRequestError
   jeevesagent.core.IsolationWarning
   jeevesagent.core.JeevesAgentError
   jeevesagent.core.LineageError
   jeevesagent.core.MCPError
   jeevesagent.core.MemoryStoreError
   jeevesagent.core.ModelError
   jeevesagent.core.OutputValidationError
   jeevesagent.core.PermanentModelError
   jeevesagent.core.PermissionDenied
   jeevesagent.core.RateLimitError
   jeevesagent.core.RuntimeJournalError
   jeevesagent.core.SandboxError
   jeevesagent.core.ToolError
   jeevesagent.core.TransientModelError


Classes
-------

.. autoapisummary::

   jeevesagent.core.AuditEntry
   jeevesagent.core.Budget
   jeevesagent.core.BudgetStatus
   jeevesagent.core.CertifiedValue
   jeevesagent.core.Embedder
   jeevesagent.core.Episode
   jeevesagent.core.Event
   jeevesagent.core.EventKind
   jeevesagent.core.Fact
   jeevesagent.core.HookHost
   jeevesagent.core.Memory
   jeevesagent.core.MemoryBlock
   jeevesagent.core.Message
   jeevesagent.core.Model
   jeevesagent.core.ModelChunk
   jeevesagent.core.PermissionDecision
   jeevesagent.core.Permissions
   jeevesagent.core.Role
   jeevesagent.core.RunContext
   jeevesagent.core.RunResult
   jeevesagent.core.Runtime
   jeevesagent.core.RuntimeSession
   jeevesagent.core.Sandbox
   jeevesagent.core.Secrets
   jeevesagent.core.Span
   jeevesagent.core.Telemetry
   jeevesagent.core.ToolCall
   jeevesagent.core.ToolDef
   jeevesagent.core.ToolEvent
   jeevesagent.core.ToolHost
   jeevesagent.core.ToolResult
   jeevesagent.core.Usage
   jeevesagent.core.set_run_context


Functions
---------

.. autoapisummary::

   jeevesagent.core.deterministic_hash
   jeevesagent.core.get_run_context
   jeevesagent.core.new_id


Package Contents
----------------

.. py:exception:: AuthenticationError(message: str, *, cause: BaseException | None = None)

   Bases: :py:obj:`PermanentModelError`


   Invalid, missing, or revoked API credentials.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: BudgetExceeded(reason: str)

   Bases: :py:obj:`JeevesAgentError`


   A run was halted because a budget limit was hit.

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: reason


.. py:exception:: CancelledByUser

   Bases: :py:obj:`JeevesAgentError`


   A user-driven interruption (signal, timeout) ended the run.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: ConfigError

   Bases: :py:obj:`JeevesAgentError`


   Invalid or unresolvable configuration passed to ``Agent``.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: ContentFilterError(message: str, *, cause: BaseException | None = None)

   Bases: :py:obj:`PermanentModelError`


   The provider's safety system blocked the request or response.

   Typically a permanent failure for the same prompt; users may
   rephrase but the framework should not silently retry.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: FreshnessError

   Bases: :py:obj:`JeevesAgentError`


   A certified value failed its freshness policy.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: InvalidRequestError(message: str, *, cause: BaseException | None = None)

   Bases: :py:obj:`PermanentModelError`


   The request was malformed or violated the provider's API
   contract — bad parameters, oversized prompt, unknown model
   name, etc. Fix the request, don't retry.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: IsolationWarning

   Bases: :py:obj:`UserWarning`


   Emitted when a memory query is likely to silently miss data
   because the caller forgot to pass ``user_id``.

   Concrete trigger: a backend's ``recall`` / ``recall_facts`` runs
   with ``user_id=None`` against a store whose persisted records
   include at least one non-None ``user_id`` — the partition is
   safe (the anonymous bucket and named-user buckets are isolated),
   but the developer probably wired up multi-tenancy somewhere and
   forgot to pass ``user_id`` here, so they will see suspiciously
   empty recall results.

   Subclass of :class:`UserWarning` so it goes through Python's
   standard ``warnings`` filter machinery — apps can silence,
   promote-to-error, or log it however they want, e.g.::

       import warnings
       from jeevesagent import IsolationWarning
       warnings.simplefilter("error", IsolationWarning)  # raise on hit

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: JeevesAgentError

   Bases: :py:obj:`Exception`


   Base class for all harness errors.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: LineageError

   Bases: :py:obj:`JeevesAgentError`


   A certified value failed its lineage policy.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: MCPError

   Bases: :py:obj:`JeevesAgentError`


   An MCP transport, handshake, or protocol error.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: MemoryStoreError

   Bases: :py:obj:`JeevesAgentError`


   The memory backend failed an operation.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: ModelError(message: str, *, cause: BaseException | None = None)

   Bases: :py:obj:`JeevesAgentError`


   A call to the underlying model adapter failed.

   Base of the model-error taxonomy: catch this to handle every
   model failure regardless of whether it is transient or
   permanent. The SDK exception that triggered the classification
   is attached via ``__cause__`` (and ``cause``) so debug code
   can still inspect the raw error.

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: cause
      :value: None



.. py:exception:: OutputValidationError(message: str, *, raw: str, schema: type, cause: BaseException | None = None)

   Bases: :py:obj:`JeevesAgentError`


   The model's final answer did not validate against the supplied
   ``output_schema``.

   Raised by :meth:`Agent.run` when the caller passed
   ``output_schema=`` and the model's final assistant text could
   not be parsed/validated as the requested Pydantic model — even
   after the optional one-shot "retry with the validation error"
   turn.

   Carries the raw model output (``raw``), the underlying Pydantic
   :class:`pydantic.ValidationError` (``cause``, also exposed via
   ``__cause__``), and the schema that was being targeted
   (``schema``) so callers can build whatever recovery strategy
   they need (re-prompt with extra examples, fall back to
   free-text, etc.).

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: cause
      :value: None



   .. py:attribute:: raw


   .. py:attribute:: schema


.. py:exception:: PermanentModelError(message: str, *, cause: BaseException | None = None)

   Bases: :py:obj:`ModelError`


   A model call failed in a way that retrying will not fix.

   Wrong API key, malformed request, content-filter rejection,
   deprecated model name, etc. The retry layer raises these
   immediately without backoff so callers can fail fast and
   surface the real problem.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: PermissionDenied(tool: str, reason: str)

   Bases: :py:obj:`JeevesAgentError`


   A tool call was denied by the permission layer or a user hook.

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: reason


   .. py:attribute:: tool


.. py:exception:: RateLimitError(message: str, *, retry_after: float | None = None, cause: BaseException | None = None)

   Bases: :py:obj:`TransientModelError`


   The provider returned a 429 / quota-exhausted response.

   Carries ``retry_after`` when the provider supplied one. Subclass
   of :class:`TransientModelError` so generic transient handlers
   cover it; catch ``RateLimitError`` specifically when you need
   to surface "slow down" to the caller (e.g. propagate a 429 to
   your own clients).

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: RuntimeJournalError

   Bases: :py:obj:`JeevesAgentError`


   The durable runtime journal is unreadable or inconsistent.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: SandboxError

   Bases: :py:obj:`JeevesAgentError`


   The sandbox refused or failed to execute a tool.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: ToolError

   Bases: :py:obj:`JeevesAgentError`


   A tool invocation failed at the tool's own boundary.

   Initialize self.  See help(type(self)) for accurate signature.


.. py:exception:: TransientModelError(message: str, *, retry_after: float | None = None, cause: BaseException | None = None)

   Bases: :py:obj:`ModelError`


   A model call failed in a way that may succeed on retry.

   Covers HTTP 5xx responses, network errors, timeouts, and
   provider-side rate limits. The retry layer treats this family
   as retryable and applies backoff.

   ``retry_after`` (in seconds) carries a provider-supplied hint
   when one is available — e.g. an ``Retry-After`` HTTP header on
   a 429 response. The retry layer respects the larger of the
   policy's computed backoff and ``retry_after`` so we never wait
   less than the provider asked for.

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: retry_after
      :value: None



.. py:class:: AuditEntry(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   An immutable, signed entry in the audit log.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: action
      :type:  str


   .. py:attribute:: actor
      :type:  str


   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: payload
      :type:  dict[str, Any]


   .. py:attribute:: seq
      :type:  int


   .. py:attribute:: session_id
      :type:  str


   .. py:attribute:: signature
      :type:  str


   .. py:attribute:: timestamp
      :type:  datetime.datetime


.. py:class:: Budget

   Bases: :py:obj:`Protocol`


   Resource governance — tokens, calls, cost, wall clock.


   .. py:method:: allows_step() -> jeevesagent.core.types.BudgetStatus
      :async:



   .. py:method:: consume(*, tokens_in: int, tokens_out: int, cost_usd: float) -> None
      :async:



.. py:class:: BudgetStatus(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Result of a budget check before each step.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: blocked_(reason: str) -> BudgetStatus
      :classmethod:



   .. py:method:: ok_() -> BudgetStatus
      :classmethod:



   .. py:method:: warn_(reason: str) -> BudgetStatus
      :classmethod:



   .. py:property:: blocked
      :type: bool



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: reason
      :type:  str | None
      :value: None



   .. py:attribute:: state
      :type:  Literal['ok', 'warn', 'blocked']


   .. py:property:: warn
      :type: bool



.. py:class:: CertifiedValue(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A value carrying provenance metadata for freshness/lineage checks.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: fetched_at
      :type:  datetime.datetime


   .. py:attribute:: lineage
      :type:  tuple[str, Ellipsis]
      :value: ()



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: schema_version
      :type:  str
      :value: '1'



   .. py:attribute:: source
      :type:  str


   .. py:attribute:: valid_until
      :type:  datetime.datetime | None
      :value: None



   .. py:attribute:: value
      :type:  Any


.. py:class:: Embedder

   Bases: :py:obj:`Protocol`


   Text-to-vector embedding model used by the memory subsystem.


   .. py:method:: embed(text: str) -> list[float]
      :async:



   .. py:method:: embed_batch(texts: list[str]) -> list[list[float]]
      :async:



   .. py:attribute:: dimensions
      :type:  int


   .. py:attribute:: name
      :type:  str


.. py:class:: Episode(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A single (input, decisions, tool calls, output) tuple from history.

   ``user_id`` is the framework-managed namespace partition. Episodes
   persisted with one ``user_id`` value are never visible to memory
   recall queries scoped to a different ``user_id``. ``None`` is its
   own bucket — the "anonymous / single-tenant" namespace — and does
   not see episodes belonging to a non-None ``user_id`` (and vice
   versa). Set automatically from :class:`~jeevesagent.RunContext`
   by the agent loop; pass explicitly when constructing episodes
   outside a run.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: format() -> str


   .. py:attribute:: embedding
      :type:  list[float] | None
      :value: None



   .. py:attribute:: id
      :type:  str
      :value: None



   .. py:attribute:: input
      :type:  str


   .. py:attribute:: occurred_at
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: output
      :type:  str


   .. py:attribute:: session_id
      :type:  str


   .. py:attribute:: tool_calls
      :type:  list[ToolCall]
      :value: None



   .. py:attribute:: user_id
      :type:  str | None
      :value: None



.. py:class:: Event(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A single observable record from a running session.

   Carries a discriminator (``kind``) plus a free-form payload. Construct
   via the class methods to ensure consistent shapes.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: architecture_event(session_id: str, name: str, **data: Any) -> Event
      :classmethod:


      Generic architecture-progress event.

      ``name`` is a namespaced string identifying the source
      architecture and the kind of progress
      (e.g. ``"self_refine.critique"``,
      ``"reflexion.lesson_persisted"``,
      ``"router.classified"``). ``data`` is merged into the
      payload alongside ``name`` so consumers can pattern-match
      on ``name`` and read structured fields off the rest.



   .. py:method:: budget_exceeded(session_id: str, status: BudgetStatus) -> Event
      :classmethod:



   .. py:method:: budget_warning(session_id: str, status: BudgetStatus) -> Event
      :classmethod:



   .. py:method:: completed(session_id: str, result: Any) -> Event
      :classmethod:



   .. py:method:: error(session_id: str, exc: BaseException) -> Event
      :classmethod:



   .. py:method:: model_chunk(session_id: str, chunk: ModelChunk) -> Event
      :classmethod:



   .. py:method:: started(session_id: str, prompt: str) -> Event
      :classmethod:



   .. py:method:: tool_call(session_id: str, call: ToolCall) -> Event
      :classmethod:



   .. py:method:: tool_result(session_id: str, result: ToolResult) -> Event
      :classmethod:



   .. py:attribute:: at
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: kind
      :type:  EventKind


   .. py:attribute:: payload
      :type:  dict[str, Any]
      :value: None



   .. py:attribute:: session_id
      :type:  str


.. py:class:: EventKind

   Bases: :py:obj:`enum.StrEnum`


   Enum where members are also (and must be) strings

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: ARCHITECTURE_EVENT
      :value: 'architecture_event'


      Generic architecture-progress event. Carries a namespaced
      ``name`` in the payload (e.g. ``"self_refine.critique"``,
      ``"reflexion.lesson_persisted"``, ``"router.classified"``) so
      each architecture can stream its own progress signal without
      expanding :class:`EventKind`.


   .. py:attribute:: BUDGET_EXCEEDED
      :value: 'budget_exceeded'



   .. py:attribute:: BUDGET_WARNING
      :value: 'budget_warning'



   .. py:attribute:: COMPLETED
      :value: 'completed'



   .. py:attribute:: ERROR
      :value: 'error'



   .. py:attribute:: MEMORY_RECALL
      :value: 'memory_recall'



   .. py:attribute:: MEMORY_WRITE
      :value: 'memory_write'



   .. py:attribute:: MODEL_CHUNK
      :value: 'model_chunk'



   .. py:attribute:: PERMISSION_ASK
      :value: 'permission_ask'



   .. py:attribute:: PERMISSION_DECISION
      :value: 'permission_decision'



   .. py:attribute:: STARTED
      :value: 'started'



   .. py:attribute:: TOOL_CALL
      :value: 'tool_call'



   .. py:attribute:: TOOL_RESULT
      :value: 'tool_result'



.. py:class:: Fact(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A semantic claim extracted from one or more episodes.

   Bi-temporal: ``valid_from``/``valid_until`` tracks when the fact was
   true in the world; ``recorded_at`` tracks when we learned it.

   ``user_id`` is the framework-managed namespace partition. Facts
   persisted with one ``user_id`` value are never visible to recall
   queries scoped to a different ``user_id``. Set automatically from
   :class:`~jeevesagent.RunContext` by the agent loop / consolidator;
   pass explicitly when constructing facts outside a run.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: format() -> str


   .. py:attribute:: confidence
      :type:  float
      :value: 1.0



   .. py:attribute:: id
      :type:  str
      :value: None



   .. py:attribute:: object
      :type:  str


   .. py:attribute:: predicate
      :type:  str


   .. py:attribute:: recorded_at
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: sources
      :type:  list[str]
      :value: None



   .. py:attribute:: subject
      :type:  str


   .. py:attribute:: user_id
      :type:  str | None
      :value: None



   .. py:attribute:: valid_from
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: valid_until
      :type:  datetime.datetime | None
      :value: None



.. py:class:: HookHost

   Bases: :py:obj:`Protocol`


   Aggregator over user-registered lifecycle callbacks.


   .. py:method:: on_event(event: jeevesagent.core.types.Event) -> None
      :async:



   .. py:method:: post_tool(call: jeevesagent.core.types.ToolCall, result: jeevesagent.core.types.ToolResult) -> None
      :async:



   .. py:method:: pre_tool(call: jeevesagent.core.types.ToolCall) -> jeevesagent.core.types.PermissionDecision
      :async:



.. py:class:: Memory

   Bases: :py:obj:`Protocol`


   Tiered memory: working blocks, episodic store, semantic graph.


   .. py:method:: append_block(name: str, content: str) -> None
      :async:


      Append to a named block, creating it if absent.



   .. py:method:: consolidate() -> None
      :async:


      Background: extract semantic facts from recent episodes.



   .. py:method:: recall(query: str, *, kind: str = 'episodic', limit: int = 5, time_range: tuple[datetime.datetime, datetime.datetime] | None = None, user_id: str | None = None) -> list[jeevesagent.core.types.Episode]
      :async:


      Retrieve episodes (or facts, when ``kind='semantic'``).

      When ``user_id`` is supplied, results are restricted to
      episodes stored with that exact ``user_id`` value. ``None``
      is its own bucket (the "anonymous / single-tenant"
      namespace) — episodes stored with ``user_id=None`` are never
      visible to a query with ``user_id="alice"`` and vice versa.
      Backends MUST honour this filter to preserve the framework's
      multi-tenant safety contract.



   .. py:method:: recall_facts(query: str, *, limit: int = 5, valid_at: datetime.datetime | None = None, user_id: str | None = None) -> list[jeevesagent.core.types.Fact]
      :async:


      Retrieve bi-temporal facts matching ``query``.

      Backends that don't expose a fact store return ``[]``. The agent
      loop calls this directly rather than duck-typing on
      ``memory.facts`` so backends without fact support don't need
      any opt-out mechanism.

      ``user_id`` filters by namespace partition with the same
      semantics as :meth:`recall`: ``None`` is its own bucket and
      does not cross-contaminate with non-None values.



   .. py:method:: remember(episode: jeevesagent.core.types.Episode) -> str
      :async:


      Persist an episode. Returns the episode ID.



   .. py:method:: session_messages(session_id: str, *, user_id: str | None = None, limit: int = 20) -> list[jeevesagent.core.types.Message]
      :async:


      Return the most-recent ``limit`` user/assistant turns from
      the conversation identified by ``session_id``, in order
      (oldest first).

      This is the conversation-continuity primitive — the agent
      loop calls it at the top of every run so that reusing a
      ``session_id`` actually continues the chat (the model sees
      previous turns as real :class:`Message` history) rather than
      starting fresh and relying solely on semantic recall.

      ``user_id`` MUST be respected by backends as a hard
      namespace partition: messages persisted under one
      ``user_id`` are never visible to a query scoped to a
      different one. Backends without persisted message logs
      return ``[]`` — the agent loop falls back to the
      semantic-recall path in that case.



   .. py:method:: update_block(name: str, content: str) -> None
      :async:


      Replace the contents of a named block.



   .. py:method:: working() -> list[jeevesagent.core.types.MemoryBlock]
      :async:


      All in-context blocks. Pinned to every prompt.



.. py:class:: MemoryBlock(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   An in-context memory block, pinned to every prompt.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: format() -> str


   .. py:attribute:: content
      :type:  str


   .. py:attribute:: name
      :type:  str


   .. py:attribute:: pinned_order
      :type:  int
      :value: 0



   .. py:attribute:: updated_at
      :type:  datetime.datetime
      :value: None



.. py:class:: Message(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A single chat message in the model's conversation.

   ``tool_calls`` is populated on assistant messages that emitted tool
   calls in the previous turn — real provider adapters (Anthropic
   ``tool_use`` blocks, OpenAI ``tool_calls`` array) need to reconstruct
   the right wire format from this.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: content
      :type:  str


   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: name
      :type:  str | None
      :value: None



   .. py:attribute:: role
      :type:  Role


   .. py:attribute:: tool_call_id
      :type:  str | None
      :value: None



   .. py:attribute:: tool_calls
      :type:  tuple[ToolCall, Ellipsis]
      :value: ()



.. py:class:: Model

   Bases: :py:obj:`Protocol`


   LLM provider interface. One adapter per lab (Anthropic, OpenAI, ...).

   The required surface is ``stream(...)`` — every adapter must
   implement it. Adapters MAY additionally override ``complete(...)``
   with a non-streaming (single-shot) call; if not, ``complete``
   falls back to consuming the stream internally and assembling the
   full response, which is correct but slower (per-chunk wire +
   parsing overhead). Architectures use ``complete`` on the
   non-streaming hot path (``agent.run()``) and ``stream`` when a
   consumer is reading from ``agent.stream()``.


   .. py:method:: stream(messages: list[jeevesagent.core.types.Message], *, tools: list[jeevesagent.core.types.ToolDef] | None = None, temperature: float = 1.0, max_tokens: int | None = None) -> collections.abc.AsyncIterator[jeevesagent.core.types.ModelChunk]

      Stream completion chunks. Each chunk is text, tool_call, or finish.



   .. py:attribute:: name
      :type:  str


.. py:class:: ModelChunk(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A single chunk from a streaming model call.

   Discriminated by ``kind``. Exactly one of the optional fields is set
   depending on the kind.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: finish_reason
      :type:  str | None
      :value: None



   .. py:attribute:: kind
      :type:  Literal['text', 'tool_call', 'finish']


   .. py:attribute:: text
      :type:  str | None
      :value: None



   .. py:attribute:: tool_call
      :type:  ToolCall | None
      :value: None



   .. py:attribute:: usage
      :type:  Usage | None
      :value: None



.. py:class:: PermissionDecision(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Outcome of a permission check or pre-tool hook.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: allow_(reason: str | None = None) -> PermissionDecision
      :classmethod:



   .. py:method:: ask_(reason: str | None = None) -> PermissionDecision
      :classmethod:



   .. py:method:: deny_(reason: str) -> PermissionDecision
      :classmethod:



   .. py:property:: allow
      :type: bool



   .. py:property:: ask
      :type: bool



   .. py:attribute:: decision
      :type:  Literal['allow', 'deny', 'ask']


   .. py:property:: deny
      :type: bool



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: reason
      :type:  str | None
      :value: None



.. py:class:: Permissions

   Bases: :py:obj:`Protocol`


   Decides whether a tool call is allowed.


   .. py:method:: check(call: jeevesagent.core.types.ToolCall, *, context: collections.abc.Mapping[str, Any]) -> jeevesagent.core.types.PermissionDecision
      :async:



.. py:class:: Role

   Bases: :py:obj:`enum.StrEnum`


   Enum where members are also (and must be) strings

   Initialize self.  See help(type(self)) for accurate signature.


   .. py:attribute:: ASSISTANT
      :value: 'assistant'



   .. py:attribute:: SYSTEM
      :value: 'system'



   .. py:attribute:: TOOL
      :value: 'tool'



   .. py:attribute:: USER
      :value: 'user'



.. py:class:: RunContext

   Typed, immutable context for one agent run.

   Set once at the start of :meth:`Agent.run` and propagated to
   every architecture, tool, hook, sub-agent, and memory operation
   via a :class:`contextvars.ContextVar`. The framework treats
   ``user_id`` and ``session_id`` as first-class fields (typed,
   namespaced); ``metadata`` is an opaque bag for app-specific keys
   the framework does not interpret.

   Construct one directly when you need to spawn work outside an
   active run with explicit scope:

   .. code-block:: python

       ctx = RunContext(user_id="alice", session_id="conv_42")
       async with set_run_context(ctx):
           await my_tool(...)

   Inside an agent run, prefer :func:`get_run_context` over
   constructing a new one — that gives you the live context the
   framework set up.


   .. py:method:: get(key: str, default: Any = None) -> Any

      Shorthand for ``self.metadata.get(key, default)``.



   .. py:method:: with_overrides(*, user_id: str | None | _Sentinel = _Sentinel.UNSET, session_id: str | None | _Sentinel = _Sentinel.UNSET, run_id: str | _Sentinel = _Sentinel.UNSET, metadata: collections.abc.Mapping[str, Any] | _Sentinel = _Sentinel.UNSET) -> RunContext

      Return a new context with selected fields replaced.

      Used by multi-agent architectures when spawning sub-agents
      that need to inherit most of the parent's context but with
      a derived ``session_id`` or augmented ``metadata``. The
      sentinel makes "leave this field unchanged" distinguishable
      from "explicitly set this field to ``None``".



   .. py:attribute:: metadata
      :type:  collections.abc.Mapping[str, Any]

      Free-form application context. Use this for keys the framework
      does not need to understand — locale, request id, feature flags,
      tenant id beyond ``user_id``, etc. Read inside tools / hooks via
      ``get_run_context().metadata``.


   .. py:attribute:: run_id
      :type:  str
      :value: ''


      Unique identifier for this single :meth:`Agent.run` invocation.
      Distinct from ``session_id`` (which identifies a conversation
      that may span many runs). Auto-set by :meth:`Agent.run`; an
      explicit value passed in by the caller is overridden.


   .. py:attribute:: session_id
      :type:  str | None
      :value: None


      Conversation thread identifier. Reusing the same ``session_id``
      across calls signals "continue this conversation" — the
      framework will rehydrate prior session messages so the model
      sees real chat history, not just memory recall. ``None`` means
      "fresh conversation"; the framework auto-generates one inside
      :meth:`Agent.run` if not supplied.


   .. py:attribute:: user_id
      :type:  str | None
      :value: None


      Namespace for memory recall + persistence. ``None`` is the
      "anonymous / single-tenant" bucket; episodes / facts stored
      with ``user_id=None`` never see episodes / facts stored with
      a non-None ``user_id`` and vice versa. The framework treats
      this as a hard partition key, not a soft filter.


.. py:class:: RunResult(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Final outcome of an ``Agent.run`` call.

   ``output`` is always the raw assistant text (the JSON itself when
   a structured-output schema was supplied). ``parsed`` is the
   validated Pydantic instance — populated only when the caller
   passed ``output_schema=`` to :meth:`Agent.run`. Use whichever
   fits the call site::

       # free-form text run
       result = await agent.run("summarise this PDF")
       print(result.output)

       # structured-output run
       result = await agent.run(prompt, output_schema=Invoice)
       invoice: Invoice = result.parsed   # typed, validated

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: cost_usd
      :type:  float
      :value: 0.0



   .. py:property:: duration
      :type: datetime.timedelta


      Wall-clock latency between ``started_at`` and ``finished_at``.


   .. py:attribute:: finished_at
      :type:  datetime.datetime


   .. py:attribute:: interrupted
      :type:  bool
      :value: False



   .. py:attribute:: interruption_reason
      :type:  str | None
      :value: None



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: output
      :type:  str


   .. py:attribute:: parsed
      :type:  Any | None
      :value: None


      The validated Pydantic instance when ``output_schema=`` was
      supplied to :meth:`Agent.run`; ``None`` otherwise. Typed as
      ``Any`` to keep the runtime type free; the call site has the
      schema and can cast or annotate as needed.


   .. py:attribute:: session_id
      :type:  str


   .. py:attribute:: started_at
      :type:  datetime.datetime


   .. py:attribute:: tokens_in
      :type:  int
      :value: 0



   .. py:attribute:: tokens_out
      :type:  int
      :value: 0



   .. py:property:: total_tokens
      :type: int


      ``tokens_in + tokens_out``.

      :type: Convenience


   .. py:attribute:: turns
      :type:  int


.. py:class:: Runtime

   Bases: :py:obj:`Protocol`


   Durable execution. Wraps every side effect in a journal entry.


   .. py:method:: session(session_id: str) -> contextlib.AbstractAsyncContextManager[RuntimeSession]

      Open or resume a durable session.



   .. py:method:: signal(session_id: str, name: str, payload: Any) -> None
      :async:


      Send an external signal (e.g., human approval) to a session.



   .. py:method:: step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.Awaitable[Any]], *args: Any, idempotency_key: str | None = None, **kwargs: Any) -> Any
      :async:


      Execute ``fn`` as a journaled step. Replays cached on resume.



   .. py:method:: stream_step(name: str, fn: collections.abc.Callable[Ellipsis, collections.abc.AsyncIterator[Any]], *args: Any, **kwargs: Any) -> collections.abc.AsyncIterator[Any]

      Execute a streaming step. Replays the aggregate on resume.



   .. py:attribute:: name
      :type:  str


.. py:class:: RuntimeSession

   Bases: :py:obj:`Protocol`


   Handle to an open durable session held by a :class:`Runtime`.


   .. py:method:: deliver(name: str, payload: Any) -> None
      :async:



   .. py:attribute:: id
      :type:  str


.. py:class:: Sandbox

   Bases: :py:obj:`Protocol`


   Isolation layer for tool execution.


   .. py:method:: execute(tool: jeevesagent.core.types.ToolDef, args: collections.abc.Mapping[str, Any]) -> jeevesagent.core.types.ToolResult
      :async:



   .. py:method:: with_filesystem(root: str) -> contextlib.AbstractAsyncContextManager[None]

      Temporary filesystem sandbox for the duration of the context.



.. py:class:: Secrets

   Bases: :py:obj:`Protocol`


   Resolution and redaction of named secrets.


   .. py:method:: redact(text: str) -> str


   .. py:method:: resolve(ref: str) -> str
      :async:



   .. py:method:: store(ref: str, value: str) -> None
      :async:



.. py:class:: Span(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A trace span handle. Concrete telemetry adapters return their own
   representation; this is the value-object contract for in-process use.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: attributes
      :type:  dict[str, Any]
      :value: None



   .. py:attribute:: name
      :type:  str


   .. py:attribute:: span_id
      :type:  str


   .. py:attribute:: started_at
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: trace_id
      :type:  str


.. py:class:: Telemetry

   Bases: :py:obj:`Protocol`


   OpenTelemetry-compatible tracing/metrics surface.


   .. py:method:: emit_metric(name: str, value: float, **attrs: Any) -> None
      :async:



   .. py:method:: trace(name: str, **attrs: Any) -> contextlib.AbstractAsyncContextManager[jeevesagent.core.types.Span]


.. py:class:: ToolCall(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   A model-emitted request to invoke a tool.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: idempotency_key() -> str


   .. py:method:: is_destructive() -> bool


   .. py:attribute:: args
      :type:  dict[str, Any]
      :value: None



   .. py:attribute:: destructive
      :type:  bool
      :value: False



   .. py:attribute:: id
      :type:  str
      :value: None



   .. py:attribute:: tool
      :type:  str


   .. py:attribute:: tool_def
      :type:  ToolDef | None
      :value: None



.. py:class:: ToolDef(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Schema description of a tool the model can call.

   Mirrors the JSON-Schema-flavored shape used across MCP and provider APIs.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: description
      :type:  str


   .. py:attribute:: input_schema
      :type:  dict[str, Any]
      :value: None



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: name
      :type:  str


   .. py:attribute:: server
      :type:  str | None
      :value: None



.. py:class:: ToolEvent(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Tool registry change notification (MCP listChanged etc.).

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: at
      :type:  datetime.datetime
      :value: None



   .. py:attribute:: kind
      :type:  Literal['added', 'removed', 'updated']


   .. py:attribute:: server
      :type:  str | None
      :value: None



   .. py:attribute:: tool
      :type:  str


.. py:class:: ToolHost

   Bases: :py:obj:`Protocol`


   MCP-aware tool registry. Lazy-loads schemas on demand.


   .. py:method:: call(tool: str, args: collections.abc.Mapping[str, Any], *, call_id: str = '') -> jeevesagent.core.types.ToolResult
      :async:


      Invoke ``tool`` with ``args``. The ``call_id`` is propagated into
      the returned :class:`ToolResult` so the loop can correlate
      results with the originating model-emitted call.



   .. py:method:: list_tools(*, query: str | None = None) -> list[jeevesagent.core.types.ToolDef]
      :async:



   .. py:method:: watch() -> collections.abc.AsyncIterator[jeevesagent.core.types.ToolEvent]

      Notifications when the tool list changes (MCP listChanged).



.. py:class:: ToolResult(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Outcome of a tool invocation.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:method:: denied_(call_id: str, reason: str, **kwargs: Any) -> ToolResult
      :classmethod:



   .. py:method:: error_(call_id: str, message: str, **kwargs: Any) -> ToolResult
      :classmethod:



   .. py:method:: success(call_id: str, output: Any, **kwargs: Any) -> ToolResult
      :classmethod:



   .. py:attribute:: call_id
      :type:  str


   .. py:attribute:: denied
      :type:  bool
      :value: False



   .. py:attribute:: duration_ms
      :type:  float | None
      :value: None



   .. py:attribute:: error
      :type:  str | None
      :value: None



   .. py:attribute:: ok
      :type:  bool


   .. py:attribute:: output
      :type:  Any
      :value: None



   .. py:attribute:: reason
      :type:  str | None
      :value: None



   .. py:attribute:: started_at
      :type:  datetime.datetime
      :value: None



.. py:class:: Usage(/, **data: Any)

   Bases: :py:obj:`pydantic.BaseModel`


   Token and cost accounting for a model call.

   Create a new model by parsing and validating input data from keyword arguments.

   Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
   validated to form a valid model.

   `self` is explicitly positional-only to allow `self` as a field name.


   .. py:attribute:: cost_usd
      :type:  float
      :value: 0.0



   .. py:attribute:: input_tokens
      :type:  int
      :value: 0



   .. py:attribute:: model_config

      Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].


   .. py:attribute:: output_tokens
      :type:  int
      :value: 0



.. py:class:: set_run_context(context: RunContext)

   Context manager that installs a :class:`RunContext` for the
   duration of an ``async with`` block.

   The framework uses this internally inside :meth:`Agent.run` to
   expose the live context to tools and hooks. Application code
   rarely needs it, but it is the supported way to invoke a tool
   *outside* an agent loop with explicit scope — for example in
   background workers that share tool implementations with the
   agent::

       async with set_run_context(RunContext(user_id="alice")):
           await some_tool(...)

   Behaves correctly under structured concurrency: nested
   ``async with`` blocks restore the prior context on exit, and
   ``anyio`` task-group spawns inherit the active context
   automatically.


.. py:function:: deterministic_hash(*parts: Any) -> str

   Stable hash of arbitrary JSON-serializable parts.

   Used as an idempotency key for journaled steps. The hash is stable
   across processes and Python versions because the input is canonicalised
   via ``json.dumps(..., sort_keys=True)``.


.. py:function:: get_run_context() -> RunContext

   Return the :class:`RunContext` for the currently-running agent.

   Inside an active :meth:`Agent.run` call this returns the live
   context with ``user_id``, ``session_id``, ``run_id``, and
   ``metadata`` populated. Outside any active run (test code,
   direct ``@tool`` invocation, REPL exploration) this returns the
   default empty :class:`RunContext` — never raises.

   Tools that need scope information call this rather than taking
   extra parameters:

   .. code-block:: python

       @tool
       async def fetch_user_orders() -> str:
           ctx = get_run_context()
           return await db.query("orders", user_id=ctx.user_id)


.. py:function:: new_id(prefix: str = '') -> str

   Return a fresh ULID, optionally prefixed for readability.

   >>> new_id("ep").startswith("ep_")
   True


