- 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.
43 lines
2.6 KiB
C#
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}");
|
|
}
|