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
2 changes: 1 addition & 1 deletion .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "conn",
"source": "./plugin",
"description": "Conn: MCP server + working-etiquette skill.",
"version": "0.8.3"
"version": "0.8.4"
}
]
}
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ jobs:
- run: python -m unittest discover -s tests/release -v
- run: npm test
working-directory: frontends/tauri
- name: Core and renderer screen parity
run: npm run test:screen
working-directory: frontends/tauri
- run: npm run build
working-directory: frontends/tauri
- name: The website builds, including the pages it generates from docs/
Expand Down Expand Up @@ -172,6 +175,8 @@ jobs:
working-directory: frontends/tauri
- run: npm run test:collaboration:harness
working-directory: frontends/tauri
- run: npm run test:context:harness
working-directory: frontends/tauri
required:
name: Required checks
if: always()
Expand Down
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@

## Unreleased

## 0.8.4 — Preview · 2026-09-21

- Keep pending requests, active control and recent agent activity visible across tabs. Clicking a notice opens the exact terminal and request; opening or switching an agent's tab preserves the person's current selection.
- Let admitted agents without an available shell request terminal preparation. Choose an existing terminal, create a private one, or defer; sharing remains an explicit decision. Duplicate requests coalesce and dismissed requests have a short cooldown.
- Improve Observe navigation, lease-expiry guidance and reconnect instructions. Reconnect to the existing live shell by its stable ID; a new connection does not inherit sharing or control by name.
- Fix truncated and mismatched agent snapshots after resizing long wrapped output. Normal-screen reflow preserves text and attributes, alternate screens keep their layout, and Unicode width handling matches the tested UI output.

Validation: 254 Rust tests (one existing ignored test), 48 frontend tests, 12 stages in the production-UI/real-Rust browser harness and 85 core/xterm screen comparisons passed locally. See [implementation and validation](docs/collaboration-context-results.md). Native taskbar/Dock attention, minimized/multiple windows, Mac/Windows interactive collaboration, real SSH reconnect/reauthentication and installed-app upgrades remain unverified for this patch. Release CI, artifact signatures and notarization are checked separately.

한국어: 여러 탭의 대기 요청·제어 상태·최근 에이전트 작업을 함께 표시하고, 안내에서 정확한 셸과 요청으로 이동합니다. 에이전트가 다른 탭으로 이동해도 사람이 보는 탭은 유지합니다. 셸이 없는 에이전트는 준비를 부탁할 수 있으며, 새 셸도 비공개로 시작해 사람이 공유를 결정합니다. Observe 이동·제어 만료·기존 셸 재접속 안내를 개선하고, 긴 출력의 크기 변경 시 화면과 스냅샷이 달라지던 오류를 수정했습니다.

한국어: Rust 254개·프런트엔드 48개·실제 UI와 Rust 하네스 12단계·화면 교차 비교 85회를 통과했습니다. [구현·검증 결과](docs/collaboration-context-results.ko.md)에 범위를 기록했습니다. 네이티브 알림과 최소화·다중 창, Mac·Windows 대화형 협업, 실제 SSH 재접속·재인증, 설치된 앱의 업그레이드는 이번 패치에서 아직 검증하지 않았습니다.

## 0.8.3 — Preview · 2026-09-21

