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
21 changes: 21 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# UAM terminology

This glossary distinguishes the process that UAM manages from the conversation
state owned by an agent provider.

| Term | Meaning |
|---|---|
| **Managed Session** | One persistent UAM record and its detached terminal host. It has a UAM ID, provider, name, workspace, prompt metadata, and lifecycle state. |
| **Provider Conversation** | Conversation state owned by Claude Code, Codex, Copilot, Oh My Pi, OpenCode, or another provider. A provider conversation may have its own identifier and resume rules. It is not the same object as a Managed Session. |
| **Attach** | Connect the current terminal to an already-running Managed Session. The attach operation itself does not start or resume a provider process, although the `uam attach` command first resumes a selected Stopped session when supported. Detaching leaves the running process in place. |
| **Resume** | Start a stopped Managed Session's provider process again, preserving the UAM identity and asking the provider to continue an earlier Provider Conversation. |
| **Workspace** | The working directory shared by a Managed Session and its provider process. Multiple Managed Sessions may use the same Workspace and therefore edit the same files. UAM does not create a worktree or other filesystem isolation automatically. |
| **Explicitly Stopped** | A Managed Session whose provider process was stopped through UAM. This is retained as reason metadata; it still appears in the **Stopped** lifecycle group. |
| **Running** | The Managed Session's provider process is alive. This is based on process liveness, not interpretation of terminal text. |
| **Stopped** | The provider process is not alive. The session record remains available and may be resumable. A clean exit, explicit stop, signal, or nonzero exit all belong to this group; exit detail distinguishes failures where known. |
| **Exact resume** | Resume targets a known Provider Conversation or a provider state directory dedicated to the Managed Session. Other sessions in the same Workspace cannot change the target. |
| **Heuristic resume** | The provider can only continue its most recent conversation or equivalent. When several retained sessions for that provider share a Workspace, UAM requires explicit confirmation because the target cannot be proven. |

See [Managed Session vs. Provider Conversation](docs/adr/0001-managed-session-vs-provider-conversation.md)
for the decision behind these definitions and [Responsive TUI operations](docs/responsive-tui.md)
for day-to-day controls.
110 changes: 96 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Supported providers:
- Runs each managed session under its own lightweight, detached host process
(a PTY + terminal emulator + Unix socket) — sessions keep running when the
TUI exits, exactly like a tmux server, with no external dependency
- Shows active and closed sessions in one dashboard
- Shows Running and Stopped sessions in one dashboard, with grounded exit detail
- Lets you peek at recent output without attaching (4000 lines of scrollback)
- Sends replies back into running agent sessions
- Persists session metadata across restarts, including each agent's exit code
Expand All @@ -50,7 +50,8 @@ from the dispatch UI instead of failing the whole app.

- Linux (Ubuntu), on amd64 and arm64
- macOS, on Intel and Apple silicon
- Native Windows is not supported.
- Native Windows is not supported. Windows Terminal and PowerShell can be used
only as SSH clients connecting to a Linux or macOS host running `uam`.

## Install

