runtime is a standalone Go module containing reusable agent runtime packages extracted from norma.
The repo stays as one Go module. The root import path github.com/normahq/runtime is documentation-only; functional APIs live in the subpackages.
- Use
agentfactorywhen you want to build runtime agents from validated provider config. - Use
appconfigwhen you need config-file loading, profile overlays, and runtime validation. - Use
agentconfigwhen you already have decoded config and need schema validation or normalization. - Use
acpagentwhen you need direct ACP subprocess control. - Use
hostedagentwhen you want to wrap a local API-backed model as an ADK agent. - Use
poolagentto provide ordered failover across providers. - Use
structuredagentto enforce JSON-schema-constrained I/O. - Use
mcpregistryandsessionstateas low-level support packages.
go get github.com/normahq/runtimegithub.com/normahq/runtime/acpagentgithub.com/normahq/runtime/agentconfiggithub.com/normahq/runtime/agentfactorygithub.com/normahq/runtime/appconfiggithub.com/normahq/runtime/hostedagentgithub.com/normahq/runtime/mcpregistrygithub.com/normahq/runtime/poolagentgithub.com/normahq/runtimegithub.com/normahq/runtime/sessionstategithub.com/normahq/runtime/structuredagent
package main
import (
"fmt"
"github.com/normahq/runtime/appconfig"
)
func main() {
settings := map[string]any{
"providers": map[string]any{
"codex": map[string]any{
"type": "codex_acp",
},
},
}
if err := appconfig.ValidateSettings(settings); err != nil {
panic(err)
}
fmt.Println("runtime config is valid")
}package main
import (
"context"
"github.com/normahq/runtime/agentconfig"
"github.com/normahq/runtime/agentfactory"
"github.com/normahq/runtime/mcpregistry"
)
func main() {
providers := map[string]agentconfig.Config{
"codex": {
Type: agentconfig.AgentTypeCodexACP,
},
}
factory := agentfactory.New(providers, mcpregistry.New(nil))
_, err := factory.Build(context.Background(), agentfactory.BuildRequest{
AgentID: "codex",
WorkingDirectory: "/workspace",
})
if err != nil {
panic(err)
}
}package main
import (
"context"
"os"
"github.com/normahq/runtime/acpagent"
)
func main() {
agent, err := acpagent.New(acpagent.Config{
Context: context.Background(),
Command: []string{"opencode", "acp"},
WorkingDir: ".",
Stderr: os.Stderr,
})
if err != nil {
panic(err)
}
defer func() { _ = agent.Close() }()
}ACP session/update.plan notifications are exposed through ADK event state at
event.Actions.StateDelta[acpagent.PlanStateKey]. Each update replaces the
full plan snapshot and is not emitted as a content part.
Optional ACP integration tests are available for real runtimes:
go test -tags='integration,opencode' -count=1 ./acpagent
go test -tags='integration,codex' -count=1 ./acpagentRequirements:
opencodemust be available onPATHfor the OpenCode integration suite.npxmust be available onPATHfor the Codex ACP bridge suite.- External runtime auth and local environment setup must already be configured.
go mod tidy
go test ./...
go test -race ./...
go tool golangci-lint run ./...
task docs:check
task docs:generateThe source comments and example tests are the source of truth for package documentation.
Generate local docs from source:
task docs:generateValidate that every public package has package docs and renders with go doc:
task docs:checkGenerated output is written to .cache/godoc/ and is not committed.
See AGENTS.md for repository-specific guidance.