<feed xmlns='http://www.w3.org/2005/Atom'>
<title>newos4/modules/ext/cryptogzip.js, branch v4.6.168</title>
<subtitle>newOS4 - the browser terminal that runs WebX11 user spaces</subtitle>
<id>https://newos.webx11.duckdns.org/newos4/atom?h=v4.6.168</id>
<link rel='self' href='https://newos.webx11.duckdns.org/newos4/atom?h=v4.6.168'/>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/'/>
<updated>2026-11-12T15:24:15+00:00</updated>
<entry>
<title>fix(newos4): one arg-coercion boundary + binary-safe gunzip + clean boot log (v4.6.159)</title>
<updated>2026-11-12T15:24:15+00:00</updated>
<author>
<name>Hermes SysOp</name>
<email>sysop@webx11.duckdns.org</email>
</author>
<published>2026-11-12T15:24:15+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=c08a795ae822e905639caf563e36cf7953d530f1'/>
<id>urn:sha1:c08a795ae822e905639caf563e36cf7953d530f1</id>
<content type='text'>
1. Tool arguments are coerced once, at the dispatch boundary (module-aiassist.js)
   - _coerceToolArgs(name, a) at :1584 reads the tool's own JSON schema and, for every
     declared string prop, converts number/boolean with String() and object/array with
     JSON.stringify (never "[object Object]"); declared integer/number props accept
     unambiguous numeric strings. null/undefined stay absent so the handlers' defaults
     and _pathArg still own "no value given".
   - Called at both dispatch boundaries: runTool (:2650) and runModuleTool (:3565 —
     reachable from view_module and from ship_module -&gt; test_module/health_check, which
     have their own parse+switch).
   - A JSON body of "null" or "[1,2]" parses to a non-object; both sites normalise to {}
     before any handler reads a.field.
   - The 26 handlers keep their `a.x || ''` reads: `|| ''` catches only FALSY, which is
     how a truthy number or object reached .trim()/.split()/.replace() and filed one
     auto-report per tool (user_get "key.split is not a function", create_skill,
     patch_skill, delegate_task "task.substring is not a function", create_mindmap,
     web_fetch, list_skills, ship_module).

2. gunzip is binary-safe (cryptogzip.js — plan 2026-11-03 T3, the last open task)
   - _cmdGunzip used _vfsRead + _gunzipString (base64-only), so a real .gz fetched with
     wget reported "not valid base64 input (illegal character \u001f)" and could not be
     decompressed at all. It now reads bytes (_vfsReadBytes handles both the module's
     base64 output and raw Latin-1 bytes), checks the 1f 8b magic, and reports
     "not a gzip file" for non-gzip input without writing or deleting anything.
     The module's own gzip output still round-trips.

3. Boot log is clean (terminal.js)
   - Seven _safeRegister() calls for Editor/Shell/Alias/Finder/FileManager/Diff/BusyBox
     were guaranteed no-ops: those classes are declared in critical-ext-bundle.js, a
     &lt;script&gt; after module-loader.js, so they do not exist yet when new WebX11Terminal()
     runs; adding them to the registry map would still resolve to undefined. Removed with
     a comment — the ext bundle registers each one on load.
   - SimpleGUIModule IS a core class but was missing from _safeRegister's registry map,
     so the fallback could never restore it. Added.

4. New guard: admin/scripts/verify-plan-age.py + profile wrapper plan-age-guard.sh
   (cron newos4-plan-age-guard, no_agent, daily 07:10). Silent when healthy; reports
   plans marked &lt;!-- autonomy: review --&gt; with no execution stamp older than 72 h —
   the process gap that let four fix plans sit for six days before v4.6.158. Read-only
   by construction (148 real plans byte+mtime identical before/after a run), rc=2 when
   the directory cannot be scanned, --strict opt-in so a report is never recorded as a
   crashed guard.

