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
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: function ChannelAccess
created: 2026-10-15T05:30:36Z
norm: '1'
sig: d9bb90f2534024f3
body_hash: d071cf690a8fbaab
raw_hash: 5df6168beeb7cbb9
vouched: 2026-10-16T21:22:40Z
body_hash: 5fc15bf520c64275
raw_hash: 45ec44a5e2b7a0ee
vouched: 2026-10-22T14:31:33Z
lines: 63-136
---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ anchor: function main
created: 2026-10-15T04:47:23Z
norm: '1'
sig: 7d5d3f63a3bddde4
body_hash: 33d554e96630bf6d
raw_hash: f8aa0b8e89a37828
vouched: 2026-10-20T17:10:51Z
body_hash: fd9cef47cdc61076
raw_hash: e9c988923aa657a6
vouched: 2026-10-22T14:31:33Z
lines: 253-482
---

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
id: 01M5J0BZWP8SCBK522YT2HMZDZ
anchor: function main
created: 2026-10-22T14:31:16Z
norm: '1'
sig: 7d5d3f63a3bddde4
body_hash: fd9cef47cdc61076
raw_hash: e9c988923aa657a6
lines: 332-614
---

ace info prints agent access as `agents`; `shared` stays the wire and storage name
14 changes: 7 additions & 7 deletions apps/app/src/channel-access.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,23 +21,23 @@ import { host } from "./host";
import { placement } from "./placement";

