Files
VolcanicAgenticCore/tests/Volcanic.AgenticCore.Tests/Support/StubExtensionHost.cs
EugeneTes 76bda6cf55 fix(processes): address review findings
- Fill TimeZoneId only for a configured session/host zone with a resolvable IANA id
  (IExtensionHost.TimeZoneId); otherwise children inherit the host's TZ instead of TZ=Local/UTC.
- Host hardening logs a warning instead of throwing when libc/prctl can't be resolved.
- A hook or process_spawn handler returning a null or incomplete context fails the spawn with
  InvalidOperationException, so bash reports "process environment hook failed".
- Move tool-result redaction into the Agent slice with a behavioural no-change check.
- ProcessSpawnContext.ApplyTo(psi) sets cwd and replaces the environment; used by bash and ExecAsync.
- Split the Telegram process environment types into their own files; move the Extensions.Tests
  NoHostHardening under Support/; check the Claude Code assembly never references the spawn policy.
- Spec and docs: TZ inheritance, incomplete contexts, ApplyTo, async spawn handlers, root skip.
2026-09-26 19:32:47 +02:00

43 lines
2.6 KiB
C#

using System.Text.Json;
using Volcanic.AgenticCore.Abstractions.Extensions;
using Volcanic.AgenticCore.Abstractions.Messages;
using Volcanic.AgenticCore.Abstractions.Providers;
using Volcanic.AgenticCore.Abstractions.Sessions;
using Volcanic.AgenticCore.Abstractions.Ui;
using Volcanic.AgenticCore.Features.Extensions;
using Volcanic.AgenticCore.Features.Models;
namespace Volcanic.AgenticCore.Tests.Support;
public sealed class StubExtensionHost : IExtensionHost
{
public List<string> Calls { get; } = [];
public string Cwd { get; init; } = Path.GetTempPath();
public string SessionId => "session-1";
public string Scope { get; init; } = "test-scope";
public IUserInterface Ui { get; init; } = NullUserInterface.Instance;
public IReadOnlySessionManager SessionManager => throw new NotSupportedException();
public IModelRegistry ModelRegistry { get; init; } = new ModelRegistry(new ApiKeyResolver(null, _ => null));
public Model? Model { get; set; }
public ThinkingLevel ThinkingLevel { get; set; } = ThinkingLevel.Off;
public TimeZoneInfo TimeZone { get; init; } = TimeZoneInfo.Utc;
public string? TimeZoneId { get; init; }
public CancellationToken Signal => CancellationToken.None;
public bool IsIdle() => true;
public void Abort() => Calls.Add("abort");
public bool HasPendingMessages() => false;
public ContextUsage? GetContextUsage() => null;
public string GetSystemPrompt() => "prompt";
public void RequestCompaction(CompactOptions? options) => Calls.Add("compact");
public Task WaitForIdleAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public Task<SwitchResult> NewSessionAsync(NewSessionOptions? options, CancellationToken cancellationToken) => Task.FromResult(new SwitchResult(false));
public Task<SwitchResult> SwitchSessionAsync(string sessionId, CancellationToken cancellationToken) => Task.FromResult(new SwitchResult(false));
public void SendMessage(CustomMessage message, MessageDelivery delivery, bool triggerTurn) => Calls.Add($"send:{message.CustomType}:{delivery}:{triggerTurn}");
public void SendUserMessage(string text, IReadOnlyList<ImageContent>? images, MessageDelivery delivery) => Calls.Add($"user:{text}:{delivery}");
public void AppendEntry(string customType, JsonElement? data) => Calls.Add($"entry:{customType}");
public Task<bool> SetModelAsync(Model model, CancellationToken cancellationToken) { Model = model; return Task.FromResult(true); }
public void SetThinkingLevel(ThinkingLevel level) => ThinkingLevel = level;
public string? GetSessionName() => "name";
public void SetSessionName(string? name) => Calls.Add($"name:{name}");
}