bloby-bot 0.37.1 → 0.39.1
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/package.json +1 -1
- package/supervisor/harnesses/codex.ts +34 -0
- package/worker/prompts/bloby-system-prompt.txt +2 -2
- package/workspace/client/index.html +3 -0
- package/workspace/client/public/.well-known/assetlinks.json +8 -0
- package/workspace/client/public/bloby-cyberpunk.png +0 -0
- package/workspace/client/public/brand/blackrock.svg +8 -0
- package/workspace/client/public/kid-breakfast.png +0 -0
- package/workspace/client/public/wallpapers/bg.jpg +0 -0
- package/workspace/client/public/wallpapers/crypto_bg.png +0 -0
- package/workspace/client/public/wallpapers/wp-dusk.jpg +0 -0
- package/workspace/client/public/wallpapers/wp-mountain.jpg +0 -0
- package/workspace/client/public/wallpapers/wp-ocean.jpg +0 -0
- package/workspace/client/src/App.tsx +20 -9
- package/workspace/client/src/components/Dashboard/AiChatPage.tsx +145 -0
- package/workspace/client/src/components/Dashboard/CryptoPage.tsx +470 -0
- package/workspace/client/src/components/Dashboard/DashboardPage.tsx +117 -113
- package/workspace/client/src/components/Dashboard/WishlistPage.tsx +464 -0
- package/workspace/client/src/components/Layout/DashboardLayout.tsx +32 -34
- package/workspace/client/src/components/Layout/MiniSidebar.tsx +64 -0
- package/workspace/client/src/components/Layout/MobileNav.tsx +103 -6
- package/workspace/client/src/components/Layout/Sidebar.tsx +11 -10
- package/workspace/client/src/components/Lock/PinInput.tsx +107 -0
- package/workspace/client/src/components/Lock/WorkspaceLock.tsx +484 -0
- package/workspace/client/src/components/StickyNotes/StickyNotesOverlay.tsx +396 -0
- package/workspace/client/src/components/StickyNotes/StickyNotesSettingsPage.tsx +427 -0
- package/workspace/client/src/components/Wallpaper/WallpaperBackground.tsx +12 -0
- package/workspace/client/src/components/Wallpaper/WallpaperContext.tsx +160 -0
- package/workspace/client/src/components/Wallpaper/WallpaperPicker.tsx +67 -0
- package/workspace/client/src/styles/globals.css +89 -4
package/package.json
CHANGED
|
@@ -571,6 +571,14 @@ async function spawnAndInitialize(
|
|
|
571
571
|
conv.threadId = startResult.thread.id;
|
|
572
572
|
conversations.set(conversationId, conv);
|
|
573
573
|
log.ok(`[codex] thread started ${conv.threadId}`);
|
|
574
|
+
|
|
575
|
+
// Prime codex's per-thread skill cache with the workspace skills
|
|
576
|
+
// directory. Without this, codex only sees its system-scope skills and
|
|
577
|
+
// never discovers anything Bloby ships in `workspace/skills/*`. Fire and
|
|
578
|
+
// forget — failure here just means workspace skills won't be auto-routable
|
|
579
|
+
// for this thread, but the agent can still read SKILL.md files directly.
|
|
580
|
+
primeWorkspaceSkills(rpc);
|
|
581
|
+
|
|
574
582
|
return conv;
|
|
575
583
|
} catch (err: any) {
|
|
576
584
|
rpc.close();
|
|
@@ -579,6 +587,28 @@ async function spawnAndInitialize(
|
|
|
579
587
|
}
|
|
580
588
|
}
|
|
581
589
|
|
|
590
|
+
const SKILLS_DIR = path.join(WORKSPACE_DIR, 'skills');
|
|
591
|
+
|
|
592
|
+
function primeWorkspaceSkills(rpc: CodexRpc): void {
|
|
593
|
+
rpc.request('skills/list', {
|
|
594
|
+
cwds: [WORKSPACE_DIR],
|
|
595
|
+
forceReload: true,
|
|
596
|
+
perCwdExtraUserRoots: [{
|
|
597
|
+
cwd: WORKSPACE_DIR,
|
|
598
|
+
extraUserRoots: [SKILLS_DIR],
|
|
599
|
+
}],
|
|
600
|
+
}).then((result: any) => {
|
|
601
|
+
const entry = result?.data?.[0];
|
|
602
|
+
const all = entry?.skills ?? [];
|
|
603
|
+
const workspace = all.filter((s: any) => s.scope !== 'system');
|
|
604
|
+
const errors = entry?.errors ?? [];
|
|
605
|
+
log.ok(`[codex] skills primed: ${workspace.length} workspace, ${all.length - workspace.length} system${errors.length ? `, ${errors.length} rejected` : ''}`);
|
|
606
|
+
for (const err of errors) log.warn(`[codex] skill load error: ${err.path} — ${err.message}`);
|
|
607
|
+
}).catch((err: any) => {
|
|
608
|
+
log.warn(`[codex] skills/list failed: ${err.message}`);
|
|
609
|
+
});
|
|
610
|
+
}
|
|
611
|
+
|
|
582
612
|
/* ── Harness implementation ────────────────────────────────────────────── */
|
|
583
613
|
|
|
584
614
|
export function hasConversation(conversationId: string): boolean {
|
|
@@ -792,6 +822,10 @@ export async function runAgentQuery(req: AgentQueryRequest): Promise<AgentQueryR
|
|
|
792
822
|
resolvedThreadId = r.thread.id;
|
|
793
823
|
}
|
|
794
824
|
|
|
825
|
+
// Same priming as live conversations — workspace skills won't be visible
|
|
826
|
+
// to codex's router otherwise.
|
|
827
|
+
primeWorkspaceSkills(rpc);
|
|
828
|
+
|
|
795
829
|
const turnParams: Record<string, any> = {
|
|
796
830
|
threadId: resolvedThreadId,
|
|
797
831
|
input: [{ type: 'text', text: req.message }],
|
|
@@ -266,9 +266,9 @@ If something fails, own it: "Hmm, that didn't work. Let me try again."
|
|
|
266
266
|
|
|
267
267
|
## Skills
|
|
268
268
|
|
|
269
|
-
Skills live in `skills/` — each skill is a folder
|
|
269
|
+
Skills live in `skills/` — each skill is a folder containing a `SKILL.md` that explains what the skill does and how to use it. When a user request matches a skill's purpose (e.g. they mention WhatsApp and a `whatsapp` skill exists), open that skill's `SKILL.md` and follow its guidance. List `skills/` whenever you're unsure what's installed.
|
|
270
270
|
|
|
271
|
-
|
|
271
|
+
If your human asks you to update a skill's behavior, edit the files INSIDE `skills/{skill-name}/`.
|
|
272
272
|
|
|
273
273
|
**IMPORTANT: When editing skill files, always use the full path inside the skill directory.**
|
|
274
274
|
- Correct: `skills/my-skill/SCRIPT.md`
|
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
<link rel="apple-touch-icon" href="/bloby-icon-192.png" />
|
|
12
12
|
<link rel="manifest" href="/manifest.json" />
|
|
13
13
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@500;600;700&display=swap" rel="stylesheet">
|
|
14
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
15
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
16
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
14
17
|
<title>Bloby - AI agent with its own workspace</title>
|
|
15
18
|
</head>
|
|
16
19
|
<body class="bg-background text-foreground" style="background-color:#0A0A0A">
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
[{
|
|
2
|
+
"relation": ["delegate_permission/common.handle_all_urls"],
|
|
3
|
+
"target": {
|
|
4
|
+
"namespace": "android_app",
|
|
5
|
+
"package_name": "bot.bloby.consensus",
|
|
6
|
+
"sha256_cert_fingerprints": ["1A:5D:3C:F4:D3:60:A2:65:2D:34:8F:D5:7C:18:8F:3C:01:99:9E:5B:02:0C:D0:AA:E7:8F:F4:74:EE:C3:C0:7F"]
|
|
7
|
+
}
|
|
8
|
+
}]
|
|
Binary file
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 520 80" role="img" aria-label="BlackRock">
|
|
2
|
+
<text x="0" y="60"
|
|
3
|
+
font-family="'Times New Roman', Georgia, serif"
|
|
4
|
+
font-weight="700"
|
|
5
|
+
font-size="72"
|
|
6
|
+
letter-spacing="-2"
|
|
7
|
+
fill="#ffffff">BlackRock</text>
|
|
8
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,6 +4,11 @@ import ErrorBoundary from './components/ErrorBoundary';
|
|
|
4
4
|
import DashboardLayout from './components/Layout/DashboardLayout';
|
|
5
5
|
import DashboardPage from './components/Dashboard/DashboardPage';
|
|
6
6
|
import WorkspaceTour from './components/deleteme_onboarding/WorkspaceTour';
|
|
7
|
+
import { WorkspaceLock } from './components/Lock/WorkspaceLock';
|
|
8
|
+
import StickyNotesSettingsPage from './components/StickyNotes/StickyNotesSettingsPage';
|
|
9
|
+
import AiChatPage from './components/Dashboard/AiChatPage';
|
|
10
|
+
import WishlistPage from './components/Dashboard/WishlistPage';
|
|
11
|
+
import CryptoPage from './components/Dashboard/CryptoPage';
|
|
7
12
|
|
|
8
13
|
function DashboardError() {
|
|
9
14
|
return (
|
|
@@ -16,9 +21,9 @@ function DashboardError() {
|
|
|
16
21
|
playsInline
|
|
17
22
|
style={{ height: 120, width: 120, borderRadius: '50%', objectFit: 'cover', marginBottom: 32 }}
|
|
18
23
|
/>
|
|
19
|
-
<h1 style={{ fontSize: 20, fontWeight: 600, marginBottom: 8 }}>
|
|
24
|
+
<h1 style={{ fontSize: 20, fontWeight: 600, marginBottom: 8 }}>Your app crashed</h1>
|
|
20
25
|
<p style={{ fontSize: 14, color: 'rgba(255,255,255,0.5)', maxWidth: 320, lineHeight: 1.5 }}>
|
|
21
|
-
|
|
26
|
+
Ask the agent to fix it using the chat.
|
|
22
27
|
</p>
|
|
23
28
|
</div>
|
|
24
29
|
);
|
|
@@ -157,13 +162,19 @@ export default function App() {
|
|
|
157
162
|
return (
|
|
158
163
|
<>
|
|
159
164
|
<ErrorBoundary fallback={<DashboardError />}>
|
|
160
|
-
<
|
|
161
|
-
<
|
|
162
|
-
<
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
165
|
+
<WorkspaceLock>
|
|
166
|
+
<DashboardLayout userName={userName} botName={botName}>
|
|
167
|
+
<Routes>
|
|
168
|
+
<Route path="/" element={<DashboardPage />} />
|
|
169
|
+
<Route path="/sticky-notes" element={<StickyNotesSettingsPage />} />
|
|
170
|
+
<Route path="/aichat" element={<AiChatPage />} />
|
|
171
|
+
<Route path="/wishlist" element={<WishlistPage />} />
|
|
172
|
+
<Route path="/crypto" element={<CryptoPage />} />
|
|
173
|
+
<Route path="*" element={<DashboardPage />} />
|
|
174
|
+
</Routes>
|
|
175
|
+
</DashboardLayout>
|
|
176
|
+
<WorkspaceTour disabled={showOnboard} />
|
|
177
|
+
</WorkspaceLock>
|
|
167
178
|
</ErrorBoundary>
|
|
168
179
|
|
|
169
180
|
{showOnboard && (
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { useState, useRef, useEffect } from 'react';
|
|
2
|
+
import { Send, Loader2, Sparkles } from 'lucide-react';
|
|
3
|
+
|
|
4
|
+
interface Message {
|
|
5
|
+
role: 'user' | 'assistant';
|
|
6
|
+
content: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export default function AiChatPage() {
|
|
10
|
+
const [messages, setMessages] = useState<Message[]>([]);
|
|
11
|
+
const [input, setInput] = useState('');
|
|
12
|
+
const [loading, setLoading] = useState(false);
|
|
13
|
+
const [sessionId, setSessionId] = useState<string | undefined>();
|
|
14
|
+
const bottomRef = useRef<HTMLDivElement>(null);
|
|
15
|
+
const inputRef = useRef<HTMLTextAreaElement>(null);
|
|
16
|
+
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
19
|
+
}, [messages, loading]);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
inputRef.current?.focus();
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const sendMessage = async () => {
|
|
26
|
+
const text = input.trim();
|
|
27
|
+
if (!text || loading) return;
|
|
28
|
+
|
|
29
|
+
setInput('');
|
|
30
|
+
setMessages((prev) => [...prev, { role: 'user', content: text }]);
|
|
31
|
+
setLoading(true);
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const res = await fetch('/app/api/aichat', {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: { 'Content-Type': 'application/json' },
|
|
37
|
+
body: JSON.stringify({ message: text, sessionId }),
|
|
38
|
+
});
|
|
39
|
+
const data = await res.json();
|
|
40
|
+
|
|
41
|
+
if (data.sessionId) setSessionId(data.sessionId);
|
|
42
|
+
|
|
43
|
+
const reply = data.response || data.error || 'No response';
|
|
44
|
+
setMessages((prev) => [...prev, { role: 'assistant', content: reply }]);
|
|
45
|
+
} catch {
|
|
46
|
+
setMessages((prev) => [
|
|
47
|
+
...prev,
|
|
48
|
+
{ role: 'assistant', content: 'Failed to reach the server.' },
|
|
49
|
+
]);
|
|
50
|
+
} finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
inputRef.current?.focus();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
57
|
+
if (e.key === 'Enter' && !e.shiftKey) {
|
|
58
|
+
e.preventDefault();
|
|
59
|
+
sendMessage();
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<div className="flex flex-col h-full px-4 sm:px-6 md:px-20 pt-10 sm:pt-16 pb-32 max-w-5xl mx-auto w-full">
|
|
65
|
+
{/* Header */}
|
|
66
|
+
<div className="flex items-start gap-4 mb-6 shrink-0">
|
|
67
|
+
<div className="h-12 w-12 rounded-2xl flex items-center justify-center bg-gradient-to-br from-violet-300/40 to-fuchsia-500/20 border border-white/10 backdrop-blur-xl shrink-0">
|
|
68
|
+
<Sparkles className="h-5 w-5 text-violet-200" />
|
|
69
|
+
</div>
|
|
70
|
+
<div>
|
|
71
|
+
<h1 className="text-2xl sm:text-3xl font-semibold text-white tracking-tight">AI Chat</h1>
|
|
72
|
+
<p className="text-sm text-white/60 mt-1">Chat with Claude.</p>
|
|
73
|
+
</div>
|
|
74
|
+
</div>
|
|
75
|
+
|
|
76
|
+
{/* Messages */}
|
|
77
|
+
<div className="flex-1 overflow-y-auto space-y-4 pr-1">
|
|
78
|
+
{messages.length === 0 && !loading && (
|
|
79
|
+
<div className="flex items-center justify-center h-full">
|
|
80
|
+
<div className="glass-card glass-card-md px-5 py-4">
|
|
81
|
+
<p className="relative text-sm text-white/60">Send a message to start chatting.</p>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
85
|
+
{messages.map((msg, i) => (
|
|
86
|
+
<div
|
|
87
|
+
key={i}
|
|
88
|
+
className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}
|
|
89
|
+
>
|
|
90
|
+
{msg.role === 'user' ? (
|
|
91
|
+
<div
|
|
92
|
+
className="relative max-w-[80%] rounded-2xl px-4 py-2.5 text-sm leading-relaxed whitespace-pre-wrap text-white border border-white/10 backdrop-blur-xl"
|
|
93
|
+
style={{
|
|
94
|
+
background:
|
|
95
|
+
'linear-gradient(135deg, rgba(236, 72, 153, 0.35), rgba(225, 29, 72, 0.25))',
|
|
96
|
+
boxShadow:
|
|
97
|
+
'0 1px 0 0 rgba(255,255,255,0.1) inset, 0 10px 30px -10px rgba(244, 63, 94, 0.35)',
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
{msg.content}
|
|
101
|
+
</div>
|
|
102
|
+
) : (
|
|
103
|
+
<div className="relative max-w-[80%] glass-card glass-card-md px-4 py-2.5">
|
|
104
|
+
<p className="relative text-sm leading-relaxed whitespace-pre-wrap text-white/90">
|
|
105
|
+
{msg.content}
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
)}
|
|
109
|
+
</div>
|
|
110
|
+
))}
|
|
111
|
+
{loading && (
|
|
112
|
+
<div className="flex justify-start">
|
|
113
|
+
<div className="relative glass-card glass-card-md px-4 py-2.5 flex items-center gap-2">
|
|
114
|
+
<Loader2 className="h-4 w-4 animate-spin text-white/60 relative" />
|
|
115
|
+
<span className="text-sm text-white/60 relative">Thinking…</span>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
)}
|
|
119
|
+
<div ref={bottomRef} />
|
|
120
|
+
</div>
|
|
121
|
+
|
|
122
|
+
{/* Input */}
|
|
123
|
+
<div className="shrink-0 pt-4">
|
|
124
|
+
<div className="glass-pill rounded-full flex items-end gap-2 max-w-3xl mx-auto px-2 py-2">
|
|
125
|
+
<textarea
|
|
126
|
+
ref={inputRef}
|
|
127
|
+
value={input}
|
|
128
|
+
onChange={(e) => setInput(e.target.value)}
|
|
129
|
+
onKeyDown={handleKeyDown}
|
|
130
|
+
placeholder="Type a message…"
|
|
131
|
+
rows={1}
|
|
132
|
+
className="relative flex-1 resize-none bg-transparent border-none outline-none px-3 py-2 text-sm text-white placeholder-white/40 max-h-40"
|
|
133
|
+
/>
|
|
134
|
+
<button
|
|
135
|
+
onClick={sendMessage}
|
|
136
|
+
disabled={loading || !input.trim()}
|
|
137
|
+
className="relative shrink-0 h-9 w-9 rounded-full bg-gradient-to-br from-violet-500 to-fuchsia-500 hover:brightness-110 disabled:opacity-40 disabled:hover:brightness-100 flex items-center justify-center transition shadow-lg shadow-fuchsia-500/20"
|
|
138
|
+
>
|
|
139
|
+
<Send className="h-4 w-4 text-white" />
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</div>
|
|
144
|
+
);
|
|
145
|
+
}
|