Metadata-Version: 2.5
Name: ironsilk
Version: 0.9.61
Summary: Tide runtime for domain/workspace context management (published under the ironsilk codename)
License: MIT
Keywords: context,domain,hermes,ironsilk,miadi,navigator,runtime,tide,tmux,workspace
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Requires-Python: >=3.11
Requires-Dist: aiosqlite>=0.20
Requires-Dist: click>=8.0
Requires-Dist: pyyaml>=6.0
Requires-Dist: rich>=13.0
Provides-Extra: controller
Requires-Dist: libtmux>=0.28; extra == 'controller'
Provides-Extra: daemon
Requires-Dist: watchdog>=4.0; extra == 'daemon'
Provides-Extra: dev
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Provides-Extra: studio
Requires-Dist: textual>=0.50; extra == 'studio'
Provides-Extra: telegram
Requires-Dist: httpx>=0.27; extra == 'telegram'
Requires-Dist: python-telegram-bot>=21.0; extra == 'telegram'
Description-Content-Type: text/markdown

# ironsilk — Domain/Workspace Context Management
<!-- Ref: jgwill/Miadi#265; jgwill/Miadi#401 -->

> **Naming note** — This package is published on PyPI as `ironsilk`
> (https://pypi.org/project/ironsilk/). Its command is `tide`, and its Python
> import path is `tide_runtime`. The naming decision is tracked in jgwill/Miadi#359.
>
> **State-root note** — `~/.miadi/navigator` is the canonical default runtime
> state root. `MIADI_HOME` is the preferred override. Workspace/project config
> lives at `.miadi/tide.yaml`.

### Naming Migration Debt

| Identifier | Classification | Decision Surface |
| --- | --- | --- |
| `runtime/tide-runtime` | Current contract | repository path/package layout |
| `ironsilk` | Current release name | PyPI distribution name |
| `tide` CLI | Current contract | canonical console entry point |
| `tide_runtime` | Migration debt | Python import/module path |
| `MIADI_HOME` | Current contract | preferred state-root env var |
| `~/.miadi/navigator` | Current contract | default Navigator state root |
| `.miadi/tide.yaml` | Current contract | workspace/project config filename |
| `tide-runtime.service` | Migration debt | user-service unit name |


## description
`ironsilk` currently ships the Tide Runtime implementation behind
the `tide` CLI. It gives William and local agents instant
orientation across active development domains and many terminal surfaces.

## What It Does

- **R1 — Domain Detection:** Auto-detects which domain each terminal/tmux pane belongs to, using a priority chain of signals (env var → tmux name → git remote → path prefix → heuristic)
- **R4 — Terminal Focus:** Lists or switches tmux focus to a domain's terminal group
- **R2 — Context Engine:** Always-on background collector that builds a queryable snapshot of all terminal activity, scoped by domain
- **R5 — Approval Queue:** Proposed terminal actions flow through a review queue before execution
- **R7 — Session Store:** Named persistent sessions resumable from desktop or Telegram (`/resume <name>`)
- **R8 — Skill Interface:** Packaged as a Tide skill, callable from agent workflows
- **Operator cockpit:** lists, peeks and steers tmux and herdr panes. Every send passes a steer gate (live target, controller posture, interactive terminal)
- **Agent continuity:** names the agent in each tmux pane and brings the agents back after a tmux restore (`tide agents list`, `tide agents restore`, 0.9.35+)

**Issue:** [jgwill/Miadi#265](https://github.com/jgwill/Miadi/issues/265)

The TypeScript side of the same runtime (`@miadi/tide`, `@miadi/tide-contract` and
Miadi's `/tide` cockpit) is described in
[How tide helps the work](https://github.com/jgwill/Miadi/blob/main/runtime/tide-runtime/docs/how-tide-helps-the-work.md).

---

## Quick Start

```bash
# Install
cd runtime/tide-runtime
pip install -e .

# Configure domains
mkdir -p ~/.miadi/navigator
cp examples/domains.yml ~/.miadi/navigator/domains.yml
# Edit ~/.miadi/navigator/domains.yml to match your domain roots

# Detect domain for current directory
tide detect

# Detect for a specific path
tide detect --cwd ~/src/Miadi

# List terminals for a domain
tide focus miadi

# Show context snapshot
tide context

# Start background daemon
tide daemon

# Install/start as a user service
tide service install --interval 15 --now

# Release/install smoke gate
tide doctor --release-target 0.9.36

# Which agent runs, or ran, in each tmux pane
tide agents list

# Operator cockpit: know/enter existing tmux work
tide operator terminals
tide operator peek 7 --lines 80
tide operator attach 7
tide operator registry save
tide operator registry last-known
tide operator registry drift
tide operator checkpoint
tide operator checkpoint --write
tide operator steer-proposal 7 --message "continue from the visible prompt"
tide session new ava-260512 --cwd /usr/local/src/ironsilk
tide session attach ava-260512
```

## Fast Demo Path For This Branch

Want to try the workspace/context slice without touching your real
`~/.miadi/navigator` state?

```bash
cd runtime/tide-runtime
pip install -e .
./examples/demo-miadi-workspace.sh /usr/local/src/mightyeagle
```

What this does:
- sets `MIADI_HOME` to an isolated temp directory
- writes a one-domain config pointed at the repo you pass in
- captures a persisted context snapshot when the demo scene is frozen
- runs `detect`, `ecf observe`, `scene activate`, `scene list`, `scene snapshot`, and `scene resume`
- leaves the generated SQLite/files behind so you can inspect them after the run

You can also point the runtime at any alternate state directory yourself:

```bash
export MIADI_HOME=/tmp/tide-demo
tide detect --cwd /usr/local/src/mightyeagle
```

Preferred override for new setups:

```bash
export MIADI_HOME=/tmp/tide-demo
tide detect --cwd /usr/local/src/mightyeagle
```

---

## Configuration

### `~/.miadi/navigator/domains.yml` — Domain Registry

```yaml
domains:
  - name: miadi
    aliases: [main, mia]
    roots:
      - ~/src/Miadi
      - ~/work/Miadi
    remotes:
      - "github.com/jgwill/Miadi"
    env_var: "MIADI_DOMAIN"
    tmux_pattern: "miadi-*"

  - name: aureon
    aliases: [aur]
    roots:
      - ~/src/aureon
    remotes:
      - "github.com/jgwill/aureon"
    tmux_pattern: "aureon-*"
```

See `examples/domains.yml` for a full example.

### `~/.miadi/navigator/config.yml` — Navigator Config

```yaml
mode: observer           # observer | controller
daemon_interval: 5.0     # seconds between context polls
session_db: ~/.miadi/navigator/sessions.db
context_log: ~/.miadi/navigator/context/log.ndjson
domains_config: ~/.miadi/navigator/domains.yml
```

See `examples/config.yml` for full options.

---

## Commands

The examples below use the canonical `tide` CLI.

### Detection

```bash
# Detect domain for current directory
tide detect

# Detect for specific cwd
tide detect --cwd ~/src/Miadi

# Detect for specific tmux pane
tide detect --pane %42

# Detect all panes in current tmux session
tide detect --all-panes
```

### Focus (R4)

```bash
# List terminals for a domain (observer mode)
tide focus miadi

# List matching desktop terminal windows (Terminator/Konsole/etc.)
tide focus miadi --desktop
tide focus miadi --desktop --format json

# Activate a matching desktop window (controller mode + approval required)
tide mode controller
tide focus miadi --desktop --activate
tide focus miadi --desktop --activate --window-id 0x00eaf0d8

# Activate tmux focus to domain pane group (controller mode required)
tide mode controller
tide focus miadi
```

### Operator Cockpit

These commands are the first "avoid context rot" surface. They are deliberately
small: list the live tmux targets, inspect recent scrollback without attaching,
and print the exact attach/switch commands before changing focus.

```bash
# List existing tmux sessions/panes and their attach commands
tide operator terminals
tide operator terminals --format json

# Inspect what is happening in a session or pane without attaching
tide operator peek 7 --lines 80
tide operator peek %7 --format json

# Print the commands for entering a target
tide operator attach 7

# Execute attach/switch from an interactive controller terminal
tide mode controller
tide operator attach 7 --run

# Persist terminal awareness as append-only JSONL
tide operator registry save
tide operator registry last-known
tide operator registry drift

# One read-only "know before entering" checkpoint
tide operator checkpoint
tide operator checkpoint --target 7 --peek-lines 20
tide operator checkpoint --format json
tide operator checkpoint --write
tide operator checkpoint --target 7 --peek-lines 20 --write

# Human-reviewable follow-up proposal; no send is performed
tide operator steer-proposal 7 --message "continue from the visible prompt"
tide operator steer-proposal 7 --message-file prompt.md --write
tide operator steer-proposal 7 --message "continue" --peek-lines 40
tide operator steer-proposal 7 --message "continue" --format json
```

The registry is a read-only awareness surface after capture: `save` appends a
timestamped snapshot under the Navigator state directory, `last-known` reads the
newest persisted snapshot, and `drift` compares current tmux state with that
snapshot. It stores observed tmux fields separately from inferred Navigator
fields such as domain and attach commands.

`operator checkpoint` combines current terminals, the last-known registry
metadata, drift summary, stale/last-seen hints, and safe entry commands. It does
not capture scrollback unless `--target` is provided.

Add `--write` to save a portable Markdown and JSON handoff under
`MIADI_HOME/operator/checkpoints/` (or the resolved Navigator state directory).
This serializes the same checkpoint report;
it does not attach, resume, or mutate terminals.

`operator steer-proposal` creates a reviewed-message artifact for a specific
target. It prints manual `tmux send-keys` suggestions only; Navigator does not
send input, attach, resume, or require controller mode.

`operator send` prints the guarded send for one target. With `--run` it types
the message, and only when the steer gate passes: the target is a live pane,
the mode is controller with an unexpired lease, and stdin is a terminal (or a
reviewed service set `TIDE_ALLOW_NON_INTERACTIVE_RUN=1`). `@miadi/tide` applies
the same three checks before its own sends.

### Agents (tmux restore)

```bash
# Which agent runs, or ran, in each pane of the running tmux server
tide agents list
tide agents list --json

# After tmux restored its panes: relaunch the agents tide last saw there
tide agents restore --dry-run --force   # print the plan, touch nothing
tide agents restore                     # what the tmux post-restore hook runs
```

The daemon records the agent in each pane every snapshot: its pid, session id,
command line and launch alias, read from the pane's process tree,
`~/.claude/sessions/<pid>.json` and the binding lines under
`/src/_sessiondata/data/terminal_bindings.jsonl`. `agents restore` reads the
last snapshot from before the running tmux server started. Agents that were
running are relaunched with their launch alias and `--resume <id>`, 10 seconds
apart, most recently active first, while 16 GiB of memory stays available.
Agents that had exited get their command typed without Enter. Claude and hermes
resume with `--resume <id>`, pi with `--session <id>`. It runs once per tmux server start
(jgwill/Miadi#691, jgwill/gaia#89).

### Drafts (unsent lines at a shell prompt)

```bash
tide drafts list            # each unsent draft: state, pane, age, length, where a mctgo draft goes
tide drafts list --json     # the words too
tide drafts observe         # read every shell prompt now (the daemon does it every 20 s)
tide drafts restore --dry-run
```

A line typed at a shell prompt and not sent, such as a long `mctgo <team> <session> '<primer>`
drafted in a `mctgo-<yyMMddHHmm>` session, lives only in the shell's memory. The daemon reads it
from the pane's screen every 20 seconds and keeps it in `<state root>/drafts/drafts.json` (0600).
A pane is in view when a client whose terminal has focus shows it (tmux `client_flags`
`focused`); on a server where no client ever reported focus, an attached session stands in. The
daemon sets the tmux hooks `client-focus-out`, `client-session-changed` and `client-detached`
(index 61) to touch `drafts/wake`, so it looks within a second of a person leaving a terminal.
Its states are typing, parked (idle 300 s or out of view), sent, cleared, abandoned, restored and
expired. Each change is one `tide.draft.<state>` line in `drafts/events.jsonl`, without the
words; a `mctgo` draft's events carry `labels` (paths, packages, repositories, Miadi addresses,
issues, team). A `mctgo` draft's words travel only in `tide.draft.versioned`, written when its
text differs from its last version and it parks or is sent (Episode 548, D1 and D2). The actor
is `MIADI_ACCOUNT_PERSON` once the account is bound (`~/.miadi/person.env`), else `user@host`. After tmux restarts, `tide agents restore` types each draft back at the prompt
of the pane at its address, without Enter, once the pane shows an empty prompt, and recreates
the session when the tmux save missed it. The prompt is `user@host:path$ ` with optional `(env)`
prefixes; set `TIDE_PROMPT_PATTERN` (a Python regex) for another.

### Clock (delayed resolution)

```bash
tide clock list          # every open delay, by date: waiting, due or owed, and whether announced
tide clock list --json
tide clock tick          # one tick now, as the daemon runs it every interval_seconds
```

The daemon reads `~/.miadi/navigator/clock.yml` at each tick. Without that file
the clock does nothing.

```yaml
lead_minutes: 60            # the event fires this long before a delay's date
interval_seconds: 60
stores:                     # coaia-narrative stores: steps with metadata.delayedResolution
  - /path/to/chart.jsonl
session_inventory:          # folders of session records with status "suspended"
  - ~/workspace/.mino/session-inventory
circle:
  url: http://127.0.0.1:3335
  token_env: MIADI_CLOCK_TOKEN   # the variable holding a credential with create_circles
witness:
  seat: mino-261019-inventory    # its inbox defaults to clock/inbox/<seat>.jsonl
```

A delay is announced once, when its date minus `lead_minutes` has passed. A date
already past is announced as `owed`. A delay that has ended is never announced. A chart
step and the suspended session it names count as one delay. The event `clock.delay_due`
goes to `audit/daemon.ndjson` and to the witness inbox. The clock then opens a one-time
talking circle with `POST /api/circles`, holding the delay's follow-up prompt. Without a
credential that carries `create_circles`, or when Miadi refuses it, the circle is written
to `clock/circles-owed.jsonl` with the request that opens it.

### Context (R2)

```bash
# Show full context snapshot
tide context

# Filter by domain
tide context --domain miadi

# JSON output
tide context --format json

# Context from last 30 minutes
tide context --since 30m
```

### Sessions (R7)

```bash
# Tmux-style named session plan (dry-run by default)
tide session new ava-260512 --cwd /usr/local/src/ironsilk
tide session attach ava-260512

# Execute from a local human-controlled terminal
tide mode controller
tide session new ava-260512 --cwd /usr/local/src/ironsilk --run
tide session attach ava-260512 --run

# Create a named session
tide session create aureon-refactor-2025

# Resume a session
tide session resume aureon-refactor-2025

# List all sessions
tide session list

# Archive a session
tide session archive aureon-refactor-2025
```

`session new` and `session attach` are tmux-backed verbs for the v0.7.0
Hermes-Agent CLI base. They print exact tmux commands by default; execution
requires `ACTIVE_CONTROLLER` mode and an interactive local terminal.

### Doctor / Release Gate

```bash
tide doctor
tide doctor --release-target 0.9.36 --strict
tide doctor --release-target 0.9.36 --require-daemon --strict
```

`doctor` checks package version, state/config shape, tmux availability, daemon
ping/context readiness, user service hints, and the operator/session CLI
surfaces needed before workspace registry work.

### Mode

```bash
# Check current mode
tide mode

# Switch to active controller (enables tmux actions)
tide mode controller

# Switch back to observer (safe default)
tide mode observer
```

### Tide Studio

```bash
tide studio --cwd /path/to/workspace
```

Tide Studio is the Textual workspace cockpit. The Explorer opens directories,
opens `.miadi/tide.yaml` project configs, and previews small UTF-8 text files.
File preview is read-only in observer mode. When the operator has explicitly
enabled controller mode, the file panel becomes editable and writes only after
Save or `Ctrl+S`.

### Daemon

```bash
# Start daemon (blocks; use & or a process manager)
tide daemon

# Start with custom poll interval
tide daemon --interval 10

# Scene launch opens a terminal/editor for a stored workspace. On systems with
# Terminator installed, Navigator prefers Terminator for new terminal windows.
tide scene activate /path/to/project --launch

# Daemon writes to:
#   ~/.miadi/navigator/context/latest.json    (last snapshot)
#   ~/.miadi/navigator/context/log.ndjson     (rolling log)
#   ~/.miadi/navigator/daemon.pid             (PID file)
```

### User Service

The ironsilk daemon should run as a **user service**, not a root/system service.
It observes your user terminals, desktop windows, tmux panes, and user-owned
state. The service writes a systemd user unit at
`~/.config/systemd/user/tide-runtime.service`.

```bash
# Install and start immediately
tide service install --interval 15 --now

# If you want an explicit isolated runtime state:
tide service install --interval 15 --state-home ~/.local/state/tide-runtime --now

# Control it later
tide service status
tide service restart
tide service stop
tide service uninstall
```

If `~/.miadi/navigator` exists but is not accessible to your current user,
Navigator falls back to `~/.local/state/tide-runtime`. If `systemctl --user`
is unavailable, keep using `tide daemon` in a terminal or a local process
manager until the desktop session imports user services.

### Cockpits

There is not yet a floating desktop window. The working surfaces are:

- `tide studio` — the Textual cockpit: Explorer, presence rail of herdr agents,
  focus pane with a controller-gated steer card
- Miadi's `/tide` page — the web cockpit, on `@miadi/tide`: panes grouped by
  domain, peek, steer, and annotating an agent's last reply from a phone or a desk
- `tide context`, `tide focus <domain> --desktop`, `tide session ...` — the CLI views
- `app/docs/miadi-agent/navigator` in Miadi — a design mock of the overlay,
  approval queue and Telegram continuity (first commit c83c2a91, 2026-04-29).
  It is not a running surface.

The desktop overlay is specified in
`rispecs/tide-runtime/05-ui-surface.spec.md`, but implementation remains the
next product step.

---

## Architecture Overview

```
domains.yml ──► DomainDetector ──► ContextEngine ──► Daemon
                                              │
                                     ┌────────┼─────────┐
                                     │        │         │
                                 SessionStore  ApprovalQ  Focus
                                     │
                                    CLI (tide)
```

**Data flow (Phase 1):**
1. `DomainDetector` reads `domains.yml` and detects domain identity for tmux panes
2. `ContextEngine` polls tmux and desktop windows every N seconds and builds `ContextSnapshot`
3. `Daemon` runs `ContextEngine.start_continuous()` and writes to `~/.miadi/navigator/`
4. `tide` commands query the daemon or directly invoke detection/focus

---

## Integration with Hermes Skill Ecosystem

The navigator is packaged as a Tide skill at `skills/navigator.skill.yml`. Any Hermes-class agent can load this manifest and call:

- `navigator.focus(domain)` — list or activate domain terminal group
- `navigator.context(domain?, format?)` — get context snapshot
- `navigator.propose(action)` — queue a terminal action for approval
- `navigator.session.resume(name)` — rehydrate a named session
- `navigator.session.list()` — list all sessions

See `rispecs/tide-runtime/04-skill-contract.spec.md` for full action definitions.

---

## Development

```bash
# Install in development mode
pip install -e ".[dev]"

# Run directly
python -m tide_runtime.cli --help

# Use the installed console entry point.
tide --help
```

### Project Structure

```
runtime/tide-runtime/
├── tide_runtime/
│   ├── models.py             # data models
│   ├── config.py, paths.py   # config and the MIADI_HOME state root
│   ├── domain_detector.py    # R1 — domain detection
│   ├── context_engine.py     # R2 — context (tmux, desktop windows, agents); snapshot index kept 7 days
│   ├── sqlite_async.py       # async SQLite helpers
│   ├── focus.py, desktop_focus.py, desktop_observer.py  # R4 — terminal and window focus
│   ├── approval_queue.py     # R5 — action approval
│   ├── session_store.py, tmux_session.py  # R7 — named sessions
│   ├── scene.py              # workspaces (`scene` is the deprecated alias)
│   ├── terminal_inventory.py, terminal_registry.py, terminal_checkpoint.py  # operator awareness
│   ├── terminal_steer.py, terminal_send.py  # steer proposals and the gated send
│   ├── herdr.py              # read/steer client for the herdr multiplexer
│   ├── zellij_bridge.py      # Zellij plugin ingest
│   ├── pane_agents.py        # which agent runs, or ran, in each pane
│   ├── launchers.py          # launch aliases and per-kind resume rules
│   ├── agent_restore.py      # `tide agents restore`
│   ├── audit.py, release_gate.py, service.py, daemon.py
│   ├── tui/                  # `tide studio`
│   └── cli.py                # tide CLI
├── docs/
│   └── how-tide-helps-the-work.md  # the Python runtime and its TypeScript side
├── foundations/              # inquiry artifacts behind the Studio slices
├── skills/
│   └── navigator.skill.yml   # Tide skill manifest
├── examples/
├── tests/
├── pyproject.toml
├── RELEASE.md                # publishing (from gaia: upload as jgi)
└── README.md
```

---

## Specs

- Master: `rispecs/tide-runtime/00-tide-runtime-master.spec.md`
- Domain Detection: `rispecs/tide-runtime/01-domain-detection.spec.md`
- Context Engine: `rispecs/tide-runtime/02-context-engine.spec.md`
- Session Store: `rispecs/tide-runtime/03-session-store.spec.md`
- Skill Contract: `rispecs/tide-runtime/04-skill-contract.spec.md`
- UI Surface: `rispecs/tide-runtime/05-ui-surface.spec.md`
- Daemon Lifecycle: `rispecs/tide-runtime/06-daemon-lifecycle.spec.md`
- Mode And Safety: `rispecs/tide-runtime/07-mode-and-safety.spec.md`
- Threat Model: `rispecs/tide-runtime/08-threat-model.spec.md`
- Telegram Bridge: `rispecs/tide-runtime/09-telegram-bridge.spec.md`
- Domain Relational Graph: `rispecs/tide-runtime/10-domain-relational-graph.spec.md`
- Zellij Augmentation: `rispecs/tide-runtime/11-zellij-augmentation.spec.md`
- Tmux Focus Tracking: `rispecs/tide-runtime/12-tmux-focus-tracking.spec.md`
- TmuxAI Observe/Act Harness: `rispecs/tide-runtime/13-tmuxai-observe-act-harness.spec.md`
- Presence-First Studio: `rispecs/tide-runtime/14-presence-first-studio.spec.md`
- Agent Continuity: `rispecs/tide-runtime/15-agent-continuity.spec.md`
- Steering Clients And Cockpit: `rispecs/tide-runtime/16-steering-clients-and-cockpit.spec.md`
- Status: `rispecs/tide-runtime/STATUS.md`

Academic grounding for the multiplexing and workspace model lives in Miadi's
`foundations/terminal-workspace-orchestration/`, `foundations/tide-runtime-tmuxai/`
and `foundations/ai-native-terminal-multiplexer-integration-layer/`.