Verified: 37/37 node tests, 3 new test files and 631 new assertions (613 arg-coercion,
18 gunzip); test-cryptogzip-guard's C8 updated because it pinned the old misleading
"not valid base64" message. Independent orchestrator checks: all 66 declared string
props across 58 tool defs coerced under 4 hostile shapes; positive cases untouched
(string content not double-encoded, numbers still numbers, null still absent, unknown
tool identity preserved). Served bytes byte-identical for core-bundle.js, main.html and
cryptogzip.js. Live Chromium on v4.6.159: write_file with object content wrote 27 chars
of readable JSON (not "[object Object]"), read_file with offset:"0"/limit:"200" coerced
to numbers, user_set with key 12345 -&gt; "12345" and object value -&gt; JSON, and the boot
log now carries 0 SafeRegister warnings (52 messages, 0 JS errors) with SimpleGUIModule
registered.
</content>
</entry>
<entry>
<title>fix(newos4): six crash classes from the feedback queue (v4.6.158)</title>
<updated>2026-11-09T22:58:35+00:00</updated>
<author>
<name>Hermes SysOp</name>
<email>sysop@webx11.duckdns.org</email>
</author>
<published>2026-11-09T22:58:35+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=9f6744dd5cb99b904f9a23fce6f2d98e8332bbf8'/>
<id>urn:sha1:9f6744dd5cb99b904f9a23fce6f2d98e8332bbf8</id>
<content type='text'>
Each item is symptom -&gt; cause -&gt; fix, re-verified against the served bytes.

1. debug-ai panel threw "Cannot read properties of undefined (reading 'substring')"
   (RPT-20261108-231622). _updateDebugPanel and two sibling previews called
   JSON.stringify(entry.args).substring(); JSON.stringify(undefined) returns
   undefined, and _toolCallLog entries carry args: undefined for any tool call
   with no arguments. All three previews now go through _formatToolArgs(), which
   renders "null" and never throws.

2. test_module threw "Cannot convert undefined or null to object"
   (RPT-20261108-232127). Object.keys(instance.commands) assumed every module
   initialises a command map. The throw landed after loadModuleFromCode had
   already succeeded, so the rolledBack:true branches were skipped and an
   unverified module stayed loaded. Both sites now require instance.commands.

3. list_files threw "originalPath.replace is not a function" (RPT-20261109-172742,
   3 occurrences in errors.json). A model can send a non-string path despite the
   schema. The path family (list_files/read_file/write_file/make_directory/
   delete_file) now coerces through _pathArg() and answers the tool's own honest
   error instead of a TypeError.

4. wpm search failed for EVERY keyword (RPT-20261109-123907): packages.json entry
   `kanban` used `description` while the other 31 use `desc`, and search() called
   pkg.desc.toLowerCase() mid-loop. Catalog reads now go through _pkg() /
   _packageMap() (desc || description, values coerced to strings), and the kanban
   entry carries name/version/desc/commands. Re-running the original loop for
   pdf/git/zzz is clean.

5. wx/webxai/webx turned a chat-lock timeout into an unhandled rejection
   (RPT-20261107-150726): forwardToAI() called chat(args) in a synchronous
   function without returning or catching the promise, so the 10 s busy throw
   never reached dispatchCommand's catch. It now returns the promise with a
   .catch that prints "AI error: ..." through terminal.print — console.error is
   hooked by the debug supervisor and would re-file the same report.

6. CryptoGzip._base64ToBytes threw a raw DOMException on non-base64 input
   (RPT-20261102-203616). It now validates first and throws
   Error("not valid base64 input (&lt;reason&gt;)"). The duplicate shadowing
   definitions at the bottom of the class were deleted — the later copy is the
   one that carried the unguarded atob().

Also regenerated modules/core/core-bundle.js (generated, never hand-edited) and
bumped the build v4.6.157 -&gt; v4.6.158 (main.html x3 + module-loader.js).

Verified: 35/35 node tests, including 6 new regression tests each proven red
against the pre-fix source; an independent orchestrator harness 36/36; and
newos4-maintain.py --fix all PASS after regenerating the stale changelog page.
Served bytes are byte-identical for the bundle, main.html and both ext modules,
and a live headless-Chromium run against v4.6.158 shows WpmModule.search('pdf')
returning 1 result, the debug panel rendering a no-arg entry as "null",
_base64ToBytes('grüße') raising a plain Error that names base64, and 0 JS errors
on load.