Validation: 246 Rust tests, 46 frontend tests, 78 release tests, production-App browser fixtures, the real Rust Harness collaboration flow and a real OpenSSH authentication/sharing regression passed locally. Interactive testing of this patch on macOS was not performed because the test Mac is offline; Linux/Windows native GUI acceptance and installed-app upgrade coverage also remain open. Release CI and signing checks are reported separately.
Expand Down
10 changes: 5 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ members = ["crates/core", "crates/cli", "crates/frontend", "crates/browser-harne
default-members = ["crates/core", "crates/cli", "crates/frontend", "crates/browser-harness"]

[workspace.package]
version = "0.8.3"
version = "0.8.4"
edition = "2021"
license = "MIT"
repository = "https://github.com/eggp-dev/conn"

[workspace.dependencies]
conn-core = { path = "crates/core", version = "0.8.3" }
conn-frontend = { path = "crates/frontend", version = "0.8.3" }
conn-core = { path = "crates/core", version = "0.8.4" }
conn-frontend = { path = "crates/frontend", version = "0.8.4" }
tokio = { version = "1", features = ["rt-multi-thread", "net", "io-util", "sync", "time", "macros", "signal"] }
portable-pty = "0.8"
vt100 = { path = "vendor/vt100", version = "0.15.2" }
Expand Down
10 changes: 5 additions & 5 deletions README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ curl -fsSL https://conn.eggp.dev/install.sh | sh

Rust나 Node.js는 필요 없습니다. 에이전트 연결 도구가 함께 들어 있습니다. 두 방법 모두 아래 다운로드와 같은 서명된 릴리스 파일을 설치하며, 이후에는 앱 안에서 업데이트됩니다. Windows는 설치 파일을 사용하세요.

| 운영체제 | v0.8.3 프리뷰 |
| 운영체제 | v0.8.4 프리뷰 |
|---|---|
| macOS · Apple Silicon | [Mac용 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [설치 파일 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [.deb 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-unknown-linux-gnu-desktop.AppImage) |
| macOS · Apple Silicon | [Mac용 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [설치 파일 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [.deb 다운로드](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-unknown-linux-gnu-desktop.AppImage) |

Mac 배포 파일은 서명·공증되어 있습니다. Windows 프리뷰는 무서명이며, Linux는 Ubuntu 24.04에서 빌드하며, AppImage는 앱 안에서 업데이트되고 `.deb`는 새 패키지를 설치해 업데이트합니다. Intel Mac 배포는 잠시 중단했습니다. [설치·업데이트 안내](docs/getting-started.ko.md) · [전체 파일·체크섬](https://github.com/eggp-dev/conn/releases/tag/v0.8.3)
Mac 배포 파일은 서명·공증되어 있습니다. Windows 프리뷰는 무서명이며, Linux는 Ubuntu 24.04에서 빌드하며, AppImage는 앱 안에서 업데이트되고 `.deb`는 새 패키지를 설치해 업데이트합니다. Intel Mac 배포는 잠시 중단했습니다. [설치·업데이트 안내](docs/getting-started.ko.md) · [전체 파일·체크섬](https://github.com/eggp-dev/conn/releases/tag/v0.8.4)

## 처음 5분

Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@ curl -fsSL https://conn.eggp.dev/install.sh | sh

No Rust or Node.js required; the agent connector is included. Both install the same signed release files as the downloads below, and the app updates itself afterwards. Windows: use the installer.

| Platform | v0.8.3 preview |
| Platform | v0.8.4 preview |
|---|---|
| macOS · Apple Silicon | [Download for Mac](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [Download installer](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [Download .deb](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.3/conn-v0.8.3-x86_64-unknown-linux-gnu-desktop.AppImage) |
| macOS · Apple Silicon | [Download for Mac](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-aarch64-apple-darwin-desktop.dmg) |
| Windows · x64 | [Download installer](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-pc-windows-msvc-setup.exe) |
| Ubuntu · x64 | [Download .deb](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-unknown-linux-gnu-desktop.deb) · [AppImage](https://github.com/eggp-dev/conn/releases/download/v0.8.4/conn-v0.8.4-x86_64-unknown-linux-gnu-desktop.AppImage) |

Mac downloads are signed and notarized. Windows previews are unsigned. Linux builds use Ubuntu 24.04, and the AppImage updates in-app while the `.deb` updates by installing a new package; Intel Mac distribution is paused. [Installation and updates](docs/getting-started.md) · [All assets and checksums](https://github.com/eggp-dev/conn/releases/tag/v0.8.3)
Mac downloads are signed and notarized. Windows previews are unsigned. Linux builds use Ubuntu 24.04, and the AppImage updates in-app while the `.deb` updates by installing a new package; Intel Mac distribution is paused. [Installation and updates](docs/getting-started.md) · [All assets and checksums](https://github.com/eggp-dev/conn/releases/tag/v0.8.4)

## First five minutes

Expand Down
11 changes: 6 additions & 5 deletions crates/cli/src/mcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,14 @@ const TOOLS: &[ToolDef] = &[
name: "terminal_request_attention",
affordance: "request_attention",
method: "request_attention",
description: "Asks the human to look at this shared session, for example to review a pending decision. Background access does not depend on attention.",
schema: || json!({ "type": "object", "properties": { "reason": { "type": "string", "description": "Why they should look, one line" } }, "additionalProperties": false }),
description: "Asks the human to review this session. After connection admission, if no live shell is accessible, asks them to choose or prepare a terminal without granting access or control. Repeated preparation requests coalesce. Background access does not depend on attention. Use cancel to withdraw a preparation request.",
schema: || json!({ "type": "object", "properties": { "reason": { "type": "string", "maxLength":512, "description": "Why they should look, one line; omit credentials" }, "cancel": {"type":"boolean"} }, "additionalProperties": false }),
},
ToolDef {
name: "terminal_list_tabs",
affordance: "*",
method: "list_tabs",
description: "Lists the tabs (sessions): number, mode, effectiveMode, whether the human is looking at it (attended), who has the conn, who opened it, and which one your connection is bound to (current). No screen contents.",
description: "Lists permitted existing shells: stable id, tab number, profileName, processAlive, mode, effectiveMode, attended, controller and current binding. After reconnecting, rediscover a surviving shell here and select its id instead of opening a replacement. Names do not establish remote host/account identity. Exited shells are not restored sessions. No screen contents.",
schema: || json!({ "type": "object", "properties": {}, "additionalProperties": false }),
},
ToolDef {
Expand All @@ -111,7 +111,7 @@ const TOOLS: &[ToolDef] = &[
name: "terminal_switch_tab",
affordance: "switch_tab",
method: "switch_tab",
description: "Moves your connection to another tab. It never moves the human's view. In a tab the human is not looking at you still cannot see or write. `tab` is a number or id from terminal_list_tabs.",
description: "Moves your connection to a permitted existing tab, including an Observe tab. It never moves the human's view or grants write control. Access follows sharing, mode and control independently of focus. Prefer the stable session id from terminal_list_tabs when rediscovering an existing shell.",
schema: || json!({ "type": "object", "properties": { "tab": { "type": ["integer", "string"], "description": "Tab number (from 1) or session id" } }, "required": ["tab"], "additionalProperties": false }),
},
ToolDef {
Expand Down Expand Up @@ -251,7 +251,7 @@ impl Bridge {
"exec_pending" => " — a previous ENTER is still in its grace window",
"busy" => " — another agent holds control; use terminal_snapshot only",
"approval_pending" => " — wait with terminal_check_approval or call terminal_interrupt",
"control_denied" => " — the human declined to hand over control; observe with terminal_snapshot and ask again later with a clearer reason",
"control_denied" => " — the human declined control; observe only and tell them. Do not repeat the request unless asked to retry",
"wrong_mode" => " — the frontend put agents in observe mode",
"proposal_pending" => " — your proposal is waiting for the human to commit or reject",
"intent_required" => " — resend ENTER with an `intent` (one line: what this command does and changes)",
Expand All @@ -263,6 +263,7 @@ impl Bridge {
};
tool_error(format!("{code}: {message}{hint}"))
}
Err(ClientError::Disconnected) => tool_error("connection_lost: Conn disconnected. The last operation may be incomplete; do not replay it automatically. Reconnect, complete any new connection approval, then use terminal_list_tabs and terminal_switch_tab with the existing session ID and read a fresh snapshot. A new connection inherits no selected sharing or control.".into()),
Err(e) => tool_error(e.to_string()),
}
}
Expand Down
14 changes: 14 additions & 0 deletions crates/core/examples/screen_probe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//! Test-only JSON-lines bridge for comparing the core grid with the UI parser.
use std::io::{self, BufRead};
use conn_core::screen::ScreenModel;
use serde_json::{json, Value};
fn main() {
let mut screen=ScreenModel::new(24,80);
for line in io::stdin().lock().lines() {
let v:Value=serde_json::from_str(&line.unwrap()).unwrap();
if v["reset"]==true { screen=ScreenModel::new(v["rows"].as_u64().unwrap() as u16,v["cols"].as_u64().unwrap() as u16); }
else if let Some(text)=v["text"].as_str() {screen.process(text.as_bytes());}
else {screen.resize(v["rows"].as_u64().unwrap() as u16,v["cols"].as_u64().unwrap() as u16);}
println!("{}",json!({"screen":screen.rows(),"cursor":screen.cursor()}));
}
}
17 changes: 17 additions & 0 deletions crates/core/src/collaboration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ impl Hub {
pub fn admission_snapshot(&self) -> crate::connections::AdmissionSnapshot {
self.connections.snapshot()
}
pub fn activity_snapshot(&self) -> crate::connections::ActivitySnapshot {
// A newly shared live shell resolves preparation, without choosing it or granting control.
let snapshot = self.connections.activity_snapshot();
for a in &snapshot.connections {
if let Some(request) = &a.preparation {
if self.has_available_session(a.conn_id) { self.connections.dismiss_preparation(a.conn_id, request.id); }
}
}
self.connections.activity_snapshot()
}
pub fn has_available_session(&self, conn: ConnId) -> bool {
self.public_ids().iter().any(|id| self.get_public(id).is_some_and(|s| {
let s = s.lock(); s.participant_allowed(conn) && s.process_alive()
}))
}
pub fn set_activity_listener(&self, listener: crate::connections::ActivityListener) { self.connections.set_activity_listener(listener); }
pub fn dismiss_preparation(&self, conn: ConnId, request_id: u64) -> bool { self.connections.dismiss_preparation(conn, request_id) }
pub fn admission_policy(&self) -> AdmissionPolicy {
self.connections.policy()
}
Expand Down
Loading