core

package
v0.0.0-...-98f5679 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 30, 2026 License: MIT Imports: 16 Imported by: 0

Documentation

Overview

Package core is jess's agentcore-touching implementation: the option-assembler (Config + Agent) that wires a model, system blocks, tools, the memory ContextManager, the tool gate, and the audit middleware into a ready *agentcore.Agent. It is shared by the root jess package and jess/subagent, so both build agents the same way without an import cycle.

Unlike the former anti-corruption layer (ADR 0001), core exposes agentcore types directly (ADR 0002). It is internal only to keep the assembly helpers out of jess's public surface, not to hide agentcore. Portability insurance is keeping jess/memory and jess/skill agentcore-free, not wrapping the harness.

Index

Constants

This section is empty.

Variables

View Source
var ErrRunActive = errors.New("core: a run is already active on this agent")

ErrRunActive is returned by begin when a run is already in flight on this agent. It enforces the one-active-run invariant instead of assuming it.

Functions

func Agent

func Agent(cfg Config) *ac.Agent

Agent assembles a ready *agentcore.Agent from cfg. The audit middleware is always installed (a nil sink becomes DiscardSink), so every tool execution is recorded unless audit is explicitly discarded.

func Once

func Once(supportsTools bool, fn GenerateFunc) ac.ChatModel

Once adapts a one-shot GenerateFunc into an agentcore.ChatModel, emitting the whole response as a single terminal stream event. supportsTools advertises tool capability to the loop.

func ReleaseAgent

func ReleaseAgent(a *ac.Agent)

ReleaseAgent removes a from the internal audit registry. Call it when an agent is permanently done (e.g. a pool discarding a per-job agent) to avoid unbounded growth. Safe to call more than once.

func SkillBlocks

func SkillBlocks(s *skill.Set) []ac.SystemBlock

SkillBlocks builds the agentcore system-prompt contributions for a skill Set: an index header listing every skill name + one-line description, then one block per skill that has a SystemPrompt. Sorted by name for stable output (matters for prompt caching). Returns nil for an empty/nil Set.

It reads the Set through its exported API (Names + Get), so the skill package stays agentcore-free; the agentcore-typed blocks are produced here.

func SkillTools

func SkillTools(s *skill.Set) []ac.Tool

SkillTools collects the agentcore tools contributed by every skill in the Set, in the same order as SkillBlocks (sorted by skill name, tools in declared order within a skill). Entries in a Skill's Tools slice that do not implement agentcore.Tool are silently skipped: the field is typed any to keep the skill package decoupled, but only ac.Tool values reach the agent.

func Stream

func Stream(ctx context.Context, agent *ac.Agent, input string) (<-chan ac.Event, func() *ac.RunSummary)

Stream drives one prompt on agent and exposes its events as a channel plus a Wait for the final RunSummary. Cancelling ctx aborts the run (the kill switch). Run-level audit (prompt, run_end, abort) is recorded to the agent's audit sink, which Agent registered at build time; tool-level audit is already covered by the middleware and gate. A nil/unknown sink degrades to no run-level audit, never to no run.

Only one active Stream per agent at a time; a second concurrent call races the first run's events.

Types

type Config

type Config struct {
	Model        ac.ChatModel
	SystemPrompt string
	Tools        []ac.Tool
	Skills       *skill.Set
	Store        memory.Store
	Recaller     memory.Recaller
	AgentID      string
	MaxTurns     int
	Gate         ac.ToolGate
	Audit        ledger.Sink
	Extra        []ac.AgentOption // passthrough for the long tail

	// Approver and AllowAll configure the default gate that Agent builds when
	// Gate is nil. A non-nil Gate (jess.WithToolGate) wins and both are ignored.
	// AllowAll is the explicit, greppable opt-out from the fail-closed default.
	// Neither bypasses the audit middleware's "no durable record, no action"
	// enforcement: that is gate-independent.
	Approver gate.Approver
	AllowAll bool
}

Config is the assembled jess agent configuration, vendor-visible (agentcore types are exposed by design). Both jess.New and subagent.Pool build from it.

type ContextManager

type ContextManager struct {
	// contains filtered or unexported fields
}

ContextManager is the agentcore.ContextManager adapter for jess/memory. It injects recalled memory entries as a leading user message before each LLM call, then delegates everything else (Compact, RecoverOverflow, Sync, Usage, Snapshot) to an inner manager. Hosts that don't have their own context strategy get PassthroughInner (the default applied when Inner is nil).

It lives in the ACL (not in jess/memory) because it speaks agentcore types; the root jess package wires it via the facade's WithMemory option. Memory failures never block the LLM call: Store/Recaller errors are swallowed and the adapter degrades to no-memory, never no-agent (ADR Decision 7).

