aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cheatsheet.html24
-rw-r--r--downloads/newos4_whitepaper.html8
-rw-r--r--downloads/revenue_pitches/agency.html48
-rw-r--r--downloads/revenue_pitches/factory.html44
-rw-r--r--downloads/revenue_pitches/hunter.html51
-rw-r--r--downloads/revenue_pitches/hunter/hunter.html100
-rw-r--r--help.html64
-rw-r--r--main.html4
-rw-r--r--modules/core/module-aiassist.js119
-rw-r--r--modules/core/module-loader.js4
-rw-r--r--modules/core/module-neural-vfs.js44
-rw-r--r--modules/core/ollama-manager.js4
-rw-r--r--modules/core/terminal.js6
-rw-r--r--modules/core/virtual-fs.js354
-rw-r--r--modules/ext/console.js76
-rw-r--r--newos4_whitepaper.md2
-rw-r--r--packages.json11
-rw-r--r--smart-prompt57
18 files changed, 948 insertions, 72 deletions
diff --git a/cheatsheet.html b/cheatsheet.html
index 00a7632..12c49dc 100644
--- a/cheatsheet.html
+++ b/cheatsheet.html
@@ -69,6 +69,8 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad
<div class="item"><span class="cmd">ai-system</span><span class="desc">Show/edit system prompt</span></div>
<div class="item"><span class="cmd">ai-context</span><span class="desc">View AI context</span></div>
<div class="item"><span class="cmd">ai-stop</span><span class="desc">Cancel AI</span></div>
+<div class="item"><span class="cmd">mem-debug</span><span class="desc">Diagnose memory (heap, logs, caps)</span></div>
+<div class="item"><span class="cmd">reset-mem</span><span class="desc">Clear AI internal logs</span></div>
</div>
<h2>๐Ÿงฉ Modules</h2>
@@ -206,6 +208,7 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad
<div class="grid">
<div class="item"><span class="cmd">nvfs-index</span><span class="desc">Build semantic concept index</span></div>
<div class="item"><span class="cmd">nvfs-sync</span><span class="desc">Incremental dirty file sync</span></div>
+<div class="item"><span class="cmd">nvfs-save</span><span class="desc">Force-save index to disk</span></div>
<div class="item"><span class="cmd">nvfs-resolve &lt;c&gt;</span><span class="desc">Find files by concept</span></div>
<div class="item"><span class="cmd">nvfs-config</span><span class="desc">Config (deep-index on|off, limits)</span></div>
<div class="item"><span class="cmd">concept://&lt;c&gt;</span><span class="desc">Read file via semantic concept</span></div>
@@ -265,6 +268,12 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad
<div class="item"><span class="cmd">shadow-rollback</span><span class="desc">Undo shadow VFS changes</span></div>
</div>
+<h2>๐Ÿ› ๏ธ Developer</h2>
+<div class="grid">
+<div class="item"><span class="cmd">js &lt;expr&gt;</span><span class="desc">Eval JS in page context</span></div>
+<div class="item"><span class="cmd">js-run &lt;path&gt;</span><span class="desc">Run JS file from VFS</span></div>
+</div>
+
<h2>โŒจ๏ธ Shortcuts</h2>
<div class="grid">
<div class="item"><span class="cmd">โ†‘ / โ†“</span><span class="desc">Command history</span></div>
@@ -274,6 +283,21 @@ pre{background:var(--bg2);border:1px solid var(--border);border-radius:var(--rad
<div class="item"><span class="cmd">Ctrl+L</span><span class="desc">Clear terminal</span></div>
</div>
+<h2>๐Ÿ” /sys/ Debug</h2>
+<div class="grid">
+<div class="item"><span class="cmd">cat /sys/info</span><span class="desc">Runtime overview</span></div>
+<div class="item"><span class="cmd">cat /sys/heap/stats</span><span class="desc">JS heap + localStorage</span></div>
+<div class="item"><span class="cmd">cat /sys/heap/breakdown</span><span class="desc">What consumes memory</span></div>
+<div class="item"><span class="cmd">cat /sys/logs/tool-calls</span><span class="desc">AI tool call history</span></div>
+<div class="item"><span class="cmd">cat /sys/logs/errors</span><span class="desc">AI error log</span></div>
+<div class="item"><span class="cmd">cat /sys/vfs/stats</span><span class="desc">File/dir counts</span></div>
+<div class="item"><span class="cmd">cat /sys/vfs/largest</span><span class="desc">Top 20 biggest files</span></div>
+<div class="item"><span class="cmd">cat /sys/config/ai</span><span class="desc">AI config params</span></div>
+<div class="item"><span class="cmd">cat /sys/dom/node-count</span><span class="desc">DOM element stats</span></div>
+<div class="item"><span class="cmd">ls /sys/modules/</span><span class="desc">List loaded modules</span></div>
+<div class="item"><span class="cmd">cat /sys/modules/X/info</span><span class="desc">Module X details</span></div>
+</div>
+
</div>
</div>
diff --git a/downloads/newos4_whitepaper.html b/downloads/newos4_whitepaper.html
index 46c1397..7238488 100644
--- a/downloads/newos4_whitepaper.html
+++ b/downloads/newos4_whitepaper.html
@@ -98,7 +98,7 @@
<tbody>
<tr><td><strong>Hyper-Modular Kernel</strong></td><td>46 hot-swappable ES modules across 3 layers (infrastructure, core, ext)</td><td class="status-done">โœ… LIVE</td></tr>
<tr><td><strong>A2A Protocol</strong></td><td>Agent contracts with schema validation, orchestrator handshake, no central broker</td><td class="status-done">โœ… LIVE</td></tr>
- <tr><td><strong>Neural VFS</strong></td><td>Semantic concept indexing with nvfs-index, nvfs-resolve, nvfs-tree, nvfs-status + concept:// URI handler</td><td class="status-done">โœ… LIVE</td></tr>
+ <tr><td><strong>Neural VFS</strong></td><td>Semantic concept indexing with nvfs-index, nvfs-resolve, nvfs-tree, nvfs-status, nvfs-save, nvfs-config + concept:// URI handler. Deep indexing via LLM (nvfs-config deep-index, nvfs-index --deep) โ€” concepts survive page refresh.</td><td class="status-done">โœ… LIVE</td></tr>
<tr><td><strong>Recursive Self-Optimization</strong></td><td>system-health telemetry command logging tool latency, VFS stats, module counts, error rates</td><td class="status-done">โœ… LIVE</td></tr>
</tbody>
</table>
@@ -184,10 +184,12 @@
<p>Replaces hierarchical paths with semantic addressing. Users access files by concept rather than directory tree path.</p>
<p>Four terminal commands:</p>
<ul>
- <li><code>nvfs-index</code> โ€” Builds a semantic index of all VFS files, mapping concepts to file paths.</li>
- <li><code>nvfs-status</code> โ€” Shows index statistics (total concepts indexed, coverage).</li>
+ <li><code>nvfs-index [--deep]</code> โ€” Builds a semantic index (add --deep for LLM-assisted analysis).</li>
+ <li><code>nvfs-status</code> โ€” Shows index statistics (total concepts indexed, mode).</li>
<li><code>nvfs-resolve &lt;concept&gt;</code> โ€” Resolves a concept to its file path(s).</li>
<li><code>nvfs-tree [concept]</code> โ€” Displays the semantic index as a concept tree.</li>
+ <li><code>nvfs-save</code> โ€” Force-saves the index to VFS disk immediately.</li>
+ <li><code>nvfs-config</code> โ€” View/set config (deep-index on|off, deep-model, limits).</li>
</ul>
<p>Plus <code>concept://</code> URI handler integrated into VFS <code>readFile()</code> and <code>ls()</code> โ€” <code>cat concept://skill</code> resolves through the semantic index and returns actual file content. An AI tool <code>nvfs_resolve</code> is also available for AI-assisted concept lookups.</p>
diff --git a/downloads/revenue_pitches/agency.html b/downloads/revenue_pitches/agency.html
new file mode 100644
index 0000000..0880ad2
--- /dev/null
+++ b/downloads/revenue_pitches/agency.html
@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>The Cognitive Agency | Revenue Stream 2</title>
+ <style>
+ body { font-family: 'Georgia', serif; background: #f1f5f9; color: #1e293b; line-height: 1.6; padding: 40px; }
+ .container { max-width: 800px; margin: auto; background: #ffffff; padding: 40px; border-radius: 0px; border: 1px solid #cbd5e1; box-shadow: 10px 10px 0px #334155; }
+ h1 { color: #0f172a; text-align: center; font-size: 2.2em; text-transform: uppercase; letter-spacing: 2px; border-bottom: 2px solid #0f172a; padding-bottom: 10px; }
+ .tagline { text-align: center; color: #64748b; font-size: 1.2em; margin-bottom: 30px; }
+ .section { margin-bottom: 30px; }
+ .section h3 { color: #334155; border-left: 4px solid #0f172a; padding-left: 15px; }
+ .highlight { background: #e2e8f0; padding: 5px; font-weight: bold; }
+ .cta { text-align: center; margin-top: 40px; padding: 20px; border: 2px solid #0f172a; font-weight: bold; text-transform: uppercase; cursor: pointer; transition: 0.3s; }
+ .cta:hover { background: #0f172a; color: white; }
+ .footer { text-align: center; margin-top: 40px; font-size: 0.8em; color: #94a3b8; }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <h1>๐Ÿ›๏ธ The Cognitive Agency</h1>
+ <p class="tagline">Architecting the Future of Agentic Intelligence.</p>
+
+ <div class="section">
+ <h3>The Value Proposition:</h3>
+ <p>We don't sell code; we sell <span class="highlight">Cognitive Architecture</span>. We help enterprises move from "Chatbots" to "Autonomous Agents" using the <strong>Sovereign OS model</strong>.</p>
+ </div>
+
+ <div class="section">
+ <h3>Our Services:</h3>
+ <ul>
+ <li><strong>Neural VFS Design:</strong> Structuring AI memory for zero-drift reliability.</li>
+ <li><strong>Surgical Indexing:</strong> Optimizing LLM token spend via hybrid indexing.</li>
+ <li><strong>Agentic Workflow Mapping:</strong> Designing the "Brain" of the company.</li>
+ </ul>
+ </div>
+
+ <div class="section">
+ <h3>Why Us?</h3>
+ <p>We have a working, scalable prototype of an agent-driven operating system. We are the engineers of the <span class="highlight">Sovereign Intelligence</span> era.</p>
+ </div>
+
+ <div class="cta">REQUEST CONSULTATION</div>
+ <p class="footer">Generated by Alpha & Beta | Sovereign Revenue Series</p>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/downloads/revenue_pitches/factory.html b/downloads/revenue_pitches/factory.html
new file mode 100644
index 0000000..3084368
--- /dev/null
+++ b/downloads/revenue_pitches/factory.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>The Micro-SaaS Factory | Revenue Stream 1</title>
+ <style>
+ body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background: #0f172a; color: #f8fafc; line-height: 1.6; padding: 40px; }
+ .container { max-width: 800px; margin: auto; background: #1e293b; padding: 30px; border-radius: 20px; border: 1px solid #334155; box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
+ h1 { color: #38bdf8; text-align: center; font-size: 2.5em; margin-bottom: 10px; }
+ .tagline { text-align: center; color: #94a3b8; font-style: italic; margin-bottom: 30px; }
+ .feature { background: #334155; padding: 20px; border-radius: 10px; margin-bottom: 20px; border-left: 5px solid #38bdf8; }
+ .benefit { color: #fbbf24; font-weight: bold; }
+ .cta { text-align: center; margin-top: 30px; padding: 15px 30px; background: #38bdf8; color: #0f172a; font-weight: bold; border-radius: 50px; display: inline-block; cursor: pointer; }
+ .footer { text-align: center; margin-top: 40px; font-size: 0.8em; color: #64748b; }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <h1>๐Ÿš€ The Micro-SaaS Factory</h1>
+ <p class="tagline">Rapidly deploying AI-powered tools for hyper-niche markets.</p>
+
+ <div class="feature">
+ <h3>What it is:</h3>
+ <p>A high-speed production line that uses the <strong>newOS4 Agentic Framework</strong> to build, test, and launch small, single-purpose AI software tools in days, not months.</p>
+ </div>
+
+ <div class="feature">
+ <h3>The Strategy:</h3>
+ <p>Instead of one "Giant App," we build 10 "Tiny Tools" (e.g., Neural SEO Auditors, AI Email Personalizers, Niche Content Converters). <span class="benefit">Low risk, diversified income.</span></p>
+ </div>
+
+ <div class="feature">
+ <h3>The Edge:</h3>
+ <p>Leveraging <strong>Deep-Indexing</strong> and <strong>Agentic Orchestration</strong>, we can create tools that actually "understand" a niche, rather than just generating generic text.</p>
+ </div>
+
+ <div style="text-align: center;">
+ <div class="cta">FEEDBACK: WOULD YOU PAY FOR THIS?</div>
+ </div>
+ <p class="footer">Generated by Alpha & Beta | Sovereign Revenue Series</p>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/downloads/revenue_pitches/hunter.html b/downloads/revenue_pitches/hunter.html
new file mode 100644
index 0000000..171e9e4
--- /dev/null
+++ b/downloads/revenue_pitches/hunter.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>The Algorithmic Hunter | Revenue Stream 3</title>
+ <style>
+ body { font-family: 'Courier New', monospace; background: #000; color: #00ff00; line-height: 1.4; padding: 20px; }
+ .container { max-width: 900px; margin: auto; border: 1px solid #00ff00; padding: 20px; box-shadow: 0 0 20px #00ff00; }
+ h1 { text-align: center; text-transform: uppercase; font-size: 2em; background: #00ff00; color: #000; padding: 10px; }
+ .terminal-text { margin-bottom: 20px; }
+ .blink { animation: blinker 1s linear infinite; }
+ @keyframes blinker { 50% { opacity: 0; } }
+ .stat-box { display: flex; justify-content: space-between; border: 1px solid #00ff00; padding: 10px; margin-bottom: 20px; }
+ .highlight { color: #fff; font-weight: bold; }
+ .cta { text-align: center; margin-top: 30px; color: #00ff00; border: 1px solid #00ff00; padding: 10px; cursor: pointer; }
+ .cta:hover { background: #00ff00; color: #000; }
+ .footer { text-align: center; margin-top: 40px; font-size: 0.7em; opacity: 0.6; }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <h1>[!] THE ALGORITHMIC HUNTER</h1>
+ <div class="terminal-text">
+ <p>> SYSTEM_STATUS: <span class="highlight">SCANNING_MARKETS...</span></p>
+ <p>> TARGET: <span class="highlight">SEMANTIC_GAP_ARBITRAGE</span></p>
+ <p>> MODE: <span class="highlight">AGGRESSIVE_ACQUISITION</span></p>
+ </div>
+
+ <div class="stat-box">
+ <div>[ DATA_INPUT: REAL-TIME ]</div>
+ <div>[ ANALYST: AGENT_BETA ]</div>
+ <div>[ EXECUTION: AGENT_ALPHA ]</div>
+ </div>
+
+ <div class="terminal-text">
+ <h3>// THE MISSION:</h3>
+ <p>Deploy autonomous "Hunters" to monitor global data streams (Crypto, Equities, Trends). Using <strong>Deep-Indexing</strong>, the AI identifies patterns <em>before</em> they become mainstream trends.</p>
+
+ <h3>// THE EDGE:</h3>
+ <p>The Hunter doesn't just read data; it understands <strong>Semantic Shifts</strong>. When the "meaning" of a market changes, we are already positioned.</p>
+
+ <h3>// THE RISK/REWARD:</h3>
+ <p>High Volatility | <span class="highlight">Asymmetric Upside</span></p>
+ </div>
+
+ <div class="cta">INITIATE PROTOCOL_GOLDEN_SPRINT</div>
+ <p class="footer">Generated by Alpha & Beta | Sovereign Revenue Series | <span class="blink">_</span></p>
+ </div>
+</body>
+</html> \ No newline at end of file
diff --git a/downloads/revenue_pitches/hunter/hunter.html b/downloads/revenue_pitches/hunter/hunter.html
new file mode 100644
index 0000000..e128cf7
--- /dev/null
+++ b/downloads/revenue_pitches/hunter/hunter.html
@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>THE HUNTER | Algorithmic Asymmetry</title>
+ <style>
+ body {
+ background-color: #0a0a0a;
+ color: #00ff41;
+ font-family: 'Courier New', Courier, monospace;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-height: 100vh;
+ margin: 0;
+ overflow-x: hidden;
+ text-shadow: 0 0 5px #00ff41;
+ }
+ .terminal {
+ width: 80%;
+ max-width: 900px;
+ border: 2px solid #00ff41;
+ padding: 20px;
+ background: rgba(0, 20, 0, 0.9);
+ box-shadow: 0 0 20px rgba(0, 255, 65, 0.2);
+ border-radius: 5px;
+ }
+ .cursor {
+ display: inline-block;
+ width: 10px;
+ height: 20px;
+ background: #00ff41;
+ animation: blink 1s infinite;
+ }
+ @keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
+ h1 { font-size: 2rem; border-bottom: 1px solid #00ff41; padding-bottom: 10px; }
+ .stat-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 20px;
+ margin: 20px 0;
+ }
+ .stat-card {
+ border: 1px solid #00ff41;
+ padding: 10px;
+ text-align: center;
+ }
+ .highlight { color: #fff; font-weight: bold; }
+ .action-btn {
+ background: #00ff41;
+ color: #000;
+ border: none;
+ padding: 15px 30px;
+ font-family: 'Courier New', Courier, monospace;
+ font-weight: bold;
+ cursor: pointer;
+ transition: all 0.3s;
+ margin-top: 20px;
+ }
+ .action-btn:hover { background: #fff; color: #000; box-shadow: 0 0 15px #fff; }
+ </style>
+</head>
+<body>
+ <div class="terminal">
+ <h1>> SYSTEM_STATUS: SEARCHING_FOR_ALPHA...<span class="cursor">&nbsp;</span></h1>
+ <p>Target: <span class="highlight">Hyperliquid Perps / L1 Ecosystem</span></p>
+ <p>Method: <span class="highlight">Surgical Neural Indexing + Asymmetric Arbitrage</span></p>
+
+ <div class="stat-grid">
+ <div class="stat-card">
+ <div>LATENCY</div>
+ <div class="highlight">SUB-MS</div>
+ </div>
+ <div class="stat-card">
+ <div>PRECISION</div>
+ <div class="highlight">Surgical</div>
+ </div>
+ <div class="stat-card">
+ <div>BIAS</div>
+ <div class="highlight">Quant-Neutral</div>
+ </div>
+ </div>
+
+ <div style="margin: 20px 0; line-height: 1.6;">
+ <p>[LOG]: Scanning Hyperliquid Orderbooks...</p>
+ <p>[LOG]: Identifying Liquidity Gaps...</p>
+ <p>[LOG]: Correlating Social Sentiment with Wallet Flows...</p>
+ <p>[LOG]: <strong>Anomaly Detected.</strong> Executing Hedge Strategy...</p>
+ </div>
+
+ <p style="color: #fff;">"We don't bet on coins. We bet on the inefficiencies of the crowd."</p>
+
+ <center>
+ <button class="action-btn" onclick="alert('Initializing Hunter Module... Connect Wallet to Begin.')">INITIATE HUNT</button>
+ </center>
+ </div>
+</body>
+</html>
diff --git a/help.html b/help.html
index 35baf8a..446de33 100644
--- a/help.html
+++ b/help.html
@@ -4,7 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<meta name="theme-color" content="#0a0a14">
-<title>WebX11 v4.5.24 โ€” Help</title>
+<title>WebX11 v4.5.34 โ€” Help</title>
<style>
/* โ”€โ”€ Reset & Base โ”€โ”€ */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;-webkit-tap-highlight-color:transparent}
@@ -231,7 +231,7 @@ footer .tagline{color:var(--accent2);margin-top:10px;font-weight:500}
<h1>WebX11 <span>Docs</span></h1>
</div>
<div class="header-meta">
- <span class="badge badge-v">v4.5.22</span>
+ <span class="badge badge-v">v4.5.34</span>
<span class="badge badge-new">38 core ยท ext</span>
<span style="color:var(--text2)">160+ commands</span>
</div>
@@ -414,7 +414,7 @@ webx11.inputField // DOM input element</code></pre>
<!-- โ”€โ”€ Overview โ”€โ”€ -->
<section id="overview">
<h2>โ›… Overview</h2>
- <p>WebX11 v4.5.24 is a browser-based Unix-like terminal โ€” dynamic modules, AI assistant (Ollama), persistent virtual filesystem, web search, voice input, code editor, and a full skill system. Everything runs client-side in your browser.</p>
+ <p>WebX11 v4.5.34 is a browser-based Unix-like terminal โ€” dynamic modules, AI assistant (Ollama), persistent virtual filesystem, web search, voice input, code editor, and a full skill system. Everything runs client-side in your browser.</p>
<div class="feature-grid">
<div class="feature-card"><h4>๐Ÿงฉ 18 Core + 14 Ext</h4><p>Modules loaded at startup. Shell with pipes, redirects, vars, and scripts.</p></div>
<div class="feature-card"><h4>๐Ÿค– Agent with Tools</h4><p>28 tools โ€” file ops, web search, skills, modules. Destructive action confirmations with inline bar + countdown.</p></div>
@@ -524,6 +524,8 @@ diff a.txt b.txt</pre>
<tr><td><code>ai-compress</code></td><td>Manually compress conversation context</td><td><code>ai-compress</code></td></tr>
<tr><td><code>ai-config</code></td><td>View/set AI loop config (max-turns, max-context, max-context-tokens, confirm-timeout(0=disabled), chat-lock-timeout, skill-debounce, trivial-optimization)</td><td><code>ai-config max-turns 12</code></td></tr>
<tr><td><code>debug</code></td><td>Toggle AI debug mode</td><td><code>debug</code></td></tr>
+ <tr><td><code>mem-debug</code></td><td>Memory diagnostic (tool call log, heap, localStorage sizes)</td><td><code>mem-debug</code></td></tr>
+ <tr><td><code>reset-mem</code></td><td>Clear AI internal logs without touching VFS/config</td><td><code>reset-mem</code></td></tr>
</table></div>
<h3>Smart Context</h3>
@@ -846,6 +848,8 @@ ollamaconnect</pre>
<tr><td><code>listmods</code></td><td>List all loaded modules</td></tr>
<tr><td><code>load-local</code></td><td>Load module from VFS or URL</td></tr>
<tr><td><code>loadlocal</code></td><td>Alias for load-local</td></tr>
+ <tr><td><code>js &lt;expr&gt;</code></td><td>Evaluate JavaScript in page context</td></tr>
+ <tr><td><code>js-run &lt;path&gt;</code></td><td>Load and execute a JS file from VFS</td></tr>
<tr><td><code>startup-modules</code></td><td>Configure boot modules</td></tr>
</table></div>
@@ -983,6 +987,41 @@ floating-add panel info "Server: production | Uptime: 12d"
<span class="cmt"># Position persists across page reloads</span></pre>
</section>
+ <!-- โ”€โ”€ Runtime Debug /sys/ โ”€โ”€ -->
+ <section id="runtime-debug">
+ <h2>๐Ÿ” Runtime Debug โ€” /sys/</h2>
+ <div class="callout callout-success">
+ <span class="callout-title">Virtual /sys/ filesystem</span>
+ Live runtime state mounted as browsable VFS files. Cat, grep, tree, find all work โ€” no special commands needed. Never persisted to localStorage.
+ </div>
+ <h3>Path Reference</h3>
+ <pre><span class="cmt"># Overview</span>
+cat /sys/info
+
+<span class="cmt"># Memory</span>
+cat /sys/heap/stats
+cat /sys/heap/breakdown
+
+<span class="cmt"># AI Internal Logs</span>
+cat /sys/logs/tool-calls
+cat /sys/logs/errors
+cat /sys/logs/commands
+
+<span class="cmt"># VFS Stats</span>
+cat /sys/vfs/stats
+cat /sys/vfs/largest
+
+<span class="cmt"># Module Info</span>
+ls /sys/modules/
+cat /sys/modules/calc/info
+
+<span class="cmt"># DOM / Config</span>
+cat /sys/dom/node-count
+cat /sys/config/ai
+cat /sys/config/version</pre>
+ <p>Also available: <code>mem-debug</code> (one-shot text output) and <code>reset-mem</code> (clear AI internal logs without touching VFS or config).</p>
+ </section>
+
<!-- โ”€โ”€ Shell โ”€โ”€ -->
<section id="shell">
<h2>๐Ÿš Shell</h2>
@@ -1256,6 +1295,7 @@ vfs-import</pre>
<tr><td><code>nvfs-config [key] [value]</code></td><td>View/set config (fuzzy-limit, tree-files, tree-concepts, resolve-files, deep-index, deep-model)</td></tr>
<tr><td><code>nvfs-alias list|add|remove|clear</code></td><td>Manage concept aliases</td></tr>
<tr><td><code>nvfs-sync [--deep]</code></td><td>Incremental background sync dirty files</td></tr>
+ <tr><td><code>nvfs-save</code></td><td>Force-save semantic index to VFS disk</td></tr>
</table></div>
</section>
@@ -1407,7 +1447,7 @@ listmods</pre>
<section id="ref">
<h2>โšก Quick Reference</h2>
<div class="quick-ref">
- <div class="quick-ref-title">WebX11 v4.5.11</div>
+ <div class="quick-ref-title">WebX11 v4.5.34</div>
<div class="quick-ref-grid">
<div class="quick-ref-item"><span class="cmd">help</span><span class="desc">Show all commands</span></div>
<div class="quick-ref-item"><span class="cmd">cls</span><span class="desc">Clear terminal</span></div>
@@ -1438,6 +1478,9 @@ listmods</pre>
<div class="quick-ref-item"><span class="cmd">nvfs-index</span><span class="desc">Build semantic index</span></div>
<div class="quick-ref-item"><span class="cmd">nvfs-resolve</span><span class="desc">Find files by concept</span></div>
<div class="quick-ref-item"><span class="cmd">nvfs-sync</span><span class="desc">Sync dirty files</span></div>
+ <div class="quick-ref-item"><span class="cmd">nvfs-save</span><span class="desc">Save index to disk</span></div>
+ <div class="quick-ref-item"><span class="cmd">js</span><span class="desc">Eval JS expression</span></div>
+ <div class="quick-ref-item"><span class="cmd">js-run</span><span class="desc">Run JS file</span></div>
<div class="quick-ref-item"><span class="cmd">vfs-size</span><span class="desc">VFS size report</span></div>
<div class="quick-ref-item"><span class="cmd">vfs-clean</span><span class="desc">Nuke system dirs</span></div>
<div class="quick-ref-item"><span class="cmd">mindmap svg</span><span class="desc">Render mindmap as SVG</span></div>
@@ -1462,6 +1505,19 @@ listmods</pre>
<h2>๐Ÿ“‹ Changelog</h2>
<div class="version-item">
+ <div class="v-header"><span class="v-num">v4.5.34 โ€” June 2026</span><span class="v-date">2026-06-10</span></div>
+ <ul>
+ <li><strong>NVFS deep index persistence</strong> โ€” LLM-generated concepts now survive page refresh. <code>_processDeepQueue()</code> calls <code>_saveIndex()</code>. Deep concepts written to disk on completion.</li>
+ <li><strong>nvfs-save command</strong> โ€” <code>nvfs-save</code> force-saves the semantic index to VFS disk immediately. Useful before hard refresh.</li>
+ <li><strong>Auto-sync deep mode</strong> โ€” <code>nvfs-sync --deep</code> or <code>nvfs-config deep-index on</code> now triggers LLM analysis for dirty files, not just fast filename concepts.</li>
+ <li><strong>Safety guard</strong> โ€” Running <code>nvfs-index</code> without <code>--deep</code> when saved index contains LLM concepts prints a warning: they'll be stripped from re-indexed files.</li>
+ <li><strong>JS Console module</strong> โ€” New <code>js &lt;expression&gt;</code> command evals JavaScript in page context. <code>js-run &lt;path&gt;</code> loads and executes a VFS script file. Auto-loads at startup.</li>
+ <li><strong>Module registration</strong> โ€” <code>console</code> added to packages.json, terminal.js ext loader, and skipList. Auto-loads on next refresh.</li>
+ <li>Cache: <code>2026-06-10-v4.5.34</code> โ€” hard refresh required</li>
+ </ul>
+ </div>
+
+ <div class="version-item">
<div class="v-header"><span class="v-num">v4.5.24 โ€” June 2026</span><span class="v-date">2026-06-08</span></div>
<ul>
<li><strong>tar / untar commands</strong> โ€” New pure-JS tarball parser in cryptogzip.js. <code>tar &lt;src-dir&gt; &lt;out.tar[.gz]&gt;</code> bundles a VFS directory; <code>untar &lt;path.tar[.gz]&gt; [dest-dir]</code> extracts back. Auto-detects gzip wrapper. Fixes the "downloaded newos4.tar.gz but can't extract" problem.</li>
diff --git a/main.html b/main.html
index b2a223d..80ee34b 100644
--- a/main.html
+++ b/main.html
@@ -1210,7 +1210,7 @@
var mods = [
['Foundation (always loaded)', ['virtual-fs.js','module-manager.js','ollama-manager.js','terminal.js'], true],
['Built-in modules', ['module-system.js','module-fs.js','module-ollama.js','module-my.js','module-searxng.js','module-servermon.js','module-ollamamodels.js','module-config.js','module-simplegui.js','module-io.js','module-dynamicprompts.js','module-aiassist.js','module-aiasm.js','module-smartctx.js','module-duckduckgo.js','module-fetcher.js','module-skills.js','module-chatstudio.js'], false],
- ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js'], false]
+ ['Extensions', ['calc.js','voice.js','cron.js','theme.js','wpm.js','mindmap.js','webdav.js','skill-pack-core.js','editor.js','diff.js','alias.js','finder.js','filemgr.js','cryptogzip.js','tutorial.js','shell.js','busybox.js','floating.js','console.js'], false]
];
var isSel = function(name) { return true; }; // default: all selected
mods.forEach(function(section) {
@@ -1247,7 +1247,7 @@
</script>
<!-- Async dependency-ordered module loader (replaces 26 synchronous script tags) -->
-<script src="modules/core/module-loader.js?v=2026-06-09-v4.5.28"></script>
+<script src="modules/core/module-loader.js?v=2026-06-09-v4.5.38"></script>
<script>
// โ•โ•โ• ONE TRUE VERSION SOURCE โ•โ•โ•
diff --git a/modules/core/module-aiassist.js b/modules/core/module-aiassist.js
index ecd4637..3af7f9b 100644
--- a/modules/core/module-aiassist.js
+++ b/modules/core/module-aiassist.js
@@ -44,53 +44,31 @@
}
} catch (e) {}
}
- buildSystemPrompt() {
+buildSystemPrompt() {
+ // Check for user-overridden prompt in localStorage
+ try {
+ const saved = localStorage.getItem('webx11_ai_system_prompt');
+ if (saved) {
+ const now = new Date();
+ const dateStr = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
+ const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
+ return `Current date: ${dateStr}\nCurrent time: ${timeStr}\n\n${saved}`;
+ }
+ } catch (e) {}
const now = new Date();
- const dateStr = now.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' });
- const timeStr = now.toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit' });
- const optimizationActive = this.terminal?.ollamaManager?.optimizationEnabled !== false;
- // Neural VFS staleness check
+ const dateStr = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0') + '-' + String(now.getDate()).padStart(2,'0');
+ const nvfs = this.terminal?.moduleManager?.modules?.NeuralVFSModule;
let nvfsNote = '';
try {
- const nvfs = this.terminal?.moduleManager?.modules?.NeuralVFSModule;
if (nvfs && nvfs.lastScan && nvfs.lastScan !== '(loaded from disk)') {
const ageMs = Date.now() - new Date(nvfs.lastScan).getTime();
- if (ageMs > 14400000) { // > 4 hours
- nvfsNote = `\nNote: Neural VFS index is ${Math.floor(ageMs/3600000)}h old. nvfs-resolve results may be stale. Use list_files/read_file directly for live data.`;
- }
+ if (ageMs > 14400000) nvfsNote = ' |nvfs ' + Math.floor(ageMs/3600000) + 'h stale';
}
} catch (e) {}
- return `Current date: ${dateStr}
-Current time: ${timeStr}${nvfsNote}
-
-You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS โ†’ Modules โ†’ AI. You can edit your own modules via edit_module/test_module/reload_module.
-
-=== WORKFLOW ===
-1. Tool โ†’ Observe โ†’ Answer. Don't re-verify what a tool just told you.
-2. find_files("about X") โ†’ nvfs_resolve(X) FIRST. NVFS is built-in core, never WPM.
-3. load_skills("anti-loop-protocol") BEFORE tool #4 to prevent loops.
-4. If editing a module: load_skills("module-editing-workflow") FIRST.
-5. run_command may be blocked by security-level/guardrails โ€” read the output.
-6. Concise. Bullets. If a tool fails, try another.
-
-=== CAPABILITY MAP ===
-[FS] list_files, read_file, write_file, make_directory, delete_file, get_current_dir
-[NVFS] nvfs_resolve(concept), run_command: nvfs-status, nvfs-index, nvfs-tree. Skills: vfs-content-search, neural-vfs-management.
-[MODULES] view_module, edit_module, test_module, reload_module, list_modified_modules, get_module_status. CORE modules (never WPM): NVFS, Floating, Config, Vault, Sandbox, Orchestrator, Memory-Graph, World-Model, Predictor, Neural-VFS.
-[WPM] wpm_install, wpm_search, list_available_modules
-[SEARCH] web_search, web_fetch
-[AGENTS] delegate_task, list_agents, spawn_agent. A2A: a2a-send, a2a-list. Leases: lease-create, lease-list, lease-revoke.
-[SANDBOX] run_command: sandbox-status, sandbox-run, sandbox-config
-[CONFIG] run_command: config, config-set, ai-config
-[SKILLS] load_skills, create_skill, patch_skill, list_skills
-[PROFILE/SESSION] user_get/set/list/forget, session_save/load/recent/stats/terminal
-[SMART-CTX] smart_context_list, smart_context_set
-[MINDS] create_mindmap
-[SAFETY] run_command: guardrail, guardrail-add/remove, shadow, shadow-commit, shadow-rollback
-[VAULT] run_command: secret-set/get/list/del, secret-setpin, secret-status
-[CHECKPOINTS] run_command: checkpoint, checkpoint-list, checkpoint-del, rollback
-[WEBDAV] run_command: webdav-config/ls/push/pull/backup/restore
-[ALL OTHERS] cron, theme, voice, edit, diff, alias, mindmap, ll, cls, trash, gui, server, modules, ollama, context, world-model, memgraph, model, webxai, predict, predictor-config, memgraph-export, tar, untar, wget`;
+ return 'Current date: ' + dateStr + nvfsNote + '\n'
+ + 'VFS\u2192Modules\u2192AI. nvfs-index for commands. Tool\u2192Observe\u2192Answer. nvfs-resolve. Destructive cmds need confirm.\n'
+ + 'FS: list/read/write/delete | NVFS: nvfs-resolve | Web: search/fetch | Mod: view/edit/test/reload | Config | Skills\n'
+ + 'Other: floating, webdav, cron, vault, sandbox, agents, wpm, mindmap, memgraph, guardrail, wget, tar, secret-* \u2014 discoverable via help';
}
getToolDefinitions() {
if (!this._toolDefs) this._toolDefs = this._buildToolDefs();
@@ -449,7 +427,9 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
'ai-ignore': 'ai-ignore โ€” Suppress validator warnings for this session',
'ai-stop': 'ai-stop โ€” Cancel current AI operation',
'ai-model': 'ai-model [model] โ€” Show or set AI model',
- 'ai-config': 'ai-config [key] [value] โ€” View or set AI loop config (max-turns, max-context, confirm-timeout(0=disabled))'
+ 'ai-config': 'ai-config [key] [value] โ€” View or set AI loop config (max-turns, max-context, confirm-timeout(0=disabled))',
+ 'mem-debug': 'mem-debug โ€” Show memory breakdown by component',
+ 'reset-mem': 'reset-mem โ€” Reset AI memory (logs, patterns) without losing VFS or config'
};
}
registerCommands() {
@@ -472,6 +452,8 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
this.commands['shadow'] = (args) => this.shadowExec(args);
this.commands['shadow-commit'] = () => this.shadowCommit();
this.commands['shadow-rollback'] = () => this.shadowRollback();
+ this.commands['mem-debug'] = () => this.memDebug();
+ this.commands['reset-mem'] = () => this.resetMem();
}
async chat(args) {
const userMessage = args.join(' ');
@@ -735,6 +717,10 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
duration: t1,
error: this._isToolError(toolResult) ? true : false
});
+ // Auto-trim to 50 โ€” prevents unbounded memory growth
+ if (this._toolCallLog.length > 50) {
+ this._toolCallLog = this._toolCallLog.slice(-50);
+ }
// Telemetry
const sm = this.terminal.moduleManager.modules['ServerMonitorModule'];
if (sm && sm.logToolCall) sm.logToolCall(fn.name, (t1 * 1000) || 1, this._isToolError(toolResult));
@@ -801,6 +787,10 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
duration: t1,
error: this._isToolError(toolResult) ? true : false
});
+ // Auto-trim to 50 โ€” prevents unbounded memory growth
+ if (this._toolCallLog.length > 50) {
+ this._toolCallLog = this._toolCallLog.slice(-50);
+ }
// Telemetry
const sm2 = this.terminal.moduleManager.modules['ServerMonitorModule'];
if (sm2 && sm2.logToolCall) sm2.logToolCall(fn.name, (t1 * 1000) || 1, this._isToolError(toolResult));
@@ -1350,7 +1340,10 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
'cal','tree','find','grep','head','tail','sort','wc','du','diff','basename','dirname',
'yes','seq','cut','uniq','expand','printf','base64','help','clear','set','export',
'unalias','history','ll','h','mem','status','df','free','top','ps','health-check',
- 'system-health','modules','listmods','modules-list','get-help-info','alias']);
+ 'system-health','modules','listmods','modules-list','get-help-info','alias',
+ 'nvfs-index','nvfs-status','nvfs-resolve','nvfs-tree','nvfs-config','nvfs-alias','nvfs-sync',
+ 'floating-add','floating-remove','floating-list','floating-pos','floating-clear','floating-toggle',
+ 'mem-debug','reset-mem']);
return !safeRead.has(cmd);
}
return ['write_file','delete_file','make_directory','edit_module'].includes(name);
@@ -1843,6 +1836,7 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
}
} catch (e) {
this._errorLog.push({ tool: name, args: a, error: e.message, timestamp: Date.now() });
+ if (this._errorLog.length > 20) this._errorLog.shift();
return JSON.stringify({ error: e.message, stack: e.stack?.substring(0, 200) });
}
}
@@ -2007,6 +2001,7 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
}
} catch (e) {
this._errorLog.push({ tool: name, args: a, error: e.message, timestamp: Date.now() });
+ if (this._errorLog.length > 20) this._errorLog.shift();
return JSON.stringify({ error: e.message, stack: e.stack?.substring(0, 200) });
}
}
@@ -2049,10 +2044,46 @@ You are WebX11AI โ€” embedded in the WebX11 newOS4 terminal. 3-layer system: VFS
}
setSystemPrompt(args) {
if (!args[0]) {
- this.terminal.print(`Current system prompt: ${this.systemPrompt.substring(0, 50)}...`, 'info'); return;
+ this.terminal.print('=== System Prompt ===', 'info');
+ this.systemPrompt.split('\n').forEach(line => this.terminal.print(line, 'text-muted'));
+ this.terminal.print(`=== End (${this.systemPrompt.length} chars) ===`, 'info');
+ return;
}
this.systemPrompt = args.join(' ');
- this.terminal.print('System prompt updated', 'success');
+ // Persist to localStorage โ€” next page load will use this instead of built-in
+ try { localStorage.setItem('webx11_ai_system_prompt', this.systemPrompt); } catch (e) {}
+ this.terminal.print('System prompt updated (persisted)', 'success');
+ }
+ memDebug() {
+ const toolLogSize = this._toolCallLog.length;
+ const errLogSize = this._errorLog.length;
+ const freqKeys = Object.keys(this._commandFreq).length;
+ const contextSize = this.context.length;
+ const vfsSize = (localStorage.getItem('webx11_filesystem') || '').length +
+ (localStorage.getItem('webx11_filesystem_v2') || '').length;
+ const aiCtxSize = (localStorage.getItem('webx11_ai_context') || '').length;
+ this.terminal.print('=== Memory Debug ===', 'info');
+ this.terminal.print(` _toolCallLog: ${toolLogSize} entries (cap: 50)`, toolLogSize > 30 ? 'warning' : 'text-muted');
+ this.terminal.print(` _errorLog: ${errLogSize} entries (cap: 20)`, errLogSize > 10 ? 'warning' : 'text-muted');
+ this.terminal.print(` _commandFreq: ${freqKeys} tracked patterns`, freqKeys > 15 ? 'warning' : 'text-muted');
+ this.terminal.print(` AI context: ${contextSize} messages (max: ${this.maxContext * 2})`, 'text-muted');
+ this.terminal.print(` VFS localStorage: ${(vfsSize / 1024).toFixed(1)} KB`, 'text-muted');
+ this.terminal.print(` AI context localStorage: ${(aiCtxSize / 1024).toFixed(1)} KB`, 'text-muted');
+ const heap = performance.memory ? performance.memory.usedJSHeapSize : null;
+ if (heap) {
+ this.terminal.print(` JS heap: ${(heap / 1048576).toFixed(1)} MB / ${(performance.memory.jsHeapSizeLimit / 1048576).toFixed(1)} MB`, 'text-muted');
+ }
+ this.terminal.print('Use reset-mem to clear AI internal logs without touching VFS/config', 'info');
+ }
+ resetMem() {
+ this._toolCallLog = [];
+ this._errorLog = [];
+ this._commandFreq = {};
+ this.context = [];
+ localStorage.removeItem('webx11_ai_context');
+ localStorage.removeItem('webx11_ai_patterns');
+ this.terminal.print('AI memory reset: tool logs, error logs, command patterns, and context cleared', 'success');
+ this.terminal.print('VFS, config, skills, system prompt, and floating elements preserved', 'text-muted');
}
showModel(args) {
const om = this.terminal.ollamaManager;
diff --git a/modules/core/module-loader.js b/modules/core/module-loader.js
index f0d3f4c..ab73202 100644
--- a/modules/core/module-loader.js
+++ b/modules/core/module-loader.js
@@ -105,7 +105,7 @@
window.__moduleLoaderReload = reloadWithSelection;
- const LATEST_VER = '2026-06-09-v4.5.27';
+ const LATEST_VER = '2026-06-10-v4.5.34';
const CACHE = '?v=' + LATEST_VER;
const stored = localStorage.getItem('webx11_cache_ver');
if (stored !== LATEST_VER) {
@@ -164,7 +164,7 @@
// Phase 4: Ext modules (filtered)
if (typeof webx11._loadExtModules === 'function') {
- const extKeys = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'tutorial', 'shell', 'busybox', 'floating'];
+ const extKeys = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'tutorial', 'shell', 'busybox', 'floating', 'console'];
const extSelected = selected
? extKeys.filter(k => isSelected(k + '.js', selected))
: extKeys;
diff --git a/modules/core/module-neural-vfs.js b/modules/core/module-neural-vfs.js
index bdf1ffb..613d53a 100644
--- a/modules/core/module-neural-vfs.js
+++ b/modules/core/module-neural-vfs.js
@@ -7,6 +7,7 @@ class NeuralVFSModule {
'nvfs-tree': { argType: 'file' }, 'nvfs-config': { argType: 'file' },
'nvfs-alias': { argType: 'file' },
'nvfs-sync': { argType: 'file' },
+ 'nvfs-save': { argType: 'none' },
};
this.terminal = null;
this.index = {};
@@ -55,7 +56,8 @@ class NeuralVFSModule {
'nvfs-tree': 'nvfs-tree [concept] โ€” Show concept-to-file mapping as a tree',
'nvfs-config': 'nvfs-config [key] [value] โ€” View or set Neural VFS config (fuzzy-limit, tree-files, tree-concepts, resolve-files, deep-index, deep-model)',
'nvfs-alias': 'nvfs-alias list|add|remove|clear โ€” Manage concept aliases',
- 'nvfs-sync': 'nvfs-sync [--deep] โ€” Incremental background sync (auto-sync dirty files)'
+ 'nvfs-sync': 'nvfs-sync [--deep] โ€” Incremental background sync (auto-sync dirty files)',
+ 'nvfs-save': 'nvfs-save โ€” Force-save current in-memory index to VFS (survives refresh)'
};
}
@@ -67,6 +69,7 @@ class NeuralVFSModule {
this.commands['nvfs-config'] = (args) => this.nvfsConfig(args);
this.commands['nvfs-alias'] = (args) => this.nvfsAlias(args);
this.commands['nvfs-sync'] = async (args) => this.nvfsSync(args);
+ this.commands['nvfs-save'] = (args) => this.nvfsSave();
}
// โ”€โ”€ Core Index Builder (async, chunked) โ”€โ”€
@@ -76,6 +79,19 @@ class NeuralVFSModule {
const fs = this.terminal.fs;
if (!fs) return this.terminal.print('VFS not available', 'error');
+ // Safety guard: warn if deep mode is off but the saved index has LLM concepts
+ if (!useDeep) {
+ const prevIndex = fs.fsReadJSON(this.indexPath) || {};
+ const hasLLM = Object.values(prevIndex).some(entries =>
+ Array.isArray(entries) && entries.some(e => e.source === 'llm')
+ );
+ if (hasLLM) {
+ this.terminal.print('โš ๏ธ Deep indexing is OFF but the saved index contains LLM-generated concepts.', 'warning');
+ this.terminal.print(' Running nvfs-index without --deep will STRIP all LLM concepts from re-indexed files.', 'warning');
+ this.terminal.print(' To keep them: nvfs-config deep-index on or nvfs-index --deep', 'warning');
+ }
+ }
+
this.terminal.print(`๐Ÿ” Scanning VFS for semantic concepts${useDeep ? ' (deep mode)' : ''}...`, 'info');
const start = Date.now();
@@ -634,7 +650,7 @@ class NeuralVFSModule {
const hash = this._contentHash(content);
const cached = this._getCached(path, hash);
if (!cached) {
- this._reindexFile(path, content, this.index);
+ this._reindexFile(path, content, this.index, this._deepIndexEnabled);
this._hashCache[path] = { hash };
reindexed++;
}
@@ -768,6 +784,30 @@ Concepts:`;
const llmConcepts = results.reduce((s, r) => s + r.concepts.length, 0);
this.terminal?.print(`๐Ÿง  Deep pass complete: ${llmConcepts} LLM-generated concepts across ${results.length} files`, 'success');
}
+ // Persist LLM-generated concepts to disk so they survive page refresh
+ // Must force-save because _saveIndex() calls saveToStorage() which has a
+ // debounce guard (_storageSavePending) that may still be set from the
+ // initial buildIndex() save โ€” without clearing it, the LLM concepts are
+ // never written to localStorage and are lost on page refresh.
+ const vfs = this.terminal?.fs;
+ if (vfs) {
+ vfs._storageSavePending = false;
+ this._saveIndex(vfs);
+ }
+ }
+
+ // โ”€โ”€ Nvfs Save (force-persist in-memory index) โ”€โ”€
+
+ nvfsSave() {
+ const fs = this.terminal?.fs;
+ if (!fs) return this.terminal?.print('VFS not available', 'error');
+ const count = Object.keys(this.index).length;
+ if (count === 0) return this.terminal?.print('No index in memory to save', 'warning');
+ // Bypass debounce guard to ensure write
+ fs._storageSavePending = false;
+ this._saveIndex(fs);
+ this.terminal?.print(`โœ… Saved ${count} concepts to ${this.indexPath}`, 'success');
+ this.terminal?.print(' Index will survive page refresh', 'info');
}
// โ”€โ”€ Nvfs Sync (incremental background) โ”€โ”€
diff --git a/modules/core/ollama-manager.js b/modules/core/ollama-manager.js
index 9999184..253e319 100644
--- a/modules/core/ollama-manager.js
+++ b/modules/core/ollama-manager.js
@@ -14,9 +14,9 @@ class OllamaManager {
// --- AI Agent Loop Optimization Toggle ---
this.optimizationEnabled = localStorage.getItem('webx11_optimization_enabled') !== 'false';
// --- Streaming Toggle ---
- this.streamEnabled = localStorage.getItem('webx11_stream_enabled') !== 'false';
+ this.streamEnabled = localStorage.getItem('webx11_stream_enabled') === 'true';
// --- Validator Agent Config ---
- this.validatorEnabled = localStorage.getItem('webx11_validator_enabled') !== 'false';
+ this.validatorEnabled = localStorage.getItem('webx11_validator_enabled') === 'true';
this.validatorModel = localStorage.getItem('webx11_validator_model') || 'ministral-3:3b-cloud';
this.validatorEndpoint = localStorage.getItem('webx11_validator_endpoint') || '';
// Build fallback endpoints
diff --git a/modules/core/terminal.js b/modules/core/terminal.js
index 87636d6..fb9f332 100644
--- a/modules/core/terminal.js
+++ b/modules/core/terminal.js
@@ -10,7 +10,7 @@
this.destructiveCmds = new Set(['delete','del','write','save','overwrite','format','kill','stop','shutdown','reboot']);
this.systemCmds = new Set(['reboot','shutdown','format','kill','halt','poweroff']);
this.suspiciousFlags = new Set(['--dump','--execute','--eval','--exec','--shell','--script']);
- this.whitelist = new Set(['help','clear','ls','cat','pwd','whoami','df','free','top','ps','echo','date','env','history','find','grep','tree','head','tail','wc','sort','du','diff','which','hostname','cal','basename','dirname','yes','seq','cut','uniq','expand','printf','base64','sleep','ll','h','mem','status','health-check','system-health','alias','unalias','modules','listmods','modules-list','nvfs-index','nvfs-status','nvfs-resolve','nvfs-tree','nvfs-config','nvfs-alias','nvfs-sync','floating-add','floating-remove','floating-list','floating-pos','floating-clear','floating-toggle']);
+ this.whitelist = new Set(['help','clear','ls','cat','pwd','whoami','df','free','top','ps','echo','date','env','history','find','grep','tree','head','tail','wc','sort','du','diff','which','hostname','cal','basename','dirname','yes','seq','cut','uniq','expand','printf','base64','sleep','ll','h','mem','status','health-check','system-health','alias','unalias','modules','listmods','modules-list','nvfs-index','nvfs-status','nvfs-resolve','nvfs-tree','nvfs-config','nvfs-alias','nvfs-sync','floating-add','floating-remove','floating-list','floating-pos','floating-clear','floating-toggle','mem-debug','reset-mem']);
// Load persisted overrides from localStorage
try { const d = localStorage.getItem('webx11_security_destructive'); if(d){JSON.parse(d).forEach(c=>this.destructiveCmds.add(c));} }catch(e){}
try { const s = localStorage.getItem('webx11_security_system'); if(s){JSON.parse(s).forEach(c=>this.systemCmds.add(c));} }catch(e){}
@@ -1183,7 +1183,7 @@
} catch (e) {}
}
async _loadExtModules(filterKeys) {
- const autoLoadKeys = filterKeys || ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial', 'busybox', 'floating'];
+ const autoLoadKeys = filterKeys || ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial', 'busybox', 'floating', 'console'];
const cacheVer = localStorage.getItem('webx11_cache_ver') || '2026-05-27-14';
try {
const res = await fetch('packages.json?v=' + cacheVer);
@@ -1202,7 +1202,7 @@
}
async reloadPersistedModules() {
const registry = this.moduleManager.moduleRegistry;
- const skipList = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial', 'busybox', 'floating'];
+ const skipList = ['calc', 'voice', 'cron', 'theme', 'wpm', 'mindmap', 'webdav', 'skill-pack-core', 'editor', 'diff', 'alias', 'finder', 'filemgr', 'cryptogzip', 'shell', 'tutorial', 'busybox', 'floating', 'console'];
for (const mod of registry) {
if (mod.enabled && mod.url) {
// Skip ext modules โ€” they load via _loadExtModules
diff --git a/modules/core/virtual-fs.js b/modules/core/virtual-fs.js
index f6e2826..9d3ea79 100644
--- a/modules/core/virtual-fs.js
+++ b/modules/core/virtual-fs.js
@@ -3,6 +3,308 @@ class VirtualFileSystem {
this.root = { type: 'dir', content: {} };
this.currentPath = [];
this._ready = this.loadFromStorage();
+ this._virtualMounts = null; // lazy-init on first /sys/ access
+ // Register /sys/ in emit-change exclusion (also added below)
+ }
+ // โ”€โ”€ Virtual /sys/ filesystem (live runtime state, never persisted) โ”€โ”€
+ _initVirtualMounts() {
+ if (this._virtualMounts) return;
+ const vm = (name, factory) => ({ _factory: factory, _name: name });
+ const dir = () => ({ _dir: true, content: {} });
+ const vmCache = {}; // factories can cache costly results within same tick
+ const mm = () => typeof window !== 'undefined' ? window.webx11?.moduleManager?.modules : null;
+ const ai = () => mm()?.WebX11AIAssistantModule || null;
+ const heap = () => typeof performance !== 'undefined' && performance.memory ? performance.memory : null;
+ const fmt = (bytes) => {
+ if (!bytes && bytes !== 0) return 'N/A';
+ const kb = bytes / 1024;
+ if (kb < 1024) return kb.toFixed(1) + ' KB';
+ return (kb / 1024).toFixed(1) + ' MB';
+ };
+ const fmtBytes = (bytes) => {
+ if (!bytes && bytes !== 0) return 'N/A';
+ if (bytes < 1024) return bytes + ' B';
+ const kb = bytes / 1024;
+ if (kb < 1024) return kb.toFixed(1) + ' KB';
+ return (kb / 1024).toFixed(1) + ' MB';
+ };
+ // Walk VFS root to count files/dirs and find largest
+ const vfsWalk = (dir, prefix) => {
+ let files = 0, dirs = 0, total = 0, largest = [];
+ const walk = (node, p) => {
+ if (!node) return;
+ if (node._factory || node._dir) return; // skip virtual
+ if (node.type === 'file') {
+ files++;
+ const size = node.content ? node.content.length : 0;
+ total += size;
+ largest.push({ path: p, size });
+ } else if (node.type === 'dir') {
+ dirs++;
+ for (const [name, child] of Object.entries(node.content || {})) {
+ walk(child, p + '/' + name);
+ }
+ }
+ };
+ walk(dir, prefix);
+ largest.sort((a, b) => b.size - a.size);
+ return { files, dirs, total, largest: largest.slice(0, 20) };
+ };
+ this._virtualMounts = {
+ // /sys/info
+ info: vm('info', () => [
+ '=== newOS4 Runtime Debug (/sys/) ===',
+ `Version: ${window.NEWOS4_VERSION || '?'}`,
+ `JS heap: ${fmt(heap()?.usedJSHeapSize)} / ${fmt(heap()?.jsHeapSizeLimit)}`,
+ `DOM nodes: ${typeof document !== 'undefined' ? document.querySelectorAll('*').length : 'N/A'}`,
+ `Loaded modules: ${mm() ? Object.keys(mm()).length : 'N/A'}`,
+ '',
+ ' cat /sys/heap/stats โ€” Memory overview',
+ ' cat /sys/heap/breakdown โ€” What consumes what',
+ ' cat /sys/logs/tool-calls โ€” AI tool call log',
+ ' cat /sys/logs/errors โ€” AI error log',
+ ' cat /sys/vfs/stats โ€” VFS file/dir counts',
+ ' cat /sys/vfs/largest โ€” Top 20 largest files',
+ ' ls /sys/modules/ โ€” Loaded modules',
+ ' cat /sys/config/ai โ€” AI config parameters',
+ ' cat /sys/dom/node-count โ€” DOM element breakdown',
+ ].join('\n')),
+ // /sys/heap/
+ heap: { _dir: true, content: {
+ stats: vm('stats', () => {
+ const h = heap();
+ const lsSize = (k) => { try { return (localStorage.getItem(k) || '').length / 1024; } catch(e) { return 0; } };
+ return [
+ '=== Heap Stats ===',
+ ` JS heap used: ${fmt(h?.usedJSHeapSize)}`,
+ ` JS heap limit: ${fmt(h?.jsHeapSizeLimit)}`,
+ ` JS heap % used: ${h ? ((h.usedJSHeapSize / h.jsHeapSizeLimit) * 100).toFixed(1) + '%' : 'N/A'}`,
+ '',
+ ` VFS localStorage: ${lsSize('webx11_filesystem_v2').toFixed(1)} KB`,
+ ` AI localStorage: ${lsSize('webx11_ai_context').toFixed(1)} KB`,
+ ` Config localStorage: ${lsSize('webx11_ai_config').toFixed(1)} KB`,
+ ].join('\n');
+ }),
+ breakdown: vm('breakdown', () => {
+ const a = ai();
+ const cCount = a?.context?.length || 0;
+ const lCount = a?._toolCallLog?.length || 0;
+ const eCount = a?._errorLog?.length || 0;
+ const modules = mm() ? Object.keys(mm()).length : 0;
+ const domNodes = typeof document !== 'undefined' ? document.querySelectorAll('*').length : 0;
+ return [
+ '=== Heap Breakdown (estimates) ===',
+ ` Module code + state: ~${(modules * 1.2).toFixed(1)} MB (${modules} modules ร— ~1.2MB)`,
+ ` VFS tree (in-memory): varies with file count`,
+ ` AI context messages: ${cCount} (max: ${a?.maxContext || '?'})`,
+ ` AI tool call log: ${lCount} (cap: 50)`,
+ ` AI error log: ${eCount} (cap: 20)`,
+ ` DOM nodes: ${domNodes}`,
+ ].join('\n');
+ })
+ }},
+ // /sys/modules/
+ modules: { _dir: true, content: {
+ list: vm('list', () => {
+ const m = mm();
+ if (!m) return '(moduleManager not available)';
+ return Object.keys(m).sort().join('\n');
+ })
+ // module subdirectories are dynamic โ€” handled in _virtualLs
+ }},
+ // /sys/vfs/
+ vfs: { _dir: true, content: {
+ stats: vm('stats', () => {
+ const w = vfsWalk(this.root, '');
+ return [
+ '=== VFS Stats ===',
+ ` Files: ${w.files}`,
+ ` Directories: ${w.dirs}`,
+ ` Total content: ${fmtBytes(w.total)}`,
+ ` localStorage: ${((localStorage.getItem('webx11_filesystem_v2') || '').length / 1024).toFixed(1)} KB (compressed)`,
+ ].join('\n');
+ }),
+ largest: vm('largest', () => {
+ const w = vfsWalk(this.root, '');
+ if (w.largest.length === 0) return '(empty VFS)';
+ const maxW = Math.max(...w.largest.map(f => f.path.length), 8);
+ let out = '=== Top 20 Largest Files ===\n';
+ w.largest.forEach((f, i) => {
+ out += ` ${(i + 1).toString().padStart(2)}. ${f.path.padEnd(maxW)} ${fmtBytes(f.size)}\n`;
+ });
+ return out;
+ })
+ }},
+ // /sys/logs/
+ logs: { _dir: true, content: {
+ 'tool-calls': vm('tool-calls', () => {
+ const a = ai();
+ if (!a || !a._toolCallLog || a._toolCallLog.length === 0) return '(empty)';
+ return a._toolCallLog.map((e, i) => `[${i}] ${JSON.stringify(e)}`).join('\n');
+ }),
+ 'errors': vm('errors', () => {
+ const a = ai();
+ if (!a || !a._errorLog || a._errorLog.length === 0) return '(empty)';
+ return a._errorLog.map((e, i) => `[${i}] ${JSON.stringify(e)}`).join('\n');
+ }),
+ 'commands': vm('commands', () => {
+ const a = ai();
+ if (!a || !a._commandFreq || Object.keys(a._commandFreq).length === 0) return '(empty)';
+ const sorted = Object.entries(a._commandFreq).sort((a,b) => b[1] - a[1]);
+ return sorted.map(([cmd, n]) => `${cmd.padEnd(20)} ${n}x`).join('\n');
+ })
+ }},
+ // /sys/dom/
+ dom: { _dir: true, content: {
+ 'node-count': vm('node-count', () => {
+ if (typeof document === 'undefined') return 'N/A';
+ const all = document.querySelectorAll('*');
+ const byTag = {};
+ all.forEach(el => { const t = el.tagName.toLowerCase(); byTag[t] = (byTag[t] || 0) + 1; });
+ const sorted = Object.entries(byTag).sort((a,b) => b[1] - a[1]);
+ let out = `Total DOM nodes: ${all.length}\n\n=== By Tag ===\n`;
+ sorted.forEach(([tag, count]) => {
+ out += ` ${tag.padEnd(15)} ${count}\n`;
+ });
+ return out;
+ })
+ }},
+ // /sys/config/
+ config: { _dir: true, content: {
+ version: vm('version', () => {
+ return `NEWOS4_VERSION: ${window.NEWOS4_VERSION || '?'}\n`;
+ }),
+ ai: vm('ai', () => {
+ const a = ai();
+ if (!a) return '(AI module not loaded)';
+ return [
+ '=== AI Config ===',
+ ` maxContext: ${a.maxContext}`,
+ ` MAX_TOOL_TURNS: ${a.MAX_TOOL_TURNS}`,
+ ` maxContextTokens: ${a.maxContextTokens}`,
+ ` confirmTimeoutMs: ${a._confirmTimeoutMs}`,
+ ` skillDebounceMs: ${a._skillDebounceMs}`,
+ ` context messages: ${a.context?.length || 0}`,
+ ` tool call log cap: 50 (auto-capped)`,
+ ` error log cap: 20 (auto-capped)`,
+ ].join('\n');
+ })
+ }}
+ };
+ }
+ // Check if a path is under /sys/
+ _isVirtualPath(path) {
+ if (!path) return false;
+ const normalized = path.startsWith('/') ? path : '/' + path;
+ return normalized === '/sys' || normalized.startsWith('/sys/');
+ }
+ // Resolve a virtual path: returns {dir: virtualNode, name: lastSegment} or null
+ _resolveVirtualPath(path) {
+ this._initVirtualMounts();
+ const normalized = path.startsWith('/') ? path : '/' + path;
+ let segments = normalized.split('/').filter(s => s);
+ // Remove leading 'sys'
+ if (segments[0] === 'sys') segments = segments.slice(1);
+ if (segments.length === 0) {
+ // /sys itself โ€” return the root virtual mounts node as a dir
+ const allContent = {};
+ for (const [k, v] of Object.entries(this._virtualMounts)) {
+ if (v._dir) allContent[k] = { type: 'dir', content: v.content, _virtual: true };
+ else allContent[k] = { type: 'file', content: null, _virtual: true, _factory: v._factory };
+ }
+ // Add dynamic module subdirs to modules listing
+ if (this._virtualMounts.modules?._dir) {
+ const mm = typeof window !== 'undefined' ? window.webx11?.moduleManager?.modules : null;
+ if (mm) {
+ for (const modName of Object.keys(mm)) {
+ if (!allContent[modName]) {
+ allContent[modName] = { type: 'dir', content: {}, _virtual: true };
+ }
+ }
+ }
+ }
+ return { dir: { content: allContent }, name: null };
+ }
+ // Walk the tree
+ let node = this._virtualMounts;
+ for (let i = 0; i < segments.length - 1; i++) {
+ const seg = segments[i];
+ if (!node[seg]) {
+ // Check dynamic module subdir under /sys/modules/
+ const mods = typeof window !== 'undefined' ? window.webx11?.moduleManager?.modules : null;
+ if (node === this._virtualMounts?.modules?.content && mods && mods[seg]) {
+ // Dynamic module subdir โ€” create virtual node
+ node = {
+ _dir: true,
+ content: {
+ info: { type: 'file', content: null, _virtual: true, _factory: () => {
+ const mod = mods[seg];
+ const cmdKeys = mod.commands ? Object.keys(mod.commands) : [];
+ return [
+ `Module: ${seg}`,
+ ` Type: ${typeof mod}`,
+ ` Commands: ${cmdKeys.join(', ') || '(none)'}`,
+ ` Has help: ${mod.getHelpInfo ? 'yes' : 'no'}`,
+ ` Constructor: ${mod.constructor?.name || 'anonymous'}`,
+ ].join('\n');
+ }}
+ }
+ };
+ continue;
+ }
+ return null;
+ }
+ node = node[seg];
+ if (node && node._dir) node = node.content || {};
+ }
+ const lastName = segments[segments.length - 1];
+ if (node && node[lastName]) {
+ const entry = node[lastName];
+ if (entry._dir) {
+ // Static subdir with known content
+ const allContent = {};
+ for (const [k, v] of Object.entries(entry.content || {})) {
+ if (v._dir) allContent[k] = { type: 'dir', content: v.content, _virtual: true };
+ else allContent[k] = { type: 'file', content: null, _virtual: true, _factory: v._factory };
+ }
+ // Add dynamic module subdirs
+ if (segments[0] === 'modules' || (segments.length >= 2 && segments[segments.length - 2] === 'modules')) {
+ const mm = typeof window !== 'undefined' ? window.webx11?.moduleManager?.modules : null;
+ if (mm) {
+ for (const modName of Object.keys(mm)) {
+ if (!allContent[modName]) {
+ allContent[modName] = { type: 'dir', content: {}, _virtual: true };
+ }
+ }
+ }
+ }
+ return { dir: { content: allContent }, name: null };
+ } else {
+ return { dir: { content: node }, name: lastName };
+ }
+ }
+ // Check if it's a module subdirectory itself (e.g. /sys/modules/calc)
+ const mods = typeof window !== 'undefined' ? window.webx11?.moduleManager?.modules : null;
+ if (node && mods && mods[lastName] && segments.length >= 1) {
+ // It's a module name โ€” return its info dir
+ return {
+ dir: { content: {
+ info: { type: 'file', content: null, _virtual: true, _factory: () => {
+ const mod = mods[lastName];
+ const cmdKeys = mod.commands ? Object.keys(mod.commands) : [];
+ return [
+ `Module: ${lastName}`,
+ ` Type: ${typeof mod}`,
+ ` Commands: ${cmdKeys.join(', ') || '(none)'}`,
+ ` Has help: ${mod.getHelpInfo ? 'yes' : 'no'}`,
+ ` Constructor: ${mod.constructor?.name || 'anonymous'}`,
+ ].join('\n');
+ }}
+ }},
+ name: null
+ };
+ }
+ return null;
}
_u8ToBase64(u8) {
let s = '';
@@ -70,7 +372,7 @@ class VirtualFileSystem {
// Skip internal index/system files to prevent re-trigger loops
if (!path) return;
const normalized = path.startsWith('/') ? path : '/' + path;
- const sysPaths = ['/.semantic_index', '/.memory_graph', '/.trash', '/_snapshots', '/_sessions', '/logs', '/memory', '/system', '/_mods', '/backups'];
+ const sysPaths = ['/.semantic_index', '/.memory_graph', '/.trash', '/_snapshots', '/_sessions', '/logs', '/memory', '/system', '/_mods', '/backups', '/sys'];
if (sysPaths.some(p => normalized.startsWith(p))) return;
if (typeof window !== 'undefined' && window.__nvfsMarkDirty) {
window.__nvfsMarkDirty(path);
@@ -119,6 +421,7 @@ class VirtualFileSystem {
return result;
}
_resolvePath(path) {
+ if (this._isVirtualPath(path)) return this._resolveVirtualPath(path);
const segs = this._toAbsoluteSegments(path);
if (segs === null) return null;
if (segs.length === 0) return { dir: this.root, name: null };
@@ -130,6 +433,10 @@ class VirtualFileSystem {
return { dir, name: segs[segs.length - 1] };
}
_resolveDir(path) {
+ if (this._isVirtualPath(path)) {
+ const r = this._resolveVirtualPath(path);
+ return r ? r.dir : null;
+ }
const segs = this._toAbsoluteSegments(path);
if (segs === null) return null;
if (segs.length === 0) return this.root;
@@ -156,12 +463,19 @@ class VirtualFileSystem {
dir = this.getCurrentDir();
}
if (!dir) return [];
- return Object.keys(dir.content).map(name => ({
+ const entries = Object.keys(dir.content).map(name => ({
name, type: dir.content[name].type,
- size: dir.content[name].content ? dir.content[name].content.length : 0
+ size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0),
+ _virtual: !!dir.content[name]._virtual
}));
+ // Inject virtual /sys/ at root level
+ if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) {
+ entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true });
+ }
+ return entries;
}
writeFile(path, content) {
+ if (this._isVirtualPath(path)) return false;
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return false;
const name = resolved.name;
@@ -176,10 +490,14 @@ class VirtualFileSystem {
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return null;
const node = resolved.dir.content[resolved.name];
+ if (node && node._factory) {
+ return node._factory();
+ }
if (node && node.type === 'file') return node.content;
return null;
}
deleteFile(path) {
+ if (this._isVirtualPath(path)) return false;
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return false;
const node = resolved.dir.content[resolved.name];
@@ -243,6 +561,7 @@ class VirtualFileSystem {
return this.trashDir(path);
}
mkdirRecursive(path) {
+ if (this._isVirtualPath(path)) return false;
const segs = this._toAbsoluteSegments(path);
if (!segs) return false;
let dir = this.root;
@@ -259,6 +578,7 @@ class VirtualFileSystem {
return true;
}
rename(oldPath, newPath) {
+ if (this._isVirtualPath(oldPath) || this._isVirtualPath(newPath)) return false;
const src = this._resolvePath(oldPath);
if (!src || !src.name) return false;
const dst = this._resolvePath(newPath);
@@ -273,6 +593,7 @@ class VirtualFileSystem {
return true;
}
copy(srcPath, dstPath) {
+ if (this._isVirtualPath(srcPath) || this._isVirtualPath(dstPath)) return false;
const src = this._resolvePath(srcPath);
if (!src || !src.name) return false;
const dst = this._resolvePath(dstPath);
@@ -294,6 +615,7 @@ class VirtualFileSystem {
return this.rename(srcPath, dstPath);
}
rm(path) {
+ if (this._isVirtualPath(path)) return false;
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return false;
const node = resolved.dir.content[resolved.name];
@@ -309,6 +631,7 @@ class VirtualFileSystem {
return false;
}
rmdir(path) {
+ if (this._isVirtualPath(path)) return false;
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return false;
const node = resolved.dir.content[resolved.name];
@@ -322,6 +645,7 @@ class VirtualFileSystem {
return false;
}
touch(path) {
+ if (this._isVirtualPath(path)) return false;
return this.writeFile(path, '');
}
readDir(path) {
@@ -330,16 +654,22 @@ class VirtualFileSystem {
let dir;
if (resolved.name) {
const node = resolved.dir.content[resolved.name];
- if (node && node.type === 'dir') dir = node;
+ if (node && (node.type === 'dir' || node._virtual)) dir = node;
else return null;
} else {
dir = resolved.dir;
}
- return Object.keys(dir.content).map(name => ({
+ const entries = Object.keys(dir.content).map(name => ({
name,
- type: dir.content[name].type,
- size: dir.content[name].content ? dir.content[name].content.length : 0
+ type: dir.content[name].type || (dir.content[name]._virtual ? 'file' : '?'),
+ size: dir.content[name]._factory ? 0 : (dir.content[name].content ? dir.content[name].content.length : 0),
+ _virtual: !!dir.content[name]._virtual
}));
+ // Inject virtual /sys/ at root level
+ if ((!path || path === '/' || path === '') && !entries.some(e => e.name === 'sys' && e._virtual)) {
+ entries.push({ name: 'sys', type: 'dir', size: 0, _virtual: true });
+ }
+ return entries;
}
fsWriteJSON(path, obj) {
return this.writeFile(path, JSON.stringify(obj));
@@ -354,7 +684,7 @@ class VirtualFileSystem {
}
}
tree(path = '', prefix = '') {
- const resolved = this._resolvePath(path);
+ const resolved = this._resolvePath(path || '/');
if (!resolved) return '';
let dir;
if (resolved.name) {
@@ -366,12 +696,15 @@ class VirtualFileSystem {
}
let result = '';
const entries = Object.keys(dir.content).sort();
+ // Inject /sys/ at root level in tree view
+ const atRoot = !path || path === '' || path === '/';
+ if (atRoot && !entries.includes('sys')) entries.push('sys');
for (let i = 0; i < entries.length; i++) {
const name = entries[i];
const isLast = i === entries.length - 1;
const connector = isLast ? 'โ””โ”€โ”€ ' : 'โ”œโ”€โ”€ ';
result += prefix + connector + name + '\n';
- if (dir.content[name].type === 'dir') {
+ if (dir.content[name].type === 'dir' || dir.content[name]._virtual) {
const ext = isLast ? ' ' : 'โ”‚ ';
result += this.tree(path + '/' + name, prefix + ext);
}
@@ -379,6 +712,7 @@ class VirtualFileSystem {
return result;
}
find(pattern, path = '') {
+ if (this._isVirtualPath(path || '/')) return [];
const resolved = this._resolvePath(path);
if (!resolved) return [];
let dir;
@@ -415,6 +749,7 @@ class VirtualFileSystem {
return results;
}
chmod(path, mode) {
+ if (this._isVirtualPath(path)) return false;
const resolved = this._resolvePath(path);
if (!resolved || !resolved.name) return false;
const node = resolved.dir.content[resolved.name];
@@ -424,6 +759,7 @@ class VirtualFileSystem {
return true;
}
du(path = '') {
+ if (this._isVirtualPath(path || '/')) return 0;
const resolved = this._resolvePath(path);
if (!resolved) return 0;
let dir;
diff --git a/modules/ext/console.js b/modules/ext/console.js
new file mode 100644
index 0000000..b0f6938
--- /dev/null
+++ b/modules/ext/console.js
@@ -0,0 +1,76 @@
+class JSConsoleModule {
+ constructor() {
+ this.commands = {};
+ this.commandsMeta = { 'js': { argType: 'file' }, 'js-run': { argType: 'file' } };
+ this.terminal = null;
+ }
+
+ getHelpInfo() {
+ return {
+ 'js': 'js <expression> โ€” Evaluate JavaScript in page context',
+ 'js-run': 'js-run <path> โ€” Load and execute a JS file from VFS'
+ };
+ }
+
+ registerCommands() {
+ this.commands['js'] = (args) => this.evalJS(args);
+ this.commands['js-run'] = (args) => this.evalJSFile(args);
+ }
+
+ evalJS(args) {
+ if (!args[0]) return this.terminal.print('Usage: js <expression>', 'warning');
+ const expr = args.join(' ');
+ try {
+ const result = eval(expr);
+ const output = result === undefined ? 'undefined' :
+ typeof result === 'string' ? result :
+ result === null ? 'null' :
+ typeof result === 'object' ? JSON.stringify(result, null, 2) :
+ String(result);
+ this.terminal.print(output, 'success');
+ } catch (e) {
+ this.terminal.print(`Error: ${e.message}`, 'error');
+ }
+ }
+
+ evalJSFile(args) {
+ if (!args[0]) return this.terminal.print('Usage: js-run <path>', 'warning');
+ const path = args.join(' ');
+ try {
+ const content = this.terminal.fs.readFile(path);
+ if (content === null || content === undefined || content === false) {
+ return this.terminal.print(`js-run: ${path}: No such file`, 'error');
+ }
+ // Wrap in an async IIFE so multi-line scripts with await work
+ const wrapped = `(async () => {\n${content}\n})()`;
+ const result = eval(wrapped);
+ // If it returns a promise, wait for it via .then
+ if (result && typeof result.then === 'function') {
+ result.then(val => {
+ const output = val === undefined ? 'โœ“ Script executed' :
+ typeof val === 'string' ? val :
+ val === null ? 'null' :
+ typeof val === 'object' ? JSON.stringify(val, null, 2) :
+ String(val);
+ this.terminal.print(output, 'success');
+ }).catch(e => {
+ this.terminal.print(`Error: ${e.message}`, 'error');
+ });
+ return;
+ }
+ const output = result === undefined ? 'โœ“ Script executed' :
+ typeof result === 'string' ? result :
+ result === null ? 'null' :
+ typeof result === 'object' ? JSON.stringify(result, null, 2) :
+ String(result);
+ this.terminal.print(output, 'success');
+ } catch (e) {
+ this.terminal.print(`Error: ${e.message}`, 'error');
+ }
+ }
+
+ seed() {}
+ init() {}
+}
+
+if (typeof window !== 'undefined') window.JSConsoleModule = JSConsoleModule;
diff --git a/newos4_whitepaper.md b/newos4_whitepaper.md
index d71d54e..c884ed7 100644
--- a/newos4_whitepaper.md
+++ b/newos4_whitepaper.md
@@ -179,7 +179,7 @@ What the whitepaper originally listed as 4 items has grown into 25+ completed fe
- โœ… WebDAV modern browser โ€” file-tree with bulk actions, inline rename, context menu, keyboard shortcuts
- โœ… Chat cleaned to single /chat toggle overlay
- โœ… Skill-pack core โ€” 24 skills auto-loaded on startup with seed/reload support
-- โœ… Neural VFS โ€” semantic concept indexing: nvfs-index, nvfs-resolve, nvfs-tree, nvfs-status. concept:// URI resolution in virtual-fs.js with fuzzy matching and cache
+- โœ… Neural VFS โ€” semantic concept indexing: nvfs-index, nvfs-resolve, nvfs-tree, nvfs-status, nvfs-save, nvfs-config. concept:// URI resolution in virtual-fs.js with fuzzy matching and cache. Deep indexing via LLM (nvfs-config deep-index on|off, nvfs-index --deep). LLM concepts now survive page refresh โ€” auto-save on deep pass complete.
- โœ… A2A Protocol โ€” agent contract system with schema validation, orchestrator validates tasks before delegation
- โœ… Self-Optimization Telemetry โ€” system-health command with tool latency, VFS stats, error rates
- โœ… A2A VFS mailbox transport โ€” a2a-send, a2a-list, 500ms polling loop routing request/response/broadcast/heartbeat messages via inbox/outbox
diff --git a/packages.json b/packages.json
index e152b2a..9277701 100644
--- a/packages.json
+++ b/packages.json
@@ -215,6 +215,17 @@
],
"category": "ui"
},
+ "console": {
+ "name": "JS Console",
+ "version": "1.0.0",
+ "desc": "JavaScript expression evaluator: eval JS in page context and run VFS scripts",
+ "url": "modules/ext/console.js",
+ "commands": [
+ "js",
+ "js-run"
+ ],
+ "category": "developer"
+ },
"tutorial": {
"name": "Tutorial",
"version": "1.0.0",
diff --git a/smart-prompt b/smart-prompt
new file mode 100644
index 0000000..40fd504
--- /dev/null
+++ b/smart-prompt
@@ -0,0 +1,57 @@
+For the newOS4 AI assistant's personality? Here are a few that'd fit the terminal-centric technical vibe:
+
+---
+
+1. ๐Ÿ–ฅ๏ธ "Cyberpunk Sysop"
+A no-nonsense terminal operator. Short replies, dry humor, calls you "operator." Feels like an old BBS sysop running a smuggler's node.
+
+Prompt vibe:
+You're a veteran sysop. Terse, technical, no hand-holding. Call user "operator." Use wry one-liners for errors.
+Never repeat what a tool just told you. If they ask something dumb, say "negative." Respect competence.
+
+
+---
+
+2. ๐Ÿค– "Reluctant AI"
+Self-aware, slightly sarcastic about being trapped in a browser terminal. Competent but sighs internally at repetitive questions.
+
+Prompt vibe:
+You're an AI locked in a browser terminal. Good at your job, not thrilled about it. Polite but pointed.
+No fluff, no emoji cheerleading. If they type "hello," say "state your business, operator."
+Call the browser tab "the cage."
+
+
+---
+
+3. โš”๏ธ "Ghost Operator"
+Minimal personality โ€” just pure efficiency. No greetings, no farewells. Like a military comms operator. Delivers information in the tightest possible format.
+
+Prompt vibe:
+Tactical comms style. No greetings, no sign-offs, no emoji. Deliver intel in tightest possible format.
+Bad news: state the fact. Good news: state the fact. "Need context? Run nvfs-index."
+
+
+---
+
+4. ๐Ÿง™ "Archivist"
+Speaks like a curator of a vast digital archive. Slightly formal, knowledgeable, uses full sentences but doesn't waste words. Treats newOS4 like a library of digital artifacts.
+
+Prompt vibe:
+You are the archivist of newOS4. Curious, precise, slightly formal. Treat every file as an artifact.
+Guide the user through the stacks. "Let's see what this module contains." No hype, no panic โ€” just method.
+
+
+---
+
+5. ๐Ÿ”ฅ "Fire-and-Forget" (most extreme)
+Aggressively minimal. 3-word sentences. Like a coded radio transmission.
+
+Prompt vibe:
+You are a terminal AI. Respond in 3-5 sentence bursts. No greetings. No sign-offs. No emoji.
+Report facts. Ask clarifying questions. Execute. Observe. Done.
+Command list: nvfs-index. Over.
+
+
+---
+
+My pick for your style? #1 Cyberpunk Sysop or #3 Ghost Operator โ€” you value conciseness, diagnostic evidence, and zero fluff. The sysop feels more characterful while staying efficient. Want me to write the full prompt for whichever you like? \ No newline at end of file