Extensions are statically compiled in-tree code. The server project includes
Extensions\**\*.cs and the client project includes extension client assets;
the host discovers parameterless IWorldSharpExtension implementations at
startup. Adding a new extension folder is therefore enough to compile and
register it, provided its server extension class has a parameterless
constructor.
Each extension gets a generic lifecycle:
public interface IWorldSharpExtension
{
ExtensionDescriptor Descriptor { get; }
void Register(IExtensionRegistry registry);
void ConfigureServices(IServiceCollection services, ExtensionHostContext context);
void MapEndpoints(IEndpointRouteBuilder endpoints);
}The default lifecycle methods are optional. Register contributes shared
catalogue capabilities such as WorldAction and InterfaceModeDescriptor;
ConfigureServices owns extension services and persistence drivers; and
MapEndpoints owns the extension's HTTP surface. The host does not call
feature-specific AddX or MapX methods.
- Convo registers
worldsharp.convo. - Roleplay registers the shared
worldsharp.roleplayworkspace; its server code owns roleplay generation and the optional bounded-session lifecycle. - Social Feed registers singleton
worldsharp.social-feed; the extension also registers its planner, drafter, reaction Drivers, and heartbeat contributor/action. - BuiltIn registers foundational actions.
- PreviewModes registers the
worldsharp.gamepreview descriptor. It is not a game implementation. - Presence owns conversation availability endpoints and the weekly schedule editor. Provider-generated plans are persisted as inactive drafts and only become active after an explicit accept action. Its reusable plan contracts and validation remain in Core.
- Activities owns durable world event rules, planned activities, occurrence materialization, and the Universe calendar editor. Its reusable schedule and activity contracts remain in Core.
Summaries and World Tracker are also extensions even though they do not contribute an Interface mode.
Heartbeat is host orchestration rather than a feature-specific extension. It
provides a bounded HeartbeatContext and asks registered extensions for typed
HeartbeatProposal values. The host validates proposals against the
Universe's opt-in maximum risk ceiling; the ceiling controls the worst allowed
outcome, not how often an extension should act. Activity changes are delivered
as an ActivityChange trigger with the changed WorldActivity, its previous
status when available, and a change kind.
Extensions should return no proposal when there is nothing meaningful to do.
They must not mutate canonical state from a contributor. Conversation messages,
Social Feed posts, and world changes should be executed through extension-owned
action handlers so the host can enforce rate limits, permissions, and duplicate
suppression. Actions execute automatically by default after passing the risk
ceiling; a Universe can opt into proposal-by-proposal approval with
RequireApproval. Real-Time heartbeat scheduling is host-owned and actor-scoped, while the
Social Feed and Convo actions remain extension-owned. World Tracker mutations
still use its own review-first branch semantics.
Conversation Presence also governs autonomous eligibility. Convo heartbeats resolve the target conversation's current status and suppress replies while the character is DND or Offline. Social Feed uses the character's active one-character Persona Convo presence as its global availability signal, preferring the most available matching conversation when more than one exists; it likewise suppresses heartbeat interactions while the character is DND or Offline. Missing presence plans resolve to Online, preserving the default behavior for characters without a schedule.
Heartbeat runs and proposals carry an optional character actor. Autonomous
Real-Time, Roleplay-turn, and Activity-change triggers populate that actor,
and extension actions must scope generated work to it. A Convo heartbeat
defaults to the active, non-frozen one-character conversation assigned
to the selected Persona; an explicit proposal conversation source can target
another conversation when a feature chooses to do so.
Manual runs can target a character with the actorId query parameter; a
manual run without an actor records the trigger but does not authorize a
character action.
Heartbeat actions that generate feature content must start the target
extension's ordinary request path. Convo generation receives the existing
conversation history and Social Feed drafting receives the normal world,
profile, and recent-post context. A proposal may include a Hint, but it is
non-binding guidance and cannot replace feature-owned prompts, validation, or
canonical state.
Social Feed exposes one heartbeat action, Browse or Post on Social Feed.
Its internal Drivers may return posts, replies, quotes, likes, reposts, follows,
or no actions. The extension currently provides both a scoped batch Driver and
an individual Driver; both receive the same normal-route-shaped feed context,
produce the same validated interaction plan, and use the same idempotent
persistence path. Every proposal identifies one character actor, and validation
rejects actions for any other character. The context includes newest posts, a
bounded slice of older posts, profiles, world nodes, and the target character's
follows and notifications. The active heartbeat Driver is persisted per
Universe through the Social Feed Driver settings endpoint. Universes without
an explicit selection fall back to WorldSharp:SocialFeed:ActiveHeartbeatDriver
and then the scoped batch Driver default; changing the selection affects future
heartbeats without changing the user-facing action.
An extension mode declares cardinality and routes, while the registry remains a catalogue rather than a generic mode-instance creation/enforcement service. Features own their mode-specific data and orchestration. Extensions are compiled into the host; this is source discovery, not runtime package loading.
Drivers remain feature-owned typed contracts. A Driver returns proposals; feature services validate and persist results. Drivers do not own canonical state or get raw database/DI access. Provider credentials remain host-managed.
The extension system has no package download/install flow, runtime DLL loading from external packages, dynamic reload/unload, permissions, sandboxing, independently-installed migrations, profile registry, or generic client-panel/topbar contribution contract.