You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Key decision: reuse-first, NO new execution schema
An executor binding IS a transport connection-descriptor — the existing type-discriminated JSON already consumed by ITransportFactory.ConnectToAsync(JsonElement) (local, user-computer-profile, http, reverse-http, …). There is NO parallel ExecutorDescriptor type/record/schema anywhere in this design. An executor resource resolves to a connection-descriptor, which is fed straight into ITransportFactoryRegistry.ConnectToAsync. A trust profile's execution target already IS such a descriptor (TrustProfile.DefaultExecutionTarget), and Llm.Trust.ExecutionTargetResolver.ResolveDescriptor already produces the local/profile shapes.
Nesting is host-OUTER, target-INNER: {"type":"user-computer-profile","entity-id":"<host>","target":{ ...inner... }} ΓÇö the OUTER descriptor reaches the host, the INNER target runs there (the TargetedTransport seam in UserComputerProfileTransportFactory). The connection-descriptor strategy (a raw inline descriptor used verbatim) makes the model open-endedly extensible with no schema change.
Requirements
Per-component binding. Each component of an agent session ΓÇö the model / Copilot-SDK chat client, and each individual MCP server/tool ΓÇö can be bound to a specific executor (the local orchestrator, or a named remote machine), instead of routing uniformly by tool-kind.
Executors are named manifest resources. Executors are expressed as manifest resources[] entries of kind:"executor", referenced by name from the model (via model.options.executor, an executor name under the model options bag) and from tools (via an optional executor field).
Unset inherits the session executor. An unset executor inherits the session's overall executor, which is the local orchestrator machine today ({"type":"local"} / "."). The system MUST NOT require or emit executor:"local".
Executor id resolution strategies. An executor resource's id selects how it resolves to a transport connection-descriptor (JsonElement): local, parameter (bind to a launch parameter), user-computer-profile-entity (fixed profile entity-id), trust-profile, and connection-descriptor (a raw escape hatch carrying an inline connection-descriptor used verbatim). There is no parallel executor schema — an executor resolves to the sametype-discriminated connection-descriptor the transport layer already consumes (local, user-computer-profile, http, reverse-http, …). The connection-descriptor strategy is what makes the model open-endedly extensible with no schema change.
executor launch parameter. A new manifest parameter kind:"executor" lets the user pick, at launch, which executor a parameter-strategy executor resource resolves to. It offers a choice among two selectable option kinds: a trust-profile entity ("choose by trust policy") → that profile's DefaultExecutionTarget; and a user-computer-profile entity → choosing one synthesizes an implicit trust profile (in-memory TrustProfileDefinition whose DefaultExecutionTarget = {"type":"user-computer-profile","entity-id":<chosen uuid>} and whose HostingWorkspacesClientInstances = [<chosen uuid>]). Both paths converge on a trust profile whose DefaultExecutionTarget is the executor binding. The chosen selection is recorded in the typed parameter-selections map (string→JsonElement): {"trust-profile":"<name-or-id>"} or {"user-computer-profile":"<entity-id>"} — a sibling of the string→stringparameter-values, which stays reserved for ${param} text templating (M7), lossless through PhantomAgentSchema.
MCP servers must actually execute on their bound executor.McpToolContextProvider MUST connect through the transport router (ExecutorTargetRouter → ITransportFactoryRegistry) when its resolved connection-descriptor is non-local; today it always connects in-process and ExecutorTargetRouter has no production consumer.
Remote MCP hosting. The remote host MUST host an arbitrary stdio/HTTP MCP server on request via a production openConnectionAsync handler registered on McpTransportListener.
Entity resolution scoped to bound executor.mcp-server-entity resource resolution MUST resolve in the context of the tool's bound executor (search order: machine profile → ${USER}/mcp-servers → defaults/mcp-servers), not the resolving instance.
Persist per-executor bindings. The session MUST persist per-executor bindings (executor-bindings: name → resolved connection-descriptor object) so the topology reconstructs correctly on resume — not just the single host-profile-entity-id.
Explicit session executor. The "session overall executor" becomes an explicit concept (default local {"type":"local"}) that per-component executors override.
Default split manifest. Ship a default manifest entity (defaults/agent-manifests/...) implementing this split: the Copilot-SDK chat client runs remotely; the chat router, the workspace tools (workspace-gui / workspace-entity), and the GitHub web MCP server run locally.
No web-vs-non-web distinction. An MCP server with no executor simply runs on the local session executor. There is no special "web tools go remote" rule.
OAuth interactivity rationale. MCP servers using interactive OAuth (authorization-code with a loopback/localhost redirect + a browser) MUST run on the machine that can open the user's browser and receive the loopback redirect ΓÇö i.e. the local executor. Therefore OAuth-interactive MCP servers MUST be pinned local; the default manifest pins the GitHub web MCP local for this reason (and because that is where the user authenticates). A key/PAT-authenticated web MCP does not strictly require local, but the default ships it local. Validation note: an MCP tool whose connection uses interactive OAuth combined with a non-local executor MUST be rejected or warned at load/validation time.
Extensible to non-persistent runtimes (future). The model MUST be able to express non-persistent runtimes ΓÇö e.g. an ephemeral container ΓÇö without any manifest or session schema change, by authoring a host-outer / target-inner connection-descriptor and resolving it through the connection-descriptor escape hatch. Implementing such a runtime (a host-side container target handler driving ContainerEngine) is explicit future work, OUT OF SCOPE for the commits in this design ΓÇö there is no container implementation commit and no container child issue. See Extensibility below.
Gaps addressed
Gap
Description
Fixed by
G1
No per-tool / per-MCP executor binding; routing is per-kind static (ExecutorTargetResolver.ForKind).
Commits 1, 6
G2
No executor field on the MCP tool type (PhantomMcpTool carries only Transport).
Commit 3
G3
No kind:"executor" manifest resource (resources[] is anyOf:[toolResource, modelResource]).
Commit 1
G4
No executor parameter kind / picker (Launchpad infers kind by name only, and there is no way to pick an executor by trust profile or implicit-from-user-computer-profile at launch).
Commits 2, 8
G5
MCP mcp-server-entity resolution is not scoped to the bound executor's profile→user→defaults context.
Commit 7
G6
Session persists only one remote (host-profile-entity-id), not per-executor bindings.
Commit 5
G7
"Session overall executor" is implicit (no explicit default-local concept).
Commit 5
G8
ExecutorTargetRouter has no production consumer; McpToolContextProvider always connects in-process.
Commit 6
G9
No remote production handler to host an arbitrary stdio/HTTP MCP server (McpTransportListener is never registered in production).
Commit 6
G10
The executor model must be structured/extensible so non-persistent runtimes (e.g. ephemeral containers) can be added later WITHOUT a manifest/session schema change. Satisfied by the reuse-first design: executors resolve to connection-descriptors and the raw connection-descriptor strategy + type-dispatched transport factories admit new types with no caller/schema change.
Commits 4, 5 + Extensibility (documentation only ΓÇö no implementation commit)
Cohesion findings (M1ΓÇôM7)
A review of the three integration seams (manifest→build, MCP tool→transport, session persistence) produced seven concrete findings that the design doc's Cohesion / integration seams section now enumerates with exact class/method/line names. Summary:
Approach: Option A ΓÇö Per-component executor binding via manifest kind:"executor" resources.
Rationale: Option A gives true per-server granularity (addressing B's fatal con) by making the existing but dormant ExecutorTargetRouter a production consumer and giving each MCP provider a resolved connection-descriptor. It avoids C's sub-session sprawl by keeping ONE AgentChat whose components are individually routed over transport. Its own cons are contained: the remote MCP host handler is a small McpTransportListener registration (the listener primitive already exists and is exercised by tests), the descriptor threading is an additive constructor parameter on McpToolContextProvider, and the parameter-kind work is localised to the manifest parameter model and the Launchpad picker. Because an unset executor and a single-machine topology both resolve to {"type":"local"}, the change is behaviour-preserving for every existing manifest. Reusing the transport connection-descriptor (rather than inventing a parallel executor schema) also means non-persistent runtimes ΓÇö e.g. ephemeral containers ΓÇö can be added later purely as a new connection-descriptor type behind the same ITransportFactory dispatch, with no manifest or session schema change (G10; see Extensibility).
Implementation plan (11 commits)
Eleven commits total: Commits 1ΓÇô9, plus Commit 6B (model/chat-client binding) and Commit 10 (LLM authoring guide). Each commit leaves the build green and all tests passing.
Commit 1 ΓÇö Executor resource schema + model ΓÇö #1433
Scope: Add the executorResource$def to agent-manifest.json and its parsed model ExecutorResource (convenience strategies local/parameter/user-computer-profile-entity/trust-profile via a simple Options string map, PLUS an optional inline ConnectionDescriptorJsonElement for the raw connection-descriptor escape hatch). There is NO bespoke ExecutorDescriptor schema ΓÇö an executor resolves to the transport connection-descriptor. Extend resources.items.anyOf to include executorResource. Add the optional executor string to the toolResource$def (the model authors its executor under model.options.executor, round-tripped by ModelOptions.AdditionalProperties, so no modelResource schema change is needed). Parse kind:"executor" resources into ExecutorResource during manifest load (via the M5 executor pre-pass, distinct from IToolResourceFactory). Files:Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json; Phantom.Workspaces.Llm.Core/Manifest/ExecutorResource.cs (new); the manifest loader that enumerates resources[]. Tests:AgentManifestExecutorResourceTests (Load_ManifestWithExecutorResource_ParsesResource, RoundTrip_ExecutorResourceAndRefs_Lossless, and a connection-descriptor-strategy resource parse/round-trip). Dependencies: none.
Scope: Add an executor parameter kind to the manifest parameter model and its documentation, plus value recording/substitution. The parameter offers two selectable option kinds — a trust-profile entity and a user-computer-profile entity (the latter synthesizing an implicit trust profile) — and records a disambiguated selection in the typed parameter-selections map (string→JsonElement) ({"trust-profile":"<name-or-id>"} or {"user-computer-profile":"<entity-id>"}), a sibling of the unchanged string→stringparameter-values (M7), lossless through PhantomAgentSchema. Make parameter kind read from the manifest parameter kind field rather than being inferred purely by name (see Contradictions). Files: the AgentManifest parameter model / substitutor (Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.cs and the parameter property model); Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md. Tests:AgentManifestExecutorResourceTests.Load_ExecutorParameter_Recognised; a recording test for the new kind (both disambiguated selection shapes, stored as typed JsonElement entries in parameter-selections). Dependencies: none.
Commit 3 ΓÇö PhantomMcpTool.Executor field ΓÇö #1435
Scope: Add Executor (nullable string) to PhantomMcpTool using the established recipe: read in PhantomAgentSchemaPostProcess (ReadExecutor), copy in From(), emit in Save(). Keep the source-scan guard intact. Files:Phantom.Workspaces.Llm.Interfaces/PhantomMcpTool.cs; Phantom.Workspaces.Llm.Interfaces/PhantomAgentSchema.cs. Tests:PhantomMcpToolExecutorTests (Save_WithExecutor_EmitsExecutorField, From_CopiesExecutor, RoundTrip_ExecutorField_Preserved, guard test). Dependencies: none.
Commit 4 ΓÇö Executor-resource resolver (returns a connection-descriptor) ΓÇö #1436
Scope: Add ExecutorResourceResolver mapping an ExecutorResource (+ resolved parameter-values + trust context) to a transport connection-descriptor (JsonElement) for all fiveid strategies (local, parameter, user-computer-profile-entity, trust-profile, connection-descriptor), with clear errors for unknown/unresolved. This is the reuse-first fix: the resolver returns the connection-descriptor that ITransportFactoryRegistry.ConnectToAsync already dispatches on (NOT a flat string, NOT an ExecutorDescriptor), DELEGATING to the existing Llm.Trust.ExecutionTargetResolver.ResolveDescriptor for the local/profile shapes. The parameter strategy reads the typed executor selection (M7) from parameter-selections (not parameter-values). Runs from a distinct pre-pass, not IToolResourceFactory (M5). Its ResolveComponent output populates RuntimeContextProviderRegistration.ConnectionDescriptor (M1). Add ExecutorBindings (session executor default {"type":"local"} + name→connection-descriptor + ResolveComponent + ToTopology + ToPersistableMap). Files:Phantom.Workspaces.Llm.Core/Manifest/ExecutorResourceResolver.cs, Phantom.Workspaces.Llm.Core/Manifest/ExecutorBindings.cs (new). Tests:ExecutorResourceResolverTests (incl. Resolve_ConnectionDescriptorId_ReturnsInlineDescriptorVerbatim, Resolve_ParameterValue_JsonEncodedString_IsParsed), ExecutorBindingsTests, ExecutorResourcePrePassTests. Dependencies: Commit 1 (ExecutorResource); Commit 2 (for the parameter strategy / typed parameter-selections value).
Scope: Make the session's overall executor explicit (default {"type":"local"}). Add executor-bindings (root shape { "session": <descriptor>, "components": { name→connection-descriptor } }) and a new typed parameter-selections root key (string→JsonElement) to agent-session.json. On build, write bindings alongside the existing parameter-values / host-profile-entity-id (AgentSessionEntityFactory.cs:39-93); on resume, rebuild ExecutorTopology and set the deferred selector's topology from the bindings (deriving the client-instance strings for local/user-computer-profile shapes). M6 back-compat: if executor-bindings.session is absent but host-profile-entity-id is present, derive SessionExecutor = {"type":"user-computer-profile","entity-id":<id>}; bindings are the source of truth. The executor selection persists in a new typed parameter-selections root key (string→JsonElement), a sibling of the unchanged string→stringparameter-values (M7 — no dictionary widening). Files:Phantom.Workspaces.Data.Core/JsonSchemas/agent-session.json; Phantom.Workspaces.Data.Core/AgentSessionEntityFactory.cs; the session build/resume path that constructs ExecutorTopology and calls DeferredTrustedExecutorSelector.SetTopology. Tests:AgentSessionExecutorBindingsTests (incl. Persist_DescriptorObjects_RoundTrips, Persist_SessionAndComponentsKeys_RoundTrip, Resume_RebuildsTopologyFromBindings, Resume_LegacyHostProfileOnly_DerivesSessionExecutor). Dependencies: Commit 4.
Commit 6 ΓÇö Per-tool MCP execution over transport + production remote MCP host ΓÇö #1438
Scope: Thread the resolved connection-descriptor + a production ExecutorTargetRouter into each McpToolContextProvider (constructed in AgentChat). When the bound descriptor is non-local, feed it straight into the router → ITransportFactoryRegistry.ConnectToAsync (no string hop), opening an McpClientOverTransport — bridged to the MCP SDK client through the NEW McpChannelClientTransport (IClientTransport over IMessageChannel, M2); when local, keep the in-process path (no round-trip). Nesting is host-outer/target-inner. Add the production RemoteMcpHostHandler (openConnectionAsync, steps a–e) and register it on a productionMcpTransportListener in WorkspacesTransportComposition (M3). This makes ExecutorTargetRouter a production consumer (G8) and provides the arbitrary stdio/HTTP MCP host (G9). The remote host handler CONSUMES #1439's scoped resolver (machine prefix first) — a two-way touchpoint documented in both issues; dependency direction unchanged. Files:Phantom.Workspaces.Llm.Core/McpToolContextProvider.cs, Phantom.Workspaces.Llm.Core/AgentChat.cs, Phantom.Workspaces.Transport.Mcp/McpChannelClientTransport.cs (new), Phantom.Workspaces.Transport.Mcp/RemoteMcpHostHandler.cs (new), Phantom.Workspaces/Services/WorkspacesTransportComposition.cs. Tests:McpToolContextProviderRoutingTests, McpChannelClientTransportTests, RemoteMcpHostHandlerTests, WorkspacesTransportCompositionTests, Scenario3_PerMcpServerRoutingTests. Dependencies: Commits 3, 4, 5. Touchpoint:#1439 (scoped resolver consumed by the host handler).
Commit 6B ΓÇö Model / chat-client executor binding (remote client, local router) ΓÇö #1443
Scope:(M4.) Honour a model.options.executor binding by transporting ONLY CopilotSdkChatClient's inner SDK session while the router (IChatClient decorators) and the AIContextProviders stay local — the structural inverse of TransportTrustedExecutor.CreateAgentChatAsync (Transport/TransportTrustedExecutor.cs:43-67, :50/:54), which remotes the whole AgentChat. When the model's resolved descriptor is {"type":"local"}, behaviour is unchanged (in-process CLI). When non-local, CopilotSdkChatClient obtains its SDK session over a transport (EnsureSessionAsync:1230 → copilotClientFactory.Create:1277 → CreateOrResumeSessionAsync:1324-1357) instead of in-process. A production client-only host path builds a local CopilotSdkChatClient on the remote (distinct from ChatClientTransportListener's whole-AgentChat build). Inserted as 6B so Commits 7/8/9 do NOT renumber. Files:Phantom.Workspaces.Llm.Core/CopilotSdkChatClient.cs; Phantom.Workspaces.Llm.Core/AgentFactory.cs; new CopilotSessionOverTransport under Phantom.Workspaces.Llm.Core/Transport/Chat/; Phantom.Workspaces/Services/WorkspacesTransportComposition.cs. Tests:CopilotSdkChatClientExecutorTests, AgentChatExecutorBindingTests, WorkspacesTransportCompositionTests. Dependencies: Commit 4 (#1436), Commit 5 (#1437). Required by: Commit 9 (#1441).
Commit 7 ΓÇö MCP mcp-server-entity resolution scoped to the bound executor ΓÇö #1439
Scope: When a tool id is mcp-server-entity, evaluate the search prefixes (machine profile → ${USER}/mcp-servers → defaults/mcp-servers) against the bound executor's profile/user context instead of the resolving instance's, constructing the McpServerEntityToolResourceFactorysearchPrefixes with the bound machine first. The remote MCP host handler from #1438 CONSUMES this scoped resolver (two-way touchpoint). Files: the mcp-server-entity resolution code (Phantom.Workspaces/McpServerEntityToolResourceFactory.cs:21-113). Tests:McpServerEntityBoundExecutorResolutionTests. Dependencies: Commit 6. Touchpoint:#1438 (its host handler consumes this resolver).
Scope: Rename the AgentManifestParameterKind enum value UserComputerProfile → Executor and add a combined picker in the Launchpad that lists bothtrust-profile entities anduser-computer-profile entities; the selection records the disambiguated value ({"trust-profile":...} or {"user-computer-profile":...}) as a typed JsonElement in parameter-selections (not parameter-values). It is no longer a user-computer-profile-only picker. Honour the manifest parameter kind field. Files:Phantom.Workspaces/ViewModels/AgentManifestParameterKind.cs, Phantom.Workspaces/ViewModels/AgentManifestParameterRowViewModel.cs, Phantom.Workspaces/ViewModels/AgentManifestLaunchpadViewModel.cs, Phantom.Workspaces/Templates/AgentManifestLaunchpadView.axaml(.cs). Tests:AgentManifestLaunchpadViewModelTests. Dependencies: Commit 2.
Scope: Add defaults/agent-manifests/copilot-split-executor with: one kind:"executor" resource worker (id parameter → worker-profile); a worker-profile parameter (kind executor) + a working-directory parameter; a model with executor:"worker"; workspace-gui / workspace-entity tools with noexecutor (inherit local); a GitHub web MCP tool with noexecutor (local, for OAuth). Add load-time validation that rejects/warns an OAuth-interactive MCP whose executor is non-local. Cross-check the exact tool/model/connection JSON shapes against features/docs/examples/github-copilot-remote-chat.json. The model-remote behaviour depends on Commit 6B (#1443). Files:Phantom.Workspaces.Data.Core/JsonEntities/agent-manifests/copilot-split-executor.json (new); the manifest/validation code that enforces the OAuth-local rule. Tests:CopilotSplitExecutorManifestTests, SplitExecutorIntegrationTests. Dependencies: Commits 1–6, 6B.
Scope: Add a single, self-contained, machine-consumable authoring reference (Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-manifest-executors.md) so an LLM can reliably author a valid executor-bound manifest or reason about a session: the kind:"executor" resource + five id strategies, the executor reference on tools and the model.options.executor reference on the model, the kind:"executor" launch parameter + disambiguated recorded value, the reused connection-descriptor types + host-outer/target-inner nesting, the persisted executor-bindings session shape, two worked end-to-end examples (split-executor topology from #1441; trivial all-local baseline), and OAuth-local guidance. Every fenced JSON block is a real, parseable example guarded by tests. Complements #1441 by documenting the GENERAL authoring rules. Files:Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-manifest-executors.md (new); cross-links in agent-options-parameters.md; schema-comment alignment in agent-manifest.json. Tests:ExecutorAuthoringGuideTests (AuthoringGuide_EmbeddedManifestExamples_ParseAndRoundTrip, AuthoringGuide_EmbeddedSessionExamples_ResolveToDescriptors, AuthoringGuide_DocumentsAllFiveIdStrategies, AuthoringGuide_ExecutorParameterValueShapes_Documented). Dependencies: Commits 1 (#1433), 2 (#1434), 3 (#1435), 5 (#1437); complements Commit 9 (#1441).
Extensibility ΓÇö non-persistent executors
Explicitly OUT OF SCOPE and FUTURE WORK ΓÇö there is no container implementation commit and no container child issue in this design. This is documentation only. It records how the reuse-first connection-descriptor model extends to non-persistent runtimes (an ephemeral container is the worked example) without any manifest or session schema change, because an ExecutorResource already supports the raw connection-descriptor strategy and the transport layer already dispatches by type.
Reuse the existing containers subsystem.Phantom.Workspaces.Containers already defines ContainerDefinition + container-definition.json and ContainerEngine (CreateAsync/PullAsync/StartAsync/StopAsync/DestroyAsync/UsableAsync) with Docker Desktop / containerd implementations (already used in production by MongoDbConnectionBroker). A future container executor would drive this subsystem ΓÇö it does not invent a new container abstraction, and no container manifest/session schema is added.
Authoring shape (host-outer, target-inner). A container executor would be authored via the raw connection-descriptor strategy as a host-outer descriptor with an inner container target: {"type":"user-computer-profile","entity-id":"<worker>","target":{"type":"container","container-definition":<ContainerDefinition>}}. The OUTER descriptor reaches the host; the INNER target runs there (threaded through the existing TargetedTransport seam). Because ExecutorResource already carries an inline connection-descriptor, no new manifest field is needed.
Extension work (future). Add a host-side container target handler ΓÇö analogous to the MCP host handler seam ΓÇö driving ContainerEngine create/start/attach and DestroyAsync on teardown, registered as an ITransportFactory/listener. Trust inherits the resolved host executor. Open question (future): what runs INSIDE the container (a full agent-executor endpoint vs. just the pinned MCP server) ΓÇö the design does not answer this; it only guarantees the connection-descriptor model can extend here with no manifest/session schema change.
Design: per-component-executor-binding
Design document:
docs/design/per-component-executor-binding.md(committed to thefeaturesbranch)https://github.com/JoshuaRowePhantom/Phantom.Workspaces/blob/features/docs/design/per-component-executor-binding.md
Key decision: reuse-first, NO new execution schema
An executor binding IS a transport connection-descriptor ΓÇö the existing
type-discriminated JSON already consumed byITransportFactory.ConnectToAsync(JsonElement)(local,user-computer-profile,http,reverse-http, …). There is NO parallelExecutorDescriptortype/record/schema anywhere in this design. An executor resource resolves to a connection-descriptor, which is fed straight intoITransportFactoryRegistry.ConnectToAsync. A trust profile's execution target already IS such a descriptor (TrustProfile.DefaultExecutionTarget), andLlm.Trust.ExecutionTargetResolver.ResolveDescriptoralready produces thelocal/profile shapes.Nesting is host-OUTER, target-INNER:
{"type":"user-computer-profile","entity-id":"<host>","target":{ ...inner... }}ΓÇö the OUTER descriptor reaches the host, the INNERtargetruns there (theTargetedTransportseam inUserComputerProfileTransportFactory). Theconnection-descriptorstrategy (a raw inline descriptor used verbatim) makes the model open-endedly extensible with no schema change.Requirements
resources[]entries ofkind:"executor", referenced by name from the model (viamodel.options.executor, an executor name under the model options bag) and from tools (via an optionalexecutorfield).executorinherits the session's overall executor, which is the local orchestrator machine today ({"type":"local"}/"."). The system MUST NOT require or emitexecutor:"local".idresolution strategies. An executor resource'sidselects how it resolves to a transport connection-descriptor (JsonElement):local,parameter(bind to a launch parameter),user-computer-profile-entity(fixed profile entity-id),trust-profile, andconnection-descriptor(a raw escape hatch carrying an inline connection-descriptor used verbatim). There is no parallel executor schema — an executor resolves to the sametype-discriminated connection-descriptor the transport layer already consumes (local,user-computer-profile,http,reverse-http, …). Theconnection-descriptorstrategy is what makes the model open-endedly extensible with no schema change.executorlaunch parameter. A new manifest parameterkind:"executor"lets the user pick, at launch, which executor aparameter-strategy executor resource resolves to. It offers a choice among two selectable option kinds: a trust-profile entity ("choose by trust policy") → that profile'sDefaultExecutionTarget; and a user-computer-profile entity → choosing one synthesizes an implicit trust profile (in-memoryTrustProfileDefinitionwhoseDefaultExecutionTarget = {"type":"user-computer-profile","entity-id":<chosen uuid>}and whoseHostingWorkspacesClientInstances = [<chosen uuid>]). Both paths converge on a trust profile whoseDefaultExecutionTargetis the executor binding. The chosen selection is recorded in the typedparameter-selectionsmap (string→JsonElement):{"trust-profile":"<name-or-id>"}or{"user-computer-profile":"<entity-id>"}— a sibling of thestring→stringparameter-values, which stays reserved for${param}text templating (M7), lossless throughPhantomAgentSchema.McpToolContextProviderMUST connect through the transport router (ExecutorTargetRouter→ITransportFactoryRegistry) when its resolved connection-descriptor is non-local; today it always connects in-process andExecutorTargetRouterhas no production consumer.openConnectionAsynchandler registered onMcpTransportListener.mcp-server-entityresource resolution MUST resolve in the context of the tool's bound executor (search order: machine profile →${USER}/mcp-servers→defaults/mcp-servers), not the resolving instance.executor-bindings: name → resolved connection-descriptor object) so the topology reconstructs correctly on resume — not just the singlehost-profile-entity-id.{"type":"local"}) that per-component executors override.defaults/agent-manifests/...) implementing this split: the Copilot-SDK chat client runs remotely; the chat router, the workspace tools (workspace-gui/workspace-entity), and the GitHub web MCP server run locally.executorsimply runs on the local session executor. There is no special "web tools go remote" rule.executorMUST be rejected or warned at load/validation time.connection-descriptorescape hatch. Implementing such a runtime (a host-side containertargethandler drivingContainerEngine) is explicit future work, OUT OF SCOPE for the commits in this design — there is no container implementation commit and no container child issue. See Extensibility below.Gaps addressed
ExecutorTargetResolver.ForKind).executorfield on the MCP tool type (PhantomMcpToolcarries onlyTransport).kind:"executor"manifest resource (resources[]isanyOf:[toolResource, modelResource]).executorparameter kind / picker (Launchpad infers kind by name only, and there is no way to pick an executor by trust profile or implicit-from-user-computer-profile at launch).mcp-server-entityresolution is not scoped to the bound executor's profile→user→defaults context.host-profile-entity-id), not per-executor bindings.ExecutorTargetRouterhas no production consumer;McpToolContextProvideralways connects in-process.McpTransportListeneris never registered in production).connection-descriptorstrategy +type-dispatched transport factories admit newtypes with no caller/schema change.Cohesion findings (M1–M7)
A review of the three integration seams (manifest→build, MCP tool→transport, session persistence) produced seven concrete findings that the design doc's Cohesion / integration seams section now enumerates with exact class/method/line names. Summary:
ExecutorBindingsis the source of truth; extend the private recordRuntimeContextProviderRegistration(AgentChat.cs:2965-2969) with aJsonElement? ConnectionDescriptorfromExecutorBindings.ResolveComponent(tool.Executor); explicitexecutorwins, unset falls back to the legacyExecutorTargetΓåÆExecutorTopologypath. ΓåÆ [per-component-executor] - Executor-resource resolver #1436, [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438.McpToolContextProvider(:26-37,:47-102) must consume the descriptor and needs a NEWMcpChannelClientTransportbridge (IClientTransportover the transportIMessageChannel) for the remote branch. ΓåÆ [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438.McpTransportListenerinWorkspacesTransportComposition(:37-82, today onlyChatClientTransportListenerat:54-63) whose handler resolves the tool config on the remote host via [per-component-executor] - MCPmcp-server-entityresolution scoped to the bound executor #1439's scoped resolver (machine prefix first). ΓåÆ [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438 (consumes [per-component-executor] - MCPmcp-server-entityresolution scoped to the bound executor #1439).TransportTrustedExecutornesting so onlyCopilotSdkChatClient's inner SDK session is transported. ΓåÆ new [per-component-executor] - Bind the Copilot SDK chat client (model) to its executor (remote client, local router) #1443.manifest.ResourcesforKind=="executor", NOT anIToolResourceFactory(which returnsTool?). ΓåÆ [per-component-executor] - Executor resource schema + model #1433, [per-component-executor] - Executor-resource resolver #1436.executor-bindingsroot key ({session, components}) with the existinghost-profile-entity-id(AgentSessionEntityFactory.cs:39-93); derive the session executor fromhost-profile-entity-idon read when bindings are absent. ΓåÆ [per-component-executor] - Explicit session executor +executor-bindingspersistence + resume #1437.parameter-valuesstaysstringΓåÆstringfor${param}text templating; theexecutorselection is recorded in a dedicated typedparameter-selectionsmap (stringΓåÆJsonElement, a sibling root key) that the resolver reads to emitexecutor-bindings(no dictionary widening). ΓåÆ [per-component-executor] -executorparameter kind #1434 (recording), [per-component-executor] - Executor-resource resolver #1436 (reading), [per-component-executor] - Explicit session executor +executor-bindingspersistence + resume #1437 (persistence). See the non-blocking [per-component-executor] - Typed manifest parameter values (beyond string→string) #1444 below.Related / non-blocking
stringΓåÆstring). The general capability to give all manifest parameters typed values. Motivated by M7 but independent and non-blocking: this feature records theexecutorselection in the dedicated typedparameter-selectionschannel and does not depend on [per-component-executor] - Typed manifest parameter values (beyond string→string) #1444. [per-component-executor] - Typed manifest parameter values (beyond string→string) #1444 would later letparameter-selectionsfold into a unified typedparameter-values. It is not one of the 11 commits/children below and is not a dependency.Chosen design
Approach: Option A ΓÇö Per-component executor binding via manifest
kind:"executor"resources.Rationale: Option A gives true per-server granularity (addressing B's fatal con) by making the existing but dormant
ExecutorTargetRoutera production consumer and giving each MCP provider a resolved connection-descriptor. It avoids C's sub-session sprawl by keeping ONEAgentChatwhose components are individually routed over transport. Its own cons are contained: the remote MCP host handler is a smallMcpTransportListenerregistration (the listener primitive already exists and is exercised by tests), the descriptor threading is an additive constructor parameter onMcpToolContextProvider, and the parameter-kind work is localised to the manifest parameter model and the Launchpad picker. Because an unsetexecutorand a single-machine topology both resolve to{"type":"local"}, the change is behaviour-preserving for every existing manifest. Reusing the transport connection-descriptor (rather than inventing a parallel executor schema) also means non-persistent runtimes ΓÇö e.g. ephemeral containers ΓÇö can be added later purely as a new connection-descriptortypebehind the sameITransportFactorydispatch, with no manifest or session schema change (G10; see Extensibility).Implementation plan (11 commits)
Eleven commits total: Commits 1ΓÇô9, plus Commit 6B (model/chat-client binding) and Commit 10 (LLM authoring guide). Each commit leaves the build green and all tests passing.
Commit 1 ΓÇö Executor resource schema + model ΓÇö #1433
Scope: Add the
executorResource$deftoagent-manifest.jsonand its parsed modelExecutorResource(convenience strategieslocal/parameter/user-computer-profile-entity/trust-profilevia a simpleOptionsstring map, PLUS an optional inlineConnectionDescriptorJsonElementfor the rawconnection-descriptorescape hatch). There is NO bespokeExecutorDescriptorschema ΓÇö an executor resolves to the transport connection-descriptor. Extendresources.items.anyOfto includeexecutorResource. Add the optionalexecutorstring to thetoolResource$def(the model authors its executor undermodel.options.executor, round-tripped byModelOptions.AdditionalProperties, so nomodelResourceschema change is needed). Parsekind:"executor"resources intoExecutorResourceduring manifest load (via the M5 executor pre-pass, distinct fromIToolResourceFactory).Files:
Phantom.Workspaces.Llm.Core/JsonSchemas/agent-manifest.json;Phantom.Workspaces.Llm.Core/Manifest/ExecutorResource.cs(new); the manifest loader that enumeratesresources[].Tests:
AgentManifestExecutorResourceTests(Load_ManifestWithExecutorResource_ParsesResource,RoundTrip_ExecutorResourceAndRefs_Lossless, and aconnection-descriptor-strategy resource parse/round-trip).Dependencies: none.
Commit 2 ΓÇö
executorparameter kind ΓÇö #1434Scope: Add an
executorparameter kind to the manifest parameter model and its documentation, plus value recording/substitution. The parameter offers two selectable option kinds — a trust-profile entity and a user-computer-profile entity (the latter synthesizing an implicit trust profile) — and records a disambiguated selection in the typedparameter-selectionsmap (string→JsonElement) ({"trust-profile":"<name-or-id>"}or{"user-computer-profile":"<entity-id>"}), a sibling of the unchangedstring→stringparameter-values(M7), lossless throughPhantomAgentSchema. Make parameter kind read from the manifest parameterkindfield rather than being inferred purely by name (see Contradictions).Files: the
AgentManifestparameter model / substitutor (Phantom.Workspaces.Llm.Core/AgentDefinitionParameterSubstitutor.csand the parameter property model);Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-options-parameters.md.Tests:
AgentManifestExecutorResourceTests.Load_ExecutorParameter_Recognised; a recording test for the new kind (both disambiguated selection shapes, stored as typedJsonElemententries inparameter-selections).Dependencies: none.
Commit 3 ΓÇö
PhantomMcpTool.Executorfield ΓÇö #1435Scope: Add
Executor(nullable string) toPhantomMcpToolusing the established recipe: read inPhantomAgentSchemaPostProcess(ReadExecutor), copy inFrom(), emit inSave(). Keep the source-scan guard intact.Files:
Phantom.Workspaces.Llm.Interfaces/PhantomMcpTool.cs;Phantom.Workspaces.Llm.Interfaces/PhantomAgentSchema.cs.Tests:
PhantomMcpToolExecutorTests(Save_WithExecutor_EmitsExecutorField,From_CopiesExecutor,RoundTrip_ExecutorField_Preserved, guard test).Dependencies: none.
Commit 4 ΓÇö Executor-resource resolver (returns a connection-descriptor) ΓÇö #1436
Scope: Add
ExecutorResourceResolvermapping anExecutorResource(+ resolvedparameter-values+ trust context) to a transport connection-descriptor (JsonElement) for all fiveidstrategies (local,parameter,user-computer-profile-entity,trust-profile,connection-descriptor), with clear errors for unknown/unresolved. This is the reuse-first fix: the resolver returns the connection-descriptor thatITransportFactoryRegistry.ConnectToAsyncalready dispatches on (NOT a flat string, NOT anExecutorDescriptor), DELEGATING to the existingLlm.Trust.ExecutionTargetResolver.ResolveDescriptorfor the local/profile shapes. Theparameterstrategy reads the typedexecutorselection (M7) fromparameter-selections(notparameter-values). Runs from a distinct pre-pass, notIToolResourceFactory(M5). ItsResolveComponentoutput populatesRuntimeContextProviderRegistration.ConnectionDescriptor(M1). AddExecutorBindings(session executor default{"type":"local"}+ name→connection-descriptor +ResolveComponent+ToTopology+ToPersistableMap).Files:
Phantom.Workspaces.Llm.Core/Manifest/ExecutorResourceResolver.cs,Phantom.Workspaces.Llm.Core/Manifest/ExecutorBindings.cs(new).Tests:
ExecutorResourceResolverTests(incl.Resolve_ConnectionDescriptorId_ReturnsInlineDescriptorVerbatim,Resolve_ParameterValue_JsonEncodedString_IsParsed),ExecutorBindingsTests,ExecutorResourcePrePassTests.Dependencies: Commit 1 (
ExecutorResource); Commit 2 (for theparameterstrategy / typedparameter-selectionsvalue).Commit 5 ΓÇö Explicit session executor +
executor-bindingspersistence + resume ΓÇö #1437Scope: Make the session's overall executor explicit (default
{"type":"local"}). Addexecutor-bindings(root shape{ "session": <descriptor>, "components": { name→connection-descriptor } }) and a new typedparameter-selectionsroot key (string→JsonElement) toagent-session.json. On build, write bindings alongside the existingparameter-values/host-profile-entity-id(AgentSessionEntityFactory.cs:39-93); on resume, rebuildExecutorTopologyand set the deferred selector's topology from the bindings (deriving the client-instance strings forlocal/user-computer-profileshapes). M6 back-compat: ifexecutor-bindings.sessionis absent buthost-profile-entity-idis present, deriveSessionExecutor = {"type":"user-computer-profile","entity-id":<id>}; bindings are the source of truth. Theexecutorselection persists in a new typedparameter-selectionsroot key (string→JsonElement), a sibling of the unchangedstring→stringparameter-values(M7 — no dictionary widening).Files:
Phantom.Workspaces.Data.Core/JsonSchemas/agent-session.json;Phantom.Workspaces.Data.Core/AgentSessionEntityFactory.cs; the session build/resume path that constructsExecutorTopologyand callsDeferredTrustedExecutorSelector.SetTopology.Tests:
AgentSessionExecutorBindingsTests(incl.Persist_DescriptorObjects_RoundTrips,Persist_SessionAndComponentsKeys_RoundTrip,Resume_RebuildsTopologyFromBindings,Resume_LegacyHostProfileOnly_DerivesSessionExecutor).Dependencies: Commit 4.
Commit 6 ΓÇö Per-tool MCP execution over transport + production remote MCP host ΓÇö #1438
Scope: Thread the resolved connection-descriptor + a production
ExecutorTargetRouterinto eachMcpToolContextProvider(constructed inAgentChat). When the bound descriptor is non-local, feed it straight into the router →ITransportFactoryRegistry.ConnectToAsync(no string hop), opening anMcpClientOverTransport— bridged to the MCP SDK client through the NEWMcpChannelClientTransport(IClientTransportoverIMessageChannel, M2); when local, keep the in-process path (no round-trip). Nesting is host-outer/target-inner. Add the productionRemoteMcpHostHandler(openConnectionAsync, steps a–e) and register it on a productionMcpTransportListenerinWorkspacesTransportComposition(M3). This makesExecutorTargetRoutera production consumer (G8) and provides the arbitrary stdio/HTTP MCP host (G9). The remote host handler CONSUMES #1439's scoped resolver (machine prefix first) — a two-way touchpoint documented in both issues; dependency direction unchanged.Files:
Phantom.Workspaces.Llm.Core/McpToolContextProvider.cs,Phantom.Workspaces.Llm.Core/AgentChat.cs,Phantom.Workspaces.Transport.Mcp/McpChannelClientTransport.cs(new),Phantom.Workspaces.Transport.Mcp/RemoteMcpHostHandler.cs(new),Phantom.Workspaces/Services/WorkspacesTransportComposition.cs.Tests:
McpToolContextProviderRoutingTests,McpChannelClientTransportTests,RemoteMcpHostHandlerTests,WorkspacesTransportCompositionTests,Scenario3_PerMcpServerRoutingTests.Dependencies: Commits 3, 4, 5. Touchpoint: #1439 (scoped resolver consumed by the host handler).
Commit 6B ΓÇö Model / chat-client executor binding (remote client, local router) ΓÇö #1443
Scope: (M4.) Honour a
model.options.executorbinding by transporting ONLYCopilotSdkChatClient's inner SDK session while the router (IChatClientdecorators) and theAIContextProvidersstay local — the structural inverse ofTransportTrustedExecutor.CreateAgentChatAsync(Transport/TransportTrustedExecutor.cs:43-67,:50/:54), which remotes the wholeAgentChat. When the model's resolved descriptor is{"type":"local"}, behaviour is unchanged (in-process CLI). When non-local,CopilotSdkChatClientobtains its SDK session over a transport (EnsureSessionAsync:1230→copilotClientFactory.Create:1277→CreateOrResumeSessionAsync:1324-1357) instead of in-process. A production client-only host path builds a localCopilotSdkChatClienton the remote (distinct fromChatClientTransportListener's whole-AgentChat build). Inserted as 6B so Commits 7/8/9 do NOT renumber.Files:
Phantom.Workspaces.Llm.Core/CopilotSdkChatClient.cs;Phantom.Workspaces.Llm.Core/AgentFactory.cs; newCopilotSessionOverTransportunderPhantom.Workspaces.Llm.Core/Transport/Chat/;Phantom.Workspaces/Services/WorkspacesTransportComposition.cs.Tests:
CopilotSdkChatClientExecutorTests,AgentChatExecutorBindingTests,WorkspacesTransportCompositionTests.Dependencies: Commit 4 (#1436), Commit 5 (#1437). Required by: Commit 9 (#1441).
Commit 7 ΓÇö MCP
mcp-server-entityresolution scoped to the bound executor ΓÇö #1439Scope: When a tool
idismcp-server-entity, evaluate the search prefixes (machine profile →${USER}/mcp-servers→defaults/mcp-servers) against the bound executor's profile/user context instead of the resolving instance's, constructing theMcpServerEntityToolResourceFactorysearchPrefixeswith the bound machine first. The remote MCP host handler from #1438 CONSUMES this scoped resolver (two-way touchpoint).Files: the
mcp-server-entityresolution code (Phantom.Workspaces/McpServerEntityToolResourceFactory.cs:21-113).Tests:
McpServerEntityBoundExecutorResolutionTests.Dependencies: Commit 6. Touchpoint: #1438 (its host handler consumes this resolver).
Commit 8 ΓÇö Launchpad
executorpicker UI ΓÇö #1440Scope: Rename the
AgentManifestParameterKindenum valueUserComputerProfile→Executorand add a combined picker in the Launchpad that lists bothtrust-profileentities anduser-computer-profileentities; the selection records the disambiguated value ({"trust-profile":...}or{"user-computer-profile":...}) as a typedJsonElementinparameter-selections(notparameter-values). It is no longer a user-computer-profile-only picker. Honour the manifest parameterkindfield.Files:
Phantom.Workspaces/ViewModels/AgentManifestParameterKind.cs,Phantom.Workspaces/ViewModels/AgentManifestParameterRowViewModel.cs,Phantom.Workspaces/ViewModels/AgentManifestLaunchpadViewModel.cs,Phantom.Workspaces/Templates/AgentManifestLaunchpadView.axaml(.cs).Tests:
AgentManifestLaunchpadViewModelTests.Dependencies: Commit 2.
Commit 9 ΓÇö Default split-executor Copilot manifest + OAuth-local validation ΓÇö #1441
Scope: Add
defaults/agent-manifests/copilot-split-executorwith: onekind:"executor"resourceworker(idparameter→worker-profile); aworker-profileparameter (kindexecutor) + aworking-directoryparameter; a model withexecutor:"worker";workspace-gui/workspace-entitytools with noexecutor(inherit local); a GitHub web MCP tool with noexecutor(local, for OAuth). Add load-time validation that rejects/warns an OAuth-interactive MCP whoseexecutoris non-local. Cross-check the exact tool/model/connection JSON shapes againstfeatures/docs/examples/github-copilot-remote-chat.json. The model-remote behaviour depends on Commit 6B (#1443).Files:
Phantom.Workspaces.Data.Core/JsonEntities/agent-manifests/copilot-split-executor.json(new); the manifest/validation code that enforces the OAuth-local rule.Tests:
CopilotSplitExecutorManifestTests,SplitExecutorIntegrationTests.Dependencies: Commits 1ΓÇô6, 6B.
Commit 10 ΓÇö LLM-readable executor authoring guide ΓÇö #1442
Scope: Add a single, self-contained, machine-consumable authoring reference (
Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-manifest-executors.md) so an LLM can reliably author a valid executor-bound manifest or reason about a session: thekind:"executor"resource + fiveidstrategies, theexecutorreference on tools and themodel.options.executorreference on the model, thekind:"executor"launch parameter + disambiguated recorded value, the reused connection-descriptortypes + host-outer/target-inner nesting, the persistedexecutor-bindingssession shape, two worked end-to-end examples (split-executor topology from #1441; trivial all-local baseline), and OAuth-local guidance. Every fenced JSON block is a real, parseable example guarded by tests. Complements #1441 by documenting the GENERAL authoring rules.Files:
Phantom.Workspaces.Data.Core/JsonEntities/documentation/agent-manifest-executors.md(new); cross-links inagent-options-parameters.md; schema-comment alignment inagent-manifest.json.Tests:
ExecutorAuthoringGuideTests(AuthoringGuide_EmbeddedManifestExamples_ParseAndRoundTrip,AuthoringGuide_EmbeddedSessionExamples_ResolveToDescriptors,AuthoringGuide_DocumentsAllFiveIdStrategies,AuthoringGuide_ExecutorParameterValueShapes_Documented).Dependencies: Commits 1 (#1433), 2 (#1434), 3 (#1435), 5 (#1437); complements Commit 9 (#1441).
Extensibility ΓÇö non-persistent executors
Explicitly OUT OF SCOPE and FUTURE WORK ΓÇö there is no container implementation commit and no container child issue in this design. This is documentation only. It records how the reuse-first connection-descriptor model extends to non-persistent runtimes (an ephemeral container is the worked example) without any manifest or session schema change, because an
ExecutorResourcealready supports the rawconnection-descriptorstrategy and the transport layer already dispatches bytype.Phantom.Workspaces.Containersalready definesContainerDefinition+container-definition.jsonandContainerEngine(CreateAsync/PullAsync/StartAsync/StopAsync/DestroyAsync/UsableAsync) with Docker Desktop / containerd implementations (already used in production byMongoDbConnectionBroker). A future container executor would drive this subsystem ΓÇö it does not invent a new container abstraction, and no container manifest/session schema is added.connection-descriptorstrategy as a host-outer descriptor with an inner containertarget:{"type":"user-computer-profile","entity-id":"<worker>","target":{"type":"container","container-definition":<ContainerDefinition>}}. The OUTER descriptor reaches the host; the INNERtargetruns there (threaded through the existingTargetedTransportseam). BecauseExecutorResourcealready carries an inline connection-descriptor, no new manifest field is needed.targethandler ΓÇö analogous to the MCP host handler seam ΓÇö drivingContainerEnginecreate/start/attach andDestroyAsyncon teardown, registered as anITransportFactory/listener. Trust inherits the resolved host executor. Open question (future): what runs INSIDE the container (a full agent-executor endpoint vs. just the pinned MCP server) ΓÇö the design does not answer this; it only guarantees the connection-descriptor model can extend here with no manifest/session schema change.Sub-issues (the 11 children ΓÇö dependency order)
executorparameter kind #1434 ΓÇöexecutorparameter kind ΓÇö deps: nonePhantomMcpTool.Executorfield #1435 ΓÇöPhantomMcpTool.Executorfield ΓÇö deps: noneexecutorparameter kind #1434executor-bindingspersistence + resume #1437 ΓÇö Explicit session executor +executor-bindingspersistence + resume ΓÇö deps: [per-component-executor] - Executor-resource resolver #1436PhantomMcpTool.Executorfield #1435, [per-component-executor] - Executor-resource resolver #1436, [per-component-executor] - Explicit session executor +executor-bindingspersistence + resume #1437; touchpoint: [per-component-executor] - MCPmcp-server-entityresolution scoped to the bound executor #1439mcp-server-entityresolution scoped to the bound executor #1439 ΓÇö MCPmcp-server-entityresolution scoped to the bound executor ΓÇö deps: [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438; touchpoint: [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438executorpicker UI #1440 ΓÇö Launchpadexecutorpicker UI ΓÇö deps: [per-component-executor] -executorparameter kind #1434executorparameter kind #1434, [per-component-executor] -PhantomMcpTool.Executorfield #1435, [per-component-executor] - Executor-resource resolver #1436, [per-component-executor] - Explicit session executor +executor-bindingspersistence + resume #1437, [per-component-executor] - Per-tool MCP execution over transport + production remote MCP host #1438, [per-component-executor] - Bind the Copilot SDK chat client (model) to its executor (remote client, local router) #1443executorparameter kind #1434, [per-component-executor] -PhantomMcpTool.Executorfield #1435, [per-component-executor] - Explicit session executor +executor-bindingspersistence + resume #1437 (complements [per-component-executor] - Default split-executor Copilot manifest + OAuth-local validation #1441)executor-bindingspersistence + resume #1437; required by [per-component-executor] - Default split-executor Copilot manifest + OAuth-local validation #1441