项目文件夹

文件
Devika 06f93e12f4 First-run UX: fix double-send, clarify model setup, add automation templates
Addresses the first-run review (sign-up -> setup -> first task), focused on
non-developers getting past model setup.

- Fix the "send twice" bug: drop `workspace` from the session-connect effect
  deps so adopting the server's Cowork scratch dir no longer tears down and
  rebuilds the socket right after connect (which silently dropped the first
  message). Add an outbound queue that flushes on open as a backstop.
- Composer: replace the misleading model picker with a "No model" warning when
  no provider is configured; sending routes to Configure Models and keeps the
  draft. The picker now lists only configured providers' models (drop the
  hardcoded seed list, including the phantom deepseek-chat).
- Onboarding: lead with the privacy-first value prop; auto-detect the provider
  from the key shape; add a "Test" button (live, no-save credential check) and
  a "Don't have a key?" helper; warn on Skip when nothing is connected.
- Settings > Configure Models: clearer connected/not-connected states, a Test
  button, a "Done - back to chat" footer, and a reassuring save confirmation.
- Backend: POST /v1/providers/verify (transient httpx model-list check) plus
  provider auto-detect; model_ready flag in settings; filter the model list to
  configured providers.
- Automations: a "New automation" CTA and one-click templates (morning news
  briefing, inbox digest, folder cleanup) via a new POST /v1/automations.
- Start screen leads with one-click task cards; add a topbar "Artifacts (N)"
  affordance when the side panel is hidden.

Tests: GUI tsc + build clean; backend 351 passed (adds provider-verify and
automation-create tests).
2026-06-23 11:08:50 +02:00

74 行
2.3 KiB
Python

"""Tests for the GUI-driven `create_automation` path (the "New automation" / template flow).
No network and no LLM: this exercises validation + that a valid create lands in the task store
with a freshly provisioned scratch workspace.
"""
from __future__ import annotations
from pathlib import Path
from coworker.server.manager import SessionManager
def _manager(tmp_path, monkeypatch) -> SessionManager:
monkeypatch.setenv("COWORKER_STATE_DIR", str(tmp_path / "state"))
return SessionManager(data_dir=tmp_path / "data")
def test_create_automation_success(tmp_path, monkeypatch):
manager = _manager(tmp_path, monkeypatch)
out = manager.create_automation(
{
"title": "Morning news briefing",
"instructions": "Search the web and write a 5-bullet briefing.",
"cron": "0 8 * * *",
}
)
assert out["ok"] is True
task = out["task"]
assert task["title"] == "Morning news briefing"
assert task["schedule"] == "Every day at ~8:00 AM"
# it really landed in the store and is bound to a fresh scratch workspace
saved = manager.task_store.get(task["id"])
assert saved is not None
assert saved.agent == "cowork"
assert Path(saved.workspace).is_dir()
def test_create_automation_invalid_cron(tmp_path, monkeypatch):
manager = _manager(tmp_path, monkeypatch)
out = manager.create_automation(
{
"title": "Bad",
"instructions": "do something",
"cron": "not-a-cron",
}
)
assert out["ok"] is False
assert "invalid cron" in out["error"]
assert manager.task_store.list() == []
def test_create_automation_missing_instructions(tmp_path, monkeypatch):
manager = _manager(tmp_path, monkeypatch)
out = manager.create_automation(
{
"title": "No instructions",
"instructions": " ",
"cron": "0 8 * * *",
}
)
assert out["ok"] is False
assert "instructions" in out["error"]
assert manager.task_store.list() == []
def test_create_automation_requires_schedule(tmp_path, monkeypatch):
manager = _manager(tmp_path, monkeypatch)
out = manager.create_automation(
{"title": "No schedule", "instructions": "do something"}
)
assert out["ok"] is False
assert manager.task_store.list() == []