From 81fed11c2126b1dbfaaeedc5968d344b79036a8a Mon Sep 17 00:00:00 2001 From: David Matejka Date: Tue, 23 Jun 2026 16:16:01 +0200 Subject: [PATCH 01/16] feat(agent-status): lifecycle indicator, session capture, persistence & resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report what an AI coding agent (Claude Code, Codex, …) is doing in each pane via a push-based, in-band OSC 9001 protocol, and remember/resume its session across restarts. The model is open: a small fixed lifecycle drives colour/sort/ notifications while a free-form message + labels carry whatever the agent wants. Display & protocol: - OSC 9001 (st/msg/lbl) parsed in the terminal sidecar into a canonical AgentStatus; surfaced as a per-tab indicator, a cross-project sidebar AGENTS section (click to jump), a per-project field in the remote API, and a desktop notification on blocked/done. Runtime-only, never persisted. - reveal_terminal navigates to a hidden pane (retargets zoom/fullscreen) behind agent-row / notification / remote-focus clicks. - Bundled Claude Code plugin (integrations/claude-code) maps lifecycle hooks to states; OKENA_TTY exports the pane's slave pts so hooks without a controlling terminal can emit the OSC. Session capture, persistence & resume (harness-extensible): - Reserved lbl keys agent/session_id/transcript_path are captured into a sticky per-pane AgentSession (UUID-validated, survives st=clear), persisted per terminal in workspace.json, and resumed on restore behind the opt-in auto_resume_agent_sessions setting (e.g. `claude --resume `). - Per-harness resume/transcript logic lives behind a gpui-free okena_core::agent_harness registry keyed by agent id (works desktop + headless); Claude Code + Codex impls in the okena-ext-* crates. Adding a harness is additive. - Auto-resume requires a session backend (the terminal_id the session is keyed by only survives a restart with tmux/dtach/screen). Hardening: - Bound OSC 9001 custom/labels to mirror the OSC 99 caps (a hostile pane could otherwise pin unbounded in-band memory, amplified to every remote client); decode off-lock; empty msg falls back to the default notification body. - Headless PTY loop now drains the remote_dirty edge so agent status pushes to remote/mobile clients (was desktop-only). - reveal_terminal no longer clears individual-zoom on a same-project click (regressed sidebar/cursor clicks); corrected the OKENA_TTY tmux-reattach docs. Docs: docs/agent-status.md, crates/okena-terminal/CLAUDE.md. Co-Authored-By: Claude Opus 4.8 (1M context) Claude-Session: https://claude.ai/code/session_01KbKbY5iGGgXSnpHz2V5aSr --- crates/okena-app-core/src/remote_snapshot.rs | 25 +- .../src/workspace/actions/execute/project.rs | 1 + .../src/views/overlays/project_switcher.rs | 1 + crates/okena-cli/src/resolve.rs | 1 + crates/okena-core/src/agent_harness.rs | 88 +++++ crates/okena-core/src/agent_session.rs | 82 +++++ crates/okena-core/src/agent_status.rs | 291 ++++++++++++++++ crates/okena-core/src/api.rs | 17 + crates/okena-core/src/lib.rs | 3 + crates/okena-daemon-core/src/command_loop.rs | 18 + crates/okena-daemon-core/src/daemon.rs | 3 + crates/okena-daemon-core/src/observers.rs | 1 + crates/okena-daemon-core/src/pty_loop.rs | 66 ++++ crates/okena-daemon-core/src/soft_close.rs | 1 + .../src/worktree_close_watchdog.rs | 1 + crates/okena-ext-claude/src/harness.rs | 55 +++ crates/okena-ext-claude/src/lib.rs | 2 + crates/okena-ext-codex/src/harness.rs | 32 ++ crates/okena-ext-codex/src/lib.rs | 2 + crates/okena-state/src/workspace_data.rs | 7 + crates/okena-terminal/CLAUDE.md | 52 +++ crates/okena-terminal/src/pty_manager.rs | 21 ++ crates/okena-terminal/src/terminal/meta.rs | 33 ++ crates/okena-terminal/src/terminal/mod.rs | 44 +++ .../src/terminal/osc_sidecar.rs | 196 +++++++++++ .../okena-terminal/src/terminal/tests/osc.rs | 316 ++++++++++++++++++ crates/okena-transport/src/client/state.rs | 2 + crates/okena-views-sidebar/src/agents_list.rs | 204 +++++++++++ crates/okena-views-sidebar/src/lib.rs | 1 + .../src/sidebar/from_project_test.rs | 1 + .../okena-views-sidebar/src/sidebar/render.rs | 1 + .../src/layout/tabs/mod.rs | 49 ++- .../src/layout/terminal_pane/mod.rs | 34 ++ crates/okena-views-terminal/src/lib.rs | 5 + crates/okena-workspace/src/actions/focus.rs | 19 +- crates/okena-workspace/src/actions/folder.rs | 2 + .../src/actions/layout/tests_gpui.rs | 2 + crates/okena-workspace/src/actions/project.rs | 3 + .../okena-workspace/src/actions/soft_close.rs | 1 + .../okena-workspace/src/actions/worktree.rs | 2 + crates/okena-workspace/src/focus.rs | 167 +++++++++ crates/okena-workspace/src/persistence.rs | 2 + crates/okena-workspace/src/remote_apply.rs | 2 + crates/okena-workspace/src/settings.rs | 9 + crates/okena-workspace/src/state.rs | 49 +++ crates/okena-workspace/src/visibility.rs | 1 + docs/agent-status.md | 231 +++++++++++++ .../.claude-plugin/marketplace.json | 15 + .../.claude-plugin/plugin.json | 11 + .../claude-code/okena-lifecycle/README.md | 47 +++ .../okena-lifecycle/hooks/hooks.json | 74 ++++ .../scripts/okena-agent-status.sh | 118 +++++++ src/main.rs | 17 + src/smoke_tests.rs | 1 + 54 files changed, 2412 insertions(+), 17 deletions(-) create mode 100644 crates/okena-core/src/agent_harness.rs create mode 100644 crates/okena-core/src/agent_session.rs create mode 100644 crates/okena-core/src/agent_status.rs create mode 100644 crates/okena-ext-claude/src/harness.rs create mode 100644 crates/okena-ext-codex/src/harness.rs create mode 100644 crates/okena-views-sidebar/src/agents_list.rs create mode 100644 docs/agent-status.md create mode 100644 integrations/claude-code/.claude-plugin/marketplace.json create mode 100644 integrations/claude-code/okena-lifecycle/.claude-plugin/plugin.json create mode 100644 integrations/claude-code/okena-lifecycle/README.md create mode 100644 integrations/claude-code/okena-lifecycle/hooks/hooks.json create mode 100755 integrations/claude-code/okena-lifecycle/scripts/okena-agent-status.sh diff --git a/crates/okena-app-core/src/remote_snapshot.rs b/crates/okena-app-core/src/remote_snapshot.rs index 9d8a53f1d..25c212c26 100644 --- a/crates/okena-app-core/src/remote_snapshot.rs +++ b/crates/okena-app-core/src/remote_snapshot.rs @@ -3,8 +3,8 @@ //! Both remote command loops answer `RemoteCommand::GetState` by projecting the //! same [`WorkspaceData`] onto the same wire DTOs: //! -//! * GUI: `okena-app`'s `app/remote_commands.rs` `remote_command_loop` -//! (reads an `Entity` / `Entity`). +//! * GUI: the daemon client command loop (reads an `Entity` / +//! `Entity`). //! * Headless: `okena-daemon-core`'s `command_loop.rs` `daemon_command_loop` //! (reads `Arc>` / `Arc>`). //! @@ -42,12 +42,14 @@ pub fn api_project_visibility(project_id: &str, hidden_project_ids: &HashSet