Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,13 +117,19 @@ jobs:
find release-assets -maxdepth 1 -type f ! -name RELEASE_NOTES.md -print |
sort
)
if [[ "${#assets[@]}" -ne 10 ]]; then
echo "::error::Expected 10 downloadable assets, found ${#assets[@]}."
if [[ "${#assets[@]}" -ne 13 ]]; then
echo "::error::Expected 13 downloadable assets, found ${#assets[@]}."
printf '%s\n' "${assets[@]}"
exit 1
fi

gh release create "${tag}" + "${release_target[@]}" + --draft + --prerelease + --title "OpenGameAgent ${tag}" + --notes-file release-assets/RELEASE_NOTES.md + "${assets[@]}"
gh release create "${tag}" \
"${release_target[@]}" \
--draft \
--prerelease \
--title "OpenGameAgent ${tag}" \
--notes-file release-assets/RELEASE_NOTES.md \
"${assets[@]}"

expected="$(printf '%s\n' "${assets[@]##*/}" | sort)"
actual="$(gh release view "${tag}" --json assets --jq '.assets[].name' | sort)"
Expand Down Expand Up @@ -166,12 +172,15 @@ jobs:
run: |
set -euo pipefail
mapfile -t packages < <(find release-assets -maxdepth 1 -type f -name '*.nupkg' -print | sort)
if [[ "${#packages[@]}" -ne 6 ]]; then
echo "::error::Expected 6 NuGet packages, found ${#packages[@]}."
if [[ "${#packages[@]}" -ne 9 ]]; then
echo "::error::Expected 9 NuGet packages, found ${#packages[@]}."
exit 1
fi
for package in "${packages[@]}"; do
dotnet nuget push "${package}" + --api-key "${NUGET_API_KEY}" + --source https://api.nuget.org/v3/index.json + --skip-duplicate
dotnet nuget push "${package}" \
--api-key "${NUGET_API_KEY}" \
--source https://api.nuget.org/v3/index.json \
--skip-duplicate
done

publish-github-release:
Expand All @@ -192,4 +201,6 @@ jobs:
set -euo pipefail
tag="v${RELEASE_VERSION}"
gh release edit "${tag}" --draft=false --prerelease
gh release view "${tag}" + --json isDraft,isPrerelease + --jq 'select(.isDraft == false and .isPrerelease == true)' >/dev/null
gh release view "${tag}" \
--json isDraft,isPrerelease \
--jq 'select(.isDraft == false and .isPrerelease == true)' >/dev/null
13 changes: 8 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
## 0.3.0-alpha.1

- Introduce a compact stateful streaming Agent kernel with typed content, validated tools, steering, follow-up, hooks, cancellation, transcript integrity checks, bounded concurrency, and explicit failure results.
- Add safe tool execution with schema validation, source-ordered results, progress, timeout handling, conflict-key serialization, and fail-closed uncertain-write semantics across a tool batch.
- Add safe tool execution with schema validation, source-ordered results, progress, model/tool deadlines, conflict-key serialization, and fail-closed uncertain-write semantics across a tool batch.
- Add the game runtime with arbitrary structured inputs, floating-point preservation, named game timelines, automatic quick/full/workflow routing, optimistic sessions, duplicate protection, live steering/abort, and per-actor concurrency.
- Add durable action intents and receipts, prepared/dispatched/final recovery, resumable workflows, game-time memory and expiry, skills, recurring schedules, actor mailboxes, transcript compaction, and media-generation API contracts.
- Add crash-tolerant single-process file stores for sessions, action journals, workflow checkpoints, memories, mailboxes, and hot-reloaded directory skills, with identity and saved-state trust checks.
- Add strict streaming OpenAI-compatible and generic HTTP media providers, bounded request/response parsing, rotating credentials, polling controls, and retry/fallback provider composition.
- Add a typed extension API with immutable composition, namespaced session state, lifecycle events, channels, diagnostics, and official policy, searchable-tool, interaction, goal, memory, artifact, knowledge, delegation, tracing, and durable workflow-graph extensions.
- Add durable action intents and receipts, prepared/dispatched/final recovery, resumable sequential and dependency-graph workflows, game-time memory and expiry, recursive skills, recurring schedules, actor mailboxes, context-window admission, large-result artifact spill, and media-generation API contracts.
- Add crash-tolerant, cross-process-coordinated local file stores for sessions, action journals, workflow checkpoints, memories, mailboxes, artifacts, delegations, and hot-reloaded directory skills, with identity and saved-state trust checks.
- Add capability-aware provider/model catalogs, reasoning and cost metadata, dynamic refresh, replaceable authentication, and developer-hosted short-lived credentials.
- Add lazy external tool-server search/describe/call by default with explicit direct exposure for small trusted catalogs.
- Add strict streaming OpenAI-compatible and generic HTTP media providers, bounded request/response parsing, rotating credentials, polling controls, and retry/fallback composition that stops before replaying meaningful streamed output.
- Add Godot 4.7 .NET and Unity 6 adapters with local and remote modes, bounded main-thread delivery with terminal reservation, package verification, and real local-runtime editor tests on Windows.
- Add an optional .NET 8 JSON/SSE server, engine-compatible client, authenticated steering and abort, strict wire contracts, and redirect/credential guidance.
- Add an optional .NET 8 JSON/SSE server, engine-compatible client, authenticated steering and abort, bounded JSON request bodies, strict wire contracts, and redirect/credential guidance.
- Add bilingual documentation, a buildable living-world action example, pinned release automation, and cross-platform .NET validation.
48 changes: 47 additions & 1 deletion OpenGameAgent.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Expand All @@ -16,6 +16,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Client", "src
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Server", "src\OpenGameAgent.Server\OpenGameAgent.Server.csproj", "{126F4F68-C8D5-403B-899D-7EA25D446007}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Extensions", "src\OpenGameAgent.Extensions\OpenGameAgent.Extensions.csproj", "{6264B0A9-9050-4ED6-A7D8-F1DBBBCE6008}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Kernel.Tests", "tests\OpenGameAgent.Kernel.Tests\OpenGameAgent.Kernel.Tests.csproj", "{E0E06633-9C9A-4D70-BE88-491734701101}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Tests", "tests\OpenGameAgent.Tests\OpenGameAgent.Tests.csproj", "{D6FF2E41-75D9-463A-B9AC-26EA360F1102}"
Expand All @@ -28,8 +30,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Providers.Med
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Server.Tests", "tests\OpenGameAgent.Server.Tests\OpenGameAgent.Server.Tests.csproj", "{8DC373B7-B6D6-42E8-BF98-7B8DC5FD1106}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Extensions.Tests", "tests\OpenGameAgent.Extensions.Tests\OpenGameAgent.Extensions.Tests.csproj", "{D23EE0CD-40FD-47F3-9549-B0675A821107}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Example", "examples\OpenGameAgent.Example\OpenGameAgent.Example.csproj", "{48B319EF-B033-4D17-A025-8987FCABA107}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{EA3AF59A-9A1C-4197-B2A3-F93894D131B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Connectors.Mcp", "src\OpenGameAgent.Connectors.Mcp\OpenGameAgent.Connectors.Mcp.csproj", "{01759D73-7B80-47A2-9D7D-154CC64C6851}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{86AE6217-BFEE-4349-945A-70ECEC211437}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Connectors.Mcp.Tests", "tests\OpenGameAgent.Connectors.Mcp.Tests\OpenGameAgent.Connectors.Mcp.Tests.csproj", "{98A0255B-E6C3-46C4-868C-A16FB559A79C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Models", "src\OpenGameAgent.Models\OpenGameAgent.Models.csproj", "{CC89911F-B920-4203-8CE4-03D434C3B01E}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenGameAgent.Models.Tests", "tests\OpenGameAgent.Models.Tests\OpenGameAgent.Models.Tests.csproj", "{839EA4C2-45A0-4E78-8FAE-E39155C96F4C}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -64,6 +80,10 @@ Global
{126F4F68-C8D5-403B-899D-7EA25D446007}.Debug|Any CPU.Build.0 = Debug|Any CPU
{126F4F68-C8D5-403B-899D-7EA25D446007}.Release|Any CPU.ActiveCfg = Release|Any CPU
{126F4F68-C8D5-403B-899D-7EA25D446007}.Release|Any CPU.Build.0 = Release|Any CPU
{6264B0A9-9050-4ED6-A7D8-F1DBBBCE6008}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6264B0A9-9050-4ED6-A7D8-F1DBBBCE6008}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6264B0A9-9050-4ED6-A7D8-F1DBBBCE6008}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6264B0A9-9050-4ED6-A7D8-F1DBBBCE6008}.Release|Any CPU.Build.0 = Release|Any CPU
{E0E06633-9C9A-4D70-BE88-491734701101}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E0E06633-9C9A-4D70-BE88-491734701101}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E0E06633-9C9A-4D70-BE88-491734701101}.Release|Any CPU.ActiveCfg = Release|Any CPU
Expand All @@ -88,9 +108,35 @@ Global
{8DC373B7-B6D6-42E8-BF98-7B8DC5FD1106}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8DC373B7-B6D6-42E8-BF98-7B8DC5FD1106}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8DC373B7-B6D6-42E8-BF98-7B8DC5FD1106}.Release|Any CPU.Build.0 = Release|Any CPU
{D23EE0CD-40FD-47F3-9549-B0675A821107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D23EE0CD-40FD-47F3-9549-B0675A821107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D23EE0CD-40FD-47F3-9549-B0675A821107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D23EE0CD-40FD-47F3-9549-B0675A821107}.Release|Any CPU.Build.0 = Release|Any CPU
{48B319EF-B033-4D17-A025-8987FCABA107}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{48B319EF-B033-4D17-A025-8987FCABA107}.Debug|Any CPU.Build.0 = Debug|Any CPU
{48B319EF-B033-4D17-A025-8987FCABA107}.Release|Any CPU.ActiveCfg = Release|Any CPU
{48B319EF-B033-4D17-A025-8987FCABA107}.Release|Any CPU.Build.0 = Release|Any CPU
{01759D73-7B80-47A2-9D7D-154CC64C6851}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{01759D73-7B80-47A2-9D7D-154CC64C6851}.Debug|Any CPU.Build.0 = Debug|Any CPU
{01759D73-7B80-47A2-9D7D-154CC64C6851}.Release|Any CPU.ActiveCfg = Release|Any CPU
{01759D73-7B80-47A2-9D7D-154CC64C6851}.Release|Any CPU.Build.0 = Release|Any CPU
{98A0255B-E6C3-46C4-868C-A16FB559A79C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{98A0255B-E6C3-46C4-868C-A16FB559A79C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{98A0255B-E6C3-46C4-868C-A16FB559A79C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{98A0255B-E6C3-46C4-868C-A16FB559A79C}.Release|Any CPU.Build.0 = Release|Any CPU
{CC89911F-B920-4203-8CE4-03D434C3B01E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CC89911F-B920-4203-8CE4-03D434C3B01E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CC89911F-B920-4203-8CE4-03D434C3B01E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CC89911F-B920-4203-8CE4-03D434C3B01E}.Release|Any CPU.Build.0 = Release|Any CPU
{839EA4C2-45A0-4E78-8FAE-E39155C96F4C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{839EA4C2-45A0-4E78-8FAE-E39155C96F4C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{839EA4C2-45A0-4E78-8FAE-E39155C96F4C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{839EA4C2-45A0-4E78-8FAE-E39155C96F4C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{01759D73-7B80-47A2-9D7D-154CC64C6851} = {EA3AF59A-9A1C-4197-B2A3-F93894D131B8}
{98A0255B-E6C3-46C4-868C-A16FB559A79C} = {86AE6217-BFEE-4349-945A-70ECEC211437}
{CC89911F-B920-4203-8CE4-03D434C3B01E} = {EA3AF59A-9A1C-4197-B2A3-F93894D131B8}
{839EA4C2-45A0-4E78-8FAE-E39155C96F4C} = {86AE6217-BFEE-4349-945A-70ECEC211437}
EndGlobalSection
EndGlobal
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
[![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
[![Status](https://img.shields.io/badge/status-alpha-orange.svg)](CHANGELOG.md)

OpenGameAgent brings the small, composable agent-kernel model to game development. Its stateful core streams model output, executes validated tools, accepts steering while running, and continues the model/tool loop until work is complete. Use that kernel by itself, or add the game layer for game time, durable actions, sessions, skills, memory primitives, routing, workflows, and bounded multi-character concurrency.
OpenGameAgent brings the small, composable agent-kernel model to game development. Its stateful core streams model output, executes validated tools, accepts steering while running, and continues the model/tool loop until work is complete. Use that kernel by itself, add the game layer for game time and durable state, then opt into extension packages for memory, goals, artifacts, delegation, external tools, structured interaction, and workflow graphs.

Inputs are bounded JSON. They may represent dialogue, combat observations, simulation ticks, UI events, plans, sensor state, or any other game-owned data; natural language is optional. No model is bundled. Cloud and local API endpoints are both supported.

> Current version: `0.3.0-alpha.1`. Public APIs can change before `1.0`.

The kernel boundary is intentionally small and designed to stabilize early. New game-specific capabilities should normally arrive as extensions, tools, policies, workflows, or game-owned services instead of expanding the model/tool loop.

## Install

Install the complete game runtime from NuGet:
Expand All @@ -38,6 +40,9 @@ OpenGameAgent keeps the reusable agent machinery independent from the game while
- game-time memory filtering, expiry, and optional custom ranking;
- skills selected by input type and available tools;
- recurring game-time triggers and persistent actor mailboxes;
- a typed extension API for tools, skills, routes, workflows, hooks, events, and services;
- capability-aware model catalogs and developer-hosted short-lived credentials;
- lazy external-tool discovery and large-result artifact spill;
- image, audio, and video generation through replaceable APIs.

The runtime does **not** decide combat legality, inventory rules, economy changes, NPC permissions, or other business rules. The game exposes narrow tools, validates every requested mutation, performs it on the correct thread or server, and returns the authoritative receipt.
Expand All @@ -50,7 +55,7 @@ Godot / Unity / .NET game server
| GameInput (bounded JSON + GameMoment)
v
GameAgentRuntime
context | skills | route | session | actor lane
context | skills | route | session | actor lane | extensions
|
v
small stateful Agent kernel <---- steering / follow-up
Expand All @@ -74,13 +79,17 @@ Read [Architecture](docs/architecture.md) for the ownership and failure boundari
| Agent kernel | Streaming typed messages, tool loop, progress events, steering, follow-up, hooks, cancellation, strict transcript validation, provider failures as results |
| Tool execution | Bounded JSON Schema subset, guaranteed result for every accepted call, safe parallel reads, conflict-key serialization, policy blocking/termination, timeouts, uncertain write outcomes |
| Game runtime | Arbitrary JSON input, game clocks/timelines, fast/full/workflow routing, optimistic sessions, duplicate-input protection, actor concurrency, active-run steering/abort |
| Extension API | Immutable builder; prompt/context/tool/skill/route/workflow/hook/provider/service registration; typed lifecycle events and channels; namespaced persistent state |
| Official extensions | Tool policy and search, structured player questions/recommended replies, goals, memory, artifacts, knowledge, delegation, tracing, and durable parallel workflow graphs |
| World primitives | Durable actions, resumable workflows, memories, skills, signals, game-time schedules, actor mailboxes |
| Models and auth | Capability/context/reasoning/cost catalog, dynamic model refresh, static/environment/stored/local auth, developer-hosted short-lived credential gateway |
| External tools | Lazy on-demand search/describe/call by default; explicit direct exposure for small trusted catalogs |
| Providers | Streaming OpenAI-compatible text/tool API; generic HTTP image/audio/video API; retry and fallback decorators |
| Persistence | Crash-tolerant local files for sessions, action journals, workflow checkpoints, memories, mailboxes, and hot-reloaded `SKILL.md` or game-manifest skills |
| Persistence | Crash-tolerant, cross-process-coordinated local files for sessions, action journals, workflow checkpoints, memories, mailboxes, artifacts, delegations, and recursive hot-reloaded skills |
| Placement | Shared `netstandard2.1` runtime in Godot, Unity, or another C# host; optional .NET 8 HTTP/SSE service and engine client |
| Engines | Godot 4.7 .NET and Unity 6 packages, both exercised in real Windows editors |

Run inputs, model content, tool catalogs, loops, queues, progress, and concurrency are bounded by explicit limits. Game-owned stores and rankers can replace the included in-memory or local-file implementations.
Run inputs, model content, tool catalogs, loops, queues, progress, and concurrency are bounded by explicit limits. Context admission runs before every model request, model and tool calls have deadlines, and large tool results can be retained as artifacts instead of repeatedly filling the prompt. Game-owned stores and rankers can replace the included in-memory or local-file implementations.

## Minimal kernel

Expand Down Expand Up @@ -138,6 +147,8 @@ See the buildable [living-world example](examples/OpenGameAgent.Example/Program.
- **In the game server:** best when the game already has an authoritative server. Run the same C# runtime beside game rules and persistence.
- **Separate agent service:** useful for centrally paid inference, secrets, scaling, or independent updates. Engine adapters call `OpenGameAgent.Server` over JSON/SSE and can steer or abort an active actor through authenticated control endpoints.

For developer-funded client inference, use a developer-controlled gateway that issues short-lived scoped credentials. The permanent upstream provider key stays on developer infrastructure; the framework supplies the client credential flow, while the game owns login, quotas, revocation, and abuse controls.

Placement does not change ownership: only game code decides whether an action commits.

## Build and verify
Expand Down
Loading
Loading