@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.
Files changed (46) hide show
  1. package/app/page.tsx +3 -8
  2. package/components/Bookmarks/ChatBookmarksButton.tsx +4 -0
  3. package/components/Bus/MessagesPanel.tsx +155 -0
  4. package/components/Bus/index.ts +1 -2
  5. package/components/Chat/ChatPanel.tsx +9 -1
  6. package/components/CommandPalette/useAppCommands.ts +12 -4
  7. package/components/Features/FeaturesSection.tsx +2 -2
  8. package/components/Inbox/InboxButton.tsx +39 -0
  9. package/components/Inbox/InboxDialog.tsx +139 -0
  10. package/components/Inbox/NeedsYouList.tsx +120 -0
  11. package/components/Inbox/index.ts +2 -0
  12. package/components/LumifyHub/index.ts +0 -1
  13. package/components/Pane/DesktopTabBar.tsx +2 -0
  14. package/components/Pane/PaneMenu.test.ts +118 -0
  15. package/components/Pane/PaneMenu.tsx +37 -0
  16. package/components/SessionList/SidebarHeader.tsx +7 -4
  17. package/components/Tasks/TaskRow.tsx +3 -3
  18. package/components/Tasks/TasksPanel.tsx +63 -0
  19. package/components/Tasks/index.ts +1 -1
  20. package/components/views/DesktopAppActions.tsx +10 -63
  21. package/components/views/DesktopView.tsx +1 -11
  22. package/hooks/useInbox.ts +69 -0
  23. package/lib/env-setup.ts +27 -12
  24. package/lib/inbox/counts.ts +140 -0
  25. package/lib/project-config/agentos.schema.json +14 -0
  26. package/lib/project-config/schema.ts +10 -0
  27. package/lib/worktree-deps/caches.ts +250 -0
  28. package/lib/worktree-deps/clone.ts +94 -9
  29. package/lib/worktree-deps/fastcopy.ts +89 -0
  30. package/lib/worktree-deps/index.ts +14 -5
  31. package/lib/worktree-deps/paths.ts +20 -0
  32. package/lib/worktree-deps/spare.ts +26 -8
  33. package/package.json +1 -1
  34. package/scripts/bench/worktree-setup.ts +101 -0
  35. package/scripts/check-bundle.mjs +25 -10
  36. package/scripts/check-bundle.test.ts +2 -2
  37. package/stores/inboxUi.ts +60 -0
  38. package/stores/needsYouFocus.ts +11 -3
  39. package/components/Bus/MessagesButton.tsx +0 -25
  40. package/components/Bus/MessagesDialog.tsx +0 -176
  41. package/components/LumifyHub/Docs/DocsButton.tsx +0 -31
  42. package/components/NotificationsMenu.tsx +0 -86
  43. package/components/Tasks/TasksButton.tsx +0 -28
  44. package/components/Tasks/TasksDialog.tsx +0 -75
  45. package/stores/busUi.ts +0 -12
  46. 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
@@ -1,12 +0,0 @@
1
- import { proxy } from "valtio";
2
-
3
- export const busUi = proxy({ open: false });
4
-
5
- export const busUiActions = {
6
- setOpen: (open: boolean) => {
7
- busUi.open = open;
8
- },
9
- open: () => {
10
- busUi.open = true;
11
- },
12
- };
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
- };