Skip to content

Commit 4c8ccee

Browse files
authored
chore(loop): bootstrap autonomous desktop-feature loop infra (#38)
Prerequisites for the unattended desktop-feature loop: - e2e CDP harness (`bun run e2e`): launches the built Electron app with remote debugging, drives the real UI over the Chrome DevTools Protocol, and asserts on rendered state. Zero new deps (Bun's WebSocket + fetch). Runs against an isolated profile (config, userData, default workspace redirected under .e2e/) and is headless-aware (xvfb + --no-sandbox on Linux). Ships an app-boot smoke assertion as the template for new ones. - docs/loop/feature-ledger.md: cross-run ledger with a status state machine (proposed/in-progress/pr-open/merged/rejected/blocked). - docs/loop/capability-inventory.md: baseline inventory of current desktop capabilities to diff candidate features against.
1 parent d5f76b0 commit 4c8ccee

9 files changed

Lines changed: 816 additions & 0 deletions

File tree

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,9 @@ pnpm-lock.yaml
5959
# Craft Agent local data (sessions, credentials, config)
6060
.craft-agent/
6161

62+
# E2E harness scratch (isolated user-data, failure screenshots)
63+
.e2e/
64+
6265
# SSH keys and secrets
6366
*.pem
6467
*.key

docs/loop/capability-inventory.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# OpenWork Capability Inventory (baseline)
2+
3+
A snapshot of what the OpenWork desktop app **already does today**, grounded in
4+
the current code. The autonomous feature loop diffs Claude Code Desktop / Codex
5+
desktop features against this baseline to find genuine gaps — so a candidate that
6+
maps to anything below is **not** a gap.
7+
8+
> Scope: the Electron frontend/shell only. The qwen-code backend (vendored from
9+
> npm) is out of scope and must not be modified. Paths are representative anchors,
10+
> not exhaustive.
11+
>
12+
> Maintenance: refresh this when a `loop-bot` PR ships a new capability, or
13+
> periodically re-survey. Treat it as a fast index, not a contract.
14+
15+
## 1. Sessions & conversation
16+
- Multi-session per workspace; create/delete/archive/flag; status (`todo`,
17+
`in_progress`, `needs_review`, `done`, `cancelled`); search & filter.
18+
- Persisted history with message-level streaming, token-usage tracking, read/unread.
19+
- Session export/import + cross-workspace resource bundles.
20+
- `apps/electron/src/renderer/atoms/sessions.ts`, `packages/core/src/types/session.ts`
21+
22+
## 2. Chat & agent interaction UI
23+
- Message bubbles (user/system), turn cards with actions, inline execution progress.
24+
- Plan accept/reject UI; streaming incremental updates; token display.
25+
- Auth-request cards for OAuth flows; tool-execution approval dialogs.
26+
- **Permission modes (4):** `explore` / `ask` / `execute` / `auto-edit`.
27+
- **Thinking levels (6):** off / low / medium / high / xhigh / max, with per-workspace defaults.
28+
- `packages/ui/src/components/chat/*`, `packages/shared/src/agent/mode-types.ts`
29+
30+
## 3. File & artifact previews
31+
- Code (Shiki highlight), diffs, JSON tree, HTML (sandboxed), Mermaid, LaTeX.
32+
- PDF viewer, spreadsheet/Excel table (sort/filter/export), DOCX, PPTX, image gallery.
33+
- Rich markdown: collapsible sections, table export (CSV/Excel), link preview overlay.
34+
- `packages/ui/src/components/markdown/*`, `apps/electron/src/renderer/components/files/FileViewer.tsx`
35+
36+
## 4. Local project & workspace management
37+
- Workspace types: `conversation` / `project`; multi-workspace; per-workspace config.
38+
- File read (text/binary), directory listing/search, file watching, allowed-dir enforcement.
39+
- Shell command execution with output capture, kill/terminate, shell-env loading (nvm/Homebrew).
40+
- Worktree creation; per-session modified/new-file tracking; notes.
41+
- `apps/electron/src/main/handlers/workspace.ts`, `apps/electron/src/main/shell-env.ts`
42+
43+
## 5. Integrations & data sources
44+
- **MCP servers:** HTTP MCP connections, workspace-scoped OAuth / bearer auth, tool listing & execution.
45+
- **Native OAuth:** Google, Slack, Microsoft (PKCE, refresh); plus a generic OAuth 2.0 client for any provider.
46+
- **Local folder sources** as agent data sources; encrypted per-workspace credential store with health tracking.
47+
- **Messaging adapters:** WhatsApp, Telegram (pairing/linking, inbound event fanout).
48+
- `packages/shared/src/mcp/*`, `packages/shared/src/auth/*`, `packages/messaging-gateway/src/adapters/*`
49+
50+
## 6. Skills & extensibility
51+
- `SKILL.md` (YAML frontmatter) format; metadata (name, description, icon, required sources, alwaysAllow tools).
52+
- Multi-tier sourcing: global (`~/.agents/`), workspace, project (`.agents/`), provider.
53+
- File-glob auto-trigger; tool allow-listing; enable/disable; marketplace discovery.
54+
- `packages/shared/src/skills/*`
55+
56+
## 7. Settings & preferences
57+
- Model selection per session/workspace; multiple LLM connections per provider; custom endpoints.
58+
- Default + per-session permission mode; per-workspace thinking level.
59+
- Theme (light/dark/system + presets), language (i18next), keep-awake, keyboard shortcuts.
60+
- Network proxy (HTTP/HTTPS/SOCKS + auth).
61+
- `apps/electron/src/main/handlers/settings.ts`, `packages/shared/src/config/*`
62+
63+
## 8. Window & UX features
64+
- Multi-window (per-workspace), window-state persistence, close confirmation, deep links (`craftagents://`).
65+
- **Pet window:** floating companion overlay, click-through, activity animations, notifications.
66+
- **Embedded browser pane:** Chromium pane (dockable/floating) with navigate/back/forward/reload,
67+
JS eval, screenshot, form interaction, a11y snapshot, CDP integration, console/network logging.
68+
- Theme system with per-workspace override; i18n; command palette / action registry; global shortcuts.
69+
- `apps/electron/src/main/window-manager.ts`, `apps/electron/src/main/browser-pane-manager.ts`
70+
71+
## 9. Model & provider configuration
72+
- Providers: Qwen (ModelStudio) primary; OpenAI-compatible custom endpoints; multi-connection.
73+
- Model metadata (capabilities, context window, pricing) with lazy-load/caching.
74+
- Auth: API key / OAuth / bearer / custom headers. Runtime proxy changes.
75+
- `packages/shared/src/config/llm-connections.ts`, `packages/shared/src/config/model-fetcher.ts`
76+
77+
## 10. Auto-update & distribution
78+
- electron-updater: macOS (DMG/zip + signature validation), Windows (NSIS), Linux (AppImage).
79+
- Download progress, update notification, install-on-quit, skip-version, dev-build exclusion.
80+
- macOS code-signing checks; brand-configurable update sources.
81+
- Platforms: macOS (arm64/x64), Windows (x64/arm64), Linux (AppImage).
82+
- `apps/electron/src/main/auto-update.ts`
83+
84+
## 11. Notifications
85+
- Native OS notifications (focus-suppressed, click-to-navigate, workspace routing).
86+
- macOS dock badge unread count (instance-aware); in-app Sonner toasts.
87+
- `apps/electron/src/main/notifications.ts`
88+
89+
## 12. Other notable
90+
- Deep linking / URL scheme actions (new-chat, resume/delete/flag session, navigate to settings/sources/skills).
91+
- Session resource export/import; remote workspace support (WebSocket-proxied handlers, chunked RPC).
92+
- Power management (keep-awake during runs); Sentry error tracking with redaction; onboarding/setup flow.
93+
- `apps/electron/src/main/deep-link.ts`, `apps/electron/src/main/onboarding.ts`
94+
95+
---
96+
97+
_Generated as a baseline survey. Verify against current code before relying on any
98+
single line — the app evolves and this index can lag._

docs/loop/feature-ledger.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Feature Ledger
2+
3+
Working memory for the autonomous desktop-feature loop. Each run appends or
4+
updates a row here.
5+
6+
**GitHub is the source of truth.** At the start of every run the loop reconciles
7+
this file against GitHub issues/PRs labeled `loop-bot`, so merge/close state is
8+
authoritative there — this table is the loop's human-readable index and learning
9+
log, not the system of record.
10+
11+
## Status legend
12+
13+
| status | meaning |
14+
| --- | --- |
15+
| `proposed` | Identified as a gap, not started. |
16+
| `in-progress` | Issue filed, branch open, implementation underway. |
17+
| `pr-open` | PR submitted, awaiting human review/merge. |
18+
| `merged` | PR merged — feature shipped. |
19+
| `rejected` | PR closed **without** merging. Record **why** in Notes. |
20+
| `blocked` | Not doable as a frontend-only change (needs qwen-code backend) or otherwise infeasible. Record **why**. |
21+
22+
## Selection rules
23+
24+
- Never pick a feature whose latest status is `merged`, `rejected`, `blocked`, or
25+
`in-progress`.
26+
- `rejected` rows are **learning signals**: read the PR's close reason / review
27+
comments before considering the area again, and only retry with a clearly
28+
different and better approach.
29+
- Keep the newest entries at the top.
30+
31+
## Ledger
32+
33+
| slug | title | source | feasibility | status | issue | pr | branch | updated | notes |
34+
| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
35+
| _(empty)_ | _first run appends here_ | | | | | | | | |

e2e/README.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# E2E harness (CDP)
2+
3+
A dependency-free end-to-end harness that launches the **built** OpenWork
4+
Electron app with the Chrome DevTools Protocol enabled, drives the real UI over
5+
CDP, and asserts on what the user would actually see.
6+
7+
It exists so an automated feature loop (and humans) can answer one question per
8+
change: **does this feature actually work in the running app?**
9+
10+
## Run it
11+
12+
```bash
13+
bun run e2e
14+
```
15+
16+
This will:
17+
18+
1. Build the Electron bundles if `apps/electron/dist/{main.cjs,renderer/index.html}`
19+
are missing (set `E2E_BUILD=1` to force a rebuild).
20+
2. For **each** `e2e/assertions/*.assert.ts`, launch a fresh, isolated app
21+
instance with `--remote-debugging-port`, run the assertion, then shut it down.
22+
3. Print a pass/fail summary and exit non-zero if anything failed (so it can gate
23+
a PR).
24+
25+
Failure screenshots land in `.e2e/screenshots/` (gitignored). The app runs
26+
against an isolated profile under `.e2e/` so runs never touch your real OpenWork
27+
data — Electron userData, app config (`~/.craft-agent`), and the default
28+
conversation workspace (`~/Documents/...`) are all redirected under `.e2e/`.
29+
30+
### Headless / CI / cloud
31+
32+
On a headless Linux host (no `DISPLAY`), the launcher automatically runs Electron
33+
under `xvfb-run` with `--no-sandbox`. The host must have `xvfb` and Electron's
34+
runtime libraries (`libgtk-3`, `libnss3`, …) installed.
35+
36+
### Environment knobs
37+
38+
| Var | Effect |
39+
| --- | --- |
40+
| `E2E_BUILD=1` | Force `electron:build` before launching. |
41+
| `E2E_PORT=<n>` | Pin the remote-debugging port instead of using a free one. |
42+
43+
## Add an assertion
44+
45+
Drop a `*.assert.ts` file into `e2e/assertions/`. It must default-export an
46+
`Assertion``{ name, run(app) }`. The runner discovers it automatically.
47+
48+
```ts
49+
import type { Assertion } from '../runner';
50+
51+
const assertion: Assertion = {
52+
name: 'settings dialog opens from the sidebar',
53+
async run(app) {
54+
const { session } = app;
55+
await session.click('[aria-label="Settings"]');
56+
await session.waitForSelector('[role="dialog"]');
57+
const heading = await session.getText('[role="dialog"] h2');
58+
if (heading?.trim() !== 'Settings') {
59+
throw new Error(`expected Settings dialog, saw heading: ${heading}`);
60+
}
61+
},
62+
};
63+
64+
export default assertion;
65+
```
66+
67+
Guidelines:
68+
69+
- **Assert on visible result/state, not "it booted."** A green check should mean
70+
the feature's key path works.
71+
- Prefer stable selectors (`role`, `aria-label`, visible text) over brittle
72+
class chains. If the UI lacks a hook, add a `data-testid` in the same PR.
73+
- Each assertion gets a clean app instance, so they're independent and order
74+
doesn't matter.
75+
76+
## How it works
77+
78+
- `cdp.ts` — minimal CDP client over Bun's global `WebSocket` + `fetch`
79+
(`evaluate`, `waitForFunction`, `waitForSelector`, `click`, `getText`,
80+
`screenshot`). Zero external deps.
81+
- `app.ts` — launches `electron apps/electron --remote-debugging-port=<port>`,
82+
finds the main renderer target (title `Qwen Code Desktop`), attaches a session,
83+
and owns teardown.
84+
- `runner.ts` — discovers assertions, runs each against a fresh app, reports.
85+
- `assertions/*.assert.ts` — the checks themselves.

0 commit comments

Comments
 (0)