Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## 0.4.7-session-routing-dev (source)

- Added Linux `execution_context=auto|service|user|desktop|system` routing to `process_exec` and `process_spawn`.
- Added a desktop-session bridge that is launched by the graphical login session so GUI/Polkit/portal/keyring/clipboard children inherit the real login-session cgroup instead of `cycomagent@USER.service`; the socket checks same UID/PID credentials and the peer CyComAgent executable.
- Kept `system` execution behind `privileged=true`, central policy, and the existing Linux root broker.
- Added conservative auto-routing for known session-sensitive commands while leaving unknown/headless commands in the service context.
- Added XDG autostart packaging for the login-session bridge plus bridge status/capability reporting.
- Added durable external-job tracking for processes launched through the session bridge.
- Added bridge, routing, timeout, job-persistence, and peer/session regression coverage.

## 0.4.6-anyapp-dev (source)

- Added optional Linux `anyapp_*` tools backed by a persistent standalone MCP helper.
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
BINARY := cycomagent
VERSION ?= 0.4.6-anyapp-dev
VERSION ?= 0.4.7-session-routing-dev
GO ?= go
LDFLAGS := -s -w -X main.version=$(VERSION)
STAGE := .release/CyComAgent-MCP
Expand Down
16 changes: 12 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

CyComAgent-MCP is an AI-native computer runtime that exposes a real machine — and optional remote machines — as a compact set of composable MCP primitives. The model supplies the reasoning; CyComAgent supplies filesystem, process, persistent job, service, network, desktop, durable state, multi-machine target, policy, audit, plugin, and privileged-execution capabilities.

`v0.4.6-anyapp-dev` adds an optional Linux Any App bridge with native KDE Wayland window control, accessibility, screenshots, and targeted input. The helper runs independently of the ChatGPT application. Existing macOS, Termux, and Windows paths remain available. This is the source development version; the curl bootstrap continues to install the published `v0.4.5-macos-dev` release until new release assets are published.
`v0.4.7-session-routing-dev` adds Linux execution-context routing so GUI/session-sensitive commands can be born inside the active graphical login session instead of inheriting the CyComAgent system-service cgroup. The existing optional Linux Any App bridge, macOS, Termux, and Windows paths remain available. This is the source development version; the curl bootstrap continues to install the latest published release until new release assets are published.

## What “Full Power” means

Expand Down Expand Up @@ -65,19 +65,27 @@ Application state is explicit (`job_*`, `state_*`, `target_*`) rather than hidde

`process_exec`, `process_spawn`, `process_inspect`, `process_signal`

`process_exec` is the universal escape hatch and now accepts optional UTF-8 `stdin`. Commands run in a process group so timeout/cancellation kills the group instead of leaving descendants behind.
`process_exec` is the universal escape hatch and accepts optional UTF-8 `stdin`. Commands run in a process group so timeout/cancellation kills the group instead of leaving descendants behind. On Linux it also accepts `execution_context=auto|service|user|desktop|system`: `desktop`/`user` route through the active login-session bridge, `service` stays in the CyComAgent service, and `system` requires `privileged=true` so the existing policy/root-broker boundary remains authoritative. `auto` is conservative and only routes a small set of known session-sensitive commands automatically; callers should explicitly request `desktop` for arbitrary GUI programs.

### Persistent jobs — 5

`job_list`, `job_get`, `job_tail`, `job_signal`, `job_prune`

`process_spawn` persists job metadata/logs on disk. If the runtime dies while the process continues, a new runtime instance rediscovers it.
`process_spawn` persists job metadata/logs on disk. It accepts the same non-root execution contexts; desktop/user jobs are launched by the login-session bridge while the normal job store still owns their PID, log and signalling metadata. If the runtime dies while a process continues, a new runtime instance rediscovers the PID metadata.

### System / network / capability — 8

`system_info`, `system_env`, `service_control`, `network_request`, `network_tcp`, `network_resolve`, `capabilities_list`, `plugins_reload`

`capabilities_list` includes detected binaries, adapter availability/scores, policy, plugins, root broker, and targets.
`capabilities_list` includes detected binaries, adapter availability/scores, policy, plugins, root broker, targets, and desktop-session-bridge status.

### Linux desktop-session bridge

The system runtime intentionally remains a boot-time service, but GUI programs, Polkit actions, desktop portals, notifications, keyrings and compositor/clipboard commands sometimes must be *born* in the active graphical login session. Copying `DISPLAY`, `WAYLAND_DISPLAY` or the session D-Bus address into a system service does not change its logind/systemd session cgroup, so it is not sufficient for those cases. The bridge refuses to start unless its own cgroup contains a real `session-N.scope`, preventing a copied environment from being mistaken for session identity.

