feat(plugins): complete Campaign LA5 cross-host hosting

This commit is contained in:
Erik 2026-08-14 18:12:59 +02:00
parent 6c4cd2bbc6
commit 95f4be94db
26 changed files with 1630 additions and 99 deletions

View file

@ -13,9 +13,9 @@ namespace AcDream.Runtime.Session;
/// is a single shared-stdout JSONL diagnostics stream with no per-session
/// file; this class writes one file per session, meant to be read by an
/// external process (the launcher) rather than scraped from console output.
/// Event shapes are versioned (<c>"v":1</c>) so a future event kind
/// (<c>pluginLoaded</c>/<c>pluginFailed</c>, LA5) can be added without
/// breaking an existing reader.
/// Event shapes are versioned (<c>"v":1</c>); LA5's
/// <c>pluginLoaded</c>/<c>pluginFailed</c> additions use that same envelope
/// without breaking an existing reader.
/// </para>
///
/// <para>
@ -29,9 +29,11 @@ namespace AcDream.Runtime.Session;
/// </para>
///
/// <para>
/// <strong>Never write credential material into this stream.</strong> Every
/// event method below takes only identifiers, names, and counts — there is no
/// parameter shape that could carry a password, by construction.
/// <strong>Never write credential material into this stream.</strong> LA5's
/// <see cref="PluginFailed"/> diagnostic is caller-supplied text, so hosts may
/// pass only the plugin lifecycle failure and must never append session
/// credentials or other secrets. Neither plugin host exposes credentials
/// through <c>IPluginHost</c>.
/// </para>
///
/// <para>
@ -205,6 +207,27 @@ public sealed class SessionStatusWriter
characterName,
});
public void PluginLoaded(string sessionId, string plugin) =>
Write(new
{
v = VocabularyVersion,
e = "pluginLoaded",
t = Now(),
sessionId,
plugin,
});
public void PluginFailed(string sessionId, string plugin, string error) =>
Write(new
{
v = VocabularyVersion,
e = "pluginFailed",
t = Now(),
sessionId,
plugin,
error,
});
public void Disconnected(string sessionId, string reason)
{
if (!IsEnabled)