diff options
| author | Agando <roduccomm@gmail.com> | 2026-10-27 15:23:38 +0100 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-10-27 15:23:38 +0100 |
| commit | 6b4049d3ba27dd68935c0565cccfe44752175393 (patch) | |
| tree | a336032a1c463dddb53c06e20fd7cefb12f721b7 /modules/core/core-bundle.js | |
| parent | 6f2255946a834f2458613450533cd2906eb31e7d (diff) | |
fix(newos4): help names the owner of an undocumented command; loop lessons leave /skills
Two board items plus the context-window close-out.
- MAINT-019 (module-system.js): `help <cmd>` answered "No help available for X" for a command
that is registered but has no help line, which reads as if the command does not exist. It now
says "X — registered by <Module>, which publishes no help line for it. Try: help". A command
that genuinely does not exist still gets the old message.
- MAINT-020 (module-aiassist.js): auto-generated loop lessons were written into /skills with
their own index entries, so `skill list` filled up with system noise. Lessons now live in
/lessons with /lessons/index.json; the reader still falls back to legacy loop-lesson-* entries
in /skills for one release, and the one-time migration deletes them from the skills index.
Verification: test-help-and-lessons.js (10 asserts, includes a lint-style guard that no lesson is
written into /skills). Suite 14/14 files green; maintain --fix "node tests 14/14 pass",
"help coverage 362/362". Live on v4.6.108: `help agent-register` -> "registered by
AgentRegistryModule, which publishes no help line for it"; `help a2a-prune` unchanged;
`help nope-not-real` still "No help available".
MAINT-001 close-out (no code change here): history is capped at 20 messages with a "[context trim]"
marker (first trim at turn 11 in a 60-turn probe), token compression took 80 msgs/8080 tok to
17/2625, and since v4.6.107 one turn's tool bodies are clipped to 6 000 chars each and elided past
24 000 with the newest 4 kept verbatim. Residual, recorded on the board: maxContextTokens is never
sent to the server as num_ctx, so the real window is whatever the endpoint defaults to.
MAINT-018 close-out: not reproducible. Live probes on v4.6.107/108 — `sed -n '1,3p' /probe.txt`
prints lines 1-3, `sed -n "2,4p"` prints 2-4, `grep 'line4'` matches, and the same commands inside
`script /q.sh` print correctly. The "Command not found: for;" symptom belongs to the line-by-line
script runner, which is MAINT-021 and stays open.
Diffstat (limited to 'modules/core/core-bundle.js')
| -rw-r--r-- | modules/core/core-bundle.js | 53 |
1 files changed, 36 insertions, 17 deletions
diff --git a/modules/core/core-bundle.js b/modules/core/core-bundle.js index c06af5f..d7d9d61 100644 --- a/modules/core/core-bundle.js +++ b/modules/core/core-bundle.js @@ -6431,7 +6431,11 @@ Only output XML function calls for tool tasks. If no tool is needed, answer norm if (helpEntry) { this.terminal.print(helpEntry, 'info'); } else { - this.terminal.print(`No help available for ${cmd}`, 'warning'); + // MAINT-019: a registered command with no help line answered "No help + // available", which reads as if the command does not exist. Name the + // module that owns it so the user knows where to look. + const owner = allCommands[cmd]; + this.terminal.print(`${cmd} — registered by ${owner}, which publishes no help line for it. Try: help`, 'warning'); } } else { this.terminal.print(`No help available for ${cmd}`, 'warning'); @@ -10045,13 +10049,21 @@ buildSystemPrompt() { try { var fs = this.terminal.fs; if (fs) { - var index = fs.fsReadJSON('/skills/index.json') || {}; - var lessonKeys = Object.keys(index).filter(function(k) { return k.indexOf('loop-lesson-') === 0; }); + // MAINT-020: lessons live in /lessons, not in /skills — writing them into + // the skills directory made auto-generated lessons show up as skills. + var index = fs.fsReadJSON('/lessons/index.json') || {}; + var lessonKeys = Object.keys(index); + if (lessonKeys.length === 0) { + // one-release fallback for lessons written by older builds + var legacy = fs.fsReadJSON('/skills/index.json') || {}; + lessonKeys = Object.keys(legacy).filter(function(k) { return k.indexOf('loop-lesson-') === 0; }); + } if (lessonKeys.length > 0) { var recentLessons = lessonKeys.sort().slice(-3); var lessonTexts = []; for (var li = 0; li < recentLessons.length; li++) { - var lc = fs.readFile('/skills/' + recentLessons[li] + '.md'); + var lc = fs.readFile('/lessons/' + recentLessons[li] + '.md') + || fs.readFile('/skills/' + recentLessons[li] + '.md'); if (lc) { var excerpt = lc.substring(0, 300); lessonTexts.push('[Lesson: ' + recentLessons[li] + '] ' + excerpt); @@ -13187,23 +13199,30 @@ buildSystemPrompt() { var suggestion = suggestions.join('\n'); try { var fs = this.terminal.fs; - var index = fs.fsReadJSON('/skills/index.json') || {}; - var autoSkills = Object.entries(index).filter(function(e) { return (e[1].tags || []).indexOf('auto-improvement') !== -1; }); - // One-time cleanup: remove all existing loop-lesson files on first run + // MAINT-020: lessons are kept in /lessons with their own index; /skills is + // reserved for real skills the user or the agent creates deliberately. + try { fs.mkdirRecursive('/lessons'); } catch (ex) {} + var index = fs.fsReadJSON('/lessons/index.json') || {}; + var autoSkills = Object.entries(index); + // One-time migration: earlier builds wrote loop-lesson-* into /skills. if (!this._loopLessonCleaned) { this._loopLessonCleaned = true; - autoSkills.forEach(function(e) { - try { fs.unlink('/skills/' + e[0] + '.md'); } catch (ex) {} - delete index[e[0]]; - }); - autoSkills = []; + try { + var sIdx = fs.fsReadJSON('/skills/index.json') || {}; + Object.keys(sIdx).filter(function(k) { return k.indexOf('loop-lesson-') === 0; }) + .forEach(function(k) { + try { fs.unlink('/skills/' + k + '.md'); } catch (ex) {} + delete sIdx[k]; + }); + fs.fsWriteJSON('/skills/index.json', sIdx); + } catch (ex) {} } // Age limit: remove lessons older than 24h var now = Date.now(); autoSkills = autoSkills.filter(function(e) { var created = new Date(e[1].created).getTime(); if (now - created > 86400000) { - try { fs.unlink('/skills/' + e[0] + '.md'); } catch (ex) {} + try { fs.unlink('/lessons/' + e[0] + '.md'); } catch (ex) {} delete index[e[0]]; return false; } @@ -13215,17 +13234,17 @@ buildSystemPrompt() { // FIFO rotation: if at cap, delete oldest before creating if (!isDuplicate && autoSkills.length >= 3) { var oldest = autoSkills[0]; - try { fs.unlink('/skills/' + oldest[0] + '.md'); } catch (ex) {} + try { fs.unlink('/lessons/' + oldest[0] + '.md'); } catch (ex) {} delete index[oldest[0]]; autoSkills.shift(); } if (!isDuplicate) { var skillName = 'loop-lesson-' + Date.now(); var content = '---\nname: ' + skillName + '\ndescription: Auto-saved from agent loop anomaly detection\ntags: [auto-improvement, agent-loop, auto]\ncategory: system\n---\n## Anomalies Detected\n\n' + JSON.stringify(anomalies, null, 2) + '\n\n## Suggestions\n\n' + suggestion + '\n\n## Context\n- Profile: ' + (this._activeLoopProfile ? this._activeLoopProfile.name : 'none') + '\n- Turns: ' + result.toolTurns + '\n- Tools used: ' + toolLog.map(function(t) { return t.name; }).join(', '); - fs.mkdirRecursive('/skills'); - fs.writeFile('/skills/' + skillName + '.md', content); + fs.mkdirRecursive('/lessons'); + fs.writeFile('/lessons/' + skillName + '.md', content); index[skillName] = { name: skillName, description: 'Auto-saved loop lesson', tags: ['auto-improvement', 'agent-loop', 'auto'], category: 'system', created: new Date().toISOString(), updated: new Date().toISOString(), anomalyTypes: anomalyTypes }; - fs.fsWriteJSON('/skills/index.json', index); + fs.fsWriteJSON('/lessons/index.json', index); } } catch (e) { /* VFS not available */ } var msg = '💡 Self-improvement: ' + anomalies.length + ' anomaly(ies)'; |