`cycomagent --session-bridge` is a small per-login-session Unix-socket helper. The system installer places an XDG autostart entry at `/etc/xdg/autostart/cycomagent-session-bridge.desktop`; desktop environments that honor XDG autostart *and preserve the login-session cgroup* can launch it automatically. If an environment delegates XDG autostart applications into `user@UID.service`, or on minimal compositors such as Labwc, start the same command from the compositor/session autostart file instead. The socket is private to the user and verifies the connecting UID/PID with `SO_PEERCRED`, then requires the peer executable to resolve to the same CyComAgent binary as the bridge.

When the bridge is absent, service/headless execution keeps working normally. Explicit `desktop`/`user` execution returns a clear unavailable error instead of silently falling back into the wrong cgroup.

### Desktop — 2

Expand Down
23 changes: 23 additions & 0 deletions RELEASE_NOTES_v0.4.7-session-routing-dev.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# v0.4.7-session-routing-dev - Linux Execution Context Routing

This source development update separates **where CyComAgent decides a command should execute** from the long-lived MCP service that requested it.

## What changed

- `process_exec` and `process_spawn` accept `execution_context=auto|service|user|desktop|system`.
- Linux `desktop` and `user` execution routes through `cycomagent --session-bridge`, a per-login-session helper started by the graphical desktop.
- Session-routed children inherit the real login-session cgroup and desktop environment, fixing operations whose authorization depends on active-session identity (for example Polkit power-profile switching).
- `system` execution still requires `privileged=true` and remains behind central policy plus the Linux root broker.
- `auto` stays conservative: it recognizes a small set of well-known session-sensitive commands and otherwise remains in the system-service execution context.
- Desktop/user background jobs remain visible through `job_*` using durable PID/log metadata.
- `system_info` and `capabilities_list` report the desktop-session bridge and its session/cgroup metadata.

## Bridge lifecycle

The systemd installer places `/etc/xdg/autostart/cycomagent-session-bridge.desktop`. Full desktop environments normally launch it on graphical login. Minimal compositors that do not consume XDG autostart entries should start `/usr/local/bin/cycomagent --session-bridge` from their compositor/session autostart.

The bridge refuses to start unless its own cgroup contains a real `session-N.scope`, so copying graphical environment variables into a service cannot impersonate a login session. Its socket is user-private (`0600`), validates the peer UID/PID with Linux `SO_PEERCRED`, and requires the peer executable to resolve to the same CyComAgent binary as the bridge. If no graphical bridge exists, normal service/headless execution is unaffected; explicit desktop/user requests fail clearly instead of silently launching in the wrong cgroup.

## Validation

The test suite covers synchronous session execution, environment/stdin forwarding, process-group timeout cleanup, background log handling, execution-context resolution, and durable external-job persistence. Linux, macOS and Android/Termux builds continue to share the portable core; the actual session bridge is Linux-only in this development version.
16 changes: 14 additions & 2 deletions cmd/cycomagent/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import (

"github.com/Cytech-Team/CyComAgent-MCP/internal/platform"
"github.com/Cytech-Team/CyComAgent-MCP/internal/runtime"
"github.com/Cytech-Team/CyComAgent-MCP/internal/sessionbridge"
)

var version = "0.4.6-anyapp-dev"
var version = "0.4.7-session-routing-dev"

