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

View File

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

View File

@ -249,6 +249,7 @@ PARAMS: {"question": "Your question"}
self.conversation.append({"role": "user", "content": f"Task: {task}"})
step_count = 0
parse_failures = 0 # Track consecutive format failures
while step_count < self.max_steps:
step_count += 1
@ -275,10 +276,34 @@ PARAMS: {"question": "Your question"}
# Parse response
try:
thought, action, params = self._parse_response(response)
parse_failures = 0 # Reset on success
except ValueError as e:
parse_failures += 1
self._log(f"Failed to parse response: {e}", "error")
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({
"role": "assistant",
"content": response

View File

@ -9,6 +9,7 @@ Requires: pystray, Pillow
import sys
import threading
import webbrowser
from pathlib import Path
try:
import pystray
@ -18,27 +19,43 @@ except ImportError:
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):
"""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))
draw = ImageDraw.Draw(img)
# Dark background circle with cyan border
draw.ellipse([1, 1, size - 2, size - 2], fill=(15, 15, 25, 255),
outline=(0, 180, 255, 255), width=2)
# Letter "A" centered
try:
font = ImageFont.truetype("arial.ttf", int(size * 0.5))
except OSError:
font = ImageFont.load_default()
bbox = draw.textbbox((0, 0), "A", font=font)
tw, th = bbox[2] - bbox[0], bbox[3] - bbox[1]
x = (size - tw) // 2
y = (size - th) // 2 - bbox[1]
draw.text((x, y), "A", fill=(0, 200, 255, 255), font=font)
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
- **`_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]
AppName=AUTARCH
AppVersion=1.5
AppVerName=AUTARCH 1.5
AppVersion=1.5.1
AppVerName=AUTARCH 1.5.1
AppPublisher=darkHal Security Group
AppPublisherURL=https://github.com/darkhal
AppSupportURL=https://github.com/darkhal
@ -32,8 +32,8 @@ DisableProgramGroupPage=yes
WizardStyle=modern
SetupLogging=yes
; Uncomment and set path if you have a custom icon:
; SetupIconFile=assets\autarch.ico
SetupIconFile=autarch.ico
UninstallDisplayIcon={app}\autarch_web.exe
[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"
@ -49,12 +49,12 @@ Source: "dist\autarch\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Icons]
; Start Menu
Name: "{group}\AUTARCH Web Dashboard"; Filename: "{app}\autarch_web.exe"; Comment: "Launch AUTARCH Web Dashboard with system tray"
Name: "{group}\AUTARCH CLI"; Filename: "{app}\autarch.exe"; Comment: "AUTARCH command-line interface"
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"; IconFilename: "{app}\autarch.ico"; Comment: "AUTARCH command-line interface"
Name: "{group}\Uninstall AUTARCH"; Filename: "{uninstallexe}"
; 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)
Name: "{userstartup}\AUTARCH Web"; Filename: "{app}\autarch_web.exe"; Tasks: startupicon

View File

@ -17,7 +17,7 @@
; App metadata
!define APPNAME "AUTARCH"
!define APPVERSION "1.5"
!define APPVERSION "1.5.1"
!define PUBLISHER "darkHal Security Group"
!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.llm_trainer',
],
'excludes': ['torch', 'transformers', 'llama_cpp', 'llama_cpp_python', 'anthropic',
'excludes': ['torch', 'transformers',
'tkinter', 'matplotlib', 'numpy',
'bitsandbytes',
'huggingface_hub', 'safetensors', 'tokenizers',
@ -74,7 +74,7 @@ bdist_msi_options = {
setup(
name='AUTARCH',
version='1.5.0',
version='1.5.1',
description='AUTARCH — Autonomous Tactical Agent for Reconnaissance, Counterintelligence, and Hacking',
author='darkHal Security Group & Setec Security Labs',
options={

View File

@ -45,14 +45,51 @@ def _ensure_model_loaded():
@chat_bp.route('/chat', methods=['POST'])
@login_required
def chat():
"""Handle chat messages — uses Agent system for tool-using tasks,
direct chat for simple questions. Streams response via SSE."""
"""Handle chat messages — direct chat or agent mode based on user toggle.
Streams response via SSE."""
data = request.get_json(silent=True) or {}
message = data.get('message', '').strip()
mode = data.get('mode', 'chat') # 'chat' (default) or 'agent'
if not message:
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())
stop_event = threading.Event()
steps = []
@ -86,7 +123,6 @@ def chat():
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 {})})"})
if step.tool_result:
# Truncate long results for display
result = step.tool_result
if len(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 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 {
flex: 1;
overflow-y: auto;

View File

@ -2109,6 +2109,14 @@ async function hwFactoryFlash() {
// ── 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() {
var p = document.getElementById('hal-panel');
if (!p) return;
@ -2133,7 +2141,7 @@ function halSend() {
fetch('/api/chat', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({message: msg})
body: JSON.stringify({message: msg, mode: halAgentMode ? 'agent' : 'chat'})
}).then(function(res) {
var reader = res.body.getReader();
var dec = new TextDecoder();

View File

@ -130,7 +130,12 @@
{% if session.get('user') %}
<div id="hal-panel" class="hal-panel" style="display:none">
<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>
</div>
<div id="hal-messages" class="hal-messages"></div>