zubo 0.1.24 → 0.1.26
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/CHANGELOG.md +20 -0
- package/README.md +24 -6
- package/package.json +2 -1
- package/src/channels/dashboard.html.ts +171 -29
- package/src/channels/router.ts +348 -32
- package/src/channels/webchat.ts +97 -26
- package/src/config/schema.ts +23 -6
- package/src/eval.ts +127 -0
- package/src/index.ts +6 -0
- package/src/memory/fts-index.ts +22 -16
- package/src/memory/hybrid-search.ts +62 -36
- package/src/setup.ts +60 -11
- package/src/tools/builtin/memory-search.ts +8 -3
- package/src/tools/executor.ts +93 -25
- package/src/tools/permissions.ts +127 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.1.25 - 2026-02-17
|
|
4
|
+
|
|
5
|
+
- Added `zubo eval` reliability command with deterministic checks for slash commands, memory explainability, and dry-run safety.
|
|
6
|
+
- Added unified slash command write-actions:
|
|
7
|
+
- `/model set <provider/model>`
|
|
8
|
+
- `/permissions set <tool> <auto|confirm|deny>`
|
|
9
|
+
- `/budget pause|resume`
|
|
10
|
+
- Added configurable memory retrieval tuning:
|
|
11
|
+
- `memoryRetrieval.contextTopK`
|
|
12
|
+
- `memoryRetrieval.minConfidence`
|
|
13
|
+
- Added configurable runtime tool policy controls:
|
|
14
|
+
- `toolScopes.allowed`
|
|
15
|
+
- `toolScopes.dryRunByDefault`
|
|
16
|
+
- `toolPermissions.<tool>`
|
|
17
|
+
- Updated dashboard settings UI with memory retrieval and tool safety controls, including preset buttons and inline guidance.
|
|
18
|
+
- Improved memory explainability display in dashboard and memory search outputs (match type, confidence, reasons).
|
|
19
|
+
- Updated front-facing docs (`README`, CLI, config, memory docs) for new commands and settings.
|
|
20
|
+
- Added CI gate for `zubo eval`.
|
package/README.md
CHANGED
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
|
|
29
29
|
- **11+ LLM providers** — Anthropic, OpenAI, Google Gemini, Ollama, Groq, Together, OpenRouter, DeepSeek, xAI, Fireworks, LM Studio, and any OpenAI-compatible endpoint. Smart routing sends simple queries to fast models automatically.
|
|
30
30
|
- **7 channels** — Telegram, Discord, Slack, WhatsApp, Signal, Email, Web Chat
|
|
31
|
-
- **Persistent memory** — Vector + full-text hybrid search with ONNX embeddings and FTS5. Remembers every conversation, preference, and fact — forever.
|
|
31
|
+
- **Persistent memory** — Vector + full-text hybrid search with ONNX embeddings and FTS5. Remembers every conversation, preference, and fact — forever.
|
|
32
|
+
- **Memory explainability** — Memory matches include confidence and why they were selected (keyword, semantic, or hybrid match).
|
|
32
33
|
- **25+ built-in tools** — Web search (Brave + DuckDuckGo), file ops, code execution, APIs, sub-agent delegation, knowledge graph, memory pruning, reminders, and automatic failover between providers.
|
|
33
34
|
- **Extensible skills** — Build custom skills in TypeScript. Share them on the registry. Install community skills with one command.
|
|
34
35
|
- **9 integrations** — GitHub, Google (Gmail, Calendar, Docs, Drive, Sheets), Notion, Linear, Jira, Slack, Twitter + Claude Code and MCP
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
- **Natural language scheduling** — "Every weekday at 9am" just works. Cron jobs, heartbeat, proactive tasks.
|
|
37
38
|
- **Voice** — Speech-to-text (Whisper, local whisper.cpp), text-to-speech (OpenAI, ElevenLabs), and continuous voice conversation mode
|
|
38
39
|
- **Personal tools** — Todos, notes, preferences, topics, and follow-ups — all manageable from the dashboard or via chat
|
|
39
|
-
- **Dashboard** — Built-in web UI with analytics, memory management, Ollama model manager, personal tools, and settings
|
|
40
|
+
- **Dashboard** — Built-in web UI with analytics, memory management, Ollama model manager, personal tools, and settings
|
|
41
|
+
- **Safety controls** — Tool scope allowlists and dry-run-by-default mode for risky tools, configurable in the dashboard
|
|
40
42
|
- **Document ingestion** — Upload PDF, DOCX, XLSX, PPTX, TXT, CSV, JSON, and more
|
|
41
43
|
- **Budget controls** — Daily/monthly spending limits with per-model cost tracking
|
|
42
44
|
- **100% local** — SQLite database, local vector store. Your data never leaves your machine.
|
|
@@ -128,12 +130,28 @@ zubo model [provider/model] Show or switch LLM
|
|
|
128
130
|
zubo skills Manage skills
|
|
129
131
|
zubo install <name> Install from registry
|
|
130
132
|
zubo search <query> Search the registry
|
|
131
|
-
zubo voice Continuous voice conversation mode
|
|
132
|
-
zubo
|
|
133
|
-
zubo
|
|
133
|
+
zubo voice Continuous voice conversation mode
|
|
134
|
+
zubo eval Run reliability + safety checks
|
|
135
|
+
zubo auth create-key Create an API key
|
|
136
|
+
zubo export / import Backup and restore
|
|
134
137
|
```
|
|
135
138
|
|
|
136
|
-
Full reference at [zubo.bot/docs/cli.html](https://zubo.bot/docs/cli.html).
|
|
139
|
+
Full reference at [zubo.bot/docs/cli.html](https://zubo.bot/docs/cli.html).
|
|
140
|
+
|
|
141
|
+
## Unified Slash Commands
|
|
142
|
+
|
|
143
|
+
Across WebChat, Telegram, Discord, Slack, and other channels:
|
|
144
|
+
|
|
145
|
+
- `/help` — list available commands
|
|
146
|
+
- `/status` — runtime status
|
|
147
|
+
- `/memory <query>` — search saved memory with confidence metadata
|
|
148
|
+
- `/model` — show current provider/model
|
|
149
|
+
- `/model set <provider/model>` — switch active model at runtime
|
|
150
|
+
- `/tools [filter]` — list available tools
|
|
151
|
+
- `/permissions <tool>` — view tool permission + scopes
|
|
152
|
+
- `/permissions set <tool> <auto|confirm|deny>` — override tool permission
|
|
153
|
+
- `/budget` — view budget usage and limits
|
|
154
|
+
- `/budget pause|resume` — pause/resume budget enforcement
|
|
137
155
|
|
|
138
156
|
## Contributing
|
|
139
157
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zubo",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "Your AI agent that never forgets. Persistent memory, 25+ tools, 7 channels, 11+ LLM providers — runs entirely on your machine.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "thomaskanze",
|
|
@@ -32,6 +32,7 @@
|
|
|
32
32
|
"logs": "bun run src/index.ts logs",
|
|
33
33
|
"logs:follow": "bun run src/index.ts logs --follow",
|
|
34
34
|
"model": "bun run src/index.ts model",
|
|
35
|
+
"eval": "bun run src/index.ts eval",
|
|
35
36
|
"skills": "bun run src/index.ts skills",
|
|
36
37
|
"dev": "bun run --watch src/index.ts start",
|
|
37
38
|
"desktop:dev": "cd desktop && npm run dev",
|
|
@@ -1525,8 +1525,8 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1525
1525
|
</div>
|
|
1526
1526
|
</div>
|
|
1527
1527
|
|
|
1528
|
-
<div class="settings-section">
|
|
1529
|
-
<h3 class="settings-title" data-tooltip="How often Zubo checks for tasks">Background Check Interval</h3>
|
|
1528
|
+
<div class="settings-section">
|
|
1529
|
+
<h3 class="settings-title" data-tooltip="How often Zubo checks for tasks">Background Check Interval</h3>
|
|
1530
1530
|
<p class="settings-desc">How often Zubo checks for reminders, scheduled tasks, and updates. Default: every 30 minutes.</p>
|
|
1531
1531
|
<div class="settings-grid">
|
|
1532
1532
|
<div class="settings-field">
|
|
@@ -1537,11 +1537,58 @@ export const DASHBOARD_HTML = `<!DOCTYPE html>
|
|
|
1537
1537
|
<div style="margin-top: 16px; display: flex; gap: 10px; align-items: center;">
|
|
1538
1538
|
<button class="btn btn-primary" onclick="saveHeartbeat()">Save</button>
|
|
1539
1539
|
<span id="heartbeat-status" class="status-text"></span>
|
|
1540
|
-
</div>
|
|
1541
|
-
</div>
|
|
1542
|
-
|
|
1543
|
-
<div class="settings-section">
|
|
1544
|
-
<h3 class="settings-title">
|
|
1540
|
+
</div>
|
|
1541
|
+
</div>
|
|
1542
|
+
|
|
1543
|
+
<div class="settings-section">
|
|
1544
|
+
<h3 class="settings-title">Memory Retrieval</h3>
|
|
1545
|
+
<p class="settings-desc">Control how many memory chunks are injected into chat context and the minimum confidence threshold.</p>
|
|
1546
|
+
<div class="settings-grid">
|
|
1547
|
+
<div class="settings-field">
|
|
1548
|
+
<label class="settings-label" for="memory-context-topk">Context Top-K</label>
|
|
1549
|
+
<input id="memory-context-topk" type="number" class="settings-input" min="1" max="10" step="1" placeholder="3">
|
|
1550
|
+
</div>
|
|
1551
|
+
<div class="settings-field">
|
|
1552
|
+
<label class="settings-label" for="memory-min-confidence">Min Confidence (0-1)</label>
|
|
1553
|
+
<input id="memory-min-confidence" type="number" class="settings-input" min="0" max="1" step="0.05" placeholder="0">
|
|
1554
|
+
</div>
|
|
1555
|
+
</div>
|
|
1556
|
+
<div style="margin-top: 16px; display: flex; gap: 10px; align-items: center;">
|
|
1557
|
+
<button class="btn btn-primary" onclick="saveMemoryRetrievalSettings()">Save</button>
|
|
1558
|
+
<button class="btn btn-ghost" onclick="applyMemoryPreset('balanced')">Balanced</button>
|
|
1559
|
+
<button class="btn btn-ghost" onclick="applyMemoryPreset('strict')">Strict</button>
|
|
1560
|
+
<span id="memory-retrieval-status" class="status-text"></span>
|
|
1561
|
+
</div>
|
|
1562
|
+
<p class="settings-desc" style="margin-top:10px;margin-bottom:0;">Recommended: <code>Top-K 3-5</code> and <code>min confidence 0.2-0.35</code>.</p>
|
|
1563
|
+
</div>
|
|
1564
|
+
|
|
1565
|
+
<div class="settings-section">
|
|
1566
|
+
<h3 class="settings-title">Tool Safety</h3>
|
|
1567
|
+
<p class="settings-desc">Limit tool scopes and optionally force dry-run mode by default for risky tools.</p>
|
|
1568
|
+
<div class="settings-grid">
|
|
1569
|
+
<div class="settings-field">
|
|
1570
|
+
<label class="settings-label" for="tool-scopes-allowed">Allowed Scopes (comma-separated)</label>
|
|
1571
|
+
<input id="tool-scopes-allowed" type="text" class="settings-input" placeholder="memory,network_read,filesystem_read">
|
|
1572
|
+
</div>
|
|
1573
|
+
<div class="settings-field">
|
|
1574
|
+
<label class="settings-label" for="tool-scopes-dry-run">Dry-Run By Default</label>
|
|
1575
|
+
<select id="tool-scopes-dry-run" class="settings-select">
|
|
1576
|
+
<option value="false">No</option>
|
|
1577
|
+
<option value="true">Yes</option>
|
|
1578
|
+
</select>
|
|
1579
|
+
</div>
|
|
1580
|
+
</div>
|
|
1581
|
+
<div style="margin-top: 16px; display: flex; gap: 10px; align-items: center;">
|
|
1582
|
+
<button class="btn btn-primary" onclick="saveToolScopeSettings()">Save</button>
|
|
1583
|
+
<button class="btn btn-ghost" onclick="applyToolScopePreset('safe')">Safe</button>
|
|
1584
|
+
<button class="btn btn-ghost" onclick="applyToolScopePreset('balanced')">Balanced</button>
|
|
1585
|
+
<span id="tool-scopes-status" class="status-text"></span>
|
|
1586
|
+
</div>
|
|
1587
|
+
<p class="settings-desc" style="margin-top:10px;margin-bottom:0;">Leave blank to allow all scopes. Use presets to start with least privilege.</p>
|
|
1588
|
+
</div>
|
|
1589
|
+
|
|
1590
|
+
<div class="settings-section">
|
|
1591
|
+
<h3 class="settings-title">Configuration</h3>
|
|
1545
1592
|
<p class="settings-desc">Manage your full config by editing <code>~/.zubo/config.json</code> directly, or re-run <code>zubo setup</code>.</p>
|
|
1546
1593
|
</div>
|
|
1547
1594
|
</div>
|
|
@@ -2841,20 +2888,30 @@ function renderMemoryItems(results, container) {
|
|
|
2841
2888
|
return;
|
|
2842
2889
|
}
|
|
2843
2890
|
document.getElementById('memory-count').textContent = String(results.length);
|
|
2844
|
-
results.forEach(function(r) {
|
|
2845
|
-
var item = document.createElement('div');
|
|
2846
|
-
item.className = 'memory-item';
|
|
2847
|
-
var src = document.createElement('div');
|
|
2848
|
-
src.className = 'source';
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2891
|
+
results.forEach(function(r) {
|
|
2892
|
+
var item = document.createElement('div');
|
|
2893
|
+
item.className = 'memory-item';
|
|
2894
|
+
var src = document.createElement('div');
|
|
2895
|
+
src.className = 'source';
|
|
2896
|
+
var sourceBits = [r.source || ''];
|
|
2897
|
+
if (r.matchType) sourceBits.push(String(r.matchType));
|
|
2898
|
+
if (typeof r.confidence === 'number') sourceBits.push('conf ' + Math.round(r.confidence * 100) + '%');
|
|
2899
|
+
src.textContent = sourceBits.filter(Boolean).join(' • ');
|
|
2900
|
+
var cnt = document.createElement('div');
|
|
2901
|
+
cnt.className = 'content';
|
|
2902
|
+
cnt.textContent = r.content;
|
|
2903
|
+
item.appendChild(src);
|
|
2904
|
+
if (r.reasons && r.reasons.length) {
|
|
2905
|
+
var why = document.createElement('div');
|
|
2906
|
+
why.className = 'source';
|
|
2907
|
+
why.style.marginTop = '6px';
|
|
2908
|
+
why.textContent = 'Reason: ' + r.reasons.join(', ');
|
|
2909
|
+
item.appendChild(why);
|
|
2910
|
+
}
|
|
2911
|
+
item.appendChild(cnt);
|
|
2912
|
+
container.appendChild(item);
|
|
2913
|
+
});
|
|
2914
|
+
}
|
|
2858
2915
|
|
|
2859
2916
|
function loadRecentMemories() {
|
|
2860
2917
|
api('/memory/recent').then(function(data) {
|
|
@@ -3501,7 +3558,7 @@ function wipeData(type) {
|
|
|
3501
3558
|
// --- SETTINGS ---
|
|
3502
3559
|
var settingsProviders = [];
|
|
3503
3560
|
|
|
3504
|
-
function loadSettings() {
|
|
3561
|
+
function loadSettings() {
|
|
3505
3562
|
api('/config').then(function(data) {
|
|
3506
3563
|
settingsProviders = data.providers || [];
|
|
3507
3564
|
var sel = document.getElementById('settings-provider');
|
|
@@ -3520,11 +3577,13 @@ function loadSettings() {
|
|
|
3520
3577
|
document.getElementById('settings-heartbeat').value = data.minutes || 30;
|
|
3521
3578
|
document.getElementById('heartbeat-status').textContent = '';
|
|
3522
3579
|
});
|
|
3523
|
-
loadChannelStatus();
|
|
3524
|
-
loadDbStats();
|
|
3525
|
-
loadSecrets();
|
|
3526
|
-
loadSmartRouting();
|
|
3527
|
-
|
|
3580
|
+
loadChannelStatus();
|
|
3581
|
+
loadDbStats();
|
|
3582
|
+
loadSecrets();
|
|
3583
|
+
loadSmartRouting();
|
|
3584
|
+
loadMemoryRetrievalSettings();
|
|
3585
|
+
loadToolScopeSettings();
|
|
3586
|
+
}
|
|
3528
3587
|
|
|
3529
3588
|
function onProviderChange() {
|
|
3530
3589
|
var sel = document.getElementById('settings-provider');
|
|
@@ -3572,7 +3631,7 @@ function testLlm() {
|
|
|
3572
3631
|
});
|
|
3573
3632
|
}
|
|
3574
3633
|
|
|
3575
|
-
function saveHeartbeat() {
|
|
3634
|
+
function saveHeartbeat() {
|
|
3576
3635
|
var mins = parseInt(document.getElementById('settings-heartbeat').value, 10);
|
|
3577
3636
|
if (!mins || mins < 1 || mins > 1440) {
|
|
3578
3637
|
document.getElementById('heartbeat-status').textContent = 'Must be 1\u20131440 minutes';
|
|
@@ -3590,7 +3649,90 @@ function saveHeartbeat() {
|
|
|
3590
3649
|
document.getElementById('heartbeat-status').textContent = data.error || 'Error';
|
|
3591
3650
|
}
|
|
3592
3651
|
});
|
|
3593
|
-
}
|
|
3652
|
+
}
|
|
3653
|
+
|
|
3654
|
+
function loadMemoryRetrievalSettings() {
|
|
3655
|
+
api('/settings/memory-retrieval').then(function(data) {
|
|
3656
|
+
document.getElementById('memory-context-topk').value = data.contextTopK || 3;
|
|
3657
|
+
document.getElementById('memory-min-confidence').value = data.minConfidence || 0;
|
|
3658
|
+
document.getElementById('memory-retrieval-status').textContent = '';
|
|
3659
|
+
});
|
|
3660
|
+
}
|
|
3661
|
+
|
|
3662
|
+
function saveMemoryRetrievalSettings() {
|
|
3663
|
+
var contextTopK = parseInt(document.getElementById('memory-context-topk').value, 10);
|
|
3664
|
+
var minConfidence = parseFloat(document.getElementById('memory-min-confidence').value);
|
|
3665
|
+
if (isNaN(contextTopK) || contextTopK < 1 || contextTopK > 10) {
|
|
3666
|
+
document.getElementById('memory-retrieval-status').textContent = 'Top-K must be 1-10';
|
|
3667
|
+
return;
|
|
3668
|
+
}
|
|
3669
|
+
if (isNaN(minConfidence) || minConfidence < 0 || minConfidence > 1) {
|
|
3670
|
+
document.getElementById('memory-retrieval-status').textContent = 'Confidence must be 0-1';
|
|
3671
|
+
return;
|
|
3672
|
+
}
|
|
3673
|
+
api('/settings/memory-retrieval', {
|
|
3674
|
+
method: 'PUT',
|
|
3675
|
+
headers: {'Content-Type':'application/json'},
|
|
3676
|
+
body: JSON.stringify({ contextTopK: contextTopK, minConfidence: minConfidence })
|
|
3677
|
+
}).then(function(data) {
|
|
3678
|
+
if (data.ok) {
|
|
3679
|
+
document.getElementById('memory-retrieval-status').textContent = 'Saved';
|
|
3680
|
+
toast('Memory retrieval settings updated');
|
|
3681
|
+
} else {
|
|
3682
|
+
document.getElementById('memory-retrieval-status').textContent = data.error || 'Error';
|
|
3683
|
+
}
|
|
3684
|
+
});
|
|
3685
|
+
}
|
|
3686
|
+
|
|
3687
|
+
function applyMemoryPreset(kind) {
|
|
3688
|
+
if (kind === 'strict') {
|
|
3689
|
+
document.getElementById('memory-context-topk').value = 2;
|
|
3690
|
+
document.getElementById('memory-min-confidence').value = 0.35;
|
|
3691
|
+
} else {
|
|
3692
|
+
document.getElementById('memory-context-topk').value = 4;
|
|
3693
|
+
document.getElementById('memory-min-confidence').value = 0.2;
|
|
3694
|
+
}
|
|
3695
|
+
document.getElementById('memory-retrieval-status').textContent = 'Preset applied';
|
|
3696
|
+
}
|
|
3697
|
+
|
|
3698
|
+
function loadToolScopeSettings() {
|
|
3699
|
+
api('/settings/tool-scopes').then(function(data) {
|
|
3700
|
+
document.getElementById('tool-scopes-allowed').value = (data.allowed || []).join(',');
|
|
3701
|
+
document.getElementById('tool-scopes-dry-run').value = data.dryRunByDefault ? 'true' : 'false';
|
|
3702
|
+
document.getElementById('tool-scopes-status').textContent = '';
|
|
3703
|
+
});
|
|
3704
|
+
}
|
|
3705
|
+
|
|
3706
|
+
function saveToolScopeSettings() {
|
|
3707
|
+
var allowed = document.getElementById('tool-scopes-allowed').value
|
|
3708
|
+
.split(',')
|
|
3709
|
+
.map(function(x) { return x.trim(); })
|
|
3710
|
+
.filter(Boolean);
|
|
3711
|
+
var dryRunByDefault = document.getElementById('tool-scopes-dry-run').value === 'true';
|
|
3712
|
+
api('/settings/tool-scopes', {
|
|
3713
|
+
method: 'PUT',
|
|
3714
|
+
headers: {'Content-Type':'application/json'},
|
|
3715
|
+
body: JSON.stringify({ allowed: allowed, dryRunByDefault: dryRunByDefault })
|
|
3716
|
+
}).then(function(data) {
|
|
3717
|
+
if (data.ok) {
|
|
3718
|
+
document.getElementById('tool-scopes-status').textContent = 'Saved';
|
|
3719
|
+
toast('Tool safety settings updated');
|
|
3720
|
+
} else {
|
|
3721
|
+
document.getElementById('tool-scopes-status').textContent = data.error || 'Error';
|
|
3722
|
+
}
|
|
3723
|
+
});
|
|
3724
|
+
}
|
|
3725
|
+
|
|
3726
|
+
function applyToolScopePreset(kind) {
|
|
3727
|
+
if (kind === 'safe') {
|
|
3728
|
+
document.getElementById('tool-scopes-allowed').value = 'memory,network_read,filesystem_read,config,scheduling';
|
|
3729
|
+
document.getElementById('tool-scopes-dry-run').value = 'true';
|
|
3730
|
+
} else {
|
|
3731
|
+
document.getElementById('tool-scopes-allowed').value = 'memory,network_read,filesystem_read,filesystem_write,config,scheduling,delegation';
|
|
3732
|
+
document.getElementById('tool-scopes-dry-run').value = 'true';
|
|
3733
|
+
}
|
|
3734
|
+
document.getElementById('tool-scopes-status').textContent = 'Preset applied';
|
|
3735
|
+
}
|
|
3594
3736
|
|
|
3595
3737
|
// --- Smart Routing ---
|
|
3596
3738
|
function loadSmartRouting() {
|