func main() {
mode := flag.String("mode", envOr("CYCOM_MODE", "http"), "transport: http or stdio")
Expand All @@ -28,12 +29,23 @@ func main() {
token := flag.String("token", envOr("CYCOM_TOKEN", ""), "optional bearer/X-CyCom-Token for HTTP MCP")
strict := flag.Bool("strict-mcp", runtime.EnvBool("CYCOM_STRICT_MCP", false), "strictly validate modern MCP routing/version headers")
allowRemote := flag.Bool("allow-unauthenticated-remote", runtime.EnvBool("CYCOM_ALLOW_UNAUTHENTICATED_REMOTE", false), "allow non-loopback HTTP bind without CYCOM_TOKEN")
sessionBridge := flag.Bool("session-bridge", false, "run the per-login-session execution bridge instead of the MCP runtime")
sessionSocket := flag.String("session-socket", envOr("CYCOM_SESSION_SOCKET", sessionbridge.DefaultSocketPath()), "desktop session bridge unix socket")
showVer := flag.Bool("version", false, "print version and exit")
flag.Parse()
if *showVer {
fmt.Println(version)
return
}
if *sessionBridge {
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
log.Printf("CyComAgent desktop session bridge listening on %s", *sessionSocket)
if err := sessionbridge.Run(ctx, *sessionSocket); err != nil {
log.Fatal(err)
}
return
}
if *pluginDir == "" {
*pluginDir = filepath.Join(*stateDir, "plugins.d")
}
Expand All @@ -46,7 +58,7 @@ func main() {
} else if goruntime.GOOS == "darwin" {
instructions = "CyComAgent is running in experimental macOS mode. The portable Go core is available; launchd, screencapture and AppleScript/cliclick adapters may require normal macOS TCC permissions. Local privileged execution is intentionally unavailable until a native macOS privilege broker exists."
} else if goruntime.GOOS == "linux" {
instructions = "CyComAgent is running in Linux computer_runtime mode. When anyapp_* tools are available, prefer anyapp_get_app_state plus semantic/window-targeted anyapp actions for GUI work; use desktop_capture/desktop_input as generic fallback tools. Use structured filesystem/process/service/network tools for non-GUI operations."
instructions = "CyComAgent is running in Linux computer_runtime mode. When anyapp_* tools are available, prefer anyapp_get_app_state plus semantic/window-targeted anyapp actions for GUI work; use desktop_capture/desktop_input as generic fallback tools. For process_exec/process_spawn, use execution_context=desktop when launching GUI apps or commands that need the active login session (Polkit, notifications, portals, keyrings, clipboard/compositor access); use service for daemon/headless work. Use structured filesystem/process/service/network tools for non-GUI operations."
}
rt, err := runtime.New(runtime.Config{Version: version, StateDir: *stateDir, PluginDir: *pluginDir, RootSocket: *rootSocket, StrictMCP: *strict, Instructions: instructions})
if err != nil {
Expand Down
19 changes: 19 additions & 0 deletions docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,25 @@ OS / OpenSSH / external binaries / privilege broker

Policy and audit are registry-wide rather than being reimplemented by each tool.

## Execution-context routing

The Linux MCP runtime remains a system service for boot-time availability and recovery, but not every child process belongs in that service cgroup. Session-sensitive commands can be routed through a small bridge that is itself started by the graphical login session:

```text
cycomagent@USER.service
|
| Unix socket, same UID only
v
cycomagent --session-bridge
(user.slice/.../session-N.scope)
|
+--> GUI app / Polkit action / portal / notification / keyring
```

`process_exec` and `process_spawn` expose `execution_context` with `auto`, `service`, `user`, `desktop`, and `system`. The `desktop` and current Linux `user` paths use the login-session bridge; `service` preserves the existing runtime behavior; and `system` requires `privileged=true` and therefore still passes through policy plus the root broker. `auto` only recognizes a conservative set of well-known session-sensitive commands and otherwise stays in `service`.

The bridge inherits the real graphical environment and, more importantly, the real logind/systemd session cgroup. It does not attempt to manufacture desktop identity by copying environment variables, and startup is rejected when the bridge's own cgroup has no `session-N.scope`. Its Unix socket is mode `0600`; the bridge verifies peer UID/PID with `SO_PEERCRED` and requires the peer executable to resolve to the same CyComAgent binary before accepting a request.

## Capability-driven adapters

`capabilities_list` reports candidate adapters and availability/score. The model can choose among generic capabilities based on the actual machine rather than assuming one OS/toolchain.
Expand Down
5 changes: 4 additions & 1 deletion docs/STABILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ The engineering goal is stronger and measurable:
| tunnel-client restart | CyComAgent remains alive |
| CyComAgent crash | systemd restarts runtime |
| root broker crash | systemd restarts broker; non-root tools remain usable |
| desktop-session bridge absent/crashed | service/headless execution remains usable; explicit desktop/user execution fails clearly until the login-session bridge returns |
| graphical logout/login | old bridge disappears with the login session; the next desktop autostart creates the new session bridge/socket |
| temporary network outage | local runtime remains ready |
| reboot | runtime/root/tunnel services start without GUI login |
| long job + MCP reconnect | job remains addressable by ID |
Expand Down Expand Up @@ -43,7 +45,8 @@ A release intended for unattended deployment should pass:
5. temporary network loss and return;
6. concurrent file/process tool calls;
7. command timeouts and large output caps;
8. persistent job recovery.
8. persistent job recovery;
9. desktop-session bridge routing verifies child cgroup/session identity and preserves Polkit-visible active-session behavior.

`scripts/chaos-test.sh` automates the process-kill subset.

Expand Down
Loading
Loading