Platform API
Reference for the scoped host services available to plugin code.
Why Platform Exists
Platform is the plugin-scoped service surface passed through capability contexts.
It exists to solve two problems:
- plugin code should only see the services it is allowed to use
- host internals should stay internal
Instead of handing plugins a global service container, Anna passes Platform through typed contexts such as ToolContext, RuntimeContext, and AdminContext.
Available Services
Platform exposes:
Logger()ConfigStore()StateStore()Scheduler()Notifier()Auth()RuntimeLookup()ChannelPlatform()ReflectPlatform()
Some of these are generic and safe for many plugins. Some are specialized for specific runtime types.
Logger
Use Logger() for plugin-scoped structured logs.
Build: func(ctx pkgplugins.ToolContext) (tools.Tool, error) {
ctx.Platform.Logger().Info("building tool")
return NewTool(), nil
}The host scopes the logger to the plugin automatically.
ConfigStore
ConfigStore() gives the plugin access to its own config row only.
state, err := ctx.Platform.ConfigStore().Get(ctx)
if err != nil {
return nil, err
}Use this when the plugin needs to read or update its own desired config outside normal admin flows.
StateStore
StateStore() gives the plugin a scoped persistence store for operational state.
This is not the same thing as config. Use it for derived state such as cursors, checkpoints, or review watermarks.
err := ctx.Platform.StateStore().Set(ctx, pkgplugins.StateScope{
Kind: pkgplugins.StateScopeSession,
ID: sessionID,
}, "review_watermark", map[string]any{
"reviewed_at": timestamp,
})The plugin does not pass its own plugin ID. The store is already scoped.
Scheduler
Scheduler() gives a scoped scheduler for plugin-owned jobs.
err := ctx.Platform.Scheduler().ReconcileJobs(ctx, []pkgplugins.SchedulerJobSpec{{
Key: "review",
RuntimeName: RuntimeName,
Name: "Reflect Review",
Schedule: pkgplugins.SchedulerSchedule{
Every: "30m",
},
Enabled: true,
}})Again, the plugin does not supply its plugin ID. Scoping is handled by the host.
Notifier
Notifier() gives access to user-visible notifications.
The notify tool is the clearest example:
Build: func(ctx pkgplugins.ToolContext) (tools.Tool, error) {
service := ctx.Platform.Notifier()
if service == nil {
return nil, nil
}
return &Tool{service: service}, nil
}Use it when your plugin needs to send completion updates, alerts, or scheduler-driven messages.
Auth
Auth() provides user and linked-identity lookup.
Use it when a plugin needs to understand the current user or route platform-specific behavior through Anna's identity model.
RuntimeLookup
RuntimeLookup() resolves running managed runtimes by plugin ID and runtime name.
This is typically used by status handlers or plugins that need to inspect another runtime they own.
handle, ok := build.Platform.RuntimeLookup().Lookup(PluginID, RuntimeName)
if !ok {
return map[string]any{"state": "stopped"}, nil
}
snap, err := handle.Snapshot(ctx)
if err != nil {
return nil, err
}
return map[string]any{"state": snap.State, "metadata": snap.Metadata}, nilChannelPlatform
ChannelPlatform() is specialized for managed channel runtimes.
It gives access to:
- parent context
- channel handler
- notification registry
The Telegram plugin uses it to construct its managed runtime:
channelRuntime := platform.ChannelPlatform()
parent := channelRuntime.ParentContext()
handler := channelRuntime.Handler()
notifications := channelRuntime.Notifications()This service is only meaningful for channel runtime plugins.
ReflectPlatform
ReflectPlatform() is specialized for the Reflect runtime.
It gives access to:
- parent context
- memory provider
- reflect store
- workspace
- provider registry construction
This is intentionally specialized. It exists because Reflect is a complex managed runtime with narrow but unusual dependencies.
Contexts Determine What Else You Get
Platform is not the whole story. Each capability context also carries capability-specific fields.
Examples:
ToolContext:
PlatformPathsUserRootToolsBinDirAnnaHomeAgentRootProjectRoot
Runtime
ProjectRoot is the current attached project, if any. Project-aware tools should resolve relative paths from ProjectRoot instead of depending on runner cwd details.
Runtime is a capability interface for tool file and process operations. Plugin tools should use it rather than importing sandbox internals directly.
RuntimeContext:
PlatformState
MemoryContext:
PlatformStateDBAnnaHomeSummarizerFn
That design keeps Platform focused while still giving each capability the extra inputs it actually needs.
Platform Usage Guidelines
Use Platform when:
- the service belongs to the host
- the plugin should only access its own scoped slice of that service
- the service is needed at build or runtime time
Do not use Platform as a general dumping ground. If a capability needs a new host-owned service, add it deliberately and document why.
Good Patterns
- keep all host access inside
Build,Run, or runtime construction functions - use
StateStore()for derived operational state, not config - use
ConfigStore()for desired config, not runtime snapshots - use
RuntimeLookup()in status paths instead of storing global pointers - keep specialized services specialized, as with
ChannelPlatform()andReflectPlatform()
Bad Patterns
- caching global host service bags in package-level variables
- passing plugin IDs back into already scoped stores
- mixing config persistence and operational state persistence
- reading another plugin's state by bypassing the scoped API
The design works best when plugin code stays inside the boundary the host gives it.