AgentSlot requires Go 1.25 or newer. The official file tools use the
root-confined atomic rename support introduced with os.Root.Rename.
Typed component slots and deterministic composition for agent systems.
Start with the AgentSlot Standard Component Map: English | 简体中文. It is the authoritative inventory of customizable agent components, their Slot IDs, cardinality, profile requirements, and implementation maturity.
Release history: CHANGELOG. Pre-1.0 validation releases are intended for real-project feedback and do not promise API stability.
A module unifies registration and lifecycle. A slot defines the component ecosystem, interface, cardinality, and ordering rule.
The versioned ComponentCatalog in the public
componentcatalog package is the structured source for
Slot identity, cardinality, profile requirements, contract availability,
maturity, and evidence gaps. The English and Chinese component maps are its
generated public views and remain the human-readable maturity scorecard. The
catalog contains no component instances and is never consulted by Runtime
assembly.
The standard AgentRuntime fixes Session ownership, CAS, cancellation, and
Gateway control. Its Run-control policy is the unique replaceable agent.loop
Slot. The other replaceable parts include one SessionStore, one ModelExecutor,
one independent TokenCounter, zero or more Tools, ordered Context and Hook
components, one or more GatewayChannels, optional keyed InteractionCommands,
Goal completion evaluation, long-term Memory, multi-agent Workflow,
Policy/Approval, synchronous usage/billing guards, and passive observation chains.
AgentSlot makes those cardinalities explicit and validates the assembled system
before startup.
The standard profile includes an independent model.token-counter for
authoritative pre-call planning counts. agent.loop is a constrained
execution-strategy Slot: a Loop receives only the current Run identity, state,
and ordered actions for model requests, prepared tool batches, continuation,
waiting, and termination. Runtime retains Session truth, budgets, cancellation,
recovery, and terminal commits.
gateway.channel is also the protocol extension seam. gRPC, WebSocket, SSH,
and inbound ACP may be implemented independently without adding a transport
Slot. Outbound ACP, where this Agent calls another Agent, belongs to
agent.provider; sharing a codec does not merge their lifecycles or evidence.
Workspace is now a Contracted optional resource-isolation scope rather than a
filesystem-root API. Runtime propagates the Session-owned Agent/Workspace
identity to policy and Tool calls; an installed Manager resolves an opaque
boundary and rejects missing resources without falling back to process state.
Long-lived tool content reuses artifact.store; the duplicate
tool.output-store map entry has been removed. ToolInvocation now carries an
explicit inline-output byte budget, ToolResult carries standard Artifact
metadata references, and Runtime rejects violations before continuing the Run.
credential.resolver is now Contracted after validation with bearer and basic
credential shapes. Outbound adapters retain only a non-secret Ref, resolve
material inside one physical-operation callback, and may copy only the opaque,
non-reversible identity into billing or audit facts. The OpenAI-compatible
adapter resolves its bearer credential afresh for every physical retry.
session_history is a standard keyed, read-only Tool rather than another Slot.
It holds only HistoryReader, returns a model-safe revision/sequence projection,
preserves complete logical Steps under the inline output budget, and defaults
to the current Workspace. Current-Session and explicitly authorized full-access
ceilings are available at construction. The agentslot init terminal wizard
consumes ComponentCatalog and generates explicit Go assembly code. Its default
local-coding preset keeps writes, shell commands, and external side effects
behind approval; minimal-chat installs no Tools, Workspace, Artifact, or
Approval components. The generator reads and writes no credential material.
The generic core remains product-neutral. A standard LLM Agent explicitly uses
standardagent.NewApplication, which returns the same *agentslot.Application
and automatically mounts the fixed AgentRuntime/Gateway layer, an inspectable
standard AgentLoop default, and the standard Agent profile. It does not infer a
profile from installed slots. All standard Agent
projects therefore keep the same Build, Start, Run, and Runtime.Stop
entry points; only the declared modules, configuration, and application name
vary.
Calling standardagent.NewApplication is the explicit choice of that standard
profile and its standard Loop fallback. The generic agentslot.NewApplication
never selects a Loop or any other domain component.
AgentSlot is a pre-1.0 foundation. v0.1.10 is the current validation release
and is intended to be consumed by real Agent projects. It adds checked model
stream lifecycles, portable Executor conformance, durable Run termination,
independent token counting, controlled Loop actions, late-bound credentials, trusted
Workspace boundaries, bounded Tool results, durable Artifact and Workspace
references, safe Session history access, explicit project generation, and
remote gRPC/ACP Gateway channels. It is not a
stable-production claim, and public APIs may still change based on integration
feedback. The composition core works today; the standard
component map is normative, while its ecosystems remain at their explicitly
recorded maturity. Every published tag is immutable; later changes receive a
new semantic version.
All implemented public boundaries remain at least Contracted.
model.token-counter is Contracted and independently replaceable from
model.executor. session.store is Conformant against the exact v0.0.10
contract; model.executor is Conformant against model.executor/v1.
No ecosystem is yet Proven.
A released agentslot binary pins its own exact AgentSlot version. A source
checkout must supply the release explicitly:
go run ./cmd/agentslot init --agentslot-version v0.1.10 ./my-agentAn interactive terminal selects a preset and collects only non-secret provider,
model, CredentialRef, Workspace, and storage settings. Non-interactive use
defaults to local-coding and exposes the same choices as flags. Repeat
--add-implementation or --remove-implementation to customize the Catalog
selection; required dependencies are either added with a visible reason or
rejected before any file is written.
The generated project contains a fixed go.mod, explicit Module assembly,
standard Profile requirements, a strict Tool allowlist, approval policy, and a
build test. It contains no local replace directive and never overwrites an
existing target directory.
interaction/grpcchannel implements the complete
GatewayAccess surface as gateway.channel/remote-grpc/v1. It uses a bounded
protobuf BytesValue envelope so uint64 revisions and History sequences remain
exact, and provides a matching GatewayAccess client. Install the server with
standardagent.NewGatewayChannelModule; the standard wrapper owns its listener
lifecycle after the fixed Gateway is available.
Authentication and authorization callbacks are mandatory. Authenticated
remote_user, service, or agent identity replaces every caller-supplied
Actor value before a write reaches Gateway. TLS, credentials, rate limits, and
deployment remain product configuration through gRPC server options and the
callbacks. A disconnected SendAndWait client does not cancel the durable Run;
application shutdown still does.
interaction/acpchannel implements the stable ACP v1
inbound surface as gateway.channel/inbound-acp/v1. The product-owned transport
must supply an already-authenticated remote identity and an authorization
callback. The channel fixes Agent, Workspace, and working-directory scope;
supports ACP initialize, session new/list/resume/prompt/cancel/close; and maps
complete durable assistant messages to ACP updates. Peer disconnect does not
cancel the durable Run, while ACP session/cancel does.
The profile advertises only what it implements. It accepts ACP's required text
and resource_link prompt blocks, projecting links to deterministic readable
text. It does not advertise image, audio, embedded context, session load, MCP,
ACP authentication, modes, or configuration options. Outbound ACP remains an
agent.provider concern.
| Concept | Meaning |
|---|---|
Application |
Named root host that automatically mounts its module list and provides standard build, start, and run entry points. |
Module |
Registration and lifecycle owner. A module may contribute to several slots. |
One[T] |
Zero or one implementation. A profile can require exactly one with RequireOne. |
Many[T] |
Zero or more implementations with unique keys, such as tools or model providers. |
Chain[T] |
Zero or more ordered contributors, such as hooks or prompt contributors. |
Requirement |
A profile constraint or a module dependency expressed against a slot, never a concrete provider type. |
Assembly |
Immutable result of validated composition. Application.Build() returns this shared application-level assembly. |
AssemblyDescription |
Versioned JSON-safe assembly description returned by Assembly.Describe(). |
Runtime |
Started module lifecycles owned by one Assembly; standardagent attaches its fixed Gateway and Runtime registry to this lifecycle. |
AgentRuntime |
Framework-owned per-Session command and execution object created by explicit Session create/resume; it is not a Slot. |
The code examples use assembly, Assembly, AssemblyDescription, and
Runtime.Assembly(). No compatibility aliases for the removed Plan names are
provided before 1.0.
The generic Module interface is deliberately not the component interface. The slot's T is the component interface:
type TextGenerator interface {
Generate(context.Context, string) (string, error)
}
type Tool interface {
Name() string
}
type ToolCatalog interface {
Keys() []string
}
var (
GeneratorSlot = agentslot.One[TextGenerator]("example.text-generator")
ToolSlot = agentslot.Many[Tool]("example.tool")
CatalogSlot = agentslot.One[ToolCatalog]("example.tool-catalog")
)These are deliberately application-local examples of the generic composition API, not AgentSlot-owned standard domain contracts. The standard Slot IDs and their maturity are tracked only in the component map.
Modules then contribute implementations:
func (m Bundle) Register(r agentslot.Registrar) error {
return r.Contribute(
agentslot.Set(GeneratorSlot, m.generator),
agentslot.Add(ToolSlot, "shell", m.shell),
agentslot.Add(ToolSlot, "files", m.files),
)
}An application profile may also install an explicit fallback module without forcing every caller to remove that module before supplying a replacement:
func (m DefaultGeneratorModule) Register(r agentslot.Registrar) error {
return r.Contribute(agentslot.SetDefault(GeneratorSlot, m.generator))
}Defaults are resolved from the complete module list during Build, so module
installation order does not decide which implementation wins:
- an explicit
Onecontribution replaces all defaults for that slot; - an explicit
Manycontribution replaces defaults with the same key; - any explicit
Chaincontribution replaces the complete default chain; - multiple defaults for the same
OneorManykey still fail when no explicit contribution resolves the ambiguity.
SetDefaultWith, AddDefaultWith, and AppendDefaultWith provide the same
behavior for build-time constructors. A fully overridden default module is
absent from the final Assembly, its constructor is not called, and its
lifecycle is not started. Defaults are never discovered or injected globally:
the application must still list every fallback module explicitly.
Official reference packages expose this choice without changing their older
explicit wrappers. For example, Goal, Memory, and Workflow provide named
NewDefault...Module constructors for profiles that want replaceable reference
defaults; New...Module continues to mean an explicit component selection.
When one contribution must be constructed from other installed components,
use a build-time constructor instead of manually assembling it before
Build:
func (m CatalogModule) Register(r agentslot.Registrar) error {
return r.Contribute(agentslot.SetWith(CatalogSlot, func(resolver agentslot.Resolver) (ToolCatalog, error) {
tools, err := agentslot.ResolveMany(resolver, ToolSlot)
if err != nil {
return nil, err
}
return NewToolCatalog(tools), nil
}))
}
func (m CatalogModule) RequiredSlots() []agentslot.Requirement {
return []agentslot.Requirement{
agentslot.RequireMany(ToolSlot, 1),
}
}Build-time constructors run only after defaults, requirements, and cycles have
been resolved. Their resolver can read only the owning module's declared
requirements and closes when the constructor returns. Constructors prepare
components; lifecycle resources still belong in Start and Stop.
Every product declares its name, module list, and required profile through the same application entry:
application := agentslot.NewApplication(
"my-agent",
[]agentslot.Module{
catalogModule,
generatorModule,
shellToolModule,
},
agentslot.RequireOne(GeneratorSlot),
agentslot.RequireOne(CatalogSlot),
agentslot.RequireMany(ToolSlot, 1),
)
assembly, err := application.Build()
runtime, err := application.Start(ctx)
defer runtime.Stop(shutdownCtx)
catalog, _ := agentslot.Get(assembly, CatalogSlot)
_ = catalogApplication.Start also calls Build automatically when the caller does not
need a separate inspection step. Application.Run starts a service-style
application, waits for context cancellation, and shuts it down. Every product
therefore uses the same Build, Start, and Stop control flow; only its name,
modules, configuration, and profile differ. Builder remains the lower-level
composition primitive used by the application host.
The module list is explicit, but mounting it is automatic during Build.
AgentSlot does not scan packages or use init() side effects. Declared
constructors automatically receive the slot contributions they depend on, so a
tool module can join the selected loop without a product-level bootstrap
function wiring the concrete tool into the concrete loop.
A module can declare dependencies without naming their implementation modules:
func (m RunnerModule) RequiredSlots() []agentslot.Requirement {
return []agentslot.Requirement{
agentslot.RequireOne(GeneratorSlot),
agentslot.RequireKey(ToolSlot, "shell"),
}
}Build resolves explicit-over-default selection, validates requirements,
rejects dependency cycles, constructs deferred contributions in dependency
order, and computes a stable lifecycle order. assembly.Describe() exposes
that order, slot kinds, Go value types, contribution owners, keys, whether each
selected contribution was explicit or default, module requirements, and
profile requirements without serializing component values.
Optional component ecosystems are still declared dependencies rather than
looked up dynamically. OptionalOne, OptionalMany, and OptionalChain add
lifecycle edges whenever providers are installed, while allowing a build with
no provider. Constructors resolve them with ResolveOptionalOne,
ResolveMany, or ResolveChain; the build-scoped resolver remains closed
outside construction.
See the complete runnable basic example.
- One module registration is transactional: one rejected contribution discards all contributions from that module.
- A successful build freezes the builder; a failed build can be corrected and retried.
- Build-time constructors resolve only declared slot dependencies; failed construction does not publish an Assembly or freeze the builder.
Application.Startbuilds automatically whenBuildwas not called explicitly.- Modules start in stable dependency order; independent modules retain installation precedence.
- A failed start stops every previously started module in reverse order.
- Normal shutdown attempts every started module in reverse order and joins their errors.
For module dependencies, RequireOne depends on the sole provider, RequireKey depends only on the named provider, and RequireMany or RequireChain depends on every contribution visible in that slot. Product-only profile requirements validate cardinality but do not add lifecycle edges.
Constructors may be retried after a failed build, so they must not start goroutines, open listeners, or acquire non-repeatable resources. Those effects belong to module lifecycle methods after an Assembly has been built successfully.
The AgentSlot Standard Component Map is the source of truth for the agent architecture. It currently maps runtime, model, tools, context, history, memory, execution, policy, multi-agent workflow, gateway, billing, and operations ecosystems.
The framework-side requirements for the next reliability work are defined in
the AgentSlot 运行可靠性设计, with separate
designs for Session/Runtime consistency and the Model execution boundary.
The Hook 扩展边界设计 distinguishes the
narrow legacy agent.hook, durable typed hook.InputGate / hook.ToolPreflight / hook.ToolResultHook / hook.CompletionGate
contracts, and a consuming product's user-configurable Hook system. It also
defines the admission rules for future lifecycle seams.
A runnable standard LLM agent requires exactly one AgentLoop, exactly one
SessionStore, exactly one ModelExecutor, and at least one GatewayChannel. The
fixed SessionManager, AgentRuntime, and in-process Gateway are supplied by the
framework. The standard Loop is a conditional default: an explicit Loop Module
replaces it without deleting or naming the default Module.
Standard applications install each Channel with
standardagent.NewGatewayChannelModule; Build rejects a raw GatewayChannel
contribution that would bypass GatewayAccess binding or lifecycle ordering.
Tools, ModelProviders, Context components, and AgentHooks are optional globally;
an installed ModelExecutor may explicitly require one or more ModelProviders.
Installed Tools are not implicitly authorized: AgentRuntimeConfig.ToolKeys is
a strict allowlist, and nil, empty, or omitted values expose no Tools. Selecting
any Tool also requires a positive MaxInlineToolResultBytes; Runtime supplies
that budget to every invocation and fails the Run on a violating result. AgentHook
only proposes follow-on input before Run completion; applied Session commits are
observed separately through the asynchronous session.commit.observer chain.
Products may also set MaxModelAttemptsPerRun and MaxToolCallsPerRun as
independent Run safety limits. Both default to zero (unlimited). Runtime checks
the physical-attempt limit before Provider dispatch and rejects a ToolCall batch
before persistence or effects when the whole batch does not fit. The rejected
operation is recorded as an append-only RunLimitExceededFact; no keyword,
tool-name, or model-generated "progress" heuristic participates in enforcement.
Reaching either hard limit interrupts the Run. It never manufactures a
tool-disabled model request or changes a non-converged Run into a successful
completion. Product-level verification and candidate collection remain
independent from the Run terminal outcome.
interaction.command is an optional Many Slot for structured commands that
register only with the Gateway. Channels render the Gateway's UI-neutral
command directory as slash commands, menus, buttons, forms, or command palettes.
Application.Start creates a started application Runtime that owns one
process-local Session-to-AgentRuntime registry. Every created or resumed
AgentRuntime registered by that application lives in the same process; durable
Sessions that have not been opened do not occupy a Runtime. A framework-internal
Runtime coordinator operates the registry; only the Gateway receives private
Runtime access. Every GatewayChannel receives the same carrier-neutral Gateway
access and cannot obtain AgentRuntime pointers. The Gateway, registry,
coordinator, and private assembly anchors are framework mechanics, not public
Slots. This single-process
ownership boundary is a deliberate part of the standard architecture, not a
first-version limitation.
One Application Assembly serves multiple Workspaces and Sessions. It does not build a separate Assembly per Session. Explicit create/resume initializes one AgentRuntime for that Session; listing Sessions does not. History, Context, Queue, RunJournal, and SessionModelConfig are durable state inside the SessionStore aggregate. Runtime-fixed SystemPrompt, ToolKeys, and Context settings do not change during one Runtime lifetime; the Session's provider, model, reasoning, and model parameters can be changed explicitly while idle and are snapshotted for each Run.
Forking copies stable Session History into a new independent Session. Every
ForkRequest and ForkSessionRequest selects an explicit mode:
ForkFullHistory copies all History and requires an idle source, while
ForkHistoryPrefix copies through CutoffSequence; prefix sequence zero means
an empty History. A stable prefix can be copied while the source Run continues.
Pending Queue, RunJournal, and active Run state are never copied.
workspace.Manager is optional. When installed, create, fork, summary-start,
and resume resolve the exact Session scope before execution; unknown or
unavailable boundaries fail explicitly. The portable Boundary exposes only
its AgentID/WorkspaceID scope—not a root path, URI, credential, or operation—so
filesystem, shell, note, and object-storage capabilities remain separate.
SessionStore.ListSessions returns bounded cursor pages for one exact
Agent/Workspace scope. Results are ordered by UpdatedAt descending and then
SessionID ascending. A cursor is opaque, scoped to the Store lifecycle that
issued it, and excludes Sessions created after the traversal's first page.
Concurrent updates may move an unreturned Session before the cursor, so callers
start a fresh traversal to refresh a resume picker; this deliberately avoids
pretending that every Store can provide a distributed snapshot. An empty
NextCursor completes the traversal. The fixed Gateway preserves the same
Limit, opaque Cursor, and NextCursor fields for every Channel.
The session package includes a reference MemoryStore; the standard framework
constructs its fixed SessionManager over the installed Store and the
Application default model. Together they implement append-only History facts, Context, Queue, RunJournal,
SessionModelConfig inheritance, Run lifecycle facts, revision/CAS commits, and
the basic crash recovery transition. Journal identity compares tool arguments
by exact JSON value rather than serialization bytes, and duplicate object
members are rejected before admission. A Runtime returns to idle after recovery
only when the durable Run has a unique terminal fact; otherwise it fails closed
for explicit resume. These are development and
contract-reference implementations, not a production database; production
storage remains a replaceable session.store Slot. Development
applications can explicitly install session.NewMemoryModule; importing the
package never selects it as a hidden default.
The separate memory.store Slot defines portable Recall, Remember, and Forget
semantics; it does not choose a database, vector index, ranking algorithm, or
retention system. Application developers provide that implementation. The
standard contract preserves typed session-summary, semantic, evidence, and
temporal candidates, source and confidence facts, execution provenance,
visibility, writeback policy, recall intent, and evidence selection. The host
injects execution identity and governance through memory.RuntimeScope; model
tool arguments cannot choose those authoritative values.
Mapping a Slot and standardizing its Go method contract are different maturity steps. A proposed method-level interface needs two independent implementations, one branch-free real consumer, and a conformance suite before it is described as proven portable. The map records this evidence instead of hiding unfinished coverage behind a family name.
The first fixed domain vocabularies are available in the model and
tool leaf packages: model input/output modalities are text, image, and
audio, serialized by those stable names rather than numeric or byte encodings;
model-facing tool inputs use self-contained JSON Schema Draft 2020-12.
Caller and Hook input uses agent.MessageInput, which carries content only;
the fixed Runtime allocates MessageID, Session/Run/Step containment, role, and
timestamp atomically when it creates a durable agent.Message fact.
Thirty-three standard component contracts are now available in the loop,
session, model, tool, context, hook, interaction, policy, observe,
goal, memory, workflow, billing, artifact, workspace, and credential
packages. They are at least Contracted; session.store has a reusable
black-box session.store/v1 result against the exact v0.0.10 contract and is
Conformant, while no domain ecosystem is yet Proven.
The standardagent package implements the application-scoped Runtime registry,
coordinator, fixed Gateway, GatewayAccess binding, GatewayChannel wrapper,
automatic standard profile, the standard AgentLoop default, and the fixed
Session AgentRuntime transaction state machine. Send,
SendAndWait, Steer, RunPending, Cancel, WhenIdle, Close, Queue mutation, and
model-config commands now execute through the Gateway. Subscribe publishes
live chunk/reset events with Run, Step, physical Attempt, and the Runtime-reserved
assistant Message identity shared with the eventual durable Message. The
reserved identity does not make temporary output or client cursors persistent.
Every newly applied commit emits
only a SessionID + Revision notification, after which the client reads the
authoritative SessionView. SessionView contains Queue, model configuration,
state, at most the latest 100 complete logical Steps, and the latest 32 safe
extension diagnostics; older History and extension diagnostics use independent
exclusive sequence cursors. Temporary events may be dropped under pressure. A
subscriber that cannot receive a durable revision notification is closed so it
can reconnect through View instead of growing an unbounded queue. Disconnecting
a subscriber never cancels its Run. Every external write carries
ExpectedRevision; stale writes return a typed conflict and are never retried
implicitly.
SendAndWait wraps the same Run and returns only that Run's durable assistant
text messages plus at most 100 newest diagnostics owned by that Run, rather
than executing a second model path or returning lifecycle/other-Run diagnostics. Run start/terminal
facts retain the frozen model configuration. The
optional hook.input_gate Chain runs before Send, Steer, and queued-input edits.
Its prepared journal commit is the caller's CAS linearization point; accepted
context is stored separately from the unchanged user message and projected only
for the exact Run/Step that claims it. Rejection, component failure, cancellation,
panic, edit/delete/claim races, and restart recovery therefore cannot leave a
half-visible input or replay an unknown external invocation. ClientMessageID
remains correlation metadata rather than an exactly-once key, and input
serialization is scoped per Session without blocking the active Run or other
Sessions. gRPC preserves the typed failure's Session ID, current revision, and
bounded safe diagnostics. The model package includes an explicitly installed deterministic
FakeModelExecutor for development and contract tests, while
model/openaicompat provides a real streaming Chat
Completions-compatible Executor with physical Attempt IDs, bounded output,
retry/reset handling, durable started/terminal Attempt facts, Provider-reported
token usage, and marked local estimates when a failed request has no usage. If a
Provider returns a recognized structured error message, the adapter may record
only that bounded, single-line, sanitized message next to the stable safe error
code. Arbitrary response bodies, credentials, headers, and request content are
not displayable diagnostics. If a
configured model declares image input, that module explicitly requires
artifact.store; attachment references are opened through that contract and
projected as real image content blocks rather than placeholder text. The fixed
Runtime also preserves optional opaque JSON continuation state returned by a
ModelExecutor. Runtime binds that state to the selected Provider/model on the durable
assistant Message and carries it unchanged through later Context projection
without rendering or interpreting it. Protocol adapters can therefore resume a
tool turn without adding their private wire vocabulary to the standard
interfaces. The fixed Runtime also owns ToolDispatcher semantics: call/pending
and result/terminal commits,
Serial/ParallelSafe batches, safe structured failures, and mandatory model
continuation. tool/bash is the first explicitly installed
built-in Tool; it fixes working directory, explicit environment, timeout,
process-group cancellation, and separate output limits. It is never installed
by import or by standardagent.NewApplication. The official
tool/files package adds root-confined read/CAS-write/exact-edit
tools, and tool/http adds an allowlisted bounded HTTP client.
The Runtime now persists each ContextSource contribution before use and stores
complete versioned logical requests. LatestOnly keeps only the newest request;
RetainAll also preserves every prior Step request, including its prompt, model
configuration, tools, and attachment projection. It runs ordered ContextSource and Hook
chains, enforces model protocol, hard context limits, and the optional per-Run
token, model-attempt, and ToolCall limits. ContextContribution facts are projected only into their exact
Run/Step, so append-only audit history does not cause one-shot extension or
ContextSource payloads to be resent on every later model request. The Runtime projects unsupported
attachments without rewriting History, and validates idle-only model switches
through ModelExecutor capabilities. model.catalog has a typed contract and
an explicit StaticCatalog reference implementation. Applications can
explicitly install interaction.NewModelCommandModule; slash, menu, and
structured clients then invoke that one Gateway command backend. The
interaction/inprocess package provides a function-style GatewayChannel, and
interaction/cli provides a lifecycle-owned line protocol;
both expose only GatewayAccess. hook.ToolPreflight can contribute ordered,
durably journaled allow, deny, or require-approval advice before tool
authorization. Static exact/all scope is frozen at build time; ToolCall and the
complete matching reservation set are prepared atomically. policy.guard and
approval.service still gate tool execution, and Hook allow cannot override
them. A durable ToolCall crosses to pending only immediately before
invocation. Recovery can resume only an exact prepared call/preflight set;
pending calls or preflights become outcome_unknown and are never replayed.
With no ToolPreflight contribution, no extension commit is added and
ParallelSafe tools retain their original scheduling. hook.ToolResultHook
sees only certain succeeded/failed results from Tools that actually entered
pending. ToolResult, terminal Journal, next-Step identity, and matching Post
reservations commit atomically; bounded context is appended once to that next
Step only after the complete Post chain succeeds. Failures preserve the
original ToolResult, discard partial context, interrupt before another model
request, and never replay pending commands after recovery. With no
ToolResultHook contribution the result path adds no extension state or extra
commit. hook.CompletionGate owns the fail-closed natural-completion seam:
ordered gates may complete or continue the same Run with one-shot context for
a preallocated next Step. Accepted steer and cancellation supersede stale gate
work; Goal continue/blocked decisions skip gates, while Goal done remains an
uncommitted candidate until every gate completes. Applied continuation cycles
are recovered from the journal without resetting Run token/attempt budgets.
Prepared gates may resume only from matching durable input, pending gates are
never replayed, and no-gate applications retain the legacy fast path.
hook.SessionLifecycle is the ordered Runtime-instance boundary for create,
resume, fork, summary, and explicit close. The coordinator registers one
opening Runtime before evaluating SessionStart; every Gateway operation and
structured command action waits for that same opening barrier. Open context is
appended once to the first Run/Step, never rewrites a Message, and is not
projected into later Steps. A newer unconsumed result from the same lifecycle
component supersedes its older pending open context, preventing restart-only
Sessions from accumulating duplicate model input. CloseSession prepares the
complete SessionEnd chain under the caller's revision CAS, cancels and settles
active Run/Prompt work, then returns a final revision plus payload-free
diagnostics. Component failure is diagnostic and does not turn an otherwise
safe open/close into failure; persistence convergence failure still does.
Disconnect, application shutdown, and process crash never forge SessionEnd.
With no matching lifecycle contribution, open and close add no journal commit.
Trace,
Metric, Audit, and Usage chains are passive. Every durable extension transition
uses the same detached diagnostic projection for Gateway and Audit; Metric
sinks may receive journal entry-count and serialized-byte gauges, never the
journal payload itself. The Runtime classifies ExtensionJournal entries once
on open: abandoned lifecycle work settles before the current SessionStart,
non-replayable Prompt/Post work settles in one post-open commit, and only
prepared ToolPreflight/Completion work backed by durable Run evidence may
resume. These recovery transitions append new Store changes and never rewrite
History. observe/jsonlines is an explicit
default sink. session.FileStore is a crash-safe
single-process persistent implementation. Sessions without extension
invocations remain byte-schema-compatible agentslot.session-file/v1 files.
The first real ExtensionJournal entry atomically upgrades only that Session to
agentslot.session-file/v2; new code reads both versions and never downgrades a
v2 Session. Older binaries cannot read an upgraded v2 file, and no sidecar
Store disguises that local downgrade boundary. The previous
session.store/v1 conformance result remains evidence for the pre-extension
contract; the expanded contract is Contracted until a complete
session.store/v2 black-box suite passes. MemoryStore and FileStore share one implementation codebase, so
they do not establish Proven maturity; no Web/RPC transport, distributed
Session lease, or reliable-delivery ACK is implied. Importing any package still
has no registration or startup side effect.
GatewayAccess.ExtensionDiagnostics returns a payload-free, newest-first page
using an immutable extension-sequence cursor (default 50, maximum 100). It does
not expose commands, environment, process streams, or pending context content.
ExtensionJournal state is recovery metadata; Session History remains strictly
append-only and is never rewritten by an extension transition.
Runtime consumers can atomically batch a terminal transition, its business
mutation, and effect finalization in one Store commit, avoiding an unnecessary
extra FileStore rewrite without weakening the recovery boundary.
examples/reference assembles the complete public path:
file-backed Sessions, the OpenAI-compatible Executor, fixed Runtime/Gateway,
CLI, model command, Policy/Approval, Bash, file and HTTP tools, and JSON Lines
observations. It never obtains an AgentRuntime or branches on a concrete
component type. Its Runtime explicitly selects LatestOnly, an unlimited
per-Run token budget, default-off attempt/ToolCall limits, a strict ToolKeys
allowlist, and a local CLI ActorIdentity.
The end-to-end test also attaches an in-process Channel to the same Gateway and
verifies persisted Context, paired physical Attempts, tool facts, resume, View,
and cursor-based History pagination.
AGENTSLOT_API_KEY=... \
AGENTSLOT_MODEL=... \
go run ./examples/referenceAGENTSLOT_PROVIDER_URL defaults to https://api.openai.com/v1.
AGENTSLOT_WORKSPACE, AGENTSLOT_SESSION_DIR, and the comma-separated
AGENTSLOT_HTTP_HOSTS configure explicit local and network boundaries. Tool
effects are denied unless AGENTSLOT_APPROVE_EFFECTS=true; file_read remains
read-only and does not require that approval. In the CLI, ordinary lines are
user messages, while /help, /model, /cancel, /pending, and /quit are
explicit protocol entries. /model without arguments returns structured
choices; a JSON object supplies a selection.
AgentSlot defines the portable component boundaries and, as evidence permits, the standard domain contracts. Existing runtime, tool, session, memory, and gateway SDKs remain valuable implementation sources and compatibility targets:
- an SDK can expose several independent AgentSlot components while preserving its existing assembly API;
- current products can keep iterating while additive adapters support new AgentSlot assemblies;
- provider- and product-specific behavior stays in the SDK or adapter rather than leaking into the generic composition core.
The core imports no product or SDK. Adapters depend on AgentSlot and the SDK they connect; applications select a profile, modules, configuration, and defaults.
gofmt -w .
go build ./...
go vet ./...Before a reliability-sensitive release, run scripts/reliability-gate.sh.
It executes the deterministic FileStore fault matrix and the complete
race-enabled test, vet, and build gates without Provider credentials or
network access. scripts/reliability-gate.sh --list prints the frozen stages.
Use the standard component map and exported Go documentation as the public reference for the composition model.
Licensed under the Apache License 2.0.