@dreb/coding-agent 2.45.0 → 2.45.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/README.md +1 -1
- package/dist/core/export-html/template.js +56 -10
- package/docs/dashboard.md +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ Or use a custom provider (corporate proxy, Bedrock, etc.) — see [Custom provid
|
|
|
81
81
|
|
|
82
82
|
Then just talk to dreb. All 11 built-in tools are enabled by default: `read`, `write`, `edit`, `bash`, `grep`, `find`, `ls`, `web_search`, `web_fetch`, `subagent`, and `wait`. Use `--tools` to restrict to a subset (e.g., `--tools read,grep,find,ls` for read-only). Three additional tools — `search`, `skill`, and `tasks_update` — are always active regardless of `--tools`. `suggest_next` is active by default but excluded when `--tools` is specified. The model uses these to fulfill your requests. Add capabilities via [skills](#skills), [prompt templates](#prompt-templates), [extensions](#extensions), or [packages](#packages).
|
|
83
83
|
|
|
84
|
-
**Also available:** [`@dreb/telegram`](https://www.npmjs.com/package/@dreb/telegram) — run dreb as a Telegram bot with live tool status and visible results for user-facing tools (`npm install -g @dreb/telegram`). [`@dreb/dashboard`](https://www.npmjs.com/package/@dreb/dashboard) — run `dreb dashboard` for a browser UI with fleet overview, full chat steering, subagent observability, host file browser, curated appearance themes (per-browser light/dark), and Tailscale/rotating-code pairing (`npm install -g @dreb/dashboard`; see [docs/dashboard.md](docs/dashboard.md)). Compact SSE snapshots update live fleet cards without repeatedly fetching the cross-project inventory, and session drill-in hydrates state, messages, and background agents through one ordered snapshot request. Its top bar and persistent session header indicators report connecting, connected, retrying, resyncing, disconnected, or auth failed; bounded SSE replay plus an explicit snapshot barrier restores session state and
|
|
84
|
+
**Also available:** [`@dreb/telegram`](https://www.npmjs.com/package/@dreb/telegram) — run dreb as a Telegram bot with live tool status and visible results for user-facing tools (`npm install -g @dreb/telegram`). [`@dreb/dashboard`](https://www.npmjs.com/package/@dreb/dashboard) — run `dreb dashboard` for a browser UI with fleet overview, full chat steering, sanitized inline raster images returned by tools, subagent observability, host file browser, curated appearance themes (per-browser light/dark), and Tailscale/rotating-code pairing (`npm install -g @dreb/dashboard`; see [docs/dashboard.md](docs/dashboard.md)). Compact SSE snapshots update live fleet cards without repeatedly fetching the cross-project inventory, and session drill-in hydrates state, messages, and background agents through one ordered snapshot request. Its top bar and persistent session header indicators report connecting, connected, retrying, resyncing, disconnected, or auth failed; bounded SSE replay plus an explicit snapshot barrier restores session state, tasks, and tool-result images after a reload, restart, gap, backpressure disconnect, or stalled stream.
|
|
85
85
|
|
|
86
86
|
**Platform notes:** [Windows](docs/windows.md) | [Termux (Android)](docs/termux.md) | [tmux](docs/tmux.md) | [Terminal setup](docs/terminal-setup.md) | [Shell aliases](docs/shell-aliases.md)
|
|
87
87
|
|
|
@@ -14,6 +14,51 @@
|
|
|
14
14
|
const data = JSON.parse(new TextDecoder('utf-8').decode(bytes));
|
|
15
15
|
const { header, entries, leafId: defaultLeafId, systemPrompt, tools, renderedTools } = data;
|
|
16
16
|
|
|
17
|
+
// ============================================================
|
|
18
|
+
// IMAGE SANITIZATION
|
|
19
|
+
// ============================================================
|
|
20
|
+
|
|
21
|
+
// Exact raster MIME allowlist. Only these canonical MIME strings may be
|
|
22
|
+
// interpolated into a data: URI for an <img>. SVG is deliberately excluded
|
|
23
|
+
// because it can carry scripts/markup.
|
|
24
|
+
const ALLOWED_IMAGE_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
|
|
25
|
+
|
|
26
|
+
// Strict base64: one-or-more groups of the base64 alphabet with optional
|
|
27
|
+
// '=' padding. Rejects whitespace, data-URI prefixes, quotes, angle
|
|
28
|
+
// brackets, or any character that could break out of the src attribute or
|
|
29
|
+
// inject additional markup/attributes.
|
|
30
|
+
const BASE64_PATTERN = /^[A-Za-z0-9+/]+={0,2}$/;
|
|
31
|
+
|
|
32
|
+
function isPlausibleBase64(value) {
|
|
33
|
+
if (typeof value !== 'string' || value.length === 0) return false;
|
|
34
|
+
if (value.length % 4 !== 0) return false;
|
|
35
|
+
return BASE64_PATTERN.test(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Build a safe `data:` URI for an image content block, or return null when
|
|
40
|
+
* the MIME type is not an allowlisted raster type or the base64 payload is
|
|
41
|
+
* not strictly valid. Because only canonical allowlisted MIME strings and
|
|
42
|
+
* validated base64 are interpolated, crafted values cannot create extra
|
|
43
|
+
* attributes or break out into markup.
|
|
44
|
+
*/
|
|
45
|
+
function sanitizeImageDataUri(mimeType, base64Data) {
|
|
46
|
+
if (typeof mimeType !== 'string') return null;
|
|
47
|
+
if (!ALLOWED_IMAGE_MIME_TYPES.has(mimeType)) return null;
|
|
48
|
+
if (!isPlausibleBase64(base64Data)) return null;
|
|
49
|
+
return `data:${mimeType};base64,${base64Data}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Render an <img> tag for an image content block, or '' if it fails
|
|
54
|
+
* sanitization. Shared by the tool-result and message image paths.
|
|
55
|
+
*/
|
|
56
|
+
function renderImageTag(img, className) {
|
|
57
|
+
const src = sanitizeImageDataUri(img && img.mimeType, img && img.data);
|
|
58
|
+
if (src === null) return '';
|
|
59
|
+
return `<img src="${src}" class="${className}" />`;
|
|
60
|
+
}
|
|
61
|
+
|
|
17
62
|
// ============================================================
|
|
18
63
|
// URL PARAMETER HANDLING
|
|
19
64
|
// ============================================================
|
|
@@ -879,9 +924,9 @@
|
|
|
879
924
|
const renderResultImages = () => {
|
|
880
925
|
const images = getResultImages();
|
|
881
926
|
if (images.length === 0) return '';
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
927
|
+
const tags = images.map(img => renderImageTag(img, 'tool-image')).filter(Boolean);
|
|
928
|
+
if (tags.length === 0) return '';
|
|
929
|
+
return '<div class="tool-images">' + tags.join('') + '</div>';
|
|
885
930
|
};
|
|
886
931
|
|
|
887
932
|
let html = `<div class="tool-execution ${statusClass}">`;
|
|
@@ -915,7 +960,6 @@
|
|
|
915
960
|
|
|
916
961
|
html += `<div class="tool-header"><span class="tool-name">read</span> <span class="tool-path">${pathHtml}</span></div>`;
|
|
917
962
|
if (result) {
|
|
918
|
-
html += renderResultImages();
|
|
919
963
|
const output = getResultText();
|
|
920
964
|
const lang = filePath ? getLanguageFromPath(filePath) : null;
|
|
921
965
|
if (output) html += formatExpandableOutput(output, 10, lang);
|
|
@@ -1000,6 +1044,11 @@
|
|
|
1000
1044
|
}
|
|
1001
1045
|
}
|
|
1002
1046
|
|
|
1047
|
+
// Sanitized tool-result images render generically for every tool name.
|
|
1048
|
+
// Kept out of the per-tool switch so it applies uniformly (and only once)
|
|
1049
|
+
// to built-in and custom tools alike.
|
|
1050
|
+
html += renderResultImages();
|
|
1051
|
+
|
|
1003
1052
|
html += '</div>';
|
|
1004
1053
|
return html;
|
|
1005
1054
|
}
|
|
@@ -1128,12 +1177,9 @@
|
|
|
1128
1177
|
|
|
1129
1178
|
if (Array.isArray(content)) {
|
|
1130
1179
|
const images = content.filter(c => c.type === 'image');
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
html += `<img src="data:${img.mimeType};base64,${img.data}" class="message-image" />`;
|
|
1135
|
-
}
|
|
1136
|
-
html += '</div>';
|
|
1180
|
+
const imageTags = images.map(img => renderImageTag(img, 'message-image')).filter(Boolean);
|
|
1181
|
+
if (imageTags.length > 0) {
|
|
1182
|
+
html += '<div class="message-images">' + imageTags.join('') + '</div>';
|
|
1137
1183
|
}
|
|
1138
1184
|
}
|
|
1139
1185
|
|
package/docs/dashboard.md
CHANGED
|
@@ -120,12 +120,29 @@ networking window above.
|
|
|
120
120
|
| Screen | What it does |
|
|
121
121
|
|---|---|
|
|
122
122
|
| **Fleet** | Home. Live-first: one grid of every live session at the top — status chip (● running / ◆ needs-attention / ○ idle / ✕ error), project path, activity line, live subagent lines, tasks progress, ctx%, model, last activity. Live cards keep a deterministic order by project path, then session start time; needs-attention cards badge the browser tab without jumping around. Below the grid: past sessions grouped by project, three compact rows per group with an "all N on disk" expander, resume and delete. |
|
|
123
|
-
| **Session view** | Full chat drill-in. Markdown streaming transcript (text, thinking blocks with expand preference, agent-result cards, tool cards with bespoke read/write/edit/bash bodies plus full expandable inputs
|
|
123
|
+
| **Session view** | Full chat drill-in. Markdown streaming transcript (text, thinking blocks with expand preference, agent-result cards, tool cards with bespoke read/write/edit/bash bodies plus full expandable inputs, markdown-rendered results for markdown-contract tools like subagent/skill/web_fetch/suggest_next, and inline tool-result images, compaction/branch summaries, custom messages), per-message copy, tasks panel, subagent strip, status line with elapsed time plus ■ stop and compaction/retry aborts, a persistent session-header live indicator, and an info bar with cwd, branch, session name, token breakdown, cost/(sub)/daily rollup, ctx%, median tok/s, and a stats popover. Composer supports auto-grow, history, `/` autocomplete from `get_commands`, image attach/paste, queued-message chips with restore-all, steer/follow-up modes, and suggest-next. The ⋯ menu covers export HTML, compact, rename, fork-from-message, loaded context, and tool expand/collapse. Session names update live from manual rename or auto-naming. Extension UI requests (select/confirm/input/editor) render as modals; notifications as toasts. |
|
|
124
124
|
| **Subagent view** | Read-only transcript of a background agent: live events via the RPC relay, hydrated from the agent's on-disk session log (`/subagents/:agentId/messages`) so the view survives browser reloads. Shows the task, streaming output, and tool activity. No composer — subagents can't be steered yet; the parent session controls them. |
|
|
125
125
|
| **Files** | Host-wide browser with places shortcuts (home, /tmp, project roots), breadcrumbs to `/`, new-folder, download, drop-zone/picker upload with explicit collision prompts, and "new session here" on any directory. It also shows the **effective global nested-context trust** for the displayed canonical directory: untrusted, trusted by that root, inherited from a granting root, or global expert trust-all. You can trust the displayed folder and descendants, or untrust the actual granting root; untrusting an inherited folder removes that root's trust for all descendants. |
|
|
126
126
|
| **Settings** | Persistent defaults (default model, thinking level, steering/follow-up queue modes, auto-compaction, auto-retry) via `get_settings`/`set_settings` — validation errors are shown verbatim. Entering Settings flushes pending writes and reloads durable global + project settings, so external edits appear; read, parse, or write failures fail loudly instead of showing stale settings. The global-only nested-context policy lists every explicit trusted root for audit and revoke, offers a simple add-by-path control, and includes a prominently warned expert trust-all toggle; the Files view remains the primary place to grant trust while browsing. Most defaults seed new sessions; context-trust changes are observed by active main/subagent processes for future lazy loads, but cannot remove already injected content. Dashboard-local preferences (always expand thinking, needs-attention notification permission) live in the browser, alongside an appearance section: a theme gallery of eight curated themes (entropist.ca, Dim, Solarized, Gruvbox, Caves of Qud, Van Gogh, and the colorblind-safe Okabe-Ito and Paul Tol) with live preview cards and a system/light/dark mode selector, saved per browser. Shows the current rotating pairing code on the host/local dashboard, plus the paired-devices list with unpair. |
|
|
127
127
|
| **Pairing** | Remote first-login: identity echo, rotating-code entry, and the security copy explaining what pairing grants. |
|
|
128
128
|
|
|
129
|
+
### Tool-result images
|
|
130
|
+
|
|
131
|
+
Tool results containing PNG, JPEG, GIF, or WebP image blocks render inline in
|
|
132
|
+
any tool card, not only `read`. This human-facing rendering is independent of
|
|
133
|
+
model vision support: a text-only model can omit an image from its own context
|
|
134
|
+
while the dashboard still shows it. MIME types use an exact raster allowlist,
|
|
135
|
+
SVG is rejected, and payloads must be valid base64 before the client builds a
|
|
136
|
+
`data:` URI. HTML transcript exports apply the same rules and embed accepted
|
|
137
|
+
images.
|
|
138
|
+
|
|
139
|
+
Small results arrive in their normal live event. If an image makes an event
|
|
140
|
+
exceed the SSE per-event byte budget, the existing resync barrier restores the
|
|
141
|
+
full persisted result over HTTP; refresh and subagent hydration use the same
|
|
142
|
+
image-aware transcript path. Display dimensions are bounded, but the underlying
|
|
143
|
+
bytes are not compressed by the dashboard. Lower-bandwidth thumbnail and HD
|
|
144
|
+
loading behavior is tracked separately.
|
|
145
|
+
|
|
129
146
|
## Fleet transport and freshness
|
|
130
147
|
|
|
131
148
|
A normal dashboard load makes one authoritative `GET /api/fleet`; the fleet is
|