@saadnvd1/agent-os 0.3.603 → 0.3.606
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/page.tsx +3 -8
- package/components/Bookmarks/ChatBookmarksButton.tsx +4 -0
- package/components/Bus/MessagesPanel.tsx +155 -0
- package/components/Bus/index.ts +1 -2
- package/components/Chat/ChatPanel.tsx +9 -1
- package/components/CommandPalette/useAppCommands.ts +12 -4
- package/components/Features/FeaturesSection.tsx +2 -2
- package/components/Inbox/InboxButton.tsx +39 -0
- package/components/Inbox/InboxDialog.tsx +139 -0
- package/components/Inbox/NeedsYouList.tsx +120 -0
- package/components/Inbox/index.ts +2 -0
- package/components/LumifyHub/index.ts +0 -1
- package/components/Pane/DesktopTabBar.tsx +2 -0
- package/components/Pane/PaneMenu.test.ts +118 -0
- package/components/Pane/PaneMenu.tsx +37 -0
- package/components/SessionList/SidebarHeader.tsx +7 -4
- package/components/Tasks/TaskRow.tsx +3 -3
- package/components/Tasks/TasksPanel.tsx +63 -0
- package/components/Tasks/index.ts +1 -1
- package/components/views/DesktopAppActions.tsx +10 -63
- package/components/views/DesktopView.tsx +1 -11
- package/hooks/useInbox.ts +69 -0
- package/lib/env-setup.ts +27 -12
- package/lib/inbox/counts.ts +140 -0
- package/lib/project-config/agentos.schema.json +14 -0
- package/lib/project-config/schema.ts +10 -0
- package/lib/worktree-deps/caches.ts +250 -0
- package/lib/worktree-deps/clone.ts +94 -9
- package/lib/worktree-deps/fastcopy.ts +89 -0
- package/lib/worktree-deps/index.ts +14 -5
- package/lib/worktree-deps/paths.ts +20 -0
- package/lib/worktree-deps/spare.ts +26 -8
- package/package.json +1 -1
- package/scripts/bench/worktree-setup.ts +101 -0
- package/scripts/check-bundle.mjs +25 -10
- package/scripts/check-bundle.test.ts +2 -2
- package/stores/inboxUi.ts +60 -0
- package/stores/needsYouFocus.ts +11 -3
- package/components/Bus/MessagesButton.tsx +0 -25
- package/components/Bus/MessagesDialog.tsx +0 -176
- package/components/LumifyHub/Docs/DocsButton.tsx +0 -31
- package/components/NotificationsMenu.tsx +0 -86
- package/components/Tasks/TasksButton.tsx +0 -28
- package/components/Tasks/TasksDialog.tsx +0 -75
- package/stores/busUi.ts +0 -12
- package/stores/tasksUi.ts +0 -14
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useMemo, useState } from "react";
|
|
4
|
-
import { useSnapshot } from "valtio";
|
|
5
|
-
import {
|
|
6
|
-
Dialog,
|
|
7
|
-
DialogContent,
|
|
8
|
-
DialogDescription,
|
|
9
|
-
DialogHeader,
|
|
10
|
-
DialogTitle,
|
|
11
|
-
} from "@/components/ui/dialog";
|
|
12
|
-
import { Button } from "@/components/ui/button";
|
|
13
|
-
import { Textarea } from "@/components/ui/textarea";
|
|
14
|
-
import {
|
|
15
|
-
Select,
|
|
16
|
-
SelectContent,
|
|
17
|
-
SelectItem,
|
|
18
|
-
SelectTrigger,
|
|
19
|
-
SelectValue,
|
|
20
|
-
} from "@/components/ui/select";
|
|
21
|
-
import { useBusMessages, useBusPeers, useSendMessage } from "@/data/bus";
|
|
22
|
-
import { compactTimeAgo, fromSqliteTime } from "@/lib/session-meta";
|
|
23
|
-
import { busUi, busUiActions } from "@/stores/busUi";
|
|
24
|
-
import { cn } from "@/lib/utils";
|
|
25
|
-
|
|
26
|
-
const ALL = "all";
|
|
27
|
-
|
|
28
|
-
export function MessagesDialog() {
|
|
29
|
-
const { open } = useSnapshot(busUi);
|
|
30
|
-
const { data: messages = [], isPending } = useBusMessages(open);
|
|
31
|
-
const { data: peers = [] } = useBusPeers(open);
|
|
32
|
-
const send = useSendMessage();
|
|
33
|
-
const [filter, setFilter] = useState(ALL);
|
|
34
|
-
const [to, setTo] = useState("");
|
|
35
|
-
const [body, setBody] = useState("");
|
|
36
|
-
|
|
37
|
-
const label = (p: { name: string; projectName: string | null }) =>
|
|
38
|
-
p.projectName ? `${p.projectName} / ${p.name}` : p.name;
|
|
39
|
-
const shown = useMemo(
|
|
40
|
-
() =>
|
|
41
|
-
filter === ALL
|
|
42
|
-
? messages
|
|
43
|
-
: messages.filter((m) => m.fromId === filter || m.toId === filter),
|
|
44
|
-
[messages, filter]
|
|
45
|
-
);
|
|
46
|
-
const recipient = to || (filter !== ALL ? filter : "");
|
|
47
|
-
|
|
48
|
-
return (
|
|
49
|
-
<Dialog
|
|
50
|
-
open={open}
|
|
51
|
-
onOpenChange={(o) => {
|
|
52
|
-
// A delivery result belongs to that send only.
|
|
53
|
-
if (!o) send.reset();
|
|
54
|
-
busUiActions.setOpen(o);
|
|
55
|
-
}}
|
|
56
|
-
>
|
|
57
|
-
<DialogContent className="flex max-h-[85vh] max-w-2xl flex-col">
|
|
58
|
-
<DialogHeader>
|
|
59
|
-
<DialogTitle>Messages</DialogTitle>
|
|
60
|
-
<DialogDescription>
|
|
61
|
-
What your agent sessions are saying to each other. Agents use the{" "}
|
|
62
|
-
<code className="font-mono text-xs">aos</code> command.
|
|
63
|
-
</DialogDescription>
|
|
64
|
-
</DialogHeader>
|
|
65
|
-
|
|
66
|
-
<Select
|
|
67
|
-
value={filter}
|
|
68
|
-
onValueChange={(v) => {
|
|
69
|
-
send.reset();
|
|
70
|
-
setFilter(v);
|
|
71
|
-
}}
|
|
72
|
-
>
|
|
73
|
-
<SelectTrigger
|
|
74
|
-
aria-label="Filter by session"
|
|
75
|
-
className="w-full sm:w-72"
|
|
76
|
-
>
|
|
77
|
-
<SelectValue />
|
|
78
|
-
</SelectTrigger>
|
|
79
|
-
<SelectContent>
|
|
80
|
-
<SelectItem value={ALL}>All sessions</SelectItem>
|
|
81
|
-
{peers.map((p) => (
|
|
82
|
-
<SelectItem key={p.id} value={p.id}>
|
|
83
|
-
{label(p)}
|
|
84
|
-
</SelectItem>
|
|
85
|
-
))}
|
|
86
|
-
</SelectContent>
|
|
87
|
-
</Select>
|
|
88
|
-
|
|
89
|
-
<div className="min-h-40 flex-1 space-y-3 overflow-y-auto pr-1">
|
|
90
|
-
{isPending && (
|
|
91
|
-
<div className="bg-muted/40 h-16 animate-pulse rounded-xl" />
|
|
92
|
-
)}
|
|
93
|
-
{!isPending && shown.length === 0 && (
|
|
94
|
-
<p className="text-muted-foreground py-8 text-center text-sm">
|
|
95
|
-
No messages yet.
|
|
96
|
-
</p>
|
|
97
|
-
)}
|
|
98
|
-
{shown.map((m) => (
|
|
99
|
-
<div key={m.id} className="space-y-1">
|
|
100
|
-
<div className="flex items-baseline gap-2 text-xs">
|
|
101
|
-
<span
|
|
102
|
-
className={cn("font-medium", !m.fromId && "text-primary")}
|
|
103
|
-
>
|
|
104
|
-
{m.fromName}
|
|
105
|
-
</span>
|
|
106
|
-
<span className="text-muted-foreground/60">→</span>
|
|
107
|
-
<span className="font-medium">{m.toName}</span>
|
|
108
|
-
<span className="text-muted-foreground/60 ml-auto font-mono tabular-nums">
|
|
109
|
-
{compactTimeAgo(fromSqliteTime(m.createdAt))}
|
|
110
|
-
</span>
|
|
111
|
-
</div>
|
|
112
|
-
<p className="bg-foreground/[0.04] rounded-lg px-3 py-2 text-sm whitespace-pre-wrap">
|
|
113
|
-
{m.body}
|
|
114
|
-
</p>
|
|
115
|
-
</div>
|
|
116
|
-
))}
|
|
117
|
-
</div>
|
|
118
|
-
|
|
119
|
-
<form
|
|
120
|
-
onSubmit={(e) => {
|
|
121
|
-
e.preventDefault();
|
|
122
|
-
send.mutate(
|
|
123
|
-
{ to: recipient, body },
|
|
124
|
-
{ onSuccess: () => setBody("") }
|
|
125
|
-
);
|
|
126
|
-
}}
|
|
127
|
-
className="space-y-2"
|
|
128
|
-
>
|
|
129
|
-
<Select
|
|
130
|
-
value={recipient}
|
|
131
|
-
onValueChange={(v) => {
|
|
132
|
-
send.reset();
|
|
133
|
-
setTo(v);
|
|
134
|
-
}}
|
|
135
|
-
>
|
|
136
|
-
<SelectTrigger aria-label="Send to">
|
|
137
|
-
<SelectValue placeholder="Send to a session..." />
|
|
138
|
-
</SelectTrigger>
|
|
139
|
-
<SelectContent>
|
|
140
|
-
{peers.map((p) => (
|
|
141
|
-
<SelectItem key={p.id} value={p.id}>
|
|
142
|
-
{label(p)}
|
|
143
|
-
{!p.running && " (not running)"}
|
|
144
|
-
</SelectItem>
|
|
145
|
-
))}
|
|
146
|
-
</SelectContent>
|
|
147
|
-
</Select>
|
|
148
|
-
<div className="flex gap-2">
|
|
149
|
-
<Textarea
|
|
150
|
-
aria-label="Message"
|
|
151
|
-
value={body}
|
|
152
|
-
onChange={(e) => setBody(e.target.value)}
|
|
153
|
-
placeholder="Message as you"
|
|
154
|
-
rows={2}
|
|
155
|
-
className="min-h-11"
|
|
156
|
-
/>
|
|
157
|
-
<Button
|
|
158
|
-
type="submit"
|
|
159
|
-
disabled={!recipient || !body.trim() || send.isPending}
|
|
160
|
-
>
|
|
161
|
-
Send
|
|
162
|
-
</Button>
|
|
163
|
-
</div>
|
|
164
|
-
{send.error && (
|
|
165
|
-
<p className="text-destructive text-xs">{send.error.message}</p>
|
|
166
|
-
)}
|
|
167
|
-
{send.data?.delivery.state === "failed" && (
|
|
168
|
-
<p className="text-destructive text-xs">
|
|
169
|
-
Saved to its inbox, but not delivered: {send.data.delivery.why}
|
|
170
|
-
</p>
|
|
171
|
-
)}
|
|
172
|
-
</form>
|
|
173
|
-
</DialogContent>
|
|
174
|
-
</Dialog>
|
|
175
|
-
);
|
|
176
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { BookOpen } from "lucide-react";
|
|
4
|
-
import { Button } from "@/components/ui/button";
|
|
5
|
-
import { useDocsWorkspace } from "@/data/lumifyhub/docs";
|
|
6
|
-
import { docsUiActions } from "@/stores/docsUi";
|
|
7
|
-
|
|
8
|
-
// Only for a project whose workspace is linked to LumifyHub.
|
|
9
|
-
export function DocsButton({
|
|
10
|
-
projectId,
|
|
11
|
-
labelClassName,
|
|
12
|
-
}: {
|
|
13
|
-
projectId?: string | null;
|
|
14
|
-
labelClassName?: string;
|
|
15
|
-
}) {
|
|
16
|
-
const workspace = useDocsWorkspace(projectId);
|
|
17
|
-
if (!workspace) return null;
|
|
18
|
-
return (
|
|
19
|
-
<Button
|
|
20
|
-
size="sm"
|
|
21
|
-
variant="ghost"
|
|
22
|
-
aria-label="Docs"
|
|
23
|
-
title="Docs"
|
|
24
|
-
className="h-7 px-2"
|
|
25
|
-
onClick={() => docsUiActions.open(workspace.id)}
|
|
26
|
-
>
|
|
27
|
-
<BookOpen className="h-4 w-4" />
|
|
28
|
-
<span className={labelClassName}>Docs</span>
|
|
29
|
-
</Button>
|
|
30
|
-
);
|
|
31
|
-
}
|
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { AlertCircle, Bell, Settings } from "lucide-react";
|
|
5
|
-
import { Button } from "./ui/button";
|
|
6
|
-
import {
|
|
7
|
-
DropdownMenu,
|
|
8
|
-
DropdownMenuContent,
|
|
9
|
-
DropdownMenuItem,
|
|
10
|
-
DropdownMenuLabel,
|
|
11
|
-
DropdownMenuSeparator,
|
|
12
|
-
DropdownMenuTrigger,
|
|
13
|
-
} from "./ui/dropdown-menu";
|
|
14
|
-
import { cn } from "@/lib/utils";
|
|
15
|
-
import type { NotificationSettings } from "@/lib/notifications";
|
|
16
|
-
import { settingsUiActions } from "@/stores/settingsUi";
|
|
17
|
-
|
|
18
|
-
interface WaitingSession {
|
|
19
|
-
id: string;
|
|
20
|
-
name: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// The bell: sessions waiting for you, and the way to Settings > Notifications.
|
|
24
|
-
export function NotificationsMenu({
|
|
25
|
-
settings,
|
|
26
|
-
waitingSessions = [],
|
|
27
|
-
onSelectSession,
|
|
28
|
-
}: {
|
|
29
|
-
settings: NotificationSettings;
|
|
30
|
-
waitingSessions?: WaitingSession[];
|
|
31
|
-
onSelectSession?: (id: string) => void;
|
|
32
|
-
}) {
|
|
33
|
-
const [open, setOpen] = useState(false);
|
|
34
|
-
const waitingCount = waitingSessions.length;
|
|
35
|
-
|
|
36
|
-
return (
|
|
37
|
-
<DropdownMenu open={open} onOpenChange={setOpen}>
|
|
38
|
-
<DropdownMenuTrigger asChild>
|
|
39
|
-
<Button
|
|
40
|
-
variant="ghost"
|
|
41
|
-
size="icon-sm"
|
|
42
|
-
aria-label="Notifications"
|
|
43
|
-
className="relative h-7 w-7"
|
|
44
|
-
>
|
|
45
|
-
<Bell
|
|
46
|
-
className={cn(
|
|
47
|
-
"h-4 w-4",
|
|
48
|
-
!settings.sound && "text-muted-foreground"
|
|
49
|
-
)}
|
|
50
|
-
/>
|
|
51
|
-
{waitingCount > 0 && (
|
|
52
|
-
<span className="absolute -top-1 -right-1 flex h-4 w-4 items-center justify-center rounded-full bg-yellow-500 text-[10px] font-bold text-yellow-950">
|
|
53
|
-
{waitingCount}
|
|
54
|
-
</span>
|
|
55
|
-
)}
|
|
56
|
-
</Button>
|
|
57
|
-
</DropdownMenuTrigger>
|
|
58
|
-
<DropdownMenuContent align="end" className="w-52">
|
|
59
|
-
{waitingCount > 0 && (
|
|
60
|
-
<>
|
|
61
|
-
<DropdownMenuLabel className="flex items-center gap-2 text-xs text-yellow-500">
|
|
62
|
-
<AlertCircle className="h-3 w-3" />
|
|
63
|
-
Waiting for input
|
|
64
|
-
</DropdownMenuLabel>
|
|
65
|
-
{waitingSessions.map((session) => (
|
|
66
|
-
<DropdownMenuItem
|
|
67
|
-
key={session.id}
|
|
68
|
-
onClick={() => onSelectSession?.(session.id)}
|
|
69
|
-
className="text-sm"
|
|
70
|
-
>
|
|
71
|
-
{session.name}
|
|
72
|
-
</DropdownMenuItem>
|
|
73
|
-
))}
|
|
74
|
-
<DropdownMenuSeparator />
|
|
75
|
-
</>
|
|
76
|
-
)}
|
|
77
|
-
<DropdownMenuItem
|
|
78
|
-
onClick={() => settingsUiActions.open("notifications")}
|
|
79
|
-
>
|
|
80
|
-
<Settings className="h-3 w-3" />
|
|
81
|
-
Notification settings
|
|
82
|
-
</DropdownMenuItem>
|
|
83
|
-
</DropdownMenuContent>
|
|
84
|
-
</DropdownMenu>
|
|
85
|
-
);
|
|
86
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { ListTodo } from "lucide-react";
|
|
4
|
-
import { Button } from "@/components/ui/button";
|
|
5
|
-
import { tasksUiActions } from "@/stores/tasksUi";
|
|
6
|
-
import { useTasksNeedingYou } from "./useTasksNeedingYou";
|
|
7
|
-
|
|
8
|
-
export function TasksButton({ labelClassName }: { labelClassName?: string }) {
|
|
9
|
-
const waiting = useTasksNeedingYou();
|
|
10
|
-
return (
|
|
11
|
-
<Button
|
|
12
|
-
size="sm"
|
|
13
|
-
variant="ghost"
|
|
14
|
-
aria-label="Tasks"
|
|
15
|
-
title="Tasks"
|
|
16
|
-
className="h-7 px-2"
|
|
17
|
-
onClick={tasksUiActions.openPanel}
|
|
18
|
-
>
|
|
19
|
-
<ListTodo className="h-4 w-4" />
|
|
20
|
-
<span className={labelClassName}>Tasks</span>
|
|
21
|
-
{waiting > 0 && (
|
|
22
|
-
<span className="bg-primary text-primary-foreground rounded-full px-1.5 font-mono text-[10px] leading-4 tabular-nums">
|
|
23
|
-
{waiting}
|
|
24
|
-
</span>
|
|
25
|
-
)}
|
|
26
|
-
</Button>
|
|
27
|
-
);
|
|
28
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
"use client";
|
|
2
|
-
|
|
3
|
-
import { useSnapshot } from "valtio";
|
|
4
|
-
import { Plus } from "lucide-react";
|
|
5
|
-
import {
|
|
6
|
-
Dialog,
|
|
7
|
-
DialogContent,
|
|
8
|
-
DialogHeader,
|
|
9
|
-
DialogTitle,
|
|
10
|
-
} from "@/components/ui/dialog";
|
|
11
|
-
import { Button } from "@/components/ui/button";
|
|
12
|
-
import { useTasksQuery } from "@/data/tasks";
|
|
13
|
-
import { isFinished, needsHuman } from "@/lib/tasks/state";
|
|
14
|
-
import { tasksUi, tasksUiActions } from "@/stores/tasksUi";
|
|
15
|
-
import { newDraft } from "@/stores/drafts";
|
|
16
|
-
import { TaskRow } from "./TaskRow";
|
|
17
|
-
import { BoardTodoSection } from "@/components/LumifyHub";
|
|
18
|
-
import { StacksSection } from "@/components/Stacks";
|
|
19
|
-
import { FeaturesSection } from "@/components/Features";
|
|
20
|
-
|
|
21
|
-
export function TasksDialog() {
|
|
22
|
-
const { panelOpen } = useSnapshot(tasksUi);
|
|
23
|
-
const { data: tasks = [], isPending } = useTasksQuery();
|
|
24
|
-
const active = tasks.filter((t) => !isFinished(t.state));
|
|
25
|
-
// Whatever needs a human first.
|
|
26
|
-
active.sort(
|
|
27
|
-
(a, b) => Number(needsHuman(b.state)) - Number(needsHuman(a.state))
|
|
28
|
-
);
|
|
29
|
-
const done = tasks.filter((t) => !active.includes(t)).slice(0, 10);
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Dialog open={panelOpen} onOpenChange={tasksUiActions.setPanelOpen}>
|
|
33
|
-
<DialogContent className="max-h-[85vh] max-w-xl overflow-y-auto [&>*]:min-w-0">
|
|
34
|
-
<DialogHeader className="flex-row items-center justify-between gap-4 space-y-0">
|
|
35
|
-
<DialogTitle>Tasks</DialogTitle>
|
|
36
|
-
<Button
|
|
37
|
-
size="sm"
|
|
38
|
-
className="mr-6"
|
|
39
|
-
onClick={() => {
|
|
40
|
-
tasksUiActions.setPanelOpen(false);
|
|
41
|
-
newDraft({ kind: "current", openPr: true });
|
|
42
|
-
}}
|
|
43
|
-
>
|
|
44
|
-
<Plus className="h-4 w-4" />
|
|
45
|
-
New task
|
|
46
|
-
</Button>
|
|
47
|
-
</DialogHeader>
|
|
48
|
-
{isPending && (
|
|
49
|
-
<div className="bg-muted/40 h-16 animate-pulse rounded-xl" />
|
|
50
|
-
)}
|
|
51
|
-
{!isPending && tasks.length === 0 && (
|
|
52
|
-
<p className="text-muted-foreground py-6 text-center text-sm">
|
|
53
|
-
No tasks yet. Start one and keep working while it runs.
|
|
54
|
-
</p>
|
|
55
|
-
)}
|
|
56
|
-
<div className="space-y-2">
|
|
57
|
-
{active.map((t) => (
|
|
58
|
-
<TaskRow key={t.id} task={t} />
|
|
59
|
-
))}
|
|
60
|
-
</div>
|
|
61
|
-
<FeaturesSection />
|
|
62
|
-
<StacksSection />
|
|
63
|
-
<BoardTodoSection />
|
|
64
|
-
{done.length > 0 && (
|
|
65
|
-
<div className="space-y-2">
|
|
66
|
-
<p className="label-mono text-muted-foreground pt-2">Finished</p>
|
|
67
|
-
{done.map((t) => (
|
|
68
|
-
<TaskRow key={t.id} task={t} />
|
|
69
|
-
))}
|
|
70
|
-
</div>
|
|
71
|
-
)}
|
|
72
|
-
</DialogContent>
|
|
73
|
-
</Dialog>
|
|
74
|
-
);
|
|
75
|
-
}
|
package/stores/busUi.ts
DELETED
package/stores/tasksUi.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { proxy } from "valtio";
|
|
2
|
-
|
|
3
|
-
// The Tasks panel's open state, shared by the header and the sidebar menu.
|
|
4
|
-
// A new task is a draft with "Open a PR when done" (stores/drafts).
|
|
5
|
-
export const tasksUi = proxy({ panelOpen: false });
|
|
6
|
-
|
|
7
|
-
export const tasksUiActions = {
|
|
8
|
-
openPanel: () => {
|
|
9
|
-
tasksUi.panelOpen = true;
|
|
10
|
-
},
|
|
11
|
-
setPanelOpen: (open: boolean) => {
|
|
12
|
-
tasksUi.panelOpen = open;
|
|
13
|
-
},
|
|
14
|
-
};
|