Repository navigation
[RemoteChat] - Integrate host-local trust and component placement #1490
Description
Part of #1483
Summary
Compose remote ownership with component placement and host-local trust resolution/compilation; machine transport carries intent and never compiled MXC policy.
Scope
Implement only commit 7 of the approved design. Preserve the contracts below exactly; local and remote behavior must remain compatible.
Files
Files: runtime hydration into AgentServices, CurrentSessionContext,
AgentExecutionTrustContext, existing ExecutorBindings consumers, remote MCP/Copilot requests.
Tests: stale trust revision, per-session context isolation, and all eight
agent-placement/component-placement/containment combinations.
Detailed design
AgentServices, CurrentSessionContext, and composition - Existing, Modified
- Namespace/project/files:
Phantom.Workspaces.Llm/AgentServices.cs,
CurrentSessionContext.cs;Phantom.Workspaces.Services/AgentServicesComposition.cs. - Visibility/kind: existing public sealed records/static composition type.
AgentServices keeps its existing object-typed layering seams and adds:
public object? AgentExecutionTrustContext { get; init; } = null; // AgentExecutionTrustContext
public object? RemoteAgentSessionRuntimeIntent { get; init; } = null; // persisted intent, host onlyCurrentSessionContext keeps existing properties and adds:
public required string OwningProfileEntityId { get; init; }
public required long OwnershipGeneration { get; init; }
public RuntimeEpoch? RuntimeEpoch { get; init; } = null;Init setters reject blank owner, negative generation, and an epoch without owner. The owning host
constructs one context per runtime; attachment identity is never installed as current-session
identity. AgentServicesComposition.ComposeSessionServicesAsync remains host-service composition and
does not parse persisted executor/trust fields. RunningAgentChatTable applies
AgentSessionRuntimeContextFactory.Create afterward via with, preventing GUI and auto-resume paths
from diverging and preventing one session's context from leaking into another.
MXC/executor-facing types - Existing plans, integrated without parallel APIs
ExecutorBindings- Existing from the component-binding design, public sealed record in
Phantom.Workspaces.Llm.Core.Manifest,
Phantom.Workspaces.Llm.Core/Manifest/ExecutorBindings.cs. Remote integration adds no member and
calls existing
JsonElement ResolveComponent(string? executorName),ExecutorTopology ToTopology(), and
JsonElement ToPersistableMap(). Resolution is deterministic and local; unknown names throw and
no transport is opened.AgentExecutionTrustContext- New in [mxc] - Execute MCP tools through MXC-constrained process executor #1477, public sealed class in
Phantom.Workspaces.Llm.Trust,
Phantom.Workspaces.Llm.Core/Trust/AgentExecutionTrustContext.cs. Remote integration adds no
parallel context. Its consumed operation is
ValueTask<TrustProfileProcessPolicyCompilation> GetCompilationAsync(CancellationToken ct = default). The first caller resolves the expected revision and compiles on the launch host;
concurrent callers share the result. Caller cancellation stops only that wait after work starts.
Missing/stale profiles and compiler errors are cached failures for the runtime epoch.ITrustProfileProcessPolicyCompiler,
TrustProfileProcessPolicyCompilation, andMxcProcessPolicy- New in [mxc] - Compile effective trust profiles into MXC sandbox policies #1475, public interface
and immutable records inPhantom.Workspaces.Llm.Trust,
Phantom.Workspaces.Llm.Core/Trust/ITrustProfileProcessPolicyCompiler.csand
MxcProcessPolicy.cs. Exact consumed signature:
TrustProfileProcessPolicyCompilation Compile(TrustProfile effectiveProfile). It validates a
nonnull composed profile, performs no launch, and returns either uncontained, a complete policy,
or diagnostics; required-policy failure is never interpreted as uncontained.IProcessExecutor,ProcessExecutionRequest, andIProcessHandle- New in [mxc] - Streaming process executor backed by MXC #1474, public
interface/records inPhantom.Workspaces.Processes,
Phantom.Workspaces.Processes/IProcessExecutor.cs. Exact consumed signatures are
Task<IProcessHandle> StartAsync(ProcessExecutionRequest request, CancellationToken ct = default),
Task<ProcessExitResult> WaitAsync(CancellationToken ct = default),
Task TerminateAsync(CancellationToken ct = default), andValueTask DisposeAsync().
Start cancellation owns and cleans any partial process; wait cancellation does not transfer
ownership; terminate and disposal are idempotent and kill the tree. An MXC launch failure never
retries ordinary execution.CopilotRuntimeConnectionFactoryandCopilotLaunchPolicyEnvelope- New in [mxc] - Launch copilot.exe through the MXC process executor #1476, public
sealed class/internal strict handoff record inPhantom.Workspaces.Llm.Copilot,
Phantom.Workspaces.Llm.Core/Copilot/CopilotRuntimeConnectionFactory.cs. Exact integration method:
Task<CopilotRuntimeConnectionLease> CreateConnectionAsync(AgentExecutionTrustContext trustContext, string? cliPath, CancellationToken ct = default). It returns one async-disposable direct-or-wrapper
connection lease perCopilotSdkChatClient; constrained mode writes a protected one-use local
envelope. Cancellation/failure deletes unconsumed files. The lease, envelope, policy, and path
never cross machine transport.
CopilotRuntimeConnectionLeaseis the New in [mxc] - Launch copilot.exe through the MXC process executor #1476 public sealed async-disposable result in
the same file; it exposesRuntimeConnection Connection { get; }and idempotent
ValueTask DisposeAsync(). Disposal deletes only an unconsumed handoff and never kills an
SDK-owned runtime after ownership has transferred.ProcessExecutorBackedClientTransportandProcessOwnedMcpTransport- New in [mxc] - Execute MCP tools through MXC-constrained process executor #1477, public
sealedIClientTransportand internal sealedITransportdecorator in
Phantom.Workspaces.Llm.Core/Mcp/ProcessExecutorBackedClientTransport.cs. Exact public members are
string Name { get; }and
Task<ModelContextProtocol.Protocol.ITransport> ConnectAsync(CancellationToken cancellationToken = default). Connect is single-use, always launches viaIProcessExecutor, separates stderr, and
returns the process-owning transport. ItsDisposeAsync()closes MCP/stdin, kills a still-running
tree, and drains exit/stderr tasks. A non-final viewer detach does not dispose it; final detach
reaches it only through graceful runtime stop when background continuation is disabled.- Effective trust resolution is host-local: the persisted reference/revision is resolved/composed,
thenCompileis called. Missing/stale references, unavailable MXC, compiler failure, wrapper
failure, or executor failure never downgrade to null policy or direct launch.
Data flow
- Create/persist. The creator persists owner, generation, executor bindings, and trust
reference/revision. No runtime policy is persisted. - Hydrate.
RunningAgentChatTable.AcquireAsynccalls the runtime-context factory once at first
materialization and merges the result into a newAgentServicesrecord. The owning host creates
CurrentSessionContextfrom its own profile/user/computer, never from viewer claims. - Open. The client opens a message channel with the strict open descriptor. The listener obtains
authenticated peer identity and the host authorizes before runtime lookup. - Start/attach. The registry single-flights runtime creation. An attachment subscribes before
snapshot capture. The host sends a retained replay only when the supplied cursor is fully covered;
otherwise it sends one authoritative snapshot. - Mutate. Each command is reauthorized and deduplicated, then checked against current
generation+epoch. The host mutates the realAgentChat, appends the resulting event under the
runtime scheduler, and broadcasts it in sequence. - Queue and steer. Every user input, including one intended to affect an active run, is an
enqueue-inputcommand against a stable target queue and expected aggregate revision. The owner assigns the
item id, applies once, acknowledges, and broadcastsqueue-changed. Based on current-run state,
queue immediacy, and mode, owningAgentChatmay consume the item immediately through the
internal Copilot path; that consumption is another ordered queue delta. Otherwise the item
remains for a tool boundary or future turn. The remote GUI never invokes Copilot and there is no
steering command. - Route/contain.
ExecutorBindingschooses the final component host. That host resolves the
persisted trust reference/revision, composes the effective profile, compiles once through [mxc] - Compile effective trust profiles into MXC sandbox policies #1475,
and launches through [mxc] - Streaming process executor backed by MXC #1474/[mxc] - Launch copilot.exe through the MXC process executor #1476/[mxc] - Execute MCP tools through MXC-constrained process executor #1477. Machine transports serialize trust intent, never compiled
policy. - Reconnect. The proxy keeps only its cursor and persisted open request. Reconnect reauthorizes
and reuses the runtime. A snapshot includesUsage,AgentInformation, and all queue snapshots;
retained replay applies queue deltas by epoch/global sequence. It neither reconstructs
AgentChatnor relaunches children. - Take over. The old host fences mutations and confirms complete runtime disposal. Persistence
compare/exchanges owner and increments generation. The new host hydrates and starts a fresh epoch.
Failure to confirm an unexpired old lease blocks takeover. - Detach/stop. Explicit detach, proxy disposal, UI tab close, and viewer-application shutdown
release immediately; transport loss reserves the attachment token for five seconds. On final
release,continue-in-background == falsefences and gracefully stops the runtime. Explicit
terminate, takeover, owner-host shutdown, or runtime-lease expiry always fences and stops it.
Stop rejects mutations, interrupts the active turn if required, disposes the chat, component
transports, MXC/process leases, wrappers, process-owned MCP transports and child trees, persists
terminal/stopped state, emits terminal where the channel remains writable, and closes channels.
Tests
RemoteExecutionContainmentMatrixTests:
ExecutionMatrix_AllEightPlacementContainmentCases_UseExpectedAgentAndLaunchHosts.ExecutionMatrix_AllEightCases_ResolveTrustOnlyOnFinalLaunchHost.ExecutionMatrix_ContainmentNotRequired_UsesOrdinaryExecutorBranch.ExecutionMatrix_ContainmentRequired_UsesMxcExecutorBranch.ExecutionMatrix_RemoteBoundary_ContainsNoCompiledPolicyOrPolicyPath.ExecutionMatrix_GuiAndEntityTools_UseApplicationAuthorizationNotMxcClaim.SplitClient_RemoteCopilot_LocalAgentChat_PreservesDistinctTopology.ContainedCopilot_RemoteOwner_CreatesWrapperEnvelopeOnlyOnCopilotHost.ContainedStdioMcp_RemoteExecutor_OwnsProcessTransportOnToolHost.ContainmentCompileFails_ReturnsSanitizedErrorAndDoesNotLaunch.MxcLaunchFails_DoesNotFallbackUncontained.ConstrainedHttpOrSse_ReturnsUnsupportedPolicy.RemoteError_PolicyPathEnvironmentArgvAndStderr_AreAbsent.Takeover_NewHost_RehydratesIntentAndRecompilesPolicy.
This matrix also consumes the public-method tests owned by #1474-#1477:
Compile_* in MxcTrustProfilePolicyCompilerTests,
ProcessExecutor_*/ProcessHandle_*,
CreateConnection_* in CopilotRuntimeConnectionFactoryTests,
ConnectAsync_*/DisposeAsync_* in ProcessExecutorBackedClientTransportTests, and
RemoteStdio_* in RemoteMcpHostHandlerTests. Those APIs are not duplicated here.
Remote integration additionally requires:
GetCompilationAsync_ConcurrentCallers_ResolveAndCompileOncein
AgentExecutionTrustContextTests.GetCompilationAsync_StaleRevision_CachesFailClosedResultin
AgentExecutionTrustContextTests.StartAsync_CancelledDuringLaunch_CleansPartialProcessand
TerminateAsync_RepeatedCall_KillsTreeOnceinProcessExecutorTests.WaitAsync_Cancelled_DoesNotReleaseProcessOwnershipinProcessHandleTests.CreateConnectionAsync_ConcurrentLifecyclePaths_ReturnsOneSelectionand
CreateConnectionAsync_Cancelled_RemovesUnconsumedEnvelopein
CopilotRuntimeConnectionFactoryTests.DisposeAsync_UnconsumedHandoff_DeletesFileOnceand
DisposeAsync_TransferredConnection_DoesNotKillSdkRuntimein
CopilotRuntimeConnectionFactoryTests.Name_ConstructedTransport_ReturnsConfiguredNamein
ProcessExecutorBackedClientTransportTests.
Context and service contract tests
CurrentSessionContext_ValidOwnerGenerationEpoch_PreservesOwningHostIdentityCurrentSessionContext_BlankOwner_RejectsInitializationCurrentSessionContext_NegativeGeneration_RejectsInitializationCurrentSessionContext_AttachmentPeer_DoesNotReplaceHostIdentityAgentServices_AgentExecutionTrustContextSetter_WithExpressionPreservesOtherServicesAgentServices_RemoteRuntimeIntentSetter_WithExpressionPreservesOtherServicesAgentServices_GetService_NewObjectTypedSeams_DoesNotExposeConcreteTypesCurrentSessionContext_TwoRemoteSessions_DoNotCrossContaminate
Dependencies
- Depends on: [RemoteChat] - Integrate running table and all open paths #1488 (requires integrated acquisition and runtime hydration)
- Depends on: [mxc] - Align trust-profile filesystem and network schema with MXC #1472 (requires trust-profile schema and composition)
- Depends on: [mxc] - Streaming process executor backed by MXC #1474 (requires the process executor)
- Depends on: [mxc] - Compile effective trust profiles into MXC sandbox policies #1475 (requires MXC policy compilation)
- Depends on: [mxc] - Execute MCP tools through MXC-constrained process executor #1477 (requires executor-backed MCP and trust integration)
Activity
JoshuaRowePhantom commentedon Sep 7, 2026
Depends on #1488 — requires integrated acquisition and runtime hydration
JoshuaRowePhantom commentedon Sep 7, 2026
Depends on #1472 — requires trust-profile schema and composition
JoshuaRowePhantom commentedon Sep 7, 2026
Depends on #1474 — requires the process executor
JoshuaRowePhantom commentedon Sep 7, 2026
Depends on #1475 — requires MXC policy compilation
JoshuaRowePhantom commentedon Sep 7, 2026
Depends on #1477 — requires executor-backed MCP and trust integration
JoshuaRowePhantom commentedon Sep 10, 2026
Design
Root cause
Persisted owner/generation, executor bindings, and trust intent are hydrated independently, but first materialization does not compose a single host-authoritative execution context:
RunningAgentChatTabledoes not create the trust context from the persisted revision,AgentFactorycan re-resolve definition trust instead of consuming that context, and remote Copilot/component paths do not consistently carry intent to the final launch host. This can select the wrong host, permit stale configuration to be reinterpreted, or lose lifecycle ownership.Chosen approach
Add deterministic matrix/contract tests first. Hydrate one immutable runtime context at first acquisition, preserve host identity per session, and create a lazy revision-pinned trust context that resolves/compiles only on the final launch host with shared cached success/failure. Route model and MCP components through existing
ExecutorBindings; machine requests carry only reference/revision intent. Integrate Copilot's policy-aware launch lease, fail closed on stale/missing/compiler/wrapper/executor/unsupported cases, sanitize remote errors, and make final runtime disposal own every transport/process/handoff resource without timing-based synchronization.Alternatives
Compiling on the owner and serializing policy was rejected because launch-host paths/environment are authoritative and policy data must not cross transport. Re-resolving per component was rejected because it creates revision races and duplicate compilation. Adding parallel placement/trust APIs was rejected in favor of existing
ExecutorBindingsandAgentExecutionTrustContext.Files
Tests in
Phantom.Workspaces.TestsandPhantom.Workspaces.Llm.Core.Tests; runtime hydration/composition inRunningAgentChatTable,AgentFactory/AgentChatFactory,AgentServices, andCurrentSessionContext; final-host routing/launch in existing Copilot and MCP transport integration files. Persistence changes are limited to revisioned intent; no compiled policy/path is persisted or transported.JoshuaRowePhantom commentedon Sep 10, 2026
Implementation notes
Commits:
0475442e,c64084a9,f3dccb68,a15ded2e,c3dc68a1Deviations from proposed design
The approved contracts were preserved. Dependency #1476 was still unmerged and based on obsolete pre-#1477/#1488 APIs, so its Copilot MXC containment was integrated here and adapted to the current revision-pinned trust/runtime contracts rather than restoring the obsolete provider APIs.
Scope changes
Added the missing async process lifecycle signatures/ownership guarantees and Copilot wrapper handoff implementation required by #1490's exact consumed contracts. No compiled policy, policy path, environment, argv, or stderr crosses a machine boundary.
Files changed
packaging/validate/Assert-CopilotRuntimePayload.ps1Phantom.Workspaces.Llm.Core: agent factories; Copilot connection/envelope/client; MCP factory/process transport; process executor; Copilot transport frames/listener/client; trusted executors; trust context/compilerPhantom.Workspaces: runtime host factory, Remote MCP handler, running-chat hydration, transport compositionRemoteExecutionContainmentMatrixTestsVerification
JoshuaRowePhantom commentedon Sep 10, 2026
Verification result
Status: ❌ 6 issue(s) found
Commits checked:
Outstanding gaps
ull (CopilotSdkChatClient.cs:1543-1553), contrary to the required deterministic throw/no-transport contract.
Tests
JoshuaRowePhantom commentedon Sep 10, 2026
Design (verification retry)
Root cause
The production creator omits revision-pinned trust intent, and several downstream parsers/routers conflate invalid intent with absent intent. Copilot additionally treats an unknown named executor as local, while remote launch boundaries expose inconsistent raw failures. Existing matrix tests model routing in test helpers instead of exercising persisted hydration, authoritative binding selection, host-local compilation, takeover, and disposal paths.
Chosen approach
Persist the selected trust reference and expected revision in the authoritative session entity request, then verify first hydration consumes exactly that persisted intent. Make trust descriptor parsing tri-state/strict so only a truly absent descriptor means untrusted; malformed/partial descriptors fail before compilation or launch. Let unknown Copilot bindings throw before opening transport. Add one shared sanitizing boundary per remote launch handler that preserves cancellation but converts resolution, compilation, wrapper, and executor failures into stable safe errors. Replace/augment synthetic tests with deterministic production-path tests covering creation→persistence→hydration, local/remote component selection, takeover recompilation, malformed frames, unknown bindings/no transport, cached compiler failures, sanitized remote failures, and final-release disposal of routed resources/child handles.
Alternatives
Defaulting malformed descriptors to a restrictive profile was rejected because it invents intent and can drift across hosts. Falling back locally for unavailable remote executors was rejected because placement must fail closed. Sanitizing only known exception types was rejected because wrapper/executor implementations can fail through arbitrary exception types.
Files
Production creation/persistence in
AgentManifestSessionLauncherandAgentSessionShortcutContext; strict trust parsing and remote error handling in Copilot/MCP transport handlers and request descriptors; routing inCopilotSdkChatClient; production-path tests in the owningPhantom.Workspaces.TestsandPhantom.Workspaces.Llm.Core.Testsclasses, replacing synthetic matrix assertions where appropriate.JoshuaRowePhantom commentedon Sep 10, 2026
Implementation notes (verification retry)
Commit:
9bdf62cbfb71f359c2bc079ea9b0233d8a681d63Deviations from proposed design
None. The synthetic eight-case placement and takeover checks were replaced/augmented with persisted-entity, runtime-hydration, real Copilot transport-selection, host-local compilation, and independent takeover materialization paths.
Scope changes
The remote lifecycle tests exposed that
LocalTransportdiscarded listener leases and that remote MCP relay startup raced immediate final release. Both tightly coupled lifecycle defects were fixed so final release deterministically disposes routed Copilot policy/client leases and process-owned MCP handles.Files changed
AgentManifestSessionLauncher.cs,AgentSessionShortcutContext.cs,DataAccessLayerTrustProfileResolver.csCopilotSdkChatClient.cs,CopilotSessionTransportFrames.cs,McpConnectionRequest.csCopilotClientTransportListener.cs,RemoteMcpHostHandler.cs,LocalTransport.csRemoteExecutionProductionPathTests.csVerification
16 remaining items
JoshuaRowePhantom commentedon Sep 11, 2026
Diagnosis: test failure is unrelated to this change
Failing test:
SubAgentDispatcherIntegrationTests.Cancellation_InterruptsRunningSubAgent_AndYieldsInterruptedFiled bug: #1572
Why unrelated: #1490 changes neither the dispatcher implementation nor this test; the test directly uses its private controllable factory. Cancellation races stream completion before the iterator's post-ack cancellation registration.
Dump: none produced.
Fix #1490: hydrate host-local revision-pinned trust contexts
Fix #1490: make contained process lifecycle cancellable
Fix #1490: close Copilot handoff lifecycle races
Fix #1490: cover remote containment placement matrix
Fix #1490: close trust and placement verification gaps
Fix #1490: preserve opaque trust revisions at creation
Fix #1490: cancel in-flight MCP initialization on release
Fix #1490: verify production routing takeover lifecycle
Fix #1490: make wrapper publish RID-consistent
Test #1490: inspect nested wrapper publish
Fix #1490: own remote input queue operations
JoshuaRowePhantom commentedon Sep 11, 2026
Checked in
Commit: feca059
Branch fast-forwarded: features → feca059
Validation: build 0 errors; changed tests 311 × 10 passes; fast suite 7,120 passed / 1 known flaky (#1572); full suite 7,353 passed / 0 failed.