v1.5.1 — Fix chat system, add system tray icon, agent mode improvements

- Fix Hal chat: add Chat/Agent mode toggle so users can switch between
  direct LLM streaming (Chat) and tool-using Agent mode
- Fix Agent system: graceful degradation when model can't follow
  structured THOUGHT/ACTION/PARAMS format (falls back to direct answer
  after 2 parse failures instead of looping 20 times)
- Fix frozen build: remove llama_cpp from PyInstaller excludes list
  so LLM works in compiled exe
- Add system tray icon: autarch.ico (from icon.svg) used for exe icons,
  installer shortcuts, and runtime tray icon
- Update tray.py to load .ico file with fallback to programmatic generation
- Add inline critical CSS for FOUC prevention
- Bump version to 1.5.1

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
DigiJ 2026-03-02 23:13:13 -08:00
parent 13cdc5657e
commit 67b7edc696
15 changed files with 497 additions and 31 deletions

179
DEVLOG.md
View File

@ -5373,3 +5373,182 @@ Added local network discovery so Archon can auto-find AUTARCH servers without ma
--- ---
## Session 16 — 2026-03-01: Threat Monitor Enhancement, Hal Agent Mode, Windows Defense, LLM Trainer
### Phase 4.17 — Threat Monitor Enhancement (7-tab Threat Monitor)
Expanded the Threat Monitor from 4 tabs to 7, adding Network Intel, Packet Capture, and DDoS Mitigation capabilities.
**Files Changed:**
- `modules/defender_monitor.py` — Added ~15 new methods + singleton `get_threat_monitor()`
- `web/routes/defense.py` — Added ~25 new routes under `/defense/monitor/`
- `web/templates/defense_monitor.html` — 3 new tabs (7 total), drill-down popups
**New ThreatMonitor methods:**
- `get_bandwidth()` — bytes in/out per interface + deltas (PowerShell / `/proc/net/dev`)
- `check_arp_spoofing()` — multiple MACs per IP detection (`arp -a` / `ip neigh show`)
- `check_new_listening_ports()` — alert on new listeners since baseline
- `geoip_lookup(ip)` — country/ISP/ASN via ipwho.is API
- `get_connections_with_geoip()` — connection table enriched with geo data
- `get_connection_rate()` — connections/sec trending
- `detect_ddos()` — SYN flood / connection flood / bandwidth spike detection
- `get_top_talkers(limit)` — top IPs by connection count
- `apply_rate_limit(ip, rate)` / `remove_rate_limit(ip)` — per-IP rate limiting (netsh / iptables)
- `get_syn_protection_status()` / `enable_syn_protection()` — SYN cookies
- `get_ddos_config()` / `save_ddos_config()` — auto-mitigation config (data/ddos_config.json)
- `auto_mitigate()` — auto-block offenders if thresholds exceeded
- `get_mitigation_history()` / `log_mitigation()` — action log (data/mitigation_log.json)
**New routes (under `/defense/monitor/`):**
- Monitoring: `bandwidth`, `arp-check`, `new-ports`, `geoip`, `connections-geo`, `connection-rate`
- Packet Capture: `capture/interfaces`, `capture/start`, `capture/stop`, `capture/stats`, `capture/stream` (SSE), `capture/protocols`, `capture/conversations`
- DDoS: `ddos/detect`, `ddos/top-talkers`, `ddos/rate-limit`, `ddos/rate-limit/remove`, `ddos/syn-status`, `ddos/syn-enable`, `ddos/syn-disable`, `ddos/config` (GET/POST), `ddos/auto-mitigate`, `ddos/history`, `ddos/history/clear`
**7 tabs in defense_monitor.html:**
1. **Live Monitor** — enhanced with bandwidth cards, ARP/port/DDoS counters, drill-down popups
2. **Connections** — existing, with clickable rows for connection details
3. **Network Intel** — bandwidth table, ARP spoof check, listening port monitor, GeoIP lookup, connections+GeoIP
4. **Threats** — existing threat list with drill-down
5. **Packet Capture** — interface selector, BPF filter, duration, start/stop, live packet SSE stream, protocol distribution, top conversations
6. **DDoS Mitigation** — detection status, top talkers, SYN protection toggle, rate limiting per IP, auto-mitigation config, mitigation history
7. **Counter-Attack** — existing
**Drill-down popups (`.tmon-overlay` + `.tmon-popup`):**
- Click any stat in Live Monitor → modal popup with detailed data table
- Connections popup with clickable rows → individual connection detail card
- CSS added: `.tmon-overlay`, `.tmon-popup`, `.tmon-popup-header`, `.tmon-popup-body`, `.tmon-stat-clickable`, `.tmon-detail-card`, `.tmon-row-clickable`, `.tmon-back-btn`
### Phase 4.18 — Hal Agent Mode + Module Factory
Wired Hal chat to the Agent system so it can create new AUTARCH modules on demand.
**Files Changed:**
- `core/tools.py` — added `create_module` tool to ToolRegistry
- `web/routes/chat.py` — rewritten to use Agent system with system prompt; agent-mode SSE streaming
- `data/hal_system_prompt.txt` (NEW) — Hal's codebase knowledge (~2000 tokens)
**`create_module` tool:**
- Validates category (defense/offense/counter/analyze/osint/simulate)
- Validates code contains required module attributes (NAME, DESCRIPTION, VERSION, CATEGORY, def run())
- Prevents overwriting existing modules
- Writes to `modules/{name}.py`
- Attempts `importlib.util.spec_from_file_location` to verify valid Python
- If import fails, deletes the file and returns the error
**Chat route rewrite:**
- Loads system prompt from `data/hal_system_prompt.txt`
- Detects action requests → Agent mode vs simple chat
- Agent mode: creates `Agent(llm, tools)`, runs in background thread, streams steps via SSE
- SSE events: `thought`, `action`, `result`, `token`, `done`, `error`
### Phase 4.19 — Windows Defense Sub-Page
**Files Created:**
- `modules/defender_windows.py` — Windows security module with firewall, UAC, Defender AV, services, SSH, NTFS, event logs
- `web/templates/defense_windows.html` — multi-tab Windows defense UI
**Files Changed:**
- `web/routes/defense.py` — added `defense.windows_index` route + Windows-specific API routes
- `web/templates/base.html` — added Linux/Windows/Threat Monitor sub-items under Defense sidebar
### Phase 4.20 — LLM Trainer
**Files Created:**
- `modules/llm_trainer.py` — LLM fine-tuning module (dataset management, training config, adapter listing)
- `web/routes/llm_trainer.py` — Flask blueprint for LLM Trainer page
- `web/templates/llm_trainer.html` — LLM Trainer UI
**Features:**
- Dataset management (create, list, delete JSONL datasets)
- Training configuration (model, epochs, learning rate, batch size)
- Adapter listing (LoRA/QLoRA adapters)
- Training status monitoring
### Refresh Modules Button
**Files Changed:**
- `web/templates/base.html` — added "Refresh Modules" button in sidebar
- `web/static/js/app.js``reloadModules()` function POSTs to `/settings/reload-modules`
- `web/routes/settings.py``POST /settings/reload-modules` route calls `MenuSystem.reload_modules()`
---
## Session 17 — 2026-03-02: System Tray, Dual-Exe Build, Installer Scripts, v1.5 Release
### Phase 4.21 — System Tray Icon
**Files Created:**
- `core/tray.py``TrayManager` class using pystray + PIL
**Files Changed:**
- `autarch.py` — added `--no-tray` flag, tray integration in `--web` mode
**TrayManager features:**
- Auto-generates dark circle icon with cyan "A" using PIL
- Menu: status line, Start, Stop, Restart, Open Dashboard, Exit
- Dynamic menu state (Start disabled when running, Stop/Restart disabled when stopped)
- Uses `werkzeug.serving.make_server` for threaded Flask in background
- SSL context passthrough for HTTPS
- `TRAY_AVAILABLE` flag for graceful fallback on systems without pystray
### Phase 4.22 — Dual Executable Build + Frozen Path Support
**Files Created:**
- `autarch_web.py` — Windowless web launcher entry point (Win32GUI, no console window)
**Files Changed:**
- `core/paths.py` — Frozen build support with dual-directory pattern
- `core/menu.py` — Module loading scans both bundled and user module directories
- `web/app.py` — Template/static paths resolve correctly in frozen (PyInstaller) builds
**Frozen build architecture:**
- `_FROZEN = getattr(sys, 'frozen', False)` detection
- `_BUNDLE_DIR` = `Path(sys._MEIPASS)` when frozen (read-only assets)
- `_APP_DIR` = `Path(sys.executable).parent` when frozen (writable data)
- New: `is_frozen()`, `get_bundle_dir()`, `get_user_modules_dir()`
- `get_config_path()` copies bundled config to writable location on first run
- Module loading: scans both `get_modules_dir()` (bundle) and `get_user_modules_dir()` (user), user overrides bundled
### Phase 4.23 — Installer Scripts
**Files Created:**
- `installer.iss` — Inno Setup script (lzma2, no solid compression for large files)
- `installer.nsi` — NSIS script with MUI2, Start Menu, desktop shortcut, uninstaller
**Files Changed:**
- `autarch_public.spec` — Rewritten for dual-exe build with MERGE/COLLECT, existence-filtered data files
- `setup_msi.py` — Dual executables, LocalAppData install, model inclusion
**PyInstaller spec details:**
- Dual Analysis: `a_cli` (autarch.py, console=True) + `a_web` (autarch_web.py, console=False)
- `MERGE()` for shared library deduplication
- Single `COLLECT` combining both executables
- Existence filter: `added_files = [(str(src), dst) for src, dst in _candidate_files if src.exists()]`
**Inno Setup details:**
- GGUF model stored with `Flags: nocompression` to avoid OOM (3.9GB, barely compressible)
- `SolidCompression=no` prevents Inno from loading entire archive into memory
- Model excluded from main recursive glob with `Excludes: "_internal\models\Hal_v2.gguf"`
- GitHub release version excludes model (34 MB vs 3.9 GB)
### Phase 4.24 — WebUI FOUC Fix
**Files Changed:**
- `web/templates/base.html` — added inline critical CSS in `<head>`
**Fix:** Inlined dark theme colors, sidebar layout, and flex container styles directly in `<style>` tag before the external stylesheet `<link>`. Prevents flash of unstyled content (white background, unstyled sidebar) when the external CSS is delayed by self-signed cert negotiation or slow loading.
### v1.5 Release
**Release:** https://github.com/DigijEth/autarch/releases/tag/v1.5
**Assets:**
- `AUTARCH_Setup.exe` (34 MB) — Inno Setup installer, installs to `%LocalAppData%\AUTARCH`
- `AUTARCH_v1.5_Portable.zip` (39 MB) — Portable build with `autarch.exe` + `autarch_web.exe`
**Note:** Hal AI model (`Hal_v2.gguf`, 3.9 GB) excluded from both downloads due to GitHub's 2 GB per-asset limit.
**All 27+ pages tested** — inline CSS + external stylesheet present, layout/sidebar/content structure verified on every route.
---