Lifecycle: Project is called once per LLM turn by agentcore. The adapter constructs a Query from AgentID + the conversation's recent user content, asks the Recaller for matching entries, formats them into a single inserted Message, and returns the inner projection with that Message prepended.

Memory injection never commits to the runtime baseline — entries appear in the prompt view for one call and vanish on the next. That keeps the conversation history readable and prevents memory text from being re-fed back into Recall via the conversation hint.

func NewContextManager

func NewContextManager(store memory.Store, recaller memory.Recaller, opts ContextManagerOptions) *ContextManager

NewContextManager wires a Store + Recaller behind an agentcore.ContextManager. Returns nil on impossible config (nil Store or nil Recaller) — callers should construct both explicitly.

func (*ContextManager) Compact

func (*ContextManager) Project

Project builds the prompt view in three layers:

  1. inner.Project produces the baseline (compaction etc).
  2. AlwaysInclude Kinds (user / feedback by default) get pulled directly from the Store, capped per-Kind by policy. These bypass recall scoring.
  3. Recall fills the remaining budget with relevance-scored entries from non-AlwaysInclude Kinds.

The two blocks are placed by how stable they are, because providers cache on a byte prefix. CORE is the same every turn until a memory is added, so it leads the projection and extends the cacheable prefix. RELEVANT is rescored against the latest turn, so its bytes differ every turn: it goes LAST, after the conversation, and the final stable message is marked as the cache breakpoint. Leading with RELEVANT would put volatile bytes ahead of the whole transcript, so nothing after them could ever match and the conversation would be re-uploaded at full price every turn.

Memory injection never commits to the runtime baseline.

func (*ContextManager) RecoverOverflow

func (m *ContextManager) RecoverOverflow(ctx context.Context, msgs []ac.AgentMessage, cause error) (ac.ContextRecoveryResult, error)

func (*ContextManager) Snapshot

func (m *ContextManager) Snapshot() *ac.ContextSnapshot

func (*ContextManager) Sync

func (m *ContextManager) Sync(msgs []ac.AgentMessage)

func (*ContextManager) Usage

func (m *ContextManager) Usage() *ac.ContextUsage

type ContextManagerOptions

type ContextManagerOptions struct {
	// AgentID scopes Recall queries to one agent. Empty matches memories with
	// no AgentID (the global scope); pass an agent identifier for per-agent
	// memory.
	AgentID string
	// MaxItems caps how many memory entries get injected per call. Default 8.
	// Set to 0 for "as many as Recaller returns".
	MaxItems int
	// Header is the prefix line for the injected message. Default "Relevant
	// memories for this conversation:". Set to "" to omit the header entirely.
	Header string
	// Inner is the underlying ContextManager. May be nil — see PassthroughInner
	// for the no-op default applied in that case.
	Inner ac.ContextManager

	// Kinds is the registry of per-Kind policies. nil uses the baked-in
	// defaults from memory.NewKindRegistry. The ContextManager uses it to decide
	// which Kinds bypass recall (AlwaysInclude=true) and how many entries of
	// each Kind to inject per turn.
	Kinds *memory.KindRegistry

	// Audit is the provenance sink. When set, Project records one KindRetrieved
	// event per turn referencing every injected memory entry (by id + text hash).
	// nil disables retrieval provenance. Recording is best-effort.
	Audit ledger.Sink

	// RunState supplies the current RunID so a KindRetrieved event correlates with
	// the run that injected the memory. nil yields an empty RunID.
	RunState *runState
}

ContextManagerOptions configures NewContextManager.

type GenerateFunc

type GenerateFunc func(ctx context.Context, msgs []ac.Message, tools []ac.ToolSpec) (*ac.LLMResponse, error)

GenerateFunc is a one-shot generation: messages + tool specs in, one response out. The streaming half is synthesized by Once.

type PassthroughInner

type PassthroughInner struct{}

PassthroughInner is the default inner ContextManager when callers don't supply one. It returns the input messages unchanged and reports zero usage. Implements the full ContextManager interface.

func (PassthroughInner) Compact

func (PassthroughInner) Project

func (PassthroughInner) RecoverOverflow

func (PassthroughInner) RecoverOverflow(ctx context.Context, msgs []ac.AgentMessage, _ error) (ac.ContextRecoveryResult, error)

func (PassthroughInner) Snapshot

func (PassthroughInner) Snapshot() *ac.ContextSnapshot

func (PassthroughInner) Sync

func (PassthroughInner) Sync(_ []ac.AgentMessage)

func (PassthroughInner) Usage

func (PassthroughInner) Usage() *ac.ContextUsage

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL