diff options
| author | Agando <roduccomm@gmail.com> | 2026-11-02 21:21:24 +0100 |
|---|---|---|
| committer | Agando <roduccomm@gmail.com> | 2026-11-02 21:21:24 +0100 |
| commit | 4587c757b17d8a92df7c98ec37ace4c5500a4f5a (patch) | |
| tree | 4b89cdf25fb7f2354cc8299260b1ddafeb568f25 /modules/test/test-tool-context-bound.js | |
| parent | 08a77451f503f531eee44c69c14bed3a2961cc5c (diff) | |
fix(newos4): read_file pages were silently truncated; delegation no longer invents "completed"
Two defects found by re-deriving a report that claimed both were still open.
read_file paging (module-aiassist.js)
- A page larger than the result clip was clipped AFTER read_file had computed
next_offset. On the last page next_offset is null, so the model was told
truncated:false while its tail had been removed: measured 11 890 of 19 999
chars delivered, returned:19999, no pointer to the rest - 8 109 chars lost
with no way to ask for them.
* the clip now sets truncated=true and next_offset=offset+keep when it removes
the tail of a page that had no continuation (and never splits a surrogate pair)
* read_file caps a page at the result budget (min(pageCap, toolResultChars-200)),
so a page is always delivered whole and next_offset is exactly contiguous
Live v4.6.143: 19 999-char file -> page 11 800 (next_offset 11 800), page 2
8 199 (next_offset null): 11 800 + 8 199 = 19 999, nothing withheld. A model
asking for limit:32000 is clamped to 11 800 and told truncated:true.
The ~500-char symptom was the 500-char floor: a caller passing limit:200 gets
500, which is why targeted sed -n reads looked better than the tool.
Delegation reporting (module-orchestrator.js, module-aiassist.js)
- chat() returning {content} was already fixed in 8a9333b, but three other places
still substituted the word "completed" for an absent answer, so the symptom
survived: delegateTask's outbox wrote status:'completed' even when the worker
returned nothing; the delegate_task tool returned {success:true, result:"completed"};
orchestrate-parallel summarised an unmatched agent as {status:'ok',
result:'completed (agent recycled)'} and a missing outbox as "completed (no outbox)".
- Now: an empty/undefined answer is status 'failed' with error 'agent produced no
answer' / 'no result in outbox' / 'agent not found — result unverifiable', and
the tool returns success:false with result:null.
Tests: test-delegation-reporting.js 6 (RED 3/6 against HEAD: D3/D4 success:true,
D5 the literal "completed"), test-tool-context-bound 26 with E6-E8 (RED 24/2:
truncated:false, next_offset null). maintain 27/27.
Diffstat (limited to 'modules/test/test-tool-context-bound.js')
| -rw-r--r-- | modules/test/test-tool-context-bound.js | 17 |
1 files changed, 17 insertions, 0 deletions
diff --git a/modules/test/test-tool-context-bound.js b/modules/test/test-tool-context-bound.js index 0b42eeb..848b307 100644 --- a/modules/test/test-tool-context-bound.js +++ b/modules/test/test-tool-context-bound.js @@ -103,6 +103,23 @@ function ok(name, cond, detail) { ok('E4 clip shortens content', parsed && parsed.content.length < 16000 && parsed.content.length >= 3000, parsed && String(parsed.content.length)); ok('E5 clipped envelope fits the limit', clipped.length <= 6000 + 400, String(clipped.length)); + + // E6-E8: the last page of a file has next_offset=null. If the clip removes its tail, + // telling the caller "truncated: false" hides the remainder with no way to ask for it + // (measured 2026-11-02: 8 109 of 19 999 chars withheld, returned claimed 19 999). + const lastPage = JSON.stringify({ + path: '/big.txt', offset: 0, returned: 19999, total_chars: 19999, + truncated: false, next_offset: null, + content: 'y'.repeat(19999), + }); + const lc = ai._clipToolResult(lastPage); + let lp = null; + try { lp = JSON.parse(lc); } catch (e) {} + ok('E6 a clipped last page reports truncated=true', lp && lp.truncated === true, lp && String(lp.truncated)); + ok('E7 it gives a resumable next_offset', lp && lp.next_offset === lp.returned_chars, + lp && String(lp.next_offset) + ' vs ' + String(lp.returned_chars)); + ok('E8 returned no longer claims the whole file', lp && lp.returned === lp.returned_chars, + lp && String(lp.returned)); } // F. budgets are resolvable per model class, with precedence call arg > profile > ai-config > default |