BIN
autarch.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -29,6 +29,9 @@ _candidate_files = [
# LLM model # LLM model
(SRC / 'models' / 'Hal_v2.gguf', 'models'), (SRC / 'models' / 'Hal_v2.gguf', 'models'),
# Icon
(SRC / 'autarch.ico', '.'),
# Root-level config and docs # Root-level config and docs
(SRC / 'autarch_settings.conf', '.'), (SRC / 'autarch_settings.conf', '.'),
(SRC / 'user_manual.md', '.'), (SRC / 'user_manual.md', '.'),
@ -100,7 +103,7 @@ hidden_imports = [
excludes = [ excludes = [
# Exclude heavy optional deps not needed at runtime # Exclude heavy optional deps not needed at runtime
'torch', 'transformers', 'llama_cpp', 'llama_cpp_python', 'anthropic', 'torch', 'transformers',
'tkinter', 'matplotlib', 'numpy', 'tkinter', 'matplotlib', 'numpy',
# CUDA / quantization libraries # CUDA / quantization libraries
'bitsandbytes', 'bitsandbytes',
@ -171,7 +174,7 @@ exe_cli = EXE(
target_arch=None, target_arch=None,
codesign_identity=None, codesign_identity=None,
entitlements_file=None, entitlements_file=None,
icon=None, icon=str(SRC / 'autarch.ico'),
) )
# ── Web executable (NO console window — tray icon only) ───────────────────── # ── Web executable (NO console window — tray icon only) ─────────────────────
@ -192,7 +195,7 @@ exe_web = EXE(
target_arch=None, target_arch=None,
codesign_identity=None, codesign_identity=None,
entitlements_file=None, entitlements_file=None,
icon=None, icon=str(SRC / 'autarch.ico'),
) )
# ── Collect everything into one directory ──────────────────────────────────── # ── Collect everything into one directory ────────────────────────────────────

View File

@ -1,21 +1,21 @@
[llama] [llama]
model_path = C:\she\autarch\models\Hal_v2.gguf model_path = C:\she\autarch\models\darkHal.gguf
n_ctx = 2048 n_ctx = 2048
n_threads = 4 n_threads = 4
n_gpu_layers = 0 n_gpu_layers = -1
temperature = 0.7 temperature = 0.7
top_p = 0.9 top_p = 0.9
top_k = 40 top_k = 40
repeat_penalty = 1.1 repeat_penalty = 1.1
max_tokens = 1024 max_tokens = 1024
seed = -1 seed = -1
n_batch = 256 n_batch = 512
rope_scaling_type = 0 rope_scaling_type = 0
mirostat_mode = 0 mirostat_mode = 0
mirostat_tau = 5.0 mirostat_tau = 5.0
mirostat_eta = 0.1 mirostat_eta = 0.1
flash_attn = false flash_attn = false
gpu_backend = cpu gpu_backend = vulkan
[autarch] [autarch]
first_run = false first_run = false

View File

@ -249,6 +249,7 @@ PARAMS: {"question": "Your question"}
self.conversation.append({"role": "user", "content": f"Task: {task}"}) self.conversation.append({"role": "user", "content": f"Task: {task}"})
step_count = 0 step_count = 0
parse_failures = 0 # Track consecutive format failures
while step_count < self.max_steps: while step_count < self.max_steps:
step_count += 1 step_count += 1
@ -275,10 +276,34 @@ PARAMS: {"question": "Your question"}
# Parse response # Parse response
try: try:
thought, action, params = self._parse_response(response) thought, action, params = self._parse_response(response)
parse_failures = 0 # Reset on success
except ValueError as e: except ValueError as e:
parse_failures += 1
self._log(f"Failed to parse response: {e}", "error") self._log(f"Failed to parse response: {e}", "error")
self._log(f"Raw response: {response[:200]}...", "warning") self._log(f"Raw response: {response[:200]}...", "warning")
# Add error feedback and continue
# After 2 consecutive parse failures, the model can't follow
# the structured format — treat its response as a direct answer
if parse_failures >= 2:
# Clean up the raw response for display
answer = response.strip()
# Remove ChatML tokens if present
for tag in ['<|im_end|>', '<|im_start|>', '<|endoftext|>']:
answer = answer.split(tag)[0]
answer = answer.strip()
if not answer:
answer = "I could not process that request in agent mode. Try switching to Chat mode."
self._log("Model cannot follow structured format, returning direct answer", "warning")
step = AgentStep(thought="Direct response (model does not support agent format)", tool_name="task_complete", tool_args={"summary": answer})
step.tool_result = answer
self.steps.append(step)
if self.on_step:
self.on_step(step)
self._set_state(AgentState.COMPLETE)
return AgentResult(success=True, summary=answer, steps=self.steps)
# First failure — give one retry with format correction
self.conversation.append({ self.conversation.append({
"role": "assistant", "role": "assistant",
"content": response "content": response

View File

@ -9,6 +9,7 @@ Requires: pystray, Pillow
import sys import sys
import threading import threading
import webbrowser import webbrowser
from pathlib import Path
try: try:
import pystray import pystray
@ -18,27 +19,43 @@ except ImportError:
TRAY_AVAILABLE = False TRAY_AVAILABLE = False
def _get_icon_path():
"""Find the .ico file — works in both source and frozen (PyInstaller) builds."""
if getattr(sys, 'frozen', False):
base = Path(sys._MEIPASS)
else:
base = Path(__file__).parent.parent
ico = base / 'autarch.ico'
if ico.exists():
return ico
return None
def create_icon_image(size=64): def create_icon_image(size=64):
"""Create AUTARCH tray icon programmatically — dark circle with cyan 'A'.""" """Load tray icon from .ico file, falling back to programmatic generation."""
ico_path = _get_icon_path()
if ico_path:
try:
img = Image.open(str(ico_path))
img = img.resize((size, size), Image.LANCZOS)
return img.convert('RGBA')
except Exception:
pass
# Fallback: generate programmatically
img = Image.new('RGBA', (size, size), (0, 0, 0, 0)) img = Image.new('RGBA', (size, size), (0, 0, 0, 0))
draw = ImageDraw.Draw(img) draw = ImageDraw.Draw(img)
# Dark background circle with cyan border
draw.ellipse([1, 1, size - 2, size - 2], fill=(15, 15, 25, 255), draw.ellipse([1, 1, size - 2, size - 2], fill=(15, 15, 25, 255),
outline=(0, 180, 255, 255), width=2) outline=(0, 180, 255, 255), width=2)
# Letter "A" centered
try: try:
font = ImageFont.truetype("arial.ttf", int(size * 0.5)) font = ImageFont.truetype("arial.ttf", int(size * 0.5))
except OSError: except OSError:
font = ImageFont.load_default() font = ImageFont.load_default()
bbox = draw.textbbox((0, 0), "A", font=font) bbox = draw.textbbox((0, 0), "A", font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1] tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
x = (size - tw) // 2 x = (size - tw) // 2
y = (size - th) // 2 - bbox[1] y = (size - th) // 2 - bbox[1]
draw.text((x, y), "A", fill=(0, 200, 255, 255), font=font) draw.text((x, y), "A", fill=(0, 200, 255, 255), font=font)
return img return img

View File

@ -2262,3 +2262,65 @@ Full Hash Toolkit added as a sub-page under Analyze (sidebar sub-item like Legen
- **Android Protection Direct mode**`apDirect()` was passing `HWDirect.adbShell()` result objects (dicts) into `raw` instead of extracting `.stdout` strings; Python `/parse` route then crashed calling `.strip()` on dicts. Fixed by extracting stdout before sending to server - **Android Protection Direct mode**`apDirect()` was passing `HWDirect.adbShell()` result objects (dicts) into `raw` instead of extracting `.stdout` strings; Python `/parse` route then crashed calling `.strip()` on dicts. Fixed by extracting stdout before sending to server
- **`_serial()` hardened** — now checks `request.form` fallback and wraps in `str()` before `.strip()` - **`_serial()` hardened** — now checks `request.form` fallback and wraps in `str()` before `.strip()`
---
## Session 16 — 2026-03-01: Threat Monitor, Hal Agent, Windows Defense, LLM Trainer
### What got done this session:
- **7-tab Threat Monitor** — expanded from 4 tabs to 7 with Network Intel, Packet Capture, DDoS Mitigation
- **Drill-down popups** — click any stat in Live Monitor for detailed modal views
- **Hal Agent Mode** — Chat bubble now uses Agent system with `create_module` tool; can create modules on demand
- **System prompt**`data/hal_system_prompt.txt` teaches Hal the codebase
- **Windows Defense**`modules/defender_windows.py` + `defense_windows.html` (firewall, UAC, Defender AV, services, SSH, NTFS, event logs)
- **LLM Trainer**`modules/llm_trainer.py` + `web/routes/llm_trainer.py` + `llm_trainer.html` (dataset management, training, adapters)
- **Refresh Modules** — sidebar button for hot-reloading modules without server restart
### Todos from session 14 resolved:
- System Tray → deferred to session 17
- Beta Release → deferred to session 17
---
## Session 17 — 2026-03-02: System Tray, Packaging, v1.5 Release
### What got done this session:
- **System tray**`core/tray.py` with `TrayManager` (pystray + PIL): Start/Stop/Restart/Open Dashboard/Exit
- **Dual executables**`autarch.exe` (CLI, console) + `autarch_web.exe` (Web, no console, tray icon)
- **PyInstaller frozen build fixes** — dual-directory pattern in `core/paths.py` (_BUNDLE_DIR vs _APP_DIR), module loading scans both bundled and user dirs
- **Installer scripts**`installer.iss` (Inno Setup) + `installer.nsi` (NSIS)
- **Inno Setup OOM fix** — 3.9GB model stored uncompressed, `SolidCompression=no`
- **Inline critical CSS** — prevents white flash / FOUC on page load
- **All 27+ pages tested** — verified inline CSS, external stylesheet, layout structure
- **Version bumped to 1.5**
- **GitHub Release v1.5** — https://github.com/DigijEth/autarch/releases/tag/v1.5
- `AUTARCH_Setup.exe` (34 MB) — installer without model
- `AUTARCH_v1.5_Portable.zip` (39 MB) — portable without model
### SESSION SAVE — 2026-03-02 (end of session)
**Phase status:**
- Phases 04.24: DONE
- Phase 5 (Path portability): DONE (frozen build support complete)
- Phase 6 (Docker): NOT STARTED
**Key files created/modified this session:**
- `core/tray.py` (NEW) — TrayManager
- `autarch_web.py` (NEW) — Windowless web launcher
- `installer.iss` (NEW) — Inno Setup installer script
- `installer.nsi` (NEW) — NSIS installer script
- `core/paths.py` — Frozen build dual-directory pattern
- `core/menu.py` — Dual module directory scanning
- `web/app.py` — Frozen template/static path resolution
- `autarch.py` — --no-tray flag
- `autarch_public.spec` — Dual-exe MERGE/COLLECT
- `setup_msi.py` — Dual executables, v1.5
- `web/templates/base.html` — Inline critical CSS
**Todos from session 14 RESOLVED:**
- System Tray: DONE (core/tray.py)
- Beta Release: DONE (v1.5 on GitHub)
**Remaining work from master_plan.md:**
- Phase 6 (Docker): NOT STARTED
- Plan file (quizzical-toasting-mccarthy.md) — Threat Monitor + Hal Module Factory: DONE

87
icon.svg Normal file
View File

@ -0,0 +1,87 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<!-- Neon cyan glow -->
<filter id="glow">
<feGaussianBlur stdDeviation="6" result="blur"/>
<feComposite in="SourceGraphic" in2="blur" operator="over"/>
</filter>
<!-- Heavy outer glow -->
<filter id="glowHeavy">
<feGaussianBlur stdDeviation="12" result="blur1"/>
<feGaussianBlur stdDeviation="24" result="blur2"/>
<feMerge>
<feMergeNode in="blur2"/>
<feMergeNode in="blur1"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
<!-- Scanlines pattern -->
<pattern id="scanlines" width="4" height="4" patternUnits="userSpaceOnUse">
<rect width="4" height="2" fill="rgba(0,255,255,0.03)"/>
<rect y="2" width="4" height="2" fill="rgba(0,0,0,0.15)"/>
</pattern>
<!-- Grid pattern -->
<pattern id="grid" width="32" height="32" patternUnits="userSpaceOnUse">
<path d="M 32 0 L 0 0 0 32" fill="none" stroke="rgba(0,255,255,0.06)" stroke-width="0.5"/>
</pattern>
</defs>
<!-- Dark background -->
<rect width="512" height="512" fill="#05080f"/>
<!-- Grid overlay -->
<rect width="512" height="512" fill="url(#grid)"/>
<!-- Faint radial ambiance -->
<circle cx="256" cy="256" r="240" fill="none" stroke="rgba(255,0,80,0.04)" stroke-width="180"/>
<circle cx="256" cy="256" r="180" fill="none" stroke="rgba(0,255,255,0.03)" stroke-width="120"/>
<!-- Glitch offset layers (red/blue chromatic aberration) -->
<g opacity="0.35">
<!-- Red offset -->
<circle cx="259" cy="254" r="200" fill="none" stroke="#ff0040" stroke-width="20"/>
<line x1="259" y1="54" x2="123" y2="428" stroke="#ff0040" stroke-width="20" stroke-linecap="round"/>
<line x1="259" y1="54" x2="395" y2="428" stroke="#ff0040" stroke-width="20" stroke-linecap="round"/>
<line x1="103" y1="318" x2="415" y2="318" stroke="#ff0040" stroke-width="20" stroke-linecap="round"/>
</g>
<g opacity="0.35">
<!-- Blue offset -->
<circle cx="253" cy="258" r="200" fill="none" stroke="#00d4ff" stroke-width="20"/>
<line x1="253" y1="58" x2="117" y2="432" stroke="#00d4ff" stroke-width="20" stroke-linecap="round"/>
<line x1="253" y1="58" x2="389" y2="432" stroke="#00d4ff" stroke-width="20" stroke-linecap="round"/>
<line x1="97" y1="322" x2="409" y2="322" stroke="#00d4ff" stroke-width="20" stroke-linecap="round"/>
</g>
<!-- Main symbol with neon glow -->
<g filter="url(#glowHeavy)">
<circle cx="256" cy="256" r="200" fill="none" stroke="#00ffcc" stroke-width="18"/>
<line x1="256" y1="56" x2="120" y2="430" stroke="#00ffcc" stroke-width="18" stroke-linecap="round"/>
<line x1="256" y1="56" x2="392" y2="430" stroke="#00ffcc" stroke-width="18" stroke-linecap="round"/>
<line x1="100" y1="320" x2="412" y2="320" stroke="#00ffcc" stroke-width="18" stroke-linecap="round"/>
</g>
<!-- Bright core layer -->
<g filter="url(#glow)">
<circle cx="256" cy="256" r="200" fill="none" stroke="#ffffff" stroke-width="4" opacity="0.6"/>
<line x1="256" y1="56" x2="120" y2="430" stroke="#ffffff" stroke-width="4" opacity="0.6" stroke-linecap="round"/>
<line x1="256" y1="56" x2="392" y2="430" stroke="#ffffff" stroke-width="4" opacity="0.6" stroke-linecap="round"/>
<line x1="100" y1="320" x2="412" y2="320" stroke="#ffffff" stroke-width="4" opacity="0.6" stroke-linecap="round"/>
</g>
<!-- Glitch bars -->
<rect x="0" y="170" width="512" height="3" fill="#00ffcc" opacity="0.15"/>
<rect x="0" y="340" width="512" height="2" fill="#ff0040" opacity="0.12"/>
<rect x="0" y="405" width="512" height="1.5" fill="#00ffcc" opacity="0.1"/>
<rect x="180" y="168" width="90" height="6" fill="#05080f" opacity="0.8"/>
<rect x="300" y="338" width="60" height="5" fill="#05080f" opacity="0.8"/>
<!-- Scanlines overlay -->
<rect width="512" height="512" fill="url(#scanlines)"/>
<!-- Corner accents -->
<g stroke="#00ffcc" stroke-width="2" opacity="0.4">
<polyline points="10,40 10,10 40,10" fill="none"/>
<polyline points="472,10 502,10 502,40" fill="none"/>
<polyline points="10,472 10,502 40,502" fill="none"/>
<polyline points="472,502 502,502 502,472" fill="none"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -13,8 +13,8 @@
[Setup] [Setup]
AppName=AUTARCH AppName=AUTARCH
AppVersion=1.5 AppVersion=1.5.1
AppVerName=AUTARCH 1.5 AppVerName=AUTARCH 1.5.1
AppPublisher=darkHal Security Group AppPublisher=darkHal Security Group
AppPublisherURL=https://github.com/darkhal AppPublisherURL=https://github.com/darkhal
AppSupportURL=https://github.com/darkhal AppSupportURL=https://github.com/darkhal
@ -32,8 +32,8 @@ DisableProgramGroupPage=yes
WizardStyle=modern WizardStyle=modern
SetupLogging=yes SetupLogging=yes
; Uncomment and set path if you have a custom icon: SetupIconFile=autarch.ico
; SetupIconFile=assets\autarch.ico UninstallDisplayIcon={app}\autarch_web.exe
[Languages] [Languages]
Name: "english"; MessagesFile: "compiler:Default.isl" Name: "english"; MessagesFile: "compiler:Default.isl"
@ -49,12 +49,12 @@ Source: "dist\autarch\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Icons] [Icons]
; Start Menu ; Start Menu
Name: "{group}\AUTARCH Web Dashboard"; Filename: "{app}\autarch_web.exe"; Comment: "Launch AUTARCH Web Dashboard with system tray" Name: "{group}\AUTARCH Web Dashboard"; Filename: "{app}\autarch_web.exe"; IconFilename: "{app}\autarch.ico"; Comment: "Launch AUTARCH Web Dashboard with system tray"
Name: "{group}\AUTARCH CLI"; Filename: "{app}\autarch.exe"; Comment: "AUTARCH command-line interface" Name: "{group}\AUTARCH CLI"; Filename: "{app}\autarch.exe"; IconFilename: "{app}\autarch.ico"; Comment: "AUTARCH command-line interface"
Name: "{group}\Uninstall AUTARCH"; Filename: "{uninstallexe}" Name: "{group}\Uninstall AUTARCH"; Filename: "{uninstallexe}"
; Desktop (optional) ; Desktop (optional)
Name: "{commondesktop}\AUTARCH Web"; Filename: "{app}\autarch_web.exe"; Tasks: desktopicon; Comment: "Launch AUTARCH Web Dashboard" Name: "{commondesktop}\AUTARCH Web"; Filename: "{app}\autarch_web.exe"; IconFilename: "{app}\autarch.ico"; Tasks: desktopicon; Comment: "Launch AUTARCH Web Dashboard"
; Windows Startup (optional) ; Windows Startup (optional)
Name: "{userstartup}\AUTARCH Web"; Filename: "{app}\autarch_web.exe"; Tasks: startupicon Name: "{userstartup}\AUTARCH Web"; Filename: "{app}\autarch_web.exe"; Tasks: startupicon

View File

@ -17,7 +17,7 @@
; App metadata ; App metadata
!define APPNAME "AUTARCH" !define APPNAME "AUTARCH"
!define APPVERSION "1.5" !define APPVERSION "1.5.1"
!define PUBLISHER "darkHal Security Group" !define PUBLISHER "darkHal Security Group"
!define DESCRIPTION "Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking" !define DESCRIPTION "Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking"

View File

@ -53,7 +53,7 @@ build_exe_options = {
'web.routes.targets', 'web.routes.encmodules', 'web.routes.targets', 'web.routes.encmodules',
'web.routes.llm_trainer', 'web.routes.llm_trainer',
], ],
'excludes': ['torch', 'transformers', 'llama_cpp', 'llama_cpp_python', 'anthropic', 'excludes': ['torch', 'transformers',
'tkinter', 'matplotlib', 'numpy', 'tkinter', 'matplotlib', 'numpy',
'bitsandbytes', 'bitsandbytes',
'huggingface_hub', 'safetensors', 'tokenizers', 'huggingface_hub', 'safetensors', 'tokenizers',
@ -74,7 +74,7 @@ bdist_msi_options = {
setup( setup(
name='AUTARCH', name='AUTARCH',
version='1.5.0', version='1.5.1',
description='AUTARCH — Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking', description='AUTARCH — Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking',
author='darkHal Security Group & Setec Security Labs', author='darkHal Security Group & Setec Security Labs',
options={ options={

View File

@ -45,14 +45,51 @@ def _ensure_model_loaded():
@chat_bp.route('/chat', methods=['POST']) @chat_bp.route('/chat', methods=['POST'])
@login_required @login_required
def chat(): def chat():
"""Handle chat messages — uses Agent system for tool-using tasks, """Handle chat messages — direct chat or agent mode based on user toggle.
direct chat for simple questions. Streams response via SSE.""" Streams response via SSE."""
data = request.get_json(silent=True) or {} data = request.get_json(silent=True) or {}
message = data.get('message', '').strip() message = data.get('message', '').strip()
mode = data.get('mode', 'chat') # 'chat' (default) or 'agent'
if not message: if not message:
return jsonify({'error': 'No message provided'}) return jsonify({'error': 'No message provided'})
# Always use agent mode so Hal can use tools including create_module if mode == 'agent':
return _handle_agent_chat(message)
else:
return _handle_direct_chat(message)
def _handle_direct_chat(message):
"""Direct chat mode — streams tokens from the LLM without the Agent system."""
def generate():
from core.llm import get_llm, LLMError
llm = get_llm()
if not llm.is_loaded:
yield f"data: {json.dumps({'type': 'status', 'content': 'Loading model...'})}\n\n"
try:
llm.load_model(verbose=False)
except LLMError as e:
yield f"data: {json.dumps({'type': 'error', 'content': f'Failed to load model: {e}'})}\n\n"
yield f"data: {json.dumps({'done': True})}\n\n"
return
system_prompt = _get_system_prompt()
try:
token_gen = llm.chat(message, system_prompt=system_prompt, stream=True)
for token in token_gen:
yield f"data: {json.dumps({'token': token})}\n\n"
except LLMError as e:
yield f"data: {json.dumps({'type': 'error', 'content': str(e)})}\n\n"
yield f"data: {json.dumps({'done': True})}\n\n"
return Response(generate(), mimetype='text/event-stream',
headers={'Cache-Control': 'no-cache', 'X-Accel-Buffering': 'no'})
def _handle_agent_chat(message):
"""Agent mode — uses the Agent system with tools for complex tasks."""
run_id = str(uuid.uuid4()) run_id = str(uuid.uuid4())
stop_event = threading.Event() stop_event = threading.Event()
steps = [] steps = []
@ -86,7 +123,6 @@ def chat():
if step.tool_name and step.tool_name not in ('task_complete', 'ask_user'): if step.tool_name and step.tool_name not in ('task_complete', 'ask_user'):
steps.append({'type': 'action', 'content': f"{step.tool_name}({json.dumps(step.tool_args or {})})"}) steps.append({'type': 'action', 'content': f"{step.tool_name}({json.dumps(step.tool_args or {})})"})
if step.tool_result: if step.tool_result:
# Truncate long results for display
result = step.tool_result result = step.tool_result
if len(result) > 800: if len(result) > 800:
result = result[:800] + '...' result = result[:800] + '...'

View File

@ -668,6 +668,50 @@ pre { background: var(--bg-primary); border: 1px solid var(--border); border-rad
} }
.hal-close:hover { color: var(--text, #e0e0e0); background: var(--bg-hover, #2a2a3e); } .hal-close:hover { color: var(--text, #e0e0e0); background: var(--bg-hover, #2a2a3e); }
/* Hal mode toggle switch */
.hal-mode-switch {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
user-select: none;
}
.hal-mode-switch input { display: none; }
.hal-mode-slider {
width: 28px;
height: 14px;
background: var(--bg-input, #2a2d3e);
border-radius: 7px;
position: relative;
transition: background 0.2s;
border: 1px solid var(--border, #333650);
}
.hal-mode-slider::after {
content: '';
position: absolute;
width: 10px;
height: 10px;
background: var(--text-secondary, #888);
border-radius: 50%;
top: 1px;
left: 1px;
transition: transform 0.2s, background 0.2s;
}
.hal-mode-switch input:checked + .hal-mode-slider {
background: var(--accent, #6366f1);
border-color: var(--accent, #6366f1);
}
.hal-mode-switch input:checked + .hal-mode-slider::after {
transform: translateX(14px);
background: #fff;
}
.hal-mode-label {
font-size: 0.7rem;
font-weight: 500;
color: var(--text-secondary, #888);
min-width: 36px;
}
.hal-messages { .hal-messages {
flex: 1; flex: 1;
overflow-y: auto; overflow-y: auto;

View File

@ -2109,6 +2109,14 @@ async function hwFactoryFlash() {
// ── Agent Hal Global Chat Panel ────────────────────────────────────────────── // ── Agent Hal Global Chat Panel ──────────────────────────────────────────────
var halAgentMode = false; // false = direct chat, true = agent mode
function halModeChanged(checkbox) {
halAgentMode = checkbox.checked;
var label = document.getElementById('hal-mode-label');
if (label) label.textContent = halAgentMode ? 'Agent' : 'Chat';
}
function halToggle() { function halToggle() {
var p = document.getElementById('hal-panel'); var p = document.getElementById('hal-panel');
if (!p) return; if (!p) return;
@ -2133,7 +2141,7 @@ function halSend() {
fetch('/api/chat', { fetch('/api/chat', {
method: 'POST', method: 'POST',
headers: {'Content-Type': 'application/json'}, headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg}) body: JSON.stringify({message: msg, mode: halAgentMode ? 'agent' : 'chat'})
}).then(function(res) { }).then(function(res) {
var reader = res.body.getReader(); var reader = res.body.getReader();
var dec = new TextDecoder(); var dec = new TextDecoder();

View File

@ -130,7 +130,12 @@
{% if session.get('user') %} {% if session.get('user') %}
<div id="hal-panel" class="hal-panel" style="display:none"> <div id="hal-panel" class="hal-panel" style="display:none">
<div class="hal-header"> <div class="hal-header">
<span>&#x25cf; Agent Hal</span> <span>&#x25cf; Hal</span>
<label class="hal-mode-switch" title="Toggle Agent mode (tools) vs Direct chat">
<input type="checkbox" id="hal-mode-toggle" onchange="halModeChanged(this)">
<span class="hal-mode-slider"></span>
<span class="hal-mode-label" id="hal-mode-label">Chat</span>
</label>
<button onclick="halToggle()" class="hal-close" title="Close">&#x2715;</button> <button onclick="halToggle()" class="hal-close" title="Close">&#x2715;</button>
</div> </div>
<div id="hal-messages" class="hal-messages"></div> <div id="hal-messages" class="hal-messages"></div>