Documentation
¶
Overview ¶
Package agent orchestrates one conversational task: it loads the project's capability documents, composes them into knowledge and provider tools, drives the agentcore tool-use loop against the resolved model, and meters the run's usage. It is the standalone analogue of the portal's assistant route — same composition, same per-task MCP lifecycle, same usage shape — minus the portal's session/env coupling.
Index ¶
- Constants
- Variables
- func BuildSystemPrompt(persona, knowledgeAddendum string) string
- func ResolveModel(cfg config.ModelConfig, logger *slog.Logger) (agentcore.Model, error)
- type Conversation
- type Deps
- type Event
- type EventKind
- type Mention
- type Params
- type Result
- type State
- type Stream
- type UsageSummary
Constants ¶
const AgentAttributionName = "patch"
AgentAttributionName is the fixed agent identity sent in the x-datum-agent attribution header (gateway mode only).
const CompactionThresholdRatio = 0.8
CompactionThresholdRatio is the fraction of the history token budget at which [Conversation.loadHistory] triggers compaction — before Truncate would actually have to drop anything, so a turn is never caught needing to both compact and still overflow (docs/conversation-summarization-design.md #1).
const DefaultHistoryTokenBudget = 6000
DefaultHistoryTokenBudget caps the estimated tokens of replayed history per turn when Deps.HistoryTokenBudget is unset. Replayed turns are billed input tokens on every subsequent request, so an unbounded conversation would grow cost quadratically; oldest turns are dropped first.
const DefaultMaxOutputTokens = 4096
DefaultMaxOutputTokens is the per-request output-token cap applied when Deps.MaxOutputTokens is unset. It matches the TypeScript service, which always sent 4096; agentcore itself imposes no default (0 defers to the provider), so this service-level policy lives here.
const DefaultPersona = `` /* 369-byte string literal not displayed */
DefaultPersona is the built-in identity/voice/scope section used when a deployment does not configure its own (see [Config.Persona] in internal/config). A deployer replacing this is only replacing branding — [operatingRules] is never overridable, so the safety-relevant instructions below can't be dropped by a persona override.
const DefaultTurnTimeout = 120 * time.Second
DefaultTurnTimeout is the overall wall-clock bound on a single conversation turn when Deps.TurnTimeout is unset. It covers every model call, tool call, and retry backoff in the turn together, so a stuck real model or tool cannot pin a request forever. Expiry ends the turn canceled (StateCanceled), distinct from a normal completion.
const SummaryBatchTurns = 15
SummaryBatchTurns is how many of a conversation's oldest turns one compaction pass folds into a summary (a small fixed batch, not "everything Truncate would otherwise drop" — see docs/conversation-summarization-design.md #3). If the stored turns already begin with a summary turn (a conversation being compacted for the second-plus time), that summary is included as the first thing folded into the new one — "anchored iterative summarization" — simply because it's turns[0] and this batch always starts from turns[0].
Variables ¶
var ErrNothingToCompact = errors.New("agent: no conversation history to compact")
ErrNothingToCompact is returned by Conversation.Compact when the conversation has no stored history to summarize (new conversation, or already a single summary turn with nothing else to fold in).
Functions ¶
func BuildSystemPrompt ¶
BuildSystemPrompt assembles the system prompt for a task: the persona (deployer-configured, or DefaultPersona when empty), the fixed operating rules, then the composed provider-knowledge addendum (already provenance-labelled) as a trailing section when non-empty.
func ResolveModel ¶
ResolveModel builds the concrete agentcore.Model for a model configuration:
- mock: the scripted in-process model (no secrets, no network).
- gateway: the OpenAI-compatible adapter pointed at the Envoy AI Gateway, with NO model credential (the gateway injects it). It may still present GatewayTokenFile to prove which workload is calling, where the gateway requires that. Custom CA / insecure TLS from config is honored via the HTTP client.
- anthropic: the Anthropic Messages adapter over the configured API key.
The returned model's mode for attribution purposes is string(cfg.Mode); pass it as Deps.ModelMode so gateway attribution headers are gated correctly.
Types ¶
type Conversation ¶
type Conversation struct {
// contains filtered or unexported fields
}
Conversation runs conversational tasks against a fixed set of dependencies. It is safe for concurrent use: each Conversation.Run is independent.
func (*Conversation) Compact ¶
func (c *Conversation) Compact(ctx context.Context, params Params) error
Compact is the manual, user-triggered entry point for history compaction (the /compact command) — it summarizes unconditionally, skipping CompactionThresholdRatio's automatic-trigger check, so a user can collapse history at any point rather than waiting for it to near the budget. It requires History to be configured and at least one stored turn; unlike the automatic path it reports failure to the caller instead of silently falling open, since a user who explicitly asked to compact should know if it didn't happen.
func (*Conversation) Entitlements ¶
func (c *Conversation) Entitlements(ctx context.Context, projectName string) []capability.ServiceEntitlement
Entitlements returns the provider services projectName is entitled to, through the SAME Source fetch and capability.ScopeDocuments gate a turn's Compose runs, so the agent card can never promise a service the next turn would not compose. It performs no MCP connect: a card claims entitlement, not health.
func (*Conversation) Run ¶
func (c *Conversation) Run(ctx context.Context, params Params) *Stream
Run starts a conversation task and returns a Stream of its events. The caller drains the stream with Recv until io.EOF, then reads Stream.Result. Composition always happens; the MCP sessions are always closed and usage is always metered (best-effort) when the stream finalizes.
type Deps ¶
type Deps struct {
// Model is the resolved language model (mock, anthropic, or gateway).
Model agentcore.Model
// ModelMode is "mock", "anthropic", or "gateway". It gates the gateway
// attribution headers; those are never sent in other modes.
ModelMode string
// Source supplies a project's capability documents. Nil means the
// assistant runs with no provider capabilities.
Source capability.Source
// Persona overrides the identity/voice section of the system prompt
// (empty uses [DefaultPersona]). The fixed operating rules always follow
// it — see [BuildSystemPrompt].
Persona string
// Emitter delivers usage events. Required (a no-op emitter is fine).
Emitter *usage.Emitter
// HTTPClient fetches provider knowledge. Nil uses the default client.
HTTPClient *http.Client
// History replays and records conversation turns per (project, contextId),
// making follow-up messages in the same A2A context conversational. Nil
// disables memory: every turn is answered standalone.
History history.Store
// SummarizationDisabled skips compaction entirely: loadHistory behaves
// exactly as it did before summarization existed — plain Truncate, no
// extra summarize model call, no History.Compact call ever issued. False
// (the default) compacts aging history once replay crosses
// CompactionThresholdRatio of the token budget. An escape hatch for
// load-testing or a customer that wants zero synthetic model calls
// injected into their history (see
// docs/conversation-summarization-design.md "Still open").
SummarizationDisabled bool
// Memory backs the memory_remember / memory_forget tools: durable,
// project-scoped facts that persist across conversations and users (unlike
// History, which is per-conversation and windowed). Nil disables the
// feature — no tools are composed.
Memory memory.Store
// PlatformAPI backs the base platform tools (resources_list,
// resources_get, schema_get, locations_list, quota_get and, when a plan
// token key is configured, the write path). Unlike a provider's tools
// these are not entitled per project and not namespaced under a service:
// every project has them. They act as the CALLER — composition binds the
// client to the caller's own bearer token and the turn's project — so the
// service reads nothing of its own. Nil disables them entirely.
PlatformAPI *projectapi.Client
// PlanTokenKey enables the base tools' change path (resources_validate,
// resources_plan, resources_apply). Empty leaves it out: a service that
// cannot check a token must not issue one. Ignored when PlatformAPI is
// nil. See internal/plantoken.
PlanTokenKey []byte
// GapReports backs the report_capability_gap__<service> tools: lets
// the model flag that a provider service is missing a tool or lookup
// a user needed, written to THAT PROVIDER's own project (see
// internal/gapreport), never to params.ProjectName. Nil disables the
// feature entirely.
GapReports gapreport.Store
// CapabilityIdentityForwardHosts is the operator-sanctioned set of MCP
// endpoint hosts that may receive the caller's own bearer token and the
// turn's project. Empty (the default) forwards to nobody; a capability
// document naming an endpoint never sanctions it. See internal/capability.
CapabilityIdentityForwardHosts []string
// AllowPrivateCapabilityNetworks relaxes the capability SSRF guard's
// loopback/RFC1918 block (link-local/metadata stay blocked either way). The
// platform's capability endpoints are in-cluster private ClusterIPs, so real
// deployments set this true; it is threaded into capability.Compose.
AllowPrivateCapabilityNetworks bool
// StepLimit and MaxOutputTokens override the loop defaults when > 0.
// HistoryTokenBudget overrides DefaultHistoryTokenBudget when > 0.
StepLimit int
MaxOutputTokens int
HistoryTokenBudget int
// TurnTimeout bounds the total wall-clock time of one turn when > 0;
// otherwise DefaultTurnTimeout applies. A negative value disables the
// per-turn deadline (the turn is bounded only by the caller's context).
TurnTimeout time.Duration
// MaxRetries, RetryBaseDelay, and RetryMaxDelay tune the loop's transient-
// failure retry (rate limit / overload / transient transport). Zero values
// use the agentcore defaults; a negative MaxRetries disables retries.
MaxRetries int
RetryBaseDelay time.Duration
RetryMaxDelay time.Duration
// Logger receives orchestration logs. Nil discards them.
Logger *slog.Logger
// Metrics records assistant_conversation_turn_duration_seconds,
// assistant_tool_call_total, assistant_model_call_duration_seconds,
// assistant_history_compaction_total, and (via capability.ComposeOptions)
// assistant_gap_report_total — see internal/metrics. Unlike Memory or
// GapReports this is never optional: [New] backs a nil value with a
// fresh, unshared instance (mirroring the Logger fallback just above) so
// metrics always record. Production boot shares one instance between
// this and server.Deps.Metrics so both land on the same /metrics
// endpoint.
Metrics *appmetrics.Metrics
}
Deps are the injected collaborators a Conversation needs. They are set once from configuration; none is read from the environment here.
type Event ¶
type Event struct {
Kind EventKind
Text string
ToolName string
// ToolCallID correlates an [EventToolCall] with its [EventToolResult];
// providers that omit an id leave it empty.
ToolCallID string
// ToolInput is the call's raw JSON arguments ([EventToolCall] only). It is
// caller-summarized before leaving the service — see internal/a2a.
ToolInput json.RawMessage
// ToolFailed marks an [EventToolResult] the tool reported as an error.
ToolFailed bool
}
Event is one streamed happening during a run. The A2A layer translates these into SSE status updates.
type EventKind ¶
type EventKind string
EventKind discriminates streamed [Event]s.
const ( // EventText is an incremental fragment of the assistant's answer. EventText EventKind = "text-delta" // EventToolCall announces that the model invoked a provider tool. EventToolCall EventKind = "tool-call" // EventToolResult reports that a tool call finished, successfully or not. EventToolResult EventKind = "tool-result" )
type Mention ¶
Mention is one resource the user pointed at with "@kind/name" in the chat. It is declared here rather than reused from internal/a2a for the same reason [AgentRunner] lives there and not here: the orchestration layer does not import the transport.
type Params ¶
type Params struct {
// UserText is the user's message.
UserText string
// ProjectName scopes capabilities and metering. Empty disables metering.
ProjectName string
// ContextID is the A2A contextId == conversation id == metering resource.
ContextID string
// TaskID is the A2A task id, used for logging.
TaskID string
// Mentions are the resources the user referenced with "@kind/name"; they
// add one system note to this turn (see mentionNote).
Mentions []Mention
}
Params identifies one task to run.
type Result ¶
type Result struct {
State State
Text string
Error string
Usage UsageSummary
UsageEvents []usage.Event
}
Result is the terminal outcome of a run, available from Stream.Result after the stream reaches io.EOF.
type Stream ¶
type Stream struct {
// contains filtered or unexported fields
}
Stream is the event stream of one running conversation. It also accumulates the terminal Result, available after Recv reports io.EOF.
func (*Stream) Close ¶
Close releases the stream's resources. It is safe to call more than once and finalizes the run (metering + session close) if that has not happened yet.
func (*Stream) Recv ¶
Recv returns the next user-facing Event, or io.EOF once the run is complete. On io.EOF the run has been finalized (sessions closed, usage metered) and Stream.Result is ready.
type UsageSummary ¶
type UsageSummary struct {
InputTokens int64
OutputTokens int64
CacheReadTokens int64
CacheWriteTokens int64
TokenEventCount int
ToolInvocationEventCount int
// Emitted is true when a collector was configured and accepted the batch.
Emitted bool
}
UsageSummary is the run's aggregated token usage and metering outcome.