Expand Down Expand Up @@ -96,10 +97,10 @@ uam new # guided dispatch wizard, then attach
uam dispatch [--safe] [--alias <name>] <agent> [#session-name] [prompt]
uam ls [--json]
uam peek <id>
uam attach <name-or-id>
uam attach [--allow-latest] <name-or-id>
uam last
uam stop <id> # kill the session, keep record
uam restart <id> # stop the agent and resume it in place
uam restart [--allow-latest] <id> # stop the agent and resume it in place
uam rm <id> # kill the session and remove record
uam kill-all # stop every managed session
uam version
Expand All @@ -115,16 +116,22 @@ uam version
| `@agent prompt` | Dispatch to a specific agent |
| `@agent:alias prompt` | Dispatch with a command alias |
| `Tab` | Cycle default agent |
| `Space` | Toggle peek panel |
| `Space` | Toggle Peek for Running; resume Stopped in the background |
| `Ctrl+T` | Pin selected session |
| `Ctrl+R` | Rename selected session |
| `Ctrl+X` | Stop, restart, or remove the selected session with confirmation |
| `Ctrl+X` | Stop and remove the selected record, or restart it, with confirmation |
| `Ctrl+S` | Toggle group-by-directory |
| `Shift+↑/↓` | Reorder rows |
| `e` | Open the guided dispatch wizard |
| `?` | Open help |
| `Esc` | Close overlays, clear input, or quit |

The dashboard responds to every terminal resize. Wide terminals show sessions
beside selected details or Peek; standard terminals stack those surfaces; and
compact or keyboard-constrained mobile terminals give Peek and the New Session
wizard the full primary surface. See [Responsive TUI design and operations](docs/responsive-tui.md)
for layout thresholds, mobile guidance, lifecycle markers, and accessibility.

## Attached sessions

`uam attach` (or `Enter` in the TUI) bridges your terminal straight to the
Expand All @@ -144,26 +151,86 @@ agent's PTY:
session would leave it impossible to foreground
- Several terminals can attach to the same session at once

`UAM_ATTACH_MOUSE` controls whether provider mouse reporting is preserved:

- `auto` (the default) enables it locally and disables it when `SSH_CONNECTION`
or `SSH_TTY` indicates an SSH session
- `on` preserves provider mouse reporting everywhere
- `off` suppresses provider mouse modes so the terminal keeps selection and
paste gestures

Bracketed-paste payload is forwarded byte-for-byte. Control bytes inside a paste
do not trigger UAM's attach shortcuts. UAM cannot access the client clipboard or
turn an unsent mouse gesture into remote input.

For PowerShell SSH, use Windows Terminal and configure a Windows Terminal paste
binding such as right-click, `Ctrl+Shift+V`, or `Shift+Insert`. Keep
`UAM_ATTACH_MOUSE=auto` (or set it to `off`) on the remote host if provider mouse
tracking captures selection or right-click. Native Windows remains unsupported;
Windows is the SSH client in this setup.

In the TUI, `Ctrl+X` followed by `y` stops the process **and removes its stored
record**. Use `uam stop <id>` when you want to stop the process but retain a
Stopped row for later resume. For paste diagnosis, follow the
[SSH troubleshooting steps](docs/responsive-tui.md#ssh-mouse-and-paste).

## Resuming sessions

Detach/reattach never restarts anything — the agent keeps running under its
host and attach is a plain reconnect (this is the tmux property, kept).
Resume only applies to sessions whose process is gone (reboot, `uam stop`):
Detach/reattach never restarts anything — the provider keeps running under its
host and attach is a plain reconnect. Resume applies only when the provider
process is Stopped, such as after a reboot, clean exit, or `uam stop`.

UAM distinguishes an **exact resume**, which targets known provider state, from
a **heuristic resume**, which asks the provider to continue its latest
conversation. If a heuristic provider has multiple retained sessions in the
same workspace, UAM fails closed before launching it. The TUI asks for explicit
confirmation; CLI users may retry only when latest-conversation behavior is
acceptable:

```sh
uam attach --allow-latest <name-or-id>
uam restart --allow-latest <id>
```

Provider behavior:

- **Claude Code**: uam seeds claude's session id with the uam id at dispatch
(`--session-id`, when the installed claude supports it) and resumes that
exact conversation with `--resume <id>` — several sessions in the same
directory each resume their own conversation. Sessions dispatched before
this feature (or with an older claude) fall back to `--continue`.
this feature (or with an older Claude Code) use the guarded `--continue`
heuristic.
- **Copilot**: exact resume — the session is named with the uam id at
dispatch (`--name`) and resumed by that exact name (`--resume=<id>`).
- **Codex / OpenCode**: these CLIs cannot preset session ids yet, so resume
uses their "most recent" mode (`codex resume --last`, `opencode -c`). When
an opencode record carries a `provider_session_id` (`ses_...`), uam resumes
that exact session via `--session`. **omp**: `-c`.
- **OpenCode**: UAM learns the current root conversation ID and resumes it
exactly with `--session`. Older records without an identity use guarded
`-c` latest-conversation behavior.
- **Oh My Pi**: new sessions receive a UAM-ID-specific provider state directory,
making `-c` exact. Legacy records without that directory retain guarded
latest-conversation behavior.
- **Codex**: `codex resume --last` is heuristic because Codex cannot currently
be given the UAM ID when the conversation is created.
- **Hermes**: no provider resume command is configured. A stopped Hermes record
cannot be resumed; dispatch a new Managed Session instead.
- After a reboot, records survive in the store and resume on attach — a
scenario where a tmux session would simply be gone.

### Multiple sessions in one workspace

UAM can run several Managed Sessions in one project directory. They have
independent UAM IDs, terminal hosts, attach points, and provider conversations,
but they share the same files. The grouped TUI warns when more than one Running
session shares a Workspace. Use separate Git worktrees or checkouts when agents
must not edit the same tree; UAM does not create them automatically.

OpenCode's `/new` creates a new provider conversation *inside the current
Managed Session*. It intentionally does not create another UAM row or host. UAM
tracks the newly selected root conversation for later exact resume. To get two
independently attachable sessions, use `uam new` or `uam dispatch` again.

The terminology and compatibility decision are documented in
[Managed Session vs. Provider Conversation](docs/adr/0001-managed-session-vs-provider-conversation.md).

## Session storage

`uam` stores session metadata at:
Expand Down Expand Up @@ -203,10 +270,25 @@ Use `uam dispatch --safe ...` when you want the provider's default approval
behavior instead. Safe mode changes provider arguments; it is not an operating-
system sandbox and does not reduce the permissions of the `uam` process itself.

OpenCode is capability-probed before launch. In default yolo mode, UAM adds
`--auto` only when the installed OpenCode advertises that flag; older versions
continue without an unsupported argument. This preserves compatibility but does
not turn OpenCode or UAM into a sandbox.

`uam` does not make git checkpoints, stash changes, or modify your repository on
its own. It starts and manages agent sessions; the provider remains responsible
for its own execution model.

## Design and operations

- [Terminology glossary](CONTEXT.md)
- [Managed Session vs. Provider Conversation](docs/adr/0001-managed-session-vs-provider-conversation.md)
- [Terminal ownership over SSH](docs/adr/0002-terminal-ownership-over-ssh.md)
- [Responsive TUI design and operations](docs/responsive-tui.md)

Session storage remains schema version 3. Updates do not require a data migration,
and unknown JSON fields are preserved when existing records are rewritten.

## Development

```sh
Expand Down
121 changes: 121 additions & 0 deletions docs/adr/0001-managed-session-vs-provider-conversation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
# ADR 0001: Managed Session vs. Provider Conversation

- Status: Accepted
- Date: 2026-07-12

## Context

UAM controls a detached terminal host while each provider controls its own
conversation database. These lifecycles overlap but are not identical. Attaching
to a live terminal is deterministic; starting a stopped provider may be exact or
may rely on a provider's "continue latest" behavior.

Treating the two objects as one caused confusing outcomes when multiple retained
sessions used the same provider and working directory. In particular, an old
provider without an addressable conversation ID could resume a different recent
conversation even though the selected UAM row was unambiguous.

## Decision

UAM distinguishes a **Managed Session** from a **Provider Conversation**:

- A Managed Session is UAM's stable record and detached host identity. Its UAM
ID, session name, provider, workspace, display metadata, and persisted order
remain stable across a resume.
- A Provider Conversation is provider-owned state. UAM records a provider ID or
creates isolated provider state when the provider supports it.
- **Attach** reconnects to a running host and never invokes provider resume.
- **Resume** launches a provider for a stopped Managed Session. The launch is
classified as exact or heuristic before it occurs.

Multiple Managed Sessions may use one Workspace. This is useful for parallel
tasks, but every provider process sees and can edit the same filesystem. UAM
shows a warning when more than one running session shares a Workspace.

### Exact and heuristic resume

The current provider behavior is:

| Provider | Exact when | Heuristic fallback |
|---|---|---|
| Claude Code | UAM seeded and retained a provider session ID | `--continue` for older records or provider versions that could not seed an ID |
| GitHub Copilot CLI | Always for UAM-created records; the UAM ID is used as the provider name | None |
| OpenCode | The UAM identity integration learned a `ses_…` ID | `-c` continues OpenCode's latest conversation |
| Oh My Pi | The Managed Session has its dedicated provider state directory | Legacy records use `-c` without isolated state |
| OpenAI Codex | Not currently available | `resume --last` |
| Hermes Agent | Not currently available | Resume is unsupported; create a new Managed Session |

Other providers may declare exact, heuristic, or unsupported resume through the
same contract.

### Ambiguity guard

Before a heuristic resume, UAM counts retained sessions for the same provider
and normalized Workspace. If more than one exists, UAM stops before launching
the provider:

- CLI callers must retry `attach` or `restart` with `--allow-latest`.
- The TUI identifies the selected provider, session, and action and asks for
confirmation. Confirming permits the provider's latest-conversation behavior;
cancelling has no side effects.

This guard is intentionally conservative. A unique retained session can use the
provider fallback without another confirmation, but UAM still cannot prove
provider state that it does not own.

### OpenCode `/new`

OpenCode's `/new` command starts another Provider Conversation inside the same
running Managed Session. It does not create a second UAM row or a second
detached host. UAM's OpenCode identity integration follows the current root
conversation so a later exact resume targets the conversation last selected in
that Managed Session.

Use `uam new` or dispatch another session when two independently attachable UAM
sessions are required. Using the same Workspace is allowed, with the shared-file
risk described above.

### Workspace isolation

UAM does not automatically create Git worktrees, branches, stashes, or commits.
Automatic isolation would change repository topology and provider-visible paths,
and could silently alter established workflows. Users who need filesystem
isolation create a worktree or separate checkout first and dispatch each Managed
Session in the desired directory.

## Alternatives considered

### Always continue the provider's latest conversation

Rejected because selecting one UAM row could resume another conversation when
several records share a Workspace.

### Refuse all heuristic resume

Rejected because legacy records and providers without addressable IDs would
become unusable after a reboot or stop. Explicit confirmation preserves that
compatibility without pretending the target is exact.

### Create a worktree for every Managed Session

Rejected as a default because it changes filesystem and Git behavior, requires
branch-management policy, and does not suit non-Git workspaces. It remains a
user-controlled option.

### Treat provider `/new` as a new Managed Session

Rejected because the command occurs inside a provider-owned terminal protocol.
It does not create a new host and cannot provide an independently attachable UAM
session.

## Consequences

- Session selection is safe by default when resume would otherwise be
ambiguous.
- Exact resume improves as providers expose stable identifiers; legacy records
remain compatible.
- A Managed Session can outlive several provider-side conversation switches,
which is visible in the terminology but does not multiply dashboard rows.
- Users retain control over worktrees and other repository isolation.
- Schema version 3 remains unchanged. Provider identity fits the existing
record, and unknown fields continue to round-trip.
Loading
Loading