Disable What's New cards
Move active What's New content into an empty slide list so the startup modal stays quiet until new cards are added. Keep manual Open available with an empty state, and update the static test plus DOX contract for dormant periods.
Alessandro committed
Jul 2, 2026 at 16:44 UTC
0a9e15be8770430af84c6e6ea02f79cd086af979
7 files changed
+64
-81
plugins/AGENTS.md
+1
-1
@@ -93,5 +93,5 @@ Direct child DOX files:
93
| [_text_editor/AGENTS.md](_text_editor/AGENTS.md) | Native text read, write, and patch tool. |
94
| [_time_travel/AGENTS.md](_time_travel/AGENTS.md) | Workspace history, diff, travel, snapshot, and revert flows. |
95
| [_whatsapp_integration/AGENTS.md](_whatsapp_integration/AGENTS.md) | WhatsApp Baileys bridge integration. |
96
-| [_whats_new/AGENTS.md](_whats_new/AGENTS.md) | Version-gated What's New showcase modal and startup trigger. |
96
+| [_whats_new/AGENTS.md](_whats_new/AGENTS.md) | Version-gated What's New showcase modal, card list, and startup trigger. |
97
| [_whisper_stt/AGENTS.md](_whisper_stt/AGENTS.md) | Whisper speech-to-text integration. |
plugins/_whats_new/AGENTS.md
+8
-5
@@ -2,20 +2,22 @@
2
3
## Purpose
4
5
-- Own the built-in version-gated "What's New" modal for showcasing Agent Zero features after updates.
5
+- Own the built-in version-gated "What's New" modal for showcasing Agent Zero features after updates, dormant when no cards are configured.
6
7
## Ownership
8
9
- `plugin.yaml` owns metadata and always-enabled status.
10
- `webui/main.html` owns the canonical modal opened by startup and the Builtin Plugins `Open` button.
11
- `webui/whats-new.html` is a compatibility redirect to `webui/main.html`.
12
+- `webui/whats-new-slides.js` owns the current card list; an empty list disables automatic display.
13
- `webui/` owns the Alpine store, copy, and showcase media assets.
13
-- `extensions/webui/initFw_end/` owns the startup trigger that opens the modal once per newer installed version unless the user has permanently opted out.
14
+- `extensions/webui/initFw_end/` owns the startup trigger that opens the modal once per newer installed version when cards exist unless the user has permanently opted out.
15
16
## Local Contracts
17
18
- Closing, Skip, or Done records only the current installed version as seen.
18
-- Future updates should auto-open the modal again unless the user checks the modal's permanent opt-out checkbox.
19
+- Future updates with cards should auto-open the modal again unless the user checks the modal's permanent opt-out checkbox.
20
+- Do not auto-open the modal when `webui/whats-new-slides.js` exports no cards.
21
- The permanent opt-out is stored in browser-local state under `a0_whats_new_never_show`.
22
- Honor the legacy `a0_whats_new_seen_version` browser-local marker as the last seen version.
23
- Keep the modal copy concise, left-aligned, and paired with feature media.
@@ -24,7 +26,7 @@
26
27
## Work Guidance
28
27
-- Add showcase assets under `webui/assets/` and reference them through `/plugins/_whats_new/webui/assets/...`.
29
+- Add showcase cards in `webui/whats-new-slides.js`; add assets under `webui/assets/` and reference them through `/plugins/_whats_new/webui/assets/...`.
30
- Keep the startup extension idempotent and tolerant of missing or non-comparable version labels.
31
- Prefer release-line comparisons over development commit-distance comparisons so local development builds do not reopen the modal on every commit.
32
- Preserve `webui/main.html` so the plugin list exposes the standard `Open` action.
@@ -32,7 +34,8 @@
34
## Verification
35
36
- Run `pytest tests/test_whats_new_static.py` after changing the modal, trigger, or assets.
35
-- Smoke-test startup display, slide navigation, dismissal, same-version reload behavior, newer-version display behavior, opt-out behavior, and manual Builtin Plugins `Open` behavior in the WebUI.
37
+- When the card list is empty, smoke-test no startup modal and the manual Builtin Plugins `Open` empty state when practical.
38
+- When cards exist, smoke-test startup display, slide navigation, dismissal, same-version reload behavior, newer-version display behavior, opt-out behavior, and manual Builtin Plugins `Open` behavior in the WebUI.
39
40
## Child DOX Index
41
plugins/_whats_new/extensions/webui/initFw_end/whats-new.js
+2
@@ -1,4 +1,5 @@
1
import { getModalStack, isModalOpen, openModal } from "/js/modals.js";
2
+import { slides } from "/plugins/_whats_new/webui/whats-new-slides.js";
3
4
const MODAL_PATH = "/plugins/_whats_new/webui/main.html";
5
const LEGACY_MODAL_PATH = "/plugins/_whats_new/webui/whats-new.html";
@@ -109,6 +110,7 @@ function shouldNeverShow() {
110
}
111
112
function shouldShowWhatsNew(version = currentVersion()) {
113
+ if (!slides.length) return false;
114
if (shouldNeverShow()) return false;
115
if (!version) return false;
116
plugins/_whats_new/webui/main.html
+19
-5
@@ -10,7 +10,11 @@
10
<div x-data>
11
<template x-if="$store.whatsNew">
12
<div class="whats-new-shell" x-init="$store.whatsNew.onOpen()" x-destroy="$store.whatsNew.cleanup()">
13
- <section class="whats-new-media-panel" :aria-label="$store.whatsNew.currentSlide.mediaLabel">
13
+ <section
14
+ class="whats-new-media-panel"
15
+ x-show="$store.whatsNew.hasSlides()"
16
+ :aria-label="$store.whatsNew.currentSlide.mediaLabel"
17
+ >
18
<template x-if="$store.whatsNew.currentSlide.mediaType === 'video'">
19
<video
20
class="whats-new-media"
@@ -35,7 +39,10 @@
39
<div class="whats-new-eyebrow" x-text="$store.whatsNew.currentSlide.eyebrow"></div>
40
<h3 x-text="$store.whatsNew.currentSlide.title"></h3>
41
<p class="whats-new-summary" x-text="$store.whatsNew.currentSlide.summary"></p>
38
- <ul class="whats-new-bullets">
42
+ <ul
43
+ class="whats-new-bullets"
44
+ x-show="$store.whatsNew.currentSlide.bullets.length"
45
+ >
46
<template x-for="item in $store.whatsNew.currentSlide.bullets" :key="item">
47
<li x-text="item"></li>
48
</template>
@@ -44,7 +51,7 @@
51
52
<div class="modal-footer whats-new-footer" data-modal-footer>
53
<div class="whats-new-footer-left">
47
- <div class="whats-new-footer-progress">
54
+ <div class="whats-new-footer-progress" x-show="$store.whatsNew.hasSlides()">
55
<span class="whats-new-progress-label" x-text="$store.whatsNew.progressLabel()"></span>
56
<div class="whats-new-progress-dots" role="tablist" aria-label="What's New slides">
57
<template x-for="(slide, index) in $store.whatsNew.slides" :key="slide.id">
@@ -60,7 +67,7 @@
67
</template>
68
</div>
69
</div>
63
- <label class="whats-new-never-show">
70
+ <label class="whats-new-never-show" x-show="$store.whatsNew.hasSlides()">
71
<input
72
type="checkbox"
73
:checked="$store.whatsNew.neverShowAgain"
@@ -70,7 +77,14 @@
77
</label>
78
</div>
79
<div class="whats-new-footer-actions">
73
- <button type="button" class="btn btn-cancel" @click="$store.whatsNew.skip()">Skip</button>
80
+ <button
81
+ type="button"
82
+ class="btn btn-cancel"
83
+ x-show="$store.whatsNew.hasSlides()"
84
+ @click="$store.whatsNew.skip()"
85
+ >
86
+ Skip
87
+ </button>
88
<button
89
type="button"
90
class="btn btn-field"
plugins/_whats_new/webui/whats-new-slides.js
new
+1
@@ -0,0 +1 @@
1
+export const slides = [];
plugins/_whats_new/webui/whats-new-store.js
+16
-52
@@ -1,59 +1,18 @@
1
import { createStore } from "/js/AlpineStore.js";
2
import { closeModal } from "/js/modals.js";
3
+import { slides } from "/plugins/_whats_new/webui/whats-new-slides.js";
4
4
-const ASSET_BASE = "/plugins/_whats_new/webui/assets";
5
const NEVER_SHOW_STORAGE_KEY = "a0_whats_new_never_show";
6
7
-const slides = [
8
- {
9
- id: "parallel-tools",
10
- eyebrow: "Parallel execution",
11
- title: "Parallel tool calls and subagents",
12
- summary:
13
- "Agent Zero can now split work across parallel tool and subagents calls and combine concurrent steps results.",
14
- mediaType: "video",
15
- media: `${ASSET_BASE}/parallel-subs.webm`,
16
- mediaLabel:
17
- "Four Agent Zero subagents working in parallel while the parent agent coordinates the result.",
18
- bullets: [
19
- "Launch coordinated subagents to explore separate paths at the same time.",
20
- "Run mixed batches together: search queries, code execution, file reads, writes, and more.",
21
- "Merge the results back into one answer without waiting through every call in sequence.",
22
- ],
23
- },
24
- {
25
- id: "mcp-configuration",
26
- eyebrow: "MCP configuration",
27
- title: "Redesigned MCP configuration UI",
28
- summary:
29
- "Global Settings and Projects now share a cleaner MCP setup flow for command and Remote URL transports.",
30
- mediaType: "image",
31
- media: `${ASSET_BASE}/mcp-servers.png`,
32
- mediaLabel:
33
- "The redesigned MCP servers screen showing server cards, transport controls, and raw JSON mode.",
34
- bullets: [
35
- "Configure npx, uvx, or custom command servers with clearer fields.",
36
- "Connect Remote URL transports from the same accessible editor.",
37
- "Switch to Raw JSON when you want to paste or move configurations between clients.",
38
- ],
39
- },
40
- {
41
- id: "skills-scanner",
42
- eyebrow: "Agent security",
43
- title: "Skills Scanner powered by Snyk Agent Scan",
44
- summary:
45
- "Scan your agent skills and MCP-connected surfaces for prompt injections and vulnerabilities.",
46
- mediaType: "image",
47
- media: `${ASSET_BASE}/skills-scanner.png`,
48
- mediaLabel:
49
- "The Skills Scanner screen showing Snyk Agent Scan controls and scan guidance.",
50
- bullets: [
51
- "Review skills with the same scanning flow you already use for plugins.",
52
- "Find prompt-injection risks and vulnerable instructions before they reach runtime.",
53
- "Catch risky skill instructions early, before they become part of an agent workflow.",
54
- ],
55
- },
56
-];
7
+const emptySlide = {
8
+ eyebrow: "What's New",
9
+ title: "No new updates right now",
10
+ summary: "New highlights will appear here when there are fresh Agent Zero updates.",
11
+ mediaType: "none",
12
+ media: "",
13
+ mediaLabel: "No new updates right now.",
14
+ bullets: [],
15
+};
16
17
function storageValue(key) {
18
try {
@@ -109,7 +68,11 @@ export const store = createStore("whatsNew", {
68
},
69
70
get currentSlide() {
112
- return this.slides[this.currentIndex] || this.slides[0];
71
+ return this.slides[this.currentIndex] || emptySlide;
72
+ },
73
+
74
+ hasSlides() {
75
+ return this.slides.length > 0;
76
},
77
78
isFirst() {
@@ -121,6 +84,7 @@ export const store = createStore("whatsNew", {
84
},
85
86
progressLabel() {
87
+ if (!this.hasSlides()) return "";
88
return `${this.currentIndex + 1} of ${this.slides.length}`;
89
},
90
tests/test_whats_new_static.py
+17
-18
@@ -5,9 +5,12 @@ PROJECT_ROOT = Path(__file__).resolve().parents[1]
5
WHATS_NEW_PLUGIN = PROJECT_ROOT / "plugins/_whats_new"
6
7
8
-def test_whats_new_modal_uses_showcase_assets_and_branded_footer():
8
+def test_whats_new_modal_handles_empty_showcase_state():
9
html = (WHATS_NEW_PLUGIN / "webui/main.html").read_text(encoding="utf-8")
10
store = (WHATS_NEW_PLUGIN / "webui/whats-new-store.js").read_text(encoding="utf-8")
11
+ slide_data = (WHATS_NEW_PLUGIN / "webui/whats-new-slides.js").read_text(
12
+ encoding="utf-8"
13
+ )
14
15
assert "What's New in Agent Zero" in html
16
assert "data-modal-footer" in html
@@ -16,25 +19,19 @@ def test_whats_new_modal_uses_showcase_assets_and_branded_footer():
19
assert "type=\"checkbox\"" in html
20
assert "Don't show automatically again" in html
21
assert "/plugins/_whats_new/webui/whats-new-store.js" in html
19
- assert "/plugins/_whats_new/webui/assets" in store
22
+ assert "/plugins/_whats_new/webui/whats-new-slides.js" in store
23
assert "a0_whats_new_never_show" in store
24
+ assert "No new updates right now" in store
25
+ assert "hasSlides()" in html + store
26
+ assert "export const slides = [];" in slide_data
27
22
- for asset in ["parallel-subs.webm", "mcp-servers.png", "skills-scanner.png"]:
23
- assert asset in html + store
24
- assert (PROJECT_ROOT / "plugins/_whats_new/webui/assets" / asset).exists()
25
-
26
- assert "Parallel tool calls and subagents" in store
27
- assert (
28
- "Agent Zero can now split work across parallel tool and subagents calls and combine concurrent steps results."
29
- in store
30
- )
31
- assert "Redesigned MCP configuration UI" in store
32
- assert "Skills Scanner powered by Snyk Agent Scan" in store
33
- assert "Remote URL transports" in store
34
- assert "Raw JSON" in store
35
- assert "prompt-injection risks" in store
36
- assert "Catch risky skill instructions early" in store
37
- assert "Include MCP servers in the same pass" not in store
28
+ old_cards = html + store + slide_data
29
+ assert "Parallel tool calls and subagents" not in old_cards
30
+ assert "Redesigned MCP configuration UI" not in old_cards
31
+ assert "Skills Scanner powered by Snyk Agent Scan" not in old_cards
32
+ assert "parallel-subs.webm" not in old_cards
33
+ assert "mcp-servers.png" not in old_cards
34
+ assert "skills-scanner.png" not in old_cards
35
36
37
def test_whats_new_legacy_modal_path_redirects_to_main_screen():
@@ -53,6 +50,8 @@ def test_whats_new_startup_trigger_is_version_gated_with_opt_out():
50
assert "globalThis.gitinfo?.version" in content
51
assert "a0_whats_new_seen_version" in content
52
assert "a0_whats_new_never_show" in content
53
+ assert "/plugins/_whats_new/webui/whats-new-slides.js" in content
54
+ assert "slides.length" in content
55
assert "/plugins/_whats_new/webui/main.html" in content
56
assert "/plugins/_whats_new/webui/whats-new.html" in content
57
assert "compareVersions" in content