Forge v0.9.2 — Skill Environment Injection, Docker Images, Agent Reliability Fixes
Release Date: April 17, 2026
Full Changelog: v0.9.1...v0.9.2
Highlights
Forge v0.9.2 delivers automatic environment variable and egress domain injection when saving skills, pre-built multi-architecture Docker images on GHCR, and critical agent reliability fixes for port allocation, stale daemon detection, and OAuth credential handling. This release modifies 25 files with over 1,200 lines of new code across all five modules.
What's New
Skill Environment & Egress Auto-Configuration (#46)
When saving a skill via the UI Skill Builder or forge skills add, Forge now automatically configures the agent's environment — no manual .env or forge.yaml editing required.
How it works:
- SKILL.md frontmatter declares
metadata.forge.requires.envandmetadata.forge.egress_domains - On save, Forge parses requirements and:
- Writes env vars to
.env(deduplicated, skips existing keys and encrypted placeholders) - Merges egress domains into
forge.yamlsecurity.egress.allowed_domains(deduplicated, sorted, YAML formatting preserved) - Reports missing env vars back to the UI for user input
- Writes env vars to
- Frontend displays what was configured and prompts for any remaining required variables
# CLI: forge skills add now also merges egress domains
forge skills add github
# ✓ Added egress domains: api.github.com, github.com
# ✓ Wrote GITHUB_TOKEN to .envNew shared utilities in forge-cli/cmd/skill_env.go:
ParseSkillRequirements()— extracts env requirements and egress domains from SKILL.mdMergeEgressDomains()— adds domains to forge.yaml allowlist with text-based YAML insertionAppendEnvVars()— appends key=value pairs to.envwith deduplicationCheckMissingEnv()— checks OS env +.env+ secret placeholders for missing entries
SkillSaveFunc now returns *SkillSaveResult with structured response fields: path, egress_added, env_configured, env_missing.
Pre-Built Docker Images on GHCR (#46)
Forge now publishes multi-architecture Docker images (linux/amd64, linux/arm64) to GitHub Container Registry on every release.
# Pull the latest release
docker pull ghcr.io/initializ/forge:latest
# Pin to a specific version
docker pull ghcr.io/initializ/forge:v0.9.2
# Run with your agent directory mounted
docker run -v /path/to/agent:/home/forge/agent -w /home/forge/agent \
-e OPENAI_API_KEY=sk-... \
ghcr.io/initializ/forge:latest run --host 0.0.0.0Image details:
- Tags:
v0.9.2,v0.9,v0,latest(semver hierarchy) - Base:
alpine:3.22.4withca-certificates,git,tzdata - Build: Multi-stage with
golang:1.25-alpine, BuildKit cache mounts,CGO_ENABLED=0static binary - Security: Non-root
forgeuser, minimal attack surface - CI: QEMU for cross-platform emulation, GitHub Actions cache for layer reuse
LLM Agent Loop Robustness (#46)
Two fixes to the core agent loop that prevent subtle failures across providers:
Tool call execution fix: The loop now terminates based solely on len(ToolCalls) == 0, ignoring FinishReason. Some providers (notably certain OpenAI models) return FinishReason: "stop" even when tool calls are present — previously this caused the agent to stop mid-execution.
Session recovery deduplication: When a session is recovered from disk after a crash or timeout, the executor checks whether the conversation already ends with an identical user message and skips the duplicate. This prevents the same prompt from appearing twice in the context window on retry.
Q&A nudge suppression: When only explore-phase tools were invoked (e.g., web_search, file_read), the agent no longer sends a continuation nudge ("You stopped..."), correctly treating the response as a final answer to an informational query.
Bug Fixes
Port Collision on Agent Start (forge-ui)
Before: When the UI restarted, PortAllocator lost its in-memory state and always tried port 9100 first — colliding with agents already running on that port from a previous session.
After: Allocate() now does a net.Listen TCP probe to verify the port is actually free before assigning it. Occupied ports are automatically skipped.
Stale Agent Status After UI Restart (forge-ui)
Before: detectExternalAgent() only checked if a port was listening via TCP probe. If serve.json was stale (agent crashed) but another process happened to be on the same port, the dead agent appeared as "running".
After: detectExternalAgent() now checks PID liveness via pidAlive() (signal 0) before the TCP probe. If the PID from serve.json is dead, the stale file is cleaned up and the agent is correctly reported as stopped.
OAuth Silent 401 in Skill Builder and Runner (forge-cli)
Before: When OpenAI OAuth credentials were missing or unloadable (no ~/.forge/credentials/openai.json), both the Skill Builder llmStreamFunc and the Runner createProviderClient silently fell through to create a regular OpenAI client with no API key — resulting in a cryptic 401 Unauthorized error.
After: When the API key is empty/__oauth__ and oauth.LoadCredentials() fails, a clear error is returned: "no OpenAI API key or OAuth credentials found; run 'forge init' with OAuth or set OPENAI_API_KEY".
Files Changed
| Area | Files | Description |
|---|---|---|
| Skill env utilities | forge-cli/cmd/skill_env.go |
ParseSkillRequirements, MergeEgressDomains, AppendEnvVars, CheckMissingEnv |
| Skill env tests | forge-cli/cmd/skill_env_test.go |
12 unit tests for shared utilities |
| CLI skills add | forge-cli/cmd/skills.go |
Egress domain merge in runSkillsAdd |
| UI save func | forge-cli/cmd/ui.go |
Enhanced skill save with env/egress handling, OAuth error surfacing |
| Runner OAuth | forge-cli/runtime/runner.go |
OAuth error surfacing in createProviderClient |
| Parser export | forge-skills/parser/parser.go |
Export ExtractForgeReqs for reuse |
| LLM loop | forge-core/runtime/loop.go |
Ignore FinishReason, session dedup, Q&A nudge suppression |
| Loop tests | forge-core/runtime/loop_test.go |
Regression tests for stop+tool_calls |
| UI types | forge-ui/types.go |
SkillSaveResult, SkillEnvEntry, updated SkillSaveFunc signature |
| UI handler | forge-ui/handlers_skill_builder.go |
Pass env vars, return SkillSaveResult |
| Port allocator | forge-ui/process.go |
net.Listen probe in Allocate(), portFree() helper |
| Agent discovery | forge-ui/discovery.go |
PID liveness check, stale serve.json cleanup |
| Frontend | forge-ui/static/dist/app.js |
Env var inputs, egress/env status display |
| Docker | Dockerfile, .dockerignore |
Multi-stage alpine build |
| CI | .github/workflows/release.yaml |
GHCR multi-arch build+push job |
| Docs | docs/{runtime,skills,dashboard,deployment,commands}.md |
Synced with code changes |
25 files changed, 1,201 insertions, 79 deletions
Installation
# Homebrew
brew upgrade initializ/tap/forge
# One-line install/upgrade
curl -sSL https://raw.githubusercontent.com/initializ/forge/main/install.sh | bash
# Docker
docker pull ghcr.io/initializ/forge:v0.9.2Contributors
Built with Forge — turn SKILL.md into portable, secure, runnable AI agents.