Harness spec 3.0.0-draft: context hygiene, at insert time only
context.md and loop.json `context`: every result has an id, a read carries its lines and content hash, an unchanged re-read (or a range inside one) is a stub, large output keeps head and tail with the elided line, recall shows any stored result, edits answer with their diff, a repeated listing or search with the same result is a stub, and compaction adds a table of reads and empties the dedupe table. Nothing already sent is ever rewritten. Conformance area context. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
3 files changed
+588
No files matched your search
@@ -0,0 +1,403 @@
|
||||
{
|
||||
"about": "Context hygiene (context.md, loop.json `context`): what is inserted into the conversation for each step of a session, in order. Steps: `read` (`path`, the file's whole `content`, `offset`, `limit` — the read tool's body: `<n>: ` lines, the note when lines remain), `edit` / `write` (`path`, `before` — absent for a new file — and `after`), `output` (any other tool's result: `tool`, `args`, `text`), `recall` (`target`, `offset`, `limit`) and `compact` (the table added after a summary; the dedupe table is emptied). Each step but `compact` carries the `id` the session gives its result. `limits` overrides loop.json's `cap` for the case. The answer is the text each step inserts: a read headed by path, lines, length and the first 8 hex digits of the content's SHA-256, a stub for an unchanged re-read inside an earlier full read, a diff for an edit, a capped output with its elided line, a stub for a repeated listing or search with the same result. LLeMbas: services/context.py (to be written); CLI: session/context.ts (to be written).",
|
||||
"cases": [
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#a001",
|
||||
"path": "src/a.ts",
|
||||
"content": "const a = 1\nexport { a }\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#a002",
|
||||
"path": "src/a.ts",
|
||||
"content": "const a = 1\nexport { a }\n"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#a001 src/a.ts lines 1-2 of 2 sha 583e86b4]\n1: const a = 1\n2: export { a }",
|
||||
"[#a002 src/a.ts lines 1-2 of 2 sha 583e86b4] Unchanged since #a001, which already shows lines 1-2."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#b001",
|
||||
"path": "notes.md",
|
||||
"content": "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n",
|
||||
"limit": 5
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#b002",
|
||||
"path": "notes.md",
|
||||
"content": "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n",
|
||||
"offset": 2,
|
||||
"limit": 3
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#b003",
|
||||
"path": "notes.md",
|
||||
"content": "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n",
|
||||
"offset": 4,
|
||||
"limit": 5
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#b004",
|
||||
"path": "notes.md",
|
||||
"content": "line 1\nline 2\nline 3\nline 4\nline 5\nline 6\nline 7\nline 8\nline 9\nline 10\n",
|
||||
"offset": 5,
|
||||
"limit": 2
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#b001 notes.md lines 1-5 of 10 sha e71d970d]\n1: line 1\n2: line 2\n3: line 3\n4: line 4\n5: line 5\n\n(5 more lines — read again with offset 6)",
|
||||
"[#b002 notes.md lines 2-4 of 10 sha e71d970d] Unchanged since #b001, which already shows lines 1-5.",
|
||||
"[#b003 notes.md lines 4-8 of 10 sha e71d970d]\n4: line 4\n5: line 5\n6: line 6\n7: line 7\n8: line 8\n\n(2 more lines — read again with offset 9)",
|
||||
"[#b004 notes.md lines 5-6 of 10 sha e71d970d] Unchanged since #b003, which already shows lines 4-8."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#c001",
|
||||
"path": "a.py",
|
||||
"content": "x = 1\ny = 2\nz = 3\n"
|
||||
},
|
||||
{
|
||||
"op": "edit",
|
||||
"id": "#c002",
|
||||
"path": "a.py",
|
||||
"before": "x = 1\ny = 2\nz = 3\n",
|
||||
"after": "x = 1\ny = 20\nz = 3\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#c003",
|
||||
"path": "a.py",
|
||||
"content": "x = 1\ny = 20\nz = 3\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#c004",
|
||||
"path": "a.py",
|
||||
"content": "x = 1\ny = 20\nz = 3\n"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#c001 a.py lines 1-3 of 3 sha af198c55]\n1: x = 1\n2: y = 2\n3: z = 3",
|
||||
"[#c002 a.py sha a6a8e52b]\n@@ -1,3 +1,3 @@\n x = 1\n-y = 2\n+y = 20\n z = 3",
|
||||
"[#c003 a.py lines 1-3 of 3 sha a6a8e52b]\n1: x = 1\n2: y = 20\n3: z = 3",
|
||||
"[#c004 a.py lines 1-3 of 3 sha a6a8e52b] Unchanged since #c003, which already shows lines 1-3."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#d001",
|
||||
"path": "a.txt",
|
||||
"content": "one\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#d002",
|
||||
"path": "a.txt",
|
||||
"content": "one\ntwo\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#d003",
|
||||
"path": "b.txt",
|
||||
"content": "one\n"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#d001 a.txt lines 1-1 of 1 sha 2c8b08da]\n1: one",
|
||||
"[#d002 a.txt lines 1-2 of 2 sha c3f9c8c2]\n1: one\n2: two",
|
||||
"[#d003 b.txt lines 1-1 of 1 sha 2c8b08da]\n1: one"
|
||||
]
|
||||
},
|
||||
{
|
||||
"limits": {
|
||||
"max_lines": 6,
|
||||
"max_chars": 40000,
|
||||
"head_lines": 2,
|
||||
"head_chars": 20000,
|
||||
"tail_lines": 2,
|
||||
"tail_chars": 10000
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#e001",
|
||||
"tool": "bash",
|
||||
"args": {
|
||||
"command": "seq 10"
|
||||
},
|
||||
"text": "1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
|
||||
},
|
||||
{
|
||||
"op": "recall",
|
||||
"id": "#e002",
|
||||
"target": "#e001",
|
||||
"offset": 3,
|
||||
"limit": 2
|
||||
},
|
||||
{
|
||||
"op": "recall",
|
||||
"id": "#e003",
|
||||
"target": "e001"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#e001]\n1\n2\n[6 lines elided, full output #e001]\n9\n10",
|
||||
"[#e002 recall #e001 lines 3-4 of 10]\n3\n4",
|
||||
"[#e003 recall #e001 lines 1-10 of 10]\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10"
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#f001",
|
||||
"tool": "list",
|
||||
"args": {
|
||||
"path": "src"
|
||||
},
|
||||
"text": "src/\n a.ts\n b.ts"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#f002",
|
||||
"tool": "list",
|
||||
"args": {
|
||||
"path": "src"
|
||||
},
|
||||
"text": "src/\n a.ts\n b.ts"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#f003",
|
||||
"tool": "list",
|
||||
"args": {
|
||||
"path": "src",
|
||||
"depth": 1
|
||||
},
|
||||
"text": "src/\n a.ts\n b.ts"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#f004",
|
||||
"tool": "list",
|
||||
"args": {
|
||||
"path": "src"
|
||||
},
|
||||
"text": "src/\n a.ts\n b.ts\n c.ts"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#f005",
|
||||
"tool": "list",
|
||||
"args": {
|
||||
"path": "src"
|
||||
},
|
||||
"text": "src/\n a.ts\n b.ts\n c.ts"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#f001]\nsrc/\n a.ts\n b.ts",
|
||||
"[#f002] Same result as #f001.",
|
||||
"[#f003]\nsrc/\n a.ts\n b.ts",
|
||||
"[#f004]\nsrc/\n a.ts\n b.ts\n c.ts",
|
||||
"[#f005] Same result as #f004."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#g001",
|
||||
"tool": "bash",
|
||||
"args": {
|
||||
"command": "git status"
|
||||
},
|
||||
"text": "nothing to commit"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#g002",
|
||||
"tool": "bash",
|
||||
"args": {
|
||||
"command": "git status"
|
||||
},
|
||||
"text": "nothing to commit"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#g003",
|
||||
"tool": "grep",
|
||||
"args": {
|
||||
"pattern": "TODO"
|
||||
},
|
||||
"text": "a.ts:3: // TODO"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#g004",
|
||||
"tool": "grep",
|
||||
"args": {
|
||||
"pattern": "TODO"
|
||||
},
|
||||
"text": "a.ts:3: // TODO"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#g001]\nnothing to commit",
|
||||
"[#g002]\nnothing to commit",
|
||||
"[#g003]\na.ts:3: // TODO",
|
||||
"[#g004] Same result as #g003."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#h001",
|
||||
"path": "a.md",
|
||||
"content": "alpha\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#h002",
|
||||
"path": "b.md",
|
||||
"content": "beta\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#h003",
|
||||
"path": "a.md",
|
||||
"content": "alpha\n"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#h004",
|
||||
"tool": "glob",
|
||||
"args": {
|
||||
"pattern": "*.md"
|
||||
},
|
||||
"text": "a.md\nb.md"
|
||||
},
|
||||
{
|
||||
"op": "compact"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#h005",
|
||||
"path": "a.md",
|
||||
"content": "alpha\n"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#h006",
|
||||
"tool": "glob",
|
||||
"args": {
|
||||
"pattern": "*.md"
|
||||
},
|
||||
"text": "a.md\nb.md"
|
||||
},
|
||||
{
|
||||
"op": "recall",
|
||||
"id": "#h007",
|
||||
"target": "#h002"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#h001 a.md lines 1-1 of 1 sha b6a98d9c]\n1: alpha",
|
||||
"[#h002 b.md lines 1-1 of 1 sha f2c82dec]\n1: beta",
|
||||
"[#h003 a.md lines 1-1 of 1 sha b6a98d9c] Unchanged since #h001, which already shows lines 1-1.",
|
||||
"[#h004]\na.md\nb.md",
|
||||
"## Files read before this summary\n\nRead a file again for its content; recall shows a result by its id.\n\n| Path | Lines | sha | Result |\n|---|---|---|---|\n| a.md | 1-1 of 1 | b6a98d9c | #h001 |\n| b.md | 1-1 of 1 | f2c82dec | #h002 |",
|
||||
"[#h005 a.md lines 1-1 of 1 sha b6a98d9c]\n1: alpha",
|
||||
"[#h006]\na.md\nb.md",
|
||||
"[#h007 recall #h002 lines 1-1 of 1]\n1: beta"
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "compact"
|
||||
},
|
||||
{
|
||||
"op": "recall",
|
||||
"id": "#i001",
|
||||
"target": "#ffff"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"",
|
||||
"[#i001] No stored result #ffff in this session."
|
||||
]
|
||||
},
|
||||
{
|
||||
"steps": [
|
||||
{
|
||||
"op": "write",
|
||||
"id": "#j001",
|
||||
"path": "new.md",
|
||||
"after": "# Title\n\nText.\n"
|
||||
},
|
||||
{
|
||||
"op": "read",
|
||||
"id": "#j002",
|
||||
"path": "empty.txt",
|
||||
"content": ""
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#j001 new.md sha fd9dba9b] Created, 3 lines.",
|
||||
"[#j002 empty.txt empty sha e3b0c442]\n(empty file)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"limits": {
|
||||
"max_lines": 400,
|
||||
"max_chars": 50,
|
||||
"head_lines": 200,
|
||||
"head_chars": 20,
|
||||
"tail_lines": 100,
|
||||
"tail_chars": 10
|
||||
},
|
||||
"steps": [
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#k001",
|
||||
"tool": "bash",
|
||||
"args": {
|
||||
"command": "cat min.js"
|
||||
},
|
||||
"text": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
|
||||
},
|
||||
{
|
||||
"op": "output",
|
||||
"id": "#k002",
|
||||
"tool": "bash",
|
||||
"args": {
|
||||
"command": "cat log"
|
||||
},
|
||||
"text": "first line\nyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy\nlast"
|
||||
}
|
||||
],
|
||||
"expect": [
|
||||
"[#k001]\nxxxxxxxxxxxxxxxxxxxx […cut]\n[0 lines elided, full output #k001]",
|
||||
"[#k002]\nfirst line\n[1 lines elided, full output #k002]\nlast"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
# Context hygiene
|
||||
|
||||
Spec 3.0.0. What a session does to keep tool results from filling the model's context — the same in
|
||||
a chat and in an agent session, in both projects. The texts and numbers are `loop.json` `context`;
|
||||
the cases are `conformance/context.json`; what the model is told is `prompts/blocks/context.md`.
|
||||
|
||||
## The one rule
|
||||
|
||||
**Every measure acts when a result is inserted. Nothing already sent to the model is ever
|
||||
rewritten.** Changing earlier context invalidates the prompt cache from that point on: a local
|
||||
server processes everything after it again, and a cloud API charges full input price for it again —
|
||||
on a long context that costs more than the measure saves. So no earlier result is ever shortened,
|
||||
replaced by a stub or removed afterwards. The one deliberate break is compaction, which already
|
||||
replaces the history with a summary.
|
||||
|
||||
## 1. Every result has an id
|
||||
|
||||
Every tool result is given an id when it is inserted: `#` and lowercase hex, at least
|
||||
`ids.digits` (4) digits, unique in the session and never reused — not after a compaction either. It
|
||||
is the first thing in the result, in brackets: `[#a3f2]`, then a newline and the result.
|
||||
|
||||
A read's bracket says more: the path as the call gave it, the lines shown, the file's length in
|
||||
lines and the first `sha_digits` (8) hex digits of the SHA-256 of the file's content —
|
||||
|
||||
```
|
||||
[#a3f2 src/app.ts lines 1-200 of 340 sha 1c9e04ab]
|
||||
1: import …
|
||||
```
|
||||
|
||||
— and an empty file `[#a3f2 notes.txt empty sha e3b0c442]`. The full hash is kept by the session;
|
||||
only it decides whether a file is unchanged.
|
||||
|
||||
**Every result is stored with the session, in full** — before any cap, and for a stub the text it
|
||||
stands for — so `recall` can show any of them for as long as the session exists.
|
||||
|
||||
## 2. An unchanged re-read is a stub
|
||||
|
||||
A read is answered with a stub when the file's content hash equals that of an **earlier full read
|
||||
of the same path still in the dedupe table**, and the lines asked for lie **wholly inside** that
|
||||
read's lines:
|
||||
|
||||
```
|
||||
[#b710 src/app.ts lines 50-80 of 340 sha 1c9e04ab] Unchanged since #a3f2, which already shows lines 1-200.
|
||||
```
|
||||
|
||||
The bracket is the new read's; the sentence names the earlier result and its lines. A stub points
|
||||
at a full result, never at another stub. A file that changed — by this session's own edit or from
|
||||
outside — has another hash and is read in full; a range reaching past the earlier read is read in
|
||||
full.
|
||||
|
||||
## 3. Large output is capped on the way in
|
||||
|
||||
A result over `cap.max_lines` (400) lines or `cap.max_chars` (40,000) characters keeps its head —
|
||||
as many whole lines as fit `head_lines` (200) and `head_chars` (20,000) — and its tail — likewise,
|
||||
`tail_lines` (100) and `tail_chars` (10,000) — with one line between:
|
||||
|
||||
```
|
||||
[1234 lines elided, full output #c9d0]
|
||||
```
|
||||
|
||||
A first line longer than `head_chars` is cut to it and ends ` […cut]`; the elided line follows it
|
||||
whatever it counts. Reads and `recall` are not capped this way: each has its own window (2000 lines
|
||||
from an offset).
|
||||
|
||||
## 4. `recall`
|
||||
|
||||
`recall(id, offset?, limit?)` shows a stored result again, in full, up to 2000 lines from `offset`:
|
||||
|
||||
```
|
||||
[#d004 recall #c9d0 lines 1-2000 of 3400]
|
||||
…
|
||||
```
|
||||
|
||||
An id the session never gave: `[#d004] No stored result #ffff in this session.` The `#` may be left
|
||||
out of the argument. A recall is not capped, is not deduplicated, and is itself stored.
|
||||
|
||||
## 5. Edits answer with the diff
|
||||
|
||||
`edit`, `multiedit`, `apply_patch` and `write` over an existing file answer with the unified diff
|
||||
of the change (three lines of context, hunks only — the path is in the bracket) under a bracket
|
||||
naming the path and the new content's hash:
|
||||
|
||||
```
|
||||
[#e201 src/app.ts sha 7d0a91c3]
|
||||
@@ -12,3 +12,3 @@
|
||||
```
|
||||
|
||||
`write` of a new file answers `[#e202 docs/new.md sha 52b6e3a0] Created, 14 lines.` — the model
|
||||
wrote the content and has it. The diff is capped like any output.
|
||||
|
||||
## 6. The same search or listing twice is a stub
|
||||
|
||||
For `dedupe.by_result` — `list`, `glob`, `grep`, `search` — a call with the same arguments (compared
|
||||
as JSON with sorted keys) as an earlier one in the dedupe table, **whose result is the same text**,
|
||||
is answered `[#f002] Same result as #f001.` The call is run again and compared, so a change from
|
||||
anywhere shows up as a full result. Other tools are never deduplicated: a command's output twice is
|
||||
two results.
|
||||
|
||||
## 7. Compaction
|
||||
|
||||
Compaction is the one deliberate break of the cache, and it already exists. In 3.0.0 two things
|
||||
come with it:
|
||||
|
||||
- After the summary the harness adds — itself, not the model — a table of the reads still in the
|
||||
dedupe table: the newest full read of each path, newest last, at most `compaction.rows` (50):
|
||||
|
||||
```
|
||||
## Files read before this summary
|
||||
|
||||
Read a file again for its content; recall shows a result by its id.
|
||||
|
||||
| Path | Lines | sha | Result |
|
||||
|---|---|---|---|
|
||||
| src/app.ts | 1-200 of 340 | 1c9e04ab | #a3f2 |
|
||||
```
|
||||
|
||||
With no reads there is no table.
|
||||
- **The dedupe table is emptied**, so no stub ever points at content that is no longer in the
|
||||
context. A read after compaction is a full read; a `recall` of any earlier id still works.
|
||||
|
||||
## 8. Earlier reasoning
|
||||
|
||||
Left to the model's own chat template: the harness sends the history as it does, and neither strips
|
||||
reasoning a template keeps nor adds what it drops.
|
||||
|
||||
## 9. What the model is told
|
||||
|
||||
`prompts/blocks/context.md`, in every session that has `recall`.
|
||||
|
||||
## Not in 3.0.0
|
||||
|
||||
Eliding old content in batches behind a threshold, and measuring quality and tokens with and without
|
||||
these measures, are left for a later spec. Every measure above ships with its unit and conformance
|
||||
cases.
|
||||
|
||||
## Over the link
|
||||
|
||||
A device keeps its sessions' stored results itself; `recall` in an agent session reads the device's
|
||||
store. The ids are in the result text, so a client showing the transcript shows them as they are.
|
||||
@@ -60,5 +60,51 @@
|
||||
"default": 50
|
||||
},
|
||||
"lifecycle": "A subagent's own background commands stop when it ends, and its report says so. A session's jobs stop when the session is deleted. A front end asked to exit while jobs run asks the person first; one with nobody to ask waits for the jobs the prompt started, then stops the rest."
|
||||
},
|
||||
"context": {
|
||||
"about": "Context hygiene (spec 3.0.0, context.md): every measure acts when a result is inserted, and nothing already sent to the model is ever rewritten — changing earlier context throws away the prompt cache from that point. conformance/context.json gives the texts.",
|
||||
"ids": {
|
||||
"about": "Every tool result gets an id: `#` and lowercase hex, at least 4 digits, unique in the session and never reused (not after a compaction either), so `recall` finds any of them. It is the first thing in the result, in brackets.",
|
||||
"digits": 4
|
||||
},
|
||||
"read_header": "[{{id}} {{path}} lines {{from}}-{{to}} of {{of}} sha {{sha}}]",
|
||||
"read_header_empty": "[{{id}} {{path}} empty sha {{sha}}]",
|
||||
"edit_header": "[{{id}} {{path}} sha {{sha}}]",
|
||||
"header": "[{{id}}]",
|
||||
"unchanged": "Unchanged since {{earlier}}, which already shows lines {{from}}-{{to}}.",
|
||||
"same_result": "Same result as {{earlier}}.",
|
||||
"elided": "[{{n}} lines elided, full output {{id}}]",
|
||||
"created": "Created, {{n}} lines.",
|
||||
"no_result": "No stored result {{target}} in this session.",
|
||||
"recall_header": "[{{id}} recall {{target}} lines {{from}}-{{to}} of {{of}}]",
|
||||
"sha_digits": 8,
|
||||
"cap": {
|
||||
"about": "A result over max_lines lines or max_chars characters keeps its head (as many whole lines as fit both head limits) and its tail (likewise, from what is left), with the elided line between. When even the first line does not fit head_chars, it is cut to it and ends ` […cut]`, and the elided line follows however many whole lines it counts (0 included): it is there whenever anything was left out. Not applied to read (its own window: 2000 lines from an offset) or recall (likewise). The full output is stored with the session.",
|
||||
"max_lines": 400,
|
||||
"max_chars": 40000,
|
||||
"head_lines": 200,
|
||||
"head_chars": 20000,
|
||||
"tail_lines": 100,
|
||||
"tail_chars": 10000
|
||||
},
|
||||
"dedupe": {
|
||||
"about": "by_hash: a read whose file has the same content hash as an earlier full read in the table, and whose lines lie wholly inside that read's, is answered with `unchanged`. by_result: a call with the same arguments as an earlier one in the table, whose result is the same text, is answered with `same_result`. Stubs point at the full result, never at another stub. The table is per session, and compaction empties it.",
|
||||
"by_hash": [
|
||||
"read"
|
||||
],
|
||||
"by_result": [
|
||||
"list",
|
||||
"glob",
|
||||
"grep",
|
||||
"search"
|
||||
]
|
||||
},
|
||||
"compaction": {
|
||||
"about": "The one deliberate break of the cache. After the summary the harness adds the table of reads still in the dedupe table — the newest full read of each path, newest last, at most `rows` — and then empties the dedupe table, so no stub ever points at content that is gone from the context. No reads: no table.",
|
||||
"heading": "## Files read before this summary",
|
||||
"line": "Read a file again for its content; recall shows a result by its id.",
|
||||
"rows": 50
|
||||
},
|
||||
"reasoning": "Earlier turns' reasoning is left to the model's chat template: the harness sends the history as it does, neither stripping reasoning the template keeps nor adding what it drops."
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user