Skip to content

v0.9.2

Latest

Choose a tag to compare

@initializ-mk initializ-mk released this 17 Apr 15:57
· 5 commits to main since this release
147a072

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:

  1. SKILL.md frontmatter declares metadata.forge.requires.env and metadata.forge.egress_domains
  2. On save, Forge parses requirements and:
    • Writes env vars to .env (deduplicated, skips existing keys and encrypted placeholders)
    • Merges egress domains into forge.yaml security.egress.allowed_domains (deduplicated, sorted, YAML formatting preserved)
    • Reports missing env vars back to the UI for user input
  3. 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 .env

New shared utilities in forge-cli/cmd/skill_env.go:

  • ParseSkillRequirements() — extracts env requirements and egress domains from SKILL.md
  • MergeEgressDomains() — adds domains to forge.yaml allowlist with text-based YAML insertion
  • AppendEnvVars() — appends key=value pairs to .env with deduplication
  • CheckMissingEnv() — 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.0

Image details:

  • Tags: v0.9.2, v0.9, v0, latest (semver hierarchy)
  • Base: alpine:3.22.4 with ca-certificates, git, tzdata
  • Build: Multi-stage with golang:1.25-alpine, BuildKit cache mounts, CGO_ENABLED=0 static binary
  • Security: Non-root forge user, 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.2

Contributors

Built with Forge — turn SKILL.md into portable, secure, runnable AI agents.