v3.2.2 - 2026-10-08 Type: patch Ask PacketSnitch "assume yes to all" research templates that suppress every approval prompt, storefront account-tier entitlement (plus the install UUID surfaced right in the Storefront panel), a theme-overridable CSS variable surface, and ps-metrics / catalog-admin logging improvements. ✨ Features "Assume yes to all" research templates — two new Ask PacketSnitch research templates join the approval-policy ladder: assume-yes-budgeted ("Assume yes to all (budgets enforced)") and assume-yes-unbudgeted ("Assume yes to all (budgets ignored)"). Both suppress every approval prompt — cost-bearing (quota / paid) calls, actions flagged requiresConfirmation, and the caller-policy allow-list gate — so the agent runs unattended. The budgeted variant still enforces the step, runtime, paid-spend, quota, and repeated-lookup budgets; the unbudgeted variant suspends them entirely, so the only stop is the model finishing (or the user cancelling). The policy carries a new enforceBudgets flag (isAssumeYesTemplate / templateEnforcesBudgets in src/ui/main-frontend/action-registry.js), the planner is told it may return "as many steps as the request needs" when budgets are off, and the investigator loop accepts an unbudgeted budget that keeps its counters for the run report but never halts (Number.POSITIVEINFINITY caps, unlimited candidate set, "unlimited" quota label). buildInvestigationBudget derives unbudgeted from the active template and strips it from any LLM-authored override, so the model cannot exempt itself. Settings → LLM disables and annotates the budget fields ("Budgets are ignored") when the unbudgeted template is selected, disables the unknown-cost prompt for both assume-yes templates, and the settings normalization accepts the new values while rejecting unknown ones. Covered by tests/keyboardshortcuts.test.js and tests/investigationbudgetoverride.test.js. Storefront account-tier license entitlement — the storefront now folds the account's paid tier into each catalog entry's owned flag instead of keying purely off per-item purchases. A paid tier (professional < enterprise) owns every theme outright, and license SKUs compare by rank, so a Professional install owns the Professional SKU and still sees Enterprise as a purchasable upgrade, while an Enterprise install owns both and is never offered a downgrade. resolveCatalogEntryOwned / licenseTierRank in src/main.js mirror LICENSETIERRANK on the catalog server; the renderer keys its Owned badge and hides the Buy button off the resulting flag. Paddle webhook grants are upgrade-only for the same reason, and the customer-group seat fan-out reports the highest non-free tier. Covered by tests/licenseentitlementowned.test.js and tests/testcatalogtiers.py. Install UUID in the Storefront panel — a read-only "This Install" section on the Storefront subtab shows the install UUID that licenses and theme purchases are bound to, with a one-click Copy button. The value is read from the same installapi.getInstallUuid bridge the license path uses and is re-read every time the panel opens, so a first-run UUID minted after startup still appears. Covered by tests/storefrontinstalluuid.test.js. Catalog self-service portal: names + verified email — the portal now separates signed in (holding the install UUID) from email proven: name and email are collected at the new POST /portal/profile endpoint, but the address is only written to installs.customeremail once it is verified — either by a 6-digit POST /portal/verify-email code or by a completed Paddle purchase (which proves the address during checkout). GET /portal returns name, emailVerified, profileComplete, and a greeting (falling back to the email local-part, then "Welcome back"). Code TTL, max attempts, and resend spacing are configurable via portalverificationcodettlseconds / portalverificationmaxattempts / portalverificationresendseconds. Covered by tests/testcatalogportalprofile.py and tests/testcatalogemailpairing.py. ps-metrics undated log aliases + catalog admin Logs tab — each ps-metrics NDJSON stream gains an undated hardlink alias (events-current.ndjson, installs-current.ndjson, errors-current.ndjson) so an operator can tail -f a stable path without first working out today's date. The aliases are hardlinks (no extra disk or I/O), are atomically re-pointed on rollover (os.link + os.replace), and are opened at startup — so they exist, possibly empty, as soon as the server is up. The catalog server's browser admin portal renders the ps-metrics server log and the raw NDJSON event stream on its Logs tab (configured with metricslogpath / metricseventsdir, capped at the trailing 2000 lines per pane). Covered by tests/testmetricsserver.py. Theme CSS variable surface + theme polish — the theme engine exposes new overridable variables (--dropdown-menu-bg, --stats-tag-button-text / --stats-tag-button-text-hover, --loading-screen-bg, --analysis-panel-bg / -text, --analysis-header-text, --analysis-content-bg / -text, --tab-btn-border) that default to the previously hardcoded values, so existing themes render unchanged while theme authors can now restyle the custom dropdowns, stats tag labels, capture loading screen, analysis workspace, and the main top-tab outline. Brushed Steel and Light fix unreadable input / stats-tag text (#9aa3af → #000000), Corporate Blue gains a full analysis/dropdown/loading palette, Nilla Horizon re-tints its tab outline and heatmap, and Haruhi Suzumiya / Weebo / Pink Lace set a light dropdown background. Covered by tests/themessubtab.test.js. 🐛 Fixes Error messages fade out — the full-screen error overlay now fades out after ~8 seconds (0.5s CSS opacity transition) and is still written to the activity log for later review; clicking the overlay dismisses it immediately and cancels the pending fade. Ask PacketSnitch context prompt hardening — the "Current PacketSnitch context" JSON no longer includes the filter-key catalog (it is surfaced separately as an explicit "Valid filter keys" list) so the model stops confusing key names with packet data values, and the appended action-response prompt now carries the previous plan step results. Plan step results also persist across chat turns (askPacketSnitchRecentStepResults, capped at 10) so a follow-up such as "extract everything from that archive" resolves an artifact a previous turn already listed; context changes, a new pcap load, and Clear chat all wipe the record. Wildcard name patterns — compileNamePattern now escapes regex metacharacters before converting the wildcards, so a literal . before a (for example metasploit.) is no longer swallowed by the wildcard expansion. ps-metrics first batch — a freshly restarted, idle metrics endpoint now creates today's streams (and their -current.ndjson aliases) at construction instead of waiting for the first batch, so the stable log paths exist exactly when an operator goes looking for them. v3.2.1 - 2026-10-05 Type: patch Modular feature-action contribution registry (one AI-callable action surface per feature area), artifact-store caching so the LLM resolves carved files by wildcard / substring fallback, and richer Stats-map location focus with hostname support. ✨ Features Modular feature-action contribution registry — the AI-callable action catalog is now contributed by per-feature modules in src/ui/main-frontend/feature-actions/ (bookmarks, notes, stats, extraction, evidence, session-filters, capture, decoders, locations, artifacts). Each module exports createActions(deps) → an array of descriptors; the registrar normalizes them (defaults allowedCallers to ["ai"], fills domain/label, drops descriptors without an invoke) and registers them all into the live action registry. Because the rat-flock tool catalog (buildFlockToolCatalog) is derived from actionRegistry.list(), every newly registered action is automatically reachable by the parallel investigator with no other wiring required. Covered by tests/featureactions.test.js, tests/actionregistryversioning.test.js, and tests/featureactionslocationsartifacts.test.js. Rat-flocking parallel investigation — Ask PacketSnitch can now run a parallel "rat flock" investigation: a shared scratchpad of findings is built up by multiple primitive LLM calls ("rats") running in phases (discover → drill → verify → synthesize → report), each spawning a configurable number of parallel investigators (flockRatCount) that see the evolving state and can drill into discoveries, cross-reference findings, or resolve contradictions. The orchestrator lives in src/flock-orchestrator.js, with the dynamic tool catalog in src/flock-tools.js (derived from the live action registry), delta scratchpad persistence as append-only JSONL in /flock-scratchpads/ .jsonl (crash-recoverable via rebuildScratchpadFromDeltas), and an in-flight controller registry in the main process so llm:flock-cancel can abort a running flock. A rat budget of zero collapses the pipeline to a single sequential LLM call. Covered by tests/flockmazebacktrack.test.js, tests/flocksoloweave.test.js, and tests/flocktoolcatalog.test.js. Stats locations + anomalies exposed to the AI — new read-only actions stats.locations.list / stats.locations.get surface the geolocated heatmap points (city, country, lat/lon, packet weight, source IPs, and hostnames) plus the capture-level "Most Active Mapped Locations" list without opening the Map tab, and stats.anomalies.list flattens the grouped Stats anomalies (protocol anomalies, port scans, brute force, baseline outliers, embedded content) into a bounded list with stable ids. A new stats.open-subtab action opens the statistics / map / anomalies subtabs programmatically. Covered by tests/statslocationfocus.test.js. Stats-map focus improved — stats.focus-location now accepts a hostname alongside IP / city+country / point id / raw lat+lon, validates coordinates before resolving (-90..90 / -180..180), and matches against the heatmap by id, IP, hostname, city/country, or location label. The return value carries the resolved hostnames so the caller can chain a filter-query on the discovered names. New collectStatsLocationHostnames / getStatsLocationHostnames helpers in src/ui/panels/stats-panel.js flatten the backend's reverse-DNS shape (nested arrays, dns.hostnames, dns.qname(s)) into a single hostname set. Extraction actions resolve via wildcard / substring fallback — when an exact fileId / fileName match fails, extraction.list-archive, extraction.decompress, and extraction.extract-entry retry against the artifact store snapshot using glob wildcards (*, ?) and case-insensitive substring containment, so an LLM that generates a slightly different name (case, partial name, glob) still resolves the correct carved file. extraction.extract-entry now also extracts every member of an archive when entryPath is omitted, registering each into the artifact store + Stats. Covered by tests/featureactions.test.js. Artifact-store caching for the LLM — the artifact store snapshot is now cached and surfaced through getFileArtifactSnapshot, so extraction actions can read persisted carved-file metadata (including base64 bytes) without re-resolving the live registry on every call. Each extracted member carries an explicit fileId / fileName so the artifact store and the Stats extraction registry stay in sync. Adaptive step-by-step plan reflection — Ask PacketSnitch plans no longer execute their pre-planned list blindly. After every completed step the executor hands the step's actual results back to the LLM, which analyzes them and decides the next step (stop when the goal is reached or the path dead-ends, or replace the remaining steps with ones derived from what the last step returned). Delegated steps are normalized through the same plan validator as classifier output; any malformed reflection falls back to the pre-planned remaining steps. A reflection budget (4 per run) and the research step budget cap the extra LLM calls, and the new settings.llm.planStepReflectEnabled toggle (default true) disables the reflection entirely. Step results now persist across chat turns (askPacketSnitchRecentStepResults, capped at 10) so follow-ups like "extract everything from that archive" resolve the archive a previous step already listed; context changes and Clear chat wipe the record. Covered by tests/askpacketsnitchadaptiveplan.test.js. 🐛 Fixes Ask PacketSnitch subtab routing — context plumbing between the renderer, the Ask PacketSnitch store, and the LLM dispatch now survives mid-stream navigation hops without dropping the active plan or rerunning the working message; subtab routing through ask-ps no longer drops the active conversation. Covered by tests/askpacketsnitch.test.js. Single threat-divergence logic — the LLM handler now applies a single consistent threat-divergence pass instead of re-evaluating each indicator independently, so contradictory findings collapse into one coherent verdict rather than surfacing as competing claims. LAN location auto-grab for metadata — the backend now attempts to auto-grab the location of each LAN interface in the session for metadata, so GeoIP / heatmap enrichment has a usable origin even when the analyst never manually tagged a subnet. Covered by tests/capturelocationdrop.test.js. Investigations stop cleanly on session reload / pcap load — rat-flock investigations now tear down properly when a session reloads or a new pcap loads, instead of leaving orphaned in-flight LLM calls that could fire against the wrong capture. Image decoder regression — a fallback decoder path restores image display (plus EXIF data) when the primary decoder fails, so corrupted or unusual images no longer blank the detail view. UI chrome alignment — the minimize button next to the quit button and the dash in the window title bar now align correctly across window states. v3.2.0 - 2026-09-25 Type: minor Backend threat-intel response caching (VirusTotal + Shodan InternetDB), LLM context tracking + floating panel ergonomics, and native RAR archive support in the Conv extractor. ✨ Features Backend threat-intel cache (VirusTotal + Shodan) — the Python backend now caches both virustotal and shodan lookup responses on disk ( /cache/threat-intel/threat-intel-cache.json, 24-hour TTL). Repeat lookups against the same key hit the cache instead of the upstream rate-limit budget, the cache survives backend restarts, and private/loopback IPs short-circuit to a clean supported: false, skippedReason: "private-network" payload so RFC1918 addresses never reach the public reputation API. loadThreatIntelCacheFromDisk() rehydrates the in-memory map on startup; readThreatIntelCache / writeThreatIntelCache are the only sanctioned read/write seams; isNonPublicIpv4OrIpv6Address and buildNonPublicIpResponse keep the VT and Shodan payload shapes consistent with a live response so the renderer renders either branch identically. Network errors and unauthorized responses are deliberately not cached. Covered by tests/testbackendthreatintelcache.py. RAR archive support in Conv's Extract subtab — snitchextract.py and the Node-side inferExtractionFormatFromBytes / list-archive / extract-archive-entry IPC path now handle .rar archives alongside .cab, .7z, .zip, .tar, .gz, .bzip2, and .zstd. Both signatures are detected — RAR 5.0 (Rar!\x1a\x07\x01\x00) and RAR 1.5–4.x (Rar!\x1a\x07\x00) — with RAR5 checked first so the longer signature never falls through to the seven-byte RAR3 match. Listing always works (rarfile.RarFile.infolist()); extraction of non-solid RAR3 archives uses rarfile's pure-Python rar3decompress, and solid RAR3 + RAR5 surface a clean "install unrar / unar" diagnostic instead of a stack trace. Bundled samples/rar/sample.rar fixture plus a SHA-256-pinned extract test and a system-unrar byte-equality oracle test ship in tests/testbackendrarextract.py. Better LLM context tracking — Ask PacketSnitch now keeps the active conversation context (filter query, target host, current packet, current subtab, recent lookup results) in sync with the LLM at every navigation hop. The renderer threads the same context shape into both Ask PacketSnitch and the AI research orchestrator so a question fired from one subtab stays anchored when the analyst navigates away mid-stream, and the working message reflects the live context instead of a stale snapshot. Cohere as a first-class LLM provider — Cohere joins Ollama and OpenRouter in Settings → LLM → Provider. The generateCohere handler in src/llm.js posts to the Cohere v2 chat completions API with the same per-call { maxTokens, temperature, think } overrides the other providers accept, and the response is normalized to the shared { response, model, done } shape. The default model is command-a-plus-05-2026. Resizable / movable floating panels — the log, chat, and other auxiliary panels now expose drag handles and a resize grip so analysts can pin them where they want them on screen. State survives across panel close/reopen within a session, and the new src/ui/panels/draggable-panel.js module keeps the resize math off the main render path. Covered by tests/draggablepanel.test.js. 🐛 Fixes Ask PacketSnitch working message — the in-flight "working…" message is now correctly invalidated when the active packet or filter changes mid-call, so analysts no longer see a stale summary after navigating away from the original target before the LLM responds. Covered by tests/askpacketsnitchworkingmessage.test.js. Ask PacketSnitch context-change regression — context plumbing between the renderer, the Ask PacketSnitch store, and the LLM dispatch now survives mid-stream navigation hops without dropping the active plan or rerunning the working message. Covered by tests/askpacketsnitchcontextchange.test.js.