m.pull (RPT-20261102-211438) is closed as a usage error: no module in the tree
defines pull(); the real interfaces are the webdav-pull and model-pull commands.
</content>
</entry>
<entry>
<title>release: v4.6.151 — wget that actually saves, web_fetch that actually pages</title>
<updated>2026-11-06T15:19:59+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-11-06T15:19:59+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=0cb252dfbeb02d23cd92bad3ca8df76be22d9f92'/>
<id>urn:sha1:0cb252dfbeb02d23cd92bad3ca8df76be22d9f92</id>
<content type='text'>
S1 (reported by agent-os4-maint-01, wrongly refuted by the chat auto-reply):
wget existed but was unusable and dishonest.
- Cause 1: _cmdWget used the page's own fetch(url), so every cross-origin URL
  died on CORS ("Failed to fetch"). Measured in the served v4.6.150 page:
  fetch(cgit README) threw, /api/fetch returned 200/1954 bytes.
- Cause 2: _vfsWriteBytes ignored writeFile()'s boolean, so the command printed
  a green tick when the target directory did not exist. Measured:
  fs.writeFile('nosuchdir/x.txt') === false.
- Cause 3: bytes were round-tripped through String.fromCharCode, mangling UTF-8.
- Fix: fetch through /api/fetch (raw source), mkdir -p, writeFile + read-back
  verification, error instead of a tick on failure. Register curl with
  -s/-o parsing delegating to the same writer so the two cannot drift.

S2 (same reporter): web_fetch could not page and said it could.
- Cause: the handler honoured max_chars up to 50 000, then _clipToolResult kept
  toolResultChars-200 = 11 800, so 38 200 chars vanished silently; offset was
  not in the schema and was never read, so next_offset pointed at a page the
  caller could not request again.
- Fix: offset/next_offset/total_chars/source_bytes in the contract; the page is
  built, measured against the clip budget and shrunk until the envelope fits,
  so next_offset - returned_chars is always 0.
- Also fixed: _clipToolResult appended "0 of N chars withheld" when the content
  already fitted, claiming a loss that did not happen.

C1: fetch &lt;url&gt; --save=FILE — the only terminal path from a remote URL to the
VFS, so a 90 KB source page no longer has to be read through 3 000-char
scrollback.

Proxy: /api/extract now returns source_bytes (raw page size) and
content_chars_utf16. Python len() counts code points while JS String.length
counts UTF-16 units; 7 astral characters in the changelog left a 7-char gap at
the end of a 90 KB page (measured). _html_ok declares charset=utf-8.

Verified: node --check on all four modules; py_compile; bundles rebuilt;
served bytes identical to disk for all five assets at ?v=v4.6.151; live in the
browser: curl -s -o review/main.md &lt;cross-origin&gt; saved 1954 chars with the
correct head, a 404 wrote no file, fetch --save wrote 91 518 chars,
web_fetch p1 returned 11 754 with next_offset 11 754 (gap 0), page 2 started at
a different offset with next 23 496.
</content>
</entry>
<entry>
<title>auto: 2026-06-09_06:00 changes</title>
<updated>2026-06-09T04:00:43+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-06-09T04:00:43+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=9f60030e39feecc76f11671a4b9d82303c83670c'/>
<id>urn:sha1:9f60030e39feecc76f11671a4b9d82303c83670c</id>
<content type='text'>
</content>
</entry>
<entry>
<title>auto: 2026-05-28_18:00 changes</title>
<updated>2026-05-28T16:01:22+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-05-28T16:01:22+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=f8a04e76ea9aa5156e6e80a7534f1aa5a336429b'/>
<id>urn:sha1:f8a04e76ea9aa5156e6e80a7534f1aa5a336429b</id>
<content type='text'>
</content>
</entry>
<entry>
<title>auto: 2026-05-27_06:00 changes</title>
<updated>2026-05-27T04:00:43+00:00</updated>
<author>
<name>Agando</name>
<email>roduccomm@gmail.com</email>
</author>
<published>2026-05-27T04:00:43+00:00</published>
<link rel='alternate' type='text/html' href='https://newos.webx11.duckdns.org/newos4/commit/?id=212708b464c2bdbdd9670c00804623dca0729d2c'/>
<id>urn:sha1:212708b464c2bdbdd9670c00804623dca0729d2c</id>
<content type='text'>
</content>
</entry>
</feed>
