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
14 changes: 7 additions & 7 deletions .github/workflows/windows-distribution.yml
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ jobs:
shell: pwsh
run: |
$version = & .\dist\interly.exe --version
if ($LASTEXITCODE -ne 0 -or $version -ne "Interly 0.5.1") {
if ($LASTEXITCODE -ne 0 -or $version -ne "Interly 0.6.0") {
throw "Standalone executable smoke test failed: $version"
}

Expand All @@ -56,16 +56,16 @@ jobs:
- name: Build Windows installer
shell: pwsh
run: |
& "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe" "/DMyAppVersion=0.5.1" packaging\interly.iss
& "${env:ProgramFiles(x86)}\Inno Setup 6\ISCC.exe" "/DMyAppVersion=0.6.0" packaging\interly.iss
if ($LASTEXITCODE -ne 0) { throw "Inno Setup failed." }

- name: Generate WinGet manifests
shell: pwsh
run: |
$sha = (Get-FileHash .\dist\InterlySetup-x64.exe -Algorithm SHA256).Hash
$url = "https://github.com/interlinkglobal/Interly/releases/download/v0.5.1/InterlySetup-x64.exe"
python packaging\render_winget.py --version 0.5.1 --installer-url $url --sha256 $sha --output dist\winget
Compress-Archive -Path dist\winget\* -DestinationPath dist\Interly-0.5.1-winget-manifests.zip
$url = "https://github.com/interlinkglobal/Interly/releases/download/v0.6.0/InterlySetup-x64.exe"
python packaging\render_winget.py --version 0.6.0 --installer-url $url --sha256 $sha --output dist\winget
Compress-Archive -Path dist\winget\* -DestinationPath dist\Interly-0.6.0-winget-manifests.zip

- name: Upload build artifact
uses: actions/upload-artifact@v4
Expand All @@ -75,7 +75,7 @@ jobs:
dist/interly.exe
dist/interlink.exe
dist/InterlySetup-x64.exe
dist/Interly-0.5.1-winget-manifests.zip
dist/Interly-0.6.0-winget-manifests.zip

- name: Publish GitHub release
if: startsWith(github.ref, 'refs/tags/v')
Expand All @@ -86,4 +86,4 @@ jobs:
dist/interly.exe
dist/interlink.exe
dist/InterlySetup-x64.exe
dist/Interly-0.5.1-winget-manifests.zip
dist/Interly-0.6.0-winget-manifests.zip
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,16 @@ Git, Node.js, or npm on the user's computer.

### Development and fallback installer

For branch testing, development installations, or recovery when WinGet is unavailable, open
Command Prompt or PowerShell and run:
For development installations or recovery when WinGet is unavailable, open Command Prompt or
PowerShell and run:

```cmd
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm 'https://raw.githubusercontent.com/interlinkglobal/Interly/agent/next-ten-roadmap/install.ps1' | iex"
powershell -NoProfile -ExecutionPolicy Bypass -Command "irm 'https://raw.githubusercontent.com/interlinkglobal/Interly/main/install.ps1' | iex"
```

This fallback installer detects suitable Python and Git installations, installs either when necessary,
installs and configures pipx, installs Interly, verifies the command, and launches it. Node.js is
not required. It remains separate from the standalone WinGet distribution.
installs and configures pipx, installs the current `main` branch, verifies the command, and launches
Interly. Node.js is not required. It remains separate from the standalone WinGet distribution.

On first launch, Interly asks for a [Groq API key](https://console.groq.com/keys). Input is
hidden, validated before saving, and stored under the current Windows user's application-data
Expand Down
72 changes: 72 additions & 0 deletions RELEASE_NOTES_0.6.0.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Interly 0.6.0

Interly 0.6.0 is the first release where the agent can inspect and interact with the visible Windows desktop while using a stronger host-side governance layer.

## Highlights

### Execution governance
- Add session dry-run mode with `dry-run`, `dry-run on`, and `dry-run off`.
- Add persistent per-tool permission policies with `prompt`, `allow`, and `deny` modes.
- Add multi-step plan presentation before complex work.
- Add one-request scoped plan approval limited to the tools and scopes shown in the plan.
- Keep process termination, logout, restart, shutdown, and other destructive Windows actions individually confirmed.
- Add persistent privacy-aware JSONL action audit logs with sensitive text and URL secrets redacted.

### Desktop perception and interaction
- List visible top-level Windows windows with exact handles, PIDs, titles, and rectangles.
- Focus, minimise, maximise, restore, move, and resize exact windows after revalidation.
- Capture the full virtual desktop or a selected window to PNG.
- Add bundled OCR with detected text and bounding boxes.
- Inspect visible foreground controls through Windows UI Automation without activating them.
- Add guarded generic mouse movement, clicking, double-clicking, and bounded scrolling.
- Add guarded generic keyboard typing and bounded key combinations.
- Add separately approved Windows clipboard reads and writes.

### Memory and reusable work
- Add persistent conversation memory with explicit retention controls.
- Add approved personal facts and preferences memory.
- Add memory inspection, export, and clear controls.
- Add reusable named workflows.
- Add the beta `make-memory` command for simple local note capture.

### Developer workflows
- Add repository inspection.
- Add bounded repository command execution for tests, linters, builds, and development-server workflows.

### Distribution and updates
- Bump the package, CLI, standalone executable, installer, and WinGet manifest version to 0.6.0.
- Include the desktop OCR/UI Automation dependencies in normal developer requirements.
- Make the fallback pipx installer install from `main` instead of the old roadmap branch.
- Make pipx update checks follow `main` as well.
- Keep `interly` as the canonical command while retaining `interlink` as a compatibility alias.
- Generate 0.6.0 Windows executable, installer, and WinGet manifest artifacts through the existing Windows distribution workflow.

## Verified roadmap state

Interly is currently at **88/101 verified roadmap items**.

## Still not included

- Recycle Bin file deletion
- Structured PDF, Word, Excel, and PowerPoint understanding
- Malware scanning and quarantine for downloads
- Scheduled tasks, reminders, and monitors
- Structured log monitoring
- Token, cost, latency, and request-count reporting
- Volume and brightness controls
- Speech input and output
- Signed Windows release artifacts
- External 1.0 security review

## Release procedure

After the 0.6.0 release-prep PR is merged and the site deployment is complete:

1. Create tag `v0.6.0` on the current `main` commit.
2. The Windows distribution workflow will build and publish:
- `interly.exe`
- `interlink.exe`
- `InterlySetup-x64.exe`
- `Interly-0.6.0-winget-manifests.zip`
3. Confirm the GitHub Release contains the installer SHA-256 digest.
4. Install or run `update` from an existing standalone Interly installation and verify `interly --version` reports `Interly 0.6.0`.
2 changes: 1 addition & 1 deletion install.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
$ErrorActionPreference = "Stop"
Set-StrictMode -Version Latest

$InterlySpec = "git+https://github.com/interlinkglobal/Interly.git@agent/next-ten-roadmap"
$InterlySpec = "git+https://github.com/interlinkglobal/Interly.git@main"
$PythonVersion = "3.13.14"

function Write-Step([string]$Message) {
Expand Down
2 changes: 1 addition & 1 deletion packaging/interly.iss
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#ifndef MyAppVersion
#define MyAppVersion "0.5.1"
#define MyAppVersion "0.6.0"
#endif

#define MyAppName "Interly"
Expand Down
3 changes: 2 additions & 1 deletion packaging/render_winget.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ def render(version: str, installer_url: str, sha256: str, output: Path) -> list[
- silentWithProgress
UpgradeBehavior: install
Commands:
- interly
- interlink
ReleaseDate: 2026-08-04
ReleaseDate: 2026-08-07
Installers:
- Architecture: x64
InstallerUrl: {installer_url}
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "interly"
version = "0.5.1"
version = "0.6.0"
description = "A permission-aware Windows computer agent powered by Groq"
readme = "README.md"
license = "MIT"
Expand Down
4 changes: 4 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
beautifulsoup4==4.15.0
groq==1.6.0
onnxruntime==1.27.0
pillow==12.3.0
playwright==1.62.0
psutil==7.2.2
pynput==1.8.2
pydantic-settings==2.14.2
python-dotenv==1.2.2
rapidocr==3.9.2
uiautomation==2.0.29
# Development and verification
pytest==9.1.1
ruff==0.16.1
2 changes: 1 addition & 1 deletion src/computer_agent/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
"""Local computer agent package."""

__version__ = "0.5.1"
__version__ = "0.6.0"
6 changes: 3 additions & 3 deletions src/computer_agent/updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

PACKAGE_NAME = "interly"
WINGET_PACKAGE_ID = "InterlinkGlobal.Interly"
UPDATE_BRANCH = "agent/next-ten-roadmap"
UPDATE_BRANCH = "main"
UPDATE_REF_URL = (
"https://api.github.com/repos/interlinkglobal/Interly/git/ref/heads/"
f"{UPDATE_BRANCH}"
Expand All @@ -42,7 +42,7 @@ def installed_commit() -> str | None:


def repository_commit() -> str:
"""Return the current commit at Interly's development update branch."""
"""Return the current commit at Interly's update branch."""
response = httpx.get(
UPDATE_REF_URL,
headers={"Accept": "application/vnd.github+json", "User-Agent": "Interly updater"},
Expand Down Expand Up @@ -91,7 +91,7 @@ def update_interly() -> str:
previous = current[:8] if current else "unknown"
return (
f"Interly updated from {previous} to {latest[:8]}.\n"
"Type exit, then run interlink again to use the new version."
"Type exit, then run interly again to use the new version."
)


Expand Down
2 changes: 1 addition & 1 deletion tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,4 @@ def test_working_groq_key_is_validated_before_save(groq: object, save: object) -
def test_version_flag_exits_before_setup(capsys: object) -> None:
main()

assert "Interly 0.5.1" in capsys.readouterr().out
assert "Interly 0.6.0" in capsys.readouterr().out
9 changes: 7 additions & 2 deletions tests/test_distribution.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,17 @@ def load_renderer() -> object:
def test_winget_renderer_creates_valid_release_manifests(tmp_path: Path) -> None:
renderer = load_renderer()
sha256 = "ab" * 32
url = "https://github.com/interlinkglobal/Interly/releases/download/v0.5.1/InterlySetup-x64.exe"
url = "https://github.com/interlinkglobal/Interly/releases/download/v0.6.0/InterlySetup-x64.exe"

paths = renderer.render("0.5.1", url, sha256, tmp_path)
paths = renderer.render("0.6.0", url, sha256, tmp_path)

assert len(paths) == 3
installer = (tmp_path / "InterlinkGlobal.Interly.installer.yaml").read_text()
assert "PackageIdentifier: InterlinkGlobal.Interly" in installer
assert "PackageVersion: 0.6.0" in installer
assert "InstallerType: inno" in installer
assert "Commands:\n - interly\n - interlink" in installer
assert "ReleaseDate: 2026-08-07" in installer
assert f"InstallerUrl: {url}" in installer
assert f"InstallerSha256: {sha256.upper()}" in installer

Expand All @@ -33,8 +36,10 @@ def test_distribution_files_keep_standalone_and_fallback_routes() -> None:
readme = (ROOT / "README.md").read_text()

assert "pyinstaller" in workflow.casefold()
assert "Interly 0.6.0" in workflow
assert "dist/interly.exe" in workflow
assert "dist/interlink.exe" in workflow
assert "InterlySetup-x64.exe" in workflow
assert "Interly-0.6.0-winget-manifests.zip" in workflow
assert "winget install --id InterlinkGlobal.Interly" in readme
assert "install.ps1" in readme
5 changes: 3 additions & 2 deletions tests/test_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ def test_windows_installer_bootstraps_required_components() -> None:
assert "-m pip install --user --upgrade pipx" in script
assert "-m pipx ensurepath" in script
assert "-m pipx install --force $InterlySpec" in script
assert "agent/next-ten-roadmap" in script
assert "Interly.git@main" in script
assert "agent/next-ten-roadmap" not in script
assert "& $interlyPath" in script


def test_readme_exposes_one_cmd_compatible_install_command() -> None:
readme = (INSTALLER.parent / "README.md").read_text(encoding="utf-8")

assert "powershell -NoProfile -ExecutionPolicy Bypass" in readme
assert "install.ps1' | iex" in readme
assert "Interly/main/install.ps1' | iex" in readme
assert "Node.js" in readme
assert "not required" in readme
19 changes: 13 additions & 6 deletions tests/test_updater.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from unittest.mock import patch

from computer_agent.updater import (
UPDATE_BRANCH,
digest_file,
installed_commit,
latest_release_installer,
Expand All @@ -12,6 +13,10 @@
)


def test_pipx_update_branch_is_main() -> None:
assert UPDATE_BRANCH == "main"


@patch("computer_agent.updater.distribution")
def test_installed_commit_reads_pip_vcs_metadata(metadata: object) -> None:
metadata.return_value.read_text.return_value = json.dumps(
Expand Down Expand Up @@ -45,6 +50,7 @@ def test_new_commit_upgrades_existing_pipx_installation(
result = update_interly()

assert "updated from" in result
assert "run interly again" in result
run.assert_called_once_with(
["C:/Python/Scripts/pipx.exe", "upgrade", "interly"],
capture_output=True,
Expand Down Expand Up @@ -96,15 +102,16 @@ def test_standalone_falls_back_to_verified_release_when_winget_cannot_find_packa
) -> None:
run.return_value = SimpleNamespace(returncode=1, stdout="", stderr="No package found")
release.return_value = (
"0.5.2",
"https://github.com/interlinkglobal/Interly/releases/download/v0.5.2/InterlySetup-x64.exe",
"0.6.1",
"https://github.com/interlinkglobal/Interly/releases/download/v0.6.1/InterlySetup-x64.exe",
"ab" * 32,
)
download.return_value = "C:/Temp/InterlySetup-update-x64.exe"

result = update_standalone()

assert "downloaded, verified" in result
assert "run interly again" in result
download.assert_called_once()
popen.assert_called_once_with(
[
Expand All @@ -120,22 +127,22 @@ def test_standalone_falls_back_to_verified_release_when_winget_cannot_find_packa
@patch("computer_agent.updater.httpx.get")
def test_latest_release_requires_official_installer_and_sha256(get: object) -> None:
get.return_value.json.return_value = {
"tag_name": "v0.5.1",
"tag_name": "v0.6.0",
"assets": [
{
"name": "InterlySetup-x64.exe",
"browser_download_url": (
"https://github.com/interlinkglobal/Interly/releases/download/"
"v0.5.1/InterlySetup-x64.exe"
"v0.6.0/InterlySetup-x64.exe"
),
"digest": f"sha256:{'ab' * 32}",
}
],
}

assert latest_release_installer() == (
"0.5.1",
"https://github.com/interlinkglobal/Interly/releases/download/v0.5.1/InterlySetup-x64.exe",
"0.6.0",
"https://github.com/interlinkglobal/Interly/releases/download/v0.6.0/InterlySetup-x64.exe",
"ab" * 32,
)

Expand Down
Loading