bloby-bot 0.47.7 → 0.47.9
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/dist-bloby/assets/{bloby-E-QLmQDW.js → bloby-BOSem5eq.js} +4 -4
- package/dist-bloby/assets/globals-CZdsyZot.css +2 -0
- package/dist-bloby/assets/globals-CgIIDNVG.js +18 -0
- package/dist-bloby/assets/{highlighted-body-OFNGDK62-CTiboTVa.js → highlighted-body-OFNGDK62-DpMYfn6Z.js} +1 -1
- package/dist-bloby/assets/mermaid-GHXKKRXX-B5_2sDIG.js +1 -0
- package/dist-bloby/assets/{onboard-C1uMxuk2.js → onboard-8AOYU7vY.js} +1 -1
- package/dist-bloby/bloby.html +3 -3
- package/dist-bloby/onboard.html +3 -3
- package/package.json +4 -4
- package/supervisor/chat/OnboardWizard.tsx +121 -178
- package/supervisor/harnesses/pi/async-queue.ts +10 -0
- package/supervisor/harnesses/pi/index.ts +21 -0
- package/supervisor/harnesses/pi/providers/stream-anthropic.ts +318 -0
- package/supervisor/harnesses/pi/providers/stream-openai-completions.ts +328 -0
- package/supervisor/harnesses/pi/providers/stream.ts +4 -2
- package/supervisor/harnesses/pi/session.ts +24 -5
- package/workspace/client/public/icons/pi.svg +20 -0
- package/dist-bloby/assets/globals-Ci0CEj1X.js +0 -18
- package/dist-bloby/assets/globals-DriF_8Q_.css +0 -2
- package/dist-bloby/assets/mermaid-GHXKKRXX-CgVqYCFU.js +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-
|
|
1
|
+
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{n as r,r as i,t as a}from"./bloby-BOSem5eq.js";var o=e(t(),1),s=n(),c=({code:e,language:t,raw:n,className:c,startLine:l,lineNumbers:u,...d})=>{let{shikiTheme:f}=(0,o.useContext)(i),p=r(),[m,h]=(0,o.useState)(n);return(0,o.useEffect)(()=>{if(!p){h(n);return}let r=p.highlight({code:e,language:t,themes:f},e=>{h(e)});r&&h(r)},[e,t,f,p,n]),(0,s.jsx)(a,{className:c,language:t,lineNumbers:u,result:m,startLine:l,...d})};export{c as HighlightedCodeBlockBody};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{i as e}from"./bloby-BOSem5eq.js";export{e as Mermaid};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{p as r,t as i}from"./globals-
|
|
1
|
+
import{c as e,r as t,t as n}from"./jsx-runtime-C0W9Wf2W.js";import{p as r,t as i}from"./globals-CgIIDNVG.js";var a=e(t(),1),o=e(r(),1),s=n();function c(){return(0,s.jsx)(i,{onComplete:()=>{window.parent?.postMessage({type:`bloby:onboard-complete`},`*`)},isInitialSetup:!0})}o.createRoot(document.getElementById(`root`)).render((0,s.jsx)(a.StrictMode,{children:(0,s.jsx)(c,{})}));
|
package/dist-bloby/bloby.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
|
|
6
6
|
<title>Bloby Chat</title>
|
|
7
|
-
<script type="module" crossorigin src="/bloby/assets/bloby-
|
|
7
|
+
<script type="module" crossorigin src="/bloby/assets/bloby-BOSem5eq.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/bloby/assets/jsx-runtime-C0W9Wf2W.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/bloby/assets/globals-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/bloby/assets/globals-
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/bloby/assets/globals-CgIIDNVG.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/bloby/assets/globals-CZdsyZot.css">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="/bloby/assets/bloby-DkK0ymA2.css">
|
|
12
12
|
</head>
|
|
13
13
|
<body class="bg-background text-foreground">
|
package/dist-bloby/onboard.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, interactive-widget=resizes-content" />
|
|
6
6
|
<title>Bloby Setup</title>
|
|
7
|
-
<script type="module" crossorigin src="/bloby/assets/onboard-
|
|
7
|
+
<script type="module" crossorigin src="/bloby/assets/onboard-8AOYU7vY.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/bloby/assets/jsx-runtime-C0W9Wf2W.js">
|
|
9
|
-
<link rel="modulepreload" crossorigin href="/bloby/assets/globals-
|
|
10
|
-
<link rel="stylesheet" crossorigin href="/bloby/assets/globals-
|
|
9
|
+
<link rel="modulepreload" crossorigin href="/bloby/assets/globals-CgIIDNVG.js">
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/bloby/assets/globals-CZdsyZot.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body class="bg-background text-foreground">
|
|
13
13
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bloby-bot",
|
|
3
|
-
"version": "0.47.
|
|
3
|
+
"version": "0.47.9",
|
|
4
4
|
"releaseNotes": [
|
|
5
|
-
"1.
|
|
6
|
-
"2.
|
|
7
|
-
"3.
|
|
5
|
+
"1. Something great..",
|
|
6
|
+
"2. ",
|
|
7
|
+
"3. ",
|
|
8
8
|
"4. "
|
|
9
9
|
],
|
|
10
10
|
"description": "Self-hosted, self-evolving AI agent with its own dashboard.",
|
|
@@ -46,9 +46,10 @@ const ACCESS_LABELS: Record<AccessMethod, string> = {
|
|
|
46
46
|
/* ── Provider config ── */
|
|
47
47
|
|
|
48
48
|
const PROVIDERS = [
|
|
49
|
-
{ id: 'pi', name: '
|
|
50
|
-
{ id: '
|
|
51
|
-
{ id: '
|
|
49
|
+
{ id: 'pi', name: 'Pi', subtitle: 'Bring your own model', icon: '/icons/pi.svg', comingSoon: false },
|
|
50
|
+
{ id: 'bloby', name: 'Bloby', subtitle: 'Coming Soon..', icon: '/bloby-icon-192.png', comingSoon: true },
|
|
51
|
+
{ id: 'anthropic', name: 'Claude', subtitle: 'by Anthropic', icon: '/icons/claude.png', comingSoon: false },
|
|
52
|
+
{ id: 'openai', name: 'OpenAI Codex', subtitle: 'ChatGPT Plus / Pro', icon: '/icons/codex.png', comingSoon: false },
|
|
52
53
|
] as const;
|
|
53
54
|
|
|
54
55
|
const MODELS: Record<string, { id: string; label: string }[]> = {
|
|
@@ -196,8 +197,6 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
196
197
|
const [piShowKey, setPiShowKey] = useState(false);
|
|
197
198
|
const [piConnecting, setPiConnecting] = useState(false);
|
|
198
199
|
const [piError, setPiError] = useState<string | undefined>();
|
|
199
|
-
const [piTestRunning, setPiTestRunning] = useState(false);
|
|
200
|
-
const [piTestResult, setPiTestResult] = useState<{ ok: boolean; text?: string; error?: string } | null>(null);
|
|
201
200
|
const [piSavedStatus, setPiSavedStatus] = useState<{ subProvider?: string; modelId?: string; baseUrl?: string } | null>(null);
|
|
202
201
|
|
|
203
202
|
// Anthropic/Claude-specific
|
|
@@ -601,7 +600,6 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
601
600
|
setOpenaiError(undefined);
|
|
602
601
|
// Pi flow cleanup is per-sub-provider; the load effect handles re-hydration.
|
|
603
602
|
setPiError(undefined);
|
|
604
|
-
setPiTestResult(null);
|
|
605
603
|
};
|
|
606
604
|
|
|
607
605
|
/* ── Bloby (pi) handlers ── */
|
|
@@ -648,7 +646,6 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
648
646
|
setPiBaseUrl(next?.baseUrl || '');
|
|
649
647
|
setPiModelId(next?.defaultModel || '');
|
|
650
648
|
setPiError(undefined);
|
|
651
|
-
setPiTestResult(null);
|
|
652
649
|
// Picking a new sub-provider invalidates the previously saved auth.
|
|
653
650
|
if (authState.pi === 'connected') {
|
|
654
651
|
setAuthState((s) => ({ ...s, pi: 'idle' }));
|
|
@@ -659,7 +656,6 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
659
656
|
const handlePiConnect = async () => {
|
|
660
657
|
if (!piSubProvider) return;
|
|
661
658
|
setPiError(undefined);
|
|
662
|
-
setPiTestResult(null);
|
|
663
659
|
setPiConnecting(true);
|
|
664
660
|
try {
|
|
665
661
|
const payload = {
|
|
@@ -703,28 +699,9 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
703
699
|
try { await fetch('/api/auth/pi', { method: 'DELETE' }); } catch {}
|
|
704
700
|
setAuthState((s) => ({ ...s, pi: 'idle' }));
|
|
705
701
|
setPiSavedStatus(null);
|
|
706
|
-
setPiTestResult(null);
|
|
707
702
|
setModel('');
|
|
708
703
|
};
|
|
709
704
|
|
|
710
|
-
const handlePiTestCompletion = async () => {
|
|
711
|
-
setPiTestRunning(true);
|
|
712
|
-
setPiTestResult(null);
|
|
713
|
-
try {
|
|
714
|
-
const res = await fetch('/api/auth/pi/completion', {
|
|
715
|
-
method: 'POST',
|
|
716
|
-
headers: { 'Content-Type': 'application/json' },
|
|
717
|
-
body: JSON.stringify({}),
|
|
718
|
-
});
|
|
719
|
-
const data = await res.json();
|
|
720
|
-
setPiTestResult({ ok: !!data?.ok, text: data?.text, error: data?.error });
|
|
721
|
-
} catch (err: any) {
|
|
722
|
-
setPiTestResult({ ok: false, error: err?.message || 'Request failed' });
|
|
723
|
-
} finally {
|
|
724
|
-
setPiTestRunning(false);
|
|
725
|
-
}
|
|
726
|
-
};
|
|
727
|
-
|
|
728
705
|
/* ── Auth handlers: Anthropic/Claude ── */
|
|
729
706
|
|
|
730
707
|
const openExternal = (url: string) => {
|
|
@@ -2241,180 +2218,146 @@ export default function OnboardWizard({ onComplete, isInitialSetup = false, onSa
|
|
|
2241
2218
|
|
|
2242
2219
|
<div className="border-t border-white/[0.06] mt-4 mb-3" />
|
|
2243
2220
|
|
|
2244
|
-
{/* ── Auth flow:
|
|
2221
|
+
{/* ── Auth flow: Pi (bring your own model) ── */}
|
|
2245
2222
|
{provider === 'pi' && (
|
|
2246
|
-
<div className="space-y-
|
|
2223
|
+
<div className="space-y-2">
|
|
2247
2224
|
{authState.pi === 'connected' && piSavedStatus ? (
|
|
2248
|
-
|
|
2249
|
-
<
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
</p>
|
|
2254
|
-
</div>
|
|
2255
|
-
|
|
2256
|
-
<div className="bg-white/[0.03] border border-white/[0.06] rounded-xl p-3.5 space-y-2.5">
|
|
2257
|
-
<p className="text-[12px] text-white/55">
|
|
2258
|
-
Send a quick test request to confirm the model is reachable.
|
|
2259
|
-
</p>
|
|
2260
|
-
<button
|
|
2261
|
-
onClick={handlePiTestCompletion}
|
|
2262
|
-
disabled={piTestRunning}
|
|
2263
|
-
className="w-full py-2 px-4 bg-white/[0.06] hover:bg-white/[0.1] text-white text-[12px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-60"
|
|
2264
|
-
>
|
|
2265
|
-
{piTestRunning ? (
|
|
2266
|
-
<><LoaderCircle className="h-3.5 w-3.5 animate-spin" />Sending test request…</>
|
|
2267
|
-
) : (
|
|
2268
|
-
<>Send test request<ArrowRight className="h-3.5 w-3.5 opacity-60" /></>
|
|
2269
|
-
)}
|
|
2270
|
-
</button>
|
|
2271
|
-
{piTestResult && piTestResult.ok && (
|
|
2272
|
-
<div className="bg-emerald-500/5 border border-emerald-500/15 rounded-lg px-3 py-2">
|
|
2273
|
-
<p className="text-[11px] text-emerald-400/70 mb-1">Model reply</p>
|
|
2274
|
-
<p className="text-[12px] text-white/85 whitespace-pre-wrap">{piTestResult.text}</p>
|
|
2275
|
-
</div>
|
|
2276
|
-
)}
|
|
2277
|
-
{piTestResult && !piTestResult.ok && (
|
|
2278
|
-
<div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3 py-2">
|
|
2279
|
-
<p className="text-[12px] text-red-400/90">{piTestResult.error}</p>
|
|
2280
|
-
</div>
|
|
2281
|
-
)}
|
|
2282
|
-
</div>
|
|
2283
|
-
|
|
2225
|
+
<div className="flex items-center justify-between bg-emerald-500/8 border border-emerald-500/15 rounded-lg px-3 py-2">
|
|
2226
|
+
<p className="text-emerald-400/90 text-[12px] truncate">
|
|
2227
|
+
Connected — {piSubProviders.find((p) => p.id === piSavedStatus.subProvider)?.name || piSavedStatus.subProvider}
|
|
2228
|
+
{piSavedStatus.modelId ? <> · <span className="font-mono">{piSavedStatus.modelId}</span></> : null}
|
|
2229
|
+
</p>
|
|
2284
2230
|
<button
|
|
2285
2231
|
onClick={handlePiDisconnect}
|
|
2286
|
-
className="
|
|
2232
|
+
className="text-white/30 hover:text-white/60 text-[11px] flex items-center gap-1 shrink-0 ml-2"
|
|
2287
2233
|
>
|
|
2288
2234
|
<RefreshCw className="h-3 w-3" />
|
|
2289
|
-
|
|
2235
|
+
Change
|
|
2290
2236
|
</button>
|
|
2291
|
-
|
|
2237
|
+
</div>
|
|
2292
2238
|
) : (
|
|
2293
2239
|
<>
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
<div
|
|
2297
|
-
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
piSubProvider === sp.id
|
|
2304
|
-
? 'border-[#AF27E3]/50 bg-[#AF27E3]/10'
|
|
2305
|
-
: 'border-white/[0.08] bg-white/[0.02] hover:bg-white/[0.04]'
|
|
2306
|
-
}`}
|
|
2307
|
-
>
|
|
2308
|
-
<p className="text-[12.5px] text-white font-medium leading-tight">{sp.name}</p>
|
|
2309
|
-
<p className="text-[10.5px] text-white/40 mt-0.5 leading-tight">{sp.subtitle}</p>
|
|
2310
|
-
{piSubProvider === sp.id && (
|
|
2311
|
-
<div className="absolute top-1.5 right-1.5 w-1.5 h-1.5 rounded-full bg-gradient-brand" />
|
|
2312
|
-
)}
|
|
2313
|
-
</button>
|
|
2314
|
-
))}
|
|
2240
|
+
{/* Two-column compact row: provider dropdown + model picker */}
|
|
2241
|
+
<div className="grid grid-cols-2 gap-2">
|
|
2242
|
+
<div>
|
|
2243
|
+
<label className="text-[11px] text-white/40 font-medium mb-1 block">Provider</label>
|
|
2244
|
+
<ModelDropdown
|
|
2245
|
+
models={piSubProviders.map((sp) => ({ id: sp.id, label: sp.name }))}
|
|
2246
|
+
value={piSubProvider}
|
|
2247
|
+
onChange={choosePiSubProvider}
|
|
2248
|
+
/>
|
|
2315
2249
|
</div>
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
</div>
|
|
2250
|
+
<div>
|
|
2251
|
+
<label className="text-[11px] text-white/40 font-medium mb-1 block">Model</label>
|
|
2252
|
+
{selectedPiSub && Array.isArray(selectedPiSub.models) ? (
|
|
2253
|
+
<ModelDropdown
|
|
2254
|
+
models={selectedPiSub.models}
|
|
2255
|
+
value={piModelId}
|
|
2256
|
+
onChange={setPiModelId}
|
|
2257
|
+
/>
|
|
2258
|
+
) : (
|
|
2259
|
+
<input
|
|
2260
|
+
type="text"
|
|
2261
|
+
value={piModelId}
|
|
2262
|
+
onChange={(e) => setPiModelId(e.target.value)}
|
|
2263
|
+
placeholder={selectedPiSub?.defaultModel || 'model-id'}
|
|
2264
|
+
className={inputSmCls + ' font-mono'}
|
|
2265
|
+
/>
|
|
2333
2266
|
)}
|
|
2267
|
+
</div>
|
|
2268
|
+
</div>
|
|
2334
2269
|
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
|
|
2340
|
-
|
|
2341
|
-
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
>
|
|
2345
|
-
Get a key <ExternalLink className="h-3 w-3" />
|
|
2346
|
-
</button>
|
|
2347
|
-
)}
|
|
2348
|
-
</label>
|
|
2349
|
-
<div className="relative">
|
|
2350
|
-
<input
|
|
2351
|
-
type={piShowKey ? 'text' : 'password'}
|
|
2352
|
-
value={piApiKey}
|
|
2353
|
-
onChange={(e) => setPiApiKey(e.target.value)}
|
|
2354
|
-
onKeyDown={(e) => e.key === 'Enter' && handlePiConnect()}
|
|
2355
|
-
placeholder="sk-..."
|
|
2356
|
-
className={inputSmCls + ' pr-10 font-mono'}
|
|
2357
|
-
/>
|
|
2358
|
-
<button
|
|
2359
|
-
type="button"
|
|
2360
|
-
onClick={() => setPiShowKey((v) => !v)}
|
|
2361
|
-
className="absolute right-3 top-1/2 -translate-y-1/2 text-white/20 hover:text-white/50 transition-colors"
|
|
2362
|
-
>
|
|
2363
|
-
{piShowKey ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
|
2364
|
-
</button>
|
|
2365
|
-
</div>
|
|
2366
|
-
</div>
|
|
2367
|
-
)}
|
|
2270
|
+
{selectedPiSub?.needsBaseUrl && (
|
|
2271
|
+
<input
|
|
2272
|
+
type="text"
|
|
2273
|
+
value={piBaseUrl}
|
|
2274
|
+
onChange={(e) => setPiBaseUrl(e.target.value)}
|
|
2275
|
+
placeholder={selectedPiSub.baseUrl || 'https://example.com/v1'}
|
|
2276
|
+
className={inputSmCls + ' font-mono'}
|
|
2277
|
+
/>
|
|
2278
|
+
)}
|
|
2368
2279
|
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2280
|
+
{selectedPiSub?.needsApiKey && (
|
|
2281
|
+
<div className="relative">
|
|
2282
|
+
<input
|
|
2283
|
+
type={piShowKey ? 'text' : 'password'}
|
|
2284
|
+
value={piApiKey}
|
|
2285
|
+
onChange={(e) => setPiApiKey(e.target.value)}
|
|
2286
|
+
onKeyDown={(e) => e.key === 'Enter' && handlePiConnect()}
|
|
2287
|
+
placeholder="API key…"
|
|
2288
|
+
className={inputSmCls + ' pr-16 font-mono'}
|
|
2289
|
+
/>
|
|
2290
|
+
<div className="absolute right-2 top-1/2 -translate-y-1/2 flex items-center gap-1.5">
|
|
2291
|
+
{selectedPiSub.apiKeyUrl && (
|
|
2292
|
+
<button
|
|
2293
|
+
type="button"
|
|
2294
|
+
onClick={() => openExternal(selectedPiSub.apiKeyUrl!)}
|
|
2295
|
+
className="text-white/25 hover:text-white/55 transition-colors"
|
|
2296
|
+
title="Get a key"
|
|
2297
|
+
>
|
|
2298
|
+
<ExternalLink className="h-3.5 w-3.5" />
|
|
2299
|
+
</button>
|
|
2385
2300
|
)}
|
|
2301
|
+
<button
|
|
2302
|
+
type="button"
|
|
2303
|
+
onClick={() => setPiShowKey((v) => !v)}
|
|
2304
|
+
className="text-white/25 hover:text-white/55 transition-colors"
|
|
2305
|
+
>
|
|
2306
|
+
{piShowKey ? <EyeOff className="h-3.5 w-3.5" /> : <Eye className="h-3.5 w-3.5" />}
|
|
2307
|
+
</button>
|
|
2386
2308
|
</div>
|
|
2309
|
+
</div>
|
|
2310
|
+
)}
|
|
2387
2311
|
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
</div>
|
|
2392
|
-
)}
|
|
2393
|
-
|
|
2394
|
-
<button
|
|
2395
|
-
onClick={handlePiConnect}
|
|
2396
|
-
disabled={
|
|
2397
|
-
piConnecting ||
|
|
2398
|
-
!piSubProvider ||
|
|
2399
|
-
(selectedPiSub.needsApiKey && !piApiKey.trim()) ||
|
|
2400
|
-
(selectedPiSub.needsBaseUrl && !piBaseUrl.trim()) ||
|
|
2401
|
-
!piModelId.trim()
|
|
2402
|
-
}
|
|
2403
|
-
className="w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
|
|
2404
|
-
>
|
|
2405
|
-
{piConnecting ? (
|
|
2406
|
-
<><LoaderCircle className="h-3.5 w-3.5 animate-spin" />Connecting…</>
|
|
2407
|
-
) : (
|
|
2408
|
-
<>Test & connect<ArrowRight className="h-3.5 w-3.5 opacity-60" /></>
|
|
2409
|
-
)}
|
|
2410
|
-
</button>
|
|
2312
|
+
{piError && (
|
|
2313
|
+
<div className="bg-red-500/8 border border-red-500/15 rounded-lg px-3 py-2">
|
|
2314
|
+
<p className="text-[12px] text-red-400/90 break-words">{piError}</p>
|
|
2411
2315
|
</div>
|
|
2412
2316
|
)}
|
|
2317
|
+
|
|
2318
|
+
<button
|
|
2319
|
+
onClick={handlePiConnect}
|
|
2320
|
+
disabled={
|
|
2321
|
+
piConnecting ||
|
|
2322
|
+
!piSubProvider ||
|
|
2323
|
+
(!!selectedPiSub?.needsApiKey && !piApiKey.trim()) ||
|
|
2324
|
+
(!!selectedPiSub?.needsBaseUrl && !piBaseUrl.trim()) ||
|
|
2325
|
+
!piModelId.trim()
|
|
2326
|
+
}
|
|
2327
|
+
className="w-full py-2.5 px-4 bg-gradient-brand hover:opacity-90 text-white text-[13px] font-medium rounded-xl transition-colors flex items-center justify-center gap-2 disabled:opacity-40"
|
|
2328
|
+
>
|
|
2329
|
+
{piConnecting ? (
|
|
2330
|
+
<><LoaderCircle className="h-3.5 w-3.5 animate-spin" />Connecting…</>
|
|
2331
|
+
) : (
|
|
2332
|
+
<>Test & connect<ArrowRight className="h-3.5 w-3.5 opacity-60" /></>
|
|
2333
|
+
)}
|
|
2334
|
+
</button>
|
|
2413
2335
|
</>
|
|
2414
2336
|
)}
|
|
2415
2337
|
</div>
|
|
2416
2338
|
)}
|
|
2417
2339
|
|
|
2340
|
+
{/* ── Auth flow: Bloby (coming soon placeholder) ── */}
|
|
2341
|
+
{provider === 'bloby' && (
|
|
2342
|
+
<div className="space-y-3">
|
|
2343
|
+
<p className="text-[12px] text-white/40 leading-relaxed">
|
|
2344
|
+
Bloby (managed) is on the way. Sign in with Google once it ships and your bot will be hosted, updated, and billed by us — no API keys to manage.
|
|
2345
|
+
</p>
|
|
2346
|
+
<button
|
|
2347
|
+
type="button"
|
|
2348
|
+
disabled
|
|
2349
|
+
title="Coming soon"
|
|
2350
|
+
className="w-full py-2.5 px-4 bg-white/[0.04] border border-white/[0.06] text-white/35 text-[13px] font-medium rounded-xl flex items-center justify-center gap-2 cursor-not-allowed"
|
|
2351
|
+
>
|
|
2352
|
+
<svg viewBox="0 0 24 24" className="h-4 w-4 opacity-50" fill="currentColor">
|
|
2353
|
+
<path d="M21.35 11.1h-9.17v2.96h5.27c-.23 1.39-1.62 4.08-5.27 4.08-3.17 0-5.76-2.62-5.76-5.85s2.59-5.85 5.76-5.85c1.81 0 3.02.77 3.71 1.43l2.53-2.43C16.85 3.91 14.74 3 12.18 3 7.03 3 2.86 7.17 2.86 12.29s4.17 9.29 9.32 9.29c5.38 0 8.94-3.79 8.94-9.12 0-.61-.07-1.08-.17-1.55Z"/>
|
|
2354
|
+
</svg>
|
|
2355
|
+
Login with Google
|
|
2356
|
+
</button>
|
|
2357
|
+
<p className="text-[10.5px] text-white/25 text-center">Not available yet.</p>
|
|
2358
|
+
</div>
|
|
2359
|
+
)}
|
|
2360
|
+
|
|
2418
2361
|
{/* ── Auth flow: Anthropic ── */}
|
|
2419
2362
|
{provider === 'anthropic' && (
|
|
2420
2363
|
<div className="space-y-2.5">
|
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
export interface AsyncQueue<T> extends AsyncIterable<T> {
|
|
10
10
|
push(item: T): void;
|
|
11
11
|
end(): void;
|
|
12
|
+
/**
|
|
13
|
+
* Non-blocking drain: return every item currently buffered without waiting
|
|
14
|
+
* for a new one. Used by the session to fold mid-turn user messages into
|
|
15
|
+
* the model's history without breaking the outer `for await` consumer.
|
|
16
|
+
*/
|
|
17
|
+
drainPending(): T[];
|
|
12
18
|
}
|
|
13
19
|
|
|
14
20
|
export function createAsyncQueue<T>(): AsyncQueue<T> {
|
|
@@ -30,6 +36,10 @@ export function createAsyncQueue<T>(): AsyncQueue<T> {
|
|
|
30
36
|
done = true;
|
|
31
37
|
if (resolve) resolve({ value: undefined as any, done: true });
|
|
32
38
|
},
|
|
39
|
+
drainPending() {
|
|
40
|
+
if (pending.length === 0) return [];
|
|
41
|
+
return pending.splice(0, pending.length);
|
|
42
|
+
},
|
|
33
43
|
[Symbol.asyncIterator]() {
|
|
34
44
|
return {
|
|
35
45
|
next(): Promise<IteratorResult<T>> {
|
|
@@ -82,6 +82,26 @@ function formatConversationHistory(messages: RecentMessage[]): string {
|
|
|
82
82
|
return messages.map((m) => `${m.role}: ${m.content}`).join('\n\n');
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Live-conversation pacing hint. The Claude Agent SDK trains its model to do
|
|
87
|
+
* this natively; non-Anthropic models (Gemini especially) tend to go silent
|
|
88
|
+
* during tool loops and never report progress. This nudge makes the
|
|
89
|
+
* conversation feel alive — quick acknowledgement before long tasks, short
|
|
90
|
+
* status notes between tool calls, and inline answers if the user types
|
|
91
|
+
* something while the agent is mid-task.
|
|
92
|
+
*/
|
|
93
|
+
const LIVE_CONVERSATION_HINT = `
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
# Live-conversation pacing
|
|
97
|
+
|
|
98
|
+
You are running in a streaming chat where the user can keep typing while you work. Make the conversation feel alive:
|
|
99
|
+
|
|
100
|
+
- Before kicking off a multi-step task, say one short line acknowledging it ("On it, looking at the widget now.").
|
|
101
|
+
- Between tool calls on long tasks, drop a brief progress note ("Found the file, checking the layout next.") so the user knows you're still working.
|
|
102
|
+
- If a new user message arrives while you're mid-task, you'll see it as a fresh user-role message in the conversation history. Answer it briefly inline, mention you're still working on the main task, then continue.
|
|
103
|
+
- Final answers should be concise and concrete.`;
|
|
104
|
+
|
|
85
105
|
async function buildSystemPrompt(
|
|
86
106
|
names?: { botName: string; humanName: string },
|
|
87
107
|
recentMessages?: RecentMessage[],
|
|
@@ -89,6 +109,7 @@ async function buildSystemPrompt(
|
|
|
89
109
|
const memoryFiles = readMemoryFiles();
|
|
90
110
|
const basePrompt = await assembleSystemPrompt(names?.botName, names?.humanName);
|
|
91
111
|
let systemPrompt = basePrompt;
|
|
112
|
+
systemPrompt += LIVE_CONVERSATION_HINT;
|
|
92
113
|
systemPrompt += `\n\n---\n# Your Memory Files\n\n## MYSELF.md\n${memoryFiles.myself}\n\n## MYHUMAN.md\n${memoryFiles.myhuman}\n\n## MEMORY.md\n${memoryFiles.memory}\n\n---\n# Your Config Files\n\n## PULSE.json\n${memoryFiles.pulse}\n\n## CRONS.json\n${memoryFiles.crons}`;
|
|
93
114
|
|
|
94
115
|
try {
|