Code cleanup, heartbeat agent, eventing improvements #99

Merged
jasoncouture merged 45 commits from refactor/code-cleanup into main 2026-05-22 18:21:43 -04:00
jasoncouture commented 2026-05-22 18:21:32 -04:00 (Migrated from github.com)

Summary

Big chunk of refactor + feature work on top of main. Highlights:

Features

  • Heartbeat agentAgentHeartbeatService periodically spawns a transient ITransientAgent under a child session when HEARTBEAT.md is present in the agent's workspace. Briefs the transient with a SystemEphemeral snapshot of recent parent activity instead of replaying turns wholesale. Dedicated HEARTBEAT.md system prompt + per-turn context template.
  • Transient agent infrastructureITransientAgent (self-shuts on idle), ITransientAgentFactory (resolves parent SessionPath from the data scope, names child session, stamps initial turn into context data, delegates to IAgentFactory). Accepts User or FrameworkUser initial turn role.
  • Live agent config reloadAgent subscribes to Lifecycle.Update events; updated config swaps into the agent's data scope at the start of the next turn so downstream scoped services pick it up by re-reading the scope.
  • EventBusFileLogDecorator — JSONL log of every Subscribe / PublishAsync to a file under the data root. Off by default, dev appsettings flips it on.
  • Web UI: sub-sessions in chat header — session dropdown now shows root sessions with indented children, deduped by (parent, name) keeping newest UUIDv7.

Refactors / conventions

  • Channel id prefix conventionuser:<channel> for human messages, subagent:<full-session-id> for sub-agent forwards. system: reserved.
  • Sentinel.NoResponse — hoisted the NO_RESPONSE literal into a single constant.
  • Generic IAgentFactory.CreateAgentAsync<TAgent> — agent service type carried on AgentHandle, resolved at Start() instead of hard-coded IAgent.
  • Prompt-context provider — drops implicit .md append; callers pass full filename, matching the system-prompt provider convention.
  • DisposableList — applied to multi-Dispose sites that fit the pattern.
  • MCP internal source constToolCall.InternalToolSource replaces the private literal in ContextCompactor.
  • Eventing DI — collapsed duplicate IEventBus registration.
  • EventBus — wraps publish in try/catch with a dedicated Events log category for diagnosis.

Fixes

  • FileShare.ReadWrite | FileShare.Delete — unix sharing semantics on GrepTool, ReadFileTool, JsonLineContextStore.
  • Sub-agent shutdown sub — preserves execution context.
  • Scriban → 7.2.0 — clears NU1903 advisory.

Tests

  • TransientAgentTests covering constructor guards, prompt removal, idle→stop, subscription topology, message_send tool short-circuit, parent forwarding rules.
  • Test project gains LlamaShears.Api reference for tests that target API types.

Run git log --oneline origin/main..HEAD for the full commit list (40+ commits).

Test plan

  • dotnet build clean, 0 warnings
  • dotnet test — all 549 pass
  • Heartbeat fires when HEARTBEAT.md populated; stays silent when empty
  • Sub-agent forwards skip when content is empty (no ghost bubbles)
  • Chat header lists child sessions indented under parent
## Summary Big chunk of refactor + feature work on top of `main`. Highlights: ### Features - **Heartbeat agent** — `AgentHeartbeatService` periodically spawns a transient `ITransientAgent` under a child session when `HEARTBEAT.md` is present in the agent's workspace. Briefs the transient with a `SystemEphemeral` snapshot of recent parent activity instead of replaying turns wholesale. Dedicated `HEARTBEAT.md` system prompt + per-turn context template. - **Transient agent infrastructure** — `ITransientAgent` (self-shuts on idle), `ITransientAgentFactory` (resolves parent SessionPath from the data scope, names child session, stamps initial turn into context data, delegates to `IAgentFactory`). Accepts `User` or `FrameworkUser` initial turn role. - **Live agent config reload** — `Agent` subscribes to `Lifecycle.Update` events; updated config swaps into the agent's data scope at the start of the next turn so downstream scoped services pick it up by re-reading the scope. - **`EventBusFileLogDecorator`** — JSONL log of every Subscribe / PublishAsync to a file under the data root. Off by default, dev appsettings flips it on. - **Web UI: sub-sessions in chat header** — session dropdown now shows root sessions with indented children, deduped by `(parent, name)` keeping newest UUIDv7. ### Refactors / conventions - **Channel id prefix convention** — `user:<channel>` for human messages, `subagent:<full-session-id>` for sub-agent forwards. `system:` reserved. - **Sentinel.NoResponse** — hoisted the `NO_RESPONSE` literal into a single constant. - **Generic `IAgentFactory.CreateAgentAsync<TAgent>`** — agent service type carried on `AgentHandle`, resolved at `Start()` instead of hard-coded `IAgent`. - **Prompt-context provider** — drops implicit `.md` append; callers pass full filename, matching the system-prompt provider convention. - **DisposableList** — applied to multi-Dispose sites that fit the pattern. - **MCP internal source const** — `ToolCall.InternalToolSource` replaces the private literal in `ContextCompactor`. - **Eventing DI** — collapsed duplicate `IEventBus` registration. - **EventBus** — wraps publish in try/catch with a dedicated `Events` log category for diagnosis. ### Fixes - **`FileShare.ReadWrite | FileShare.Delete`** — unix sharing semantics on `GrepTool`, `ReadFileTool`, `JsonLineContextStore`. - **Sub-agent shutdown sub** — preserves execution context. - **Scriban → 7.2.0** — clears NU1903 advisory. ### Tests - `TransientAgentTests` covering constructor guards, prompt removal, idle→stop, subscription topology, message_send tool short-circuit, parent forwarding rules. - Test project gains `LlamaShears.Api` reference for tests that target API types. Run `git log --oneline origin/main..HEAD` for the full commit list (40+ commits). ## Test plan - [ ] `dotnet build` clean, 0 warnings - [ ] `dotnet test` — all 549 pass - [ ] Heartbeat fires when `HEARTBEAT.md` populated; stays silent when empty - [ ] Sub-agent forwards skip when content is empty (no ghost bubbles) - [ ] Chat header lists child sessions indented under parent
Sign in to join this conversation.
No description provided.