Documentation
¶
Overview ¶
Package agent implements the turn loop: one live turn per session, mid-turn steering for overflow messages, bounded worker concurrency, and a system prompt assembled from identity, workspace bootstrap files, drop-in instructions, the skills catalog, and smrti memory recall.
Index ¶
- type Activity
- type ChatProvider
- type ContextBuilder
- type Loop
- func (l *Loop) Idle() bool
- func (l *Loop) LastChannel() (channel, chatID string, ok bool)
- func (l *Loop) OnActivity(fn func(Activity))
- func (l *Loop) ProcessDirect(ctx context.Context, content, sessionKey string) (string, error)
- func (l *Loop) ProcessDirectNotice(ctx context.Context, content, sessionKey string, notice func(string)) (string, error)
- func (l *Loop) ProcessEphemeral(ctx context.Context, content string) (string, error)
- func (l *Loop) Run(ctx context.Context)
- func (l *Loop) SetContextWindow(fn func() int)
- func (l *Loop) SetReachable(fn func(channel string) bool)
- func (l *Loop) WaitBackground(timeout time.Duration)
- type Phase
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Activity ¶
Activity is one phase change on one session. Detail carries whatever the phase names: the tool being run, or the line the agent wants the user to read while it works.
type ChatProvider ¶
type ChatProvider interface {
Chat(ctx context.Context, req *provider.Request) (*provider.Response, error)
}
ChatProvider is what the loop needs from the provider layer (satisfied by *provider.Chain; tests use scripted fakes).
type ContextBuilder ¶
type ContextBuilder struct {
// contains filtered or unexported fields
}
ContextBuilder assembles the system prompt. The static portion (identity, bootstrap files, instructions, skill catalog) is cached and invalidated by file mtimes; the memory portion is recalled fresh every turn.
func NewContextBuilder ¶
func (*ContextBuilder) SystemPrompt ¶
func (cb *ContextBuilder) SystemPrompt(ctx context.Context, history []provider.Message, current string) string
SystemPrompt builds the full prompt for one turn: identity opens it, the operating rules close it.
Everything in between — the user's workspace files, drop-in instructions, the skills catalog, recalled memories — grows without bound as the agent is used, and recall is weakest in the middle of a long prompt. Rules that must hold on every turn are therefore re-anchored at the tail, the other position that stays reliable. This costs nothing in cache terms: the memory block above already changes every turn, so the cacheable prefix ends before it either way.
type Loop ¶
type Loop struct {
// contains filtered or unexported fields
}
func NewLoop ¶
func NewLoop(cfg *config.Config, b *bus.MessageBus, chat ChatProvider, registry *tools.Registry, sessions *session.Store, builder *ContextBuilder, ambient *memory.Ambient) *Loop
func (*Loop) Idle ¶ added in v0.7.0
Idle reports whether every session has finished its turn. The gateway asks before restarting itself: a reload mid-turn is an answer the user never gets.
func (*Loop) LastChannel ¶
LastChannel returns the most recent external channel/chat, for heartbeat and cron delivery. It survives a restart — the address is on disk — so ok is false until the user's first ever message, and again whenever that chat's connector is not running: an address whose channel was switched off is not somewhere Factor can reach anyone, and a caller that treats it as one reports a delivery that never happened.
func (*Loop) OnActivity ¶
OnActivity installs the activity watcher, replacing any previous one and clearing it when fn is nil. It is called from turn goroutines, so it must not block and must not call back into the loop.
func (*Loop) ProcessDirect ¶
ProcessDirect runs a synchronous turn outside the bus (CLI one-shot, cron, delegated jobs) with nobody listening for progress.
func (*Loop) ProcessDirectNotice ¶ added in v0.17.1
func (l *Loop) ProcessDirectNotice(ctx context.Context, content, sessionKey string, notice func(string)) (string, error)
ProcessDirectNotice runs a synchronous turn outside the bus and reports what the agent says on its way to the answer to notice, as it says it — the connector running the turn is the only place that line can arrive while it is still news. It honors the one-live-turn-per-session invariant: if the session is busy (e.g. an overlapping cron firing), it waits for the claim instead of interleaving histories.
func (*Loop) ProcessEphemeral ¶
ProcessEphemeral runs a history-less, memory-less turn (heartbeat).
func (*Loop) Run ¶
Run drains the inbound bus until ctx is cancelled. One live turn per session key; overflow becomes steering.
func (*Loop) SetContextWindow ¶ added in v0.25.0
SetContextWindow teaches the loop how much context the models in play carry, so compaction triggers against the real window instead of an assumption. fn answers 0 when nothing knows — the loop then falls back to config, and past that to a fixed default. It is consulted on every check: the answer can improve after startup, once the model catalog arrives.
func (*Loop) SetReachable ¶ added in v0.17.1
SetReachable teaches the loop which channels have a connector behind them right now, so LastChannel never hands out an address nothing can deliver to. Unset — the CLI, tests — every channel counts as reachable.
func (*Loop) WaitBackground ¶
WaitBackground blocks until async work (memory stores, compaction) drains, or the timeout passes. One-shot mode calls this so memory writes are not lost to process exit.
type Phase ¶
type Phase string
Phase is what a turn is busy with at a given moment. Front-ends map these onto whatever they draw while the user waits.
const ( PhaseContext Phase = "context" // assembling the prompt: skills, memory recall PhaseThinking Phase = "thinking" // waiting on the provider PhaseTool Phase = "tool" // running a tool call (Detail is its name) PhaseNotice Phase = "notice" // the agent said what it is about to do (Detail is that line) PhaseCompacting Phase = "compacting" // summarizing history after a context overflow PhaseSteering Phase = "steering" // folding in a message that arrived mid-turn PhaseDone Phase = "done" // the turn ended, with a reply or an error )