mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-11 14:50:14 +00:00
coworker picker: 'Use temporary folder' copy; retire Chat persona
Chat ships disabled+unsurfaced (Coworker covers quick Q&A); recoverable from Settings.
This commit is contained in:
@@ -50,6 +50,10 @@ class PersonaEntry:
|
|||||||
default_surfaced: bool = (
|
default_surfaced: bool = (
|
||||||
True # whether it shows in the picker before any user choice
|
True # whether it shows in the picker before any user choice
|
||||||
)
|
)
|
||||||
|
# Whether it ships enabled before any user choice. Builtins default on (UX-029: the
|
||||||
|
# composer picker is their front door) — except retired ones (Chat: Coworker covers
|
||||||
|
# quick Q&A). Installed third-party personas always start disabled pending consent.
|
||||||
|
default_enabled: bool = True
|
||||||
_builder: Optional[Callable[[], Agent]] = None
|
_builder: Optional[Callable[[], Agent]] = None
|
||||||
manifest: Optional[PersonaManifest] = None
|
manifest: Optional[PersonaManifest] = None
|
||||||
|
|
||||||
@@ -101,6 +105,7 @@ class PersonaRegistry:
|
|||||||
tools,
|
tools,
|
||||||
workspace="deliverable",
|
workspace="deliverable",
|
||||||
default_surfaced=True,
|
default_surfaced=True,
|
||||||
|
default_enabled=True,
|
||||||
) -> None:
|
) -> None:
|
||||||
self._entries[id] = PersonaEntry(
|
self._entries[id] = PersonaEntry(
|
||||||
id=id,
|
id=id,
|
||||||
@@ -113,13 +118,14 @@ class PersonaRegistry:
|
|||||||
workspace=workspace,
|
workspace=workspace,
|
||||||
tools=list(tools),
|
tools=list(tools),
|
||||||
default_surfaced=default_surfaced,
|
default_surfaced=default_surfaced,
|
||||||
|
default_enabled=default_enabled,
|
||||||
_builder=builder,
|
_builder=builder,
|
||||||
)
|
)
|
||||||
|
|
||||||
def _load_builtin(self, builtin_dir: Optional[str | Path]) -> None:
|
def _load_builtin(self, builtin_dir: Optional[str | Path]) -> None:
|
||||||
# Core surfaces keep their exact prompts via the existing builders. Cowork (the default)
|
# Core surfaces keep their exact prompts via the existing builders. Cowork (the default)
|
||||||
# leads; Chat is hidden from the picker by default (Cowork covers quick Q&A) — recoverable
|
# leads; Chat is RETIRED (owner call 2026-08-11: Coworker covers quick Q&A) — it ships
|
||||||
# from the Personas tab.
|
# disabled and unsurfaced, recoverable from Settings ▸ Coworkers.
|
||||||
self._register_builder(
|
self._register_builder(
|
||||||
"cowork",
|
"cowork",
|
||||||
"OpenWorker",
|
"OpenWorker",
|
||||||
@@ -153,6 +159,7 @@ class PersonaRegistry:
|
|||||||
[],
|
[],
|
||||||
workspace="none",
|
workspace="none",
|
||||||
default_surfaced=False,
|
default_surfaced=False,
|
||||||
|
default_enabled=False,
|
||||||
)
|
)
|
||||||
# Markdown-backed built-ins (Ops, …) — dogfood the manifest path.
|
# Markdown-backed built-ins (Ops, …) — dogfood the manifest path.
|
||||||
d = Path(builtin_dir) if builtin_dir else Path(__file__).parent / "builtin"
|
d = Path(builtin_dir) if builtin_dir else Path(__file__).parent / "builtin"
|
||||||
@@ -219,15 +226,16 @@ class PersonaRegistry:
|
|||||||
return self._entries.get(persona_id)
|
return self._entries.get(persona_id)
|
||||||
|
|
||||||
def is_enabled(self, persona_id: str) -> bool:
|
def is_enabled(self, persona_id: str) -> bool:
|
||||||
# Explicit state (either way) always wins. Absent a user choice, BUILT-IN personas
|
# Explicit state (either way) always wins. Absent a user choice, the entry's
|
||||||
# ship enabled — the composer picker is their front door (UX-029, supersedes the
|
# default applies: builtins ship enabled — the composer picker is their front door
|
||||||
# 2026-07-09 Coworker-only default that fit the old hidden ▾ menu). Installed
|
# (UX-029, supersedes the 2026-07-09 Coworker-only default that fit the old hidden
|
||||||
# third-party personas stay disabled until the user consents from the risk screen.
|
# ▾ menu) — except retired ones (Chat). Installed third-party personas stay
|
||||||
|
# disabled until the user consents from the risk screen.
|
||||||
if persona_id in self._enabled:
|
if persona_id in self._enabled:
|
||||||
return bool(self._enabled[persona_id])
|
return bool(self._enabled[persona_id])
|
||||||
entry = self._entries.get(persona_id)
|
entry = self._entries.get(persona_id)
|
||||||
if entry is not None and entry.builtin:
|
if entry is not None and entry.builtin:
|
||||||
return True
|
return entry.default_enabled
|
||||||
return persona_id == self._default or persona_id == DEFAULT_PERSONA_ID
|
return persona_id == self._default or persona_id == DEFAULT_PERSONA_ID
|
||||||
|
|
||||||
def is_surfaced(self, persona_id: str) -> bool:
|
def is_surfaced(self, persona_id: str) -> bool:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ const PERSONAS = {
|
|||||||
personas: [
|
personas: [
|
||||||
{ id: "cowork", name: "OpenWorker", icon: "cowork", tagline: "Produce a deliverable — research, analysis, scripts", needs_workspace: true, builtin: true, family: "knowledge", workspace: "deliverable", tools: ["files", "search"], enabled: true, surfaced: true, default: true },
|
{ id: "cowork", name: "OpenWorker", icon: "cowork", tagline: "Produce a deliverable — research, analysis, scripts", needs_workspace: true, builtin: true, family: "knowledge", workspace: "deliverable", tools: ["files", "search"], enabled: true, surfaced: true, default: true },
|
||||||
{ id: "code", name: "Code", icon: "code", tagline: "Work in a codebase — files, git, shell", needs_workspace: true, builtin: true, family: "code", workspace: "git", tools: ["code_files", "git"], enabled: true, surfaced: true, default: false },
|
{ id: "code", name: "Code", icon: "code", tagline: "Work in a codebase — files, git, shell", needs_workspace: true, builtin: true, family: "code", workspace: "git", tools: ["code_files", "git"], enabled: true, surfaced: true, default: false },
|
||||||
{ id: "chat", name: "Chat", icon: "chat", tagline: "Quick questions — no workspace", needs_workspace: false, builtin: true, family: "knowledge", workspace: "none", tools: [], enabled: true, surfaced: false, default: false },
|
{ id: "chat", name: "Chat", icon: "chat", tagline: "Quick questions — no workspace", needs_workspace: false, builtin: true, family: "knowledge", workspace: "none", tools: [], enabled: false, surfaced: false, default: false },
|
||||||
{ id: "ops", name: "Ops Coworker", icon: "wrench", tagline: "Operate and investigate — runbooks, logs, infrastructure", needs_workspace: true, builtin: true, family: "knowledge", workspace: "deliverable", tools: ["files", "shell"], enabled: true, surfaced: true, default: false },
|
{ id: "ops", name: "Ops Coworker", icon: "wrench", tagline: "Operate and investigate — runbooks, logs, infrastructure", needs_workspace: true, builtin: true, family: "knowledge", workspace: "deliverable", tools: ["files", "shell"], enabled: true, surfaced: true, default: false },
|
||||||
// A non-builtin install (disabled pending consent — invisible to picker specs) so the
|
// A non-builtin install (disabled pending consent — invisible to picker specs) so the
|
||||||
// Personas page's delete/enable affordances have a target.
|
// Personas page's delete/enable affordances have a target.
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ export function SendFolderDialog({ coworkerName, onPick, onTemp, onCancel }: Pro
|
|||||||
}}
|
}}
|
||||||
disabled={busy}
|
disabled={busy}
|
||||||
>
|
>
|
||||||
Start in a temporary folder
|
Use temporary folder
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{error && <div className="mt-2 text-[11.5px] text-warnInk">{error}</div>}
|
{error && <div className="mt-2 text-[11.5px] text-warnInk">{error}</div>}
|
||||||
|
|||||||
@@ -34,13 +34,15 @@ def test_sidebar_defaults_to_surfaced_builtins(tmp_path):
|
|||||||
assert "code" not in [e["name"] for e in reg.sidebar()]
|
assert "code" not in [e["name"] for e in reg.sidebar()]
|
||||||
|
|
||||||
|
|
||||||
def test_chat_hidden_by_default_but_resolvable(tmp_path):
|
def test_chat_retired_by_default_but_resolvable(tmp_path):
|
||||||
reg = _reg(tmp_path)
|
reg = _reg(tmp_path)
|
||||||
assert reg.is_surfaced("chat") is False # default-hidden from the grouped nav
|
# Chat is retired (owner call 2026-08-11): disabled + unsurfaced out of the box,
|
||||||
assert reg.is_enabled("chat") is True # builtins ship enabled (UX-029)
|
# unlike the other builtins — Coworker covers quick Q&A.
|
||||||
|
assert reg.is_enabled("chat") is False
|
||||||
|
assert reg.is_surfaced("chat") is False
|
||||||
assert reg.agent("chat").name == "chat" # live sessions keep resolving
|
assert reg.agent("chat").name == "chat" # live sessions keep resolving
|
||||||
# Surfacing it adds it to the sidebar picker too.
|
# Still recoverable from Settings ▸ Coworkers (enable implies surface).
|
||||||
reg.set_surfaced("chat", True)
|
reg.set_enabled("chat", True)
|
||||||
assert "chat" in [e["name"] for e in reg.sidebar()]
|
assert "chat" in [e["name"] for e in reg.sidebar()]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user