/**
* What collaborators' runs can reach. A cloud container has no desktop tools and no macOS
* What teammates' runs can reach. A cloud container has no desktop tools and no macOS
* permissions, so its copy names neither.
*/
function reach(channel: Listing): { agents: string; confirm: string; desktop: boolean } {
const place = placement(channel);
if (place.kind === "cloud") {
return {
agents:
`Allow collaborators to invoke agents, which run commands in this channel's cloud container on ${place.service}, with its GitHub access to the repository. No macOS permissions apply. Turning this off leaves chat available and active runs continue.`,
`Let teammates invoke this channel's agents, which run commands in this channel's cloud container on ${place.service}, with its GitHub access to the repository. No macOS permissions apply. Turning this off leaves chat available and active runs continue.`,
confirm:
"Teammates can run commands in this channel's cloud container, with its GitHub access to the repository. No macOS permissions apply.",
desktop: false,
};
}
return {
agents:
"Allow collaborators to invoke agents, which run commands as you on this machine with Ace's macOS permissions, such as Screen Recording. Turning this off leaves chat available and active runs continue.",
"Let teammates invoke this channel's agents, which run commands as you on this machine with Ace's macOS permissions, such as Screen Recording. Turning this off leaves chat available and active runs continue.",
confirm:
"Teammates can run commands as you on this machine, with Ace's macOS permissions such as Screen Recording.",
desktop: true,
Expand Down Expand Up @@ -102,7 +102,7 @@ function AccessToggle({ label, value, live, owner, pending, description, childre
</span>
}
/>
<TooltipContent className="max-w-72">
<TooltipContent className="max-w-72 flex-col items-start">
<p className="font-medium">{label}: {state}</p>
<p>{description}</p>
{!owner && <p>Only the channel owner can change this.</p>}
Expand Down Expand Up @@ -146,7 +146,7 @@ export function ChannelAccess({ channel, user }: { channel: Listing; user: strin
>
<Machine channel={channel} />
<AccessToggle
label="Collaborator agent access"
label="Agent access"
value={shared}
live={sharedLive}
owner={owner}
Expand All @@ -163,7 +163,7 @@ export function ChannelAccess({ channel, user }: { channel: Listing; user: strin
live={desktopLive}
owner={owner}
pending={changingDesktop}
description="Allow native computer use in this channel, including owner and collaborator runs. Turning this off refuses new desktop calls only. Shell commands keep Ace's macOS permissions, such as Screen Recording, so this isn't a security boundary."
description="Allow native computer use in this channel, including the owner's and teammates' runs. Turning this off refuses new desktop calls only. Shell commands keep Ace's macOS permissions, such as Screen Recording, so this isn't a security boundary."
onClick={computer}
>
<IconMonitor aria-hidden />
Expand All @@ -172,7 +172,7 @@ export function ChannelAccess({ channel, user }: { channel: Listing; user: strin
<Dialog open={confirming} onOpenChange={setConfirming}>
<DialogContent className="gap-3 sm:max-w-80" showCloseButton={false}>
<DialogHeader>
<DialogTitle>Let collaborators invoke agents?</DialogTitle>
<DialogTitle>Let teammates invoke agents?</DialogTitle>
<DialogDescription>
{copy.confirm}
{copy.desktop && desktop
Expand Down
1 change: 1 addition & 0 deletions apps/app/src/open-project.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ export function OpenProject({ onOpen, onClose }: {
{error && <p role="alert" className="mt-2 text-xs text-destructive">{error}</p>}
</form>
<DialogFooter>
<Button variant="outline" disabled={busy} onClick={onClose}>Cancel</Button>
<Button type="submit" form={id} disabled={busy || !path.trim()}>
{busy ? "Opening…" : "Open folder"}
</Button>
Expand Down
20 changes: 12 additions & 8 deletions apps/app/src/palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,13 @@ const ACTIONS = [
action("dashboard", "Dashboard", "Go to", "1"),
action("channels", "Channels", "Go to", "2"),
action("issues", "Issues", "Go to", "3"),
action("prs", "Pull Requests", "Go to", "4"),
action("project-open", "Open Folder…", "Project", "o"),
action("prs", "Pull requests", "Go to", "4"),
action("project-open", "Open folder…", "Project", "o"),
action("project-clone", "Clone from URL…", "Project"),
action("settings", "Settings", "Ace", ","),
...(desktop ? [action("updates", "Check for Updates…", "Ace")] : []),
action("nav-toggle", "Toggle Navigation", "View", "b"),
action("channels-toggle", "Toggle Channels Sidebar", "View", "b", true),
...(desktop ? [action("updates", "Check for updates…", "Ace")] : []),
action("nav-toggle", "Toggle navigation", "View", "b"),
action("channels-toggle", "Toggle channels sidebar", "View", "b", true),
];

const CREATE = "channel-new";
Expand All @@ -52,6 +52,7 @@ function channelItems(
const names = new Map(projects.map((project) => [project.id, label(project, host)]));
return channels.map((channel) => {
const project = channelProject(channel);
const outcome = attention.get(channel.id);
return {
kind: "session",
id: `channel:${channel.id}`,
Expand All @@ -61,9 +62,12 @@ function channelItems(
archived: channel.state === "archived",
// Other projects' channels wait for a search so the current project's stay in reach.
hiddenUntilSearch: project !== current,
status: channel.state === "running" && channel.busy
// A run waiting on a question is still busy, but it waits on them, as in the sidebar.
status: outcome === "question" || outcome === "failed"
? "attention"
: channel.state === "running" && channel.busy
? "busy"
: attention.get(channel.id) === "done"
: outcome === "done"
? "unread"
: undefined,
};
Expand Down Expand Up @@ -112,7 +116,7 @@ export function Palette({ channels, projects, host, project, attention, onChanne

if (!open) return null;
const items: CommandPaletteItem[] = [
...(onCreate ? [action(CREATE, `New Channel in ${onCreate.name}`, "Channel")] : []),
...(onCreate ? [action(CREATE, `New channel in ${onCreate.name}`, "Channel")] : []),
...ACTIONS,
...tabs,
...channelItems(channels, projects, host, project, attention),
Expand Down
7 changes: 5 additions & 2 deletions apps/host/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -510,8 +510,11 @@ async function main() {
const { name, session } = await join(ref);
try {
switch (command) {
case "info":
return console.log(JSON.stringify(await session.request({ op: "info" }), null, 2));
case "info": {
// docs/terms.md names it agent access, after `ace agents`; `shared` is the wire name.
const { shared, ...info } = await session.request<ChannelInfo>({ op: "info" });
return console.log(JSON.stringify({ ...info, agents: shared }, null, 2));
}
case "say":
if (!text) throw new Error("Nothing to say");
await session.request({ op: "say", chat: chat(), author: catalog.user, text });
Expand Down
10 changes: 6 additions & 4 deletions packages/ui/src/components/command-palette/command-palette.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ function shortcutLabel(shortcut?: CommandPaletteShortcut) {
function statusLabel(item: CommandPaletteItem) {
if (item.kind !== "session") return shortcutLabel(item.shortcut);
if (item.archived) return "Archived";
if (item.status === "attention") return "Needs you";
if (item.status === "mention") return "Mention";
if (item.status === "unread") return "Unread";
if (item.status === "busy") return "Busy";
Expand Down Expand Up @@ -163,15 +164,15 @@ function Palette({ items, onOpenChange, onSelect, placeholder, emptyLabel, title
aria-activedescendant={results[index]
? `command-palette-${results[index]!.item.id}`
: undefined}
className="block inline-size-full bg-transparent px-4 py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground"
className="block w-full bg-transparent px-4 py-3 text-sm text-foreground outline-none placeholder:text-muted-foreground"
/>
</div>

<div
ref={list}
id="command-palette-results"
role="listbox"
className="min-block-size-0 overflow-y-auto p-2 overscroll-contain"
className="min-h-0 overflow-y-auto p-2 overscroll-contain"
>
{results.length
? (
Expand All @@ -190,7 +191,7 @@ function Palette({ items, onOpenChange, onSelect, placeholder, emptyLabel, title
onClick={() => choose(item)}
onPointerEnter={() => !item.disabled && setSelected(item.id)}
className={cn(
"group grid inline-size-full grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-md px-2 py-1.5 text-left text-sm/5 outline-none transition-[background-color,color,transform] duration-150 ease-out",
"group grid w-full grid-cols-[minmax(0,1fr)_auto] items-center gap-3 rounded-md px-2 py-1.5 text-left text-sm/5 outline-none transition-[background-color,color,transform] duration-150 ease-out",
selected && "bg-accent text-accent-foreground",
!selected && "text-foreground",
item.disabled && "cursor-not-allowed opacity-45",
Expand All @@ -211,7 +212,8 @@ function Palette({ items, onOpenChange, onSelect, placeholder, emptyLabel, title
<span
className={cn(
"shrink-0 rounded border border-border/70 bg-background/80 px-1.5 py-0.5 font-mono text-[0.625rem]/none text-muted-foreground",
item.kind === "session" && item.status === "mention"
item.kind === "session"
&& (item.status === "mention" || item.status === "attention")
&& "border-amber-500/30 text-amber-600 dark:text-amber-300",
item.kind === "session" && item.status === "unread"
&& "border-sky-500/30 text-sky-600 dark:text-sky-300",
Expand Down
2 changes: 1 addition & 1 deletion packages/ui/src/components/command-palette/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export type CommandPaletteCommandItem = {
hiddenUntilSearch?: boolean;
};

export type CommandPaletteSessionStatus = "idle" | "busy" | "unread" | "mention";
export type CommandPaletteSessionStatus = "idle" | "busy" | "unread" | "mention" | "attention";

export type CommandPaletteSessionItem = {
kind: "session";
Expand Down